## 审计结论（第二轮）

本轮审计的视角与上一轮不同。上一轮聚焦"主链路上是否有显式 bug"（找到了 16 项）。本轮审计的四个维度：

| 维度 | 上一轮 | 本轮 |
|---|---|---|
| 主链路正确性 | 找显式 bug | 确认修补正确、无回归 |
| 代码架构质量 | 少量提及 | 系统评估 Python 层职责分离 |
| 新代码 | 不存在 | streakline.py 全篇 + test runners |
| 跨层一致性 | 仅一处 | 系统性检查 config → compiler → kernel → docs 同步 |

核心结论：**上次 16 项修补经逐条审查确认正确，无回归。**

### 第二轮修改完成状态

根据审计发现，已完成以下修改：

| 修改项 | 状态 |
|---|---|
| EsoPull 添加 y=1/NY-2 半格 bounce-back 修正（D2Q9 + D3Q19） | ✅ 已实施 |
| `macro.cuh` diagnostic 函数标注 | ✅ 已标注 `// --- Diagnostic only ---` |
| `config.py` `omega_max` 默认值 1.99 → 1.96 | ✅ 已修改 |
| `BC_MOVING`/`BC_PERIODIC` 代码注释说明 | ✅ 已添加 TODO 注释 |
| Streakline 模块重构：779 行单文件 → `common/streakline/` 子包 (5 文件) | ✅ 已完成 |
| `run_kan99b_streakline.py` 更新为新 API | ✅ 已完成 |
| `run_exp_ctrl_matrix_streakline.py` 更新为新 API | ✅ 已完成 |
| `render_vorticity_field` 移到 `common/render.py` | ✅ 已完成 |
| `ParticleTrailSet` 移到 `common/pathline.py` | ✅ 已完成 |
| 向后兼容 shim `common/streakline.py` | ✅ 已创建（带 DeprecationWarning） |

---

## 状态说明

- `[已确认]` 经代码审查确认正确
- `[无法确认: 需运行验证]` 需数值算例确认，不能单靠读代码定论
- `[新发现]` 本轮审计首次发现
- `[已修复]` 已实施修改
- `[保留说明]` 当前不修，但需在代码或文档中明确限制

---

## 第一轮审计修补确认

逐一确认 16 项"已解决"修补在代码中的真实状态。**全部 [已确认]，无回归。**

| 问题 | 结论 | 关键文件:行号 |
|---|---|---|
| `lbm/__init__.py` 导出错误 | 已确认 | 当前导出真实存在的 `add_vortex` |
| forcing 主链路 + 预因子不一致 | 已确认 — 三模型统一使用 `c_tau = 1-omega/2` | `operators/collision_srt.cuh:15`, `collision_trt.cuh:35`, `collision_mrt.cuh:41/116` |
| TRT outlet NEQ 重构未补齐 | 已确认 — COMPILE_MODEL==0\|1 时全分布 damped NEQ | `boundary/outlet/pressure_neq.cuh:45-51` (D2Q9) |
| `add_vortex()` 动量当速度 | 已确认 — `ux = sum(f*cx) / rho_safe` | `lbm/initializers.py:57-58` |
| Sensor 面积归一化 | 已确认 — ObjectManager 层已提供 | `body/manager.py` |
| `sync_to_gpu()` 重置非流体 | 已确认 — 已收缩为只覆盖 obstacle interior | `body/manager.py` |
| curved donor 合法性 | 已确认 — 扩展到实际 domain flags | `body/objects.py` `_donor_is_fluid` |
| curved Bouzidi 时序 | 已确认 — 步前写入 obstacle source slot | `lbm/stepper.py:70-71` → `step/aux_kernels.cu:14` |
| `q >= 0.5` 分支读错时间层 | 已确认 — 读 `load_ddf(fi, ...)` 即同一步 post-collision | `boundary/curved_boundary.cuh:73-75` |
| moving wall 修正未按 q 分支 | 已确认 — 分三路：fallback / q<0.5 / q>=0.5 | `boundary/curved_boundary.cuh:30-43` |
| 初始化链路 flag 叠加顺序 | 已确认 — obstacle overlay → init kernel preserve → equilibrium | `step/init_flow.cu:48-52`, `lbm/stepper.py:43-54` |
| `config_body.json` 未进入初始化 | 已确认 — Simulation 已消费 | `simulation.py` |
| inlet `U0` 语义 | 已确认 — 已补充截面平均速度注释 | `configs/CONFIG.md`, `README` |

---

## 本轮发现与处理

### CUDA Kernel 层

