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>
11 KiB
FP16 与 ddf_shifting 兼容性指导
目标
这份文档用于指导下一阶段的兼容性工作,范围限定为:
store_precision = FP16Sddf_shifting = true- 与以下能力的兼容性
double_bufferesopull- curved boundary
- 新 body 模块
Kan99b_validationSah04_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 / 精度误差 |
如果计算预算紧,可以先跑:
K2S2S4
这三项站住,FP16S 基本就可视为进入正式可用状态。
FP16S 主要观测量
Kan99b K2
Stmean Cdmean ClC'_LC'_D- torque 趋势
- 最终涡量图
Sah04 S2 / S4
StRe_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.cuhwest_velocity_rho_closure_d2q9west_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_localchannel_stabilizedregularized
结论
这不是 benchmark 才能发现的小问题,而是公式语义已变。必须先修。
风险点 S2:curved force/torque 统计不兼容 shifting
位置
curved_boundary.cuhPrepareEsoPullCurvedKernel中的 obs 累积CurvedBoundaryKernel中的 obs 累积
问题本质
当前动量交换统计直接使用:
f_toward + f_reflected
如果这两个量是 shifted population,那么它们包含了权重偏移项,不能再直接当成原始物理分布和来做 force 统计。
影响范围
这会直接污染:
- force
- torque
CdCl- 任何依赖 body obs 的 benchmark 输出
结论
只要 ddf_shifting 打开,当前 curved force/torque 统计不能默认可信。
风险点 S3:host upload/download 路径不对称
位置
field.download_ddf()field.upload_ddf()
当前状态
download_ddf()在 shifting 模式下会加回w_iupload_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
但不检查:
streamingddf_shifting
影响范围
可能出现:
- double-buffer checkpoint 被加载到 esopull
- unshifted checkpoint 被加载到 shifted 配置
- 反过来亦然
结论
只要 storage semantics 不同,checkpoint 就不应默认兼容。
ddf_shifting 的正确推进顺序
Step S-A:先修代码级高风险点
在任何 benchmark 之前,建议优先处理以下事项:
- 修 inlet closure
- 让
west_velocity_rho_closure_*对 shifting 成立
- 让
- 修 curved force/torque 统计
- 统计时使用物理 population,而不是 shifted storage 量
- 处理 host upload/download 对称性
- 修
upload_ddf() - 或对 shifting 下 host-DDF-edit 路径显式加 guard
- 修
- 补 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:
PrepareEsoPullCurvedKernelCurvedBoundaryKernelSensorKernelForceRegionKernelMacroscopicEsoPullKernel
其中需要区分风险等级
| 路径 | FP16 风险 | shifting 风险 |
|---|---|---|
MacroscopicEsoPullKernel |
低 | 低到中等 |
SensorKernel |
低 | 低到中等 |
ForceRegionKernel |
低到中等 | 中等 |
| curved force/torque obs | 中等 | 高 |
| inlet / outlet related source states | 中等 | 高 |
结论
新 body 模块不需要另起一套大而全的验证设计。更有效的做法是:
- 在 FP16S / shifting 验证时,自动把 body 的核心使用路径覆盖掉
- 重点盯住 force、torque、sensor、curved、macro 这些 DDF 消费点
第四部分:推荐推进顺序
当前最推荐顺序
- 先更新文档和注释
- 先做 FP16S 最小 benchmark 验证
- 再修 ddf_shifting 高风险代码
- 修完后做 shifting sanity
- 最后做 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 先修什么
先修四个点:
- inlet rho closure
- curved force/torque 统计
- host upload/download 对称性或 guard
- checkpoint semantic match check
修完再怎么测
先做 sanity:
- init
- inlet
- curved force
再做:
K2S2S4
最终一句话结论
FP16S 现在主要是精度验证问题;ddf_shifting 现在首先是代码语义一致性问题。
因此:
- FP16S 可以直接用少量 benchmark 判可用性
ddf_shifting必须先修明确高风险代码,再做 benchmark 才有意义