chore(SR): freeze legacy SR_analysis snapshot before contract repair

Create pre-fix baseline with:
- Manifest recording ignored NPZ hashes, model inventory, env versions
- Plan file for long-term SR Legacy Pipeline execution
- Snapshot of current stage_1_infer, PIPELINE, STAGE_1_INFER, results/README

This commit preserves the frozen historical state before Stage 1-3
contract repair begins. All canonical results are marked historical_frozen;
no formula or validation data will be overwritten during round 1.

Round 1 scope: legacy Karman (code Re 50/100/200/400) + Illusion (0.75L/1L).
V5, Vortex, unseen-Re, erase, 1.5L, and paper figures excluded.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Frank14f
2026-07-16 22:03:20 +08:00
co-authored by Cursor
parent 4feac49f1c
commit ca8ee5f238
6 changed files with 289 additions and 4 deletions
+1 -1
View File
@@ -88,7 +88,7 @@ done
**Output per scene** (in `data/{scene_id}/{scene_name}/`): **Output per scene** (in `data/{scene_id}/{scene_name}/`):
- `controlled.npz`: actions [N,3], sensors [N,6], forces [N,6] - `controlled.npz`: actions [N,3], sensors [N,6], forces [N,6]
- `target.npz`: target sensor signals [FIFO_LEN, 6] - `target.npz`: target signals [FIFO_LEN, 6] for Karman/Vortex; **8 columns for Illusion** = [cyl_fx,cyl_fy, s0_ux,s0_uy, s1_ux,s1_uy, s2_ux,s2_uy] — extract sensors with `target[:, 2:8]`
- `norm.json`: normalization factors - `norm.json`: normalization factors
- `result.json`: similarity + reward summary - `result.json`: similarity + reward summary
- `target_harmonics.json` (Illusion only): FFT harmonics for force reconstruction - `target_harmonics.json` (Illusion only): FFT harmonics for force reconstruction
+5 -1
View File
@@ -23,7 +23,11 @@ actions in controlled.npz are normalized [-1,+1]. Physical omega = (action*scale
## Per-Scene Notes ## Per-Scene Notes
- **Karman**: 7 objects, obs_slice=(2,14), action_bias=[0,-4,4], s_dim=12 - **Karman**: 7 objects, obs_slice=(2,14), action_bias=[0,-4,4], s_dim=12
- **Illusion**: 6 objects, obs_slice=(0,12), action_bias=[0,-2,2], s_dim=14, FIFO bias=[0,-U0,U0] differs from DRL bias - **Illusion**: 6 objects, obs_slice=(0,12), action_bias=[0,-2,2], s_dim=14, FIFO bias=[0,-U0,U0] differs from DRL bias.
**target.npz format**: 8 columns = [cyl_fx, cyl_fy, s0_ux, s0_uy, s1_ux, s1_uy, s2_ux, s2_uy].
Consumers must use `target[:, 2:8]` to extract the 6 pure-sensor channels.
(Historical: stage_1_infer.py had a `[:, :6]` write bug that omitted s2; fixed 2026-07-14.
On-disk files were regenerated with gen_illusion_target.py and are correct.)
- **Vortex**: 6 objects, MAX_STEPS=150, action_scale=4, vortex added after DDF checkpoint - **Vortex**: 6 objects, MAX_STEPS=150, action_scale=4, vortex added after DDF checkpoint
## Expected Similarities ## Expected Similarities
+1 -1
View File
@@ -54,7 +54,7 @@
|------|---------| |------|---------|
| `data/karman/karman_re{50,100,200,400}/controlled.npz` | PPO actions, sensors, forces | | `data/karman/karman_re{50,100,200,400}/controlled.npz` | PPO actions, sensors, forces |
| `data/illusion/illusion_{0.5L..2L}/controlled.npz` | PPO data (trained diameters only) | | `data/illusion/illusion_{0.5L..2L}/controlled.npz` | PPO data (trained diameters only) |
| `data/illusion/illusion_{0.5L..2L}/target.npz` | Target sensor signals | | `data/illusion/illusion_{0.5L..2L}/target.npz` | Target signals (8 columns: [cyl_fx,cyl_fy, s0_ux,s0_uy, s1_ux,s1_uy, s2_ux,s2_uy]; use `[:, 2:8]` for pure sensor channels) |
| `data/illusion/illusion_{0.5L..2L}/target_harmonics.json` | FFT harmonics | | `data/illusion/illusion_{0.5L..2L}/target_harmonics.json` | FFT harmonics |
| `data/vortex/vortex_{lamb,taylor}/controlled.npz` | Vortex PPO data | | `data/vortex/vortex_{lamb,taylor}/controlled.npz` | Vortex PPO data |
@@ -0,0 +1,105 @@
{
"schema_version": "sr_legacy_manifest_v1",
"snapshot_id": "pre_fix_round1",
"created_at_utc": "2026-07-16T14:00:00Z",
"status": "historical_frozen",
"pipeline": "legacy_karman_illusion",
"purpose": "Baseline snapshot before Stage1-3 contract repair. All results and data are canonical from previous sessions; do NOT overwrite.",
"git_commit": "4feac49f1cc32925cbac9c2f24a5beaaba74b796",
"git_branch": "main",
"git_upstream": "origin/main",
"git_ahead_behind": "+0/-0",
"git_dirty": true,
"worktree_notes": "SR_analysis has 4 modified tracked files (PIPELINE.md, STAGE_1_INFER.md, results/README.md, stage_1_infer.py). Other modules (LegacyCelerisLab, drl_pinball) also modified but excluded from this SR snapshot scope. Untracked V5 files (bridge_v5.py, stage_2_fit_v5.py, data/v5/, results/formulas_v5/) excluded.",
"environment": {
"pycuda_3_10": {
"python": "3.10.13",
"numpy": "1.26.4",
"torch": "2.5.1+cu124",
"sympy": "1.13.1",
"stable_baselines3": "2.4.0",
"cuda_available": "check at runtime (sandbox may differ)",
"platform": "Linux-6.8.0-124-generic-x86_64"
},
"sr_env": {
"python": "3.10.20",
"numpy": "2.2.6",
"sympy": "1.14.0"
},
"gpu": "device 2 recommended; verify with nvidia-smi -i 2 at runtime"
},
"ignored_npz_count": 46,
"ignored_npz": [
{"path": "src/SR_analysis/data/illusion/illusion_0.5L/target.npz", "size_bytes": 8940, "sha256": "ab0832bf6418afa8b4ba9c9527c3c2fde2628b76e4d6513f8f5ebba08c00e868"},
{"path": "src/SR_analysis/data/illusion/illusion_0.6L/target.npz", "size_bytes": 8940, "sha256": "e6e4dd001c8ca2a6c5a481983e762f5caf74f61a13c7e93bdad8010fe758f12f"},
{"path": "src/SR_analysis/data/illusion/illusion_0.75L/controlled.npz", "size_bytes": 14616, "sha256": "f4e88a26ce5cb2121a40f4c098fa5f3151ba0ea1c1c0e2d6a6e1cf58f6f01499"},
{"path": "src/SR_analysis/data/illusion/illusion_0.75L/target.npz", "size_bytes": 8940, "sha256": "c96e23010f2f11f4a6a46c571acbd9ccb2f80464a5242cb02dcf62df512d0f4a"},
{"path": "src/SR_analysis/data/illusion/illusion_0.8L/target.npz", "size_bytes": 8940, "sha256": "8eca70a94aa937fb59a1e6121b6a2d1603ff48eac4be227c2cb46fb4b5c17d1d"},
{"path": "src/SR_analysis/data/illusion/illusion_1.2L/target.npz", "size_bytes": 8940, "sha256": "ba5236e029658cf9e5b1ee0db9af2353e4da0b5a5d405cef5fd4a49e64cc2656"},
{"path": "src/SR_analysis/data/illusion/illusion_1.5L/controlled.npz", "size_bytes": 14616, "sha256": "78b13c41c2e9913d3b18fb0c457e6c6a778ac8e7c8afebfa3e0a1811a10712a5"},
{"path": "src/SR_analysis/data/illusion/illusion_1.5L/target.npz", "size_bytes": 8940, "sha256": "69c7885632247848c47b5b1405bca5b5e3c7d1cf922994c03f41484e7fae945f"},
{"path": "src/SR_analysis/data/illusion/illusion_1L/controlled.npz", "size_bytes": 14616, "sha256": "755dfc4b3ef4e25198c17abfb4eecf6cd98b2351b5183557ca861d04cd9e1599"},
{"path": "src/SR_analysis/data/illusion/illusion_1L/target.npz", "size_bytes": 8940, "sha256": "33c0034102b14b3495e2ec3944a4d788cb01f7833fc438f4a8302b8caf15038e"},
{"path": "src/SR_analysis/data/illusion/illusion_2L/target.npz", "size_bytes": 8940, "sha256": "b4d553bb11f088bb31c85137a401551a1c575984eeb56d4ce8df175c5d1b2009"},
{"path": "src/SR_analysis/data/karman/karman_re100/controlled.npz", "size_bytes": 13804, "sha256": "efd8eaab807dfbaafd741e202f543a7654ed95b201df3a2246e9a8eaf3ed62b6"},
{"path": "src/SR_analysis/data/karman/karman_re100/target.npz", "size_bytes": 3880, "sha256": "c50dd977de325d603246c8a5b2dcc8972a57a255a46bece69688963dc15a18a8"},
{"path": "src/SR_analysis/data/karman/karman_re100/uncontrolled.npz", "size_bytes": 10112, "sha256": "e1ca266ee8e18c8019eeab839ca4e7e73b7609d6adf95c1cb94b7333a8e55c8d"},
{"path": "src/SR_analysis/data/karman/karman_re200/controlled.npz", "size_bytes": 13804, "sha256": "ec7f38dc3a3ee0fce55ffa1e6463a7c6a0c2d0bf00b6d98850da07e65282240e"},
{"path": "src/SR_analysis/data/karman/karman_re200/target.npz", "size_bytes": 3880, "sha256": "140a0794de258bc8bcf21bd99e0bbbcaad1093c1734a8e9a453faa3ea65cb40f"},
{"path": "src/SR_analysis/data/karman/karman_re200/uncontrolled.npz", "size_bytes": 10112, "sha256": "0bbd318ce1e27ece0ebf10d6adf6291987bd201474e413eedf572bd2f2541756"},
{"path": "src/SR_analysis/data/karman/karman_re400/controlled.npz", "size_bytes": 18042, "sha256": "d88141a9299d0092ff82c48e5436c8f1f27f7490ff35abf22f6cda62cfe89453"},
{"path": "src/SR_analysis/data/karman/karman_re400/target.npz", "size_bytes": 3880, "sha256": "c63869234ebf87145bd1bbe5212356885944a22cc92ccfa78931a04ca70c86cb"},
{"path": "src/SR_analysis/data/karman/karman_re400/uncontrolled.npz", "size_bytes": 15150, "sha256": "7b9250af15e1c8ed1507468474ca35aef4eaae2cee086b1289ef9f1f663db098"},
{"path": "src/SR_analysis/data/karman/karman_re50/controlled.npz", "size_bytes": 13804, "sha256": "302bca1d677e1cd82634e5eece612f6a183213d29e7f03f3b5abe42f5bdff2e2"},
{"path": "src/SR_analysis/data/karman/karman_re50/target.npz", "size_bytes": 3880, "sha256": "5c4f646e7b10861e5578f7b678364434d0ca75497e02e01eb356b958206ee3fa"},
{"path": "src/SR_analysis/data/karman/karman_re50/uncontrolled.npz", "size_bytes": 10112, "sha256": "b8be401ccb6c2e0ca745a81e02bcf6e8ef02917038d4537e9946ad20ba0c3512"},
{"path": "src/SR_analysis/data/vortex/vortex_lamb/controlled.npz", "size_bytes": 9758, "sha256": "b6e7e4c9c30daabd126762483d3f5f63641242aedb6df4905e58126e5e38a5f6"},
{"path": "src/SR_analysis/data/vortex/vortex_lamb/target.npz", "size_bytes": 3880, "sha256": "7ca30bd783776d52b2297c3fffe2598614bebfe7cefa43f2ef35e1b1e9302b2b"},
{"path": "src/SR_analysis/data/vortex/vortex_taylor/controlled.npz", "size_bytes": 9758, "sha256": "4c912c96fd33ee19c3bc93bcb92d8a68e04f8f01c496a71434c61866a8d0da25"},
{"path": "src/SR_analysis/data/vortex/vortex_taylor/target.npz", "size_bytes": 3880, "sha256": "e98045241935e157230e6fcbcc50a1fcd868a24b1b9e4f9d1da8a22397e050c3"},
{"path": "src/SR_analysis/data/steady/steady/controlled.npz", "size_bytes": 12758, "sha256": "98fe6ff3c1e90684238681758a47ce3f48818c4503c1bde2606d4c4bcdb7a3ae"},
{"path": "src/SR_analysis/data/steady/steady/target.npz", "size_bytes": 3880, "sha256": "c4eac1e1cfacb9b6a0e2f4d374ea3926e5e39af6c736eeb1fa1fd0f64c239f45"},
{"path": "src/SR_analysis/data/steady/steady_data.npz", "size_bytes": 16902, "sha256": "fedf4f2216e5ff2c7b1a0becf27de975c2e22a2a4a7d360fc1c7cd22c86c8c6c"},
{"path": "src/SR_analysis/data/v5/ill_075L_sc/controlled.npz", "size_bytes": 19913, "sha256": "57e1675c866adcc272c81294aea4d1fdfce43a3a6f10cd3e8bbae602c64b5132"},
{"path": "src/SR_analysis/data/v5/ill_075L_sc/target.npz", "size_bytes": 4704, "sha256": "06ac9d58563f58883b8d88f78810bdfc3a8eab98d88d44e00ba68efe72fbc350"},
{"path": "src/SR_analysis/data/v5/ill_15L_sc/controlled.npz", "size_bytes": 19207, "sha256": "937c3d082227288ca1e3c2ecfca1cbdb3c5431602c4ea8dba78ec2efc8b52264"},
{"path": "src/SR_analysis/data/v5/ill_15L_sc/target.npz", "size_bytes": 4713, "sha256": "3d851db7f57c82a61bbbb6320de4fe3fded4264743e824285de081c84f6380ac"},
{"path": "src/SR_analysis/data/v5/ill_1L_sc/controlled.npz", "size_bytes": 19106, "sha256": "ff59ba323da500b21c4f84b8bed4f85fdb1839eabf9ce72b94bbe39445b112ab"},
{"path": "src/SR_analysis/data/v5/ill_1L_sc/target.npz", "size_bytes": 4709, "sha256": "f6999035db270b0b82d1be89b39857b08b1e7c0e89d038fb8d77fcce26f1d585"},
{"path": "src/SR_analysis/data/v5/ill_2L_sc/controlled.npz", "size_bytes": 19792, "sha256": "29823406aa3502c31d2af268067dc647f4a312aaeacc8928c588e4253e96ab24"},
{"path": "src/SR_analysis/data/v5/ill_2L_sc/target.npz", "size_bytes": 4715, "sha256": "5ceb8245bfed5500900ccd130ebd30fde4f01f2cf17a750253d6bc29703c3a16"},
{"path": "src/SR_analysis/data/v5/kar_d075_sc/controlled.npz", "size_bytes": 20587, "sha256": "f9a01654d1afebcb7ced4c7f3b91f8720f9e14e75bf91ce1faf366f1d1b62328"},
{"path": "src/SR_analysis/data/v5/kar_d075_sc/target.npz", "size_bytes": 3521, "sha256": "db8b07109ae6c0463f4b3e33aec4c9fd51448ce27b5bcb3d7ac5c16143d2bdfa"},
{"path": "src/SR_analysis/data/v5/kar_d15_sc/controlled.npz", "size_bytes": 20518, "sha256": "1a5d3e0a82e48dd964ae91e37e9f85e0c5bce38f6614e3ed16e452caaf90c5cd"},
{"path": "src/SR_analysis/data/v5/kar_d15_sc/target.npz", "size_bytes": 3533, "sha256": "cb606d3fd511afffb3e3bbc3d22a4ef6f2806d442b2d31c216968c440f61e680"},
{"path": "src/SR_analysis/data/v5/kar_d2_sc/controlled.npz", "size_bytes": 20493, "sha256": "46edfb1e596f8daa1afc5b72e81c38295a45408dac72b89c2ab21b6748fb1f4e"},
{"path": "src/SR_analysis/data/v5/kar_d2_sc/target.npz", "size_bytes": 3540, "sha256": "2c081891d2981d5c211258e37d6d877c9a255aa2689ab8ac9120b9fc2eadfeec"},
{"path": "src/SR_analysis/data/v5/kar_re100_sc/controlled.npz", "size_bytes": 20588, "sha256": "bb020692afa1770982fbb67e8fd67f65fe9c8f8e70a43a7ff4b5bea4a964cd94"},
{"path": "src/SR_analysis/data/v5/kar_re100_sc/target.npz", "size_bytes": 3536, "sha256": "e8c2ea5456b8bbb321d4f24b0e9170819c7cb4309497825e2bbd01657a7a0535"}
],
"legacy_models": [
{"name": "d1a3o12_re50.zip", "path": "models/old/d1a3o12_re50.zip", "size_bytes": 154169, "sha256": "dfd2163b0bf89115303015b010aa5ad5fe95b8f4bd81176a356714a1984445f4"},
{"name": "d1a3o12_re100.zip", "path": "models/old/d1a3o12_re100.zip", "size_bytes": 154169, "sha256": "148240c1dcb8b11d8e5a0a9e991f6874c4e799c9744707bf0fb32a8efeb0468d"},
{"name": "d1a3o12_re200.zip", "path": "models/old/d1a3o12_re200.zip", "size_bytes": 154169, "sha256": "62ceb682fbfcff1119e049d306d3ac05144b780f166c2835d7d01ca719ffad11"},
{"name": "d1a3o12_re400.zip", "path": "models/old/d1a3o12_re400.zip", "size_bytes": 154169, "sha256": "e251240ed1179290345f0331f7e014abb74429ec517039fe5836f5f070352617"},
{"name": "vortex_lamb.zip", "path": "models/old/vortex_lamb.zip", "size_bytes": 155424, "sha256": "69f2a863b08d2313a46523a5cda8ecc3b988b98cef5a7b67f0cae0b2fc9d6e62"},
{"name": "vortex_taylor.zip", "path": "models/old/vortex_taylor.zip", "size_bytes": 155543, "sha256": "58882721900f1eb0779f64aea0a2e233d307a94b640b9aaf33efa036db3c79e3"},
{"name": "imit_075L_1U.zip", "path": "models/250525/d1a3o12_250525_imit_075L_1U.zip", "size_bytes": 154176, "sha256": "661afbb11d5229d1f6a10b8d2e44809adc432aae4d97fde78eabf67b68ec39c0"},
{"name": "imit_1L_1U.zip", "path": "models/250525/d1a3o12_250525_imit_1L_1U.zip", "size_bytes": 154176, "sha256": "637a6a2e9972dd189aff5f64761d12dd79854ae7201dedab994d3bf6b9c7d95e"},
{"name": "imit_075L_2U.zip", "path": "models/250525/d1a3o14_250525_imit_075L_2U.zip", "size_bytes": 157326, "sha256": "7681d1b3aa24c401100c53bf5f010d64e9b156a5e32b9157df4c4fe7f3567d6e"},
{"name": "imit_1L_2U.zip", "path": "models/250525/d1a3o14_250525_imit_1L_2U.zip", "size_bytes": 157326, "sha256": "835d116f839cac051648fe3c8bbdde45433e8a8bd290f24fffb80dcb16139bfa"},
{"name": "imit_15L_2U.zip", "path": "models/250525/d1a3o14_250525_imit_15L_2U.zip", "size_bytes": 157326, "sha256": "3e115a36d4365cfc70994c9307053aa44d9c3ded8c4312f636520b62862be9b6"}
],
"canonical_results_status": "historical_frozen",
"canonical_numeric_conflicts": [
"scene_registry.json: illusion_0.75L similarity=0.982194947, results/README.md: writes 0.978",
"scene_registry.json: illusion_1L similarity=0.958060958, results/README.md writes 0.970",
"karman_joint_front: fitted expression includes 0.385*daB_dt; README and PIPELINE show simplified deployment form without it"
],
"excluded_from_snapshot": {
"v5_data": "src/SR_analysis/data/v5/ (untracked, not in legacy round 1 scope)",
"v5_formulas": "src/SR_analysis/results/formulas_v5/ (untracked, not in legacy round 1 scope)",
"v5_scripts": ["src/SR_analysis/bridge_v5.py", "src/SR_analysis/stage_2_fit_v5.py"],
"other_modules": "drl_pinball and LegacyCelerisLab modifications excluded from this SR snapshot"
}
}
@@ -0,0 +1,162 @@
---
name: SR Legacy Pipeline
overview: 冻结当前 legacy 证据后,修复并版本化 Kármán 与 Illusion 的 Stage 1–3 数据、特征、公式和闭环契约;第一轮只在四个 Kármán 训练 Re 与 Illusion 0.75L/1L 上完成端到端复现并暂停复核。DTW 闭环是公式选择的最终判据,离线指标仅用于排查时序错位、共线性和部署不一致。
todos:
- id: snapshot-baseline
content: 创建 SR 专用 Git 快照并记录 ignored 数据、模型和环境资产清单
status: pending
- id: freeze-contracts
content: 统一 legacy 场景、layout、无量纲化、G 映射和 DTW 契约
status: pending
- id: repair-stage1
content: 修复并版本化 Kármán/Illusion Stage 1 数据与因果时序
status: pending
- id: unify-features
content: 实现 Stage 2/3 共用的 stateful 特征和导数计算
status: pending
- id: repair-stage2
content: 实现可复现的 joint front/rear PySR 拟合与公式 schema
status: pending
- id: repair-stage3
content: 实现公式/G 部署、versioned DTW 与完整 validation provenance
status: pending
- id: run-round1
content: 在指定 Conda 环境和 GPU 2 完成第一轮 legacy Stage 13 复跑
status: pending
- id: review-candidates
content: 审计历史与 candidate 结果并暂停等待 canonical 晋升决定
status: pending
isProject: false
---
# SR Legacy 端到端完善计划
## 范围与原则
- 主体系冻结为 legacyV5、erase、unseen-Re、Vortex、1.5L、论文绘图均不进入第一轮验收。
- CFD、PPO 推理与闭环统一使用 `pycuda_3_10` 和 GPU 2PySR/SINDy 使用 `sr_env`
- 所有新数据、公式和 validation 先写入带 `run_id` 的 staging 目录,不覆盖现有 [`data/`](src/SR_analysis/data/) 与 [`results/`](src/SR_analysis/results/) canonical 文件。
- 离线拟合的 \(R^2\)、MAE 等只作为数据/模型诊断;公式取舍以 legacy DTW 闭环结果为最终标准。legacy DTW 固定为独立版本,不混用 V5 的 target-amplitude normalization。
- observation、action、force 均使用实际物理无量纲量;明确 legacy action 是圆柱表面切向速度,统一定义 \(\alpha=u_{wall}/U_0\)。
- G 映射作为结构约束:front 为 odd channelupper/lower 交换并反号;新公式必须记录 rear shared-head 的 anchor 和镜像构造。
```mermaid
flowchart LR
snapshot["Git快照与资产清单"] --> contracts["冻结数据和物理契约"]
contracts --> infer["Stage1 PPO推理"]
infer --> fit["Stage2 PySR拟合"]
fit --> deploy["Stage3 CFD闭环"]
deploy --> review["结果审计与人工复核"]
review -->|"通过后"| promote["晋升canonical"]
review -->|"不通过"| diagnose["时序或公式诊断"]
```
## 第一阶段:冻结当前状态与建立可回滚基线
- 按用户选择先创建一个专用 Git 快照提交;提交前按 Git 协议核对完整 status、diff 和近期提交,只纳入当前 SR 工作及明确相关文件,不混入其他模块的无关改动。
- 记录当前 commit、工作树状态、Conda/Python/PySR/Julia/CUDA/GPU 信息,以及 legacy 模型最终解析路径。
- 为 ignored 的 NPZ 和 PPO 模型生成只读资产清单:路径、大小、SHA-256、NPZ keys/shape/dtype。Git 快照本身不能恢复 ignored 数据,因此第一轮脚本必须禁止覆盖这些文件;资产清单用于检测意外变化。
- 将现有 [`scene_registry.json`](src/SR_analysis/scene_registry.json)、[`results/README.md`](src/SR_analysis/results/README.md)、formula 和 validation 标记为 `historical_frozen`,不立即判断 0.978/0.970 与 0.982/0.958 哪组为最终 canonical。
## 第二阶段:冻结跨 Stage 的唯一契约
### 场景与数据契约
- 在 [`configs.py`](src/SR_analysis/configs.py) 集中定义 Kármán/Illusion 的:几何、目标位置、`Re_code``Re_D`、SI、\(dt_c=SI/(D/U_0)=SI/2000\)、动作 decoder、FIFO 初始动作、sensor/force/action layout、target slices。
- 保持 legacy 物理定义与数值:`Re_code=2Re_D`;论文输出可以转换成 `Re_D`,文件名和模型名继续保留 code Re。
- 明确 Illusion target 契约为 `[target_fx,target_fy, six_sensor_channels]`,传感器比较使用 `[:,2:8]`,策略目标力使用 `[:,0:2]`
- 先核对真实 native body order,不直接交换历史 NPZ 列;新数据写显式 layout metadata,内部统一使用 `front/upper/lower`,旧 `top/bottom` 名称通过兼容映射读取。
### 物理特征与 G 映射
- 收敛 [`feature_builder.py`](src/SR_analysis/utils/feature_builder.py) 与 [`g_operator.py`](src/SR_analysis/utils/g_operator.py) 的重复实现,使所有 Stage 调用同一个 G 和无量纲化入口。
- 增加 `G(G(x))=x`、target drag even/target lift odd、rear shared-head equivariance 测试。
- 缺失 required feature 时默认报错,禁止再由 `build_feature_matrix` 静默补零;仅历史兼容读取可显式选择零填充。
### legacy DTW
- 在 [`cfd_interface.py`](src/SR_analysis/utils/cfd_interface.py) 中固定 scene-specific metric`legacy_dtw_v1_abs_n_unclipped`,明确 lag channel、target slice、window、无幅值归一化、无 clipping。
- 文档明确 legacy 与 V5 DTW 不直接混用;第一轮不改变历史算法。
## 第三阶段:修复 Stage 1 并隔离运行产物
- 修改 [`stage_1_infer.py`](src/SR_analysis/stage_1_infer.py) 使路径不依赖 cwd,配置 dry-run 能在不启动 CUDA 时完成模型、target、norm 和输出路径检查。
- 修复 Illusion 的目标位置配置、target force 索引和 `controlled.npz``target_forces` 缺失;首步 observation、target harmonic phase 与已验证 legacy 测试保持一致。
- 明确数据采集时序并写入 metadata:哪个 observation 是 action 执行前或执行后、target phase index、action command index。先以旧推理循环和 legacy test 为事实依据,不凭数组同名假设对齐。
- 新 NPZ 同时保存 schema/version、layout、SI、\(dt_c\)、normalization source、model hash 和 alignment;保留旧 key alias 只用于兼容。
- Stage 1 只写 `data/runs/<run_id>/...`;没有显式 promotion 时拒绝覆盖历史数据。
## 第四阶段:让离线与在线特征严格一致
- 建立 stateful feature state,统一保存 previous observation、previous target force、previous actions 和 \(dt_c\)。
- 在真实 legacy 数据上比较两种主要 alignment:当前同行 \(X_t\to a_t\) 与按控制循环推导的 \(X_t\to a_{t+1}\)。这一步是契约审计,不用较高 \(R^2\) 自动决定正确性;最终采用与 PPO 决策时刻一致的因果定义。
- 对同一 trajectory 验证:Stage 2 批量计算与 Stage 3 逐步计算的每个 required feature 数值一致;包含 `du_a_dt``dCl_tot_dt``dCd_err_dt` 和 action derivative。
- 将 alignment、导数定义和丢弃的初始样本数写入 formula metadata,避免未来再次产生“拟合公式与部署公式语义不同”。
## 第五阶段:修复 Stage 2 的双头拟合与公式记录
- 修改 [`stage_2_fit.py`](src/SR_analysis/stage_2_fit.py),使 per-scene 和 joint 都生成:
- front head
- rear shared head;另一个 rear action 由 Stage 3 通过 G 构造。
- 统一 formula JSON schema,兼容读取旧 `feature_names/feature_keys``best_sympy`,新写出至少包含:training scenes、feature order/definitions、fitted expression、deployment expression、role/anchor、G version、data hashes、alignment、\(dt_c\)、PySR seed/operators/complexity、in-sample diagnostics。
- 固定 PySR seed并提供极短 smoke 参数;候选公式写入 `results/runs/<run_id>/formulas/`,不覆盖现有 formula。
- 对 Kármán 同时保留:
- fitted expression
- 人工物理审计后的 deployment expression,例如删除部署时恒零/共线产生的 `daB_dt` 项。
二者必须分别带 hash,禁止用一个公式的 DTW 给另一个公式背书。
- SINDy/STLSQ 第一轮只作为内部筛选诊断:检查 feature 共线、lag 与支持稳定性,不作为闭环候选的自动裁决,也不写成正文核心方法。
## 第六阶段:修复 Stage 3 的真实部署
- 修改 [`stage_3_validate.py`](src/SR_analysis/stage_3_validate.py),使用统一 formula loader:有 symbolic expression 时直接编译,只有明确 linear schema 时才读取 coefficient;禁止缺 coefficient 时默认为全零。
- 使用与 Stage 2 完全相同的 stateful feature builder 和 \(dt_c\),并按 formula metadata 构造 front 与 G-mirrored rear actions。
- Kármán和 Illusion 共享部署核心,只在 target-force/reference 和环境 builder 上分支。
- validation 文件按 scene、mode、formula hash 和 SI 独立命名,异常必须返回非零;不再让 PPO、SR、uncontrolled 或不同 SI 相互覆盖。
- 每个结果记录:formula/data/model hash、Git SHA、环境、scene config、metric version、n_steps、full/tail DTW、per-channel DTW、action range、NaN/Inf、termination reason 和原始 telemetry 路径。
## 第七阶段:分层运行与第一轮验收
### 无 GPU 与短 smoke
- 先运行 config/NPZ/formula schema、G、DTW fixture、batch-vs-step feature equivalence 测试。
-`sr_env` 中用 Kármán Re100 小样本、极少 iterations 验证 PySR/Julia 启动、front/rear 双输出和 Stage 3 可读性。
-`pycuda_3_10` 中确认 GPU 2 可用,再做 Kármán Re100 与 Illusion 1L 的最短 wiring smoke;短跑只检查 finite/actions/schema,不用 DTW 判断控制效果。
### 完整第一轮
按以下顺序运行,单场失败即停止批量扩展:
1. Kármán Re100 Stage 1→2→3
2. Illusion 1L Stage 1→2→3
3. Kármán code Re 50/100/200/400 joint fit 与闭环;
4. Illusion 0.75L+1L joint fit 与各自闭环。
验收条件:
- Stage 1 数据、Stage 2 formula 和 Stage 3 telemetry 均有完整 provenance
- required features 无静默零填充,批量/在线特征一致,G 测试通过;
- 每个 formula 实际部署 expression 与 validation 中的 hash 一致;
- CFD 无 NaN/Inf 或动作异常;
- legacy DTW 可与历史结果并列比较。若结果改变,不以“必须复现旧数字”强行通过,而是通过 telemetry 定位是 target 修复、alignment 修复、导数修复还是公式变化造成;
- 第一轮结束后只生成审计报告和 candidate registry,不自动修改 canonical [`scene_registry.json`](src/SR_analysis/scene_registry.json)。到此暂停,请用户复核后再决定晋升或继续机制分析。
## 后续阶段:第一轮复核后再启动
### 机制与公式项价值
- Kármánuncontrolled、constant rear、front-only、full fitted law、reduced deployment law,比较总 DTW、ux/uy、drag/lift、控制功率。
- Illusiontarget-force feedforward-only、feedback-only、error-only、phase hybrid、front-only、rear-only;重点检验当前 joint front 化简为 `target_Cd + phase term` 后是否只是周期轨道上的等价表示。
- 做受限 lag cross-correlation、coherence/cross-spectrum、delay embedding 和 target/observed quadrature phase;候选仍由 CFD DTW 裁决。
- 检查 Illusion PPO 与公式的完整 G-equivariance,特别是 `target_Cd` 为 even 而 front action 应为 odd 的结构冲突。
### 泛化与扩展
- 流程稳定后再补 unseen-Re;随后恢复 Kármán law 到 Lamb/Taylor 的专用 Vortex validator。
- 重采 1.5L trajectory,分析高频/phase-state 边界;不把 lagged-action 平凡解标为 canonical。
- 最后再引入 V5 数据、生成论文图、同步 README/registry/报告和写作。
## 文档与长期维护
- 更新 [`README.md`](src/SR_analysis/README.md)、[`PIPELINE.md`](src/SR_analysis/PIPELINE.md) 与 Stage 文档,使命令、环境、GPU、schema 和实际代码一致。
- `scene_registry` 只索引 artifact ID/path/hash/run IDformula 和 validation JSON 各自保存原始数值。README 与后续图表从 registry 自动生成,避免手工重复 0.978/0.970 等数字。
- 在 SR 根目录最终保存这份长期计划,并在每轮完成后更新状态、风险和下一验收点。
+15 -1
View File
@@ -168,6 +168,12 @@ def gen_target_states_at(t, harmonics):
def infer_illusion(scene_name: str, device_id: int, out_dir: str, n_steps: int, target_only: bool) -> dict: def infer_illusion(scene_name: str, device_id: int, out_dir: str, n_steps: int, target_only: bool) -> dict:
"""Run PPO inference for illusion scene.
Output target.npz layout (8 columns):
[cyl_fx, cyl_fy, s0_ux, s0_uy, s1_ux, s1_uy, s2_ux, s2_uy]
Consumers should use target[:, 2:8] for pure sensor signals.
"""
cfg = get_scene(scene_name) cfg = get_scene(scene_name)
nu, u0, si, l0 = cfg["nu"], cfg["u0"], cfg["sample_interval"], 20.0 nu, u0, si, l0 = cfg["nu"], cfg["u0"], cfg["sample_interval"], 20.0
action_scale, action_bias = cfg["action_scale"], np.array(cfg["action_bias"]) action_scale, action_bias = cfg["action_scale"], np.array(cfg["action_bias"])
@@ -195,12 +201,20 @@ def infer_illusion(scene_name: str, device_id: int, out_dir: str, n_steps: int,
warmup = int(4 * nx / u0) warmup = int(4 * nx / u0)
ff.run(warmup, np.zeros(4, dtype=DATA_TYPE)) ff.run(warmup, np.zeros(4, dtype=DATA_TYPE))
# Target layout: 8 columns = [cyl_fx,cyl_fy,s0_ux,s0_uy,s1_ux,s1_uy,s2_ux,s2_uy]
# obs comes from: 1 target cylinder + 3 sensors added to FlowField
target_states = [] target_states = []
for _ in range(FIFO_LEN): for _ in range(FIFO_LEN):
ff.run(si, np.zeros(4, dtype=DATA_TYPE)) ff.run(si, np.zeros(4, dtype=DATA_TYPE))
target_states.append(ff.obs.copy()[0:8]) target_states.append(ff.obs.copy()[0:8])
target_arr = np.array(target_states, dtype=np.float32) target_arr = np.array(target_states, dtype=np.float32)
np.savez(os.path.join(out_dir, "target.npz"), target_states=target_arr[:, :6]) # BUGFIX (2026-07-14): was target_arr[:, :6] which omitted s2 (bottom sensor) and
# mixed cylinder force (col0-1) with sensor channels. The 8-column layout is the
# canonical format — consumers use target[:, 2:8] to extract the 6 pure-sensor
# channels. Historical note: the old gen_illusion_target.py already wrote 8 columns,
# so on-disk files were correct via that path, but re-running this script would
# overwrite them with truncated data.
np.savez(os.path.join(out_dir, "target.npz"), target_states=target_arr[:, :])
harmonics = analyze_harmonics(target_arr, n_harmonics=5) harmonics = analyze_harmonics(target_arr, n_harmonics=5)
json.dump(harmonics, open(os.path.join(out_dir, "target_harmonics.json"), "w"), indent=2) json.dump(harmonics, open(os.path.join(out_dir, "target_harmonics.json"), "w"), indent=2)