Compare commits

..

2 Commits

Author SHA1 Message Date
Frank14f
4360bb2047 feat(ccd): light reorganization — results/ dir, PIPELINE.md, numbered figures
- Add PIPELINE.md as primary entry (pipeline overview, conventions, results index,
  new training integration guide)
- Create results/ directory with numbered symlinks to all 16 core figures
- Copy final CCD JSON into results/ccd/
- Rewrite README.md to point to PIPELINE.md

Design follows SR_analysis lightweight pattern: clear structure,
small number of entry files, results/ as canonical output directory.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-05 18:15:12 +08:00
Frank14f
44f7687509 refactor(oid): lightweight reorganization — PIPELINE.md, scene_registry.json, move papers
- Move Sch12.md, Li22b.md → papers/ (reference papers, not code docs)
- Add PIPELINE.md — pipeline overview, scene table, conventions, quick commands
- Add scene_registry.json — machine-readable index (from configs.py + master_table.json)
- Rewrite README.md — lean entry point with file map + core results table

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-05 18:09:02 +08:00
25 changed files with 1729 additions and 220 deletions

View File

@ -0,0 +1,160 @@
# CCD Analysis Pipeline
> Correction-field CCD analysis for fluidic pinball DRL control.
> Core question: does `dq_ctl` (what the controller adds) match `dq_tar` (what the target requires)?
## Quick Start
```bash
cd src/CCD_analysis
# Panorama comparison figure (primary output)
conda run -n pycuda_3_10 python3 correction_analysis/compare_dqctl_scenes.py
# CCD quantitative decomposition
conda run -n pycuda_3_10 python3 correction_analysis/decompose_corrections.py
# Single-scene zone diagnostics
conda run -n pycuda_3_10 python3 correction_analysis/diagnose_corrections.py
```
## Pipeline Architecture
```
[Data Collection] [Phase Alignment] [Correction Fields] [Analysis]
scripts/collect_*.py → detect_period.py → compute_correction_ → compare_dqctl_scenes.py
(GPU, device 2) replay_fields.py fields.py decompose_corrections.py
(CPU/GPU) (CPU) diagnose_corrections.py
(CPU)
```
## Key Conventions
### Geometry (unified 2026-06-25)
- **All scenes**: pinball center ≈ 613 px, sensors at 800 px (40×L0)
- Collected at source — no post-processing translation needed
### Correction Fields
- **`dq_blk = q_blk q_in`**: pinball blockage (passive)
- **`dq_ctl = q_ctl q_blk`**: control correction (active) — **primary analysis object**
- **`dq_tar = q_tar q_blk`**: target correction (theoretical)
- Core question: **O(dq_ctl, dq_tar)** — how well does control match theory?
### Observation Normalization
- **Force-first**: `obs = [forces/force_norm, sensors/sens_norm]`
- Each scene computes its own `force_norm_fact`, `sens_deviation`, `sens_norm_fact` during collection
- Same norm values MUST be used during inference
### Reynolds Number
- Code Re uses reference length 2D = 40: `Re = U0×40/ν`
- Physical Re_D uses D = 20: `Re_D = Re/2`
- Default: Re=100 → Re_D=50, nu=0.004
### Inlet
- Parabolic velocity profile (not uniform)
- Top/bottom walls: no-slip bounce-back
- U0 = 0.01 (centerline, lattice units)
### Vortex Bug History (2026-06-29)
Three bugs in `collect_vortex.py` caused incorrect Lamb data:
1. **Cylinder order**: add order is front→TOP(+y)→BOTTOM(y); reversed caused wrong bias mapping
2. **Obs swap**: used `[sensors/force_norm, forces/sens_norm]` instead of force-first `[forces/force_norm, sensors/sens_norm]`
3. **Missing fade-in/out**: 25-step transition from steady-cloak bias to PPO action required
See `collect_vortex.py` header and `ccd_knowledge.md` §12 for full details.
## Results Index
All figures in `results/figures/`; CCD JSON in `results/ccd/`.
### Panorama (main deliverable)
| # | Figure | Content |
|---|--------|---------|
| 01 | `01_panorama_all_scenes.png` | 7 scenes × 4 quantities (ux_mean, uy_mean, RMS, vorticity) |
| 02 | `02_cloak_comparison.png` | 4 cloak scenes (steady, karman, vortex_lamb, vortex_taylor) |
| 03 | `03_illusion_comparison.png` | 3 illusion scenes (0.75L, 1.0L, 1.5L) |
### Per-Scene dq_ctl vs dq_tar
| # | Figure | Scene |
|---|--------|-------|
| 04 | `04_steady_cloak_cancel.png` | Steady cloak cancellation test |
| 05 | `05_illusion_075L_ctl_vs_tar.png` | Illusion 0.75L |
| 06 | `06_illusion_10L_ctl_vs_tar.png` | Illusion 1.0L |
| 07 | `07_illusion_15L_ctl_vs_tar.png` | Illusion 1.5L |
| 08 | `08_karman_ctl_vs_tar.png` | Karman cloak re100 |
| 09 | `09_vortex_lamb_ctl_vs_tar.png` | Vortex Lamb |
| 10 | `10_vortex_taylor_ctl_vs_tar.png` | Vortex Taylor |
### Vortex Diagnosis
| # | Figure | Content |
|---|--------|---------|
| 11 | `11_vortex_lamb_diagnosis.png` | Lamb sensor + action comparison |
| 12 | `12_vortex_taylor_diagnosis.png` | Taylor sensor + action comparison |
| 13 | `13_vortex_lamb_vorticity.png` | Lamb vorticity field evolution |
| 14 | `14_vortex_taylor_vorticity.png` | Taylor vorticity field evolution |
| 15 | `15_vortex_lamb_target_vorticity.png` | Lamb target (no pinball) reference |
| 16 | `16_vortex_taylor_target_vorticity.png` | Taylor target (no pinball) reference |
### Key Numerical Results
**Correction-field CCD (2026-06-28, unified geometry)**
| Metric | 0.75L | 1.0L | 1.5L |
|--------|:-----:|:----:|:----:|
| O(dq_ctl, dq_tar) mode1 (r=6) | 0.383 | **0.926** | **0.922** |
| O(dq_ctl, dq_tar) mode1 (r=10) | 0.320 | 0.684 | 0.661 |
| Force-CCD m80 (r=6) | 2 | 2 | 1 |
| Action sigma1 (r=6) | 1.49 | 1.17 | **0.20** |
**Cloak dq_ctl RMS (cropped x=[300,1100])**
| Scene | RMS | Type |
|-------|:---:|------|
| steady_cloak | 0.196 | Steady, open-loop |
| karman_re100 | 0.397 | Periodic, PPO closed-loop |
| vortex_lamb | 0.146 | Transient, PPO closed-loop |
| vortex_taylor | 0.188 | Transient, PPO closed-loop |
**Key findings:**
- Cloak mechanism is **independent of upstream condition** (steady/vortex street/transient vortex all share the same dq_ctl structure)
- Illusion 1.0L achieves near-perfect overlap (O=0.926) via "cloak physics + target frequency modulation"
- Illusion 1.5L uses a fundamentally different mechanism (high-freq modulation, action sigma1=0.20 vs 1.17-1.49)
- 0.75L overlap dropped from 0.564→0.383 after fixing geometry alignment — old number was inflated
### CCD Quantitative (JSON)
| File | Content |
|------|---------|
| `results/ccd/correction_ccd_results.json` | Force/Action-CCD per scene (r=6,8,10), O(dq_ctl,dq_tar) per mode |
| `results/ccd/zone_metrics.json` | Per-zone KE and enstrophy |
## Full Documentation
| File | Content |
|------|---------|
| `PIPELINE.md` | This file — pipeline overview, conventions, results index |
| `README.md` | Quick start |
| `ccd_knowledge.md` | Complete knowledge base (theory, methodology, detailed results, bug history) |
| `Lyu23.md` | CCD method paper (Lyu 2023) |
## Adding New Training Results
When new DRL models are trained (e.g. on new CelerisLab solver):
1. **Collect fields**: Run the appropriate `scripts/collect_*.py` with the new model path
2. **Phase alignment**: `detect_period.py``replay_fields.py` (for periodic scenes)
3. **Correction fields**: Already handled by `compute_correction_fields.py` — just ensure the scene name is registered in `configs.py` `_SCENE_MAP`
4. **Regenerate figures**: `compare_dqctl_scenes.py` and `diagnose_corrections.py`
5. **Regenerate CCD**: `decompose_corrections.py`
6. **Symlink**: Add new figures to `results/figures/`
The `configs.py` `SCENES` dict is the single source of truth — add new scenes there and all analysis scripts automatically pick them up.
## Environment
- **Conda**: `pycuda_3_10`
- **GPU**: Device 2 (check with `nvidia-smi` before collection)
- **CPU-only steps**: POD, CCD, analysis scripts

