Files
CelerisLab/tests/audit/兼容性测试.md
T
Frank14fandCursor d5b7e98750 feat(compat): FP16S and ddf_shifting compatibility, EsoPull curved closure
Phase A: FP16S store precision verification
- Kan99b K2 FP16S: quantization sensitivity documented (St 0.170 -> 0.142)
- Sah04 S2 FP16S: PASS (St error 1.53% within 5% gate)
- Sah04 S4 FP16S: diverges at high blockage (known limitation)

Phase B: ddf_shifting code fixes
- Fix inlet west_velocity_rho_closure for shifted DDF (common.cuh)
- Fix curved force/torque accumulation for shifted DDF (curved_boundary.cuh, aux_kernels.cu)
- Fix host upload_ddf() asymmetry (field.py)
- Add checkpoint streaming/ddf_shifting match check (checkpoint.py)
- MRT shifting fix: MRT is NOT shift-invariant; unshift/reshift around collision
- Generalize inlet knowns repair from Zou-He to all west inlet schemes

Phase C: EsoPull curved boundary semantic closure (from round 2)
- streaming/esopull_semantic_helpers.cuh: single truth for physical-value semantics
- step/esopull_macro.cu: MacroscopicEsoPullKernel for correct GPU diagnostics
- SensorKernel, ForceRegionKernel share semantic helpers
- Kan99b K2: bit-identical to double-buffer
- Code-level comments document compatibility boundaries
- README updated with compatibility matrix
- output/round3_compatibility_summary.md: full round documentation

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 10:48:42 +08:00

11 KiB
Raw Blame History

FP16 与 ddf_shifting 兼容性指导

目标

这份文档用于指导下一阶段的兼容性工作,范围限定为:

  • store_precision = FP16S
  • ddf_shifting = true
  • 与以下能力的兼容性
    • double_buffer
    • esopull
    • curved boundary
    • 新 body 模块
    • Kan99b_validation
    • Sah04_validation

这里的核心目标不是“多跑一些测试”,而是先把 真正有代码级风险的点 查清楚,再为这些点设计最少但有力的验证。


总体判断

1. FP16S 和 ddf_shifting 不是同一类问题

这两个开关虽然都作用在 DDF 存储层,但风险性质不同。

项目 风险类型 当前判断
FP16S 精度风险 低到中等,可直接进入 benchmark 验证
ddf_shifting 语义风险 中到高,先修高风险代码,再做 benchmark

更直接地说:

  • FP16S 主要是“误差会不会把结果推偏”的问题
  • ddf_shifting 主要是“现有公式是否仍然在算同一个物理量”的问题

因此两者不应混在一起推进。


第一部分:FP16S

当前代码结构判断

FP16S 当前结构的优点很明确:

  • 所有 DDF 存取都集中在 load_ddf() / store_ddf()
  • 所有数值计算都在 float 下完成
  • EsoPull helper、macro kernel、sensor、force region、collision 都读入 float 后再算
  • body 模块的 cut-link、action、obs、geometry 都仍然是 float32

这意味着 FP16S 的主要影响路径是:

  • DDF 被量化到 half + scale
  • 再由 load_ddf() 恢复到 float
  • 后续计算不再额外降精度

因此 FP16S 的主要风险不是语义错,而是:

  • 量化误差是否在长期统计量中积累
  • curved 邻域和力学统计是否对量化更敏感

当前最合理的策略

FP16S 不需要先大规模改代码。当前更合理的是:

  • 直接进入最小 benchmark 验证
  • 用少量高价值算例判断是否达到可用精度

FP16S 最小验证集

建议只做三项:

编号 用例 作用
F1 Kan99b K2 rotating cylinder 主锚点,覆盖 curved + body force/torque
F2 Sah04 S2 中等 blockage,覆盖 channel + inlet/outlet + curved
F3 Sah04 S4 高 blockage 敏感 case,放大 wall / curved / 精度误差

如果计算预算紧,可以先跑:

  1. K2
  2. S2
  3. S4

这三项站住,FP16S 基本就可视为进入正式可用状态。

FP16S 主要观测量

Kan99b K2

  • St
  • mean Cd
  • mean Cl
  • C'_L
  • C'_D
  • torque 趋势
  • 最终涡量图

