19#ifndef PECLET_AMR_FLOW_HPP
20#define PECLET_AMR_FLOW_HPP
53#include "peclet/core/common/host_parallel.hpp"
54#include "peclet/core/common/types.hpp"
55#include "peclet/core/common/view.hpp"
62 const char*
v = std::getenv(
name);
63 return v &&
v[0] && !(
v[0] ==
'0' &&
v[1] ==
'\0');
70template <
int Dim,
unsigned Bits,
class Flu
idFn>
73 std::vector<Index> start(
static_cast<std::size_t
>(n) + 1, 0);
77 start[
static_cast<std::size_t
>(
i) + 1] = start[
static_cast<std::size_t
>(
i)] +
cnt;
79 const Index nf = start[
static_cast<std::size_t
>(n)];
80 std::vector<Index> nbr(
static_cast<std::size_t
>(
nf));
81 std::vector<int> axis(
static_cast<std::size_t
>(
nf)), dir(
static_cast<std::size_t
>(
nf));
82 std::vector<double>
aArea(
static_cast<std::size_t
>(
nf)),
rArea(
static_cast<std::size_t
>(
nf)),
83 dist(
static_cast<std::size_t
>(
nf)), alpha(
static_cast<std::size_t
>(
nf));
84 std::vector<Index> upupI(
static_cast<std::size_t
>(
nf)), upupJ(
static_cast<std::size_t
>(
nf));
85 std::vector<double> invVol(
static_cast<std::size_t
>(n));
86 std::vector<char> fluid(
static_cast<std::size_t
>(n));
88 invVol[
static_cast<std::size_t
>(
i)] = 1.0 / ap.
cellVolume(
i);
89 fluid[
static_cast<std::size_t
>(
i)] = isFluid(
i) ? 1 : 0;
90 Index k = start[
static_cast<std::size_t
>(
i)];
92 nbr[
static_cast<std::size_t
>(k)] =
j;
93 axis[
static_cast<std::size_t
>(k)] =
ax;
94 dir[
static_cast<std::size_t
>(k)] =
dr;
96 rArea[
static_cast<std::size_t
>(k)] =
area;
97 dist[
static_cast<std::size_t
>(k)] = d;
98 alpha[
static_cast<std::size_t
>(k)] =
al;
135 Kokkos::parallel_for(
144 const double ui = (
a == 0) ?
u0(
i) : (
a == 1) ?
u1(
i) :
u2(
i);
146 const double uj = (
a == 0) ?
u0(
j) : (
a == 1) ?
u1(
j) :
u2(
j);
147 d +=
aA(k) *
dr(k) * 0.5 * (
ui +
uj);
166 Kokkos::parallel_for(
171 const double ui = (
a == 0) ?
u0(
i) : (
a == 1) ?
u1(
i) :
u2(
i);
172 const double uj = (
a == 0) ?
u0(
j) : (
a == 1) ?
u1(
j) :
u2(
j);
188 Kokkos::parallel_reduce(
189 "amr::flow_divface", g.
n,
195 d +=
aA(k) *
dr(k) *
uf(k);
215 Kokkos::parallel_for(
221 const double fi =
f(
i);
222 double gp[3] = {0, 0, 0},
gm[3] = {0, 0, 0};
223 int np[3] = {0, 0, 0},
nm[3] = {0, 0, 0};
238 for (
int a = 0;
a < 3; ++
a) {
270 Kokkos::parallel_for(
274 for (
int a = 0;
a < 3; ++
a) {
276 for (
int k = 0; k < 3; ++k)
277 acc += w(
s * 9 +
a * 3 + k) *
f(idx(
s * 9 +
a * 3 + k));
293 Kokkos::parallel_for(
295 b(
i) = fluid(
i) ? (idiag *
uc(
i) +
fc - gradP(
i) -
adv(
i)) * rscale(
i) : 0.0;
318 Kokkos::parallel_for(
326 const double rs = rscale(
i);
335 const double ui = (
a == 0) ?
u0(
i) : (
a == 1) ?
u1(
i) :
u2(
i);
336 const double uj = (
a == 0) ?
u0(
j) : (
a == 1) ?
u1(
j) :
u2(
j);
364template <
class FldFn>
408 Kokkos::parallel_for(
414 auto fld = [&](
Index c) {
return (comp == 0) ?
u0(
c) : (comp == 1) ?
u1(
c) :
u2(
c); };
415 double sou = 0.0,
fou = 0.0;
456 Kokkos::parallel_for(
462 auto fld = [&](
Index c) {
return (comp == 0) ?
u0(
c) : (comp == 1) ?
u1(
c) :
u2(
c); };
489 Kokkos::parallel_for(
499 Kokkos::parallel_for(
509template <
unsigned Bits = 21u>
516 init(
t, detail::filledVec<3>(h0), origin);
542 for (
int d = 0; d < 3; ++d)
543 b[d] = mu_ / (h0_[d] * h0_[d]);
598 ghostProjReq_ = on ? 1 : 0;
614 ghostSampledReq_ = on ? 1 : 0;
723 outerIters_ = (n < 1) ? 1 : n;
729 template <
class SdfFn>
735 const bool prof_ = [] {
736 const char*
e = std::getenv(
"PECLET_AMR_PROFILE_SETUP");
737 return e &&
e[0] ==
'1';
739 auto profT0_ = std::chrono::steady_clock::now();
744 auto now = std::chrono::steady_clock::now();
745 double ms = std::chrono::duration<double, std::milli>(
now -
profT0_).count();
746 std::fprintf(
stderr,
"[setSolid] %-28s %9.1f ms (%6.1f us/leaf)\n",
name,
ms,
747 1
e3 *
ms /
static_cast<double>(n));
748 profT0_ = std::chrono::steady_clock::now();
751 mom_.init(*t_, h0_, origin_);
752 pres_.
init(*t_, h0_);
762 const bool wantGhost = (ghostProjReq_ != 0);
764 ghostSampled_ = (ghostSampledReq_ == 1) &&
wantGhost;
779 momSolver_.setDistributed({}, {}, 0);
780 pcg_.setDistributed({}, {}, 0);
783 profPhase(
"mom.build (SDF+cut stencils)");
789 &
gfine, shiftD_,
false, gpsRho_, gpsMaxN_);
791 }
else if (ghostProj_) {
803 if (ghostProjReq_ == 1)
804 throw std::runtime_error(
805 "amr ghost projection: an overlay row's ±2 closure reach crosses a 2:1 level "
806 "boundary — widen the refineToSdf band margin");
809 "peclet::core AmrFlow: AUTO scheme fell back to the aperture projection (the "
810 "finest band is too thin for the ghost overlay). Select explicitly with "
811 "setGhostProjection to silence this notice.\n");
828 presMGD_.build(*dist_, h0_,
binFn, &dhalo_,
true, presBottomExtent_);
830 presMG_.build(*t_, h0_,
binFn,
true,
false,
831 true, presBottomExtent_);
837 presMGD_.build(*dist_, h0_,
binFn, &dhalo_,
true, presBottomExtent_);
839 presMG_.build(*t_, h0_,
binFn,
true,
false,
840 true, presBottomExtent_);
848 gpPocket_ = dist_ ? std::vector<char>{} :
findPocketCells(*t_, pres_, mom_.sdfCRaw());
855 presMGD_.build(*dist_, h0_,
openFn, &dhalo_,
true, presBottomExtent_);
857 presMG_.build(*t_, h0_,
openFn,
true,
false,
858 true, presBottomExtent_);
863 presMGD_.setBottom(presBottom_);
865 presMG_.setBottom(presBottom_);
868 presMGD_.setRemoveMean(
true);
869 pcg_.setDistributed([
this](
View<double> v) { presMGD_.sync(0,
v); }, allred_,
870 presMGD_.extendedSize(0));
872 presMG_.setRemoveMean(
true);
889 auto A = mom_.assembleOperator();
890 momOp_ = MomentumOp{};
892 momOp_.diag =
toDevice(
A.diag,
"mo_diag");
893 momOp_.faceStart =
toDevice(
A.start,
"mo_start");
894 momOp_.faceNbr =
toDevice(
A.nbr,
"mo_nbr");
895 momOp_.faceCoef =
toDevice(
A.coef,
"mo_coef");
916 if (momMGon_ && dist_ && !useStaircaseMG_) {
917 auto A = mom_.assembleOperator();
918 std::vector<Index>
lstart(1, 0);
919 std::vector<Index>
lnbr;
920 std::vector<double>
lcoef;
922 for (
Index k =
A.start[
static_cast<std::size_t
>(
i)];
923 k <
A.start[
static_cast<std::size_t
>(
i) + 1]; ++k)
924 if (
A.nbr[
static_cast<std::size_t
>(k)] < n) {
925 lnbr.push_back(
A.nbr[
static_cast<std::size_t
>(k)]);
926 lcoef.push_back(
A.coef[
static_cast<std::size_t
>(k)]);
931 momMG_.setGaussSeidel(momGS_);
932 }
else if (momMGon_ && !dist_) {
933 if (useStaircaseMG_) {
934 std::vector<double>
kap(
static_cast<std::size_t
>(n));
935 std::vector<char>
fl(
static_cast<std::size_t
>(n)),
cu(
static_cast<std::size_t
>(n));
937 kap[
static_cast<std::size_t
>(
i)] = mom_.kappa(
i);
938 fl[
static_cast<std::size_t
>(
i)] = mom_.isFluid(
i) ? 1 : 0;
939 cu[
static_cast<std::size_t
>(
i)] = mom_.isCut(
i) ? 1 : 0;
941 velMG_.build(*t_, h0_, rho_ / dt_, mu_, momOp_,
kap,
fl,
cu, mgMinCoarse_);
942 velMG_.setGaussSeidel(momGS_);
946 auto A = mom_.assembleOperator();
947 momMG_.build(*t_,
A.diag,
A.start,
A.nbr,
A.coef);
948 momMG_.setGaussSeidel(momGS_);
952 std::vector<char>
fluidVec(
static_cast<std::size_t
>(n));
954 fluidVec[
static_cast<std::size_t
>(
i)] = mom_.isFluid(
i) ? 1 : 0;
965 std::vector<double>
rs(
static_cast<std::size_t
>(n));
967 rs[
static_cast<std::size_t
>(
i)] = mom_.rhsScale(
i);
969 fluid_ = geom_.
fluid;
970 if (ghostGrad_ && !ghostSampled_)
971 buildGhostGradOverlay();
983 std::vector<char>
cfRegular(
static_cast<std::size_t
>(nExt_), 0);
985 cfRegular[
static_cast<std::size_t
>(
i)] = (mom_.isFluid(
i) && !mom_.isCut(
i)) ? 1 : 0;
987 std::vector<double>
rx(
static_cast<std::size_t
>(nExt_), 0.0);
989 rx[
static_cast<std::size_t
>(
i)] =
cfRegular[
static_cast<std::size_t
>(
i)] ? 1.0 : 0.0;
994 cfRegular[
static_cast<std::size_t
>(
s)] = (
rh[
static_cast<std::size_t
>(
s)] > 0.5) ? 1 : 0;
1000 throw std::logic_error(
"peclet::amr: C/F regular flag queried outside [0, nExt)");
1001 return cfRegular[
static_cast<std::size_t
>(
s)] != 0;
1029 auto countGhost = [&](
const std::vector<Index>& col) {
1036 for (
int a = 0;
a < 3; ++
a)
1041 for (
int a = 0;
a < 3; ++
a)
1042 cfGrad_[
static_cast<std::size_t
>(
a)] =
1063 for (
int a = 0;
a < 3; ++
a)
1064 cfGrad_[
static_cast<std::size_t
>(
a)] =
CfCsrDev{};
1076 std::vector<double>
mc(
static_cast<std::size_t
>(n), 0.0);
1078 mc[
static_cast<std::size_t
>(
i)] =
1079 (mom_.isFluid(
i) && !(!gpPocket_.empty() && gpPocket_[
static_cast<std::size_t
>(
i)]))
1083 if (!
rows.coupled[
static_cast<std::size_t
>(
r)])
1084 mc[
static_cast<std::size_t
>(
rows.cell[
static_cast<std::size_t
>(
r)])] = 0.0;
1090 if (ghostSampled_) {
1094 const double rs = mom_.rhsScale(
i);
1095 for (
Index k =
msd.start[
static_cast<std::size_t
>(
i)];
1096 k <
msd.start[
static_cast<std::size_t
>(
i) + 1]; ++k)
1097 msd.coef[
static_cast<std::size_t
>(k)] *=
rs;
1111 auto mk = [&](
const char*
l) {
return View<double>(
l,
static_cast<std::size_t
>(nExt_)); };
1113 gprh_ =
mk(
"gp_rhat");
1115 gpph_ =
mk(
"gp_phat");
1118 gpsh_ =
mk(
"gp_shat");
1126 for (
int c = 0;
c < 3; ++
c) {
1127 u_[
c] =
View<double>(
"df_u",
static_cast<std::size_t
>(nExt_));
1129 Kokkos::deep_copy(u_[
c], 0.0);
1131 p_ =
View<double>(
"df_p",
static_cast<std::size_t
>(nExt_));
1132 phi_ =
View<double>(
"df_phi",
static_cast<std::size_t
>(nExt_));
1133 div_ =
View<double>(
"df_div",
static_cast<std::size_t
>(nExt_));
1135 geom_.
nbr.extent(0));
1136 faceFieldBuilt_ =
false;
1137 bmom_ =
View<double>(
"df_bmom",
static_cast<std::size_t
>(n));
1138 Kokkos::deep_copy(p_, 0.0);
1144 for (
int c = 0;
c < 3; ++
c) {
1145 defc_[
c] =
View<double>(
"df_defc",
static_cast<std::size_t
>(n));
1146 Kokkos::deep_copy(defc_[
c], 0.0);
1149 u0_[
c] =
View<double>(
"df_u0",
static_cast<std::size_t
>(nExt_));
1150 uprev_[
c] =
View<double>(
"df_uprev",
static_cast<std::size_t
>(nExt_));
1156 advDiag_ =
View<double>(
"df_advdiag",
static_cast<std::size_t
>(n));
1158 momOp_.advStart = geom_.
start;
1159 momOp_.advNbr = geom_.
nbr;
1160 momOp_.advDiag = advDiag_;
1161 momOp_.advCoef = advCoef_;
1162 momOp_.hasAdv =
false;
1163 momSolver_.setJacobi(2, 0.7);
1167 if (momMGon_ && !(dist_ && useStaircaseMG_)) {
1168 if (useStaircaseMG_)
1169 momSolver_.setPreconditioner(
1172 momSolver_.setPreconditioner(
1175 pcg_.setVcycle(2, 2, 60, 0.8);
1176 pcg_.setSingular(
true);
1179 profPhase(
"overlays upload + state alloc");
1214 return SpClock::time_point{};
1216 return SpClock::now();
1222 spAcc_[
ph] += std::chrono::duration<double, std::milli>(SpClock::now() -
t0).count();
1229 spMom_ += lastMomIters_;
1230 spPres_ += lastPresIters_;
1231 spOuter_ += lastOuterIters_;
1235 std::fprintf(
stderr,
"[step-prof] leaves %lld | pressure MG levels %zu:", (
long long)n_,
1238 std::fprintf(
stderr,
" %lld", (
long long)n);
1242 if (spSteps_ < spWindow_)
1245 "glue (copies + syncs)",
"grad3 (binary)",
"cf delta kernels",
"overlay delta kernels",
1246 "advection build",
"momentum rhs",
"momentum solve",
"divergence",
1247 "pressure solve",
" . binary matvec",
" . overlay matvec",
" . projection",
1248 " . MG preconditioner",
"finish projection"};
1249 const double w =
static_cast<double>(spSteps_);
1250 const double leaves =
static_cast<double>(n_);
1252 for (
int k = 0; k <
SP_N; ++k)
1256 "[step-prof] %d steps | %.3f ms/step (%.3f us/leaf) | mom %.1f it, "
1257 "pres %.1f it, outer %.2f\n",
1258 spSteps_,
tot / w, 1
e3 *
tot / w /
leaves, spMom_ / w, spPres_ / w, spOuter_ / w);
1259 for (
int k = 0; k <
SP_N; ++k) {
1261 std::fprintf(
stderr,
"[step-prof] %-22s %8.3f ms/step %8.4f us/leaf %5.1f%%%s\n",
1263 100.0 * spAcc_[k] / (
tot > 0 ?
tot : 1.0),
nested ?
" (nested)" :
"");
1265 for (
int k = 0; k <
SP_N; ++k)
1268 spMom_ = spPres_ = spOuter_ = 0.0;
1276 throw std::runtime_error(std::string(
"amr::AmrFlow::") +
what +
1277 ": no cut-cell operator -- call setSolid() first (Python: "
1278 "Flow.set_solid / set_solid_spheres; after begin_adapt, "
1285 const double idiag = rho_ / dt_;
1287 lastOuterIters_ = 1;
1292 for (
int c = 0;
c < 3; ++
c)
1302 for (
int a = 0;
a < 3; ++
a)
1320 momOp_.hasAdv = implicitFou_;
1327 const bool useUf = faceFieldBuilt_ && ufAdvect_;
1332 for (
int c = 0;
c < 3; ++
c) {
1344 if (momMGon_ && useStaircaseMG_)
1345 velMG_.setFineOp(momOp_);
1359 for (
int c = 0;
c < 3; ++
c) {
1377 (momMGSolver_ ? momSolver_.solveDefectCorrection(
1387 if (outerIters_ > 1) {
1388 lastOuterIters_ =
outer + 1;
1391 for (
int c = 0;
c < 3; ++
c)
1394 if (
dmax < outerTol_)
1397 for (
int c = 0;
c < 3; ++
c)
1426 Kokkos::deep_copy(phi_, 0.0);
1452 if (
dbg && !dist_) {
1455 const FvOp&
op0 = presMG_.op(0);
1456 auto st =
op0.faceStart;
1459 auto iv =
op0.invVol;
1461 double su = 0.0,
sv = 0.0,
sn = 0.0;
1462 Kokkos::parallel_reduce(
1463 "amr::dbg_compat", n,
1468 const double m = (d > 1
e-30) ? 1.0 : 0.0;
1474 std::fprintf(
stderr,
"[amr pres] rhs fluid-mean=%.3e |rhs|_D=%.3e (rel mean %.3e)\n",
su /
sv,
1475 std::sqrt(
sn), (
su /
sv) / (std::sqrt(
sn) + 1
e-300));
1476 if (!presDbgSpdDone_) {
1480 presDbgSpdDone_ =
true;
1482 yr(
"dbg_y",
static_cast<std::size_t
>(n)),
Ax(
"dbg_Ax",
static_cast<std::size_t
>(n)),
1483 Ay(
"dbg_Ay",
static_cast<std::size_t
>(n));
1484 Kokkos::parallel_for(
1486 xr(
i) = std::sin(0.7 *
static_cast<double>(
i) + 0.3);
1487 yr(
i) = std::cos(1.3 *
static_cast<double>(
i) + 1.1);
1493 Kokkos::parallel_reduce(
1503 "[amr pres] SPD probe: <y,Lx>_D=%.15e <x,Ly>_D=%.15e rel asym=%.2e; "
1504 "Rayleigh <x,Lx>_D=%.3e <y,Ly>_D=%.3e (must be <=0)\n",
1510 const auto R = dist_
1513 lastPresIters_ =
R.iters;
1515 std::fprintf(
stderr,
"[amr pres] pcg iters=%d res0=%.3e res=%.3e rel=%.3e\n",
R.iters,
1516 R.res0,
R.res,
R.res0 > 0 ?
R.res /
R.res0 : 0.0);
1518 Kokkos::deep_copy(presMGD_.b(0), div_);
1519 Kokkos::deep_copy(presMGD_.x(0), 0.0);
1521 presMGD_.vcycle(2, 2, 60, 0.8);
1522 Kokkos::deep_copy(phi_, presMGD_.x(0));
1525 Kokkos::deep_copy(presMG_.b(0), div_);
1526 Kokkos::deep_copy(presMG_.x(0), 0.0);
1531 presMG_.vcycle(2, 2, 60, 0.8);
1537 std::fprintf(
stderr,
"[amr pres] vcycle %2d |r|=%.6e\n",
it + 1,
rn);
1540 Kokkos::deep_copy(phi_, presMG_.x(0));
1584 faceFieldBuilt_ =
true;
1586 for (
int a = 0;
a < 3; ++
a)
1590 for (
int c = 0;
c < 3; ++
c)
1593 rho_ / dt_, mu_, n);
1614 adaptOldT_ = std::make_unique<Octree>(*t_);
1615 for (
int c = 0;
c < 3; ++
c)
1616 adaptU_[
static_cast<std::size_t
>(
c)] =
velocity(
c);
1622 for (
int c = 0;
c < 3; ++
c)
1623 adaptGradU_[
static_cast<std::size_t
>(
c)] =
1630 template <
class SdfFn>
1633 throw std::runtime_error(
"amr::AmrFlow::finishAdapt called without beginAdapt");
1634 std::array<std::vector<double>, 3>
nu;
1635 for (
int c = 0;
c < 3; ++
c)
1636 nu[
static_cast<std::size_t
>(
c)] =
1637 transferField(*adaptOldT_, adaptU_[
static_cast<std::size_t
>(
c)], *t_,
true,
1638 dist_ ? &adaptGradU_[
static_cast<std::size_t
>(
c)] :
nullptr);
1639 std::vector<double>
np =
1640 transferField(*adaptOldT_, adaptP_, *t_,
true, dist_ ? &adaptGradP_ :
nullptr);
1642 for (
int c = 0;
c < 3; ++
c) {
1649 for (
int c = 0;
c < 3; ++
c)
1650 adaptU_[
static_cast<std::size_t
>(
c)].clear();
1660 template <
class SdfFn>
1663 throw std::runtime_error(
"amr::AmrFlow::rebalanceMpi requires initMpi");
1664 std::vector<std::vector<double>>
cols(4);
1665 for (
int c = 0;
c < 3; ++
c)
1668 dist_->rebalance(
cols);
1670 for (
int c = 0;
c < 3; ++
c) {
1680 auto m = Kokkos::create_mirror_view(p_);
1682 m(
i) =
h[
static_cast<std::size_t
>(
i)];
1683 Kokkos::deep_copy(p_, m);
1689 Kokkos::parallel_for(
1704 std::vector<double>
h(
static_cast<std::size_t
>(n_));
1705 auto m = Kokkos::create_mirror_view(
s);
1706 Kokkos::deep_copy(m,
s);
1708 h[
static_cast<std::size_t
>(
i)] = m(
i);
1724 Kokkos::deep_copy(
mg.x(0), 0.0);
1725 mg.vcycle(mgVcPre_, mgVcPre_, mgVcBottom_, 0.7);
1734 Kokkos::parallel_reduce(
1735 "amr::flow_maxdiff", n,
1737 double d =
a(
i) - b(
i);
1743 Kokkos::Max<double>(m));
1748 auto m = Kokkos::create_mirror_view(d);
1749 Kokkos::deep_copy(m, d);
1751 h[
static_cast<std::size_t
>(
i)] = m(
i);
1755 auto m = Kokkos::create_mirror_view(u_[
c]);
1757 m(
i) =
h[
static_cast<std::size_t
>(
i)];
1758 Kokkos::deep_copy(u_[
c], m);
1769 if (
v.extent(0) ==
static_cast<std::size_t
>(n_))
1770 return peclet::core::toVector(
v);
1771 View<double> packed(Kokkos::view_alloc(
"amr::local_packed", Kokkos::WithoutInitializing),
1772 static_cast<std::size_t
>(n_));
1774 return peclet::core::toVector(
packed);
1782 View<double> packed(Kokkos::view_alloc(
"amr::vel_packed", Kokkos::WithoutInitializing),
1783 static_cast<std::size_t
>(n_) * 3);
1784 for (
int c = 0;
c < 3; ++
c) {
1788 Kokkos::parallel_for(
1791 return peclet::core::toVector(
packed);
1835 std::vector<Index>
out;
1837 for (std::size_t
L = 0;
L < presMG_.numLevels(); ++
L)
1838 out.push_back(presMG_.numLeaves(
L));
1841 for (std::size_t
L = 0;
L < presMGD_.numLevels(); ++
L)
1842 out.push_back(presMGD_.numLeaves(
L));
1843 for (std::size_t
L = 0;
L < presMGD_.numStageLevels(); ++
L)
1844 out.push_back(presMGD_.stageLeaves(
L));
1853 return dist_ ? presMGD_.bottomName() : presMG_.bottomName();
1871 presBottom_ = B::Auto;
1872 else if (kind ==
"smoother")
1873 presBottom_ = B::Smoother;
1874 else if (kind ==
"agglomerated")
1875 presBottom_ = B::Agglomerated;
1877 throw std::runtime_error(
1878 "amr::AmrFlow::setPressureBottom: expected 'auto', 'smoother' or 'agglomerated'");
1880 presMGD_.setBottom(presBottom_);
1882 presMG_.setBottom(presBottom_);
1896 throw std::runtime_error(
"amr::AmrFlow::setPressureBottomExtent: must be >= 1");
1897 presBottomExtent_ =
e;
1904 std::vector<double>
faceField()
const {
return peclet::core::toVector(uf_); }
1933 t.nbr = peclet::core::toVector(geom_.
nbr);
1934 t.upupI = peclet::core::toVector(geom_.
upupI);
1935 t.upupJ = peclet::core::toVector(geom_.
upupJ);
1936 t.axis = peclet::core::toVector(geom_.
axis);
1937 t.dir = peclet::core::toVector(geom_.
dir);
1938 t.rawArea = peclet::core::toVector(geom_.
rawArea);
1939 t.dist = peclet::core::toVector(geom_.
dist);
1940 t.alpha = peclet::core::toVector(geom_.
alpha);
1941 t.seam = peclet::core::toVector(geom_.
seam);
1942 t.sampI = peclet::core::toVector(geom_.
sampI);
1943 t.sampJ = peclet::core::toVector(geom_.
sampJ);
1944 t.uuRecI = peclet::core::toVector(geom_.
uuRecI);
1945 t.uuRecJ = peclet::core::toVector(geom_.
uuRecJ);
1946 t.recStart = peclet::core::toVector(geom_.
recStart);
1947 t.recCell = peclet::core::toVector(geom_.
recCell);
1948 t.d1I = peclet::core::toVector(geom_.
d1I);
1949 t.d1J = peclet::core::toVector(geom_.
d1J);
1950 t.recDist = peclet::core::toVector(geom_.
recDist);
1951 t.recW = peclet::core::toVector(geom_.
recW);
1960 std::vector<double>
c(
static_cast<std::size_t
>(nExt_) * 3, 0.0);
1961 for (
Index s = 0;
s < nExt_; ++
s) {
1962 const std::array<long, 3>
lo = pres_.
loOf(
s);
1963 const double w =
static_cast<double>(1L << pres_.
levelOf(
s));
1964 for (
int a = 0;
a < 3; ++
a)
1965 c[
static_cast<std::size_t
>(
s) * 3 +
static_cast<std::size_t
>(
a)] =
1966 origin_[
a] + h0_[
a] * (
static_cast<double>(
lo[
a] + shiftD_[
a]) + 0.5 * w);
2024 std::array<long, 3> g{};
2025 for (
int a = 0;
a < 3; ++
a)
2026 g[
a] = dist_ ?
static_cast<long>(dist_->globalFineSize()[
a])
2027 :
static_cast<long>(pres_.
fineExt()[
a]);
2032 const FvOp&
gpOp0() {
return dist_ ? presMGD_.op(0) : presMG_.op(0); }
2037 template <
class SdfFn>
2039 dhalo_.init(*dist_);
2040 for (
int a = 0;
a < 3; ++
a)
2041 shiftD_[
a] = dist_->blockFineOrigin()[
a];
2042 auto resv = [hp = &dhalo_,
sh = shiftD_](
const std::array<long, 3>&
p) ->
Index {
2043 std::array<long, 3> g =
p;
2044 for (
int a = 0;
a < 3; ++
a)
2046 return hp->resolveGlobal(g);
2048 mom_.setFrameShift(shiftD_);
2049 mom_.setResolver(
resv);
2058 const auto rT0 = std::chrono::steady_clock::now();
2065 mom_.build(
sdfFn, rho_ / dt_, beta);
2078 if (ghostSampled_) {
2089 &
gfine, shiftD_,
true, gpsRho_, gpsMaxN_);
2093 }
else if (ghostProj_ || ghostGrad_) {
2102 std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() -
rT0).count();
2103 const long pend = dhalo_.resolveMisses();
2105 std::fprintf(
stderr,
"[setSolid] round %d: build %.1f ms, %lld ghosts, %ld pending\n",
2115 std::fprintf(
stderr,
"[setSolid] distributed discovery fixpoint: %d rounds (%lld ghosts)\n",
2116 rounds, (
long long)dhalo_.numGhosts());
2119 mom_.build(
sdfFn, rho_ / dt_, beta);
2121 nExt_ = dhalo_.extendedSize();
2123 allred_ = [
this](
double s) {
return allSum(
s); };
2170 const long sc = 1L <<
Lc,
sh =
sc >> 1;
2171 for (
int tt = 0;
tt < 3; ++
tt) {
2174 for (
int dir = -1; dir <= 1; dir += 2) {
2176 std::array<long, 3>
lo =
bc;
2178 for (
int oct = 0;
oct < (1 << 3); ++
oct) {
2179 std::array<long, 3>
q =
lo;
2180 for (
int d = 0; d < 3; ++d)
2213 const std::array<long, 3>
lo = pres_.
loOf(
j);
2214 const long sj = 1L <<
Lj,
sh =
sj >> 1;
2215 std::array<long, 3>
p{};
2216 for (
int d = 0; d < 3; ++d)
2218 p[axis] = (dir > 0) ?
lo[axis] +
sj :
lo[axis] - 1;
2219 for (
int k = 0; k < 4; ++k) {
2220 std::array<long, 3>
q =
p;
2222 for (
int t = 0;
t < 3; ++
t) {
2240 const Index ng = dhalo_.numGhosts();
2241 std::vector<std::array<long, 3>>
glo(
static_cast<std::size_t
>(
ng));
2242 std::vector<unsigned>
glv(
static_cast<std::size_t
>(
ng));
2243 for (
Index g = 0; g <
ng; ++g) {
2244 for (
int a = 0;
a < 3; ++
a)
2245 glo[
static_cast<std::size_t
>(g)][
a] =
2246 static_cast<long>(dhalo_.ghostCoord(g)[
a]) - shiftD_[
a];
2247 glv[
static_cast<std::size_t
>(g)] =
static_cast<unsigned>(dhalo_.level(dhalo_.numLocal() + g));
2249 mom_.setGhosts(
glo,
glv);
2279 Kokkos::deep_copy(presMGD_.b(0),
r);
2280 Kokkos::deep_copy(presMGD_.x(0), 0.0);
2281 presMGD_.vcycle(2, 2, 60, 0.8);
2282 presMGD_.vcycle(2, 2, 60, 0.8);
2283 Kokkos::deep_copy(
z, presMGD_.x(0));
2285 Kokkos::deep_copy(presMG_.b(0),
r);
2286 Kokkos::deep_copy(presMG_.x(0), 0.0);
2287 presMG_.vcycle(2, 2, 60, 0.8);
2288 presMG_.vcycle(2, 2, 60, 0.8);
2289 Kokkos::deep_copy(
z, presMG_.x(0));
2311 Kokkos::deep_copy(gprh_, gpr_);
2318 Kokkos::deep_copy(gpv_, 0.0);
2319 Kokkos::deep_copy(gpp_, 0.0);
2335 Kokkos::deep_copy(gps_, gpr_);
2337 const double snorm =
2353 Kokkos::deep_copy(gpr_, gps_);
2355 const double rnorm =
2390 seamSampleRecords_ =
h.numSampleRecords;
2391 seamLayerRecords_ =
h.numLayerRecords;
2394 void clearSeamRecon() {
2406 seamSampleRecords_ = 0;
2407 seamLayerRecords_ = 0;
2415 void buildGhostGradOverlay() {
2417 std::vector<Index> cells;
2421 const Index m =
static_cast<Index>(cells.size());
2422 std::vector<Index> idx(
static_cast<std::size_t
>(m) * 9, 0);
2423 std::vector<double> w(
static_cast<std::size_t
>(m) * 9, 0.0);
2434 const Index i = cells[
static_cast<std::size_t
>(
s)];
2436 for (
int a = 0;
a < 3; ++
a) {
2437 const std::size_t
o =
static_cast<std::size_t
>(
s) * 9 +
static_cast<std::size_t
>(
a) * 3;
2438 for (
int k = 0; k < 3; ++k)
2439 idx[
o +
static_cast<std::size_t
>(k)] =
i;
2447 w[
o + 1] = -0.5 /
h;
2456 w[
o + 2] = -0.5 /
h;
2461 w[
o + 1] = -1.0 /
h;
2469 w[
o + 1] = -2.0 /
h;
2476 w[
o + 1] = -1.0 /
h;
2494 static double triFrac(
double a,
double b,
double c) {
2495 const bool pa =
a >= 0.0,
pb = b >= 0.0,
pc =
c >= 0.0;
2496 const int np = (
pa ? 1 : 0) + (
pb ? 1 : 0) + (
pc ? 1 : 0);
2516 const double den = (x -
y) * (x -
z);
2517 return den > 1
e-300 ? (x * x) /
den : 1.0;
2532 const double den = (x -
y) * (x -
z);
2533 return 1.0 - (
den > 1
e-300 ? (x * x) /
den : 1.0);
2535 template <
class SdfFn>
2537 if (apertureOrder_ >= 2) {
2540 const int t1 = (axis + 1) % 3,
t2 = (axis + 2) % 3;
2542 const double e1 = 0.5 * h0_[
t1],
e2 = 0.5 * h0_[
t2];
2543 auto at = [&](
double d1,
double d2) {
2551 const double frac = 0.25 * (triFrac(
c00,
c10, cc) + triFrac(
c10,
c11, cc) +
2559 for (
int d = 0; d < 3; ++d) {
2565 double gmag = std::sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2]);
2568 int t1 = (axis + 1) % 3,
t2 = (axis + 2) % 3;
2570 double denom = (std::fabs(g[
t1]) * h0_[
t1] + std::fabs(g[
t2]) * h0_[
t2]) /
gmag;
2577 const Octree* t_ =
nullptr;
2578 Vec<3> h0_ = detail::filledVec<3>(1.0);
2580 double rho_ = 1.0, mu_ = 1.0, dt_ = 1
e6;
2582 bool presPCG_ =
true;
2583 bool presDbgSpdDone_ =
false;
2584 bool momMGon_ =
true;
2585 bool useStaircaseMG_ =
false;
2586 int mgVcPre_ = 2, mgVcBottom_ = 30;
2587 Index mgMinCoarse_ = 256;
2588 bool momGS_ =
false;
2591 bool ghostGrad_ =
true;
2594 bool ghostProj_ =
false;
2595 int8_t ghostProjReq_ = -1;
2597 int apertureOrder_ = 2;
2598 int gpMatrixOrder_ = 2, gpRhsOrder_ = 2;
2614 int outerIters_ = 1;
2615 double outerTol_ = 1
e-6;
2616 double momTol_ = 1
e-8;
2617 double presTol_ = 1
e-10;
2618 bool advect_ =
false;
2619 bool implicitFou_ =
true;
2622 bool ready_ =
false;
2623 int lastMomIters_ = 0, lastPresIters_ = 0, lastOuterIters_ = 1;
2625 bool stepProf_ =
amrEnvFlag(
"PECLET_AMR_PROFILE_STEP");
2626 bool spHeader_ =
false;
2627 int spWindow_ = [] {
2628 const char*
e = std::getenv(
"PECLET_AMR_PROFILE_STEP_WINDOW");
2629 const int v =
e ? std::atoi(
e) : 0;
2630 return v > 0 ?
v : 50;
2633 double spAcc_[
SP_N] = {};
2634 double spMom_ = 0.0, spPres_ = 0.0, spOuter_ = 0.0;
2637 AmrPoisson<3, Bits> pres_;
2642 Index presBottomExtent_ = 4;
2648 std::array<View<double>, 3> defc_;
2651 GhostGradOverlay gc_;
2652 GhostOverlayDev gpOv_;
2653 bool ghostSampled_ =
false;
2654 int8_t ghostSampledReq_ = 0;
2655 double gpsRho_ = 2.2;
2657 GhostOverlaySampledDev gpOvS_;
2658 GhostGradCsrDev gcS_;
2659 CfCsrDev gpsMomDelta_;
2660 std::vector<char> gpPocket_;
2662 CfCompCsrDev cfDiv_;
2663 std::array<CfCsrDev, 3> cfGrad_;
2664 CfCompCsrDev cfUfVel_;
2665 Index cfGhostCols_ = 0;
2666 Index cfCutFaces_ = 0;
2667 bool seamRecon_ =
true;
2668 Index seamSampleRecords_ = 0;
2669 Index seamLayerRecords_ = 0;
2671 View<double> gpr_, gprh_, gpp_, gpph_, gpv_, gps_, gpsh_, gpt_;
2674 std::array<View<double>, 3> u_, gx_;
2675 std::array<View<double>, 3> u0_,
2679 bool faceFieldBuilt_ =
2681 bool ufAdvect_ =
true;
2683 std::unique_ptr<Octree> adaptOldT_;
2684 std::array<std::vector<double>, 3> adaptU_;
2685 std::vector<double> adaptP_;
2686 std::array<std::vector<std::array<double, 3>>, 3> adaptGradU_;
2687 std::vector<std::array<double, 3>> adaptGradP_;
2692 LeafHaloExchange dhex_;
2694 std::array<long, 3> shiftD_{};
2696 std::function<
double(
double)> allred_;
void setImplicitAdvection(bool on)
Implicit-FOU deferred correction (default ON).
Index numSeamSampleRecords() const
Seam-reconstruction census (docs/amr_cf_convective.md §5.2): how many sample records (one per 2:1 sub...
void setApertureOrder(int order)
FULL directional ghost-cell projection (the AMR port of flow's collocated set_ghost_projection): the ...
void setMomentumMG(bool on)
Use the Galerkin velocity multigrid (MomentumMG) as the momentum BiCGStab preconditioner.
std::vector< double > velocity(int c) const
Copy a velocity component back to host (single D2H, no host loop — S2a).
void finishAdapt(SdfFn &&sdfFn)
Rebuild on the mutated octree and transfer the snapshotted fields onto it.
void setGhostGradient(bool on)
Directional ghost cell-gradient for the −∇pⁿ predictor and the projection's cell correction (the AMR ...
void ghostPrec(View< const double > r, View< double > z)
void step(int momIters=100, int presIters=60)
void initMpi(DistributedOctree< 3, Bits > &d)
Distributed mode (docs/amr_distributed_flow.md, rung 4): run this solver on one ORB block of a Distri...
void gpProject(View< double > v)
void prepareDistributed(SdfFn &&sdfFn)
Install the resolver seams, run every prober to the miss-collect fixpoint, freeze the ±2 halo,...
void setBodyForce(double fx, double fy, double fz)
void setPressurePCG(bool on)
Use MG-preconditioned CG for the pressure solve (default) vs plain V-cycles.
void syncVel()
Refresh the ghost tails of the three velocity components (one batched message round).
std::vector< double > velocities() const
All three velocity components interleaved as a flat (n,3) row-major host buffer (out[i*3+c]) with a s...
std::vector< double > pressure() const
Copy the pressure field back to host (single D2H), (num_leaves,) — the incremental-rotational p.
void setPressureTol(double tol)
Relative tolerance of the PRESSURE solve, for both drivers: the MG-PCG (the default) and the ghost pr...
void setVelocityMGMinCoarse(Index m)
Pore-scale cap for the staircase velocity-MG: the coarsest level keeps ≥ this many cells,...
void setVelocity(int c, const std::vector< double > &h)
Set a velocity component from host (testing / initial conditions).
bool isFluid(Index i) const
Per-leaf fluid mask (false inside the solid) — for host-side post-processing / bindings.
double divNormL2()
L2 norm of the (openness-weighted) divergence of the current velocity — the ghost-closed divergence w...
std::array< long, 3 > globalFineExtent() const
The DOMAIN's fine extent (D1): the block's own single-rank, where block == domain.
void copyToHost(const View< double > &d, std::vector< double > &h) const
Copy a device View into a host vector (sized n_).
void setAdvectionScheme(int s)
High-order advection scheme: 0 = second-order upwind (SOU, default), 1 = Koren TVD.
int solveGhostBiCGStab(View< double > x, View< const double > b, int maxIters, double tol=1e-10)
void init(const Octree &t, const Vec< 3 > &h0, Vec< 3 > origin=Vec< 3 >{})
void installGhostMeta()
Mirror the halo registry's ghost metadata (block-local lo + level) into mom_ and pres_.
std::vector< double > debugSou(int comp)
DEBUG: the raw high-order advection ∇·(u u_comp) per cell from the current velocity (== host oracle::...
void setViscosity(double mu)
Index numGhostCells() const
Distributed: number of ghost slots in the ±2 registry (0 single-rank).
void runMgVcycle(MG &mg, View< const double > r, View< double > z)
Run one V-cycle of a momentum MG as a preconditioner: z = M⁻¹ r.
Index numCfGhostColumns() const
Developer diagnostic: entries of the four C/F-scheme overlays that read a GHOST slot (0 single-rank,...
void finishProjection(Index n)
Shared projection tail: build the div-free face field from u* + φ, correct the cell velocities (ABC /...
void setGhostProjection(bool on, int matrixOrder=2, int rhsOrder=2)
void probeCfScheme()
Discovery arm for the C/F quadratic scheme (cf_scheme.hpp), run inside prepareDistributed's miss-coll...
Index pressureBottomExtent() const
The bottom extent the next setSolid builds the ladder with (default 4).
Index numSeamLayerRecords() const
The face-layer half of the census: one record per (regular coarse cell, face) whose far side is refin...
int lastOuterIters() const
Picard outer iterations actually run in the last step (1 unless setOuterIterations(>1)).
void setDensity(double rho)
Index numCfCutFaces() const
The C/F census (docs/amr_cf_flux_gate.md §6.5): owned forEachFaceFull slots on a 2:1 sub-face between...
double divNormFace()
L2 norm of the APERTURE-weighted divergence of the ABC face field uf_ (built each project()).
void ghostMatvec(View< const double > x, View< double > y)
void spAdd(int ph, SpClock::time_point t0)
void setPressureBottom(const std::string &kind)
What solves the coarsest pressure level (docs/amr_mg_depth.md §6.6/§11.6): "auto" (the default — the ...
std::string pressureMgBottom() const
What actually solves the coarsest pressure level (docs/amr_mg_depth.md §6.6/§6.7): "jacobi" (60 dampe...
void project(int presIters=60)
Pressure projection of the current velocity in place.
void syncScalar(View< double > v)
Refresh the ghost tail of one cell scalar (p, φ, Krylov scratch).
std::vector< double > faceField() const
Copy the divergence-free face field to host (one value per CSR (sub)face, forEachFaceFull order).
void setMomentumTol(double tol)
Relative tolerance for the per-step momentum BiCGStab solve (default 1e-8).
void setSeamReconstruction(bool on)
ABLATION (docs/amr_cf_convective.md §5.5, Q-C).
void setPressureBottomExtent(Index e)
Where the pressure ladder stops lifting the root and hands over to the bottom (docs/amr_mg_depth....
void setUfAdvection(bool on)
ABLATION (developer tier).
void setPressure(const std::vector< double > &h)
Write the accumulated rotational pressure from host (restart / finishAdapt).
std::vector< double > localVector(const View< double > &v) const
Host copy of the LOCAL rows of a (possibly ghost-extended) per-cell field.
const FvOp & gpOp0()
The pressure operator the ghost solver runs on (distributed MG level 0 or presMG_'s).
std::chrono::steady_clock SpClock
void probeCfTangential(Index coarse, int axis)
Every coordinate cfAppendStencil can probe from the coarse cell of one C/F face: the tangential ± nei...
void setVelocityMGStaircase(bool on)
Choose the momentum-MG coarse-operator strategy: false (default) = Galerkin (MomentumMG,...
BlockOctree< 3, Bits > Octree
void zeroSolid(View< double > v)
Zero a per-leaf field on non-fluid cells (the transferred fields' solid cleanup).
void spEndStep()
Close a profiled step; print + reset when the window fills.
int lastPresIters() const
Pressure PCG iterations of the last step.
void requireOperator(const char *what) const
Every solve entry point needs the cut-cell operators that setSolid builds; without them the state vie...
void rebalanceMpi(SdfFn &&sdfFn)
Distributed load rebalance (docs/amr_distributed_flow.md, rung 6): re-decompose the octree by leaf co...
void probeSeamLayer()
Discovery arm for the SEAM builder (seam_recon.hpp), run beside probeCfScheme() inside prepareDistrib...
StepPhase
One incompressible step on device (Stokes, or Navier–Stokes with setAdvection).
static double maxAbsDiff(View< const double > a, View< const double > b, Index n)
Max |a − b| over all cells (the Picard outer-loop convergence measure).
std::vector< Index > pressureMgLevels() const
The pressure multigrid's ladder as BUILT on this rank: per-level leaf counts, level 0 first (docs/amr...
int lastMomIters() const
Total momentum BiCGStab iterations (summed over the 3 components) of the last step.
void beginAdapt()
Snapshot the octree topology + (u, p) ahead of an external mesh mutation.
void setGhostSampled(bool on, double rho=2.2, long maxSamples=0)
SAMPLED ghost projection: the mixed-level cut-band mode (docs/amr_mixed_level_cut_band_plan....
Vec< 3 > betaPerAxis() const
The viscous coefficient of each derivative axis, mu/h0_a^2 — the octree twin of the structured solver...
void setMomentumGS(bool on)
Opt-in: use the multicolour Gauss–Seidel smoother in the momentum MG (Galerkin or staircase) instead ...
void setMomentumMGSolver(bool on)
Opt-in (P4): solve the momentum predictor with the velocity multigrid used as the solver — MG-precond...
std::vector< double > slotCenters() const
World centre of every slot of the extended (local + ghost) leaf array, row-major (nExt,...
double allSum(double s) const
Global sum (identity single-rank).
void setAdvection(bool on)
Enable momentum advection ∇·(u u) (default OFF ⇒ Stokes).
void setCfScheme(int scheme)
Coarse/fine (2:1) interface scheme (cf_scheme.hpp): 1 = Martin–Cartwright tangential quadratic,...
void setSolid(SdfFn &&sdfFn)
Build the cut-cell operators (host) + upload all device structures.
void setOuterIterations(int n, double tol=1e-6)
Optional Picard outer loop over the lagged advection (mirror of flow's outerIters_): each outer itera...
FaceTopologyHost faceTopology() const
void init(const Octree &t, Real h0, Vec< 3 > origin=Vec< 3 >{})
Cubic-cell overload — the pre-Phase-3 spelling (docs/amr_anisotropic.md §2).
SpClock::time_point spMark() const
Cell-centered FV Poisson operator on one (periodic) block octree.
unsigned levelOf(Index slot) const
Octree level of an extended slot (local leaf or declared ghost).
void buildOpenness(OpenFn &&openFn)
Build face openness from a geometry callable openFn(faceCentreWorld, axis) -> [0,1] (1 = fully fluid,...
Real cellWidth(Index i, int axis) const
Cell width along axis (Phase 3: per axis).
void setFrameShift(const std::array< long, Dim > &s)
Distributed frame shift: this block's global fine origin.
void forEachFaceFull(Index i, Fn &&fn) const
Like forEachFaceNeighbor but exposes geometry for a consistent FV divergence/gradient: fn(neighbour,...
const std::array< Coord, Dim > & fineExt() const
Per-axis fine-grid extent (brick·2^lmax) — the periodic wrap modulus.
std::pair< Index, unsigned > probeSlot(const std::array< long, Dim > &p) const
Covering slot + level of a block-local probe (longs, possibly outside the block).
Index periodicNeighbor(Index i, int axis, int dir) const
Periodic face neighbour leaf (covering the cell just across the face).
void setGhosts(std::vector< std::array< long, Dim > > lo, std::vector< unsigned > lv)
Declare the ghost slots [n, n+nGhost): block-local lo corner (longs — ghosts lie outside the block) a...
const Octree & octree() const
void setOrigin(const Vec< Dim > &o)
void init(const Octree &t, Real h0)
Cubic-cell overload — the pre-Phase-3 spelling, and exactly the per-axis one with all three spacings ...
void setResolver(ExtResolver r)
std::array< long, Dim > loOf(Index slot) const
Block-local lo corner of an extended slot as longs (ghosts may lie outside the block).
Real cellVolume(Index i) const
Per-block adaptive octree over block-local Morton codes.
const Vec< Dim > & h0() const
Phase 3: the FINEST spacing per axis (docs/amr_anisotropic.md §2).
const AmrGeometry< Dim > & globalGeometry() const
void init(const LeafHalo< Dim, Bits > &h)
void exchange3(View< double > x0, View< double > x1, View< double > x2, int tag=46) const
Batched 3-component refresh (one message round for u0,u1,u2 — stride-3 packing).
void exchange(View< double > x, int tag=45) const
Refresh x[nLocal, nLocal+nGhost) (x is the extended device field, size >= extendedSize()).
Bottom
What solves the coarsest level (docs/amr_mg_depth.md §6.6).
FaceGeom buildFaceGeom(const AmrPoisson< Dim, Bits > &ap, FluidFn &&isFluid)
Build FaceGeom from a built AmrPoisson (openness set) + a fluid predicate.
void divergence(const FaceGeom &g, View< const double > u0, View< const double > u1, View< const double > u2, View< double > div)
Openness-weighted FV divergence: div_i = invVol_i Σ_faces α·area·dir·½(u^axis_i+u^axis_j),...
void correct(View< double > uc, View< const double > gphi, View< const char > fluid, Index n)
u_c -= gradPhi_c on fluid cells (the projection velocity correction).
void removeMeanVolReduced(View< double > u, View< const double > invVol, View< const double > mask, Index n, const std::function< double(double)> &reduce)
Project u onto the FLUID range: zero solid cells, then subtract the volume-weighted mean over the flu...
Index countCfCutFaces(const AmrPoisson< 3, Bits > &ap, const BlockOctree< 3, Bits > &t, RegularFn &®ularOk, FluidFn &&fluidOk)
The C/F CENSUS (docs/amr_cf_flux_gate.md §6.5): how many OWNED forEachFaceFull slots (i,...
auto makeBinaryOpenFnMixed(const BlockOctree< 3, Bits > &t, const AmrPoisson< 3, Bits > &pres, SdfFn sdfFn, const Vec< 3 > &h0, Vec< 3 > origin, std::array< long, 3 > frameShift={})
The canonical (sub)face openness for mixed-level cut bands: open iff both adjacent ACTUAL leaf center...
CfCsrDev uploadCfCsr(const CfCsr &h, const char *name)
GhostOverlaySampled buildGhostOverlaySampled(const BlockOctree< 3, Bits > &t, const AmrPoisson< 3, Bits > &pres, SdfFn &&sdf, int matrixOrder, int rhsOrder, Vec< 3 > origin=Vec< 3 >{}, const std::array< long, 3 > *globalFine=nullptr, std::array< long, 3 > frameShift={}, bool discovery=false, double rhoFactor=2.2, long maxSamples=0)
Build the sampled overlay.
void momRhs(View< const double > uc, View< const double > gradP, View< const double > adv, View< const double > rscale, View< const char > fluid, double idiag, double fc, View< double > b, Index n)
Momentum RHS for one component: b_i = fluid ? (idiag·u_i + f_c − gradP_i − adv_i)·rscale_i : 0 (== Am...
void applyGhostGrad(const GhostGradOverlay &ov, View< const double > f, View< double > gx, View< double > gy, View< double > gz)
Overwrite gx/gy/gz on the overlay cells with the directional stencil applied to f.
CfCompCsr buildCfDivDelta(const AmrPoisson< 3, Bits > &ap, const BlockOctree< 3, Bits > &t, RegularFn &®ularOk, FluidFn &&fluidOk, CfScheme scheme)
(D_scheme − D_std) for the face-average divergence div_i = invV·Σ α·A·dir·(face value).
void advectExplicit(const FaceGeom &g, View< const double > u0, View< const double > u1, View< const double > u2, int comp, double rho, int advScheme, View< double > defc, View< const double > uf, bool useFace, bool seamOn)
Fully-explicit high-order advection for component comp: defc = ρ·SOU (no implicit FOU; the setImplici...
void deferredSou(const FaceGeom &g, View< const double > u0, View< const double > u1, View< const double > u2, int comp, double rho, int advScheme, View< double > defc, View< const double > uf, bool useFace, bool seamOn)
Deferred-correction advection term for component comp: defc = ρ·SOU − ρ·FOU (UNSCALED; the predictor ...
void cfApply(const CfCsrDev &c, View< const double > f, View< double > out)
out(i) += Σ coef·f(slot).
double hoFaceValue(double upup, double up, double down, int scheme)
High-order advected face value from the two upwind cells (upup, up) and the downwind cell (down).
void buildFou(const FaceGeom &g, View< const double > u0, View< const double > u1, View< const double > u2, double rho, View< const double > rscale, View< double > advDiag, View< double > advCoef, View< const double > uf, bool useFace)
Build the implicit-FOU advection operator from the lagged velocity u0..2 (uⁿ) entirely on device,...
GhostOverlaySampledDev uploadGhostOverlaySampled(const GhostOverlaySampled &h)
CfCompCsrDev uploadCfCompCsr(const CfCompCsr &h, const char *name)
CfUfDelta buildCfUfDelta(const AmrPoisson< 3, Bits > &ap, const BlockOctree< 3, Bits > &t, RegularFn &®ularOk, FluidFn &&fluidOk, CfScheme scheme)
void presUpdate(View< double > p, View< const double > phi, View< const double > div, View< const char > fluid, double rho_dt, double mu, Index n)
Rotational incremental pressure update: p += (ρ/dt)φ − μ·div, on fluid cells.
SeamReconHost buildSeamRecon(const AmrPoisson< 3, Bits > &ap, RegularFn &®ularOk, FluidFn &&fluidOk, CfScheme scheme)
Build the seam-reconstruction tables over the LOCAL rows of ap (docs/amr_cf_convective....
double hoFaceValueSeam(double upup, double up, double upStar, double downStar, double d1, double dUU, double dD, int scheme)
The same reconstruction with every distance and every sample made explicit — the rule of docs/amr_cf_...
void ghostApplyDelta(const GhostOverlayDev &ov, View< const double > x, View< double > y)
Device matrix overlay (== ghostApplyDeltaHost). Distinct rows per thread: no atomics.
std::vector< std::array< double, Dim > > transferGradients(const DistributedOctree< Dim, Bits > &d, const std::vector< double > &f)
transferField's minmod prolongation gradients on a DistributedOctree: bit-identical to the block-loca...
std::vector< char > findPocketCells(const BlockOctree< 3, Bits > &t, const AmrPoisson< 3, Bits > &pres, const std::vector< double > &sdfC)
Fragmentation guard (the AMR port of flow's host-BFS pocket guard): the BINARY coupled-face graph (a ...
std::vector< double > transferField(const BlockOctree< Dim, Bits > &oldT, const std::vector< double > &oldF, const BlockOctree< Dim, Bits > &newT, bool linear=true, const std::type_identity_t< std::vector< std::array< double, Dim > > > *gradIn=nullptr)
Conservative remap of a leaf field from oldT to newT (same domain).
GhostGradCsrHost buildSampledGradCsr(const GhostOverlaySampled &ov, const BlockOctree< 3, Bits > &t, const AmrPoisson< 3, Bits > &pres, IsCutFn &&isCut, OkFn &&classicOk)
void ghostApplyDeltaSampled(const GhostOverlaySampledDev &ov, View< const double > x, View< double > y)
Device matrix overlay (== ghostApplyDeltaSampledHost). Distinct rows per thread: no atomics.
void residualFv(const FvOp &op, View< const double > u, View< const double > rhs, View< double > res)
res = rhs − H u.
void grad3(const FaceGeom &g, View< const double > f, View< double > gx, View< double > gy, View< double > gz)
ABC cell-gradient of a scalar field f: gx/gy/gz = ½(g⁻+g⁺) of the adjacent face gradients along each ...
bool amrEnvFlag(const char *name)
Truthy environment flag (unset / "" / "0" ⇒ false).
void buildFaceField(const FaceGeom &g, View< const double > u0, View< const double > u1, View< const double > u2, View< const double > phi, View< double > uf)
Build the ABC/Basilisk divergence-free FACE field: uf(k) = ½(u^axis_i+u^axis_j) − (φ₊−φ₋)/dist for fa...
double divFaceNorm(const FaceGeom &g, View< const double > uf)
L2 norm of the divergence of the FACE field uf (the div-free flux diagnostic / host-parity check).
auto makeBinaryOpenFn(SdfFn sdfFn, const Vec< 3 > &h0)
Binary openness callable factory for the MG surrogate: a face is open iff both adjacent centers (prob...
void applyGhostGradCsr(const GhostGradCsrDev &ov, View< const double > f, View< double > gx, View< double > gy, View< double > gz)
CfCsr buildCfLapDelta(const AmrPoisson< 3, Bits > &ap, const BlockOctree< 3, Bits > &t, double factor, RowFn &&rowOk, FluidFn &&fluidOk, CfScheme scheme)
(∇²_scheme − ∇²_std) as a scalar CSR, ×factor (pass μ for the momentum deferred-correction RHS,...
void ghostDivergDeltaSampled(const GhostOverlaySampledDev &ov, View< const double > u0, View< const double > u1, View< const double > u2, View< double > d)
Device divergence overlay (== ghostDivergDeltaSampledHost).
void applyFv(const FvOp &op, View< const double > u, View< double > Lu)
Hu = (c0·I + cD·L) u (consistent conservative FV Laplacian, c0=0/cD=1 ⇒ pure L).
GhostGradCsrDev uploadGhostGradCsr(const GhostGradCsrHost &h)
GhostOverlayDev uploadGhostOverlay(const GhostOverlay &h)
double advectedFaceValue(const SeamView &sv, Index k, bool upIsI, double phiUpUp, double phiUp, double phiDown, double distK, int advScheme, const FldFn &fld)
THE advected face value at one (sub)face slot — written once so deferredSou and advectExplicit cannot...
std::array< CfCsr, 3 > buildCfGradDelta(const AmrPoisson< 3, Bits > &ap, const BlockOctree< 3, Bits > &t, RegularFn &®ularOk, FluidFn &&fluidOk, CfScheme scheme)
(G_scheme − G_std) for the ABC cell gradient gradOf/grad3.
SeamView makeSeamView(const FaceGeom &g, bool on)
Bundle a FaceGeom's seam tables for the kernels.
std::vector< std::array< double, Dim > > transferFieldGradients(const BlockOctree< Dim, Bits > &oldT, const std::vector< double > &oldF)
Per-old-leaf minmod prolongation gradients (per fine-coordinate unit) — transferField's stencil,...
void ghostDivergDelta(const GhostOverlayDev &ov, View< const double > u0, View< const double > u1, View< const double > u2, View< double > d)
Device divergence overlay (== ghostDivergDeltaHost).
CfCsr buildMomSeamDelta(const GhostOverlaySampled &ov, const BlockOctree< 3, Bits > &t, const AmrPoisson< 3, Bits > &pres, const Mom &mom, SdfFn &&sdf, Vec< 3 > origin, double idiag, double mu, long *nSkipped=nullptr, std::array< long, 3 > frameShift={})
Momentum ξ-overlay seam correction (plan §6.2, rung 1): at overlay rows with a different-level ±1 nei...
void cfApplyComp(const CfCompCsrDev &c, View< const double > u0, View< const double > u1, View< const double > u2, View< double > out)
out(i) += Σ coef·u[comp](slot).
CfScheme
Coarse/fine interface scheme for the collocated flow operators.
@ standard
raw coarse value (two-point flux; 1st-order at 2:1 faces)
@ quadratic
Martin–Cartwright tangential quadratic (2nd-order at 2:1 faces)
GhostOverlay buildGhostOverlay(const BlockOctree< 3, Bits > &t, const AmrPoisson< 3, Bits > &pres, const std::vector< double > &sdfC, int matrixOrder, int rhsOrder, bool *bandViolation=nullptr)
Build the overlay from the octree + the cell-centered SDF samples (AmrCutCell::sdfCRaw — EXTENDED ove...
The face CSR topology faceField() is indexed by, copied to host: start the per-cell row offsets (size...
std::vector< double > d1J
std::vector< double > center
World centre of EVERY slot (local leaves then ghosts, size nExt): the only way a caller can say what ...
std::vector< Index > seam
std::vector< Index > upupI
std::vector< Index > recStart
std::vector< Index > recCell
std::vector< double > recDist
std::vector< Index > uuRecJ
std::vector< Index > sampI
std::vector< Index > uuRecI
std::vector< double > alpha
std::vector< double > recW
std::vector< double > d1I
std::vector< Index > upupJ
std::vector< double > rawArea
std::vector< double > dist
std::vector< Index > sampJ
std::vector< Index > start
Device mirror of a BlockOctree's leaf arrays + device-callable queries.
void upload(const Host &t)
(Re)upload the host octree's current leaf set to the device.
Device mirror of a CfCsr (empty when the scheme is standard / no C/F rows).
Scalar-input CSR overlay: out(i) += Σ_k coef·f(slot) over rows with C/F faces.
View< Index > uuRecJ
per descriptor: upstream-probe record for up == j, else −1
View< double > recDist
per record: dUU for UPSTREAM use (unused as a φ_U* sample)
View< Index > uuRecI
per descriptor: upstream-probe record for up == i, else −1
View< Index > recCell
record entries: cell slot
View< double > alphaArea
α·area (physical) per face, size nFaces
View< int > dir
face direction +1/-1, size nFaces
View< double > recW
record entries: weight
View< double > invVol
1/V_i per cell, size n
View< double > rawArea
raw face area (physical, no openness) per face — advection flux
View< int > axis
face axis 0/1/2, size nFaces
View< Index > upupJ
upstream-of-j probe (periodicNeighbor(j,axis,+dir)) — SOU, size nFaces
View< Index > nbr
neighbour leaf per face, size nFaces
View< double > alpha
openness per face (gradient gate), size nFaces
View< double > dist
face-normal distance (physical) per face, size nFaces
View< Index > start
CSR row offsets, size n+1.
View< Index > sampI
per descriptor: sample record when i is the COARSE cell, else −1
View< Index > seam
descriptor id per face slot, −1 = plain slot; size nFaces
View< Index > upupI
upstream-of-i probe (periodicNeighbor(i,axis,−dir)) — SOU, size nFaces
View< Index > recStart
record CSR offsets, size nRec+1
View< char > fluid
per-cell fluid flag, size n
View< double > d1J
per descriptor: half width of j along the face axis (physical)
View< double > d1I
per descriptor: half width of i along the face axis (physical)
View< Index > sampJ
per descriptor: sample record when j is the COARSE cell, else −1
Device CSR gradient overlay (sampled mode) + upload + apply (the variable-length analog of GhostGradO...
Ghost-gradient overlay (setGhostGradient): per cut cell, a precomputed 3-point directional FD stencil...
View< double > w
[n*9] stencil weight (0 = unused)
Index n
number of overlay (cut) cells
View< Index > idx
[n*9] stencil cell, slot s*9 + axis*3 + k
View< Index > cell
[n] leaf index
Device mirror of GhostOverlay (uploaded once per setSolid).
Device mirror of GhostOverlaySampled (uploaded once per setSolid).
Sampled overlay: the classic per-row fields (weights, states, rescale — base; base....
Host ghost-projection overlay: one row per non-clean fluid leaf (== cut cell: some ±1 center sample s...
Host-side seam-reconstruction tables (docs/amr_cf_convective.md §5.2), built once per AmrFlow::setSol...
The seam tables as the advective kernels capture them: const Views plus the ablation flag (AmrFlow::s...