View File

@ -2,84 +2,54 @@
Analyzes DRL-controlled fluidic pinball using **correction-field decomposition** + **Canonical Correlation Decomposition (CCD/Lyu23)**. Core question: does `dq_ctl` (what the controller adds) match `dq_tar` (what the target requires)?
## Quick Start
## Start Here
**→ [`PIPELINE.md`](PIPELINE.md)** — pipeline overview, results index, conventions, new training integration guide.
## Quick Commands
```bash
# 1. Panorama comparison figure (primary output)
cd src/CCD_analysis
# Panorama comparison (main output)
conda run -n pycuda_3_10 python3 correction_analysis/compare_dqctl_scenes.py
# 2. CCD quantitative decomposition
# CCD quantitative analysis
conda run -n pycuda_3_10 python3 correction_analysis/decompose_corrections.py
# 3. Single-scene diagnostics (zone metrics)
# Zone diagnostics
conda run -n pycuda_3_10 python3 correction_analysis/diagnose_corrections.py
```
## Pipeline Architecture
```
scripts/collect_*.py → scripts/{detect_period,replay_fields}.py
(GPU采集) (phase alignment)
correction_analysis/compute_correction_fields.py
(dq_blk, dq_ctl, dq_tar)
┌────┴────┬────────────┐
↓ ↓ ↓
compare_ decompose_ diagnose_
dqctl_ corrections corrections
scenes.py .py .py
(全景对比) (CCD定量) (zone诊断)
```
## Directory Structure
## Directory
```
CCD_analysis/
README.md # 本文件
ccd_knowledge.md # 唯一知识库 (理论, 结果, 操作流程, bug经验)
Lyu23.md # CCD 方法文献
configs.py # 场景元数据 (统一几何)
utils/
resampling.py # POD, CCD, 场加载
cfd_interface.py # LegacyCelerisLab 封装 (GPU) + build_observation
field_translate.py # 场平移 (备用)
load_vortex_fields.py # 瞬态 vortex 场加载
scripts/
collect_*.py # GPU 数据采集
detect_period.py # 周期检测 → phase_plan.json
replay_fields.py # 场回放 → fields_aligned.npz
correction_analysis/
compute_correction_fields.py # dq 计算 (核心)
compare_dqctl_scenes.py # 全景对比图 (核心输出)
decompose_corrections.py # CCD 定量 (POD + force/action CCD)
diagnose_corrections.py # 单场景 zone 诊断
run_signature_line.py # Signature CCD
run_zone_ccd.py # Zone-restricted CCD
run_steady_metrics.py # Steady cloak 定量
run_15L_correction.py # 1.5L 专项
visualize_action_ccd.py # Action-CCD mode1 可视化
process_legacy_steady.py # 旧格式加载
ccd/ # Round 5 冻结基线 (勿改)
data/
figures/ # 诊断图 (仅保留核心对比图)
ccd/ # JSON 结果
old_data/ # 归档 (废弃脚本/旧报告/旧数据)
README.md # This file
PIPELINE.md # Primary entry — overview, results, conventions
ccd_knowledge.md # Full knowledge base (theory, methods, bug history)
Lyu23.md # CCD method paper
configs.py # Scene registry (single source of truth)
results/ # Canonical outputs
figures/ # Symlinks to all core figures (numbered)
ccd/ # Final CCD JSON results
correction_analysis/ # All analysis scripts
scripts/ # GPU data collection + phase alignment
utils/ # Core algorithms (POD, CCD, field loading)
data/ # Raw data + generated figures
ccd/ # Round 5 frozen baseline (do not modify)
```
## Key Documentation
| File | Content |
|------|---------|
| `ccd_knowledge.md` | **Primary entry** — theory, results, conventions, bug history |
| `Lyu23.md` | CCD method paper (Lyu 2023) |
| `data/old_data/ccd_correction_field_report.md` | Old full report (archived reference) |
| `data/old_data/ccd_handover.md` | Old handover notes (archived) |
| `PIPELINE.md` | **Primary entry** — pipeline, convention, results index |
| `ccd_knowledge.md` | Complete knowledge base (509 lines) |
| `results/figures/` | All core figures with numerical prefix ordering |
## Key Conventions
## Environment
- **Main analysis object**: `dq_ctl` (not raw `q_ctl`)
- **Observation order**: `[forces/force_norm, sensors/sens_norm]` — force first (see Bug 3 in ccd_knowledge.md §12)
- **All scenes unified geometry**: pinball center at 613px, sensors at 800px
- **Environment**: `conda run -n pycuda_3_10`
- **GPU**: Device 2 for collection
```
conda run -n pycuda_3_10
```

View File

