## 审计结论

当前代码的问题不是单点误差，而是沿着主链路分布。最重的问题集中在三处：curved Bouzidi 的施加时机不对，Bouzidi 分支公式与文献不一致，以及若干运行时接口与内核实现脱节。这几类问题叠加后，足以让同一 case 在 SRT、TRT、MRT 之间出现远大于正常数值差异的结果。

需要单独说明的是，部分未来接口属于有意预留，不应被视为 bug。本审计只把已经进入当前执行契约、但实现与语义不一致的部分列为问题。

按当前项目约束，以下两类设计不再单列为缺陷：

- 运行时接口为后续能力预留，但当前未完全接通，只要注释明确即可
- `obs` 采用跨多步累加，由调用者决定何时清零，这属于有意设计而非实现错误

## 状态说明

- `[已解决]` 已进入当前代码
- `[待重构]` 不是单点修补能彻底解决，适合下一阶段重构
- `[待验证]` 需要最小算例或单元测试确认
- `[保留说明]` 当前不修，但需要在代码或文档中明确限制

## 当前总览

### 已解决

- [已解决] `lbm/__init__.py` 导出错误
- [已解决] forcing 主链路未接通，以及 SRT TRT MRT forcing 预因子不一致
- [已解决] TRT outlet NEQ 重构未补齐
- [已解决] `add_vortex()` 把动量当速度
- [已解决] Sensor 面积归一化缺失，已在 `ObjectManager` 层提供
- [已解决] `sync_to_gpu()` 末尾重置非流体节点的架构错误
- [已解决] curved donor 合法性未检查真实 domain flags
- [已解决] curved Bouzidi 时序错误
- [已解决] `q >= 0.5` 分支读错时间层
- [已解决] moving wall 修正未按 q 分支实现
- [已解决] 初始化链路与 object flag 叠加关系错误
- [已解决] `config_body.json` 未进入实际初始化链路
- [已解决] inlet `U0` 语义已补注释

### 仍需重构或继续处理

- [待重构] `body` 模块整体职责边界仍不清晰，几何描述、flag overlay、compact list 生成、动作状态、观测打包仍然耦合过重
- [待重构] curved boundary 当前仍是“圆形简单几何特化 + Bouzidi kernel 特化”架构，不适合继续扩到离散几何与通用移动刚体
- [待重构] 3D 刚体旋转契约仍是 z 轴占位实现
- [待重构] plain linear Bouzidi 与 TRT 不相容问题已注明限制，但没有方法级替代方案
- [待重构] `config.py` 与文档层对预留能力、当前能力、限制条件的边界还不够清楚
- [待验证] MRT 路径仍需最小算例单独核对
- [待验证] Esopull 邻壁处理仍需与 double-buffer 做一致性复核
- [待验证] force 提取与 host 侧系数归一化仍需放到同一处核对

## 历史问题清单


### 主执行链路

| 文件 | 问题 | 影响 |
|---|---|---|
| `lbm/__init__.py` | [已解决] 导入了并不存在的 `add_lamb_oseen` 与 `add_taylor_green`。实际 `initializers.py` 只提供 `add_vortex`。 | `CelerisLab.lbm` 包导入会退化到空 `__all__`，对外 API 与代码不一致。 |
| `field.py`、`config.py`、`inlet_outlet.cuh`、`init_flow.cu` | [已解决] `update_runtime_params()` 暴露了 `u_inlet` 与 `rho_ref` 一类接口，但入口、出口、初始化全部仍然读编译期宏 `U0` 与 `RHO`。如果这些接口继续保留在活跃 API 中，就会形成假功能。 | 运行时接口语义与真实执行路径不一致。 |
| `helpers.cuh` 与 `forcing_guo.cuh` | [已解决] `collide_dispatch()` 每步都调用 `zero_forcing(Fin)`，没有任何地方根据 `d_params.fx fy fz` 生成 Guo forcing，也没有调用速度半步修正。 | 运行时体力项接口是死路径。任何依赖体力驱动的算例都会静默失效。 |
| `collision_trt.cuh` 与 `collision_mrt.cuh` | [已解决] 在 forcing 真的接通之前，TRT 与 MRT 版本目前都是直接加 `Fin[i]`，SRT 则乘了 `(1-omega/2)`。 | forcing 一旦接通，三种碰撞模型会立刻表现出不一致的体力离散。 |

### Curved boundary 与几何预处理