Sah04 S2 / S4

  • St
  • Re_real
  • 周期性
  • 最终涡量图
  • 宏观场是否有明显假结构

FP16S 评估方式

不建议用“逐点场几乎完全相同”作为判据。更合理的是:

  • benchmark 统计量是否稳定
  • 流动结构是否保持
  • 是否出现明显非物理噪声放大

FP16S 通过标准建议

沿用现有 benchmark 文档标准即可,不需要另立一套更紧的标准。

若出现轻微偏差,优先看:

  • 偏差是否主要体现在力振幅而不是频率
  • 是否只在高 blockage 或强近壁 case 放大
  • 是否存在长期漂移而不是瞬时差异

FP16S 失败时优先排查

现象 优先怀疑
St 稳定但振幅偏差大 curved 邻域对量化敏感
rho 漂移明显 精度不足导致守恒误差积累
高 blockage case 失真、低 blockage 正常 wall-gap 分辨率与量化共同放大误差
只有 EsoPull 差、double 正常 single-buffer 路径对量化更敏感

第二部分:ddf_shifting

当前代码结构判断

ddf_shifting 不是“已跑通但没验证”的状态,而是“已有若干明确高风险点”的状态。

从当前代码看,至少有四类问题必须先处理。


风险点 S1:inlet rho closure 不兼容 shifting

位置

  • boundary/inlet/common.cuh
  • west_velocity_rho_closure_d2q9
  • west_velocity_rho_closure_d3q19

问题本质

这些 closure 直接对 f[] 求和并做速度闭合。

但在 shifting 模式下,存储变量是:

[ \tilde f_i = f_i - w_i ]

因此原来对 f_i 成立的 closure,不能直接对 \tilde f_i 使用。

影响范围

所有依赖 west closure 的 inlet 都会受影响:

  • zou_he_local
  • channel_stabilized
  • regularized

结论

这不是 benchmark 才能发现的小问题,而是公式语义已变。必须先修。


风险点 S2:curved force/torque 统计不兼容 shifting

位置

  • curved_boundary.cuh
  • PrepareEsoPullCurvedKernel 中的 obs 累积
  • CurvedBoundaryKernel 中的 obs 累积

问题本质

当前动量交换统计直接使用:

  • f_toward + f_reflected

如果这两个量是 shifted population,那么它们包含了权重偏移项,不能再直接当成原始物理分布和来做 force 统计。

影响范围

这会直接污染:

  • force
  • torque
  • Cd
  • Cl
  • 任何依赖 body obs 的 benchmark 输出

结论

只要 ddf_shifting 打开,当前 curved force/torque 统计不能默认可信。


风险点 S3:host upload/download 路径不对称

位置

  • field.download_ddf()
  • field.upload_ddf()

当前状态

  • download_ddf() 在 shifting 模式下会加回 w_i
  • upload_ddf() 不会在上传前减去 w_i

影响范围

所有 host 侧修改 DDF 再上传的路径都可能错:

  • initializers.add_vortex()
  • snapshot()/restore()
  • 任何直接操作 field.ddf 的调试脚本

结论

当前 host DDF 修改路径与 shifting 语义不对称,必须:

  • 要么修正
  • 要么显式禁止使用

风险点 S4:checkpoint 对 streaming 不做匹配检查

位置

  • common/checkpoint.py

当前状态

当前 checkpoint load 检查:

  • lattice
  • grid
  • store precision

但不检查:

  • streaming
  • ddf_shifting

影响范围

可能出现:

  • double-buffer checkpoint 被加载到 esopull
  • unshifted checkpoint 被加载到 shifted 配置
  • 反过来亦然

结论

只要 storage semantics 不同,checkpoint 就不应默认兼容。


ddf_shifting 的正确推进顺序

Step S-A:先修代码级高风险点

在任何 benchmark 之前,建议优先处理以下事项:

  1. 修 inlet closure
    • 让 west_velocity_rho_closure_* 对 shifting 成立
  2. 修 curved force/torque 统计
    • 统计时使用物理 population,而不是 shifted storage 量
  3. 处理 host upload/download 对称性
    • 修 upload_ddf()
    • 或对 shifting 下 host-DDF-edit 路径显式加 guard
  4. 补 checkpoint 配置检查
    • 至少检查 streaming
    • 最好同时检查 ddf_shifting