@ -0,0 +1,731 @@
{
"illusion_0.75L_dqctl_force_fy_r6": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "force_fy",
"r": 6,
"m80": 2,
"N": 6,
"sigma_top3": [
0.9363693259668311,
0.5427124016524685,
0.02867293614015484
],
"special_mechanism": false
},
"illusion_0.75L_dqctl_force_fx_r6": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "force_fx",
"r": 6,
"m80": 2,
"N": 6,
"sigma_top3": [
0.8887476512241682,
0.7024685939692135,
0.17373174245109088
],
"special_mechanism": false
},
"illusion_0.75L_dqctl_action_r6": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "action",
"r": 6,
"m80": 2,
"N": 6,
"sigma_top3": [
1.4929827205494475,
1.0564978623336443,
0.3536240923864859
],
"special_mechanism": false
},
"illusion_0.75L_dqtar_force_fy_r6": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "force_fy_tar",
"r": 6,
"m80": 2,
"N": 6,
"sigma_top3": [
0.9557871261173136,
0.5104093433240032,
0.00272381506789553
],
"special_mechanism": false
},
"illusion_0.75L_O_dqctl_vs_dqtar_r6_mode1": {
"overlap": 0.38274004938666406,
"mode": 1,
"r": 6
},
"illusion_0.75L_O_dqctl_vs_dqtar_r6_mode2": {
"overlap": 0.3742735673782987,
"mode": 2,
"r": 6
},
"illusion_0.75L_O_dqctl_vs_dqtar_r6_mode3": {
"overlap": 0.4160221885366777,
"mode": 3,
"r": 6
},
"illusion_0.75L_O_dqctl_vs_dqtar_r6_mode4": {
"overlap": 0.7933885479724803,
"mode": 4,
"r": 6
},
"illusion_0.75L_O_dqctl_vs_dqtar_r6_mode5": {
"overlap": 0.6200548221579631,
"mode": 5,
"r": 6
},
"illusion_0.75L_dqctl_force_fy_r8": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "force_fy",
"r": 8,
"m80": 2,
"N": 7,
"sigma_top3": [
1.1478269894791648,
0.652863182963739,
0.028786077604042823
],
"special_mechanism": false
},
"illusion_0.75L_dqctl_force_fx_r8": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "force_fx",
"r": 8,
"m80": 2,
"N": 7,
"sigma_top3": [
0.9786149641068052,
0.7691113807859654,
0.188609876636516
],
"special_mechanism": false
},
"illusion_0.75L_dqctl_action_r8": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "action",
"r": 8,
"m80": 2,
"N": 8,
"sigma_top3": [
1.8212349775439216,
1.2876771686498985,
0.36519455351462626
],
"special_mechanism": false
},
"illusion_0.75L_dqtar_force_fy_r8": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "force_fy_tar",
"r": 8,
"m80": 2,
"N": 7,
"sigma_top3": [
0.9559584636516365,
0.5114433620666231,
0.020016175925514433
],
"special_mechanism": false
},
"illusion_0.75L_O_dqctl_vs_dqtar_r8_mode1": {
"overlap": 0.32804391166994556,
"mode": 1,
"r": 8
},
"illusion_0.75L_O_dqctl_vs_dqtar_r8_mode2": {
"overlap": 0.3279264837693497,
"mode": 2,
"r": 8
},
"illusion_0.75L_O_dqctl_vs_dqtar_r8_mode3": {
"overlap": 0.04479564315361202,
"mode": 3,
"r": 8
},
"illusion_0.75L_O_dqctl_vs_dqtar_r8_mode4": {
"overlap": 0.345183841487867,
"mode": 4,
"r": 8
},
"illusion_0.75L_O_dqctl_vs_dqtar_r8_mode5": {
"overlap": 0.4363341302545839,
"mode": 5,
"r": 8
},
"illusion_0.75L_dqctl_force_fy_r10": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "force_fy",
"r": 10,
"m80": 2,
"N": 7,
"sigma_top3": [
1.2041535893498374,
0.6687434874320507,
0.033382686367867884
],
"special_mechanism": false
},
"illusion_0.75L_dqctl_force_fx_r10": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "force_fx",
"r": 10,
"m80": 2,
"N": 7,
"sigma_top3": [
1.021082402461413,
0.7906946910917539,
0.19569390445830676
],
"special_mechanism": false
},
"illusion_0.75L_dqctl_action_r10": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "action",
"r": 10,
"m80": 2,
"N": 10,
"sigma_top3": [
1.873772820479455,
1.3476153959060446,
0.3912418253445644
],
"special_mechanism": false
},
"illusion_0.75L_dqtar_force_fy_r10": {
"scene": "illusion_0.75L",
"diam": 0.75,
"obs": "force_fy_tar",
"r": 10,
"m80": 2,
"N": 7,
"sigma_top3": [
0.95651717599455,
0.5116328905682535,
0.02153808322188994
],
"special_mechanism": false
},
"illusion_0.75L_O_dqctl_vs_dqtar_r10_mode1": {
"overlap": 0.3200853391492714,
"mode": 1,
"r": 10
},
"illusion_0.75L_O_dqctl_vs_dqtar_r10_mode2": {
"overlap": 0.3169378757210263,
"mode": 2,
"r": 10
},
"illusion_0.75L_O_dqctl_vs_dqtar_r10_mode3": {
"overlap": 0.07447905724690952,
"mode": 3,
"r": 10
},
"illusion_0.75L_O_dqctl_vs_dqtar_r10_mode4": {
"overlap": 0.05289143799442159,
"mode": 4,
"r": 10
},
"illusion_0.75L_O_dqctl_vs_dqtar_r10_mode5": {
"overlap": 0.3699778630857817,
"mode": 5,
"r": 10
},
"illusion_1.0L_dqctl_force_fy_r6": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "force_fy",
"r": 6,
"m80": 2,
"N": 6,
"sigma_top3": [
0.9133527075400956,
0.5492417355984791,
0.03698984539919565
],
"special_mechanism": false
},
"illusion_1.0L_dqctl_force_fx_r6": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "force_fx",
"r": 6,
"m80": 2,
"N": 6,
"sigma_top3": [
0.9627700852255946,
0.575350111829231,
0.12210932634955265
],
"special_mechanism": false
},
"illusion_1.0L_dqctl_action_r6": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "action",
"r": 6,
"m80": 3,
"N": 6,
"sigma_top3": [
1.1703371479902003,
0.928425799748123,
0.6993315776263908
],
"special_mechanism": false
},
"illusion_1.0L_dqtar_force_fy_r6": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "force_fy_tar",
"r": 6,
"m80": 2,
"N": 6,
"sigma_top3": [
0.9230800944823152,
0.5409412328754315,
0.013881195337352553
],
"special_mechanism": false
},
"illusion_1.0L_O_dqctl_vs_dqtar_r6_mode1": {
"overlap": 0.9255017481198297,
"mode": 1,
"r": 6
},
"illusion_1.0L_O_dqctl_vs_dqtar_r6_mode2": {
"overlap": 0.9092817930388517,
"mode": 2,
"r": 6
},
"illusion_1.0L_O_dqctl_vs_dqtar_r6_mode3": {
"overlap": 0.5921722575222331,
"mode": 3,
"r": 6
},
"illusion_1.0L_O_dqctl_vs_dqtar_r6_mode4": {
"overlap": 0.3695873593868685,
"mode": 4,
"r": 6
},
"illusion_1.0L_O_dqctl_vs_dqtar_r6_mode5": {
"overlap": 0.49273985058737546,
"mode": 5,
"r": 6
},
"illusion_1.0L_dqctl_force_fy_r8": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "force_fy",
"r": 8,
"m80": 2,
"N": 7,
"sigma_top3": [
1.0512290431026903,
0.6038168034405464,
0.038604687614151825
],
"special_mechanism": false
},
"illusion_1.0L_dqctl_force_fx_r8": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "force_fx",
"r": 8,
"m80": 2,
"N": 7,
"sigma_top3": [
1.024212111702677,
0.6235362523473601,
0.1398413327047235
],
"special_mechanism": false
},
"illusion_1.0L_dqctl_action_r8": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "action",
"r": 8,
"m80": 3,
"N": 8,
"sigma_top3": [
1.289052813250248,
0.9661024499957929,
0.7306012213889583
],
"special_mechanism": false
},
"illusion_1.0L_dqtar_force_fy_r8": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "force_fy_tar",
"r": 8,
"m80": 2,
"N": 7,
"sigma_top3": [
0.9296477714715806,
0.5431506822355127,
0.013939617415588563
],
"special_mechanism": false
},
"illusion_1.0L_O_dqctl_vs_dqtar_r8_mode1": {
"overlap": 0.7335300559150799,
"mode": 1,
"r": 8
},
"illusion_1.0L_O_dqctl_vs_dqtar_r8_mode2": {
"overlap": 0.7735101257517341,
"mode": 2,
"r": 8
},
"illusion_1.0L_O_dqctl_vs_dqtar_r8_mode3": {
"overlap": 0.5847457198896681,
"mode": 3,
"r": 8
},
"illusion_1.0L_O_dqctl_vs_dqtar_r8_mode4": {
"overlap": 0.361282468563919,
"mode": 4,
"r": 8
},
"illusion_1.0L_O_dqctl_vs_dqtar_r8_mode5": {
"overlap": 0.024744386112383842,
"mode": 5,
"r": 8
},
"illusion_1.0L_dqctl_force_fy_r10": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "force_fy",
"r": 10,
"m80": 2,
"N": 7,
"sigma_top3": [
1.1068219379848616,
0.6279999575810425,
0.03877250537017653
],
"special_mechanism": false
},
"illusion_1.0L_dqctl_force_fx_r10": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "force_fx",
"r": 10,
"m80": 2,
"N": 7,
"sigma_top3": [
1.0798028524010137,
0.6538475361620503,
0.14841911584738715
],
"special_mechanism": false
},
"illusion_1.0L_dqctl_action_r10": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "action",
"r": 10,
"m80": 3,
"N": 10,
"sigma_top3": [
1.3276794140080077,
1.0190742997211644,
0.7375547554838907
],
"special_mechanism": false
},
"illusion_1.0L_dqtar_force_fy_r10": {
"scene": "illusion_1.0L",
"diam": 1.0,
"obs": "force_fy_tar",
"r": 10,
"m80": 2,
"N": 7,
"sigma_top3": [
0.9303683839417445,
0.5438911279398368,
0.015264317923416145
],
"special_mechanism": false
},
"illusion_1.0L_O_dqctl_vs_dqtar_r10_mode1": {
"overlap": 0.6843033711672328,
"mode": 1,
"r": 10
},
"illusion_1.0L_O_dqctl_vs_dqtar_r10_mode2": {
"overlap": 0.7092374384087569,
"mode": 2,
"r": 10
},
"illusion_1.0L_O_dqctl_vs_dqtar_r10_mode3": {
"overlap": 0.5408170275775555,
"mode": 3,
"r": 10
},
"illusion_1.0L_O_dqctl_vs_dqtar_r10_mode4": {
"overlap": 0.2185189595791332,
"mode": 4,
"r": 10
},
"illusion_1.0L_O_dqctl_vs_dqtar_r10_mode5": {
"overlap": 0.19476801271238336,
"mode": 5,
"r": 10
},
"illusion_1.5L_dqctl_force_fy_r6": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "force_fy",
"r": 6,
"m80": 1,
"N": 6,
"sigma_top3": [
0.9147833091150019,
0.45038325163212484,
0.03489015873428606
],
"special_mechanism": true
},
"illusion_1.5L_dqctl_force_fx_r6": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "force_fx",
"r": 6,
"m80": 2,
"N": 6,
"sigma_top3": [
0.24320738039972734,
0.21287788693838852,
0.042222381595628855
],
"special_mechanism": true
},
"illusion_1.5L_dqctl_action_r6": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "action",
"r": 6,
"m80": 2,
"N": 6,
"sigma_top3": [
0.20100087148018667,
0.16474318694556772,
0.06287962977503929
],
"special_mechanism": true
},
"illusion_1.5L_dqtar_force_fy_r6": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "force_fy_tar",
"r": 6,
"m80": 2,
"N": 6,
"sigma_top3": [
0.9652520561509902,
0.5009040282539201,
0.03619846551092434
],
"special_mechanism": true
},
"illusion_1.5L_O_dqctl_vs_dqtar_r6_mode1": {
"overlap": 0.9219537578400411,
"mode": 1,
"r": 6
},
"illusion_1.5L_O_dqctl_vs_dqtar_r6_mode2": {
"overlap": 0.9523902874156159,
"mode": 2,
"r": 6
},
"illusion_1.5L_O_dqctl_vs_dqtar_r6_mode3": {
"overlap": 0.6142970258676237,
"mode": 3,
"r": 6
},
"illusion_1.5L_O_dqctl_vs_dqtar_r6_mode4": {
"overlap": 0.6094429047936349,
"mode": 4,
"r": 6
},
"illusion_1.5L_O_dqctl_vs_dqtar_r6_mode5": {
"overlap": 0.7802610781754543,
"mode": 5,
"r": 6
},
"illusion_1.5L_dqctl_force_fy_r8": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "force_fy",
"r": 8,
"m80": 1,
"N": 7,
"sigma_top3": [
1.0829392538529714,
0.5377241276357321,
0.035957125479337396
],
"special_mechanism": true
},
"illusion_1.5L_dqctl_force_fx_r8": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "force_fx",
"r": 8,
"m80": 2,
"N": 7,
"sigma_top3": [
0.24532002823760304,
0.2176027887532337,
0.06997228412312347
],
"special_mechanism": true
},
"illusion_1.5L_dqctl_action_r8": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "action",
"r": 8,
"m80": 2,
"N": 8,
"sigma_top3": [
0.24569133479372693,
0.19734109779802844,
0.07358222383616513
],
"special_mechanism": true
},
"illusion_1.5L_dqtar_force_fy_r8": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "force_fy_tar",
"r": 8,
"m80": 2,
"N": 7,
"sigma_top3": [
0.965559410900674,
0.5011783760115653,
0.036374759425362196
],
"special_mechanism": true
},
"illusion_1.5L_O_dqctl_vs_dqtar_r8_mode1": {
"overlap": 0.7669082530299124,
"mode": 1,
"r": 8
},
"illusion_1.5L_O_dqctl_vs_dqtar_r8_mode2": {
"overlap": 0.7846172591640821,
"mode": 2,
"r": 8
},
"illusion_1.5L_O_dqctl_vs_dqtar_r8_mode3": {
"overlap": 0.5966131594534103,
"mode": 3,
"r": 8
},
"illusion_1.5L_O_dqctl_vs_dqtar_r8_mode4": {
"overlap": 0.4961899748365384,
"mode": 4,
"r": 8
},
"illusion_1.5L_O_dqctl_vs_dqtar_r8_mode5": {
"overlap": 0.554083931172919,
"mode": 5,
"r": 8
},
"illusion_1.5L_dqctl_force_fy_r10": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "force_fy",
"r": 10,
"m80": 1,
"N": 7,
"sigma_top3": [
1.2267278703286486,
0.583312734476056,
0.03658838363884156
],
"special_mechanism": true
},
"illusion_1.5L_dqctl_force_fx_r10": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "force_fx",
"r": 10,
"m80": 2,
"N": 7,
"sigma_top3": [
0.2567417793308261,
0.23357924739128508,
0.10637915056271505
],
"special_mechanism": true
},
"illusion_1.5L_dqctl_action_r10": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "action",
"r": 10,
"m80": 2,
"N": 10,
"sigma_top3": [
0.2696699328581466,
0.23742010933522745,
0.08104308573268743
],
"special_mechanism": true
},
"illusion_1.5L_dqtar_force_fy_r10": {
"scene": "illusion_1.5L",
"diam": 1.5,
"obs": "force_fy_tar",
"r": 10,
"m80": 2,
"N": 7,
"sigma_top3": [
0.9662457501034244,
0.5015382439664361,
0.03666891184815278
],
"special_mechanism": true
},
"illusion_1.5L_O_dqctl_vs_dqtar_r10_mode1": {
"overlap": 0.6614436164339873,
"mode": 1,
"r": 10
},
"illusion_1.5L_O_dqctl_vs_dqtar_r10_mode2": {
"overlap": 0.7185566980291951,
"mode": 2,
"r": 10
},
"illusion_1.5L_O_dqctl_vs_dqtar_r10_mode3": {
"overlap": 0.5549038455928854,
"mode": 3,
"r": 10
},
"illusion_1.5L_O_dqctl_vs_dqtar_r10_mode4": {
"overlap": 0.42330974813153344,
"mode": 4,
"r": 10
},
"illusion_1.5L_O_dqctl_vs_dqtar_r10_mode5": {
"overlap": 0.4199314352386385,
"mode": 5,
"r": 10
}
}

