重构body api,性能分析,项目整理
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
## 目标
|
||||
|
||||
第二阶段不再以补丁式修 bug 为主,而是重建 `body` 与 `LBM` 之间的模块边界,形成可持续扩展到 Bouzidi、IBM、粒子和多相耦合的骨架。重构后的代码需要保持短文件、清晰职责、显式契约,并避免几何语义直接渗入 CUDA 核心计算链路。
|
||||
|
||||
## 第一阶段与第二阶段的边界
|
||||
|
||||
### 第一阶段
|
||||
|
||||
目标是验证当前修复后的代码没有恶化,并保住最基本执行链路。
|
||||
|
||||
- 能稳定计算流场
|
||||
- 能读取力与力矩
|
||||
- Bouzidi 路径不出现新的时序性错误
|
||||
- 初始化、flag、object overlay 不再互相污染
|
||||
- 现有接口不做大改,只修运行期 bug 和明显契约错误
|
||||
|
||||
### 第二阶段
|
||||
|
||||
目标是结构性重构,而不是继续在旧链路上叠加特例。
|
||||
|
||||
- 重构 `body`
|
||||
- 重构 curved boundary 数据链
|
||||
- 为 IBM 预留统一入口
|
||||
- 为简单几何和离散几何建立同一中间表示
|
||||
- 为未来多相与多物理场保留清晰耦合面
|
||||
|
||||
## 当前架构的核心问题
|
||||
|
||||
### 主要问题
|
||||
|
||||
| 模块 | 当前问题 | 后果 |
|
||||
|---|---|---|
|
||||
| `body` | 几何、flag overlay、compact list、action、obs、coupling 混在一起 | 任一功能扩展都会牵动整条链路 |
|
||||
| `objects.py` | 几何对象直接生成 Bouzidi 专用数据 | 简单几何和离散几何无法共享同一接口 |
|
||||
| `curved_boundary` | 几何解释、边界格式、移动壁修正、力提取绑在同一层 | 替换边界方法成本高 |
|
||||
| `ObjectManager` | 过度集中,已经接近万能管理器 | 可读性下降,后续只能继续膨胀 |
|
||||
| host-device contract | 分散在多个文件,缺少统一定义 | 调试困难,容易出现隐式耦合 |
|
||||
|
||||
### 本质判断
|
||||
|
||||
当前最大问题不是公式本身,而是缺少稳定的中间层。几何对象、边界方法、运行时打包目前没有被拆开,导致代码很难既简洁又可扩展。
|
||||
|
||||
结合本轮修 bug 的经验,再补一条判断:很多问题不是几何本身出错,而是 donor、fallback、time layer 这类数值语义散落在不同文件中,读代码时必须来回跳转才能确认。当前项目更适合把这些语义集中写进少数注释位置,而不是继续加更多隐式保护逻辑。
|
||||
|
||||
## 第二阶段的目标架构
|
||||
|
||||
### 顶层模块
|
||||
|
||||
保持两大物理模块:
|
||||
|
||||
- `LBM`
|
||||
- `body`
|
||||
|
||||
但它们之间不直接互相了解实现细节,而通过显式中间数据结构耦合。
|
||||
|
||||
### 推荐分层
|
||||
|
||||
| 层级 | 职责 | 不应负责 |
|
||||
|---|---|---|
|
||||
| `simulation` | 装配模块,定义推进顺序 | 几何处理,边界公式细节 |
|
||||
| `body` | 几何、刚体状态、预处理、力回收 | DDF 操作,LBM 核心算法 |
|
||||
| `lbm` | 格子流体、collision、streaming、boundary operator | 几何来源,刚体业务语义 |
|
||||
| `coupling` | cut-link、IBM marker、body-fluid 数据交换 | 具体几何类定义,具体 collision 实现 |
|
||||
|
||||
## 推荐的 body 内部结构
|
||||
|
||||
### 目标
|
||||
|
||||
`body` 应只表达拉格朗日对象和几何处理,不直接承载 LBM 业务逻辑。
|
||||
|
||||
补充边界:`body` 负责管理对象并产出统一 cut-link 几何记录,`lbm` 只消费 cut-link 做数值计算。对象类型区分、几何来源、以及“该记录来自圆柱还是离散几何”都不应进入 LBM kernel 视野。
|
||||
|
||||
### 推荐文件树
|
||||
|
||||
```text
|
||||
body
|
||||
geometry
|
||||
base
|
||||
circle
|
||||
sphere
|
||||
polygon
|
||||
mesh
|
||||
state
|
||||
rigid_body_state
|
||||
particle_state
|
||||
preprocess
|
||||
flag_overlay
|
||||
cut_links
|
||||
ibm_markers
|
||||
sensors
|
||||
runtime
|
||||
action_buffer
|
||||
telemetry_buffer
|
||||
registry
|
||||
coupling
|
||||
wall_velocity
|
||||
force_torque
|
||||
```
|
||||
|
||||
### 说明
|
||||
|
||||
- `geometry` 只回答几何问题
|
||||
- `state` 只保存状态量
|
||||
- `preprocess` 负责把几何投影到欧拉网格
|
||||
- `runtime` 负责 GPU 上传与 buffer 管理
|
||||
- `coupling` 负责 body 与 fluid 的交换规则
|
||||
|
||||
进一步要求:
|
||||
|
||||
- `geometry` 不直接生成 Bouzidi 专用 SoA
|
||||
- `preprocess` 先产出统一 cut-link 结果,再由更薄的一层做运行时打包
|
||||
- `runtime` 不回头参与几何判断
|
||||
- 单文件保持简短,避免再出现一个文件同时含几何、打包、上传、观测解释四类职责
|
||||
|
||||
## 推荐的 LBM 边界
|
||||
|
||||
### LBM 应该看到什么
|
||||
|
||||
LBM 只应看到这些输入:
|
||||
|
||||
- `flag`
|
||||
- `cut-link records`
|
||||
- `IBM marker records`
|
||||
- `runtime params`
|
||||
- `obs buffers`
|
||||
|
||||
LBM 不应知道:
|
||||
|
||||
- 该 link 来自圆柱还是三角网格
|
||||
- 该对象是粒子还是障碍物
|
||||
- 该边界记录由哪种 host 几何算法产生
|
||||
|
||||
## curved boundary 的重构方向
|
||||
|
||||
### 核心原则
|
||||
|
||||
不要把 curved boundary 等同于 Bouzidi。
|
||||
|
||||
需要拆成三个维度:
|
||||
|
||||
- 几何表示
|
||||
- 边界处理方法
|
||||
- 运动模型
|
||||
|
||||
补充一条实现原则:donor、fallback、time layer 等关键数值语义,优先通过集中注释写清楚,不额外引入很多“自动保证语义”的复杂逻辑。当前项目更优先保持代码短、直、可读。
|
||||
|
||||
### 建议的统一中间表示
|
||||
|
||||
定义通用 `cut-link record`,至少包含:
|
||||
|
||||
| 字段 | 含义 |
|
||||
|---|---|
|
||||
| `fluid_idx` | 流体格点索引 |
|
||||
| `dir` | 指向边界的格子方向 |
|
||||
| `q` | 交点沿链路的位置 |
|
||||
| `body_id` | 所属物体 |
|
||||
| `hit_point` | 壁面交点 |
|
||||
| `lever_arm` | 相对参考点的力臂 |
|
||||
| `normal` | 壁面法向 |
|
||||
| `motion_tag` | 静止、平移、旋转等 |
|
||||
| `scheme_tag` | Bouzidi、half-way、未来 TRT-compatible scheme |
|
||||
| `fallback_tag` | donor 非法时的退化方式 |
|
||||
|
||||
补充约束:
|
||||
|
||||
- `cut-link record` 只表达几何命中结果与最小运行时字段,不直接长成某一种 kernel 专用格式
|
||||
- donor 的来源与时间层语义不额外做复杂自动推断,靠集中注释写清楚
|
||||
- 记录字段命名要直接对应 kernel 使用含义,避免同一字段在不同文件中有不同解释
|
||||
|
||||
### 这样做的好处
|
||||
|
||||
- 圆形和离散几何可以输出同一种记录
|
||||
- Bouzidi 与 IBM 可以共享一部分几何预处理
|
||||
- boundary kernel 只消费记录,不关心几何来源
|
||||
- 以后替换边界方法时,不必回改对象类
|
||||
- donor、fallback、hit-point 这类契约可以在少数固定注释位置集中说明,而不是散落在对象类和 kernel 两侧
|
||||
|
||||
## IBM 的预留方式
|
||||
|
||||
IBM 不应和 Bouzidi 混成一条链,而应与 cut-link 平行。
|
||||
|
||||
建议另设统一 `marker record`,用于:
|
||||
|
||||
- 插值点位置
|
||||
- 支撑域格点
|
||||
- 权重
|
||||
- 与刚体的归属关系
|
||||
|
||||
这样未来可以并存:
|
||||
|
||||
- cut-link boundary
|
||||
- IBM boundary
|
||||
- 混合策略
|
||||
|
||||
## 运行时契约的建议
|
||||
|
||||
### 必须显式化的契约
|
||||
|
||||
应把 host-device contract 从各文件中收口,单独维护。
|
||||
|
||||
建议集中定义:
|
||||
|
||||
- action buffer layout
|
||||
- telemetry buffer layout
|
||||
- cut-link record layout
|
||||
- marker record layout
|
||||
- force torque sign convention
|
||||
- body velocity contract
|
||||
|
||||
### 运动状态契约
|
||||
|
||||
即使短期只做 2D 圆柱旋转,也建议按最终形式设计:
|
||||
|
||||
| 状态 | 建议字段 |
|
||||
|---|---|
|
||||
| 平移 | `vx vy vz` |
|
||||
| 角运动 | `wx wy wz` 或 2D 的 `omega` 特化视图 |
|
||||
| 参考点 | `cx cy cz` |
|
||||
| 姿态 | `theta` 或旋转表示 |
|
||||
|
||||
不要再把 3D 路径固化为“只读一个 z 轴 `omega`”。
|
||||
|
||||
## 第二阶段推荐顺序
|
||||
|
||||
### 阶段 2A
|
||||
|
||||
先把职责边界拆开,不追求新功能。
|
||||
|
||||
- 拆 `ObjectManager`
|
||||
- 建立 `BodyRegistry`
|
||||
- 建立 clean `runtime buffer` 层
|
||||
- 建立单独的 geometry preprocess 层
|
||||
- 把“对象管理”和“cut-link 产出”分开
|
||||
|
||||
验收标准:
|
||||
|
||||
- `body` 不再直接知道具体 LBM kernel 调度
|
||||
- `ObjectManager` 不再承担几何、obs、state、flag 全部职责
|
||||
- `objects.py` 不再直接输出某一种 boundary method 专用打包格式
|
||||
|
||||
### 阶段 2B
|
||||
|
||||
重构 curved boundary 数据链。
|
||||
|
||||
- 定义统一 `cut-link record`
|
||||
- 让圆柱先输出新 record
|
||||
- 让 Bouzidi kernel 消费新 record
|
||||
- 把现有 `CurvedLinkSoA` 从“Bouzidi 专用”改成“通用 cut-link buffer`
|
||||
- donor 与 fallback 的契约集中写入注释,不额外增加复杂语义保护代码
|
||||
|
||||
验收标准:
|
||||
|
||||
- kernel 不需要知道几何来源
|
||||
- host 侧可以替换 cut-link builder 而不改 kernel 接口
|
||||
- 读 builder 与 kernel 时,不需要跨很多文件才能理解 donor 和 fallback 的基本语义
|
||||
|
||||
### 阶段 2C
|
||||
|
||||
为 IBM 留入口。
|
||||
|
||||
- 定义 `marker record`
|
||||
- 预留独立 preprocess 和 runtime buffer
|
||||
- 暂不实现完整 IBM 细节,只把架构位置留好
|
||||
|
||||
验收标准:
|
||||
|
||||
- IBM 不需要重写 body 骨架
|
||||
- IBM 与 Bouzidi 可以共存于同一 body 系统
|
||||
|
||||
### 阶段 2D
|
||||
|
||||
补文档与契约说明。
|
||||
|
||||
- 当前能力
|
||||
- 预留能力
|
||||
- 不支持项
|
||||
- 2D 与 3D 差异
|
||||
- TRT 与 plain Bouzidi 的限制
|
||||
|
||||
## 不建议做的事
|
||||
|
||||
- 不要继续强化万能 `ObjectManager`
|
||||
- 不要让几何对象直接生成某一种边界格式专用 SoA
|
||||
- 不要把 curved boundary 和 Bouzidi 永久绑定
|
||||
- 不要在 3D 运动契约上继续堆占位特例
|
||||
- 不要在第二阶段把多相、IBM、粒子一起全做完
|
||||
- 不要为了“自动保证语义”继续增加很多隐式逻辑分支,优先用集中注释把契约写清楚
|
||||
- 不要引入必须跨很多文件来回跳转才能理解的 host-kernel 组织方式
|
||||
|
||||
## 第二阶段完成后的理想状态
|
||||
|
||||
### 代码层面
|
||||
|
||||
- 文件更短
|
||||
- 模块职责更单一
|
||||
- host-device contract 更显式
|
||||
- kernel 更专注于数值操作
|
||||
- geometry 与 boundary method 解耦
|
||||
|
||||
### 扩展层面
|
||||
|
||||
后续可自然扩展到:
|
||||
|
||||
- 离散几何 cut-link
|
||||
- IBM marker 链路
|
||||
- 粒子和刚体共用 body runtime
|
||||
- 多相流对 body 的额外 coupling
|
||||
|
||||
### 维护层面
|
||||
|
||||
后续新增功能时,优先在对应层增加文件,而不是回到单个 manager 中继续堆逻辑。
|
||||
|
||||
## 建议的执行方式
|
||||
|
||||
第二阶段执行前,先固定一个简短设计约束:
|
||||
|
||||
- 单文件不过长
|
||||
- 新增模块必须有明确职责说明
|
||||
- 任何 host-device 数据结构必须有集中定义
|
||||
- 新接口先定义契约,再写 kernel
|
||||
- 能通过新增 builder 解决的问题,不要回写到 geometry 类中
|
||||
- donor、fallback、time-layer 等关键数值语义必须在少数固定位置集中注释说明
|
||||
- 优先保持文件短和职责单一,不为“保险”堆太多诊断与保护代码
|
||||
|
||||
这个阶段的核心产出不是新功能,而是一个能长期承载新功能的骨架。
|
||||
@@ -0,0 +1,209 @@
|
||||
## 审计结论(第二轮)
|
||||
|
||||
本轮审计的视角与上一轮不同。上一轮聚焦"主链路上是否有显式 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 缺口 |
|
||||
@@ -0,0 +1,126 @@
|
||||
# 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 符号约定需运行验证。
|
||||
Reference in New Issue
Block a user