# Frozen SR pipeline

## Authority

The only active route is:

```text
stage_1_infer.py -> stage_2_fit.py -> stage_3_validate.py
```

It covers Kármán and Illusion. The `steady` Stage 3/config scene is retained solely for the Article2 constant-rotation calibration used to interpret the Kármán rear constant. Current evidence is indexed by `results/README.md`; archived workflows are non-authoritative and may not execute.

## Preconditions and immutable contracts

Use LegacyCelerisLab, its frozen PPO model, and the scene's frozen norm. Keep native body/action order `front, upper, lower`, native force order `front_fx, front_fy, upper_fx, upper_fy, lower_fx, lower_fy`, and sensor order upper/centre/lower `(u_x,u_y)`. Run `checks/order_contract.py` after any solver/kernel change and `checks/policy_replay.py` before admitting data.

The fit alignment is exactly `causal_post_state_to_next_action`: post-state `i` predicts decoded physical action `i+1`; no lag, derivative, split, or warm-up state crosses a trajectory boundary. Formula output is `alpha=omega/U0`. Canonical deployment is mapped-shared under reflection `G`: `alpha_F=(h_F(x)-h_F(Gx))/2`, `alpha_U=h_R(x)`, `alpha_L=-h_R(Gx)`.

The validation metric is exactly `legacy_dtw_v1_abs_n_unclipped` / `legacy_reference_cycle_vs_last_recorded_cycle`: estimate lag from transverse sensor channel 1 using the historical target/reference windows, circularly shift the full target, compute absolute normalized unclipped DTW independently for six channels, then average. Lag is alignment metadata, not a physical delay.

Interpret names literally: `Re_D=re_code/2` because the code Reynolds reference is `2D`; the Illusion `target_diameter` field is historically named but passed as the Legacy cylinder radius.

## Stage 1 — collect accepted trajectories

Environment: `pycuda_3_10`. CFD is serial. With physical GPU 2 masked, PyCUDA sees logical device 0; keep PPO inference on CPU.

```bash
CUDA_VISIBLE_DEVICES=2 PYTHONPATH=src conda run -n pycuda_3_10 python src/SR_analysis/stage_1_infer.py --group karman_trained --run-id <run> --device 0 --model-device cpu --steps 200 --norm-source existing
CUDA_VISIBLE_DEVICES=2 PYTHONPATH=src conda run -n pycuda_3_10 python src/SR_analysis/stage_1_infer.py --group illusion_trained --run-id <run> --device 0 --model-device cpu --steps 200 --norm-source existing
```

Outputs are immutable beneath `data/runs/<run>/<objective>/<scene>/` and include config, manifest, hashes, frozen norm, target, controlled telemetry, and result; Kármán also retains uncontrolled data and Illusion target harmonics. The canonical accepted roots are the two `article-joint-data-*-20260718` families.

## Stage 2 — discover then refit

Environment: `sr_env`. Start broad with `raw_complete` and `symmetry`; for Illusion compare `actual_only`, `target_only`, `actual_plus_target`, and `actual_plus_error`. Run per-case searches with multiple seeds, then joint searches only within one objective. Keep contiguous train/validation/blind diagnostics and case/trajectory-equal weighting.

```bash
PYTHONPATH=src conda run -n sr_env python src/SR_analysis/stage_2_fit.py --scenes karman_re50,karman_re100,karman_re200,karman_re400 --mode joint --run-id <discovery> --data-root src/SR_analysis/data/runs/article-joint-data-karman-20260718 --feature-set symmetry --feature-profile actual_only --deployment-architecture mapped_shared --fit-augmentation G --fit-purpose discovery --seed 0 --niterations 40 --smoke
```

After topology recurs and survives diagnostics, freeze it and run `--fit-purpose fixed-topology-refit` with explicit front/rear topology expressions and discovery-parent paths. Only coefficients may change. Do not rewrite article parent paths or manifests. PySR score, Pareto complexity, blind R², and static safety are discovery evidence only.

## Stage 3 — closed-loop acceptance

Environment: `pycuda_3_10`; run one CFD process at a time. Screen at 40 steps, then validate accepted candidates for 200 steps with a new immutable run ID.

```bash
CUDA_VISIBLE_DEVICES=2 PYTHONPATH=src conda run -n pycuda_3_10 python src/SR_analysis/stage_3_validate.py --group karman_re50,karman_re100,karman_re200,karman_re400 --mode pysr --formula-front <front.json> --formula-rear <rear.json> --device 0 --steps 200 --run-id <run>
```

Reject any case that is non-finite, terminates early, violates action/order/schema contracts, or lacks required provenance. Retain rejected telemetry. Closed-loop stability and exact legacy DTW outrank offline fit.

## Extensions and necessity tests

Use the frozen accepted coefficients for 400-step duration runs, pointwise unseen Kármán Re-code and Illusion size-label conditions, the disturbance-free `steady` constant-rotation sweep, and deterministic additive-term deletion/coefficient scaling (`0, 0.5, 0.75, 1, 1.25, 1.5`). These are sampled deployments, not a continuous parameter law or statistical robustness study. A term is important only if deletion causes repeatable closed-loop degradation; scale zero must agree with deletion and parent formulas remain immutable.

## Publication exports

```bash
PYTHONPATH=src conda run -n sr_env python -m SR_analysis.tools.prepare_plotting_data --output-dir src/SR_analysis/results/runs/article2-plotting-package-<date>
PYTHONPATH=src conda run -n sr_env python -m SR_analysis.tools.plot_sr_diagnostics
PYTHONPATH=src conda run -n sr_env python -m SR_analysis.tools.plot_sr_presentation
```

`telemetry_to_csv.py` exports Stage 1/3 trajectories and exact DTW-window diagnostics. `export_flow_comparison.py` defines the same-sample phase-matched field-comparison contract; generating flow fields requires separate serial GPU CFD and is not part of CPU verification.

## Acceptance hierarchy and claim limits

1. Contract and provenance integrity.
2. Finite closed-loop completion for every required case.
3. Exact legacy DTW at standard duration.
4. Duration and explicitly sampled unseen-condition behavior.
5. Deletion/scaling evidence for term necessity.
6. Physical interpretation, stated as limited by the preceding evidence.

Supported: reproducible PPO-to-symbolic reduction; dominant Kármán rear counter-rotation with secondary rear-lift feedback; a finite symmetric Illusion numerical family; and pointwise Article2 extension at named samples. Unsupported: global/unique symbolic optimality, universal Re or size generalization, explicit Illusion target-tracking mechanism, necessity of every Illusion term, causal flow structures, or physical delay inferred from DTW lag.

## Exact CPU verification

```bash
PYTHONPATH=src conda run -n sr_env python -m pytest src/SR_analysis/tests -q
python3 -m compileall -q src/SR_analysis/configs.py src/SR_analysis/stage_1_infer.py src/SR_analysis/stage_2_fit.py src/SR_analysis/stage_3_validate.py src/SR_analysis/utils src/SR_analysis/checks src/SR_analysis/tools
git diff --check -- src/SR_analysis
```