View File

@ -0,0 +1,282 @@
{
"illusion_0.75L_dq_blk": {
"near_body": {
"n_points": 76800,
"mean_KE": 0.0007648248574696481,
"mean_enstrophy": 2.9027347636656486e-07,
"KE_fraction": 0.0023668697103857994
},
"body_wake": {
"n_points": 102400,
"mean_KE": 0.07021629065275192,
"mean_enstrophy": 0.0008679562015458941,
"KE_fraction": 0.28972700238227844
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.09573789685964584,
"mean_enstrophy": 0.001991209341213107,
"KE_fraction": 0.1382620632648468
}
},
"illusion_0.75L_dq_ctl": {
"near_body": {
"n_points": 76800,
"mean_KE": 3.614435627241619e-05,
"mean_enstrophy": 1.743321575986556e-08,
"KE_fraction": 0.000138708230224438
},
"body_wake": {
"n_points": 102400,
"mean_KE": 0.031560733914375305,
"mean_enstrophy": 0.001438321196474135,
"KE_fraction": 0.16149072349071503
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.04915999248623848,
"mean_enstrophy": 0.003602436976507306,
"KE_fraction": 0.0880400612950325
}
},
"illusion_1.0L_dq_blk": {
"near_body": {
"n_points": 76800,
"mean_KE": 0.0007648248574696481,
"mean_enstrophy": 2.9027347636656486e-07,
"KE_fraction": 0.0023668697103857994
},
"body_wake": {
"n_points": 102400,
"mean_KE": 0.07021629065275192,
"mean_enstrophy": 0.0008679562015458941,
"KE_fraction": 0.28972700238227844
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.09573789685964584,
"mean_enstrophy": 0.001991209341213107,
"KE_fraction": 0.1382620632648468
}
},
"illusion_1.0L_dq_ctl": {
"near_body": {
"n_points": 76800,
"mean_KE": 2.7065932954428717e-05,
"mean_enstrophy": 1.3172467561162193e-08,
"KE_fraction": 0.000290345138637349
},
"body_wake": {
"n_points": 102400,
"mean_KE": 0.0158767718821764,
"mean_enstrophy": 0.0004291802179068327,
"KE_fraction": 0.2270871102809906
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.015243785455822945,
"mean_enstrophy": 0.0009271366288885474,
"KE_fraction": 0.07631170749664307
}
},
"illusion_1.5L_dq_blk": {
"near_body": {
"n_points": 76800,
"mean_KE": 0.0007648248574696481,
"mean_enstrophy": 2.9027347636656486e-07,
"KE_fraction": 0.0023668697103857994
},
"body_wake": {
"n_points": 102400,
"mean_KE": 0.07021629065275192,
"mean_enstrophy": 0.0008679562015458941,
"KE_fraction": 0.28972700238227844
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.09573789685964584,
"mean_enstrophy": 0.001991209341213107,
"KE_fraction": 0.1382620632648468
}
},
"illusion_1.5L_dq_ctl": {
"near_body": {
"n_points": 76800,
"mean_KE": 2.4420158297289163e-05,
"mean_enstrophy": 1.2525031012344812e-09,
"KE_fraction": 8.383671229239553e-05
},
"body_wake": {
"n_points": 102400,
"mean_KE": 0.04845782741904259,
"mean_enstrophy": 0.00021500905859284103,
"KE_fraction": 0.22181373834609985
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.11757005751132965,
"mean_enstrophy": 0.0005185439949855208,
"KE_fraction": 0.18836025893688202
}
},
"steady_cloak_dq_blk": {
"near_body": {
"n_points": 76800,
"mean_KE": 0.0007648248574696481,
"mean_enstrophy": 2.9027347636656486e-07,
"KE_fraction": 0.0023668697103857994
},
"body_wake": {
"n_points": 102400,
"mean_KE": 0.07021629065275192,
"mean_enstrophy": 0.0008679562015458941,
"KE_fraction": 0.28972700238227844
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.09573789685964584,
"mean_enstrophy": 0.001991209341213107,
"KE_fraction": 0.1382620632648468
}
},
"steady_cloak_dq_ctl": {
"near_body": {
"n_points": 76800,
"mean_KE": 6.172616849653423e-05,
"mean_enstrophy": 3.953555349767157e-08,
"KE_fraction": 0.00015435306704603136
},
"body_wake": {
"n_points": 102400,
"mean_KE": 0.13577842712402344,
"mean_enstrophy": 0.008918941020965576,
"KE_fraction": 0.45270517468452454
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.3214779794216156,
"mean_enstrophy": 0.02501676417887211,
"KE_fraction": 0.375149130821228
}
},
"karman_re100_dq_blk": {
"near_body": {
"n_points": 71680,
"mean_KE": 0.14623497426509857,
"mean_enstrophy": 0.0006234294269233942,
"KE_fraction": 0.20486503839492798
},
"body_wake": {
"n_points": 66560,
"mean_KE": 0.10329263657331467,
"mean_enstrophy": 0.0001615467481315136,
"KE_fraction": 0.13436967134475708
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.0839567631483078,
"mean_enstrophy": 0.00010710477363318205,
"KE_fraction": 0.05880879983305931
}
},
"karman_re100_dq_ctl": {
"near_body": {
"n_points": 71680,
"mean_KE": 0.20531150698661804,
"mean_enstrophy": 0.008244173601269722,
"KE_fraction": 0.2548401653766632
},
"body_wake": {
"n_points": 66560,
"mean_KE": 0.12807133793830872,
"mean_enstrophy": 0.00012567009252961725,
"KE_fraction": 0.147612065076828
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.12139707058668137,
"mean_enstrophy": 8.339789928868413e-05,
"KE_fraction": 0.07534124702215195
}
},
"vortex_lamb_dq_blk": {
"near_body": {
"n_points": 71680,
"mean_KE": 0.13352914154529572,
"mean_enstrophy": 0.0010146809509024024,
"KE_fraction": 0.46505114436149597
},
"body_wake": {
"n_points": 66560,
"mean_KE": 0.11699999123811722,
"mean_enstrophy": 0.0002210606326116249,
"KE_fraction": 0.37837791442871094
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.09222870320081711,
"mean_enstrophy": 0.00014765505329705775,
"KE_fraction": 0.16060562431812286
}
},
"vortex_lamb_dq_ctl": {
"near_body": {
"n_points": 71680,
"mean_KE": 0.18387039005756378,
"mean_enstrophy": 0.008811434730887413,
"KE_fraction": 0.48393887281417847
},
"body_wake": {
"n_points": 66560,
"mean_KE": 0.13139627873897552,
"mean_enstrophy": 0.0002448623126838356,
"KE_fraction": 0.32112717628479004
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.12105172127485275,
"mean_enstrophy": 0.00019949178386013955,
"KE_fraction": 0.15930141508579254
}
},
"vortex_taylor_dq_blk": {
"near_body": {
"n_points": 71680,
"mean_KE": 0.1294081211090088,
"mean_enstrophy": 0.0011693575652316213,
"KE_fraction": 0.44358906149864197
},
"body_wake": {
"n_points": 66560,
"mean_KE": 0.10131462663412094,
"mean_enstrophy": 0.0001417396852048114,
"KE_fraction": 0.3224829137325287
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.07597015053033829,
"mean_enstrophy": 4.9815782404039055e-05,
"KE_fraction": 0.1302063763141632
}
},
"vortex_taylor_dq_ctl": {
"near_body": {
"n_points": 71680,
"mean_KE": 0.16766612231731415,
"mean_enstrophy": 0.008833244442939758,
"KE_fraction": 0.48838308453559875
},
"body_wake": {
"n_points": 66560,
"mean_KE": 0.08698907494544983,
"mean_enstrophy": 0.000124395388411358,
"KE_fraction": 0.23528558015823364
},
"sensor_zone": {
"n_points": 35840,
"mean_KE": 0.06907761096954346,
"mean_enstrophy": 3.919627488357946e-05,
"KE_fraction": 0.10060571134090424
}
}
}

