core 1.5.0
Shared MPI block decomposition, asynchronous ghost-layer exchange, SDF geometry, load balancing (header-only C++20)
Loading...
Searching...
No Matches
wetting.hpp
Go to the documentation of this file.
1
113#ifndef PECLET_CORE_VOF_WETTING_HPP
114#define PECLET_CORE_VOF_WETTING_HPP
115
116#include <Kokkos_Core.hpp>
117#include <Kokkos_MathematicalFunctions.hpp>
118
119#include "peclet/core/vof/curvature.hpp" // plicPolygon + polygonAreaCentroid (the PLIC centroid)
121
122namespace peclet::core::vof {
123
132
146
166KOKKOS_INLINE_FUNCTION bool youngsNormalFluidOnly(const double c[27], const unsigned char fluid[27],
167 double m[3]) {
168 for (int d = 0; d < 3; ++d) {
169 double s[3] = {0.0, 0.0, 0.0}, w[3] = {0.0, 0.0, 0.0}; // planes at offset -1, 0, +1 in d
170 for (int q = 0; q < 27; ++q) {
171 if (!fluid[q])
172 continue;
173 const int o[3] = {q % 3 - 1, (q / 3) % 3 - 1, q / 9 - 1};
174 const int t1 = o[(d + 1) % 3], t2 = o[(d + 2) % 3];
175 const double ww = (2.0 - (t1 < 0 ? -t1 : t1)) * (2.0 - (t2 < 0 ? -t2 : t2));
176 const int p = o[d] + 1;
177 s[p] += ww * c[q];
178 w[p] += ww;
179 }
180 if (w[0] > 0.0 && w[2] > 0.0)
181 m[d] = s[0] / w[0] - s[2] / w[2];
182 else if (w[0] > 0.0 && w[1] > 0.0)
183 m[d] = 2.0 * (s[0] / w[0] - s[1] / w[1]);
184 else if (w[2] > 0.0 && w[1] > 0.0)
185 m[d] = 2.0 * (s[1] / w[1] - s[2] / w[2]);
186 else
187 m[d] = 0.0;
188 }
189 return plicNormalizeL1(m) >= 1e-30;
190}
191
195KOKKOS_INLINE_FUNCTION void vofPlicCentroid(const double m[3], double alpha, double p[3]) {
196 double v[8][3];
197 const int nv = plicPolygon(m[0], m[1], m[2], alpha, v);
198 if (nv >= 3) {
199 double area = 0.0;
200 polygonAreaCentroid(v, nv, p, area);
201 if (area > 0.0)
202 return;
203 }
204 const double nn = m[0] * m[0] + m[1] * m[1] + m[2] * m[2];
205 const double t = nn > 0.0 ? (alpha - 0.5 * (m[0] + m[1] + m[2])) / nn : 0.0;
206 p[0] = 0.5 + t * m[0];
207 p[1] = 0.5 + t * m[1];
208 p[2] = 0.5 + t * m[2];
209}
210
226KOKKOS_INLINE_FUNCTION int vofWettingPlane(const double mfIn[3], double cf, const double nwIn[3],
227 double cosT, double sinT, double sdfF, int pivot,
228 double tEps, double mth[3], double& alphaTh,
229 double& cosApp, const VofMetric& g) {
230 // (a) L2-unit frames — PHYSICAL (Phase 3, `flow/doc/anisotropic_vof.md` §6 decision V4.1).
231 //
232 // theta is an angle between two directions, so the rotation below has to happen in physical
233 // space: both inputs arrive as INDEX-space gradients (a colour Youngs gradient and a central
234 // difference of the SDF on the index lattice), and an index normal `m` is the physical direction
235 // `H^-1 m` renormalized. `vofPhysNormal` is that pullback, and at `g.h = {1,1,1}` it divides by
236 // 1.0 and returns `m/|m|` — the same two expressions this function had, bit for bit.
237 //
238 // The plane is handed back to `plicAlpha`/`plicVolume` in INDEX space (`m_idx = H m_theta`),
239 // because those routines live on the unit cube; they renormalize internally, so no L1 pass is
240 // needed and the isotropic path multiplies by 1.0.
241 double mh[3] = {mfIn[0], mfIn[1], mfIn[2]};
242 if (!(vofPhysNormal(mfIn, g, mh) > 0.0)) {
243 mh[0] = mfIn[0];
244 mh[1] = mfIn[1];
245 mh[2] = mfIn[2];
246 }
247 double nw[3] = {nwIn[0], nwIn[1], nwIn[2]};
248 if (!(vofPhysNormal(nwIn, g, nw) > 0.0)) {
249 nw[0] = nwIn[0];
250 nw[1] = nwIn[1];
251 nw[2] = nwIn[2];
252 }
253
254 // (b) rotate to the prescribed angle about the contact line's direction: keep the AZIMUTH of the
255 // interface (its in-wall component, which a fluid-only stencil measures well) and replace the
256 // angle to the wall by the prescribed one.
257 cosApp = mh[0] * nw[0] + mh[1] * nw[1] + mh[2] * nw[2];
258 double t[3] = {mh[0] - cosApp * nw[0], mh[1] - cosApp * nw[1], mh[2] - cosApp * nw[2]};
259 const double tn = Kokkos::sqrt(t[0] * t[0] + t[1] * t[1] + t[2] * t[2]);
260 int branch = kVofWetTheta;
261 if (tn < tEps) {
262 mth[0] = mh[0];
263 mth[1] = mh[1];
264 mth[2] = mh[2];
266 } else {
267 for (int d = 0; d < 3; ++d)
268 mth[d] = cosT * nw[d] + sinT * t[d] / tn;
269 }
270
271 // (c) back to INDEX space (m_idx = H m_theta), then anchor it on the unit cube.
273 if (pivot == kVofPivotVolume) {
274 // The plane of normal m_theta whose liquid volume in the anchor cell is exactly C_f. No pivot
275 // point is needed and the poorly-measured wall-normal part of m_f is never read.
276 alphaTh = plicAlpha(mth[0], mth[1], mth[2], cf);
277 return branch;
278 }
279 // The three ablations all reconstruct the FLUID cell with m_f's own normal first. `mfIn` is
280 // already the index normal the unit-cube reconstruction wants.
281 double mfl[3] = {mfIn[0], mfIn[1], mfIn[2]};
283 double pf[3];
284 vofPlicCentroid(mfl, plicAlpha(mfl[0], mfl[1], mfl[2], cf), pf);
285 double c0[3] = {pf[0], pf[1], pf[2]};
287 // |grad sdf| = 1, so the signed distance at p_f is the cell-centre value plus the normal
288 // displacement. (Cell centre = (0.5,0.5,0.5) in the local frame; lengths are cells.)
289 // `pf` is an index position on the unit cube; the wall normal is physical, so the
290 // displacement is mapped before the dot product (identity at g.h = {1,1,1}).
291 const double dpi[3] = {pf[0] - 0.5, pf[1] - 0.5, pf[2] - 0.5};
292 double dpp[3];
293 g.toPhys(dpi, dpp);
294 const double sp = sdfF + nw[0] * dpp[0] + nw[1] * dpp[1] + nw[2] * dpp[2];
295 // A physical displacement `s * n` is the index displacement `H^-1 (s n)` (identity at
296 // g.h = {1,1,1}: `x / 1.0 == x`).
297 if (pivot == kVofPivotWallNormal) {
298 for (int d = 0; d < 3; ++d)
299 c0[d] = pf[d] - sp * nw[d] / g.h[d];
300 } else if (tn >= tEps) {
301 // Move within the FLUID plane along the in-plane part of n_w until the wall is reached.
302 double q[3] = {nw[0] - cosApp * mh[0], nw[1] - cosApp * mh[1], nw[2] - cosApp * mh[2]};
303 const double qn = Kokkos::sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2]);
304 if (qn > tEps)
305 for (int d = 0; d < 3; ++d)
306 c0[d] = pf[d] - (sp / qn) * (q[d] / qn) / g.h[d];
307 }
308 }
309 alphaTh = mth[0] * c0[0] + mth[1] * c0[1] + mth[2] * c0[2];
310 return branch;
311}
312
314KOKKOS_INLINE_FUNCTION int vofWettingPlane(const double mfIn[3], double cf, const double nwIn[3],
315 double cosT, double sinT, double sdfF, int pivot,
316 double tEps, double mth[3], double& alphaTh,
317 double& cosApp) {
319 VofMetric{});
320}
321
325 const int ds[3]) {
326 const double a = alphaTh - (mth[0] * ds[0] + mth[1] * ds[1] + mth[2] * ds[2]);
327 const double v = plicVolume(mth[0], mth[1], mth[2], a);
328 return v < 0.0 ? 0.0 : (v > 1.0 ? 1.0 : v);
329}
330
331} // namespace peclet::core::vof
332
333#endif // PECLET_CORE_VOF_WETTING_HPP
core — VoF rung V3 (WO-O): interface curvature from volume fractions, as the Popinet (2009) height-fu...
VofWettingBranch
Which rule produced a band cell's colour, for contact_angle_diagnostics().
Definition wetting.hpp:134
@ kVofWetPure
pure-phase continuation (C_f is 0 or 1, no interface in reach)
Definition wetting.hpp:141
@ kVofWetNeutral
fell back to WO-Q's neutral mean (no usable n_w/f/m_f)
Definition wetting.hpp:143
@ kVofWetTheta
the theta-plane of this column's own anchor cell
Definition wetting.hpp:136
@ kVofWetNone
not written by pass 1
Definition wetting.hpp:135
@ kVofWetNeighbour
the mean of the theta-planes of the anchor's MIXED neighbours, used where the anchor itself is pure p...
Definition wetting.hpp:137
@ kVofWetParallel
interface parallel to the wall (|t| < tEps): m_f kept unrotated
Definition wetting.hpp:142
void vofPlicCentroid(const double m[3], double alpha, double p[3])
The centroid of the PLIC polygon of cell f in the cell's own unit-cube frame, for the plane m .
Definition wetting.hpp:195
void vofIndexNormal(const double n[3], const VofMetric &g, double m[3])
The INDEX-space normal of a physical normal n (any scale): m = H n, returned unnormalized (plicAlpha ...
Definition plic.hpp:177
VofWettingPivot
How the theta-plane is anchored in the fluid cell.
Definition wetting.hpp:126
@ kVofPivotInterface
through p_f (Afkhami-Bussmann / Basilisk). Idempotent.
Definition wetting.hpp:128
@ kVofPivotContactLine
the point of the fluid plane on the wall nearest p_f. Idempotent.
Definition wetting.hpp:130
@ kVofPivotWallNormal
WO-S as written: c = p_f - sdf(p_f) n_w. NOT idempotent.
Definition wetting.hpp:129
@ kVofPivotVolume
plicAlpha(m_theta, C_f): volume-consistent. DEFAULT, idempotent.
Definition wetting.hpp:127
double vofPhysNormal(const double m[3], const VofMetric &g, double n[3])
The PHYSICAL unit normal of an index-space normal m (any scale), i.e.
Definition plic.hpp:143
int vofWettingPlane(const double mfIn[3], double cf, const double nwIn[3], double cosT, double sinT, double sdfF, int pivot, double tEps, double mth[3], double &alphaTh, double &cosApp, const VofMetric &g)
Build the theta-plane in the FLUID cell's own unit-cube frame.
Definition wetting.hpp:226
double plicAlpha(double mx, double my, double mz, double v)
Plane offset alpha such that plicVolume(mx,my,mz,alpha) == V, exactly (analytic inverse).
Definition plic.hpp:303
double plicNormalizeL1(double m[3])
L1-normalize a normal in place. Returns the L1 norm before normalization (0 => untouched).
Definition plic.hpp:70
int plicPolygon(double mx, double my, double mz, double alpha, double v[8][3])
Vertices of the polygon {x in [0,1]^3 : m .
double plicVolume(double mx, double my, double mz, double alpha)
Volume fraction of the unit cell [0,1]^3 lying on the fluid side m .
Definition plic.hpp:221
double vofWettingFraction(const double mth[3], double alphaTh, const int ds[3])
The liquid fraction of the solid cell at integer offset ds from the fluid cell, under the theta-plane...
Definition wetting.hpp:324
void polygonAreaCentroid(const double v[8][3], int nv, double ctr[3], double &area)
Area and centroid of a planar polygon in 3-D (fan triangulation from v[0]).
bool youngsNormalFluidOnly(const double c[27], const unsigned char fluid[27], double m[3])
Youngs' 27-point weighted gradient restricted to non-solid cells.
Definition wetting.hpp:166
Kokkos::View< T *, MemSpace > View
1D device array.
Definition view.hpp:26
core — PLIC (piecewise-linear interface construction) geometric toolbox: the analytic plane<->volume ...
The per-axis cell size, in the solver's reference length (hRef), of the cell these kernels map to the...
Definition plic.hpp:108
void toPhys(const double X[3], double out[3]) const
Index displacement -> physical displacement (in hRef): ‘X’_a = h[a] X_a`.
Definition plic.hpp:122