# 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).