为什么必须先修

因为这些不是“跑几个 benchmark 看看”的问题,而是:

  • 不修的话,benchmark 结果没有明确物理解释
  • 即使数值接近,也可能只是错误抵消

Step S-B:再做最小 sanity 验证

在代码修完之后,先不要直接上长跑 benchmark,先做最小 sanity:

编号 用例 目标
S5 channel init sanity 确认 shifting 下宏观场与初始条件一致
S6 inlet closure local check 确认 west closure 不再系统偏移
S7 curved force local check 确认 force/torque 不因 shift 常数偏置

这部分不需要设计得很复杂,重点是回答:

  • inlet 闭合是否恢复物理意义
  • curved force 是否恢复物理意义

Step S-C:再做 benchmark

修完并过了 sanity 之后,再做最小 benchmark:

编号 用例 作用
S8 Kan99b K2 主锚点,最关键
S9 Sah04 S2 中等 blockage channel 验证
S10 Sah04 S4 高 blockage 敏感验证

这套最小集与 FP16S 相同,便于复用现有脚本和比较口径。


第三部分:新 body 模块应该怎么理解

不要把“body 兼容性”当成一个单独的大问题

新 body 模块本身并不是当前主要风险源。因为它主要负责:

  • geometry
  • flag overlay
  • compact list packing
  • action/obs buffer 管理

这些层本身不直接定义 DDF 物理语义。

真正要关注的是,body 模块接入后,哪些 kernel 会读或写 DDF:

  • PrepareEsoPullCurvedKernel
  • CurvedBoundaryKernel
  • SensorKernel
  • ForceRegionKernel
  • MacroscopicEsoPullKernel

其中需要区分风险等级

路径 FP16 风险 shifting 风险
MacroscopicEsoPullKernel 低 低到中等
SensorKernel 低 低到中等
ForceRegionKernel 低到中等 中等
curved force/torque obs 中等 高
inlet / outlet related source states 中等 高

结论

新 body 模块不需要另起一套大而全的验证设计。更有效的做法是:

  • 在 FP16S / shifting 验证时,自动把 body 的核心使用路径覆盖掉
  • 重点盯住 force、torque、sensor、curved、macro 这些 DDF 消费点

第四部分:推荐推进顺序

当前最推荐顺序

  1. 先更新文档和注释
  2. 先做 FP16S 最小 benchmark 验证
  3. 再修 ddf_shifting 高风险代码
  4. 修完后做 shifting sanity
  5. 最后做 shifting benchmark

不要反过来。

原因

  • FP16S 现在已经具备直接验证条件
  • shifting 现在还不具备可信 benchmark 条件

第五部分:每一步如何评估

FP16S 评估口径

类型 关注点 判读方式
K2 St, Cd, Cl, amplitude 是否保持 benchmark 可用精度
S2 St, Re_real 中等 channel case 是否稳定
S4 St, vorticity 高敏感 case 是否放大量化误差

shifting 评估口径

类型 关注点 判读方式
sanity inlet / force / init 是否语义正确 是否恢复清楚的物理解释
K2 力学统计量 是否与 unshifted 路径一致且可解释
S2/S4 channel / blockage 行为 是否出现边界条件系统偏差

第六部分:执行摘要

对 coder 的直接指导

先做什么

先做:

  • 文档和注释更新
  • FP16S 的 K2 + S2 + S4

暂时不要直接做什么

暂时不要直接把 ddf_shifting 扔进长跑 benchmark,然后凭结果判断“看起来能不能用”。

ddf_shifting 先修什么

先修四个点:

  1. inlet rho closure
  2. curved force/torque 统计
  3. host upload/download 对称性或 guard
  4. checkpoint semantic match check

修完再怎么测

先做 sanity:

  • init
  • inlet
  • curved force

再做:

  • K2
  • S2
  • S4

最终一句话结论

FP16S 现在主要是精度验证问题;ddf_shifting 现在首先是代码语义一致性问题。

因此:

  • FP16S 可以直接用少量 benchmark 判可用性
  • ddf_shifting 必须先修明确高风险代码,再做 benchmark 才有意义