leapfrog_p 输出、诊断和 restart
源码范围:
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\leapfrog_p.F
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\dynredem_p.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\inithist_p.F
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\bilan_dyn_p.F
Mars 运行参与度:条件经过。Mars 并行 GCM 使用 dyn3dpar/gcm.F -> leapfrog_p,只有开启动力 history、dynav、zonal diagnostic 或写 restart.nc 时才进入本页描述的 I/O 路径。
调度位置
leapfrog_p 的输出段位于阶段计算之后、状态机跳回 label 1/2 之前。非 purmats 分支在 itau 已由 forward 或 leapfrog 阶段推进后写出;purmats 分支只在 backward 半步写出。两个分支都先处理平均/纬向诊断,再处理 instant history 和 Grads,最后判断 restart。
| 输出 | 非 purmats 源码位置 |
purmats 源码位置 |
门控 |
|---|---|---|---|
| zonal diagnostic | 1677-1685 | 1910-1918 | MOD(itau,iperiod)==0 .OR. itau==itaufin 且 ok_dynzon |
| dynav 平均输出 | 1687-1708 | 1920-1941 | 同一 iperiod/itaufin 门,且 ok_dyn_ave |
| instant history | 1715-1785 | 1946-2010 | MOD(itau,iecri)==0;非 purmats 还要求 leapf 或 backward Matsuno |
| restart | 1804-1838 | 2029-2060 | itau==itaufin 或 ecritstart>0 且整除 |
iperiod 同时影响 Matsuno restart 周期和 dynav/zonal 输出周期;iecri 只控制 instant history。排查缺步时不要把这两个频率混用。
dynav 和 zonal diagnostic
平均输出门打开后,ok_dynzon 在 IOIPSL 编译条件下调用:
bilan_dyn_p(2, dtvr*iperiod, dtvr*day_step*periodav,
ps, masse, pk, pbaru, pbarv, teta, phi, ucov, vcov, q)
bilan_dyn_p 自己在并行子域上建 dynzon history domain,内部通过 halo request 补齐 ucov/vcov/q/flux_u/flux_v 等边界,按 dt_cum/dt_app 累积到周期后由 OMP MASTER 写出。因此 bilan_dyn_p 不是简单 root-only dump,它保留并行局部纬向切片语义。
ok_dyn_ave 的 dynav 路径不同:leapfrog_p 在 master 段内先对 vcov/ucov/teta/pk/phi/q/masse/ps/phis 调 Gather_Field(...,0),再由 mpi_rank==0 调 writedynav(itau,...)。当前源码没有实际调用 writedynav_p;并行主循环选择 gather 到 root 后复用串行写例程。
instant history 和 Grads
instant history 的 iecri 门打开后,先把位温转换为温度相关的 tsurpk,再调用 geopot_p 重算 phi,并由 ucov/cu、vcov/cv 构造 unat/vnat。随后:
ok_dyn_ins且启用 IOIPSL 时,gathervcov/ucov/teta/phi/q/masse/ps/phis到 root,mpi_rank==0调writehist(itau,vcov,ucov,teta,phi,q,masse,ps,phis)。output_grads_dyn为真时,gatherunat/vnat/teta/ps/q后在 root includewrite_grads_dyn.h。- 非
purmats下 forward Matsuno 半步不写 instant history,条件是leapf .OR. (.NOT.leapf .AND. .NOT.forward)。
这意味着 history 文件缺字段时,先查 ok_dyn_ins/CPP_IOIPSL/iecri 和阶段过滤;字段错位或缺全局带时,再查 Gather_Field 和当前 SetDistrib 状态。
restart 写出
restart 判断分两步避免 ecritstart==0 时取模除零:
lrestart = .false.
if (itau == itaufin) lrestart = .true.
else if (ecritstart > 0 .and. MOD(itau,ecritstart) == 0) lrestart = .true.
写出时调用 dynredem1_p("restart.nc", time, vcov, ucov, teta, q, masse, ps)。dynredem1_p 内部先 Gather_Field 状态数组和 tracer,再只让 mpi_rank==0 打开 NetCDF、追加 temps 或 Time record,并重写 controle。dynredem0_p 创建文件骨架时同样只有 root 定义维度、控制数组、controle_descriptor、坐标和变量。
Mars 与非 Mars 的 time 实参不同:
| 分支 | Mars time |
非 Mars time |
|---|---|---|
非 purmats, ecritstart>0 |
REAL(itau)/REAL(day_step)+time_0-floor(time_0) |
JH_cur |
非 purmats, 最终且 ecritstart==0 |
REAL(itau)/REAL(day_step)-(day_end-day_ini)+time_0-floor(time_0) |
JH_cur |
purmats, ecritstart>0 |
REAL(itau)/REAL(day_step) |
JH_cur |
purmats, 最终且 ecritstart==0 |
REAL(itau)/REAL(day_step)-(day_end-day_ini) |
JH_cur |
purmats Mars 分支没有加 time_0-floor(time_0),而非 purmats Mars 分支会加;这是 当前源码事实,设计意图需要维护者确认。
初始化边界
本页的写出段依赖 gcm.F 早先完成输出文件初始化:
dynredem0_p("restart.nc",...)创建 restart skeleton,Mars/generic 使用 planetoTime坐标,Earth 使用temps。- 当前
dyn3dpar/gcm.F在mpi_rank==0条件内调用inithist和initdynav,与后续 gather 后 root 调writehist/writedynav的路径一致。 inithist_p和initdynav_p是并行 domain 初始化例程,源码中用flio_dom_set为本 rank 的 U/V/scalar grid 建 IOIPSL domain;但当前gcm.F主路径没有实际调用inithist_p/initdynav_p。
因此 leapfrog_p 负责“何时写”和“写前如何 gather/root”,gcm.F 的 rank-0 初始化和 dynredem0_p 负责“文件结构是否已经定义”。若改回 _p 初始化例程,需要同步检查后续是否仍 gather 后复用串行写例程。
复现要点
- 先确认当前分支是非
purmats还是purmats,因为itau推进和 Mars restart 时间实参不同。 ok_dynzon走bilan_dyn_p并行局部累积;ok_dyn_ave和ok_dyn_ins在leapfrog_p中 gather 后 root 写。writehist_p/writedynav_p存在,但当前主循环实际调用的是 gather 后的writehist/writedynav。dynredem1_p自己再 gather restart 字段,root 写 NetCDF;排查并行 restart 要同时看调用前分布和dynredem1_p内部 gather。- Mars 并行 restart 不应套用串行页的单一时间公式,必须区分
purmats和ecritstart。
待确认
purmatsMars 中途 restart 不加time_0-floor(time_0)是否为保留行为还是历史遗漏。leapfrog_p中声明的dynhist_file/dynhistave_file未在本例程使用,实际文件名初始化由gcm.F承担。