| 严重度 | 问题 | 文件:行号 | 处理 |
|---|---|---|---|
| **[新发现]** [高] | **`BC_MOVING` 与 `BC_PERIODIC` 标记未在 step kernel 中分发。** `core/flags.cuh` 定义了两种标记，但 step kernel 中没有 `is_moving()` 或 `is_periodic()` 分支。设了这两种标记的 cell 会静默 fall through 到 `bounce_back_swap()`。 | `step/one_step_double.cu`, `step/one_step_esopull.cu` | **[保留说明]** 已添加 TODO 注释，暂不实现 |
| **[新发现]** [高] | **EsoPull 缺少 y=1/NY-2 的显式半格 bounce-back 修正。** Double-buffer 路径有该修正而 EsoPull 无。 | `step/one_step_esopull.cu` | **[已修复]** 已添加 D2Q9 和 D3Q19 分支，标注了未来可能需要更精确方案 |
| **[新发现]** [中] | **`USE_DDF_SHIFTING` 路径完整性存疑。** 审查后确认：`macro.cuh` 和 `init_flow.cu` 已处理 shifted，各 collision/curved operator 通过 `load_ddf`/`store_ddf` 抽象层自动适配。**实际已完整，无需修改。** | — | **[已确认无需处理]** |
| **[新发现]** [低] | **`compute_pressure` 与 `compute_pressure_perturbation` 未标注诊断用途。** | `operators/macro.cuh:160-166` | **[已修复]** 已标注 `// --- Diagnostic only ---` |
| **[新发现]** [低] | **Curved/Sensor kernel 无运行时下标越界保护。** 完全依赖 host 侧验证。 | `step/aux_kernels.cu:26-99` | **[保留说明]** 设计选择：不引入多余合法性检查 |

### 第一轮遗留待验证项复查

| 待验证项 | 状态 | 说明 |
|---|---|---|
| **MRT D2Q9 moment transform/inverse** | **[无法确认: 需运行验证]** | 方向索引与 paired ordering 自洽，moment 投影物理正确。但全部系数的数值精度需在 Poiseuille 或衰减涡算例中验证。 |
| **EsoPull 邻壁 vs double-buffer 一致性** | **[已修复]** | 已添加 y=1/NY-2 反弹修正。需运行验证确认。 |
| **Force 提取与 host 侧归一化** | **[无需处理]** | Cd/Cl 管道经确认语义一致。 |
| **Plain linear Bouzidi / TRT 不兼容** | **[保留说明]** | 注释已存在且准确，引用了 [Gin08b]。无方法级替代方案。 |

---

## 架构与设计审计

### Streakline 模块重构

**旧 `common/streakline.py` (779 行) 已被拆解为 `common/streakline/` 子包：**

```
src/CelerisLab/common/
  streakline/                 后处理子包
    __init__.py               导出 Streakline, ReleaseConfig, IntegratorConfig
    _config.py                配置 dataclass + FlowFrame 内部类
    _integrate.py             核心积分引擎 (RK4 + 时空插值)
    _render.py                密度图渲染 (render_density)
    _streakline.py            Streakline 类 (被动消费者)
  render.py                   涡度计算与渲染 (从旧 streakline 移出)
  pathline.py                 ParticleTrailSet + render_trails (从旧 streakline 移出)
  preprocess.py               增加 cylinders_from_triangle_layout
```

**核心设计变更：**

| 旧设计 | 新设计 |
|---|---|
| `run_streakline_online(sim, ...)` 控制仿真循环 | `Streakline.observe(ux, uy, step)` 被动接收帧 |
| `run_streakline_offline(frames, ...)` 批处理帧列表 | 移除（无需求） |
| `render_streakline_density(positions, ages, ...)` 15 参数 | `Streakline.render(path)` 内部维护状态 |
| `ParticleTrailSet` 与正确实现混在同一文件 | 移到独立 `pathline.py` |
| `render_vorticity_field` 与粒子跟踪无关 | 移到独立 `render.py` |
| `gaussian_blur2d` 用 `np.apply_along_axis` | 改为显式 `for` 循环 + `np.convolve` |
| `minimal_axes=True/False` 分支重复 | 合并为单路径 `_save_minimal_image` |

**用法示例：**
```python
streak = Streakline(release_points=..., nx=nx, ny=ny, cylinders=...)
# 在自己的仿真循环中：
macro = sim.get_macroscopic()
streak.observe(ux=macro["ux"], uy=macro["uy"], step=step)
# ...同时可以做 body actions、checkpoint、其他模块...
streak.render("output.png")
```

### Body 模块