View File

@ -0,0 +1 @@
../../data/figures/corr_comparison_all_scenes.png

View File

@ -0,0 +1 @@
../../data/figures/corr_cloak_comparison_dqctl.png

View File

@ -0,0 +1 @@
../../data/figures/corr_illusion_comparison_dqctl.png

View File

@ -0,0 +1 @@
../../data/figures/steady_cloak_cancel_test.png

View File

@ -0,0 +1 @@
../../data/figures/corr_illusion_0.75L_ctl_vs_tar.png

View File

@ -0,0 +1 @@
../../data/figures/corr_illusion_1.0L_ctl_vs_tar.png

View File

@ -0,0 +1 @@
../../data/figures/corr_illusion_1.5L_ctl_vs_tar.png

View File

@ -0,0 +1 @@
../../data/figures/corr_karman_re100_ctl_vs_tar.png

View File

@ -0,0 +1 @@
../../data/figures/corr_vortex_lamb_ctl_vs_tar.png

View File

@ -0,0 +1 @@
../../data/figures/corr_vortex_taylor_ctl_vs_tar.png

View File

@ -0,0 +1 @@
../../data/figures/vortex_lamb_diagnosis.png

View File

@ -0,0 +1 @@
../../data/figures/vortex_taylor_diagnosis.png

