127 lines
11 KiB
Markdown
127 lines
11 KiB
Markdown
# 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]` = f1−f2 + f5−f6 + f7−f8 = ρ·ux(与 `macro.cuh:36` 的 `compute_rho_u()` 完全一致)
|
||
- `m[5]` = f3−f4 + f5−f6 − f7 + f8 = ρ·uy(一致)
|
||
- `m[7]` = f1+f2−f3−f4 = ρ·(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 符号约定需运行验证。
|