# 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 才有意义**