View File

@ -0,0 +1 @@
../../data/figures/vortex_lamb_vorticity.png

View File

@ -0,0 +1 @@
../../data/figures/vortex_taylor_vorticity.png

View File

@ -0,0 +1 @@
../../data/figures/vortex_lamb_target_vorticity.png

View File

@ -0,0 +1 @@
../../data/figures/vortex_taylor_target_vorticity.png

View File

@ -0,0 +1,132 @@
# OID Analysis Pipeline
Observable-Inferred Decomposition (Schlegel et al. 2012) applied to correction fields of DRL-controlled fluidic pinball flows. Identifies which flow structures best predict forces and downstream sensor signatures, across 5 control tasks: steady cloak, Karman cloak, illusion at 0.75L/1.0L/1.5L target diameters.
## Pipeline Overview
```
Raw fields (GPU, pycuda_3_10) Analysis (CPU, sr_env)
═══════════════════════════════ ════════════════════════
scripts/collect_*.py analysis/phase*.py
│ │
├── empty_channel ─── q_in ─────────┐ │
├── pinball_baseline ─ q_blk ───────┤ │
└── controlled ─────── q_ctl ───────┤ │
▼ ▼
┌─────────────────────────────┐
│ Phase 1: Δq_ctl = q_ctlq_blk│
│ → correction-field POD │
│ → rank sensitivity (6-16) │
└─────────────┬───────────────┘
┌─────────────────────────────┐
│ Phase 2: observable Y build │
│ force_total, sensor_error, │
│ sensor_error_delayed │
└─────────────┬───────────────┘
┌──────────────────────┴──────────────────────┐
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ Phase 3: Force-OID │ │ Phase 4: Signature-OID│
│ Y = force_total │ │ Y = delayed sensor err│
│ SVD(C_AY) → U_f │ │ SVD(C_AY) → U_s │
└──────────┬───────────┘ └──────────┬───────────┘
│ │
└──────────────┬───────────────────────────┘
┌──────────────────────────┐
│ Phase 5: Steady OID │
│ (suppression / mean-wake) │
└──────────────┬───────────┘
┌──────────────────────────┐
│ Phase 6: POD vs OID vs PCD│
│ 70/30 R² comparison │
└──────────────┬───────────┘
┌──────────────────────────┐
│ Phase 7: White-box chain │
│ obs→act / OID→act / ... │
└──────────────────────────┘
```
## Scene Table
| Scene Key | Inflow | Controlled | Dist Cyl? | Sensors | Pinball X | SI | S_DIM |
|-----------|--------|-----------|:---:|--------|-----------|:---:|:---:|
| steady_cloak | Parabolic | Open-loop const ±5.1U0 | No | x=40 | 30/31.3 | 800 | 12 |
| karman_re100 | Parabolic + vor-tex street | PPO d1a3o12_re100 | Yes | x=40 | 30/31.3 | 800 | 12 |
| illusion_0.75L | Parabolic | PPO d1a3o14_075L_2U_400S | No | x=30 | 19/20.3 | 400 | 14 |
| illusion_1.0L | Parabolic | PPO d1a3o14_1L_2U_600S | No | x=30 | 19/20.3 | 600 | 14 |
| illusion_1.5L | Parabolic | PPO d1a3o14_15L_2U | No | x=30 | 19/20.3 | 800 | 14 |
Action biases: cloak [0,4,4], illusion [0,2,2]. Action scale: 8 for all.
## Quick Commands
```bash
# Full collection (GPU, ~1-2 hours per scene group)
conda run -n pycuda_3_10 python src/OID_analysis/scripts/collect_all_data.py
# Full analysis for one scene (CPU, ~5 min)
PYTHONPATH="src:$PYTHONPATH" conda run -n sr_env python3 \
src/OID_analysis/analysis/run_full_analysis.py --scene karman_re100
# Robustness + figures
PYTHONPATH="src:$PYTHONPATH" conda run -n sr_env python3 \
src/OID_analysis/analysis/robustness_analysis.py
PYTHONPATH="src:$PYTHONPATH" conda run -n sr_env python3 \
src/OID_analysis/analysis/make_figures.py
```
## Key Conventions
| Convention | Rule |
|-----------|------|
| Re definition | Code Re uses 2D reference (=40). Physical Re_D = Re_code/2. Default: Re_code=100 → Re_D=50. |
| OID object | Always `Δq_ctl = q_ctl q_blk`, NOT raw `q_ctl`. |
| Field storage | Full 1280×512 resolution. ROI mask applied ONLY at analysis stage (POD). |
| force-OID vs sig-OID | Reported separately. Their divergence is a mechanism result, not a failure. |
| Illusion q_blk | Uses separate geometry (pinball_x=19/20.3, sensor_x=30), NOT cloak q_blk. |
| Norm | Data-driven (force_norm_fact = 6·max|forces|, sens_norm = 5·max|sensormean|). Model-specific. |
| GPU | Karman on device 1, steady/illusion on device 3. Fresh context per scene. |
| Environment split | `pycuda_3_10` for GPU collection, `sr_env` for CPU analysis. |
## Where Results Live
```
data/derived/
├── pod/{scene}/ Phase 1: POD modes + coefficients
├── observables/{scene}/ Phase 2: Y matrices
├── oid/force/{scene}/ Phase 3: force-OID (U, S, z, modes)
├── oid/signature/{scene}/ Phase 4: sig-OID (delayed + current)
├── oid/pcd/{scene}/ Phase 4b: sig-PCD (whitened)
├── comparison/{scene}.json Phase 6: POD-vs-OID R² tables
├── whitebox/{scene}.json Phase 7: action prediction R²
├── robustness/ Rank/tau_c/window sensitivity
├── master/master_table.json Cross-scene canonical values
└── figures/ All 7 figures (PNG)
```
## Adding a New Scene
1. Register in `configs.py`: add scene dict with physics, geometry, control params
2. Collect fields: write GPU script (or use generic `collect_controlled.py` for PPO)
3. Run pipeline: `run_full_analysis.py --scene <new_scene> --force`
4. Update `master_table.json` with new overlap/R² values
5. Regenerate figures: `make_figures.py`
## Companion Documents
| File | Role |
|------|------|
| `OID_knowledge.md` | Hard rules, confirmed facts, full results table, bug history |
| `OID_notes.md` | Task tracking, open items, handover notes |
| `Final_Conclusions.md` | Six key questions answered |
| `README.md` | Entry point — scope, file map, pitfalls |
| `papers/Sch12.md` | Schlegel et al. (2012) — OID original paper |
| `papers/Li22b.md` | Li et al. (2022) — pinball state estimation paper |
| `docs/sch12_code_mapping.md` | Sch12 formula → code traceability |
| `docs/OID_analysis_results.md` | External report — methods, 7 figures, all results |

View File

