flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
energy_advect.hpp
Go to the documentation of this file.
1
45#ifndef PECLET_FLOW_VOF_ENERGY_ADVECT_HPP
46#define PECLET_FLOW_VOF_ENERGY_ADVECT_HPP
47
48#include <functional>
49#include <Kokkos_Core.hpp>
50#include <stdexcept>
51
52#include "policy.hpp"
53#include "vof/advect_wy.hpp"
54#include "vof/momentum_advect.hpp" // vofMinmod
55#include "vof/phase_change.hpp"
56
57namespace peclet::flow::vof {
58
60 public:
64 void init(const WyAdvector& w, double rcpG, double rcpL) {
65 if (!(rcpG > 0.0) || !(rcpL > 0.0))
66 throw std::invalid_argument(
67 "peclet::flow::vof::VofEnergyAdvector: both phase rho*c_p must be > 0");
68 n_ = w.inner();
69 e_ = w.extent();
70 g_ = w.ghost();
71 len_ = static_cast<long>(e_.x) * e_.y * e_.z;
72 rcpG_ = rcpG;
73 rcpL_ = rcpL;
74 T_ = SField("vof::energyT", len_);
75 Tw_ = SField("vof::energyTw", len_);
76 }
77 bool initialized() const { return len_ > 0; }
78 void setPhaseRcp(double g, double l) {
79 rcpG_ = g;
80 rcpL_ = l;
81 }
82 double phaseRcpG() const { return rcpG_; }
83 double phaseRcpL() const { return rcpL_; }
85 SField temperature() const { return T_; }
86
103 bool energyMuscl = false;
104
108 std::function<void(SField)> exchange;
109
112 void advect(WyAdvector& w, double dt, long step) {
113 if (!initialized())
114 throw std::runtime_error("peclet::flow::vof::VofEnergyAdvector: init() was never called");
115 if (!exchange)
116 throw std::runtime_error("peclet::flow::vof::VofEnergyAdvector: no `exchange` hook");
117 if (w.hasGeometry())
118 throw std::runtime_error(
119 "peclet::flow::vof::VofEnergyAdvector: cut-cell geometry is not composed with the "
120 "consistent energy transport at this rung (enable_phase_change already refuses a solid)");
121 w.requireExchange();
122 w.checkCourant(dt);
123 const double dth = dt / w.h();
124 w.freezeDilationFlag();
125 w.timedExchange([&] { exchange(T_); });
126 const int* perm = kWySweepPerm[static_cast<int>(step % 6)];
127 for (int s = 0; s < 3; ++s) {
128 const int d = perm[s];
129 w.reconstruct();
130 w.computeFluxes(d, dth);
131 w.applySweep(d, dth); // C^{(s+1)}: the capacity the recovery divides by
132 energyUpdate(w, d, dth); // T from the pre-sweep T, the fluxes, and that new C (swaps T_)
133 w.exchangeTimed(w.colour());
134 w.timedExchange([&] { exchange(T_); }); // regenerates every ghost of the swapped-in buffer
135 }
136 }
137
139 void extrema(double& lo, double& hi) const {
140 const I3 e = e_, n = n_;
141 const int g = g_;
142 SField T = T_;
143 double mn = 0.0, mx = 0.0;
144 using MD = MDRange3<SExec>;
145 MD pol(SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z});
146 Kokkos::parallel_reduce(
147 "vof::energy::min", pol,
148 KOKKOS_LAMBDA(int x, int y, int z, double& a) { a = Kokkos::fmin(a, T(L3(x, y, z, e))); },
149 Kokkos::Min<double>(mn));
150 Kokkos::parallel_reduce(
151 "vof::energy::max", pol,
152 KOKKOS_LAMBDA(int x, int y, int z, double& a) { a = Kokkos::fmax(a, T(L3(x, y, z, e))); },
153 Kokkos::Max<double>(mx));
154 Kokkos::fence();
155 lo = mn;
156 hi = mx;
157 }
158
159 // nvcc requires members holding extended device lambdas to be public.
160
171 void energyUpdate(WyAdvector& w, int d, double dth) {
172 const I3 e = e_, n = n_;
173 const int g = g_;
174 const long sd =
175 d == 0 ? 1 : (d == 1 ? static_cast<long>(e_.x) : static_cast<long>(e_.x) * e_.y);
176 // The update is OUT OF PLACE: every cell reads its two upwind neighbours' temperatures, so
177 // writing `T` in place is a read-write race between threads (measured: it moved the P2
178 // interface position by 0.6 % and turned an order-2.8 ladder into order 0.5). `Tw_` takes the
179 // new values and the two handles are swapped; `exchange` then regenerates every ghost.
180 SField T = T_, Tw = Tw_, fl = w.faceFlux(), u = w.faceVel(d), c = w.colour();
181 const double rg = rcpG_, rl = rcpL_;
182 const double floor = 1e-12 * (rg < rl ? rg : rl);
183 const bool muscl = energyMuscl;
184 Kokkos::parallel_for(
185 "vof::energy::update", MDRange3<SExec>(SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z}),
186 KOKKOS_LAMBDA(int x, int y, int z) {
187 const long i = L3(x, y, z, e);
188 const double aP = u(i) * dth, aM = u(i - sd) * dth;
189 const double FP = fl(i), FM = fl(i - sd);
190 const double phiP = rg * (aP - FP) + rl * FP;
191 const double phiM = rg * (aM - FM) + rl * FM;
192 const double ti = T(i);
193 double tP, tM;
194 if (!muscl) {
195 tP = (aP > 0.0) ? ti : T(i + sd);
196 tM = (aM > 0.0) ? T(i - sd) : ti;
197 } else {
198 // the flux slab of a face occupies the fraction |a| of the DONOR next to that face, so
199 // its centroid sits (1/2 - |a|/2) from the donor centre TOWARDS the face
200 const double wP = 0.5 - 0.5 * Kokkos::fabs(aP), wM = 0.5 - 0.5 * Kokkos::fabs(aM);
201 if (aP > 0.0)
202 tP = ti + wP * vofMinmod(ti - T(i - sd), T(i + sd) - ti);
203 else
204 tP = T(i + sd) - wP * vofMinmod(T(i + sd) - ti, T(i + 2 * sd) - T(i + sd));
205 if (aM > 0.0)
206 tM = T(i - sd) + wM * vofMinmod(T(i - sd) - T(i - 2 * sd), ti - T(i - sd));
207 else
208 tM = ti - wM * vofMinmod(ti - T(i - sd), T(i + sd) - ti);
209 }
210 const double cl = Kokkos::fmin(Kokkos::fmax(c(i), 0.0), 1.0); // C AFTER this sweep
211 const double r = pcPhaseMix(rg, rl, cl);
212 Tw(i) = ti + (phiM * (tM - ti) - phiP * (tP - ti)) / (r > floor ? r : floor);
213 });
214 Kokkos::fence();
215 SField tmp = T_;
216 T_ = Tw_;
217 Tw_ = tmp;
218 }
219
220 private:
221 I3 n_{0, 0, 0}, e_{0, 0, 0};
222 int g_ = 0;
223 long len_ = 0;
224 double rcpG_ = 1.0, rcpL_ = 1.0;
225 SField T_, Tw_;
226};
227
228} // namespace peclet::flow::vof
229
230#endif // PECLET_FLOW_VOF_ENERGY_ADVECT_HPP
flow — VoF rung V1: Weymouth & Yue (2010) directionally-split, exactly volume-conserving geometric ad...
void setPhaseRcp(double g, double l)
void extrema(double &lo, double &hi) const
Min/max of the transported temperature over the inner region (a boundedness read-out).
SField temperature() const
The transported temperature on the advector's block (inner cells mirror the solver's scalar).
std::function< void(SField)> exchange
Ghost policy for the temperature — the halo/periodic exchange plus the block's non-periodic zero-grad...
void advect(WyAdvector &w, double dt, long step)
One consistent colour + energy Weymouth-Yue step.
void init(const WyAdvector &w, double rcpG, double rcpL)
bool energyMuscl
MinMod-limited donor reconstruction of the face temperature, instead of the DEFAULT plain donor-cell ...
void energyUpdate(WyAdvector &w, int d, double dth)
The DEVIATION form of the update, and it is not cosmetic.
Weymouth-Yue split advection of a colour field on an extended (inner + ghost) block.
flow — VoF rung V2b (WO-K): momentum-consistent transport of rho^c u_c on the half-shifted (MAC) mome...
constexpr int kWySweepPerm[6][3]
The six permutations of (x, y, z), cycled by step index so no direction is systematically favoured (p...
double vofMinmod(double a, double b)
double pcPhaseMix(double vg, double vl, double c)
Linear-in-C phase property (k, rho c_p), written so that C = 0 and C = 1 return the endpoint EXACTLY ...
long L3(int x, int y, int z, I3 e)
Kokkos::DefaultExecutionSpace SExec
Kokkos::View< double *, SMem > SField
flow — VoF phase change (Part II, rungs P0/P1): the container-free kernel set.
flow — the multidimensional iteration policies: iteration order follows storage.
static double perm(const std::vector< double > &gsdf, const BlockDecomposer< 3 > &dec, int rank, bool amg, int levels, MPI_Comm comm)