| 文件 | 问题 | 影响 |
|---|---|---|
| `one_step_double.cu`、`aux_kernels.cu`、`curved_boundary.cuh` | [已解决] Curved Bouzidi 是在 `OneStep` 完成 collision 与 store 之后，通过 `CurvedBoundaryKernel` 二次修补。 | 紧邻曲壁的 fluid 节点在本步 collision 时已经使用了从 solid 方向拉来的错误分布。后修补只能影响下一步，不能修正本步碰撞污染。 |
| `curved_boundary.cuh` | [已解决] `q >= 0.5` 分支使用 `fi_in[k_f, dir_opp]`，也就是上一步缓冲区，而不是同一步、碰撞后传播前的分布函数。Bouzidi 线性插值两支公式都要求同一时间层的 post-collision 数据 [Bou01]。 | 该分支与文献公式不一致，会直接破坏 curved wall 的局部反射关系。 |
| `curved_boundary.cuh` | [已解决] moving wall 修正统一写成 `+ 6 w_i (c_i · u_w)`，没有区分 `q < 0.5` 与 `q >= 0.5` 两支，也没有体现 Bouzidi moving boundary 中的分支依赖系数 [Bou01]。 | 对旋转圆柱或移动物体，壁面速度修正的量级与形式都不对。 |
| `body/objects.py` | [已解决] `Cylinder.get_curved_list()` 的 donor 合法性只检查了“是否越界”和“是否落在圆柱内部”，没有检查 donor 是否落在上壁、下壁、入口、出口等非流体节点。 | `fallback_class` 的主机侧担保并不成立。靠近通道壁或边界时，`q < 0.5` 分支可能继续使用非法 donor。 |
| `aux_kernels.cu` | [待重构] 3D curved body 的运行时角速度契约只读取一个标量 `omega`，并硬编码成 z 轴转动，代码里也明确写了 placeholder。 | 3D 旋转物体路径并未真正完成，但接口没有把这种限制暴露出来。 |

### 初始化、观测与工具函数

| 文件 | 问题 | 影响 |
|---|---|---|
| `initializers.py` | [已解决] `add_vortex()` 里 `ux_old` 与 `uy_old` 用的是动量和，没有除以 `rho_old`。 | 任何基于该函数构造的初值都会把速度场放大为密度加权动量场。 |
| `step/aux_kernels.cu` 与 `body/manager.py` | [已解决] `SensorKernel` 对传感器区域做的是逐格点求和，`ObjectManager` 也没有按面积归一化。 | 如果外部把传感器输出当成平均速度或平均观测量，结果会系统偏大并随探针面积变化。 |
| `body/manager.py` | [已解决] `sync_to_gpu()` 最后调用 `_rest_nonfluid()`，会把所有非流体节点都重置成静止平衡态，而不仅是物体节点。 | 只要场中有物体，就会额外改写入口、出口、壁面节点的初始化状态。 |

### 文档与配置层

| 文件 | 问题 | 影响 |
|---|---|---|
| `configs/CONFIG` | 文档写 `nx` 必须整除 `threads_per_block`，但实际 launch 使用的是 ceiling division。 | 配置说明与执行实现不一致。 |
| `configs/CONFIG` | [已解决] 文档写 `neq_extrap` 下 SRT 与 TRT 都使用全分布 damped NEQ 重构，但代码里只有 SRT 走全分布分支，TRT 仍是少量未知方向重构。 | 算法说明与真实边界实现不一致。 |
| `README` 与 `config.py` | 文档把 `FP16C` 当成可选存储精度，但 `LBMConfig.validate()` 会直接拒绝 `FP16C`。 | 对外能力声明与当前运行时不一致。 |

## 高风险问题

### 边界方法与碰撞模型耦合

| 文件 | 问题 | 影响 |
|---|---|---|
| `curved_boundary.cuh` 与 `collision_trt.cuh` | [保留说明] 当前实现采用 plain linear Bouzidi 与 TRT 直接拼接。TRT 的黏性无关参数化只在边界离散满足相应条件时才成立，普通线性插值边界并不会自动保留该性质 [Gin08b]。 | 即使把显式 bug 全部修掉，TRT 与 SRT、MRT 的结果仍可能因边界离散而系统分叉。 |
| `curved_boundary.cuh` | 当前 curved wall 没有任何质量守恒修正。对于高 Re、长时间、周期性 curved flow，文献已经报告 plain Bouzidi 会出现质量泄漏与力漂移 [San18]。 | 长时间拖算时，平均密度、阻力与升力可能持续漂移。 |
| `collision_mrt.cuh` | D2Q9 MRT 为配合新方向排序手工重写了整套 moment transform 与 inverse transform，但代码里没有任何一致性校验或自测痕迹。 | 一旦某个符号、系数或方向映射有误，结果会直接体现在升阻力与稳定性上，而且不容易从表面现象定位。 |
| `inlet_outlet.cuh` 与 `config.py` | 抛物入口把 `U0` 解释成截面平均速度，峰值自动变成 `1.5 * U0`。接口名字仍然叫 `velocity`，没有区分平均速度与最大速度。 | 只要用户按最大入口速度设参数，实际 Re 就会整体偏移。 |
| `init_flow.cu` | 四个角点优先按 inlet 或 outlet 分类，而不是 wall。随后边界核又把非 `interior_y` 的角点落回 `bounce_back_swap()`。 | 角点的标记语义与实际处理语义不一致，容易在后续扩展中埋下隐患。 |

