leapfrog_p 分布和 halo 调度
源码范围:
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\leapfrog_p.F
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\parallel_lmdz.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\mod_hallo.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\bands.F90
Mars 运行参与度:条件经过。Mars 并行 GCM 经过 dyn3dpar/leapfrog_p.F 的分布切换和 halo/swap;串行 Mars GCM 不经过本页路径。
职责边界
本页解释 leapfrog_p 如何在 caldyn、physics、dissip 等阶段之间切换纬向 band,并用 mod_hallo request 补齐 halo 或重新分布字段。时间步状态机见 leapfrog_p 时间步调度,入口数组和工作区见 leapfrog_p 状态约定。
三个动作不能混用:
| 动作 | 例程 | 语义 |
|---|---|---|
| 切当前分布 | SetDistrib(jj_Nb_*) |
只重算当前 rank 的 jj_begin/jj_end/jj_nb 和 ij_begin/ij_end。 |
| 当前分布 halo | Register_Hallo |
在当前分布上向上下邻居收发边界 band。 |
| 分布切换交换 | Register_SwapField / Register_SwapFieldHallo |
在旧分布和目标分布之间搬运重叠字段;SwapFieldHallo 还按 Up/Down 扩展目标 halo。 |
SendRequest 负责按 request 打包并 post MPI send/recv,WaitRequest 等待所有 request 并把接收缓冲解包回字段。WaitRequest 结束后会清空 request 并把 tag 重置为 1。
SetDistrib 只改索引
parallel_lmdz:SetDistrib 在 223-251 行接收全 rank 的 jj_Nb_New(0:MPI_Size-1):
- 写入
jj_Nb_Para。 - 从 rank 0 开始累计
jj_begin_para/jj_end_para。 - 取当前
mpi_rank的jj_begin/jj_end/jj_nb。 - 派生一维数组范围
ij_begin=(jj_begin-1)*iip1+1、ij_end=jj_end*iip1。
它不复制任何数组数据。因此 leapfrog_p 每次切分布前后都必须配套 Register_SwapField* -> SendRequest -> WaitRequest,否则当前 rank 的 ij_begin/ij_end 会指向新范围,但字段内容仍只保证旧范围有效。
request 类型在 leapfrog_p 中的角色
leapfrog_p 声明三个 request:
| request | 主要用途 |
|---|---|
TestRequest |
caldyn 初始或动态调整后的字段重分布,以及常规动力 halo。 |
Request_physic |
caldyn/physics 分布切换、physics tendency halo、physics 后回到 caldyn。 |
Request_Dissip |
dissip 前后分布切换、sponge/dissip 守恒修正相关 halo。 |
physics request 在两处调用 SetTag(Request_physic,800),用于把 physics 阶段的 MPI tag 与默认 tag 分开。实际 MPI tag 还会加 1000*omp_rank,以区分 OpenMP 线程。
初始化和自适应调整
490-500 行第一次进入 caldyn 时:
- master 调
SetDistrib(jj_Nb_Caldyn)。 - barrier 后
firstCaldyn=.FALSE.。 - master 初始化 timing。
509-605 行是可选 Adjust 路径。只有满足 iapptrac==iapp_tracvl、当前为 forward 或 leapf、itau/iphysiq>2 且 AdjustCount>30 时才进入。流程是:
allgather_timer_average汇总计时。AdjustBands_caldyn可能移动 caldyn band,rank 0 可WriteBands。- 对
ucov/vcov/teta/masse/ps/pk/pks/phis/phi/q/temp/tsurpk等当前状态注册Register_SwapFieldHallo(..., jj_Nb_caldyn, 0, 0, TestRequest)。 - 调
SetDistrib(jj_nb_caldyn)。 SendRequest(TestRequest)后WaitRequest(TestRequest)完成重分布。- 再调用
AdjustBands_dissip和AdjustBands_physic,更新后续阶段的目标分布。
这里 Up=0, Down=0,所以虽然使用 SwapFieldHallo,实际不扩展 halo;它主要是在 caldyn band 自适应改变后搬运全状态到新 caldyn 分布。
动力计算前 halo
631-660 行在动力 tendency 计算前做当前 caldyn 分布的 halo:
Register_Hallo覆盖ucov/vcov/teta/ps/pkf/pk/pks/p/temp/tsurpk。ps用RUp=1,RDown=2,SUp=2,SDown=1,需要比普通 3D 场更宽的不对称边界。- tracer
q的 halo 注册被注释掉,本段不交换q。 - request 顺序是
Register_Hallo* -> SendRequest(TestRequest) -> barrier -> WaitRequest(TestRequest) -> barrier。
这一步不是分布切换;它只补齐当前 caldyn 分布的上下邻居边界,供后续 geopot_p/caldyn_p/caladvtrac 等使用。
physics 前切到物理分布
当 apphys 为真时,948-1002 行把动力字段从 caldyn 分布换到 physics 分布:
- 设
Request_physictag 为 800。 - 用
Register_SwapFieldHallo(..., jj_Nb_physic, Up, Down, Request_physic)注册字段。 - 主要 prognostic state、pressure/Exner、位势、垂直速度、每个 tracer 和
flxw都参与交换。 - 多数字段使用
Up=2, Down=2;masse使用Up=1, Down=2。 SendRequest/WaitRequest完成后,master 调SetDistrib(jj_nb_Physic)。
这里必须先注册交换再切当前分布,因为 Register_SwapFieldHallo 需要同时知道旧的 jj_begin/jj_end 和目标 jj_Nb_physic。SetDistrib 放在通信完成后,避免当前 rank 在字段还没搬完时就按新范围解释数组。
physics tendency halo 和回填
calfis_p 之后,1040-1077 行先切到 jj_nb_Physic_bis,再给 physics tendency 做 halo:
dufi/dvfi/dtetafi/dpfi/dqfi用Register_Hallo(..., 1,0,0,1, Request_physic)。SendRequest/WaitRequest后回到SetDistrib(jj_nb_Physic)。- 1078-1097 行把
_tmp暂存 tendency 加回dufi/dvfi/dtetafi/dqfi/dpfi。
这段的重点是 tendency 的边界合并,而不是完整状态重分布。jj_nb_Physic_bis 提供一个辅助分布来处理 physics tendency 的上下拼接。
physics 后回到 caldyn
addfi_p、pressure/mass/Exner 刷新和可选 top-bound 之后,1135-1189 行把状态换回 caldyn:
- 再次设置
Request_physictag 为 800。 - 对
ucov/vcov/teta/masse/ps/p/pk/phis/phi/w/q注册Register_SwapField(..., jj_Nb_caldyn, Request_physic)。 Register_SwapField不带 halo 扩展,只把目标 caldyn 分布需要的重叠字段搬回。SendRequest/WaitRequest后 master 调SetDistrib(jj_Nb_caldyn)。
因此 physics 阶段前是“切到 physics 并扩 halo”,physics 后是“回 caldyn,不额外扩 halo”。后续若动力或输出还需要边界,会再单独走 Register_Hallo。
friction 和 dissip 前后
iflag_phys==2 分支中,1249-1255 行在 friction_p 前对 ucov/vcov 做 halo:
ucov用Register_Hallo(0,1,1,0)。vcov用Register_Hallo(1,1,1,1)。
apdiss 分支从 1291 行开始,通信分三段:
| 段 | 源码范围 | 行为 |
|---|---|---|
| sponge 前 | 1303-1319 | 若 callsponge,对 teta 和 ps 注册 halo。源码注释说明原则上只需 ps,但 mixed MPI/OpenMP 下只注册 2D 场会让 WaitRequest 崩溃,因此额外注册 3D teta。 |
| 切到 dissip | 1323-1347 | ucov/vcov 用 Register_SwapFieldHallo(..., jj_Nb_dissip, 1,1),teta/p/pk 用 Register_SwapField;通信完成后 SetDistrib(jj_Nb_dissip)。 |
| dissip 后回 caldyn | 1518-1542 | ucov/vcov/teta/p/pk 用 Register_SwapField(..., jj_Nb_caldyn),通信完成后 SetDistrib(jj_Nb_caldyn)。 |
如果 dissip_conservative 为真,1385-1399 行在重新计算动能前还会对 ucov/vcov 做一次普通 halo。
复现检查表
- 记录当前阶段的
jj_Nb_Caldyn/jj_Nb_physic/jj_Nb_physic_bis/jj_Nb_dissip,不要只看init_parallel后的初始分布。 - 看到
SetDistrib前,检查是否已经注册并完成了对应Register_SwapField*request。 - 区分
Register_Hallo与Register_SwapField*:前者补边界,后者搬运到新分布。 - physics 前交换使用
SwapFieldHallo并带Up/Down,physics 后回 caldyn 使用SwapField。 - 若 mixed MPI/OpenMP 在
WaitRequest附近崩溃,优先检查omp_chunk、request 是否包含 3D 字段、tag 是否与 OpenMP rank 匹配。 - 对输出/restart 字段缺失,先查 dyn3dpar/leapfrog_p 的 gather/root-only 输出;本页只覆盖计算阶段分布和 halo。
待确认
callsponge分支中“只注册ps会让 mixed MPI/OpenMP 的WaitRequest崩溃”的注释说明了经验性规避,但根因需结合具体 MPI/OMP 平台复现。AdjustBands_*的运行时收益依赖timer_average稳定性;典型 Mars 生产配置是否启用Adjust需由运行样例确认。- physics 前
masse使用Up=1,Down=2而其他多数字段使用2,2,源码未在本段解释设计理由。