embedded3 PERF-3 P3-1: the device predictor's interior fast path (bit 11, default ON; RTX_E3_PREDICT_FAST=0 = uniform path), the face update templated on the component + unrolled (392-byte local frame -> 64), exact-zero divisions/sqrt skipped on the fast path; all byte-identical (DFG 2D ny 122, slab flag ny 62, DFG 3D ny 62; device tests); predictor 22.4 -> 12.5 ms/step at DFG 3D ny 62
CI / Build (macos-latest) (push) Waiting to run
CI / Test (macos-latest) (push) Blocked by required conditions
CI / Test (ubuntu-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (macos-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
CI / WASM Build + Size Check (push) Blocked by required conditions
CI / Distributed Training Tests (push) Blocked by required conditions
CI / CI Success (push) Blocked by required conditions
CI / Format Check (push) Failing after 4s
CI / Clippy Check (push) Failing after 4s
Performance Benchmarks / Run Benchmarks (push) Failing after 5s
CI / Build (ubuntu-latest) (push) Failing after 4s
CI / Build CPU-Only (Explicit) (push) Failing after 4s
Documentation / Build User Guide (push) Successful in 5s
Documentation / Build API Documentation (push) Failing after 21s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-19 20:29:57 -05:00
co-authored by Claude Fable 5.1
parent 77ecc54b24
commit b126915759
2 changed files with 63 additions and 24 deletions
@@ -73,7 +73,7 @@ __device__ __forceinline__ double cut_ap_at(const E3Params& g, const E3Cut& m, i
/* The control volume's geometry of the face of component c at (i, j, k) /* The control volume's geometry of the face of component c at (i, j, k)
(cutwall.rs cv_geometry): side apertures, the closing wall vector, the (cutwall.rs cv_geometry): side apertures, the closing wall vector, the
floored wall distance. */ floored wall distance. */
__device__ void cut_cv(const E3Params& g, const E3Cut& m, int c, int i, int j, int k, int fidx, __device__ __forceinline__ void cut_cv(const E3Params& g, const E3Cut& m, int c, int i, int j, int k, int fidx,
double* alpha_out, double* apm, double* app, double* wall, double* distance_out) double* alpha_out, double* apm, double* app, double* wall, double* distance_out)
{ {
double h[3] = { g.dx, g.dy, g.dz }; double h[3] = { g.dx, g.dy, g.dz };
@@ -81,6 +81,7 @@ __device__ void cut_cv(const E3Params& g, const E3Cut& m, int c, int i, int j, i
double alpha = cut_ap(m, c)[fidx]; double alpha = cut_ap(m, c)[fidx];
int cm[3] = { i, j, k }; cm[c] -= 1; /* cell minus */ int cm[3] = { i, j, k }; cm[c] -= 1; /* cell minus */
int cp[3] = { i, j, k }; /* cell plus */ int cp[3] = { i, j, k }; /* cell plus */
#pragma unroll
for (int d = 0; d < 3; ++d) { for (int d = 0; d < 3; ++d) {
if (d == c) { if (d == c) {
int qm[3] = { i, j, k }; qm[c] -= 1; int qm[3] = { i, j, k }; qm[c] -= 1;
@@ -96,6 +97,7 @@ __device__ void cut_cv(const E3Params& g, const E3Cut& m, int c, int i, int j, i
app[d] = 0.5 * (cut_ap_at(g, m, d, q1[0], q1[1], q1[2], 1.0) + cut_ap_at(g, m, d, q2[0], q2[1], q2[2], 1.0)); app[d] = 0.5 * (cut_ap_at(g, m, d, q1[0], q1[1], q1[2], 1.0) + cut_ap_at(g, m, d, q2[0], q2[1], q2[2], 1.0));
} }
} }
#pragma unroll
for (int d = 0; d < 3; ++d) wall[d] = -(app[d] - apm[d]) * area[d]; for (int d = 0; d < 3; ++d) wall[d] = -(app[d] - apm[d]) * area[d];
double h_min = fmin(fmin(g.dx, g.dy), g.dz); double h_min = fmin(fmin(g.dx, g.dy), g.dz);
const double* dist = c == 0 ? m.d_u : (c == 1 ? m.d_v : m.d_w); const double* dist = c == 0 ? m.d_u : (c == 1 ? m.d_v : m.d_w);
@@ -115,12 +117,13 @@ __device__ void cut_cv(const E3Params& g, const E3Cut& m, int c, int i, int j, i
between the face (fidx, its geometry given) and its open neighbour fq at between the face (fidx, its geometry given) and its open neighbour fq at
q along d — grad(u) . ds_perp with the cut faces' own wall-normal q along d — grad(u) . ds_perp with the cut faces' own wall-normal
gradients (u - U_b)/d_f (cut_predictor.rs `transverse`). */ gradients (u - U_b)/d_f (cut_predictor.rs `transverse`). */
__device__ double cut_transverse(const E3Params& g, const E3Cut& m, int c, int d, int fidx, int fq, const int* q, __device__ __forceinline__ double cut_transverse(const E3Params& g, const E3Cut& m, int c, int d, int fidx, int fq, const int* q,
double sign, double u0, double ub, double alpha, const double* wall, double distance, double sign, double u0, double ub, double alpha, const double* wall, double distance,
double uq, const double* sh, const double* ubt) double uq, const double* sh, const double* ubt)
{ {
double ds[3] = { 0.0, 0.0, 0.0 }; double ds[3] = { 0.0, 0.0, 0.0 };
int any = 0; int any = 0;
#pragma unroll
for (int e = 0; e < 3; ++e) { for (int e = 0; e < 3; ++e) {
if (e == d) continue; if (e == d) continue;
ds[e] = sign * (sh[3 * fq + e] - sh[3 * fidx + e]); ds[e] = sign * (sh[3 * fq + e] - sh[3 * fidx + e]);
@@ -140,19 +143,28 @@ __device__ double cut_transverse(const E3Params& g, const E3Cut& m, int c, int d
int cut0 = a0 > 0.0 && alpha < 1.0, cutq = aq > 0.0 && alpha_q < 1.0; int cut0 = a0 > 0.0 && alpha < 1.0, cutq = aq > 0.0 && alpha_q < 1.0;
if (!cut0 && !cutq) return 0.0; if (!cut0 && !cutq) return 0.0;
double n0[3], nq[3]; double n0[3], nq[3];
#pragma unroll
for (int e = 0; e < 3; ++e) { n0[e] = cut0 ? wall[e] / a0 : 0.0; nq[e] = cutq ? wall_q[e] / aq : 0.0; } for (int e = 0; e < 3; ++e) { n0[e] = cut0 ? wall[e] / a0 : 0.0; nq[e] = cutq ? wall_q[e] / aq : 0.0; }
if (!cut0) for (int e = 0; e < 3; ++e) n0[e] = nq[e]; if (!cut0) {
if (!cutq) for (int e = 0; e < 3; ++e) nq[e] = n0[e]; #pragma unroll
for (int e = 0; e < 3; ++e) n0[e] = nq[e];
}
if (!cutq) {
#pragma unroll
for (int e = 0; e < 3; ++e) nq[e] = n0[e];
}
int far = distance >= d_min || distance_q >= d_min; int far = distance >= d_min || distance_q >= d_min;
double gr[3] = { 0.0, 0.0, 0.0 }; double gr[3] = { 0.0, 0.0, 0.0 };
double count = 0.0; double count = 0.0;
if (!((far && distance < d_min) || (!far && !cut0))) { if (!((far && distance < d_min) || (!far && !cut0))) {
double slope = (u0 - ub) / fmax(distance, d_min); double slope = (u0 - ub) / fmax(distance, d_min);
#pragma unroll
for (int e = 0; e < 3; ++e) gr[e] -= slope * n0[e]; for (int e = 0; e < 3; ++e) gr[e] -= slope * n0[e];
count += 1.0; count += 1.0;
} }
if (!((far && distance_q < d_min) || (!far && !cutq))) { if (!((far && distance_q < d_min) || (!far && !cutq))) {
double slope = (uq - ubt[fq]) / fmax(distance_q, d_min); double slope = (uq - ubt[fq]) / fmax(distance_q, d_min);
#pragma unroll
for (int e = 0; e < 3; ++e) gr[e] -= slope * nq[e]; for (int e = 0; e < 3; ++e) gr[e] -= slope * nq[e];
count += 1.0; count += 1.0;
} }
@@ -161,8 +173,10 @@ __device__ double cut_transverse(const E3Params& g, const E3Cut& m, int c, int d
} }
/* The predicted value of the open face of component c at (i, j, k). */ /* The predicted value of the open face of component c at (i, j, k). */
__device__ double cut_face_update(const E3Params& g, const E3Ptrs& f, const E3Cut& m, int c, int i, int j, int k, int fidx) template <int C>
__device__ double cut_face_update(const E3Params& g, const E3Ptrs& f, const E3Cut& m, int i, int j, int k, int fidx)
{ {
const int c = C; /* compile-time: every [c] index is a register, not local memory (P3-1) */
double h[3] = { g.dx, g.dy, g.dz }; double h[3] = { g.dx, g.dy, g.dz };
double n[3] = { (double)g.nx, (double)g.ny, (double)g.nz }; double n[3] = { (double)g.nx, (double)g.ny, (double)g.nz };
double area[3] = { g.dy * g.dz, g.dx * g.dz, g.dx * g.dy }; double area[3] = { g.dy * g.dz, g.dx * g.dz, g.dx * g.dy };
@@ -171,18 +185,34 @@ __device__ double cut_face_update(const E3Params& g, const E3Ptrs& f, const E3Cu
int p[3] = { i, j, k }; int p[3] = { i, j, k };
const double* old_c = cut_old(f, c); const double* old_c = cut_old(f, c);
double u0 = old_c[fidx]; double u0 = old_c[fidx];
/* PERF-3 P3-1 (bit 11 of wall_order): an INTERIOR face — further than the imposition band
(4 h_min) from the wall, so its own and its neighbours' apertures are 1, the wall vector 0,
the centroid shifts 0 and the surface velocity 0 — skips the geometry loads; every skipped
term is an exact 0.0 or a factor 1.0 in the general path, so the result is bit-identical. */
const double* dist_c = c == 0 ? m.d_u : (c == 1 ? m.d_v : m.d_w);
double h_min_f = fmin(fmin(g.dx, g.dy), g.dz);
int fast = (g.wall_order & 2048) && fabs(dist_c[fidx]) > 4.0 * h_min_f;
/* the control volume's geometry (cutwall.rs cv_geometry) */ /* the control volume's geometry (cutwall.rs cv_geometry) */
double alpha, apm[3], app[3], wall[3], distance; double alpha, apm[3], app[3], wall[3], distance;
cut_cv(g, m, c, i, j, k, fidx, &alpha, apm, app, wall, &distance); if (fast) {
alpha = 1.0;
#pragma unroll
for (int d = 0; d < 3; ++d) { apm[d] = 1.0; app[d] = 1.0; wall[d] = 0.0; }
distance = dist_c[fidx];
} else {
cut_cv(g, m, c, i, j, k, fidx, &alpha, apm, app, wall, &distance);
}
int cm[3] = { i, j, k }; cm[c] -= 1; /* cell minus */ int cm[3] = { i, j, k }; cm[c] -= 1; /* cell minus */
int cp[3] = { i, j, k }; /* cell plus */ int cp[3] = { i, j, k }; /* cell plus */
const double* ubt = c == 0 ? m.ub_u : (c == 1 ? m.ub_v : m.ub_w); const double* ubt = c == 0 ? m.ub_u : (c == 1 ? m.ub_v : m.ub_w);
double ub = ubt[fidx]; double ub = fast ? 0.0 : ubt[fidx];
/* face position */ /* face position */
double x[3]; double x[3];
#pragma unroll
for (int d = 0; d < 3; ++d) x[d] = (p[d] + (d == c ? 0.0 : 0.5)) * h[d]; for (int d = 0; d < 3; ++d) x[d] = (p[d] + (d == c ? 0.0 : 0.5)) * h[d];
double mass_out = 0.0, conv = 0.0, diff = 0.0, wall_implicit = 0.0, wall_rhs = 0.0; double mass_out = 0.0, conv = 0.0, diff = 0.0, wall_implicit = 0.0, wall_rhs = 0.0;
#pragma unroll
for (int d = 0; d < 3; ++d) { for (int d = 0; d < 3; ++d) {
double a_d = area[d]; double a_d = area[d];
int q[3]; int q[3];
@@ -197,8 +227,8 @@ __device__ double cut_face_update(const E3Params& g, const E3Ptrs& f, const E3Cu
double dn2 = f_dn2 >= 0 ? old_c[f_dn2] : 0.0; double dn2 = f_dn2 >= 0 ? old_c[f_dn2] : 0.0;
double m_plus, m_minus; double m_plus, m_minus;
if (d == c) { if (d == c) {
double f_up = (f_up1 >= 0 ? cut_ap(m, c)[f_up1] : alpha) * (f_up1 >= 0 ? up1 : u0); double f_up = (f_up1 >= 0 ? (fast ? 1.0 : cut_ap(m, c)[f_up1]) : alpha) * (f_up1 >= 0 ? up1 : u0);
double f_dn = (f_dn1 >= 0 ? cut_ap(m, c)[f_dn1] : alpha) * (f_dn1 >= 0 ? dn1 : u0); double f_dn = (f_dn1 >= 0 ? (fast ? 1.0 : cut_ap(m, c)[f_dn1]) : alpha) * (f_dn1 >= 0 ? dn1 : u0);
double f0 = alpha * u0; double f0 = alpha * u0;
m_plus = 0.5 * (f0 + f_up) * a_d; m_plus = 0.5 * (f0 + f_up) * a_d;
m_minus = 0.5 * (f_dn + f0) * a_d; m_minus = 0.5 * (f_dn + f0) * a_d;
@@ -210,10 +240,10 @@ __device__ double cut_face_update(const E3Params& g, const E3Ptrs& f, const E3Cu
int fb = cut_face(g, d, q2[0], q2[1], q2[2]); int fb = cut_face(g, d, q2[0], q2[1], q2[2]);
int fc = cut_face(g, d, cm[0], cm[1], cm[2]); int fc = cut_face(g, d, cm[0], cm[1], cm[2]);
int fd = cut_face(g, d, cp[0], cp[1], cp[2]); int fd = cut_face(g, d, cp[0], cp[1], cp[2]);
double fla = fa >= 0 ? cut_ap(m, d)[fa] * old_d[fa] : 0.0; double fla = fa >= 0 ? (fast ? 1.0 : cut_ap(m, d)[fa]) * old_d[fa] : 0.0;
double flb = fb >= 0 ? cut_ap(m, d)[fb] * old_d[fb] : 0.0; double flb = fb >= 0 ? (fast ? 1.0 : cut_ap(m, d)[fb]) * old_d[fb] : 0.0;
double flc = fc >= 0 ? cut_ap(m, d)[fc] * old_d[fc] : 0.0; double flc = fc >= 0 ? (fast ? 1.0 : cut_ap(m, d)[fc]) * old_d[fc] : 0.0;
double fld = fd >= 0 ? cut_ap(m, d)[fd] * old_d[fd] : 0.0; double fld = fd >= 0 ? (fast ? 1.0 : cut_ap(m, d)[fd]) * old_d[fd] : 0.0;
m_plus = 0.5 * (fla + flb) * a_d; m_plus = 0.5 * (fla + flb) * a_d;
m_minus = 0.5 * (flc + fld) * a_d; m_minus = 0.5 * (flc + fld) * a_d;
} }
@@ -237,8 +267,8 @@ __device__ double cut_face_update(const E3Params& g, const E3Ptrs& f, const E3Cu
double u_plus, delta_plus; double u_plus, delta_plus;
/* bit 10 of wall_order (A2): a prescribed neighbour face exchanges no convective momentum */ /* bit 10 of wall_order (A2): a prescribed neighbour face exchanges no convective momentum */
const int* opn = c == 0 ? m.open_u : (c == 1 ? m.open_v : m.open_w); const int* opn = c == 0 ? m.open_u : (c == 1 ? m.open_v : m.open_w);
int presc_up = (g.wall_order & 1024) && f_up1 >= 0 && !opn[f_up1]; int presc_up = !fast && (g.wall_order & 1024) && f_up1 >= 0 && !opn[f_up1];
int presc_dn = (g.wall_order & 1024) && f_dn1 >= 0 && !opn[f_dn1]; int presc_dn = !fast && (g.wall_order & 1024) && f_dn1 >= 0 && !opn[f_dn1];
if (presc_up) { u_plus = u0; delta_plus = 0.0; } if (presc_up) { u_plus = u0; delta_plus = 0.0; }
else if (f_up1 >= 0) { else if (f_up1 >= 0) {
if (g.scheme == SCHEME_UPWIND) delta_plus = 0.0; if (g.scheme == SCHEME_UPWIND) delta_plus = 0.0;
@@ -260,21 +290,21 @@ __device__ double cut_face_update(const E3Params& g, const E3Ptrs& f, const E3Cu
/* bit 5 of wall_order: the exchange with a SOLID neighbour over the axis /* bit 5 of wall_order: the exchange with a SOLID neighbour over the axis
distance to the wall, implicit (S2-5) */ distance to the wall, implicit (S2-5) */
double delta_x = h[d]; double delta_x = h[d];
if (g.wall_order & 32) { if (!fast && (g.wall_order & 32)) {
double a_w0 = sqrt(wall[0] * wall[0] + wall[1] * wall[1] + wall[2] * wall[2]); double a_w0 = sqrt(wall[0] * wall[0] + wall[1] * wall[1] + wall[2] * wall[2]);
if (a_w0 > 0.0) { if (a_w0 > 0.0) {
double n_d = fabs(wall[d]) / a_w0; double n_d = fabs(wall[d]) / a_w0;
if (n_d >= 1e-12) delta_x = fmin(distance / n_d, h[d]); if (n_d >= 1e-12) delta_x = fmin(distance / n_d, h[d]);
} }
} }
int solid_up = (g.wall_order & 32) && f_up1 >= 0 && cut_ap(m, c)[f_up1] == 0.0; int solid_up = !fast && (g.wall_order & 32) && f_up1 >= 0 && cut_ap(m, c)[f_up1] == 0.0;
int solid_dn = (g.wall_order & 32) && f_dn1 >= 0 && cut_ap(m, c)[f_dn1] == 0.0; int solid_dn = !fast && (g.wall_order & 32) && f_dn1 >= 0 && cut_ap(m, c)[f_dn1] == 0.0;
/* bit 6 of wall_order: cross diffusion over the open-part centroid spacing, /* bit 6 of wall_order: cross diffusion over the open-part centroid spacing,
the part beyond 1/h point-implicit (S2-5) */ the part beyond 1/h point-implicit (S2-5) */
int centroid = (g.wall_order & 64) && d != c; int centroid = (g.wall_order & 64) && d != c;
const double* sh = c == 0 ? m.s_u : (c == 1 ? m.s_v : m.s_w); const double* sh = c == 0 ? m.s_u : (c == 1 ? m.s_v : m.s_w);
/* bit 7 of wall_order: the transverse centroid correction, cross and own direction (S2-6) */ /* bit 7 of wall_order: the transverse centroid correction, cross and own direction (S2-6) */
if ((g.wall_order & 128) && (g.wall_order & 64)) { if (!fast && (g.wall_order & 128) && (g.wall_order & 64)) {
if (f_up1 >= 0 && !solid_up && cut_ap(m, c)[f_up1] > 0.0) { if (f_up1 >= 0 && !solid_up && cut_ap(m, c)[f_up1] > 0.0) {
int qn[3] = { i, j, k }; qn[d] += 1; int qn[3] = { i, j, k }; qn[d] += 1;
double dl = h[d]; double dl = h[d];
@@ -289,6 +319,8 @@ __device__ double cut_face_update(const E3Params& g, const E3Ptrs& f, const E3Cu
} }
} }
if (solid_up) { double kx = mu * g_plus * a_d / delta_x; wall_implicit += kx; wall_rhs += kx * up1; } if (solid_up) { double kx = mu * g_plus * a_d / delta_x; wall_implicit += kx; wall_rhs += kx * up1; }
/* fast: dl == h exactly, kx == +0.0 exactly, so the else branch below is (X + 0.0) * (up1 - u0) == X * (up1 - u0) */
else if (fast && f_up1 >= 0 && centroid) diff += (mu * g_plus * a_d / h[d]) * (up1 - u0);
else if (f_up1 >= 0 && centroid) { else if (f_up1 >= 0 && centroid) {
double dl = h[d] + (sh[3 * f_up1 + d] - sh[3 * fidx + d]); double dl = h[d] + (sh[3 * f_up1 + d] - sh[3 * fidx + d]);
dl = fmin(fmax(dl, 0.25 * h[d]), 2.0 * h[d]); dl = fmin(fmax(dl, 0.25 * h[d]), 2.0 * h[d]);
@@ -299,6 +331,7 @@ __device__ double cut_face_update(const E3Params& g, const E3Ptrs& f, const E3Cu
else if (f_up1 >= 0) diff += mu * g_plus * a_d * (up1 - u0) / h[d]; else if (f_up1 >= 0) diff += mu * g_plus * a_d * (up1 - u0) / h[d];
else if (sides[d][1] == SIDE_VELOCITY) diff += mu * g_plus * a_d * (beyond_p - u0) / (0.5 * h[d]); else if (sides[d][1] == SIDE_VELOCITY) diff += mu * g_plus * a_d * (beyond_p - u0) / (0.5 * h[d]);
if (solid_dn) { double kx = mu * g_minus * a_d / delta_x; wall_implicit += kx; wall_rhs += kx * dn1; } if (solid_dn) { double kx = mu * g_minus * a_d / delta_x; wall_implicit += kx; wall_rhs += kx * dn1; }
else if (fast && f_dn1 >= 0 && centroid) diff -= (mu * g_minus * a_d / h[d]) * (u0 - dn1);
else if (f_dn1 >= 0 && centroid) { else if (f_dn1 >= 0 && centroid) {
double dl = h[d] - (sh[3 * f_dn1 + d] - sh[3 * fidx + d]); double dl = h[d] - (sh[3 * f_dn1 + d] - sh[3 * fidx + d]);
dl = fmin(fmax(dl, 0.25 * h[d]), 2.0 * h[d]); dl = fmin(fmax(dl, 0.25 * h[d]), 2.0 * h[d]);
@@ -319,13 +352,14 @@ __device__ double cut_face_update(const E3Params& g, const E3Ptrs& f, const E3Cu
double v_u = alpha * h[c] * area[c]; double v_u = alpha * h[c] * area[c];
const double* src = c == 0 ? f.su : (c == 1 ? f.sv : f.sw); const double* src = c == 0 ? f.su : (c == 1 ? f.sv : f.sw);
double source = src[fidx] * v_u; double source = src[fidx] * v_u;
double a_w = sqrt(wall[0] * wall[0] + wall[1] * wall[1] + wall[2] * wall[2]); /* fast: no wall vector, so a_w == 0.0 and shear == mu * 0.0 * c1 == 0.0 for any finite c1 */
double a_w = fast ? 0.0 : sqrt(wall[0] * wall[0] + wall[1] * wall[1] + wall[2] * wall[2]);
/* The wall gradient: one-point (order 1) or quadratic through the next /* The wall gradient: one-point (order 1) or quadratic through the next
open face away from the body along the wall normal's dominant axis open face away from the body along the wall normal's dominant axis
(order 2; the neighbour's old value explicit). */ (order 2; the neighbour's old value explicit). */
double c1 = 1.0 / distance, shear_explicit = 0.0; double c1 = fast ? 0.0 : 1.0 / distance, shear_explicit = 0.0;
/* bit 9 of wall_order: the advancing-wall closure on the one-point coefficient (A3-i) */ /* bit 9 of wall_order: the advancing-wall closure on the one-point coefficient (A3-i) */
if (g.wall_order & 512) { if (!fast && (g.wall_order & 512)) {
const double* vnt = c == 0 ? m.vn_u : (c == 1 ? m.vn_v : m.vn_w); const double* vnt = c == 0 ? m.vn_u : (c == 1 ? m.vn_v : m.vn_w);
c1 = adv_factor(vnt[fidx] * distance / g.nu) / distance; c1 = adv_factor(vnt[fidx] * distance / g.nu) / distance;
} }
@@ -363,7 +397,8 @@ extern "C" __global__ void e3_cut_predict(E3Params g, E3Ptrs f, E3Cut m, int c)
if (c == 2) { if (g.periodic_z) { if (k == g.nz) return; } else if (k == 0 || k == g.nz) return; } if (c == 2) { if (g.periodic_z) { if (k == g.nz) return; } else if (k == 0 || k == g.nz) return; }
const int* open = c == 0 ? m.open_u : (c == 1 ? m.open_v : m.open_w); const int* open = c == 0 ? m.open_u : (c == 1 ? m.open_v : m.open_w);
if (!open[t]) return; if (!open[t]) return;
double val = cut_face_update(g, f, m, c, i, j, k, t); double val = c == 0 ? cut_face_update<0>(g, f, m, i, j, k, t)
: (c == 1 ? cut_face_update<1>(g, f, m, i, j, k, t) : cut_face_update<2>(g, f, m, i, j, k, t));
double* out = c == 0 ? f.u : (c == 1 ? f.v : f.w); double* out = c == 0 ? f.u : (c == 1 ? f.v : f.w);
out[t] = val; out[t] = val;
} }
@@ -306,7 +306,11 @@ impl DeviceStep {
// bit 9: the advancing-wall friction closure (A3-i). // bit 9: the advancing-wall friction closure (A3-i).
+ 512 * i32::from(self.solver.params.wall_advancing) + 512 * i32::from(self.solver.params.wall_advancing)
// bit 10: no convective exchange with prescribed faces (A2). // bit 10: no convective exchange with prescribed faces (A2).
+ 1024 * i32::from(self.solver.params.exchange_convection_off), + 1024 * i32::from(self.solver.params.exchange_convection_off)
// bit 11: the predictor's interior fast path (PERF-3 P3-1; bit-identical
// by construction and gated so; default ON, `RTX_E3_PREDICT_FAST=0` restores
// the uniform path).
+ 2048 * i32::from(!std::env::var("RTX_E3_PREDICT_FAST").is_ok_and(|v| v == "0")),
dx: g.dx, dx: g.dx,
dy: g.dy, dy: g.dy,
dz: g.dz, dz: g.dz,