Study/inclined plate sweep tests - #123
Merged
andytorrestb merged 11 commits intoJul 25, 2026
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.