## 待验证问题

### 需要单元测试或最小算例复核的项

| 文件 | 问题 | 影响 |
|---|---|---|
| `one_step_esopull.cu` | Esopull 路径没有像 double-buffer 那样对 `y == 1` 和 `y == NY-2` 的流体邻壁行做显式半格 bounce-back 修正。 | bounded channel 在 esopull 与 double-buffer 之间可能存在额外差异，需要最小通道算例核对。 |
| `collision_mrt.cuh` | [待验证] D2Q9 MRT 的新配对顺序、moment basis、inverse transform 是否与 `compute_rho_u()`、`compute_feq()` 完全一致，目前只能靠数值表现间接推断。 | 这条路径可能包含隐藏的符号错位，需要用均匀流、Poiseuille、衰减涡等简单算例单独验算。 |
| `inlet_outlet.cuh` | SRT 出口在 `neq_extrap` 下重构了全体分布，不仅是未知方向。 | 该设计是否有利于稳定性、是否额外改变质量守恒，需要独立做 outlet 对比测试。 |
| `curved_boundary.cuh` 与 `aux_kernels.cu` | [待验证] 当前力提取采用 `f_toward + f_reflected` 的链路级动量交换，但没有与 host 侧的阻力系数归一化和符号约定放在同一处核对。 | 即使流场正确，力的符号、量级和平均方式也仍可能在后处理阶段出错。 |

## 本轮已修复

- `lbm/__init__.py` 已改为导出真实存在的 `add_vortex`
- 运行时参数接口已收紧到当前真实生效的 `omega` 与 forcing 相关项
- Guo forcing 已接入主碰撞分发链路，SRT、TRT、MRT 的 forcing 预因子已统一
- TRT 的 outlet NEQ 重构已补齐为与 SRT 一致的全分布 damped NEQ 路径
- `add_vortex()` 已修正为用速度而不是动量直接叠加初值
- 传感器读数已在 `ObjectManager` 层提供面积归一化接口
- 物体同步后的静止平衡重置已收缩为只作用于 obstacle interior，而不再覆盖所有非流体节点
- curved link donor 合法性检查已扩展到实际 domain flags，而不只检查是否在圆柱内部
- curved Bouzidi 已从“步后修补流体节点”改为“步前写入 obstacle source slot”，并改掉了 `q >= 0.5` 分支对前一时间层 opposite population 的读取
- curved moving-wall 修正已继续收紧为独立 helper，并把 `q < 0.5` 与 fallback 分支改回与 [Bou01] 一致的符号约定
- 初始化链路已改为“先构建 clean channel flags，再叠加 object mask，再由 init kernel 保持 obstacle flag 并写入静止平衡态”，移除了 `sync_to_gpu()` 末尾对 obstacle interior 的二次主机侧重置
- `Simulation` 已开始消费 `config_body.json` 中的简单物体定义，并在编译期同步 `N_OBJS` 契约
- inlet 抛物入口已补充注释，明确 `U0` 在当前实现中表示截面平均速度，峰值为 `1.5 * U0`
- curved boundary 文件已显式注释：plain linear Bouzidi 与 TRT 不具备 TRT-parametrized curved-wall 保证，当前实现保留该限制说明

## 交叉症状解释

当前现象和代码结构是吻合的。TRT 加 Bouzidi 直接发散，最符合 curved wall 时序错误与 `q >= 0.5` 分支读错时间层这两个问题叠加后的表现。SRT 与 MRT 加 Bouzidi 不发散但升阻力偏离，则更像是主链路虽然还能跑，但边界修正、观测累加、入口参数语义和 MRT 未校验实现共同把结果拖离了正常范围。

文献层面的两条提醒也和代码现状一致。第一，plain linear boundary 与 TRT 的参数化并不天然相容 [Gin08b]。第二，plain Bouzidi 在高 Re 周期 curved flow 中会出现质量泄漏与力漂移 [San18]。这两条不是当前代码里最先要解释的大偏差，但它们说明就算显式 bug 修完，边界策略本身仍然需要额外核验。
