refactor(oid): reset analysis to claim-free core
Archive superseded studies and payload metadata while preserving a tested Schlegel LR/LE implementation with explicit scientific and physics contracts. Co-authored-by: Cursor <cursoragent@cursor.com>
@@ -79,6 +79,19 @@ tensorboard/
|
|||||||
|
|
||||||
# Analysis outputs (generated)
|
# Analysis outputs (generated)
|
||||||
src/OID_analysis/data/derived/
|
src/OID_analysis/data/derived/
|
||||||
|
# Archived OID generated payloads live on Optane; keep only relocation stubs/manifests locally.
|
||||||
|
src/OID_analysis/archive/legacy_oid_v1/data_derived/*
|
||||||
|
!src/OID_analysis/archive/legacy_oid_v1/data_derived/RELOCATED.txt
|
||||||
|
src/OID_analysis/archive/v2_development_runs/*
|
||||||
|
!src/OID_analysis/archive/v2_development_runs/RELOCATED.txt
|
||||||
|
src/OID_analysis/data_li22b/derived/*
|
||||||
|
!src/OID_analysis/data_li22b/derived/RELOCATED.txt
|
||||||
|
# OID scientific-reset archives: preserve source/docs/compact evidence; large arrays remain ignored by extension.
|
||||||
|
# Historical study paths are intentionally regular directories (never symlinks).
|
||||||
|
src/OID_analysis/archive/studies/*/evidence/**/*.npz
|
||||||
|
src/OID_analysis/archive/materials/pre-reset-active/data/**/*.npz
|
||||||
|
src/OID_analysis/archive/materials/pre-reset-active/data/**/*.npy
|
||||||
|
|
||||||
src/OID_analysis/data/*/vorticity_*.png
|
src/OID_analysis/data/*/vorticity_*.png
|
||||||
src/OID_analysis/data/*/*/vorticity_*.png
|
src/OID_analysis/data/*/*/vorticity_*.png
|
||||||
src/OID_analysis/data/*/*/ddf_checkpoint.npy
|
src/OID_analysis/data/*/*/ddf_checkpoint.npy
|
||||||
@@ -105,6 +118,9 @@ outputs/
|
|||||||
|
|
||||||
ref/
|
ref/
|
||||||
docs/
|
docs/
|
||||||
|
# Historical OID authority documents are compact preservation evidence.
|
||||||
|
!src/OID_analysis/archive/studies/*/presentation/docs/
|
||||||
|
!src/OID_analysis/archive/studies/*/presentation/docs/**
|
||||||
ParaView/
|
ParaView/
|
||||||
# Runtime outputs (generated, not committed)
|
# Runtime outputs (generated, not committed)
|
||||||
src/drl_pinball/legacy_test/output/
|
src/drl_pinball/legacy_test/output/
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# OID handoff
|
||||||
|
|
||||||
|
There is no active redo entrypoint. Do not revive an archived runner in place.
|
||||||
|
|
||||||
|
Read `SCIENTIFIC_RESET.md`, `METHOD.md`, and `PHYSICS_CONTRACT.md`; then inspect the July status and migration manifest. Before writing collection or analysis code, choose exactly one question:
|
||||||
|
|
||||||
|
1. observable-resolving modes;
|
||||||
|
2. sensor state estimation;
|
||||||
|
3. policy sensitivity; or
|
||||||
|
4. physical correction mechanism.
|
||||||
|
|
||||||
|
These require different states, observables, interventions, metrics, and gates and must not be recombined as “importance.” Create a new study ID and new run IDs. Bind complete provenance: source tree/hash, action convention and units, body order, requested and effective action at field time, policy/checkpoint hash, parent checkpoint, DDF/FIFO state, observations, dual clocks/sample IDs, phase definition, geometry, metrics, and all generated artifacts. Never reuse `formal-three`, infer `COMPLETE`, or mutate the archived freeze.
|
||||||
|
|
||||||
|
Candidate checks are questions, not a prescribed redo: sensor-excluded or upstream-only states; explicit measurement-operator/POD baselines; rank/domain/metric sensitivity; task-error observables; force symmetry coordinates; distributed delays; independent parents; matched action interventions; closed-loop ablation; and momentum/vorticity/energy budgets.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Claim-free OID method
|
||||||
|
|
||||||
|
## Scope and claim levels
|
||||||
|
|
||||||
|
- **Mathematical theorem/finite-dimensional consequence:** weighted POD orthogonality; least-squares normal equations; right-inverse identities on the generated range; LR minimum training mean-square state residual; LE minimum `G`-norm field among exact generated-range inverses.
|
||||||
|
- **Standard extension:** diagonal spatial quadrature/masks, sign-invariant MAC, principal angles, projector similarity, and delayed sample pairing.
|
||||||
|
- **Project choice:** state domain, metric `G`, observable metric `H`, POD rank, delay, truncation tolerance, and observable definition. Each changes the question.
|
||||||
|
- **Unsupported claim:** importance, causal effect, physical mechanism, controller necessity, controllability, or actuator/control energy inferred from reconstruction scores or modes.
|
||||||
|
|
||||||
|
For centered retained state coefficients `a` and observable POD coordinates `b`, the forward map is the minimum-MSE linear regression `C = Sigma_ba Sigma_aa^+`, so `b_hat=C a`. If `C` is rank deficient, active code either fails or explicitly restricts all identities to `range(C)`.
|
||||||
|
|
||||||
|
The LR inverse is `L_R = Sigma_aa C^T (C Sigma_aa C^T)^+`. On the generated range it is an exact right inverse and minimizes empirical state reconstruction MSE among exact linear inverses. “Minimum MSE linear inverse” does not mean most-probable nonlinear state.
|
||||||
|
|
||||||
|
For a declared positive-definite state-field metric `G`, the LE inverse is `L_E = G^-1 C^T (C G^-1 C^T)^+`. It minimizes the `G`-norm of the reconstructed field under the empirical mapping constraint. This is not control energy, actuator cost, controllability, or a Navier–Stokes energy principle.
|
||||||
|
|
||||||
|
`q_ctl-q_blk` is a same-parent, time-paired difference between closed-loop branches. Feedback changes future state and future action together; this difference is not an isolated action effect, autonomous perturbation, tangent state, or causal intervention estimate.
|
||||||
|
|
||||||
|
## Schlegel section and equation map
|
||||||
|
|
||||||
|
The active finite-dimensional implementation uses Schlegel et al.'s notation only where the mathematical objects match:
|
||||||
|
|
||||||
|
- Section 2.3, equations (2.8)-(2.10): the linear stochastic estimate `b = C a`, observable quadratic form, and EPOD context. Active `C = Sigma_ba Sigma_aa^+` is the fitted regression map, not raw cross-covariance.
|
||||||
|
- Section 2.4, equations (2.11)-(2.15): the delayed linear OID assumption, its finite-dimensional map, and modes obtained by applying an inverse map to observable POD directions. Active delayed pairing implements `b(t+tau) = C(tau) a(t)` without padding or crossing declared segments.
|
||||||
|
- Sections 2.5-2.6: pseudoinverse choice, LR/LE variants, snapshot algorithm, and delay selection context. This project fixes delay and rank by declared analysis choices; it does not infer causality from a fitted delay.
|
||||||
|
- Appendix, especially (A 1)-(A 4): generated-range projection/right-inverse constraint and least-residual versus least-energetic variational definitions. Active code restricts identities to `range(C)` when truncation is explicitly enabled.
|
||||||
|
|
||||||
|
Schlegel's application language about observer/control design is not inherited as a result here. In particular, this project's `G`-minimum is a declared state metric, not actuator energy or a Lyapunov-control certificate.
|
||||||
|
|
||||||
|
## Literature support matrix
|
||||||
|
|
||||||
|
| Source | Direct support | Boundary |
|
||||||
|
|---|---|---|
|
||||||
|
| Schlegel et al. (2012) | LR/LE forward/inverse construction and observable-linked modes | Does not license this project's importance, causal, or controller claims |
|
||||||
|
| Sirovich (1987) | Method of snapshots for POD | Weighting, masks, ranks, and domains here are project choices/standard extensions |
|
||||||
|
| Adrian (1994), LSE | Conditional linear minimum-mean-square estimation | A regression estimate is not causal or a most-probable nonlinear state |
|
||||||
|
| Borée (2003), EPOD | Correlated-field extension of POD and its connection to LSE; Schlegel identifies prefiltered LR-OID with EPOD | Equivalence requires the stated POD prefiltering and metric assumptions |
|
||||||
|
| Everson & Sirovich (1995), gappy POD | Least-squares recovery from incomplete snapshots in a fixed empirical basis | Active OID is not gappy POD; local sensors overlapping the state domain require explicit overlap analysis |
|
||||||
|
| Theiler et al. (1992) | Broad rationale for surrogate/null testing of time series | The project's circular-shift null, exclusions, and selection loop are not directly derived from Theiler |
|
||||||
|
|
||||||
|
Bibliographic leads do not upgrade project choices to theorems. Any new study must inspect the primary source and state its exact assumptions.
|
||||||
|
|
||||||
|
## Bibliography
|
||||||
|
|
||||||
|
- Schlegel, M., Noack, B. R., Jordan, P., Dillmann, A., Gröschel, E., Schröder, W., Wei, M., Freund, J. B., Lehmann, O., & Tadmor, G. (2012). “On least-order flow representations for aerodynamics and aeroacoustics.” *Journal of Fluid Mechanics*, 697, 367-398. DOI: 10.1017/jfm.2012.70.
|
||||||
|
- Sirovich, L. (1987). “Turbulence and the dynamics of coherent structures. I. Coherent structures.” *Quarterly of Applied Mathematics*, 45(3), 561-571. DOI: 10.1090/qam/910462.
|
||||||
|
- Adrian, R. J. (1994). “Stochastic estimation of conditional structure: a review.” *Applied Scientific Research*, 53, 291-303. DOI: 10.1007/BF00849106.
|
||||||
|
- Borée, J. (2003). “Extended proper orthogonal decomposition: a tool to analyse correlated events in turbulent flows.” *Experiments in Fluids*, 35, 188-192. DOI: 10.1007/s00348-003-0656-3.
|
||||||
|
- Everson, R., & Sirovich, L. (1995). “Karhunen-Loève procedure for gappy data.” *Journal of the Optical Society of America A*, 12(8), 1657-1664. DOI: 10.1364/JOSAA.12.001657.
|
||||||
|
- Theiler, J., Eubank, S., Longtin, A., Galdrikian, B., & Farmer, J. D. (1992). “Testing for nonlinearity in time series: the method of surrogate data.” *Physica D: Nonlinear Phenomena*, 58(1-4), 77-94. DOI: 10.1016/0167-2789(92)90102-S.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Physics and rotation contract
|
||||||
|
|
||||||
|
This contract is source-derived. Historical labels do not override it.
|
||||||
|
|
||||||
|
## Coordinates and storage
|
||||||
|
|
||||||
|
- `CelerisLab/src/CelerisLab/body/geometry/circle.py` stores native linear index `k = x + y*nx`.
|
||||||
|
- `CelerisLab/src/CelerisLab/common/render.py` uses maintained `imshow(..., origin="lower")`; displayed `+y` is upward. Array storage and plotting orientation must not be silently conflated.
|
||||||
|
- Pinball body order is `[front, upper, lower]`, consistent with creation/order use in `src/drl_pinball/train/env_karman.py` and archived OID metadata.
|
||||||
|
|
||||||
|
## Rotation oracle
|
||||||
|
|
||||||
|
`LegacyCelerisLab/driver.py` stores rim direction components `(-ry/R, rx/R)` as `(y_c-y)/R, (x-x_c)/R`; `LegacyCelerisLab/kernels/kernel.cu` multiplies both by scalar surface action `u_s`. Thus `(Uw,Vw)=u_s(-ry/R,rx/R)`.
|
||||||
|
|
||||||
|
Modern `CelerisLab/src/CelerisLab/lbm/kernels/step/aux_kernels.cu` uses `(Uw,Vw)=(-Omega*ry, Omega*rx)`. Therefore `Omega=u_s/R` is the nominal continuum sign-and-unit conversion. It is not an exact discrete wall-velocity parity statement. Legacy normalizes the interface solid-cell center direction by nominal `R`; modern applies `Omega` to the boundary-hit lever arm. Those discrete vectors generally have different lengths and directions, so local wall speeds differ by the corresponding geometry factor even under the same nominal conversion.
|
||||||
|
|
||||||
|
The V5 policy adapter `src/drl_pinball/train/env_karman.py::_action_to_omega` uses
|
||||||
|
`Omega = -(a*scale + bias) U0/R`. Requested and EMA-effective action must be recorded separately in any future study.
|
||||||
|
|
||||||
|
The archived OID steady tuple `[0,-5.1,+5.1] U0` and the stated CCD tuple `[0,+5.1,-5.1] U0` are physically opposite under the same `[front,upper,lower]` ordering. Current CCD source `src/CCD_analysis/scripts/collect_vortex.py` contains `[0,-5.1,+5.1] U0`, contradicting that stated CCD label; treat this as unresolved documentation/source disagreement, not corroboration. Existing `src/steady_pinball_theory` documents also contain historical sign interpretations that conflict with this source-derived `Omega=u_s/R` oracle. They are outside this reset and were not edited.
|
||||||
|
|
||||||
|
The CPU oracle in `tests/test_physics_contract.py` binds the sign and nominal `Omega=u_s/R` conversion, and explicitly demonstrates the expected discrete geometry mismatch, without CUDA import or CFD initialization. Any future physics result must additionally record body IDs/order, requested and effective actions, action units, source hash, checkpoint hash, and plotting convention.
|
||||||
@@ -1,85 +1,12 @@
|
|||||||
# OID_analysis — Observable-Inferred Decomposition for Fluidic Pinball
|
# OID analysis: reset state
|
||||||
|
|
||||||
Identifies which correction-field structures the DRL controller modulates, ranked by cross-correlation with force and signature observables (not by POD energy).
|
The active package is a claim-free, CPU-only method core. It contains weighted snapshot POD, the Schlegel LR/LE linear maps and generated-range handling, sign-invariant mode/subspace metrics, exact/delayed pairing, and atomic artifact validation. It contains no current scientific result, scene runner, GPU path, plotting path, audit, freeze builder, or redo command.
|
||||||
|
|
||||||
## Quick Start
|
The July 2026 two-scene work is preserved at `archive/studies/2026-07-two-scene-conditional/`. Its status is conditional: sensor high R² is partly self-observation because those sensors are local velocity functionals inside the state domain; force failed gates; no active LR/LE action result exists. Prediction or reconstruction does not imply importance, mechanism, causality, controller necessity, or control authority.
|
||||||
|
|
||||||
```bash
|
Read `METHOD.md`, `SCIENTIFIC_RESET.md`, `PHYSICS_CONTRACT.md`, then `HANDOFF.md`. Active tests exercise only mathematics, alignment, atomic artifacts, and a CPU source oracle. Historical `OID_METHOD.md`, `PIPELINE.md`, and `RESULTS.md` moved with the study and are not active instructions.
|
||||||
# Read first: PIPELINE.md pipeline overview, scene table, conventions
|
|
||||||
# Deep dive: OID_knowledge.md rules, results table, bug history
|
|
||||||
# Tasks: OID_notes.md open items, handover
|
|
||||||
# Conclusions: Final_Conclusions.md six key questions answered
|
|
||||||
|
|
||||||
# Run (from repo root):
|
|
||||||
PYTHONPATH="src:$PYTHONPATH" conda run -n sr_env python3 \
|
|
||||||
src/OID_analysis/analysis/run_full_analysis.py --scene karman_re100 --force
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Map
|
## Preservation status
|
||||||
|
|
||||||
```
|
These worktree files are not Git-preserved merely because they exist locally. Git preservation requires deliberate review, staging, and commit. Compact authority documents, manifests, source, and tests are intended to remain trackable; bulk NPZ/NPY payloads may remain ignored because recovery depends on the SHA-256-bound Optane archive inventories documented in `archive/RELOCATION.md`. Verify those manifests and restore into staging before using archived payloads.
|
||||||
src/OID_analysis/
|
|
||||||
├── PIPELINE.md ← START HERE — overview, scene table, conventions
|
|
||||||
├── OID_knowledge.md hard rules, full results, bugs
|
|
||||||
├── OID_notes.md task tracking, open items
|
|
||||||
├── Final_Conclusions.md six key questions answered
|
|
||||||
├── scene_registry.json machine-readable scene index + canonical values
|
|
||||||
├── configs.py single source of truth: 13 scene definitions
|
|
||||||
│
|
|
||||||
├── utils/ core library (CPU, no GPU dependency)
|
|
||||||
│ ├── analysis.py POD, force-OID, sig-OID, PCD, zone stats
|
|
||||||
│ └── cfd_interface.py re-exports from CCD_analysis
|
|
||||||
│
|
|
||||||
├── scripts/ GPU data collection (pycuda_3_10 env)
|
|
||||||
│ ├── collect_empty_channel.py / collect_pinball_baseline.py
|
|
||||||
│ ├── collect_karman_blk.py / collect_disturbance_only.py
|
|
||||||
│ ├── collect_controlled.py / collect_steady_cloak.py
|
|
||||||
│ ├── collect_illusion_qblk.py / collect_target_cylinder.py
|
|
||||||
│ ├── collect_all_data.py batch orchestrator
|
|
||||||
│ └── replay_full_fields.py full-field PPO replay
|
|
||||||
│
|
|
||||||
├── analysis/ CPU analysis pipeline (sr_env)
|
|
||||||
│ ├── phase1_correction_pod.py → phase7_whitebox.py (7 phases)
|
|
||||||
│ ├── robustness_analysis.py / steady_reanalysis.py
|
|
||||||
│ ├── compile_master_table.py / make_figures.py
|
|
||||||
│ └── run_full_analysis.py batch runner
|
|
||||||
│
|
|
||||||
├── data/ raw collected data (NOT committed)
|
|
||||||
│ ├── steady_cloak/ q_in, q_blk, q_ctl
|
|
||||||
│ ├── karman_cloak/ q_in, q_blk, q_ctl
|
|
||||||
│ ├── illusion/ q_ctl (3 diameters)
|
|
||||||
│ ├── target_cylinder/ reference targets
|
|
||||||
│ └── derived/ all computed results + 7 figures
|
|
||||||
│
|
|
||||||
├── papers/ reference papers
|
|
||||||
│ ├── Sch12.md OID original paper
|
|
||||||
│ └── Li22b.md pinball state estimation paper
|
|
||||||
├── docs/
|
|
||||||
│ └── sch12_code_mapping.md Sch12 formula → code traceability
|
|
||||||
├── tests/ unit tests (7/7 pass)
|
|
||||||
└── archive/ deprecated files
|
|
||||||
```
|
|
||||||
|
|
||||||
## Core Results (one table)
|
|
||||||
|
|
||||||
| Finding | Key Value | Confidence |
|
|
||||||
|---------|-----------|------------|
|
|
||||||
| Force-sig monotonic separation | +0.763 → -0.034 → -0.082 → -0.495 → -0.932 | High |
|
|
||||||
| OID beats POD (force prediction) | R²=0.44-0.75 (OID) vs -2.4~0.42 (POD) | High |
|
|
||||||
| OID beats POD (sig prediction) | R²=0.32-0.66 (OID) vs -0.16~0.06 (POD) | High |
|
|
||||||
| Action orthogonal to force+sig | |overlap|<0.33 across all scenes | Confirmed |
|
|
||||||
| Steady cloak RMS reduction | 99.4% | High |
|
|
||||||
| Li22b cross-map | modes 0-5 diagonal 0.81-0.98 | Verified |
|
|
||||||
| SR validation | OID z1 ↔ Cl_tot r=-0.82 | Verified |
|
|
||||||
|
|
||||||
Full numbers: `data/derived/master/master_table.json` · `scene_registry.json`
|
|
||||||
|
|
||||||
## Pitfalls
|
|
||||||
|
|
||||||
1. OID operates on **Δq_ctl = q_ctl − q_blk**, not raw q_ctl.
|
|
||||||
2. **force-OID and sig-OID reported separately** — divergence is a mechanism result.
|
|
||||||
3. **Illusion q_blk uses separate geometry** (pinball_x=19/20.3, sensor_x=30).
|
|
||||||
4. Fields stored at **full 1280×512**; ROI mask applied only at analysis stage.
|
|
||||||
5. GPU: Karman on device 1, steady/illusion on device 3.
|
|
||||||
6. Conda: `pycuda_3_10` for GPU, `sr_env` for CPU analysis.
|
|
||||||
7. "2U" in model name = S_DIM=14, NOT 2× velocity.
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Scientific reset
|
||||||
|
|
||||||
|
The July 2026 study is historical and conditional, not an active result. The reset withdraws any reading that ranks sensor, force, or action “importance” from predictive or inverse-reconstruction performance.
|
||||||
|
|
||||||
|
Sensor observables are local velocity functionals sampled inside the state-field domain. Reconstructing them from a state that contains the same local flow structure creates partial self-observation; high sensor R² therefore cannot establish privileged mechanism or control importance. Force is an integrated surface projection with different dimension, noise, symmetry, and conditioning, and the force jobs failed required gates. Action is generated inside a feedback loop; delayed association does not isolate a commanded action from state-dependent policy response. These three observables are not exchangeable scores on one importance scale.
|
||||||
|
|
||||||
|
There is no active LR/LE action result. No archived score establishes mechanism, causality, controller necessity, actuator authority, controllability, or a paper conclusion. `q_ctl-q_blk` remains a paired closed-loop branch difference only.
|
||||||
|
|
||||||
|
Preservation is non-destructive. The study payload, failed gates, contradiction evidence, old entrypoints, plots, and frozen provenance remain under `archive/studies/2026-07-two-scene-conditional/`. Older material has a machine-readable disposition index at `archive/MATERIAL_DISPOSITION_INDEX.json`; uncertain data was retained.
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"created_or_extracted_files": [
|
||||||
|
".gitignore",
|
||||||
|
"src/OID_analysis/README.md",
|
||||||
|
"src/OID_analysis/METHOD.md",
|
||||||
|
"src/OID_analysis/SCIENTIFIC_RESET.md",
|
||||||
|
"src/OID_analysis/PHYSICS_CONTRACT.md",
|
||||||
|
"src/OID_analysis/HANDOFF.md",
|
||||||
|
"src/OID_analysis/v2/__init__.py",
|
||||||
|
"src/OID_analysis/v2/analysis.py",
|
||||||
|
"src/OID_analysis/v2/alignment.py",
|
||||||
|
"src/OID_analysis/v2/artifacts.py",
|
||||||
|
"src/OID_analysis/tests/__init__.py",
|
||||||
|
"src/OID_analysis/tests/test_schlegel_oid.py",
|
||||||
|
"src/OID_analysis/tests/test_alignment_artifacts.py",
|
||||||
|
"src/OID_analysis/tests/test_physics_contract.py",
|
||||||
|
"src/OID_analysis/archive/MATERIAL_DISPOSITION_INDEX.json",
|
||||||
|
"src/OID_analysis/archive/studies/2026-07-two-scene-conditional/STATUS.md",
|
||||||
|
"src/OID_analysis/archive/studies/2026-07-two-scene-conditional/lineage/CURRENT_STATE_MIGRATION_MANIFEST.json"
|
||||||
|
],
|
||||||
|
"deleted_files": [],
|
||||||
|
"moved_file_count": 516,
|
||||||
|
"moved_files_manifest": "src/OID_analysis/archive/studies/2026-07-two-scene-conditional/lineage/CURRENT_STATE_MIGRATION_MANIFEST.json",
|
||||||
|
"notes": [
|
||||||
|
"No commit created",
|
||||||
|
"Plan file not edited",
|
||||||
|
"Nowledge memories were reviewed and updated after the filesystem reset",
|
||||||
|
"No CFD/GPU initialization",
|
||||||
|
"Final-review hardening edited only active files already enumerated above; the original 516-file migration count and manifest remain unchanged"
|
||||||
|
],
|
||||||
|
"schema_id": "oid-scientific-reset-changeset/v1"
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"disposition": "conditional-historical-study",
|
||||||
|
"path": "archive/studies/2026-07-two-scene-conditional",
|
||||||
|
"reason": "July 2026 source, evidence, lineage, and presentation preserved with conditional status"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "superseded-method",
|
||||||
|
"path": "archive/legacy_oid_v1",
|
||||||
|
"reason": "preserves v1 methods, PLS mislabeling, and error history"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "preserve-evidence",
|
||||||
|
"path": "archive/v2_development_code",
|
||||||
|
"reason": "development source and compact evidence retained"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "unknown-provenance",
|
||||||
|
"path": "archive/v2_development_runs",
|
||||||
|
"reason": "relocation stub and external hash-bound payload retained"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "unknown-provenance",
|
||||||
|
"path": "archive/materials/pre-reset-active/data",
|
||||||
|
"reason": "18 GB local older data retained conservatively"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "superseded-method",
|
||||||
|
"path": "archive/materials/pre-reset-active/data_li22b",
|
||||||
|
"reason": "sensor reconstruction context retained"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "preserve-contradiction",
|
||||||
|
"path": "provenance/contradictions/steady_reanalysis.json",
|
||||||
|
"reason": "steady physics contradiction evidence; now within July study lineage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "preserve-evidence",
|
||||||
|
"path": "archive/RELOCATION_MANIFEST.json",
|
||||||
|
"reason": "hash-bound external relocation authority"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "preserve-evidence",
|
||||||
|
"path": "archive/RUNS_RELOCATION_MANIFEST.json",
|
||||||
|
"reason": "hash-bound run relocation authority"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "preserve-evidence",
|
||||||
|
"path": "archive/materials/pre-reset-active/papers",
|
||||||
|
"reason": "preserved local literature notes used by older work"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "superseded-method",
|
||||||
|
"path": "archive/legacy_oid_v1/utils",
|
||||||
|
"reason": "preserved executable history; inactive legacy utilities"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "preserve-evidence",
|
||||||
|
"path": "archive/materials/pre-reset-active/legacy_inventory.json",
|
||||||
|
"reason": "inventory of pre-reset material and original path identities"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disposition": "preserve-evidence",
|
||||||
|
"path": "archive/materials/pre-reset-active/data/configs/legacy",
|
||||||
|
"reason": "preserved legacy solver configuration files at their current archive path"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"policy": "conservative preservation; no payload deleted during scientific reset",
|
||||||
|
"schema_id": "oid-material-disposition/v1"
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# OID archive relocation
|
||||||
|
|
||||||
|
Large historical OID payloads were relocated on 2026-07-22 using the selected **move + local stub, no symlink** policy.
|
||||||
|
|
||||||
|
- Archive root: `/home/frank14f/optane/DynamisLab-backups/OID_analysis-archive-20260722/`
|
||||||
|
- Payload root: `/home/frank14f/optane/DynamisLab-backups/OID_analysis-archive-20260722/payload/`
|
||||||
|
- Local manifest: `RELOCATION_MANIFEST.json`
|
||||||
|
- Manifest SHA-256: `0f4a7c25b0e76ec60a9c2229260917444d2420fccf101b091a5aa0b2f34a32aa`
|
||||||
|
- Verified payload: 250 files, 6,240,991,692 bytes
|
||||||
|
|
||||||
|
The relocation manifest contains each source-relative path, byte size, mtime, and SHA-256. The destination was independently rehashed before publication. The local manifest is sufficient to inspect provenance without mounting Optane.
|
||||||
|
|
||||||
|
## Restore
|
||||||
|
|
||||||
|
From the repository root, first verify the manifest and perform a checksum-only dry run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sha256sum -c /home/frank14f/optane/DynamisLab-backups/OID_analysis-archive-20260722/source_manifest.sha256
|
||||||
|
rsync -a --checksum --dry-run /home/frank14f/optane/DynamisLab-backups/OID_analysis-archive-20260722/payload/ ./
|
||||||
|
```
|
||||||
|
|
||||||
|
After reviewing the dry-run output, restore with the same command without `--dry-run`. Never restore over newly generated files; use a clean checkout or review every conflict.
|
||||||
|
|
||||||
|
## Local evidence retained
|
||||||
|
|
||||||
|
- `../provenance/contradictions/steady_reanalysis.json`: small contradiction evidence consumed by the active audit.
|
||||||
|
- `../provenance/parents/illusion_1.0L/`: Illusion parent checkpoint, trajectory, and manifest needed to close formal lineage.
|
||||||
|
|
||||||
|
The formal Kármán branch metadata records checkpoint bundle SHA-256 `2c98a02d6309b597f1aeb927324c4ddd7d318a47ac345fcd70a4b2cd897d83b7`, but that bundle was not present in the development archive at relocation time. This is a fail-closed lineage limitation and must be resolved by creating a new independently initialized parent for subsequent realizations; the old formal run remains regression evidence only.
|
||||||
|
|
||||||
|
## Phase 0 active-runs relocation (2026-07-23)
|
||||||
|
|
||||||
|
The active OID runtime payloads were compacted under the same **copy, independently SHA-256 verify, then remove local payload; local stub; no symlink** convention.
|
||||||
|
|
||||||
|
- Archive root: `/home/frank14f/optane/DynamisLab-backups/OID_analysis-runs-phase0-20260723/`
|
||||||
|
- Payload root: `/home/frank14f/optane/DynamisLab-backups/OID_analysis-runs-phase0-20260723/payload/`
|
||||||
|
- Archive and local inventory: `RUNS_RELOCATION_MANIFEST.json`
|
||||||
|
- Inventory SHA-256: `97a7611a7788d473081fb0c5498fd868d927f055d70910e818a9007eb5e69e67`
|
||||||
|
- Verified archive: 202 files, 71,144,300,428 bytes
|
||||||
|
- Relocated local NPZ payloads: 94 files, 71,144,174,465 bytes
|
||||||
|
- Compact local `runs/` size after relocation: 151,691 logical file bytes (about 792 KiB allocated)
|
||||||
|
- Active evidence entry: `../runs/ACTIVE_EVIDENCE.json`
|
||||||
|
- Active evidence SHA-256 at publication: `1f381aef568e850a2d416a398e0301ad11b244ddc5a9199588a3b25f42a14d87`
|
||||||
|
|
||||||
|
The archive contains the complete pre-relocation `src/OID_analysis/runs` inventory, including the six corrected formal-three paired realizations, six parent bundles, invalid-telemetry quarantine, legacy formal runtime payloads, and rebuildable canonical/analysis payloads. The worktree retains compact branch, paired, parent, twin-gate, canonical, audit, quarantine, and analysis reports. `ACTIVE_EVIDENCE.json` is the single manifest-driven entry for the six trusted paired realizations and their archive-bound payload hashes.
|
||||||
|
|
||||||
|
### Formal COMPLETE status
|
||||||
|
|
||||||
|
No `COMPLETE.json` was created. This is intentional fail-closed behavior. The six `formal-three` realization directories have neither the original `RUNNING.json` state nor a canonical run-level `oid-formal-run/v2` manifest that binds all artifacts and the three-realization contract. Their branch, parent, paired-collection, twin-gate, and canonical-product hashes were verified before relocation, but retroactively inventing an immutable COMPLETE transition would not be faithful. They remain **verified existing lineage, not strict-loader COMPLETE runs**.
|
||||||
|
|
||||||
|
### Verify without restoring
|
||||||
|
|
||||||
|
From the repository root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sha256sum src/OID_analysis/archive/RUNS_RELOCATION_MANIFEST.json
|
||||||
|
sha256sum /home/frank14f/optane/DynamisLab-backups/OID_analysis-runs-phase0-20260723/RUNS_RELOCATION_MANIFEST.json
|
||||||
|
cmp src/OID_analysis/archive/RUNS_RELOCATION_MANIFEST.json \
|
||||||
|
/home/frank14f/optane/DynamisLab-backups/OID_analysis-runs-phase0-20260723/RUNS_RELOCATION_MANIFEST.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Both SHA-256 commands must print `97a7611a7788d473081fb0c5498fd868d927f055d70910e818a9007eb5e69e67`, and `cmp` must produce no output.
|
||||||
|
|
||||||
|
### Restore selected or all runtime payloads
|
||||||
|
|
||||||
|
Always restore into an empty staging directory first; never overwrite the compact evidence tree or newly generated files.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /tmp/oid-runs-restore
|
||||||
|
rsync -a --checksum --dry-run \
|
||||||
|
/home/frank14f/optane/DynamisLab-backups/OID_analysis-runs-phase0-20260723/payload/src/OID_analysis/runs/ \
|
||||||
|
/tmp/oid-runs-restore/
|
||||||
|
rsync -a --checksum \
|
||||||
|
/home/frank14f/optane/DynamisLab-backups/OID_analysis-runs-phase0-20260723/payload/src/OID_analysis/runs/ \
|
||||||
|
/tmp/oid-runs-restore/
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify staged files against `RUNS_RELOCATION_MANIFEST.json` before copying selected payloads back. Paths in the inventory are repository-relative. Review every destination conflict manually; do not use `--delete` and do not restore the archived compact JSON over current compact evidence.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Legacy OID v1 archive
|
||||||
|
|
||||||
|
Archived on 2026-07-22 to prevent unverified results from being confused with the two-scene trusted baseline.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
This directory is historical, read-only, and non-executable. Do not run its scripts, import its modules, or use its numerical claims in a paper. The old pipeline used index-based field subtraction, leaky decomposition fitting, ambiguous signed mode overlap, and a cross-covariance SVD labelled as Schlegel OID without implementing Schlegel et al.'s LR-OID or LE-OID pseudoinverse construction.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
- `analysis/`: old seven-phase CPU analysis and figure generation.
|
||||||
|
- `scripts/`: old GPU collection/replay scripts. They are retained only for provenance and must not be run as the trusted pipeline.
|
||||||
|
- `data_derived/`: generated v1 products and figures. Source raw data remain in the original immutable `data/` tree.
|
||||||
|
- `documents/`: old conclusions, pipeline descriptions, and result tables.
|
||||||
|
- `li22b/`: historical Li22b reproduction scripts. The associated `data_li22b/` tree remains separately marked archive/read-only because the large flow fields have already been removed.
|
||||||
|
|
||||||
|
## Archive-only warning
|
||||||
|
|
||||||
|
Nothing in this archive is an active workflow or supported entrypoint. Do not run archived commands or treat archived documents, scripts, or results as current guidance. For the current active scope and recovery state, read `../../README.md`, `../../SCIENTIFIC_RESET.md`, and `../../HANDOFF.md`. For the July historical study record only, see `../studies/2026-07-two-scene-conditional/STATUS.md`; that status is provenance, not an active pipeline.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Relocated. See ../../RELOCATION.md and ../../RELOCATION_MANIFEST.json.
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# OID_analysis — Observable-Inferred Decomposition for Fluidic Pinball
|
||||||
|
|
||||||
|
Identifies which correction-field structures the DRL controller modulates, ranked by cross-correlation with force and signature observables (not by POD energy).
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Read first: PIPELINE.md pipeline overview, scene table, conventions
|
||||||
|
# Deep dive: OID_knowledge.md rules, results table, bug history
|
||||||
|
# Tasks: OID_notes.md open items, handover
|
||||||
|
# Conclusions: Final_Conclusions.md six key questions answered
|
||||||
|
|
||||||
|
# Run (from repo root):
|
||||||
|
PYTHONPATH="src:$PYTHONPATH" conda run -n sr_env python3 \
|
||||||
|
src/OID_analysis/analysis/run_full_analysis.py --scene karman_re100 --force
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Map
|
||||||
|
|
||||||
|
```
|
||||||
|
src/OID_analysis/
|
||||||
|
├── PIPELINE.md ← START HERE — overview, scene table, conventions
|
||||||
|
├── OID_knowledge.md hard rules, full results, bugs
|
||||||
|
├── OID_notes.md task tracking, open items
|
||||||
|
├── Final_Conclusions.md six key questions answered
|
||||||
|
├── scene_registry.json machine-readable scene index + canonical values
|
||||||
|
├── configs.py single source of truth: 13 scene definitions
|
||||||
|
│
|
||||||
|
├── utils/ core library (CPU, no GPU dependency)
|
||||||
|
│ ├── analysis.py POD, force-OID, sig-OID, PCD, zone stats
|
||||||
|
│ └── cfd_interface.py re-exports from CCD_analysis
|
||||||
|
│
|
||||||
|
├── scripts/ GPU data collection (pycuda_3_10 env)
|
||||||
|
│ ├── collect_empty_channel.py / collect_pinball_baseline.py
|
||||||
|
│ ├── collect_karman_blk.py / collect_disturbance_only.py
|
||||||
|
│ ├── collect_controlled.py / collect_steady_cloak.py
|
||||||
|
│ ├── collect_illusion_qblk.py / collect_target_cylinder.py
|
||||||
|
│ ├── collect_all_data.py batch orchestrator
|
||||||
|
│ └── replay_full_fields.py full-field PPO replay
|
||||||
|
│
|
||||||
|
├── analysis/ CPU analysis pipeline (sr_env)
|
||||||
|
│ ├── phase1_correction_pod.py → phase7_whitebox.py (7 phases)
|
||||||
|
│ ├── robustness_analysis.py / steady_reanalysis.py
|
||||||
|
│ ├── compile_master_table.py / make_figures.py
|
||||||
|
│ └── run_full_analysis.py batch runner
|
||||||
|
│
|
||||||
|
├── data/ raw collected data (NOT committed)
|
||||||
|
│ ├── steady_cloak/ q_in, q_blk, q_ctl
|
||||||
|
│ ├── karman_cloak/ q_in, q_blk, q_ctl
|
||||||
|
│ ├── illusion/ q_ctl (3 diameters)
|
||||||
|
│ ├── target_cylinder/ reference targets
|
||||||
|
│ └── derived/ all computed results + 7 figures
|
||||||
|
│
|
||||||
|
├── papers/ reference papers
|
||||||
|
│ ├── Sch12.md OID original paper
|
||||||
|
│ └── Li22b.md pinball state estimation paper
|
||||||
|
├── docs/
|
||||||
|
│ └── sch12_code_mapping.md Sch12 formula → code traceability
|
||||||
|
├── tests/ unit tests (7/7 pass)
|
||||||
|
└── archive/ deprecated files
|
||||||
|
```
|
||||||
|
|
||||||
|
## Core Results (one table)
|
||||||
|
|
||||||
|
| Finding | Key Value | Confidence |
|
||||||
|
|---------|-----------|------------|
|
||||||
|
| Force-sig monotonic separation | +0.763 → -0.034 → -0.082 → -0.495 → -0.932 | High |
|
||||||
|
| OID beats POD (force prediction) | R²=0.44-0.75 (OID) vs -2.4~0.42 (POD) | High |
|
||||||
|
| OID beats POD (sig prediction) | R²=0.32-0.66 (OID) vs -0.16~0.06 (POD) | High |
|
||||||
|
| Action orthogonal to force+sig | |overlap|<0.33 across all scenes | Confirmed |
|
||||||
|
| Steady cloak RMS reduction | 99.4% | High |
|
||||||
|
| Li22b cross-map | modes 0-5 diagonal 0.81-0.98 | Verified |
|
||||||
|
| SR validation | OID z1 ↔ Cl_tot r=-0.82 | Verified |
|
||||||
|
|
||||||
|
Full numbers: `data/derived/master/master_table.json` · `scene_registry.json`
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
1. OID operates on **Δq_ctl = q_ctl − q_blk**, not raw q_ctl.
|
||||||
|
2. **force-OID and sig-OID reported separately** — divergence is a mechanism result.
|
||||||
|
3. **Illusion q_blk uses separate geometry** (pinball_x=19/20.3, sensor_x=30).
|
||||||
|
4. Fields stored at **full 1280×512**; ROI mask applied only at analysis stage.
|
||||||
|
5. GPU: Karman on device 1, steady/illusion on device 3.
|
||||||
|
6. Conda: `pycuda_3_10` for GPU, `sr_env` for CPU analysis.
|
||||||
|
7. "2U" in model name = S_DIM=14, NOT 2× velocity.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Superseded Sch12 mapping warning
|
||||||
|
|
||||||
|
`sch12_code_mapping.md` is retained only to document an earlier, incorrect interpretation. Its assertion that standardized cross-covariance SVD is Schlegel LR-OID is false: cross-covariance omits the state-covariance inverse and constructs neither the LR nor LE generalized inverse. Use `../../../OID_METHOD.md` and active `../../../v2/analysis.py` instead.
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Sch12 → OID Code Mapping
|
||||||
|
|
||||||
|
> Map from Schlegel et al. (2012) "On least-order flow representations" to `src/OID_analysis/utils/analysis.py`.
|
||||||
|
> Date: 2026-06-28
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OID Variant Identification
|
||||||
|
|
||||||
|
**Our implementation**: Cross-covariance SVD in POD coefficient subspace. Finds directions in r-dimensional POD space that maximize correlation with the observable. Closest to Sch12's **LR-OID (least-residual) / EPOD** approach solved in coefficient space, implemented via Canonical Correlation Analysis restricted to POD subspace.
|
||||||
|
|
||||||
|
**NOT implemented**: Sch12's LE-OID (least-energetic) variant that uses Moore-Penrose pseudoinverse and projects onto row vectors of C (Eq 2.25).
|
||||||
|
|
||||||
|
**Rationale**: LR-OID identifies the most correlated structures for observer design — appropriate for our goal of finding which correction structures most strongly relate to force/signature observables.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Formula → Code Mapping
|
||||||
|
|
||||||
|
| Sch12 Eq | Formula | analysis.py Function | Lines | Implementation Correct? |
|
||||||
|
|----------|---------|---------------------|-------|------------------------|
|
||||||
|
| (2.3) | `Q^Ω = ⟨∫ u'·u' dx⟩` | `compute_pod` (energy from S²) | 66-68 | YES — `energy = S²/ΣS²` |
|
||||||
|
| (2.6a) | `a(t) = [a₁,...,a_N]^T` | `compute_pod` returns `coefs` (N,r) | 73 | YES — POD coefs as (N_samples × r_rank) |
|
||||||
|
| (2.7) | `Q^E(a) = ⟨a·a⟩` | `compute_pod` → `standardize` | 303-309 | YES — standardized to mean=0, std≈1 |
|
||||||
|
| (2.8) | `b = C a` (LSE) | `compute_force_oid` → `C_AY = (1/N)A^T@Y` | 103 | YES — `C_AY` is the LSE estimate of C |
|
||||||
|
| (2.17) | `⟨u⟩ = (1/K)Σ u(t^i)` | `compute_pod` → `mean = np.mean(snapshots, axis=0)` | 35 | YES — arithmetic mean of snapshots |
|
||||||
|
| (2.18) | POD expansion | `compute_pod` → coefs @ modes.T + mean | 35,73 | YES — snapshot-to-coef representation |
|
||||||
|
| (2.20) | `(u',v')_A = (C_A u', C_A v')_Γ` | `compute_force_oid` cross-covariance | 103-105 | YES — approximation via C_AY SVD |
|
||||||
|
| (2.22) | `R_u^OID = (1/K)[C a^j · C a^k]` | OID coords `z = A @ U` | 105 | Equivalent in coefficient space |
|
||||||
|
| (2.23) | Eigenvalue eq `R_u^OID c^[i] = λ_i^p c^[i]` | `SVD(C_AY)` → S, U | 104-105 | YES — SVD solves it directly |
|
||||||
|
| (2.24) | `u_i^A = Σ d_j^[i] u^j` | `reconstruct_oid_modes(pod_modes, U)` | 327 | YES — `psi_OID = Phi @ U` |
|
||||||
|
| §2.6 | Time delay τ | `phase2_build_observables.py` → `sensor_error_delayed` | — | YES — tau_c shifts observable |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cross-Covariance vs Expectation
|
||||||
|
|
||||||
|
Sch12 uses expectation `⟨b a^T⟩` which estimates C in (2.8). Our `(1/N)*A^T@Y` is the sample estimate of this expectation from N snapshots. **Mathematically equivalent** for empirical data.
|
||||||
|
|
||||||
|
## Standardization
|
||||||
|
|
||||||
|
Sch12 POD coefs are zero-mean by construction (POD removes mean). We additionally standardize to unit variance:
|
||||||
|
```python
|
||||||
|
std = np.where(std < 1e-12, 1.0, std) # prevent div-by-zero
|
||||||
|
X_std = (X - mean) / std
|
||||||
|
```
|
||||||
|
This is a standard preprocessing step that ensures each POD coefficient (and observable channel) contributes equally to the cross-covariance, regardless of physical units. **Not specified in Sch12 but mathematically valid** — it's equivalent to using a weighted inner product.
|
||||||
|
|
||||||
|
## PCD Whitening
|
||||||
|
|
||||||
|
```python
|
||||||
|
C_AA_inv_half = sqrtm(inv(C_AA_reg)).real
|
||||||
|
C_PP_inv_half = sqrtm(inv(C_PP_reg)).real
|
||||||
|
K = C_AA_inv_half @ C_AP @ C_PP_inv_half
|
||||||
|
```
|
||||||
|
This is standard **Canonical Correlation Analysis** (CCA) restricted to POD subspace. The Tikhonov regularization (`+eps*eye(r)`) ensures numerical stability for near-singular covariance matrices. Taking `.real` after `sqrtm` handles any negligible imaginary components from numerical rounding.
|
||||||
|
|
||||||
|
## POD: Method of Snapshots
|
||||||
|
|
||||||
|
When N_samples < N_DOF:
|
||||||
|
```python
|
||||||
|
C = Q @ Q.T # (N, N) — much smaller than (DOF, DOF)
|
||||||
|
eigvals, eigvecs = eigh(C)
|
||||||
|
modes = (Q.T @ eigvecs) / S
|
||||||
|
```
|
||||||
|
Standard method-of-snapshots. The division by S+1e-30 is the correct recovery formula: `phi_i = (1/σ_i) * Q^T * v_i`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Known Deviations from Sch12
|
||||||
|
|
||||||
|
1. **No Moore-Penrose pseudoinverse**: We don't implement `C^- q_i` (Eq 2.14). Instead we compute directions in POD space via cross-covariance SVD. This is the LR-OID variant solved in coefficient space, not the LE-OID variant.
|
||||||
|
|
||||||
|
2. **No OID snapshot matrix**: We don't compute `R_u^OID` (Eq 2.22) in physical snapshot space. We operate entirely in POD coefficient space, which is equivalent due to the orthogonality of POD modes.
|
||||||
|
|
||||||
|
3. **Standardization not in Sch12**: We z-score both A and Y. Sch12 assumes POD coefs have intrinsic energy weighting. Our standardization removes this weighting, making it a pure correlation-based analysis.
|
||||||
|
|
||||||
|
4. **PCD has no direct Sch12 equivalent**: The whitened CCA (`compute_pcd`) extends OID by equalizing variance across all POD coefs and observable channels before computing cross-correlation. This is a natural extension but not described in Sch12.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
The implementation is **mathematically sound and correctly applies the Sch12 framework**. The key insight is that we implement the LR-OID (least-residual / EPOD) variant via cross-covariance SVD in POD coefficient space, which is the most appropriate variant for our goal of identifying observable-relevant correction structures. The deviation from Sch12's LE-OID is intentional — LE-OID minimizes energy (suited for control design), while LR-OID maximizes correlation (suited for structure identification, which is our goal).
|
||||||
@@ -108,9 +108,11 @@ def test_oid_field_reconstruction():
|
|||||||
|
|
||||||
# Reconstruct in standardized space: z @ psi_OID.T = A_std @ U @ U^T @ Phi.T
|
# Reconstruct in standardized space: z @ psi_OID.T = A_std @ U @ U^T @ Phi.T
|
||||||
# Since U is square orthogonal, this = A_std @ Phi.T
|
# Since U is square orthogonal, this = A_std @ Phi.T
|
||||||
psi_oid = reconstruct_oid_modes(Phi, U) # (DOF, r)
|
# Standardized POD coordinates map to physical directions through Phi D U.
|
||||||
q_std_oid = z @ psi_oid.T # standardized reconstruction
|
_, _, a_scale = standardize(A)
|
||||||
q_std_direct = A_std @ Phi.T # direct standardized reconstruction
|
psi_oid = reconstruct_oid_modes(Phi, U, a_scale) # (DOF, r)
|
||||||
|
q_std_oid = z @ psi_oid.T
|
||||||
|
q_std_direct = A_std @ np.diag(a_scale.reshape(-1)) @ Phi.T
|
||||||
rel_err = np.mean((q_std_oid - q_std_direct)**2) / \
|
rel_err = np.mean((q_std_oid - q_std_direct)**2) / \
|
||||||
(np.mean(q_std_direct**2) + 1e-30)
|
(np.mean(q_std_direct**2) + 1e-30)
|
||||||
assert rel_err < 1e-6, f"Reconstruction error: {rel_err:.10f}"
|
assert rel_err < 1e-6, f"Reconstruction error: {rel_err:.10f}"
|
||||||
@@ -57,13 +57,20 @@ def compute_pod(
|
|||||||
modes = Vt.T # (DOF, N)
|
modes = Vt.T # (DOF, N)
|
||||||
coefs = U * S # (N, N)
|
coefs = U * S # (N, N)
|
||||||
|
|
||||||
# Truncate
|
# Energy is always normalized by the complete snapshot spectrum. The old
|
||||||
if rank is not None and rank < N:
|
# implementation normalized after truncation, making every retained rank
|
||||||
|
# incorrectly appear to capture 100% of fluctuation energy.
|
||||||
|
total_energy = np.sum(S ** 2)
|
||||||
|
|
||||||
|
# Truncate only after preserving the full-spectrum denominator.
|
||||||
|
max_rank = min(N, DOF)
|
||||||
|
if rank is not None:
|
||||||
|
if type(rank) is not int or rank < 1 or rank > max_rank:
|
||||||
|
raise ValueError(f"rank must be in [1, {max_rank}]")
|
||||||
modes = modes[:, :rank]
|
modes = modes[:, :rank]
|
||||||
S = S[:rank]
|
S = S[:rank]
|
||||||
coefs = coefs[:, :rank]
|
coefs = coefs[:, :rank]
|
||||||
|
|
||||||
total_energy = np.sum(S ** 2)
|
|
||||||
energy = (S ** 2) / total_energy if total_energy > 0 else np.zeros_like(S)
|
energy = (S ** 2) / total_energy if total_energy > 0 else np.zeros_like(S)
|
||||||
cum_energy = np.cumsum(energy)
|
cum_energy = np.cumsum(energy)
|
||||||
|
|
||||||
@@ -312,19 +319,23 @@ def standardize(X: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|||||||
def reconstruct_oid_modes(
|
def reconstruct_oid_modes(
|
||||||
pod_modes: np.ndarray,
|
pod_modes: np.ndarray,
|
||||||
U_oid: np.ndarray,
|
U_oid: np.ndarray,
|
||||||
|
coefficient_scale: Optional[np.ndarray] = None,
|
||||||
) -> np.ndarray:
|
) -> np.ndarray:
|
||||||
"""Reconstruct OID spatial modes from POD modes and OID rotation.
|
"""Reconstruct physical PLS-type directions from standardized POD space.
|
||||||
|
|
||||||
psi_k_OID = sum_j U_{jk} * phi_j
|
If decomposition directions ``U_oid`` were fitted to standardized POD
|
||||||
|
coefficients ``(A - mean) / scale``, the physical field direction is
|
||||||
Args:
|
``Phi @ diag(scale) @ U``. ``coefficient_scale=None`` is retained only for
|
||||||
pod_modes: (DOF, r) POD spatial modes
|
legacy callers whose coefficients were not standardized.
|
||||||
U_oid: (r, r) OID rotation matrix
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
oid_modes: (DOF, r) OID spatial modes
|
|
||||||
"""
|
"""
|
||||||
return pod_modes @ U_oid
|
modes = np.asarray(pod_modes)
|
||||||
|
directions = np.asarray(U_oid)
|
||||||
|
if coefficient_scale is None:
|
||||||
|
return modes @ directions
|
||||||
|
scale = np.asarray(coefficient_scale).reshape(-1)
|
||||||
|
if modes.shape[1] != len(scale) or directions.shape[0] != len(scale):
|
||||||
|
raise ValueError("pod_modes, coefficient_scale, and U_oid dimensions differ")
|
||||||
|
return modes @ (scale[:, None] * directions)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"multi_gpu": false,
|
||||||
|
"gpu_connection": "NVLink",
|
||||||
|
"required_cuda_capability": "7.0",
|
||||||
|
"threads_per_block": 128,
|
||||||
|
"X_1U": 128,
|
||||||
|
"Y_1U": 32,
|
||||||
|
"Z_1U": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"data_type": "FP32",
|
||||||
|
"dimensionality": 2,
|
||||||
|
"lattice": 9,
|
||||||
|
"field_dim_in_U": [10, 16, 1],
|
||||||
|
"viscosity": 0.004,
|
||||||
|
"velocity": 0.01,
|
||||||
|
"boundary_conditions": {
|
||||||
|
"x": ["parabolic", "outflow"],
|
||||||
|
"y": ["noslip", "noslip"],
|
||||||
|
"z": ["none", "none"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 260 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 270 KiB |
|
After Width: | Height: | Size: 314 KiB |
|
After Width: | Height: | Size: 375 KiB |
|
After Width: | Height: | Size: 292 KiB |
|
After Width: | Height: | Size: 310 KiB |
|
After Width: | Height: | Size: 289 KiB |
|
After Width: | Height: | Size: 161 KiB |
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"scene_id": "target_cylinder",
|
||||||
|
"target_diameter": 0.75,
|
||||||
|
"re_code": 100,
|
||||||
|
"has_disturbance": false,
|
||||||
|
"sample_interval": 400,
|
||||||
|
"conv_len": 36,
|
||||||
|
"source": "open_loop",
|
||||||
|
"model_name": null,
|
||||||
|
"n_objects_env": 4,
|
||||||
|
"obs_slice": [
|
||||||
|
0,
|
||||||
|
8
|
||||||
|
],
|
||||||
|
"sensor_x": 40.0,
|
||||||
|
"cylinder_x": 30.65,
|
||||||
|
"target_type": "periodic",
|
||||||
|
"s_dim": null,
|
||||||
|
"u0": 0.01,
|
||||||
|
"nu": 0.004,
|
||||||
|
"St": 0.1275,
|
||||||
|
"f_dom": 8.5e-05,
|
||||||
|
"n_steps": 500
|
||||||
|
}
|
||||||