leapfrog_p 时间步调度
源码范围:
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\leapfrog_p.F
Mars 运行参与度:条件经过。Mars 并行 GCM 经过 dyn3dpar/gcm.F -> leapfrog_p;串行 Mars GCM 经过 dyn3d/leapfrog.F,MARS phymars/nogcm.F90 只作边界背景,不属于 COMMON 源码。
调度职责
本页只说明并行 leapfrog_p 的时间步状态机:forward/leapf/purmats/apphys/apdiss/lafin、True_itau、itau 推进、history/restart 写出和最终停止。入口数组、SAVE 工作区、SetDistrib、halo/swap 和 root gather 的状态约定见 leapfrog_p 状态约定 与 dyn3dpar/leapfrog_p。
leapfrog_p 仍沿用串行 Matsuno/leapfrog 思路,但阶段转换发生在 OpenMP parallel 区内部。并行差异主要是:输出/restart 写出前需要 gather,physics 前后需要 band/halo 切换;这些并行通信并不改变 itau/forward/leapf 的时间步语义。
终止步数
237-250 行计算 itaufin:
| 条件 | 结果 |
|---|---|
nday >= 0 |
itaufin = nday * day_step |
nday < 0 |
itaufin = -nday,按动力步数运行 |
less1day |
覆盖为 int(fractday * day_step) |
ndynstep > 0 |
最终覆盖为 ndynstep |
随后 itaufinp1 = itaufin + 1。itau 从 0 开始,physics 初值为 .TRUE.,但 iflag_phys==0 或 iflag_phys==2 会关闭 physics 调度。
停止不是正常跑出循环:非 purmats 分支在 itau == itaufinp1 时 finalize_parallel 后调用 abort_gcm(modname,'Simulation finished',0);purmats 分支在 forward 半步后检查同一条件并停止。
两个 label
主循环由 label 1 和 label 2 组成:
| label | 源码位置 | 作用 |
|---|---|---|
1 |
约 405 行前后 | Matsuno forward 入口,设置 forward=.TRUE., leapf=.FALSE., dt=dtvr。 |
2 |
417 行 | backward 或 leapfrog 阶段入口,重新计算日期、门控标志、动力/物理/耗散/输出,然后在步末决定下一个阶段。 |
label 2 每次进入都会把 apphys/statcl/conser/apdiss 先清零,再按 purmats 和当前 itau/forward 计算门控。随后 685 行把 True_itau=True_itau+1,用于日志输出“实际进入计算段的迭代次数”。因此:
True_itau计数每次 label 2 计算段。itau是模式时间步计数,只在步末按分支推进。forward、leapf是当前阶段标志,不等于“已经完成的阶段”。
日期刷新
432-446 行只有在 leapf 为真时刷新 jD_cur/jH_cur:
jD_cur = jD_ref + day_ini - day_ref + (itau+1)/day_step
Mars: jH_cur = jH_ref + hour_ini + mod(itau+1,day_step)/day_step
non-Mars: jH_cur = jH_ref + start_time + mod(itau+1,day_step)/day_step
若 jH_cur > 1.0,则日期进一日并把小时减 1。Forward/backward Matsuno 段不会在这里刷新日期;Mars restart 写出还有单独的 time_0 小数修正,见 leapfrog_p 状态约定 和 dynredem-restart。
门控标志
454-471 行重新计算 apphys/conser/apdiss:
| 分支 | conser |
apdiss |
apphys |
|---|---|---|---|
purmats |
MOD(itau,iconser)==0 .AND. forward |
MOD(itau,dissip_period)==0 .AND. .NOT.forward |
MOD(itau,iphysiq)==0 .AND. .NOT.forward .AND. physics |
非 purmats |
MOD(itau,iconser)==0 |
MOD(itau+1,dissip_period)==0 .AND. .NOT.forward |
MOD(itau+1,iphysiq)==0 .AND. physics |
随后还有两个覆盖:
llm==1的 shallow-water 情况关闭apdiss。NODYN编译分支关闭apdiss。
并行版还有一个串行页容易漏掉的细节:487-488 行再次把 statcl=.FALSE.、conser=.FALSE.,注释说明并行版不向 stdout 输出 control variables。因此 conser 的周期逻辑存在,但在当前并行实现中被强制关闭,不应把它解释成会实际触发 sortvarc 控制量输出。
lafin 与 physics
851-855 行在进入 physics 前设置 lafin:
| 分支 | 最后一次 physics 标志 |
|---|---|
purmats |
itau == itaufin .AND. .NOT.forward |
非 purmats |
itau + 1 == itaufin |
只有 apphys 为真时才调用 physics。并行 physics 调度在 calfis_p(lafin,jD_cur,jH_cur,...) 后通过 tendency halo merge 和 addfi_p(dtphys,leapf,forward,...) 回写状态。这里的 lafin 是传给 physics 的“最终调用”语义,不是主循环退出条件;真正退出仍由 itaufinp1 或最终阶段写出后的状态转换决定。
非 purmats 状态机
非 purmats 分支从 1621 行开始,使用 Matsuno + leapfrog:
- 步末若
forward .OR. leapf,执行itau = itau + 1。Backward Matsuno 段不增加itau。 - 若
itau == itaufinp1,结束并abort_gcm(...,0)。 - 若
MOD(itau,iperiod)==0 .OR. itau==itaufin,写 dynav/zonal 平均输出;itau==itaufin时iav=1。 - 若
MOD(itau,iecri)==0,只在leapf或 backward Matsuno 阶段写 instant history;forward Matsuno 阶段不写。 - restart 门控先设
lrestart=.false.,最终步必写;否则ecritstart>0且MOD(itau,ecritstart)==0时写。 - 阶段转换:
MOD(itau,iperiod)==0:回 label 1,下一轮从 forward 开始。MOD(itau-1,iperiod)==0且当前是forward:转 backward,forward=.FALSE.,leapf=.FALSE., 回 label 2。MOD(itau-1,iperiod)==0且当前不是forward:进入第一个 leapfrog,leapf=.TRUE.,dt=2*dtvr, 回 label 2。- 其他情况:常规 leapfrog,
leapf=.TRUE.,dt=2*dtvr, 回 label 2。
这意味着 iperiod 同时控制 Matsuno restart 周期和 dynav/zonal 平均输出门。iecri 控制 instant history,但还要经过“只在 LF 或 backward Matsuno 输出”的额外阶段过滤。
purmats 状态机
purmats 分支从 1873 行后开始,只在 forward/backward Matsuno 之间往返:
- 若当前
forward,先itau=itau+1,再forward=.FALSE.。 - forward 半步结束后若
itau==itaufinp1,停止。 - 否则回 label 2 执行 backward 半步。
- backward 半步负责 dynav/history/restart 写出。
- 写出和 restart 检查后设置
forward=.TRUE.,回 label 1。
purmats 下没有常规 leapf=.TRUE. 的 leapfrog 阶段;apphys 与 apdiss 都要求 .NOT.forward,因此只落在 backward 半步。
输出和 restart 门控
并行版两个分支的输出门控基本一致,但写出前需要 gather 到 root:
| 输出 | 非 purmats |
purmats |
并行写出约束 |
|---|---|---|---|
bilan_dyn_p / dynav |
MOD(itau,iperiod)==0 .OR. itau==itaufin |
同左,只在 backward 半步 | ok_dynzon 或 ok_dyn_ave 后触发;dynav 前 gather 全场。 |
| instant history | MOD(itau,iecri)==0 且 leapf 或 backward Matsuno |
MOD(itau,iecri)==0,发生在 backward 半步 |
ok_dyn_ins 时 gather 后 rank 0 调 writehist。 |
| restart | itau==itaufin 或 ecritstart>0 且整除 |
同左,只在 backward 半步 | master 调 dynredem1_p;Mars 与非 Mars 时间实参不同。 |
Mars restart 时间在两个分支不同:
- 非
purmats中途/最终 restart 使用REAL(itau)/REAL(day_step) + time_0 - floor(time_0),最终ecritstart==0分支还减day_end-day_ini。 purmatsMars restart 在 2047-2053 行使用REAL(itau)/REAL(day_step)或再减day_end-day_ini,没有中途分支的time_0-floor(time_0)修正。- 非 Mars fallback 使用
JH_cur。
复现要点
- 先判断
purmats,因为itau、apphys、apdiss和阶段转换规则都随它改变。 - 非
purmats下 backward 阶段不递增itau;forward 和 leapfrog 阶段才递增。 apphys非purmats看itau+1,purmats看itau且必须是 backward 半步。lafin只传给 physics,不能当作主循环退出标志。conser在并行版最终被强制清零;排查控制量 stdout 输出时不要照串行路径推断。True_itau适合对应日志迭代号,itau适合对应模型时间、输出和 restart 门控。- 输出/restart 发生在阶段转换前;若输出缺字段,先查
Gather_Field和 root-only 写出,再查时间门控。
待确认
conser周期逻辑保留但随后被强制关闭,是否仍有历史配置依赖它的 stdout 控制量输出,需要结合旧并行版本或维护者意图确认。purmatsMars restart 中途写出不加time_0-floor(time_0),而非purmats中途写出会加;该差异是源码事实,设计意图待确认。True_itau未在本例程内初始化,需由调用前状态或模块定义确认其初始值来源。