flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
phase_change.hpp
Go to the documentation of this file.
1
48#ifndef PECLET_FLOW_VOF_PHASE_CHANGE_HPP
49#define PECLET_FLOW_VOF_PHASE_CHANGE_HPP
50
51#include <Kokkos_Core.hpp>
52#include <Kokkos_MathematicalFunctions.hpp>
53
54#include "vof/plic.hpp"
55
56namespace peclet::flow::vof {
57
58// ---------------------------------------------------------------------------------------------
59// (1) the PLIC polygon area
60// ---------------------------------------------------------------------------------------------
61
88KOKKOS_INLINE_FUNCTION double plicArea(double mx, double my, double mz, double alpha) {
89 const double ax = Kokkos::fabs(mx), ay = Kokkos::fabs(my), az = Kokkos::fabs(mz);
90 const double s = ax + ay + az;
91 if (!(s > 0.0))
92 return 0.0; // degenerate normal: no polygon
93
94 double beta = alpha; // mirror into the positive octant (plic.hpp `plicVolume` convention)
95 if (mx < 0.0)
96 beta += ax;
97 if (my < 0.0)
98 beta += ay;
99 if (mz < 0.0)
100 beta += az;
101 const double a1 = beta / s;
102 if (!(a1 > 0.0) || !(a1 < 1.0))
103 return 0.0; // the plane misses the cell
104
105 const double n1 = Kokkos::fmin(Kokkos::fmin(ax, ay), az) / s;
106 const double n3 = Kokkos::fmax(Kokkos::fmax(ax, ay), az) / s;
107 const double n2 = Kokkos::fmax(1.0 - n1 - n3, 0.0);
108 const double nrm = Kokkos::sqrt(n1 * n1 + n2 * n2 + n3 * n3); // |m|_2 in the normalized frame
109 const double w = Kokkos::fmin(a1, 1.0 - a1);
110
111 if (!(n2 > 0.0))
112 return nrm; // axis-aligned plane (n3 = 1): the polygon is a whole cell face, V'(w) = 1
113
114 if (!(n1 > 0.0)) { // 2-D: a rectangle. V'(w) = (w > n2 ? n2 : w)/(n2 n3); <w-n3> == 0 for w<=1/2
115 return nrm * ((w > n2) ? n2 : w) / (n2 * n3);
116 }
117
118 // full 3-D, cancellation-free (see the note above)
119 const double A1 = (w > n1) ? n1 * (2.0 * w - n1) : w * w;
120 const double u2 = w - n2, u3 = w - n3;
121 const double A2 = (u2 > n1) ? n1 * (n1 - 2.0 * u2) : ((u2 > 0.0) ? -u2 * u2 : 0.0);
122 const double A3 = (u3 > n1) ? n1 * (n1 - 2.0 * u3) : ((u3 > 0.0) ? -u3 * u3 : 0.0);
123 return nrm * (A1 + A2 + A3) / (2.0 * n1 * n2 * n3);
124}
125
137KOKKOS_INLINE_FUNCTION double plicAreaMetric(double mx, double my, double mz, double alpha,
138 const VofMetric& g) {
139 const double a = plicArea(mx, my, mz, alpha);
140 if (!(a > 0.0))
141 return a;
142 const double m[3] = {mx, my, mz};
143 double n[3];
144 const double sh = vofPhysNormal(m, g, n);
145 return a * g.det() * sh;
146}
147
148// ---------------------------------------------------------------------------------------------
149// (2) plane geometry in the cell frame
150// ---------------------------------------------------------------------------------------------
151
154KOKKOS_INLINE_FUNCTION double pcUnitNormal(double mx, double my, double mz, double n[3]) {
155 const double q = Kokkos::sqrt(mx * mx + my * my + mz * mz);
156 if (q > 0.0) {
157 n[0] = mx / q;
158 n[1] = my / q;
159 n[2] = mz / q;
160 }
161 return q;
162}
163
167KOKKOS_INLINE_FUNCTION double pcCentreDistance(double mx, double my, double mz, double alpha) {
168 const double q = Kokkos::sqrt(mx * mx + my * my + mz * mz);
169 if (!(q > 0.0))
170 return 0.0;
171 return (0.5 * (mx + my + mz) - alpha) / q;
172}
173
176KOKKOS_INLINE_FUNCTION double pcOffsetDistance(double phic, const double n[3], double dx, double dy,
177 double dz) {
178 return phic + dx * n[0] + dy * n[1] + dz * n[2];
179}
180
181// ---------------------------------------------------------------------------------------------
182// (3) the one-sided, pure-cell weighted least-squares normal gradient (Malan et al. 2021)
183// ---------------------------------------------------------------------------------------------
184
189struct PcGradFit {
190 double num = 0.0;
191 double den = 0.0;
192 double num2 = 0.0;
193 double s3 = 0.0;
194 double s4 = 0.0;
195 int n = 0;
196};
197
201KOKKOS_INLINE_FUNCTION double pcGradWeight(double dx, double dy, double dz, const double n[3]) {
202 const double d2 = dx * dx + dy * dy + dz * dz;
203 if (!(d2 > 0.0))
204 return 0.0;
205 const double dn = dx * n[0] + dy * n[1] + dz * n[2];
206 return (dn * dn) / (d2 * Kokkos::sqrt(d2));
207}
208
210KOKKOS_INLINE_FUNCTION void pcGradAdd(PcGradFit& f, double w, double phi, double T, double Tg) {
211 const double dT = T - Tg, p2 = phi * phi;
212 f.num += w * phi * dT;
213 f.den += w * p2;
214 f.num2 += w * p2 * dT;
215 f.s3 += w * p2 * phi;
216 f.s4 += w * p2 * p2;
217 ++f.n;
218}
219
222 return (f.den > 0.0) ? (f.num / f.den) : 0.0;
223}
224
245KOKKOS_INLINE_FUNCTION double pcCurvedDistance(double phi, double dx, double dy, double dz,
246 const double n[3], double kappa) {
247 const double dn = dx * n[0] + dy * n[1] + dz * n[2];
248 const double rho2 = Kokkos::fmax(dx * dx + dy * dy + dz * dz - dn * dn, 0.0);
249 return phi + 0.25 * kappa * rho2;
250}
251
267 const double det = f.den * f.s4 - f.s3 * f.s3;
268 if (f.n < 2 || !(Kokkos::fabs(det) > 1e-12 * f.den * f.s4))
269 return pcGradSolve(f);
270 return (f.num * f.s4 - f.num2 * f.s3) / det;
271}
272
273// ---------------------------------------------------------------------------------------------
274// (4) the physics rules
275// ---------------------------------------------------------------------------------------------
276
279KOKKOS_INLINE_FUNCTION double pcMassFlux(double kg, double gradG, double kl, double gradL,
280 double hlv) {
281 return (kg * gradG - kl * gradL) / hlv;
282}
283
286KOKKOS_INLINE_FUNCTION double pcRegressVolume(double mdot, double area, double dt, double rhoL) {
287 return mdot * area * dt / rhoL;
288}
289
292KOKKOS_INLINE_FUNCTION double pcDivSource(double mdot, double area, double rhoG, double rhoL) {
293 return mdot * area * (1.0 / rhoG - 1.0 / rhoL);
294}
295
299 return Tsat + mdot * Rint;
300}
301
336KOKKOS_INLINE_FUNCTION double pcCarriedValue(double Tg, double grad, double phic) {
337 return Tg + grad * phic;
338}
339
356KOKKOS_INLINE_FUNCTION double pcGfmTheta(double phiC, double nd, double s, double thetaMin,
357 double thetaMax) {
358 const double den = Kokkos::fabs(nd);
359 const double num = Kokkos::fabs(phiC - s * nd);
360 if (!(num < thetaMax * den)) // includes den == 0: the limit, reached continuously
361 return thetaMax;
362 return Kokkos::fmax(num / den, thetaMin);
363}
364
375KOKKOS_INLINE_FUNCTION double pcFaceConductivity(double ki, double kj, bool mi, bool mj) {
376 if (mi && !mj)
377 return kj;
378 if (mj && !mi)
379 return ki;
380 return 0.5 * (ki + kj);
381}
382
385KOKKOS_INLINE_FUNCTION double pcPhaseMix(double vg, double vl, double c) {
386 return vg * (1.0 - c) + vl * c;
387}
388
404KOKKOS_INLINE_FUNCTION bool pcPushWeights(const double n[3], double sgn, const bool avail[3],
405 int step[3], double w[3]) {
406 double tot = 0.0, tota = 0.0;
407 for (int d = 0; d < 3; ++d) {
408 const double p = sgn * n[d];
409 w[d] = p * p;
410 step[d] = (p > 0.0) ? 1 : ((p < 0.0) ? -1 : 0);
411 tot += w[d];
412 if (avail[d])
413 tota += w[d];
414 }
415 const bool ok = tota > 0.0;
416 for (int d = 0; d < 3; ++d) {
417 if (ok)
418 w[d] = avail[d] ? w[d] / tota : 0.0;
419 else
420 w[d] = (tot > 0.0) ? w[d] / tot : 0.0;
421 }
422 return ok;
423}
424
425// ---------------------------------------------------------------------------------------------
426// (5) WO-P3g — the SECOND-ORDER interfacial energy operator
427// ---------------------------------------------------------------------------------------------
428
446KOKKOS_INLINE_FUNCTION double pcGfmThetaK(double phiC, double nd, double s, double kappa,
447 double thetaMin, double thetaMax) {
448 if (kappa == 0.0)
449 return pcGfmTheta(phiC, nd, s, thetaMin, thetaMax);
450 const double den = Kokkos::fabs(nd);
451 const double num = Kokkos::fabs(phiC - s * nd + 0.25 * kappa * (1.0 - nd * nd));
452 if (!(num < thetaMax * den))
453 return thetaMax;
454 return Kokkos::fmax(num / den, thetaMin);
455}
456
476KOKKOS_INLINE_FUNCTION double pcGfmThetaKAniso(double phiC, const double n[3], int d, double s,
477 double kappa, double thetaMin, double thetaMax,
478 const VofMetric& g) {
479 const double hd = g.h[d];
480 const double nd = n[d];
481 const double den = Kokkos::fabs(nd) * hd;
482 double num = Kokkos::fabs(phiC - s * hd * nd);
483 if (kappa != 0.0)
484 num = Kokkos::fabs(phiC - s * hd * nd + 0.25 * kappa * hd * hd * (1.0 - nd * nd));
485 if (!(num < thetaMax * den)) // includes den == 0: the limit, reached continuously
486 return thetaMax;
487 return Kokkos::fmax(num / den, thetaMin);
488}
489
521struct PcGfmRow {
522 double aGamma;
523 double aBehind;
524};
525KOKKOS_INLINE_FUNCTION PcGfmRow pcGfmRow(double theta, bool behind, int order) {
526 if (order < 2 || !behind)
527 return PcGfmRow{1.0 / theta, 1.0};
528 return PcGfmRow{2.0 / ((1.0 + theta) * theta), 2.0 / (1.0 + theta)};
529}
530
546KOKKOS_INLINE_FUNCTION double pcOperatorMassFlux(double q, double coefSum, double area, double hlv,
547 double Rint) {
548 const double den = hlv * area + Rint * coefSum;
549 return (den > 0.0) ? q / den : 0.0;
550}
551
555KOKKOS_INLINE_FUNCTION bool pcIsInterfacial(double c, double eps) {
556 return c > eps && c < 1.0 - eps;
557}
558
559} // namespace peclet::flow::vof
560
561#endif // PECLET_FLOW_VOF_PHASE_CHANGE_HPP
double pcGradSolve2(const PcGradFit &f)
The QUADRATIC one-sided fit (WO-P23): T - T_G = G phi + Q phi^2, returning G.
double pcFaceConductivity(double ki, double kj, bool mi, bool mj)
Face conductivity of the variable-coefficient energy operator, k(C) arithmetic in the colour with ONE...
double pcMassFlux(double kg, double gradG, double kl, double gradL, double hlv)
mdot = (k_g dT_g/dn - k_l dT_l/dn)/h_lv with n the PLIC normal (liquid -> gas).
void pcGradAdd(PcGradFit &f, double w, double phi, double T, double Tg)
Add one pure-phase sample at normal distance phi with temperature T.
double pcCentreDistance(double mx, double my, double mz, double alpha)
Signed distance (in cells) from the CELL CENTRE to the PLIC plane, measured along n and POSITIVE ON T...
double pcRegressVolume(double mdot, double area, double dt, double rhoL)
Liquid volume (fraction of a cell) removed from an interfacial cell in one step: dV = mdot A_G dt / r...
bool pcPushWeights(const double n[3], double sgn, const bool avail[3], int step[3], double w[3])
The clip-and-redistribute allocation: which face neighbours a clipped cell's residue is pushed into,...
double pcGfmTheta(double phiC, double nd, double s, double thetaMin, double thetaMax)
thetaMin keeps the coefficient finite when the plane grazes the cell centre (the standard GFM clamp);...
double pcDivSource(double mdot, double area, double rhoG, double rhoL)
The volumetric divergence source of one interfacial cell (1/s, cell volume = 1): S = mdot A_G (1/rho_...
PcGfmRow pcGfmRow(double theta, bool behind, int order)
double pcCurvedDistance(double phi, double dx, double dy, double dz, const double n[3], double kappa)
WO-P3f — the sample's distance to the CURVED interface, not to the tangent plane.
double pcCarriedValue(double Tg, double grad, double phic)
The plane-anchored (ghost-fluid) interfacial Dirichlet condition (WO-P23, the first item).
bool pcIsInterfacial(double c, double eps)
Is this a cell the regression acts on? The PLIC contract of advect_wy.hpp (wyIsMixed) with an explici...
double pcInterfaceTemperature(double Tsat, double mdot, double Rint)
Interfacial temperature under the Schrage-derived interfacial heat-transfer resistance (Bureš & Sato)...
double pcOffsetDistance(double phic, const double n[3], double dx, double dy, double dz)
Signed distance from the plane of the neighbour at INTEGER cell offset d, given the centre distance p...
double pcGfmThetaKAniso(double phiC, const double n[3], int d, double s, double kappa, double thetaMin, double thetaMax, const VofMetric &g)
The same on an ANISOTROPIC cell (Phase 3, flow/doc/anisotropic_vof.md §7, V5.3).
double pcOperatorMassFlux(double q, double coefSum, double area, double hlv, double Rint)
WO-P3g item 1 — mdot from the ENERGY OPERATOR's own interfacial flux.
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 ...
double plicArea(double mx, double my, double mz, double alpha)
Area of the PLIC polygon {x in [0,1]^3 : m.x = alpha}, as a multiple of h^2.
double pcGfmThetaK(double phiC, double nd, double s, double kappa, double thetaMin, double thetaMax)
WO-P3g item 3 — the curvature-consistent GFM distance.
double plicAreaMetric(double mx, double my, double mz, double alpha, const VofMetric &g)
The PHYSICAL area of the PLIC polygon of an ANISOTROPIC cell, in hRef^2 (flow/doc/anisotropic_vof....
double pcGradSolve(const PcGradFit &f)
The fitted normal derivative dT/dn (0 if the side carried no usable sample).
double pcUnitNormal(double mx, double my, double mz, double n[3])
Unit (L2) interface normal from a PLIC normal of any scale.
double pcGradWeight(double dx, double dy, double dz, const double n[3])
Malan's collinearity weight for a sample at cell offset d: w = xi / |d| with xi = (d....
flow — thin shim: the container-free VoF PLIC geometric toolbox (rung V0) now live in core.
WO-P3g item 2 — the Gibou–Fedkiw second-order one-sided (ghost-fluid) row.
double aBehind
coefficient of (T_behind - T_i), times that face's own k_f o_f
double aGamma
coefficient of (T_Gamma - T_i), times k_f o_f
Accumulator for dT/dn fitted through the interface value: T - T_G = G * phi, so G = sum_j w_j phi_j (...
double num
b1 = sum w phi (T - T_G)
double num2
b2 = sum w phi^2 (T - T_G) (quadratic fit only)
double s4
S4 = sum w phi^4 (quadratic fit only)
double den
S2 = sum w phi^2.
double s3
S3 = sum w phi^3 (quadratic fit only)