R8-c: the 3D flag's interface — deformed-plate body (host + device φ/ub) and the conservative load transfer onto a Hex20 plate

- embedded3/plate.rs: PlateSurface (mid-surface on span stations) and the
  host evaluation of DeviceSdf (phi_host / velocity_host / is_flag_host),
  expression for expression e3_geom.cu; a span-uniform plate is the
  polyline capsule to the bit; first-order spanwise-slope correction.
- e3_geom.cu / device/geom.rs: plate branch of geom_phi_at and
  body_velocity (nst = 0 keeps the polyline path unchanged).
- cutwall.rs / exchange.rs: the load loops observed through a sink (sums
  unchanged); interface.rs: Mask::cut_wall_loads (every summand of
  cut_wall_force with its foot), HexPlate (R8-b's Hex20 lattice numbering),
  consistent point-force transfer conserving force and moment to round-off,
  locate() for the transpose, mid_surface() for the fluid body.
- flag test: RTX_E3_FLAG_BODY=plate, _STATIONS, _TWIST, _TRANSFER(_EVERY,
  _CSV, _NODAL); all default off.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-25 18:03:26 -05:00
co-authored by Claude Opus 5.5
parent d63806c0e6
commit 9c2ae32061
9 changed files with 1639 additions and 56 deletions
@@ -24,6 +24,7 @@ struct GeomSdf {
double fillet; /* root fillet radius (0: min) */
int cyl_cut, flag_cut; /* cut to the span */
int npts; /* polyline points (x, y interleaved) */
int nst, ns; /* R8-c plate: stations and points per station (nst 0: the polyline) */
};
struct GeomGrid {
@@ -45,15 +46,107 @@ __device__ __forceinline__ double span_cut(double d2, double z, const GeomSdf& s
return outside + rs_min(rs_max(q1, q2), 0.0) - r;
}
/*
* R8-c: the deformed plate (`plate.rs`, the host twin expression for
* expression): `poly` holds the stations' rows (x, y interleaved, row-major
* by station) followed by the stations' z. The stations bracketing z are
* interpolated linearly into one polyline (outside their range: the end
* station as it is), the in-plane closest point taken as the polyline's,
* the distance corrected for the spanwise slope d / sqrt(1 + (e·c)²);
* with `vel`, the velocity at the closest point the same way.
*/
__device__ double plate_dist(double x, double y, double z, const GeomSdf& s,
const double* __restrict__ P, const double* __restrict__ V,
double* vx, double* vy)
{
int nst = s.nst, ns = s.ns;
const double* Z = P + 2 * (long long) nst * ns;
int k = 0, interp = 0;
double w = 0.0;
if (nst > 1) {
if (z <= Z[0]) {
k = 0;
} else if (z >= Z[nst - 1]) {
k = nst - 2;
} else {
while (k + 1 < nst && Z[k + 1] <= z) ++k;
if (k > nst - 2) k = nst - 2;
}
interp = 1;
w = (z - Z[k]) / (Z[k + 1] - Z[k]);
/* beyond the end stations: at most half an interval extrapolated */
if (w < -0.5) w = -0.5;
if (w > 1.5) w = 1.5;
}
const double* R0 = P + 2 * (long long) k * ns;
const double* R1 = interp ? R0 + 2 * ns : R0;
double best = 1.0 / 0.0, ub = 0.0, qbx = 0.0, qby = 0.0;
int mb = 0;
for (int m = 0; m + 1 < ns; ++m) {
double ax, ay, bx, by;
if (interp) {
ax = R0[2 * m] + w * (R1[2 * m] - R0[2 * m]);
ay = R0[2 * m + 1] + w * (R1[2 * m + 1] - R0[2 * m + 1]);
bx = R0[2 * m + 2] + w * (R1[2 * m + 2] - R0[2 * m + 2]);
by = R0[2 * m + 3] + w * (R1[2 * m + 3] - R0[2 * m + 3]);
} else {
ax = R0[2 * m]; ay = R0[2 * m + 1];
bx = R0[2 * m + 2]; by = R0[2 * m + 3];
}
double ex = bx - ax, ey = by - ay;
double l2 = ex * ex + ey * ey;
double u = ((x - ax) * ex + (y - ay) * ey) / l2;
if (u < 0.0) u = 0.0;
if (u > 1.0) u = 1.0;
double px = ax + u * ex, py = ay + u * ey;
double qx = x - px, qy = y - py;
double d = sqrt(qx * qx + qy * qy);
if (d < best) {
best = d;
mb = m;
ub = u;
qbx = qx;
qby = qy;
}
}
if (interp && best > 0.0) {
double dz = Z[k + 1] - Z[k];
double cax = R1[2 * mb] - R0[2 * mb], cay = R1[2 * mb + 1] - R0[2 * mb + 1];
double cbx = R1[2 * mb + 2] - R0[2 * mb + 2], cby = R1[2 * mb + 3] - R0[2 * mb + 3];
double cx = (cax + ub * (cbx - cax)) / dz;
double cy = (cay + ub * (cby - cay)) / dz;
double qn = (qbx * cx + qby * cy) / best;
best = best / sqrt(1.0 + qn * qn);
}
if (V) {
const double* V0 = V + 2 * (long long) k * ns;
const double* V1 = interp ? V0 + 2 * ns : V0;
double avx, avy, bvx, bvy;
if (interp) {
avx = V0[2 * mb] + w * (V1[2 * mb] - V0[2 * mb]);
avy = V0[2 * mb + 1] + w * (V1[2 * mb + 1] - V0[2 * mb + 1]);
bvx = V0[2 * mb + 2] + w * (V1[2 * mb + 2] - V0[2 * mb + 2]);
bvy = V0[2 * mb + 3] + w * (V1[2 * mb + 3] - V0[2 * mb + 3]);
} else {
avx = V0[2 * mb]; avy = V0[2 * mb + 1];
bvx = V0[2 * mb + 2]; bvy = V0[2 * mb + 3];
}
*vx = avx + ub * (bvx - avx);
*vy = avy + ub * (bvy - avy);
}
return best;
}
__device__ double geom_phi_at(double x, double y, double z, const GeomSdf& s, const double* __restrict__ poly)
{
/* the circle */
double ex0 = x - s.cx, ey0 = y - s.cy;
double dc = sqrt(ex0 * ex0 + ey0 * ey0) - s.rc;
if (s.cyl_cut) dc = span_cut(dc, z, s);
/* the capsule: distance to the polyline */
/* the capsule: distance to the polyline (or the plate, R8-c) */
double best = 1.0 / 0.0;
for (int m = 0; m + 1 < s.npts; ++m) {
if (s.nst > 0) best = plate_dist(x, y, z, s, poly, nullptr, nullptr, nullptr);
else for (int m = 0; m + 1 < s.npts; ++m) {
double ax = poly[2 * m], ay = poly[2 * m + 1];
double bx = poly[2 * m + 2], by = poly[2 * m + 3];
double ex = bx - ax, ey = by - ay;
@@ -299,7 +392,8 @@ __device__ double body_velocity(double x, double y, double z, int c, const GeomS
const double* __restrict__ poly, const double* __restrict__ vel)
{
double best = 1.0 / 0.0, vx = 0.0, vy = 0.0;
for (int m = 0; m + 1 < s.npts; ++m) {
if (s.nst > 0) best = plate_dist(x, y, z, s, poly, vel, &vx, &vy);
else for (int m = 0; m + 1 < s.npts; ++m) {
double ax = poly[2 * m], ay = poly[2 * m + 1];
double bx = poly[2 * m + 2], by = poly[2 * m + 3];
double ex = bx - ax, ey = by - ay;