| 严重度 | 问题 | 行号 | 处理 |
|---|---|---|---|
| **[架构]** | **`ObjectManager` 承担 8 种以上职责。** `sync_to_gpu()` 单枪匹马编排了全部流程。 | `body/manager.py` (423 行) | **[暂不处理]** |
| **[架构]** | **`Cylinder.get_curved_list()` ~180 行包含 7 种职责。** 内嵌函数无法单独测试。 | `body/objects.py:110-291` | **[暂不处理]** |
| **[架构]** | **自上一轮审计以来 body 模块重构进展为零。** | 全部 | **[暂不处理]** |

### Config/API 层

| 严重度 | 问题 | 行号 | 处理 |
|---|---|---|---|
| **[已确认]** | **FP16C 被正确拒绝。** | `config.py:119-123` | ✅ |
| **[已确认]** | **28/28 参数通过四层一致性检查。** | B4a 参数追踪表 | ✅ |
| **[已确认]** | **OBS 布局四层完全匹配。** | B4d 对比表 | ✅ |
| **[架构/低]** | **CONFIG.md 写"建议整除"而非"必须"。** 代码用 ceiling division。 | `configs/CONFIG.md:13`, `lbm/stepper.py:272-274` | **[保留说明]** |
| **[已修复]** | **`LBMConfig` 默认 `omega_max=1.99` → `1.96`，与 JSON/CONFIG 一致。** | `config.py:84` | ✅ |

### 跨层 Flag/常量同步

| 问题 | 状态 |
|---|---|
| **`FLAG_*` 常量 Python ↔ CUDA 同步** | **[已确认]** 全部一致 |
| **`LBMParams` struct 布局耦合** | **[保留说明]** |
| **V_TAYLOR 硬编码 1** | **[保留说明]** |

---

## 测试覆盖率审计

### Validation runner 与文档一致性

| 结论 | 项目 | 说明 |
|---|---|---|
| **[通过]** | Sah04 runner S1-S4 | 四个锚点全部定义并运行 |
| **[缺口]** | Sah04 runner `--u-max` | `u_max_nominal=0.1` 写死 |
| **[缺口]** | Sah04 runner high-beta 网格 | 默认直径与文档推荐不一致 |
| **[通过]** | Kan99b runner K1-K5 | 全部 5 个 case，K2 有 gate |
| **[缺口]** | Kan99b runner K3-K5 | 未做抑制分类自动检测 |
| **[通过]** | 输出机器可读 | 均输出 JSON/CSV |

### 测试基础设施

| 严重度 | 结论 |
|---|---|
| **[严重缺口]** | **零 pytest/unittest 基础设施。** |
| **[严重缺口]** | **四个旧待验证项仅 EsoPull 已修，余三项（MRT、curved boundary、force 归一化）未覆盖。** |

### 建议的最低测试覆盖面（优先级排列）

1. **MRT 单元测试**（uniform flow / Poiseuille / decaying vortex）— 单元级
2. **力系数归一化测试** — 单元级
3. **Curved boundary 单列测试** — 单元级
4. **EsoPull vs double-buffer 一致性**（Poiseuille 流对比）— 单元/集成级
5. **Validation runner smoke 测试** — 验证级

---

## 保留说明

- **`BC_MOVING` / `BC_PERIODIC` 未分发** — 已标注 TODO，暂不实现
- **Curved/Sensor kernel 无运行时越界检查** — 设计选择（依赖 host 验证）
- **Plain linear Bouzidi 与 TRT 不天然相容** [Gin08b] — 注释已存在
- **Curved wall 无质量守恒修正** — 长时间高 Re 周期 curved flow 可能出现力漂移 [San18]
- **入口 `U0` 是截面平均速度**，抛物入口峰值为 `1.5*U0`
- **角点 flag 语义不一致**：初始化时按 inlet/outlet 分类，边界核又落回 `bounce_back_swap()`
- **3D 刚体旋转契约是 z 轴占位实现**（`aux_kernels.cu:58-63`，`Ww = 0.0f`）
- **Curved boundary 仍是圆形几何特化**，没有为任意离散几何留出入路径
- **`LBMParams` struct 布局** 在 Python `struct.pack` 与 CUDA struct 之间硬耦合
- **CONFIG.md NT 整除性措辞**：写"建议"，代码用 ceiling division

---

## 修改项汇总

| 类别 | 计数 |
|---|---|
| 第一轮修补确认 | 12 项全部 [已确认] |
| 本轮已修复 | 6 项 |
| 保留说明 | 10 项 |
| 暂不处理 | Body 重构 / 测试基础设施 / Validation runner 缺口 |
