31#include <Kokkos_Core.hpp>
35#include "peclet/core/common/types.hpp"
36#include "peclet/core/decomp/block_decomposer.hpp"
40static constexpr int NX = 64,
NY = 4,
NZ = 4;
46 std::fprintf(stderr, "CHECK failed: %s\n at %s:%d\n", #cond, __FILE__, __LINE__); \
51static std::size_t
gidx(
int x,
int y,
int z) {
52 return (std::size_t)x + (std::size_t)y *
NX + (std::size_t)z *
NX *
NY;
57 std::vector<double> v((std::size_t)
lnx *
lny *
lnz);
58 for (
int z = 0; z <
lnz; ++z)
59 for (
int y = 0; y <
lny; ++y)
60 for (
int x = 0; x <
lnx; ++x)
61 v[(std::size_t)x + (std::size_t)y *
lnx + (std::size_t)z *
lnx *
lny] =
62 f(x + ox, y + oy, z + oz);
65static std::vector<double>
sliceOf(
const std::vector<double>& g,
int ox,
int oy,
int oz,
int lnx,
67 return blockOf([&](
int x,
int y,
int z) {
return g[
gidx(x, y, z)]; }, ox, oy, oz,
lnx,
lny,
lnz);
72 int oy,
int oz,
int lnx,
int lny,
int lnz) {
74 for (
int z = 0; z <
lnz; ++z)
75 for (
int y = 0; y <
lny; ++y)
76 for (
int x = 0; x <
lnx; ++x)
78 m, std::fabs(
local[(std::size_t)x + (std::size_t)y *
lnx + (std::size_t)z *
lnx *
lny] -
86 const double rhs =
St / std::sqrt(
M_PI);
87 double a = 1e-8, b = 5.0;
88 for (
int i = 0; i < 200; ++i) {
89 const double m = 0.5 * (a + b);
90 ((m * std::exp(m * m) * std::erf(m) - rhs) > 0 ? b : a) = m;
100 if (
const char* e = std::getenv(
"PECLET_P3C_AREA"))
113 if (
const char* e = std::getenv(
"PECLET_P3F_CARRY"))
115 if (
const char* e = std::getenv(
"PECLET_P3F_KAPPA"))
120 if (
const char* e = std::getenv(
"PECLET_P3G_ORDER"))
122 if (
const char* e = std::getenv(
"PECLET_P3G_OPMDOT"))
124 if (
const char* e = std::getenv(
"PECLET_P3G_GFMORDER"))
126 if (
const char* e = std::getenv(
"PECLET_P3G_CURVDIST"))
133 Kokkos::initialize(
argc, argv);
135 int rank = 0, size = 1;
138 auto dec = peclet::flow::CutcellMG::decomposition(
static_cast<std::size_t
>(size),
NX,
NY,
NZ);
139 auto blk =
dec.block(rank);
140 const int ox = (int)blk.origin[0], oy = (
int)blk.origin[1], oz = (int)blk.origin[2];
141 const int lnx = (int)blk.size[0],
lny = (
int)blk.size[1],
lnz = (int)blk.size[2];
143 for (
const auto& sz :
dec.sizes())
144 if ((
int)sz[0] !=
NX)
147 std::printf(
"VOF PHASE CHANGE MPI np=%d grid %dx%dx%d block %dx%dx%d x cut: %s\n", size,
154 const double x0 = 32.25,
mdot = 0.02, dt = 1.0;
155 auto colour = [&](
int x, int, int) {
return std::fmin(1.0, std::fmax(0.0, x0 - x)); };
156 std::vector<double>
refC;
164 ref.enablePhaseChange(1.0, 1.0, 1.0);
167 ref.setMassFluxUniform(
mdot);
168 for (
int k = 0; k < 1000; ++k)
169 ref.applyPhaseChange(dt);
183 for (
int k = 0; k < 1000; ++k)
187 for (
double v :
refC)
191 " P0a 1000 steps: max |C_dist - C_ref| = %.3e (bitwise expected); x_ref = "
192 "%.15g, exact %.15g\n",
199 const double St = 1.0, alpha = 1.0,
x0p = 0.10,
xep = 0.25,
Fo = 0.5;
201 const double t0 = (
x0p / (2 * lam)) * (
x0p / (2 * lam)) / alpha;
202 const double te = (
xep / (2 * lam)) * (
xep / (2 * lam)) / alpha;
203 const double D = alpha *
NX *
NX;
204 const int ns = (int)std::llround((
te - t0) / (
Fo / D));
205 const double dt = (
te - t0) / ns;
207 auto colour = [&](
int x, int, int) {
return std::fmin(1.0, std::fmax(0.0, (x + 1) -
xg)); };
208 auto temp = [&](
int x, int, int) {
209 const double xp = (x + 0.5) /
NX;
210 return xp <
x0p ? 1.0 - std::erf(lam * xp /
x0p) / std::erf(lam) : 0.0;
230 std::vector<double>
refC;
234 for (
int k = 0; k < ns; ++k) {
235 ref.applyPhaseChange(dt);
236 ref.advanceScalars();
243 for (
int k = 0; k < ns; ++k) {
249 for (
double v :
refC)
251 const double xExact = 2 * lam * std::sqrt(alpha *
te) *
NX;
254 " P1 Stefan %d steps: max |C_dist - C_ref| = %.3e (RB-GS reduction floor); layer_ref "
255 "= %.5f, exact %.5f (%+.4f %%)\n",
269 const char* xe = std::getenv(
"PECLET_P23_XEP");
270 const double ratio = 10.0,
ja = 1.0,
alpha_l = 1.0,
x0p = 0.10,
Fo = 0.5;
271 const double xep = xe ? std::atof(xe) : 0.14;
272 const double cfl = 0.2,
rr = 1.0 / ratio;
273 double b =
ja / std::sqrt(
M_PI);
274 for (
int i = 0; i < 300; ++i) {
275 const double g = b *
rr;
276 b =
ja * std::exp(-g * g) / (std::sqrt(
M_PI) * std::erfc(g));
278 const double t0 = (
x0p / (2 * b)) * (
x0p / (2 * b)) /
alpha_l;
284 auto colour = [&](
int x, int, int) {
return std::fmin(1.0, std::fmax(0.0, (x + 1) -
X0)); };
285 auto temp = [&](
int x, int, int) {
286 const double xp = (x + 0.5) /
NX;
289 const double sv = xp / (2 * std::sqrt(
alpha_l * t0)) - b * (1.0 -
rr);
290 return dT -
dT * std::erfc(sv) / std::erfc(b *
rr);
292 auto farT = [&](
double tt) {
293 const double sv = 1.0 / (2 * std::sqrt(
alpha_l *
tt)) - b * (1.0 -
rr);
294 return dT -
dT * std::erfc(sv) / std::erfc(b *
rr);
317 const char* off = std::getenv(
"PECLET_P23_OFF");
318 const bool offP = off && std::strchr(off,
'p');
319 const bool offQ = off && std::strchr(off,
'q');
320 const bool offE = off && std::strchr(off,
'e');
330 const double Xd = b * std::sqrt(
alpha_l / tc) *
NX;
331 double dt = std::fmin(
cfl / std::fmax(
Xd * (1.0 -
rr), 1e-30),
Fo / al);
332 dt = std::fmin(dt,
te - tc);
348 refT = ref.getField(
"T");
357 for (
double v :
refC)
368 " P2 sucking %d coupled steps (plane-anchored Dirichlet + quadratic fit + consistent "
369 "rho c_p T): layer_dist = %.9f vs layer_ref = %.9f (rel %.3e); pointwise max "
370 "|C_dist - C_ref| = %.3e, |T_dist - T_ref| = %.3e; exact %.5f (%+.4f %%)\n",
void setPhaseChangeGfmOrder(int order)
WO-P3g item 2: the order of the one-sided (ghost-fluid) Dirichlet row.
void setMassFluxUniform(double v)
Prescribe a UNIFORM mass flux (P0), in mass per area per time.
void setDomainBc(int face, int type, double vx, double vy, double vz)
void setPhaseChangeFitCurvature(double kappa)
WO-P3f INSTRUMENT: prescribe the interface curvature kappa = div(n) the one-sided fits use to correct...
void setPhaseChangeMdotOperator(bool on)
WO-P3g item 1: take mdot from the energy operator's own interfacial flux instead of the one-sided lea...
std::vector< double > getField(const std::string &name)
void addScalar(const std::string &name, double D, int scheme, int iters, bool cutcell=false)
std::vector< double > getVof()
void setPropertyModel(const std::string &target, ClosureKind kind, const std::string &in0, const std::string &in1, const std::vector< double > ¶ms)
void setVof(const std::vector< double > &c)
void setScalarBc(const std::string &name, int face, int type, double value)
void setPhaseChangeThermal(const std::string &tname, double Tsat, double kg, double kl, double Rint)
P1: compute mdot each step from the registered scalar tname by the one-sided pure-cell weighted least...
void setPhaseChangePlaneDirichlet(bool on)
The PLANE-ANCHORED (ghost-fluid) Dirichlet condition, ON by default.
void setPhaseChangeQuadraticFit(bool on)
The QUADRATIC one-sided gradient fit (T - T_G = G phi + Q phi^2) instead of the linear one.
void setPressureGeometry(const std::vector< double > &sdfInner)
void applyPhaseChange(double dtPhysArg)
Kinematic entry point (the P0a/P1 driver): build mdot/A_G/n from the current colour and temperature,...
void setPhaseChangeCarryConserve(bool on)
WO-P3f: make the per-cell Dirichlet overwrite ENTHALPY-CONSERVING.
void setField(const std::string &name, const std::vector< double > &v)
void enablePhaseChange(double rhoG, double rhoL, double hlv)
Turn on phase change.
void setMu(double m)
Dynamic viscosity, in the caller's units.
void setPhaseChangeCurvatureDistance(bool on)
WO-P3g item 3: measure the GFM row's theta and the one-sided fits' sample distances to the CURVED int...
void setPhaseChangeEnergyOrder(int order)
WO-P3g — the SECOND-ORDER interfacial energy operator, as one package.
void setPhaseChangeEnergy(double rcpGas, double rcpLiquid)
Turn on the CONSISTENT energy transport (VOF_PLAN §9 item 6) for the scalar set_phase_change_thermal ...
void setRho(double r)
Fluid density, in the caller's units.
void setPressureFcg(bool on, int maxit, double rtol)
void setPhaseChangeArea(int mode)
WO-P3c — which geometry the interfacial AREA comes from.
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
static void configure(IbmSolver &s)
static std::vector< double > colour(int m)
static std::vector< double > blockOf(Fn f, int ox, int oy, int oz, int lnx, int lny, int lnz)
static double blockDiff(const std::vector< double > &local, const std::vector< double > &global, int ox, int oy, int oz, int lnx, int lny, int lnz)
static std::size_t gidx(int x, int y, int z)
static double stefanLambda(double St)
static std::vector< double > sliceOf(const std::vector< double > &g, int ox, int oy, int oz, int lnx, int lny, int lnz)
void applyAreaModeEnv(S &s)