Skip to content

Study/inclined plate sweep tests - #123

Merged
andytorrestb merged 11 commits into
plume-kit:masterfrom
andytorrestb:study/inclined-plate-sweep-tests
Jul 25, 2026
Merged

Study/inclined plate sweep tests#123
andytorrestb merged 11 commits into
plume-kit:masterfrom
andytorrestb:study/inclined-plate-sweep-tests

Conversation

@andytorrestb

Copy link
Copy Markdown
Member

No description provided.

andytorrestb and others added 11 commits July 18, 2026 21:26
Add pyrpod/plume/CaiImpingement2016.py, a plain-function verification
reference (not a plume model) for Cai 2016 Section 4: exact diffuse-plate
surface coefficients Cp,d/Cf1,d/Cf2,d/Cq,d (Eqs. 9-13), specular Cp,s
(Eq. 14), and Eq. 15 plate-averaged properties, via tensor-product
Gauss-Legendre quadrature over the exit disk with the overflow-safe
exp(-S0^2) scaling style of RarefiedPlumeGasKinetics.

The diffuse-wall re-emission density n_w (not printed for 3D in the
paper) is derived from non-penetration as the A1-analog exit-disk flux
integral; the interpretation is documented in the module docstring and
validated against Figs. 17-18 magnitudes (diffuse peak 0.268 above the
0.2 innermost contour, specular peak 0.368 above 0.3 and higher at the
impingement center, heat-flux peak 0.080 beneath the plate center, Cf1
stagnation line at tau ~ -2.1 -- all matching the paper). Built-in
sanity checks (s-parity, Cf2 antisymmetry, Cp,s = 2x jet-only Cp,
dblquad flux balance for n_w, quadrature-order convergence) pass at
machine precision.

Figure scripts plume_verification_test_28..32 reproduce Figs. 17-21
following the established manual-run conventions (design decision D5),
with digitized-overlay slots fig17_*.csv..fig21_*.csv. Each figure also
overlays the current PyRPOD approximation -- SimplifiedGasKinetics local
field values fed through the Shen/Maxwellian wall formulas at the TRUE
incidence angle (radial flow direction vs inclined plate normal), i.e.
exactly what the Phase-2-fixed strike pipeline computes per face
(sigma=0 for the specular figure). plume_impingement_error_summary.py
extends the error-summary pattern with max/mean relative-error rows
restricted to |ref| >= 5% of peak: pressure agrees to ~4% mean / 13%
max, shear ~8% mean, heat flux ~18% mean -- the measured gap of the
Maxwellian engineering chain vs the exact solution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fix the orientation-blind load path in PlumeStrikeCalculator: the
Shen/Maxwellian wall formulas now receive the TRUE incidence angle
between the local flow direction (face centroid minus thruster exit --
the collisionless flow is radial) and the face unit normal, via the
shared _surface_loads_with_incidence() helper used identically by the
vectorized core and the scalar reference path. The positional off-axis
theta keeps its two legitimate roles unchanged: the legacy 3.14-based
wedge hit test (bit-for-bit untouched) and the SimplifiedGasKinetics
field evaluation (where it is a plume-field coordinate). Previously the
positional theta was also fed to the wall formulas as the incidence
angle, so plate orientation never affected load magnitudes.

Regression: all 96 tests/rpod + tests/plume tests pass unchanged. The
expected-strike logs of rpod_integration_test_01..04 assert strike
counts and struck-face IDs only, both of which are produced by the
untouched hit test, so no expected logs needed updating. Load values do
change on non-normal faces by design: e.g. faces seen at grazing
positional angles but struck head-on now take full normal pressure. The
scalar-vs-vectorized (plume_unit_test_04) and serial-vs-parallel
(plume_integration_test_02) equivalences hold with the shared helper.

New case case/plume/plume_inclined_plate mirrors plume_sq_plate: a
generated 10368-face 8 m x 8 m plate rotated 60 deg about its center at
(4, 0, 0) m (parametrized transform_inclined_plate.py for the Phase-3
sweep), single argon thruster at the paper conditions (S0 = 2.0,
T0 = 200 K, D = 1 m, Tw = 300 K, sigma = 1), script-generated
single-firing JFH, and wedge_theta = 1.55 so the gating wedge clears
the whole plate (the 0.436 default would clip it).

rpod_integration_test_07 runs the case through
PlumeStrikeEstimationStudy, asserts all faces struck once, both compute
paths identical, and pipeline-vs-analytic-chain consistency <2%, then
documents the pipeline-vs-exact-reference gap quantitatively (Cp mean
3.8% / max 12%, shear mean 7.4%, heat flux mean 17.2% over significant
faces) in tests/plume/output/cai2016_pipeline_error_summary.md --
acceptance is the measured gap, not a hard tolerance gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
generate_sweep_jfh.py emits ONE 95-firing JFH: the plate stays
stationary while the visiting vehicle is swept over arcs of radius L
about the plate center with the thruster aimed at the center -- alpha
from -90 to +90 deg in 10 deg steps (0 = head-on = paper alpha0 = 90
deg, alpha_paper = 90 - |alpha|) x L/D in {2, 4, 6, 8, 10}. The JFH DCM
first column carries the thruster axis per the pipeline plume-normal
convention; xyz precision raised to 9 significant digits.