@ -1,173 +1,85 @@
# OID_analysis: Observable-Inferred Decomposition for Fluidic Pinball Control
# OID_analysis Observable-Inferred Decomposition for Fluidic Pinball
## What this directory does
Identifies which correction-field structures the DRL controller modulates, ranked by cross-correlation with force and signature observables (not by POD energy).
This directory implements an **OID (Observable-Inferred Decomposition)** pipeline for analyzing which flow structures the DRL controller actually modulates in the fluidic pinball experiments. OID ranks modes by cross-correlation with a chosen observable (force, sensor error), rather than by fluctuation energy (POD).
## Quick Start
**The three companion documents:**
- `OID_knowledge.md` -- confirmed facts, hard rules, critical caveats, **current results**
- `OID_notes.md` -- execution plan, task tracking, handover notes
- This file (README.md) -- engineering entry point, how to run
```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
**Critical reading order for new agent:**
1. `docs/OID_analysis_results.md` -- full project report (start here)
2. README.md (this file) -- understand scope and how to run
3. `OID_knowledge.md` -- know the rules, the results, and pitfalls
4. `OID_notes.md` -- understand what's left to do
# 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
```
---
## Current scope (as of 2026-06-28)
**All 5 scenes analyzed end-to-end:**
| Scene | Type | Source | Status |
|-------|------|--------|--------|
| steady_cloak | Open-loop constant rotation (rear=+-5.1U0) | Collected | Full Phases 1-7 |
| karman_re100 | PPO (d1a3o12_re100) | Replayed from CCD | Full Phases 1-7 |
| illusion_0.75L | PPO (d1a3o14_075L_2U_400S) | Replayed from CCD | Full Phases 1-7 |
| illusion_1.0L | PPO (d1a3o14_1L_2U_600S) | Replayed from CCD | Full Phases 1-7 |
| illusion_1.5L | PPO (d1a3o14_15L_2U) | Replayed from CCD | Full Phases 1-7 |
**Fixed scope notes (inherited from SR/CCD):**
- All illusion models use **u0=0.01, nu=0.004** (standard defaults from config_flowfield.json)
- "2U" in model name means **S_DIM=14** (2 extra target force observations), NOT 2x velocity
- SAMPLE_INTERVAL depends on target diameter: 0.75L=400, 1.0L=600, 1.5L=800
- Illusion q_blk was collected **separately** from cloak-position q_blk (different geometry)
---
## Directory structure
## File Map
```
src/OID_analysis/
configs.py # Unified scene metadata (12 scene definitions)
utils/
cfd_interface.py # Re-exports CCD's proven CFD interface
analysis.py # CPU-only: POD, OID, PCD, zone statistics, comparison
scripts/
collect_*.py # Data collection scripts (GPU, pycuda_3_10)
replay_*.py # Field replay + verification
compute_delta_fields.py # Phase 0 draft
analysis/
phase*.py # Analysis scripts Phases 1-7 (CPU, sr_env)
robustness_analysis.py # Rank/tau_c/window/zone robustness
steady_reanalysis.py # Steady cloak suppression metrics
compile_master_table.py # Cross-scene comparison
make_figures.py # Generate 7 figures
run_full_analysis.py # Batch pipeline runner
data/
configs/legacy/ # Legacy CFD configs (symlinked)
steady_cloak/ # Baseline + controlled fields
karman_cloak/ # Baseline + controlled fields
illusion/ # PPO controlled fields
target_cylinder/ # Reference targets
derived/ # All analysis results + figures
OID_knowledge.md # Knowledge base
OID_notes.md # Task tracking
Final_Conclusions.md # Six-question conclusions
Sch12.md # OID reference paper
docs/
OID_analysis_results.md # Full project report (7 figures, 292 lines)
├── 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)
## How to run
| 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 |
All commands from repo root (`/home/frank14f/DynamisLab`).
Full numbers: `data/derived/master/master_table.json` · `scene_registry.json`
### Data collection (GPU, pycuda_3_10)
## Pitfalls
```bash
# Steady group:
conda run -n pycuda_3_10 python src/OID_analysis/scripts/collect_empty_channel.py --device 3 --steps 200
conda run -n pycuda_3_10 python src/OID_analysis/scripts/collect_pinball_baseline.py --device 3 --steps 500
conda run -n pycuda_3_10 python src/OID_analysis/scripts/collect_steady_cloak.py --device 3 --steps 500 --omega-rear 5.1
# Karman group:
conda run -n pycuda_3_10 python src/OID_analysis/scripts/collect_disturbance_only.py --device 1 --steps 500
conda run -n pycuda_3_10 python src/OID_analysis/scripts/collect_karman_blk.py --device 1 --steps 500
# Illusion q_blk (important: separate geometry from cloak):
conda run -n pycuda_3_10 python src/OID_analysis/scripts/collect_illusion_qblk.py --device 3 --steps 500
# PPO field replay (from CCD checkpoints):
conda run -n pycuda_3_10 python src/OID_analysis/scripts/replay_full_fields.py --scene karman_re100 --device 1
conda run -n pycuda_3_10 python src/OID_analysis/scripts/replay_full_fields.py --scene illusion_1.0L --device 3
# Target cylinders:
conda run -n pycuda_3_10 python src/OID_analysis/scripts/collect_target_cylinder.py --diameter 1.0 --device 3 --steps 500
```
### Analysis (CPU only, sr_env)
```bash
# Full pipeline for one scene:
cd /home/frank14f/DynamisLab
PYTHONPATH="src:$PYTHONPATH" conda run -n sr_env python3 src/OID_analysis/analysis/run_full_analysis.py --scene karman_re100
# Robustness analysis:
PYTHONPATH="src:$PYTHONPATH" conda run -n sr_env python3 src/OID_analysis/analysis/robustness_analysis.py
# Generate figures:
PYTHONPATH="src:$PYTHONPATH" conda run -n pycuda_3_10 python3 src/OID_analysis/analysis/make_figures.py
# Cross-scene table:
PYTHONPATH="src:$PYTHONPATH" conda run -n sr_env python3 src/OID_analysis/analysis/compile_master_table.py
```
### Replay verification
```bash
# Replay verification is now integrated into collect_fields_replay.py (archived) and replay_full_fields.py
# Use replay_full_fields.py for full-field (uncropped) replay with new CelerisLab
conda run -n pycuda_3_10 python src/OID_analysis/scripts/replay_full_fields.py --scene karman_re100 --device 2
```
---
## Key results
| Finding | Evidence | Confidence |
|---------|----------|------------|
| Force-sig structures systematically separate across tasks | Monotonic trend: +0.763 -> -0.034 -> -0.082 -> -0.495 -> -0.932 | High (rank-stable for 4/5 scenes) |
| OID beats POD for force prediction | OID R2=0.435-0.750 vs POD R2=-2.4 to 0.418 | High |
| OID beats POD for signature prediction | OID R2=0.315-0.661 vs POD R2=-0.16 to 0.06 | High |
| OID coordinates != control state | force-OID->act R2=0.225 vs obs->act R2=0.956 | Confirmed (expected design) |
| Steady cloak suppresses 99.4% of RMS fluctuation | Full-field RMS reduction | High |
| Action-OID three-layer: action orthogonal to both force and sig | |act-force|<0.33, |act-sig|<0.40 across all scenes | High confirms Li22b insight that b must be explicit input |
---
## Common pitfalls (read these before making changes)
1. **Always use model name as single source of truth.** "2U" = S_DIM=14, NOT 2x velocity. u0 is ALWAYS 0.01.
2. **NEVER use nu_from_re() for illusion models.** Only valid for standard S_DIM=12 cases.
3. **Distinguish action_bias from preset_action.** They are DIFFERENT. action_bias is for DRL scaling; preset_action is for FIFO warmup.
4. **context.push()/pop() around every run() call.** Mandatory for multi-step loops.
5. **Fresh GPU per PPO scene.** GPU state contamination degrades similarity. Minimum 4NX/U0 steps separation between scenes on the same GPU.
6. **No field cropping.** All fields must be full 1280x512 resolution.
7. **Three-field decomposition is MANDATORY.** OID operates on Delta-q_ctl = q_ctl - q_blk, NOT on raw q_ctl.
8. **OID identifies directions within the chosen POD subspace.** It CANNOT recover structures truncated by POD rank reduction.
---
## Delivered files (non-data)
These files should be committed to the repository:
**Code (src/OID_analysis/):**
- `configs.py`, `utils/cfd_interface.py`, `utils/analysis.py`
- `scripts/` (11 collection + 3 replay scripts)
- `analysis/` (7 phase scripts + robustness + figures + batch runner)
- `OID_knowledge.md`, `OID_notes.md`, `Final_Conclusions.md`
**Report (docs/):**
- `docs/OID_analysis_results.md`
**Large data files (NOT committed -- see .gitignore):**
- All `.npz` in `data/`
- All `.npy` in `data/derived/`
- All `.png` in `data/derived/figures/`
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.

View File

@ -0,0 +1,306 @@
{
"scenes": {
"empty_channel": {
"scene_id": "empty_channel",
"re_code": 100,
"has_disturbance": false,
"sample_interval": 800,
"source": "open_loop",
"n_objects_env": 3,
"s_dim": 12,
"a_dim": 3,
"sensor_x": 40.0,
"pinball_front_x": 30.0,
"pinball_rear_x": 31.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-4.0,
4.0
],
"u0": 0.01,
"target_type": "steady"
},
"pinball_baseline": {
"scene_id": "pinball_baseline",
"re_code": 100,
"has_disturbance": false,
"sample_interval": 800,
"source": "open_loop",
"n_objects_env": 6,
"s_dim": 12,
"a_dim": 3,
"sensor_x": 40.0,
"pinball_front_x": 30.0,
"pinball_rear_x": 31.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-4.0,
4.0
],
"u0": 0.01,
"target_type": "periodic"
},
"pinball_baseline_illusion": {
"scene_id": "pinball_baseline_illusion",
"re_code": 100,
"has_disturbance": false,
"sample_interval": 800,
"source": "open_loop",
"n_objects_env": 6,
"s_dim": 12,
"a_dim": 3,
"sensor_x": 30.0,
"pinball_front_x": 19.0,
"pinball_rear_x": 20.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-2.0,
2.0
],
"u0": 0.01,
"target_type": "periodic"
},
"disturbance_only": {
"scene_id": "disturbance_only",
"re_code": 100,
"has_disturbance": true,
"sample_interval": 800,
"source": "open_loop",
"n_objects_env": 4,
"s_dim": 12,
"a_dim": 3,
"sensor_x": 40.0,
"pinball_front_x": 30.0,
"pinball_rear_x": 31.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-4.0,
4.0
],
"u0": 0.01,
"target_type": "periodic"
},
"karman_blk": {
"scene_id": "karman_blk",
"re_code": 100,
"has_disturbance": true,
"sample_interval": 800,
"source": "open_loop",
"n_objects_env": 7,
"s_dim": 12,
"a_dim": 3,
"sensor_x": 40.0,
"pinball_front_x": 30.0,
"pinball_rear_x": 31.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-4.0,
4.0
],
"u0": 0.01,
"target_type": "periodic"
},
"steady_cloak": {
"scene_id": "steady_cloak",
"re_code": 100,
"has_disturbance": false,
"sample_interval": 800,
"source": "open_loop",
"n_objects_env": 6,
"s_dim": 12,
"a_dim": 3,
"sensor_x": 40.0,
"pinball_front_x": 30.0,
"pinball_rear_x": 31.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-4.0,
4.0
],
"u0": 0.01,
"target_type": "steady"
},
"karman_re100": {
"scene_id": "karman",
"re_code": 100,
"has_disturbance": true,
"sample_interval": 800,
"source": "PPO_inference",
"n_objects_env": 7,
"s_dim": 12,
"a_dim": 3,
"sensor_x": 40.0,
"pinball_front_x": 30.0,
"pinball_rear_x": 31.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-4.0,
4.0
],
"u0": 0.01,
"target_type": "periodic"
},
"illusion_0.75L": {
"scene_id": "illusion",
"re_code": 100,
"has_disturbance": false,
"sample_interval": 400,
"source": "PPO_inference",
"n_objects_env": 6,
"s_dim": 14,
"a_dim": 3,
"sensor_x": 30.0,
"pinball_front_x": 19.0,
"pinball_rear_x": 20.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-2.0,
2.0
],
"u0": 0.01,
"target_type": "periodic"
},
"illusion_1.0L": {
"scene_id": "illusion",
"re_code": 100,
"has_disturbance": false,
"sample_interval": 600,
"source": "PPO_inference",
"n_objects_env": 6,
"s_dim": 14,
"a_dim": 3,
"sensor_x": 30.0,
"pinball_front_x": 19.0,
"pinball_rear_x": 20.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-2.0,
2.0
],
"u0": 0.01,
"target_type": "periodic"
},
"illusion_1.5L": {
"scene_id": "illusion",
"re_code": 100,
"has_disturbance": false,
"sample_interval": 800,
"source": "PPO_inference",
"n_objects_env": 6,
"s_dim": 14,
"a_dim": 3,
"sensor_x": 30.0,
"pinball_front_x": 19.0,
"pinball_rear_x": 20.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-2.0,
2.0
],
"u0": 0.01,
"target_type": "periodic"
},
"target_cylinder_0.75L": {
"scene_id": "target_cylinder",
"re_code": 100,
"has_disturbance": false,
"sample_interval": 400,
"source": "open_loop",
"n_objects_env": 4,
"s_dim": 12,
"a_dim": 3,
"sensor_x": 30.0,
"pinball_front_x": 30.0,
"pinball_rear_x": 31.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-4.0,
4.0
],
"u0": 0.01,
"target_type": "periodic"
},
"target_cylinder_1.0L": {
"scene_id": "target_cylinder",
"re_code": 100,
"has_disturbance": false,
"sample_interval": 600,
"source": "open_loop",
"n_objects_env": 4,
"s_dim": 12,
"a_dim": 3,
"sensor_x": 30.0,
"pinball_front_x": 30.0,
"pinball_rear_x": 31.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-4.0,
4.0
],
"u0": 0.01,
"target_type": "periodic"
},
"target_cylinder_1.5L": {
"scene_id": "target_cylinder",
"re_code": 100,
"has_disturbance": false,
"sample_interval": 800,
"source": "open_loop",
"n_objects_env": 4,
"s_dim": 12,
"a_dim": 3,
"sensor_x": 30.0,
"pinball_front_x": 30.0,
"pinball_rear_x": 31.3,
"action_scale": 8.0,
"action_bias": [
0.0,
-4.0,
4.0
],
"u0": 0.01,
"target_type": "periodic"
}
},
"canonical_results": {
"steady_cloak": 0.763,
"karman_re100": -0.034,
"illusion_0.75L": -0.082,
"illusion_1.0L": -0.495,
"illusion_1.5L": -0.932
},
"three_layer_overlap": {
"karman_re100": {
"act_force": -0.03164697269821093,
"act_sig": -0.07431818697760144,
"force_sig": -0.0343739260491455
},
"illusion_0.75L": {
"act_force": -0.32732725369388294,
"act_sig": -0.4007721802551729,
"force_sig": -0.6941197281957918
},
"illusion_1.0L": {
"act_force": 0.1208564966066174,
"act_sig": -0.3183892941307407,
"force_sig": -0.8423715368324566
},
"illusion_1.5L": {
"act_force": 0.0017766926815728562,
"act_sig": 0.017393103043959173,
"force_sig": -0.9319165643601308
}
}
}