动力输出状态持有者
输入范围
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d\temps_mod.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\temps_mod.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d_common\com_io_dyn_mod.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d_common\inithist.F
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d_common\writehist.F
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d_common\initdynav.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d_common\writedynav.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d_common\dynetat0.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d_common\dynredem.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\dynredem_p.F90
Mars 运行参与度:必经/条件经过。temps_mod 是 GCM 时间推进和 restart 的共享状态层,Mars 3D 和 Mars nogcm 路径都会使用;com_io_dyn_mod 只在 COMMON 动力 history/dynav 输出开启且 CPP_IOIPSL 编译进入时条件经过。
职责边界
本页只解释状态持有者如何连接 I/O 注册和时间步写出:
temps_mod保存动力时间、参考日期、当前步长和 Mars 初始小时。com_io_dyn_mod保存 dynamics history 文件名常量和 IOIPSL file id。inithist/initdynav从temps_mod:itau_dyn读取初始化时间基准,并把histbeg返回的 file id 写入com_io_dyn_mod。writehist/writedynav再读取同一批 file id,并用itau_dyn + time构造histwrite的时间步。
字段注册清单、ok_dyn_* 门控和 Mars physics 输出边界见 history/dynav 字段注册和写出门控。
temps_mod 状态
串行 dyn3d/temps_mod.F90 和并行 dyn3dpar/temps_mod.F90 定义同名模块和同一批核心变量。并行版额外声明 OpenMP THREADPRIVATE,并在注释中要求打开 OpenMP parallel section 时把新增变量加入 COPYIN。
| 变量 | 类型 | 写入/维护方 | 被 history/dynav 使用方式 |
|---|---|---|---|
itau_dyn |
INTEGER |
gcm/nogcm 新运行置 0;dynetat0 从 restart tab_cntrl(31) 恢复;dynredem* 用它写出累计动力步。 |
inithist/initdynav 取作 tau0;writehist/writedynav 用 itau_w=itau_dyn+time。 |
itaufin |
INTEGER |
leapfrog/leapfrog_p/leapfrog_nogcm 由 nday/day_step/fractday/ndynstep 推出。 |
不直接传给 history API;输出门用局部 itau 与 itaufin 判断最终步。 |
itau_phy |
INTEGER |
主流程和 physics 调度状态。 | history/dynav 串行例程不直接使用。 |
day_ini, day_end |
INTEGER |
gcm/nogcm、dynetat0、Mars utility 和 leapfrog* 维护运行起止日。 |
参与 leapfrog* 日期计算和 restart,间接决定输出时间上下文。 |
annee_ref, day_ref |
INTEGER |
新运行从 anneeref/dayref 复制;restart 从 controle 恢复。 |
传入 inithist/initdynav 的 day0/anne0,并由 ymds2ju 生成 history 起始 Julian day。 |
jD_ref, jH_ref |
REAL |
gcm/gcm_p 用 ymds2ju 派生,leapfrog* 用它计算当前 Julian day/hour。 |
history 例程不直接使用;用于 GCM 日期和 restart 输出一致性。 |
start_time, hour_ini |
REAL |
conf_gcm/restart/Mars utility 设置;leapfrog* 检查二者不能同时非零。 |
不进入 histwrite,但影响当前日期和 restart 时间解释。 |
dt |
REAL |
leapfrog* 在 Matsuno 和 leapfrog 阶段切换为 dtvr 或 2*dtvr。 |
history 例程使用调用方传入的 time_step/tstep,不直接读取 dt。 |
calend |
CHARACTER(len=10) |
conf_gcm 读取。 |
IOIPSL/XIOS 日期配置链可使用;history 四个串行例程不直接读取。 |
com_io_dyn_mod 状态
com_io_dyn_mod.F90 没有分配数组、没有 SAVE 声明,也没有 OpenMP 指令;模块变量天然保持在进程全局模块状态中。它把文件名和 file id 分成 instant 与 averaged 两组。
| 状态 | 常量/id | 写入者 | 读取者 | 生命周期 |
|---|---|---|---|---|
| instant 文件名 | dynhist_file, dynhistv_file, dynhistu_file |
编译期参数常量 | inithist |
始终可读,默认 dyn_hist.nc/dyn_histv.nc/dyn_histu.nc。 |
| instant file id | histid, histvid, histuid |
inithist 的三次 histbeg 返回值 |
writehist 的 histwrite/histsync |
只有 ok_dyn_ins 初始化成功后有效。 |
| averaged 文件名 | dynhistave_file, dynhistvave_file, dynhistuave_file |
编译期参数常量 | initdynav |
始终可读,默认 dyn_hist_ave.nc/dyn_histv_ave.nc/dyn_histu_ave.nc。 |
| averaged file id | histaveid, histvaveid, histuaveid |
initdynav 的三次 histbeg 返回值 |
writedynav 的 histwrite/histsync |
只有 ok_dyn_ave 初始化成功后有效。 |
源码搜索只发现 com_io_dyn_mod 被 inithist.F、writehist.F、initdynav.F90、writedynav.F90 使用。并行 _p history 实现通过实参传递 file id,不直接使用这个模块;当前 dyn3dpar/gcm.F 的 root 分支仍调用串行 inithist/initdynav 初始化这些模块 id。
初始化到写出的状态链
gcm / gcm_p / Mars nogcm
-> 设置 temps_mod: annee_ref, day_ref, day_ini, start_time, itau_dyn
-> 若 ok_dyn_ins:
inithist(day_ref, annee_ref, time_step, t_ops, t_wrt)
tau0 = temps_mod:itau_dyn
histbeg(..., tau0, zjulian, tstep, ..., com_io_dyn_mod:histuid/histvid/histid)
-> 若 ok_dyn_ave:
initdynav(day_ref, annee_ref, time_step, t_ops, t_wrt)
tau0 = temps_mod:itau_dyn
histbeg(..., tau0, zjulian, tstep, ..., histuaveid/histvaveid/histaveid)
leapfrog / leapfrog_p / Mars leapfrog_nogcm
-> 局部 itau 按时间循环递增
-> 若 ok_dyn_ins 且 MOD(itau,iecri)==0:
writehist(time=itau, ...)
itau_w = temps_mod:itau_dyn + time
histwrite(com_io_dyn_mod:hist*id, field, itau_w, ...)
-> 若 ok_dyn_ave 且 (MOD(itau,iperiod)==0 或 itau==itaufin):
writedynav(time=itau, ...)
itau_w = temps_mod:itau_dyn + time
histwrite(com_io_dyn_mod:hist*aveid, field, itau_w, ...)
itau_dyn 表示 restart 基准中的累计动力步,time 实参是当前运行段内的局部 itau。因此续跑时 history/dynav 写出的步号不是从 0 重新开始,而是以 itau_dyn + itau 延续。
Restart 对时间状态的影响
dynetat0.F90 读取 start.nc 的 controle 和 Time:
tab_cntrl(4/5)参与恢复day_ref/annee_ref。tab_cntrl(29)在新格式中恢复hour_ini。tab_cntrl(30)恢复day_ini。tab_cntrl(31)恢复itau_dyn。tab_cntrl(32)恢复start_time。- 若存在
Time变量,dynetat0选定timestart对应索引后把time0 + hour_ini折算回day_ini/hour_ini。
dynredem.F90 和 dynredem_p.F90 写 restart 时把 itau_dyn + itaufin 写入 tab_cntrl(31),把当前写出时间写入 tab_cntrl(32)。这和 history 写出中的 itau_dyn + time 是同一类累计步号语义:restart 记录运行段结束时的累计步,history 记录运行段中每次写出的累计步。
并行和 OpenMP 边界
并行 dyn3dpar/temps_mod.F90 把 dt/jD_ref/jH_ref/start_time/hour_ini/day_ini/day_end/annee_ref/day_ref/itau_dyn/itau_phy/itaufin/calend 都声明为 THREADPRIVATE。dyn3dpar/gcm.F 中保留注释说明打开 OpenMP parallel section 时应 COPYIN 这些变量。当前 history/dynav 初始化在 gcm.F 的 root 分支调用串行 inithist/initdynav,写出则在 leapfrog_p 的 root/gather 输出段调用串行 writehist/writedynav。
复现并行 history 时间时要区分两层状态:
temps_mod在并行构建中是 threadprivate 时间状态,必须在进入相关并行区时保持 copyin 一致。com_io_dyn_mod不是 threadprivate;当前可达 root 输出路径依赖 root 初始化后的模块 file id。
排查顺序
- 检查是否从 restart 读取:若
read_start为真,先看dynetat0是否正确恢复itau_dyn/day_ini/hour_ini/start_time。 - 检查是否开启
ok_dyn_ins/ok_dyn_ave且编译包含CPP_IOIPSL;否则com_io_dyn_mod的 id 不会被有效设置。 - 检查初始化是否先于写出发生:
histid/hist*aveid只有inithist/initdynav后才有效。 - 检查写出时间是否符合预期:
histwrite使用itau_dyn + itau,不是单独的局部itau。 - 并行运行若时间状态异常,优先确认
temps_modthreadprivate copyin,而不是修改com_io_dyn_mod。
待确认
dyn3dpar/gcm.F中 OpenMPCOPYIN片段当前是注释形式;实际构建路径是否由预处理或旧 OpenMP 语法启用,需要结合编译配置实测确认。- IOIPSL history 文件内部 time axis 对
tau0/tstep/itau_w的解释未做生成文件 header 实测;本页只记录源码传参语义。