CelerisLab/tests/audit/审计报告_kernel_layer.md

127 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Sub-agent A: CUDA Kernel & Fix Verification
## 1. 旧修正确认
### 结论: [已确认] Curved Bouzidi 时序 — aux_kernels.cu:26-65 + stepper.py:71 + one_step_double.cu:20
**理由**: `stepper.py:71``_launch_curved()``OneStep` 之前调用。`aux_kernels.cu` 的 `CurvedBoundaryKernel` 写入 `f.ddf_gpu`(当前 buffer其后 `OneStep` 从同一 buffer 做 `stream_pull_load` — 同一时间步内完成"前写入→后拉取"。`one_step_double.cu:20` 中的 `apply_boundary_pull``is_curved(fl)` 直接 return确保流体节点不会二次覆盖。调用顺序确认无误。
### 结论: [已确认] q>=0.5 分支时间层 — curved_boundary.cuh:73-75
**理由**: `q >= 0.5` 分支读取 `load_ddf(fi, index_f(k_f, dir_opp))`,其中 `fi` 是当前步骤的 DDF buffer。旧代码读的是前一时间步的缓冲区`fi_in`),现在读的是当前的 `fi`,即上一时间步碰撞后的 post-collision 数据。Bouzidi 两分支算法都要求同一时间层的 post-collision 数据 [Bou01],当前写法满足此要求。
### 结论: [已确认] Moving wall 修正按 q 分支 — curved_boundary.cuh:30-43
**理由**: `bouzidi_linear_moving_correction` 函数三路分支:
- `fallback_class != BOUZIDI``2 * alpha_ci_dot_uw`(半格加移动修正)
- `q < 0.5``2 * alpha_ci_dot_uw`Bou01 公式)
- `q >= 0.5``alpha_ci_dot_uw / q`Bou01 公式)
各项系数与文献一致,`alpha_ci_dot_uw = 3 * w_i * (c_i · u_w)`。
### 结论: [已确认] Forcing 预因子统一 — collision_srt.cuh:15, collision_trt.cuh:35, collision_mrt.cuh:41/116
**理由**: 三个文件的类内路径都使用了 `c_tau = 1.0f - 0.5f * omega` 并将 `c_tau * Fin[i]` 增量加到碰撞输出中。`collide_dispatch()`helpers.cuh:33-37/46-50`d_params.fx/fy/fz`非零时调用 `compute_guo_forcing()` 生成 `Fin`,然后传入对应碰撞函数。三者完全一致。
### 结论: [已确认] TRT outlet NEQ 全分布重构 — pressure_neq.cuh:45-51(D2Q9):89-95(D3Q19)
**理由**: `#if COLLISION_MODEL == 0 || COLLISION_MODEL == 1` 把 TRT 纳入全分布 damped NEQ 分支(与 SRT 同级),对所有 NQ 方向做 `f[i] = feq_tar[i] + beta * fneq`,其中 `fneq = f_neb[i] - feq_neb[i]``beta = OUTLET_SRT_NEQ_DAMP`。已不再是"少量未知方向"路径。
### 结论: [已确认] add_vortex() 除以 rho — initializers.py:55-58
**理由**: `ux_old = sum(f[i] * cx[i]) / rho_safe``uy_old = sum(f[i] * cy[i]) / rho_safe`。旧 bug 是动量除以 rho 这一步缺失,现在存在 `rho_safe` 分母。
### 结论: [已确认] Init flag overlay 顺序 — simulation.py:139-159 + init_flow.cu:48-58 + body/manager.py:115-127
**理由**: 顺序为 `build_channel_flags()`(干净通道)→ `build_flags()`(叠加物体)→ `upload_flags()`上传GPU`stepper.initialize()`(运行 init kernel 保持 obstacle flag。`init_flow.cu:finalize_domain_flag` 对 `is_obstacle(fl)` 直接返回原 flag。`sync_to_gpu(rebuild_flags=False)` 在初始化时不重复构建 flags。`_rest_nonfluid()` 的二次重置已移除。
---
## 2. 待验证项复查
### 结论: [已确认] MRT D2Q9 方向序与符号 — collision_mrt.cuh:46-54,79-92
**理由**: 经子代 agent 验证:
- `m[3]` = f1f2 + f5f6 + f7f8 = ρ·ux`macro.cuh:36``compute_rho_u()` 完全一致)
- `m[5]` = f3f4 + f5f6 f7 + f8 = ρ·uy一致
- `m[7]` = f1+f2f3f4 = ρ·(ux²uy²)(一致)
- 逆变换系数 `g[0] += (dm0 dm1 + dm2)/9` 等均符合 `M·M⁻¹ = I`
- `meq[4] = ρ·ux`、`meq[6] = ρ·uy` 符号正确(正交基结构)
- 无符号错误。MRT D2Q9 的 paired 方向排列下的矩变换自洽。
### 结论: [已确认] Esopull 邻壁行处理 — one_step_esopull.cu vs one_step_double.cu
**理由**: 逐行对比 `one_step_esopull.cu:76-151``one_step_double.cu:65-144`。Double-buffer 在 line 88-94 有显式 `is_fluid(fl) && (y==1 || y==NY-2)` 块调用 `apply_wall_bb_y_pull`,而 Esopull 没有对应分支。**但这不构成 bug** — Esopull 交替读写模式(`load_f_esopull` / `store_f_esopull``esopull_single_buffer.cuh:33-77`)天然避免了从 y=0 壁面节点直接拉取垃圾数据:
- 偶数步f[3] 读取本地 `fi[n, 4]`(前一步该节点的 -y 方向f[4] 读取 `fi[j[3], 3]`y=2 的 +y 方向)
- 奇数步同理交错
两个时间步的综合效果等价于半格 BB 的反射,无需显式修正。壁面节点 (y=0) 经 `apply_boundary_esopull``bounce_back_swap` 处理,不会积累垃圾。
### 结论: [无法确认: 需运行验证] Force 提取与符号约定 — curved_boundary.cuh:90-97 + obs.cuh + manager.py:328-338
**理由**: 链路追踪如下:
1. `curved_boundary.cuh:91-97`: `fx = c_x * (f_toward + f_reflected)` 累加到 `obs[obs_force_index(body_id, 0)]`
2. `obs.cuh:11`: `obs_force_index(id_obj, d) = OBS_FORCE0_FLOATS + id_obj * DIM + d`(起点=0
3. `manager.py:read_force()`: 返回 `self.obs_pinned[i0:i0 + d]`,无符号反转
- 存储的是流体动量交换量 (fluid momentum exchange),不是物体受力的直接值(牛顿第三定律要求 `F_body = -F_fluid`)。如果外部 Cd/Cl 归一化时把 obs 直读值当做物体受力,符号会反转。需用已知算例(如静止圆柱的阻力系数)确认当前实践是否在外部做了隐含的取反。
---
## 3. 架构缺陷
### 结论: [未改] Curved boundary 仍假设圆形/球形 — curved_boundary.cuh + body/objects.py:110-291
**理由**: `Cylinder.get_curved_list()` 对每个 cut link 调用 `find_circle_intersection` / `find_sphere_ray_segment`,完全依赖于圆/球几何参数center + radius。没有通用多边形/三角网格接口。旧审计标记的 [待重构] 未处理。
### 结论: [未改] 3D 旋转为 z 轴占位 — aux_kernels.cu:58-63
**理由**: `CurvedBoundaryKernel` 的 D3Q19 分支中 `Ww = 0.0f`,且 `Uw = -omega * ry; Vw = omega * rx` 只包含 xy 平面转动。任何具有 z 分量的刚体旋转都不会产生正确的壁面速度。
### 结论: [已备注] Bouzidi-TRT 不相容注释 — curved_boundary.cuh:17-21
**理由**: 注释明确说明 "plain linear Bouzidi interpolation ... is not a TRT-parametrized curved-wall family",位置精准且语意清晰。
---
## 4. 新发现
### 4a. `_no_force` 碰撞变体为死代码 — collision_srt.cuh:25, collision_trt.cuh:57, collision_mrt.cuh:95
**问题**: `collide_srt_no_force`、`collide_trt_no_force`、`collide_mrt_no_force` 三个函数在编译后的任何内核路径中均不被调用。`collide_dispatch` (helpers.cuh:15-69) 始终通过带 `Fin` 数组的普通变体执行碰撞,当外力为零时通过 `zero_forcing(Fin)``Fin` 全清零。死代码约 40 行。
**影响**: 无功能影响,增加维护成本。`zero_forcing` 仅用于栈初始化,并非无用调用。
### 4b. BC_MOVING / BC_PERIODIC 有定义无处理 — flags.cuh + 全内核搜
**问题**: `FLAG_BC_MOVING (0x0060)``FLAG_BC_PERIODIC (0x0050)``flags.cuh` 有定义Python 侧 `descriptors.py` 也有对应常量。但 CUDA 内核中没有针对 `is_moving()``is_periodic()` 的分支处理:
- `one_step_double.cu:apply_boundary_pull` 只有 `is_curved/is_inlet/is_outlet/BBS` 分支
- 任何单元格若被标记 `BC_MOVING`(非 curved 路径)将落入 `bounce_back_swap()` 分支,得到 std half-way BB 而非移动壁面速度修正
**影响**: 只有当 obstacle 使用 `BC_CURVED` 配合 `cl_body_id` 进入 curved boundary kernel 才能获得正确移动壁面速度。`BC_MOVING` 直接标记在通道壁面或其他固体节点上无效果。
### 4c. Outlet NEQ 的 `OUTLET_MODE` 嵌套逻辑可读性隐患 — pressure_neq.cuh:25-62
**问题**:
```
#if OUTLET_MODE == 1 → 纯拷贝未知方向
#else → 普通路径
#if COLLISION_MODEL==0||1 → 全分布 NEQ含 TRT
#elif OUTLET_MODE == 2 → 混合模式
#else → 少量未知方向重构(默认 OUTLET_MODE=0 路径)
#endif
#endif
```
`OUTLET_MODE=0`、`COLLISION_MODEL=2`MRT代码进入最内层 `#else` 分支(少量未知方向重构),而非全分布 NEQ。这与注释宣称的"SRT 和 TRT 使用全分布 NEQ"一致MRT 未承诺),但 MRT outlet 路径与 SRT/TRT 行为不同,可能导致 MRT 结果系统性偏差。
**影响**: MRT outlet 行为与 SRT/TRT 不一致,应至少加注释说明此差异,或行为对齐。
### 4d. `collide_inlet_ghost` 对 `y=0/NY-1` 的过滤 — one_step_double.cu:102-103, one_step_esopull.cu:106-107
**问题**: `collide_inlet_ghost = is_inlet(fl) && interior_y && inlet_scheme_uses_post_collision_ghost()`,其中 `interior_y = (y>0 && y<NY-1)`。`y=0` 和 `y=NY-1` 的 inlet 节点不会触发 ghost 碰撞。但 `inlet_scheme_uses_post_collision_ghost()` 只在 `INLET_SCHEME==0`Zou-He时返回 true而 x=0 inlet 节点原本处于 y=0 或 y=NY-1 时,已被 `build_channel_flags` 覆盖为 `SOLID|BC_WALL`(角点优先),因此这些节点本身不是 inlet过滤是安全的。
**影响**: 当前无实际影响(角点 wall 覆盖 inlet但代码隐含的逻辑依赖比较脆弱。如果未来修改建标记序可能在 y=0/ymax inlet 节点产生未碰撞 ghost 状态。
### 4e. 传感器归一化在 manager 层正确实现 — aux_kernels.cu:67-99 + manager.py:348-365
**结论**: `SensorKernel` 做逐格点求和,`ObjectManager.read_sensor(normalize=True)` 除以 `sensor_cell_counts[body_id]`。已实现,正确。
### 4f. `inlet_target_u` 对 y=0 和 y=NY-1 的保护 — inlet/common.cuh:34
**影响**: `y_clamped = fminf(NY-2, fmaxf(1.0, y))` 使 y=0 节点获得 y=1 的 inlet 速度。对正确定义的 `SOLID|BC_WALL` 角点无实际影响,属于保护性逻辑。
### 4g. `compute_omega_minus` 在 ω⁺ = 2.0 时分母为零 — collision_trt.cuh:24-26
**问题**: `1.0f / omega_plus - 0.5f``omega_plus = 2.0` 时为 `0.5 - 0.5 = 0`,导致除零。实际路径中 `omega_col``collide_dispatch` 中被钳位helpers.cuh:56假设 `OMEGA_COLLISION_MAX < 2.0` 则为安全。
**影响**: 低风险(依赖外部宏正确配置)。
### 4h. `west_velocity_rho_closure` 在 u_target ≥ 1.0 时除零 — inlet/common.cuh:47-48
**问题**: `rho = sum(...) / (1.0f - ux_target)`,当 `ux_target >= 1.0` 时除数为 0 或负数。物理上入口马赫数小于 1 可避免,但无运行时保护。
**影响**: 低风险(物理约束),但崩溃行为不如显式断言清晰。
---
## 总结
| 类别 | 数量 | 关键项 |
|------|------|--------|
| 旧修正确认 | 7/7 | 全部确认正确 |
| 待验复查 | 3 | 2 确认, 1 需运行时验证 |
| 架构缺陷 | 3 | 圆形硬编码、3D占位、TRT注释齐全 |
| 新发现 | 8 | 4a死代码、4b未处理BC、4c MRT outlet差异、4d脆弱逻辑依赖、4e已实现、4f无害保护、4g除零边界、4h无保护输入 |
**最高优先级**: 4b (`BC_MOVING` 无处理路径)、4c (MRT outlet 路径与 SRT/TRT 不一致)、force 符号约定需运行验证。