20#ifndef PECLET_CORE_AMR_FLOW_HPP
21#define PECLET_CORE_AMR_FLOW_HPP
23#ifdef PECLET_CORE_HAVE_MORTON
50template <
int Dim,
unsigned Bits,
class Flu
idFn>
52 const Index n =
ap.octree().numLeaves();
53 std::vector<Index> start(
static_cast<std::size_t
>(n) + 1, 0);
56 ap.forEachFaceFull(
i, [&](
Index,
int,
int,
double,
double,
double) { ++
cnt; });
57 start[
static_cast<std::size_t
>(
i) + 1] = start[
static_cast<std::size_t
>(
i)] +
cnt;
59 const Index nf = start[
static_cast<std::size_t
>(n)];
60 std::vector<Index> nbr(
static_cast<std::size_t
>(
nf));
61 std::vector<int> axis(
static_cast<std::size_t
>(
nf)), dir(
static_cast<std::size_t
>(
nf));
62 std::vector<double>
aArea(
static_cast<std::size_t
>(
nf)),
rArea(
static_cast<std::size_t
>(
nf)),
63 dist(
static_cast<std::size_t
>(
nf)), alpha(
static_cast<std::size_t
>(
nf));
64 std::vector<Index> upupI(
static_cast<std::size_t
>(
nf)), upupJ(
static_cast<std::size_t
>(
nf));
65 std::vector<double> invVol(
static_cast<std::size_t
>(n));
66 std::vector<char> fluid(
static_cast<std::size_t
>(n));
68 invVol[
static_cast<std::size_t
>(
i)] = 1.0 /
ap.cellVolume(
i);
69 fluid[
static_cast<std::size_t
>(
i)] = isFluid(
i) ? 1 : 0;
70 Index k = start[
static_cast<std::size_t
>(
i)];
72 nbr[
static_cast<std::size_t
>(k)] =
j;
73 axis[
static_cast<std::size_t
>(k)] =
ax;
74 dir[
static_cast<std::size_t
>(k)] =
dr;
76 rArea[
static_cast<std::size_t
>(k)] =
area;
77 dist[
static_cast<std::size_t
>(k)] = d;
78 alpha[
static_cast<std::size_t
>(k)] =
al;
81 upupI[
static_cast<std::size_t
>(k)] =
ap.periodicNeighbor(
i,
ax, -
dr);
82 upupJ[
static_cast<std::size_t
>(k)] =
ap.periodicNeighbor(
j,
ax,
dr);
115 Kokkos::parallel_for(
124 const double ui = (
a == 0) ?
u0(
i) : (
a == 1) ?
u1(
i) :
u2(
i);
126 const double uj = (
a == 0) ?
u0(
j) : (
a == 1) ?
u1(
j) :
u2(
j);
127 d +=
aA(k) *
dr(k) * 0.5 * (
ui +
uj);
146 Kokkos::parallel_for(
151 const double ui = (
a == 0) ?
u0(
i) : (
a == 1) ?
u1(
i) :
u2(
i);
152 const double uj = (
a == 0) ?
u0(
j) : (
a == 1) ?
u1(
j) :
u2(
j);
168 Kokkos::parallel_reduce(
169 "amr::flow_divface", g.
n,
175 d +=
aA(k) *
dr(k) *
uf(k);
195 Kokkos::parallel_for(
201 const double fi =
f(
i);
202 double gp[3] = {0, 0, 0},
gm[3] = {0, 0, 0};
203 int np[3] = {0, 0, 0},
nm[3] = {0, 0, 0};
218 for (
int a = 0;
a < 3; ++
a) {
236 Kokkos::parallel_for(
261 Kokkos::parallel_for(
269 const double rs = rscale(
i);
278 const double ui = (
a == 0) ?
u0(
i) : (
a == 1) ?
u1(
i) :
u2(
i);
279 const double uj = (
a == 0) ?
u0(
j) : (
a == 1) ?
u1(
j) :
u2(
j);
312 Kokkos::parallel_for(
319 double sou = 0.0,
fou = 0.0;
356 Kokkos::parallel_for(
387 Kokkos::parallel_for(
397 Kokkos::parallel_for(
407template <
unsigned Bits = 21u>
489 outerIters_ = (n < 1) ? 1 : n;
495 template <
class SdfFn>
497 const Index n = t_->numLeaves();
499 mom_.init(*t_, h0_, origin_);
500 mom_.build(
sdfFn, rho_ / dt_, mu_ / (h0_ * h0_));
501 pres_.
init(*t_, h0_);
505 presMG_.build(*t_, h0_,
openFn,
true);
506 presMG_.setRemoveMean(
true);
526 if (useStaircaseMG_) {
527 std::vector<double>
kap(
static_cast<std::size_t
>(n));
528 std::vector<char>
fl(
static_cast<std::size_t
>(n)),
cu(
static_cast<std::size_t
>(n));
530 kap[
static_cast<std::size_t
>(
i)] = mom_.kappa(
i);
531 fl[
static_cast<std::size_t
>(
i)] = mom_.isFluid(
i) ? 1 : 0;
532 cu[
static_cast<std::size_t
>(
i)] = mom_.isCut(
i) ? 1 : 0;
534 velMG_.build(*t_, h0_, rho_ / dt_, mu_, momOp_,
kap,
fl,
cu, mgMinCoarse_);
535 velMG_.setGaussSeidel(momGS_);
539 auto A = mom_.assembleOperator();
540 momMG_.build(*t_, A.diag, A.start, A.nbr, A.coef);
541 momMG_.setGaussSeidel(momGS_);
544 std::vector<char>
fluidVec(
static_cast<std::size_t
>(n));
546 fluidVec[
static_cast<std::size_t
>(
i)] = mom_.isFluid(
i) ? 1 : 0;
548 std::vector<double>
rs(
static_cast<std::size_t
>(n));
550 rs[
static_cast<std::size_t
>(
i)] = mom_.rhsScale(
i);
552 fluid_ = geom_.
fluid;
555 for (
int c = 0;
c < 3; ++
c) {
558 Kokkos::deep_copy(u_[
c], 0.0);
561 phi_ =
View<double>(
"df_phi",
static_cast<std::size_t
>(n));
562 div_ =
View<double>(
"df_div",
static_cast<std::size_t
>(n));
564 geom_.
nbr.extent(0));
565 faceFieldBuilt_ =
false;
566 bmom_ =
View<double>(
"df_bmom",
static_cast<std::size_t
>(n));
567 Kokkos::deep_copy(p_, 0.0);
573 for (
int c = 0;
c < 3; ++
c) {
574 defc_[
c] =
View<double>(
"df_defc",
static_cast<std::size_t
>(n));
575 Kokkos::deep_copy(defc_[
c], 0.0);
579 uprev_[
c] =
View<double>(
"df_uprev",
static_cast<std::size_t
>(n));
585 advDiag_ =
View<double>(
"df_advdiag",
static_cast<std::size_t
>(n));
592 momSolver_.setJacobi(2, 0.7);
597 momSolver_.setPreconditioner(
600 momSolver_.setPreconditioner(
603 pcg_.setVcycle(2, 2, 60, 0.8);
604 pcg_.setSingular(
true);
613 const double idiag = rho_ / dt_;
619 for (
int c = 0;
c < 3; ++
c)
634 momOp_.
hasAdv = implicitFou_;
643 ufv, faceFieldBuilt_);
644 for (
int c = 0;
c < 3; ++
c) {
656 if (momMGon_ && useStaircaseMG_)
657 velMG_.setFineOp(momOp_);
662 for (
int c = 0;
c < 3; ++
c) {
670 (momMGSolver_ ? momSolver_.solveDefectCorrection(
679 if (outerIters_ > 1) {
680 lastOuterIters_ =
outer + 1;
683 for (
int c = 0;
c < 3; ++
c)
686 if (
dmax < outerTol_)
689 for (
int c = 0;
c < 3; ++
c)
701 Kokkos::deep_copy(phi_, 0.0);
709 if (presPCG_ && !advect_) {
712 Kokkos::deep_copy(presMG_.b(0), div_);
713 Kokkos::deep_copy(presMG_.x(0), 0.0);
715 presMG_.vcycle(2, 2, 60, 0.8);
716 Kokkos::deep_copy(phi_, presMG_.x(0));
722 faceFieldBuilt_ =
true;
724 for (
int c = 0;
c < 3; ++
c)
737 std::vector<double>
h(
static_cast<std::size_t
>(n_));
738 auto m = Kokkos::create_mirror_view(
s);
739 Kokkos::deep_copy(m,
s);
741 h[
static_cast<std::size_t
>(
i)] = m(
i);
749 Kokkos::deep_copy(
mg.b(0),
r);
750 Kokkos::deep_copy(
mg.x(0), 0.0);
751 mg.vcycle(mgVcPre_, mgVcPre_, mgVcBottom_, 0.7);
752 Kokkos::deep_copy(
z,
mg.x(0));
757 Kokkos::parallel_reduce(
758 "amr::flow_maxdiff", n,
760 double d =
a(
i) - b(
i);
766 Kokkos::Max<double>(m));
771 auto m = Kokkos::create_mirror_view(d);
772 Kokkos::deep_copy(m, d);
774 h[
static_cast<std::size_t
>(
i)] = m(
i);
778 auto m = Kokkos::create_mirror_view(u_[
c]);
780 m(
i) =
h[
static_cast<std::size_t
>(
i)];
781 Kokkos::deep_copy(u_[
c], m);
794 View<double> packed(Kokkos::view_alloc(
"amr::vel_packed", Kokkos::WithoutInitializing),
795 static_cast<std::size_t
>(n_) * 3);
796 for (
int c = 0;
c < 3; ++
c) {
800 Kokkos::parallel_for(
829 template <
class SdfFn>
835 for (
int d = 0; d < 3; ++d) {
841 double gmag = std::sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2]);
844 int t1 = (axis + 1) % 3,
t2 = (axis + 2) % 3;
845 double denom = (std::fabs(g[t1]) + std::fabs(g[
t2])) /
gmag * h0_;
852 const Octree* t_ =
nullptr;
855 double rho_ = 1.0, mu_ = 1.0, dt_ = 1
e6;
857 bool presPCG_ =
true;
858 bool momMGon_ =
true;
859 bool useStaircaseMG_ =
false;
860 int mgVcPre_ = 2, mgVcBottom_ = 30;
861 Index mgMinCoarse_ = 256;
866 double outerTol_ = 1
e-6;
867 double momTol_ = 1
e-8;
868 bool advect_ =
false;
869 bool implicitFou_ =
true;
872 int lastMomIters_ = 0, lastPresIters_ = 0, lastOuterIters_ = 1;
875 AmrPoisson<3, Bits> pres_;
882 std::array<View<double>, 3> defc_;
887 std::array<View<double>, 3> u_, gx_;
888 std::array<View<double>, 3> u0_,
892 bool faceFieldBuilt_ =
void setMomentumGS(bool on)
Opt-in: use the multicolour Gauss–Seidel smoother in the momentum MG (Galerkin or staircase) instead ...
void setViscosity(double mu)
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.
std::vector< double > faceField() const
Copy the divergence-free face field to host (one value per CSR (sub)face, forEachFaceFull order).
void setVelocityMGStaircase(bool on)
Choose the momentum-MG coarse-operator strategy: false (default) = Galerkin (MomentumMG,...
void setAdvectionScheme(int s)
High-order advection scheme: 0 = second-order upwind (SOU, default), 1 = Koren TVD.
std::vector< double > pressure() const
Copy the pressure field back to host (single D2H), (num_leaves,) — the incremental-rotational p.
void copyToHost(const View< double > &d, std::vector< double > &h) const
Copy a device View into a host vector (sized n_).
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).
BlockOctree< 3, Bits > Octree
void setVelocity(int c, const std::vector< double > &h)
Set a velocity component from host (testing / initial conditions).
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...
void setBodyForce(double fx, double fy, double fz)
double divNormFace()
L2 norm of the divergence of the ABC face field uf_ (built each project()): the φ-solve residual,...
void setMomentumTol(double tol)
Relative tolerance for the per-step momentum BiCGStab solve (default 1e-8).
bool isFluid(Index i) const
Per-leaf fluid mask (false inside the solid) — for host-side post-processing / bindings.
std::vector< double > debugSou(int comp)
DEBUG: the raw high-order advection ∇·(u u_comp) per cell from the current velocity (== host oracle::...
void setVelocityMGMinCoarse(Index m)
Pore-scale cap for the staircase velocity-MG: the coarsest level keeps ≥ this many cells,...
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).
int lastMomIters() const
Total momentum BiCGStab iterations (summed over the 3 components) of the last step.
double divNormL2()
L2 norm of the (openness-weighted) divergence of the current velocity.
void setMomentumMGSolver(bool on)
Opt-in (P4): solve the momentum predictor with the velocity multigrid used as the solver — MG-precond...
void setDensity(double rho)
void project(int presIters=60)
Pressure projection of the current velocity in place.
void setOuterIterations(int n, double tol=1e-6)
Optional Picard outer loop over the lagged advection (mirror of flow's outerIters_): each outer itera...
void setImplicitAdvection(bool on)
Implicit-FOU deferred correction (default ON).
void init(const Octree &t, Real h0, Vec< 3 > origin=Vec< 3 >{})
int lastPresIters() const
Pressure PCG iterations of the last step.
void setSolid(SdfFn &&sdfFn)
Build the cut-cell operators (host) + upload all device structures.
void step(int momIters=100, int presIters=60)
One incompressible step on device (Stokes, or Navier–Stokes with setAdvection).
void setPressurePCG(bool on)
Use MG-preconditioned CG for the pressure solve (default) vs plain V-cycles.
int lastOuterIters() const
Picard outer iterations actually run in the last step (1 unless setOuterIterations(>1)).
void setAdvection(bool on)
Enable momentum advection ∇·(u u) (default OFF ⇒ Stokes).
void init(const Octree &t, Real h0)
void setOrigin(const Vec< Dim > &o)
void buildOpenness(OpenFn &&openFn)
Build face openness from a geometry callable openFn(faceCentreWorld, axis) -> [0,1] (1 = fully fluid,...
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.
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).
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)
Deferred-correction advection term for component comp: defc = ρ·SOU − ρ·FOU (UNSCALED; the predictor ...
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)
Fully-explicit high-order advection for component comp: defc = ρ·SOU (no implicit FOU; the setImplici...
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 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...
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 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,...
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 ...
double dotPlain(View< const double > a, View< const double > b, Index n)
Plain (unweighted) dot product.
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),...
MORTON_HD 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).
View< T > toDevice(const std::vector< T > &h, const std::string &label)
Upload a host std::vector into a freshly-sized device View (empty vector => empty view).
Kokkos::View< T *, MemSpace > View
1D device array.
double Real
Default host floating type. Device kernels may use float; conversions happen at the boundary.
std::vector< std::remove_const_t< typename V::value_type > > toVector(const V &view)
Copy a (host- or device-resident) Kokkos View of any rank into a contiguous host std::vector,...
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
void upload(const Host &t)
(Re)upload the host octree's current leaf set to the device.
View< Index > upupI
upstream-of-i probe (periodicNeighbor(i,axis,−dir)) — SOU, size nFaces
View< double > dist
face-normal distance (physical) per face, size nFaces
View< Index > upupJ
upstream-of-j probe (periodicNeighbor(j,axis,+dir)) — SOU, size nFaces
View< double > rawArea
raw face area (physical, no openness) per face — advection flux
View< int > axis
face axis 0/1/2, size nFaces
View< char > fluid
per-cell fluid flag, size n
View< double > alphaArea
α·area (physical) per face, size nFaces
View< Index > start
CSR row offsets, size n+1.
View< double > invVol
1/V_i per cell, size n
View< double > alpha
openness per face (gradient gate), size nFaces
View< Index > nbr
neighbour leaf per face, size nFaces
View< int > dir
face direction +1/-1, size nFaces
View< Index > advStart
face-geom CSR row offsets, size n+1
View< Index > advNbr
face-geom neighbour per face, size nFaces
View< double > advDiag
per-cell outflow (diagonal) advection weight, size n
View< double > advCoef
per-face inflow advection coefficient (0 on outflow/solid faces)