scripts/inclined_plate_sweep_study.py (standalone; pyrpod/mdao
untouched, no optimization) runs each firing through
compute_plume_strikes -- the per-firing pipeline core -- keeping
per-firing results separated from any cumulative accumulation, and
writes to case/plume/plume_inclined_plate/results/sweep/ (gitignored):
Eq.-15 plate-averaged CP/CF1/CF2/CQ/CM/s_cc per pose from the pipeline
AND the exact Cai 2016 reference on the same axes, peak per-face
Cp/|Cf|/Cq curves, and a full CSV. Pipeline |shear| is decomposed onto
the plate axes along the tangential projection of the radial flow
direction; alpha > 0 poses are mirror images of the paper geometry, so
CF1/CM are sign-flipped into the paper convention.

Built-in sanity checks: mirror symmetry in +/-alpha holds to 2.15e-07
(the JFH file's 6-digit DCM precision); CF2 vanishes to the mesh's
O(h^2) triangulation asymmetry; the +/-90 deg edge-on poses average to
~zero load as expected (CP a few percent of head-on). Documented
degeneracy: at exactly edge-on the facing test dot product is 0, so
float32 mesh normals make strike membership epsilon-arbitrary and a few
near-nozzle faces carry large grazing values at L/D = 2; those rows
stay in the CSV/report but are excluded from the plots and symmetry
assertions.

Measured agreement: pipeline plate-averaged CP tracks the exact
reference within ~1-3% over the sweep and CF1 within ~5%. Timings on
the 10368-face mesh: 15.6 s for all 95 firings (164 ms/firing) plus
86 s for the reference quadrature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route the Figs. 17-21 reproduction scripts (plume_verification_test_28
..32) through a study-local save_figure that writes to the Cai2016
subfolder of the figure output directory, keeping the 2016 impingement
set separate from the 2012 figure outputs. The output/.gitignore
(ignore-all) already covers the subfolder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extend pyrpod/plume/CaiImpingement2016.py -- the single source of truth
for every Cai-2016 verification figure -- with the remaining paper
solutions:

* Section 3, 2D slot jet on an inclined planar plate: exact surface
  coefficients Cp,d/Cf,d/Cq,d/Cp,s (Eqs. 2-4, 8) with the
  non-penetration wall density derived as the A0 number-flux integral
  (2D analog of the 3D A1 integral), and the combined jet + plate
  flowfield moments for the temperature contours -- diffuse wall
  emission integrated over arrival directions with n_w(s) at the
  ray-plate intersection (finite plate edges handled naturally), and
  the specular virtual nozzle built by literal mirroring of the exit
  (reproduces Eq. 7). The Eq.-4 energy integrand was re-derived and
  verified to equal 2 e^(a^2)(I4 + I2/2); the virtual DRIFT is the
  mirror image U0(cos 2a0, sin 2a0) -- the papers printed negative
  signs are the velocity-space domain vertex of Fig. 14, documented
  like the 2012 Eq. 7 typo note. Section-3 geometry (L = 4*(2H),
  W = 5*(2H)) is read off the figures and documented.

* Section 4 flowfield pressure in the Y = 0 plane (local-T static
  pressure, as the 2012 paper requires): free-jet raw moments via the
  K/M/N factors imported from RarefiedPlumeGasKinetics (never
  re-derived), plus either the Eq.-9 wall-emission solid-angle integral
  over the plate or the 3D virtual nozzle.

New sanity checks pass at machine precision: 2D parity at alpha0 = 90,
2D n_w quad flux balance, wall-emission half-space closure (3e-4 at
h = 1e-3), specular mirror symmetry of the 2D and 3D fields (~1e-15,
the defining property of Figs. 6/16), a closed-form solid-angle sum
rule for the 3D emission kernel, and GL-vs-dblquad of the assembled
emission integral. Paper anchors reproduced: 2D peak Cp,d 0.919 (~0.93),
Cp,s 1.233 (~1.23), Cf,d 0.325 (~0.33) with zero crossing at
s = -2.12 (~-2), Cq,d 0.268 (~0.27); flowfield peaks T/T0 2.44 / 3.60
(Figs. 5/6 top contours 2.4/3.5) and impingement-region p/p0
0.58 / 0.50 (Figs. 15/16 innermost contours 0.5/0.4).

Figure scripts plume_verification_test_33..40 reproduce Figs. 5-10 and
15-16 (manual-run, digitized slots registered); they carry no
PyRPOD-chain overlay because SimplifiedGasKinetics is a round-nozzle
model (no 2D slot analog) and the pipeline computes no
flowfields-with-plates. All Cai-2016 digitized slots move to a cai16_
stem prefix (cai16_fig05 ... cai16_fig21), fixing a latent collision:
the 2016 figure numbers 17-21 overlapped the Cai & Wang 2012 fig17..21
slots already registered by tests 04-27.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 2012 figure scripts (plume_verification_test_04..27) and the 2012
error summary wrote fig02..fig25 and model_error_summary.* loose in
tests/plume/output; move them into an output/Cai2012 subfolder, parallel
to the Cai 2016 impingement set already in output/Cai2016.

save_figure now targets a new CAI2012_DIR = OUTPUT_DIR/Cai2012 and the
2012 error summary writes there too. OUTPUT_DIR stays the shared output
root, so plume_impingement_utils (OUTPUT_DIR = base.OUTPUT_DIR/Cai2016)
and the loose cai2016_* summaries are unaffected -- verified by
re-running a Cai2016 script (still lands in Cai2016) and the plume test
suite (81 passed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 2016 reference-vs-chain error tables (cai2016_error_summary.csv/.md,
from plume_impingement_error_summary.py) and the pipeline-vs-reference
summary (cai2016_pipeline_error_summary.md, from
rpod_integration_test_07) landed loose in tests/plume/output; point them
at output/Cai2016 alongside that study's figures. The former switch to
u.OUTPUT_DIR (already output/Cai2016 in plume_impingement_utils); the
latter adds the Cai2016 segment to SUMMARY_PATH (its parent.mkdir
already creates it). output/ now holds only Cai2012/ and Cai2016/.
rpod_integration_test_07 passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The sweep driver called the array-only pipeline core (compute_plume_
strikes) and wrote only the CSV + coefficient/peak plots, so -- unlike
jfh_plume_strikes for the other RPOD cases -- it produced no VTK to
visualize. Add a per-firing VTK export via the shared convert_stl_to_vtk
writer: one results/sweep/vtk/firing-<i>_a<alpha>_LoD<LD>.vtu per pose
carrying that firing's own per-face strikes, dimensional loads
(pressure_Pa, shear_Pa, heat_flux_W_m2), and coefficients (Cp, Cshear,
Cf1_case, Cf2_case, Cq). The Cf components are in the mesh CASE frame,
un-flipped, so shear vectors point as they physically do on each pose's
mesh (the paper-convention aggregate CF1/CM in the CSV are unchanged).

Because each file is a single independent pose, there is no cumulative
accumulation across firings (the concern that kept the sweep on the
array core), so per-pose VTK is safe. Five sweep_LoD<LD>.pvd ParaView
collections group the fixed-distance poses with alpha as the time
coordinate, so an angle sweep is scrubbable in ParaView. Default on;
--no-vtk skips it. Verified: full run writes 95 .vtu (152 MB, gitignored)
+ 5 .pvd cleanly; VTK export adds ~7 s to the sweep.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Promote the assertion-relevant core of scripts/inclined_plate_sweep_study.py
into tests/rpod/rpod_verification_test_06.py (the swept analog of
rpod_integration_test_07): runs the 95-firing angle x distance sweep through
compute_plume_strikes and gates on JFH length, mirror symmetry (<1e-5), CF2~0,
and a loose plate-averaged pipeline-vs-Cai2016 envelope, writing a compact
error summary to tests/plume/output/Cai2016. Opt-in per-pose strike VTK via
PYRPOD_SWEEP_VTK=1.

Split the sweeps into dedicated, self-contained case folders so each writes
strikes to its own results/strikes/firing-<i>.vtu (standard RPOD convention,
0-based JFH index, + sweep_LoD*.pvd ParaView collections):
  - plume_inclined_plate/       paper single pose only (rpod_integration_test_07)
  - plume_inclined_plate_sweep/ 60 deg sweep (study script; CASE_DIR repointed)
  - plume_flat_plate_sweep/     flat alpha0=0 reframe (rpod_verification_test_06)

The flat reframe is physics-invariant (identical coefficients; mirror worst
9e-11 vs 2.15e-7) but reads cleanly in ParaView. Kept separate because the
paper STL is shared with test_07 + the script, so flattening in place would
break them. generate_sweep_jfh.py is now argparse-parameterized
(--alpha0-deg/--distance/--out; defaults reproduce the 60 deg paper values).

Verified: rpod_verification_test_06 and rpod_integration_test_07 pass; the
study script runs against plume_inclined_plate_sweep and both sweeps emit
firing-0.vtu .. firing-94.vtu.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New case case/plume/plume_cylinder_sweep/ sweeps the VV over 19 angles x 5
orbit radii (95 firings; jfh_cylinder_sweep.A, generated by
generate_cylinder_sweep_jfh.py) about the centroid of the shared high-res
cylinder mesh data/stl/cylinder.stl (14036 faces, radius 2 m, axis along X
in [-7, 0]), aiming the thruster inward. The target STL is referenced from
the shared data/ dir per the repo de-dup convention (not copied in).

tests/rpod/rpod_verification_test_07.py drives the full
PlumeStrikeEstimationStudy pipeline (jfh_plume_strikes) end-to-end on the
cylinder and gates only on it completing with well-formed per-firing strikes
and the sweep illuminating the target -- a smoke test that the pipeline runs
on a curved/closed mesh, not a validated impingement scenario. Passes in
~20 s (95 firings x 14036 faces; peak 7026 faces struck in a single firing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@andytorrestb
andytorrestb merged commit 9e005ee into plume-kit:master Jul 25, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant