# 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) ``` ## Dual-clock field sampling Legacy DRL 的控制时钟与 DDF 保存时钟必须独立:模型仍只在固定控制周期边界读取完整 observation 和预测动作,DDF 可以在周期内任意 lattice step 读取。通用 API、OID 复用方式、输出时间轴合同和 GPU A/B 结果见 [`DUAL_CLOCK_SAMPLING.md`](DUAL_CLOCK_SAMPLING.md)。 ## 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