11 KiB
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
理由: 链路追踪如下:
curved_boundary.cuh:91-97:fx = c_x * (f_toward + f_reflected)累加到obs[obs_force_index(body_id, 0)]obs.cuh:11:obs_force_index(id_obj, d) = OBS_FORCE0_FLOATS + id_obj * DIM + d(起点=0)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 符号约定需运行验证。