Mars 垂直坐标与动力-物理共享状态约定
源码:
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d_common\disvert_noterre.FLMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d_common\disvert.F90LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3d_common\comvert_mod.F90LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\vertical_layers_mod.F90- 边界上下文:
LMDZ.MARS\libf\dynphy_lonlat\phymars\iniphysiq_mod.F90、ini_archive.F、lect_start_archive.F、readhead_NC.F、newstart.F、start2archive.F,以及LMDZ.MARS\libf\phymars\physiq_mod.F、dyn1d\init_testphys1d_mod.F90。
本文只覆盖 COMMON 垂直坐标状态如何生成、复制到物理侧、被 Mars utility/physics 使用;Mars 文件只作为使用位置和接口边界。
结论
Mars/Generic 默认垂直坐标链是:
iniconst
-> disvert_type default = 2 when planet_type != "earth"
-> disvert_noterre
-> read esasig.def or z2sig.def
-> write comvert_mod: ap/bp/aps/bps/presnivs/pseudoalt
-> inigeom
-> iniphysiq
-> inigeomphy
-> init_vertical_layers(...)
-> copy comvert_mod state into phy_common/vertical_layers_mod
-> Mars physiq/outputs/1D/archive consumers
comvert_mod 是动力侧固定维度数组状态;vertical_layers_mod 是物理侧 ALLOCATABLE, SAVE, THREADPRIVATE 状态。两者不是自动共享内存,必须通过 inigeomphy -> init_vertical_layers 或 1D 初始化显式复制。
状态表
| 状态 | 所在模块 | 维度 | 单位/含义 | 主要使用位置 |
|---|---|---|---|---|
ap(llm+1) |
comvert_mod, vertical_layers_mod |
interface | Pa,hybrid pressure contribution | pression, Mars physics, archive/output |
bp(llm+1) |
同上 | interface | sigma contribution | pression, vertical velocity, Mars physics |
aps(llm) |
同上 | mid-layer | Pa,mid-layer pressure contribution | Mars vertical interpolation, physics zplay |
bps(llm) |
同上 | mid-layer | sigma contribution | Mars vertical interpolation, physics zplay |
presnivs(llm) |
同上 | mid-layer | reference mid-layer pressure, Pa | history/XIOS vertical axes, physics diagnostics, dissipation profiles |
pseudoalt(llm) |
同上 | mid-layer | km, -scaleheight*log(presnivs/preff) |
Mars/XIOS/stats/diagfi altitude axis |
pa |
comvert_mod |
scalar | Pa,hybrid 坐标转 pressure-dominated 的参考压力 | disvert, disvert_noterre, restart constants |
preff |
comvert_mod and copied to vertical_layers_mod |
scalar | Pa,reference surface pressure | pressure axes and pseudo altitude |
scaleheight |
comvert_mod and copied to vertical_layers_mod |
scalar | km | pseudoalt, dissipation/profile diagnostics |
disvert_type |
comvert_mod |
scalar | 1 Earth automatic, 2 planets z2sig/ esasig |
iniconst branch |
pressure_exner |
comvert_mod |
scalar | layer pressure computation option | Exner/pressure helper consumers |
disvert_noterre 生成逻辑
disvert_noterre 是 Mars/Generic 默认路径。入口先设置:
hybrid=.true.,然后getin('hybrid', hybrid);hybrid=.false.时生成纯 sigma 坐标。- 先尝试打开
esasig.def;失败后打开z2sig.def;两者都不存在时打印错误并stop。
esasig.def 分支
esasig.def 分支读取:
scaleheightdz0dz1nhaut
然后把 dz0/dz1 除以 scaleheight,用指数和双曲正切构造 interface sig(1:llm+1)。该分支还构造 s(l) 并归一化,源码注释强调它来自能量守恒相关旧方案。
z2sig.def 分支
z2sig.def 分支读取:
- 第一行
scaleheight - 接下来
llm行zsig(l)
若行数少于 llm,调用 abort_gcm(modname,"z2sig.def too short?",1)。若多于 llm,只写 WARNING,不 abort。
interface sigma 的构造是:
sig(1) = 1
sig(l) = 0.5 * ( exp(-zsig(l)/scaleheight)
+ exp(-zsig(l-1)/scaleheight) ), l=2..llm
sig(llm+1) = 0
这意味着 z2sig.def 给的是层中心或目标高度序列,disvert_noterre 用相邻高度的指数压力平均构造 interface sigma。
hybrid 与 sigma 分支
nivsigs(l)=l,nivsig(l)=l 只是垂直层索引。
若 hybrid=.true.:
sig_hybrid(sig(l), pa, preff, newsig)
bp(l) = exp(1 - 1/newsig**2)
ap(l) = pa * (newsig - bp(l))
bp(llm+1) = 0
ap(llm+1) = 0
sig_hybrid 求解的目标方程是:
(1 - pa/preff) * exp(1 - 1/newsig**2)
+ (pa/preff) * newsig = sig
当 sig*preff/pa < 0.25 时,源码直接用 newsig=sig*preff/pa;其他情况使用二分迭代,收敛标准按 pseudo-altitude 误差约束。
若 hybrid=.false.:
ap(l) = 0
bp(l) = sig(l)
ap(llm+1) = 0
bp(llm+1) = 0
mid-layer 与参考压力
大多数层:
aps(l) = 0.5 * (ap(l) + ap(l+1))
bps(l) = 0.5 * (bp(l) + bp(l+1))
顶层特殊处理:
- hybrid:
aps(llm)=aps(llm-1)**2/aps(llm-2),bps(llm)=0.5*(bp(llm)+bp(llm+1)) - sigma:
bps(llm)=bps(llm-1)**2/bps(llm-2),aps(llm)=0
然后:
presnivs(l) = aps(l) + bps(l) * preff
pseudoalt(l) = -scaleheight * log(presnivs(l)/preff)
源码注释提醒:顶层 mid-layer 选择是人为约定,并要求与 exner_milieu.F 使用相同假设。
disvert.F90 对照
disvert.F90 是 Earth/通用自动生成路径,iniconst 在 planet_type=="earth" 时默认选择它。它支持 vert_sampling:
param:读sigma.defsigmatropostratostrato_correctstrato_custom0strato_customread:读hybrid.txt
它同样写入 comvert_mod: ap/bp/nivsigs/nivsig/preff/pa/presnivs/dpres/scaleheight,但 Mars/Generic 默认不走这条路径,除非 run.def 显式覆盖 disvert_type=1。
动力侧压力使用
动力核通过 pression(ngrid, ap, bp, ps, p) 计算 interface pressure:
p(:,l) = ap(l) + bp(l) * ps(:)
典型使用位置包括:
caldyn/caldyn_p:动力 tendency 前计算 pressure。leapfrog/leapfrog_p与 MARSleapfrog_nogcm:时间推进和 physics coupling 前刷新 pressure。integrd:积分写回前计算 pressure。vitvert/vitvert_p:用bp(l+1)修正垂直质量通量。inidissip、top_bound、sponge:使用presnivs/scaleheight/pseudoalt构造垂直剖面或诊断。
复制到物理侧
Mars iniphysiq_mod.F90 先调用 COMMON inigeomphy。inigeomphy_mod.F90 第 22-24 行 USE comvert_mod 读取 preff, ap, bp, aps, bps, presnivs, scaleheight, pseudoalt,第 243-245 行调用:
CALL init_vertical_layers(nlayer, preff, scaleheight,
ap, bp, aps, bps, presnivs, pseudoalt)
vertical_layers_mod 为物理侧分配数组并复制这些值。由于该模块变量是 THREADPRIVATE,OpenMP 运行中每个线程有自己的物理侧垂直层状态。
Mars physiq_mod.F 直接 USE vertical_layers_mod, ONLY: ap,bp,aps,bps,presnivs,pseudoalt:
- 初始化 XIOS 时把
presnivs/pseudoalt传给initialize_xios_output。 - 表面压力变化后用
zplay=aps+bps*ps、zplev=ap+bp*ps更新压力层。 - XIOS 输出
ap/bp/aps/bps。
Mars callphysiq_mod.F90 仍从动力-物理接口接收 zplev/zplay/presnivs,但 physics 内部的垂直坐标常量来自 vertical_layers_mod。
Mars utility 和 archive 约定
ini_archive.F
ini_archive.F 从 comvert_mod 读取 ap,bp,aps,bps,pa,preff,presnivs,pseudoalt,写入 archive 变量:
ap(llm+1)bp(llm+1)aps(llm)bps(llm)presnivs(llm)pseudoalt(llm),带units="km"、positive="up"
readhead_NC.F
readhead_NC.F 用于读取 header/control。它 USE comvert_mod, ONLY: aps,bps,preff,并要求 NetCDF 中存在 aps 和 bps;缺失或读取失败都会 CALL abort。
lect_start_archive.F
lect_start_archive.F 读取旧 archive 做插值:
aps缺失时把apsold=0。bps缺失时尝试读旧变量sig_s;若也不存在则 abort。- 垂直插值调用
interp_vert(old, new, apsold, bpsold, aps, bps, psold, ...)。 q2等 interface 量使用ap/bp作为新网格 interface 坐标。
这说明 archive 兼容层对旧文件有 fallback,但当前 header 读取链对 aps/bps 更严格。
newstart.F 和 start2archive.F
newstart.F 在初始化 physics 前调用 iniphysiq,因此会经 inigeomphy 初始化 vertical_layers_mod;后续在写新 start/startfi 前调用 pression(ip1jmp1, ap, bp, ps, p3d)。
start2archive.F 先调用 iniconst/inigeom/inifilr,再调用 iniphysiq,随后用 pression(ap,bp,ps,p3d) 和 exner_hyb 准备 archive 转换。
1D/testphys1d 约定
Mars 1D 初始化 init_testphys1d_mod.F90 直接使用 COMMON 转发的 comvert_mod:
- 设置或读取
psurf、pa、preff。 - 设置
hybrid=.true.,再由getin("hybrid",hybrid)覆盖。 - 调用
disvert_noterre。 - 调用
init_vertical_layers(nlayer,preff,scaleheight,ap,bp,aps,bps,presnivs,pseudoalt)。 - 计算
plev=ap+psurf*bp、play=aps+psurf*bps。
这条链不经过 3D iniphysiq,所以 1D 必须自己完成 comvert_mod -> vertical_layers_mod 的复制。
复现检查清单
- 确认
planet_type和disvert_type:Mars/Generic 默认disvert_type=2。 - 确认运行目录有
esasig.def或z2sig.def;若两者都有,esasig.def优先。 - 确认
pa/preff来源:restartcontrole、conf_planete、或 1D 初始化默认/输入。 - 确认
hybrid:默认.true.,可由getin("hybrid")覆盖为纯 sigma。 - 检查
ap/bp/aps/bps是否写入comvert_mod后再调用inigeomphy或 1Dinit_vertical_layers。 - 若处理 archive,检查
aps/bps是否存在;旧 archive 只有sig_s时只能走lect_start_archivefallback,不适合所有读取器。 - 若排查输出坐标,确认
presnivs是 Pa,history 常把它除以 100 写成 mb,而pseudoalt是 km。
风险和待确认
disvert_noterre的z2sig.def多余行只警告不 abort;少行才 abort。批量算例检查时应主动比对llm和文件行数。readhead_NC.F只读取aps/bps,不读取完整ap/bp/pseudoalt;调用链如果需要完整 interface 坐标,必须确认它们已经由当前运行的disvert_noterre或其他入口生成。- 旧 archive 的
sig_sfallback 只在lect_start_archive中存在;不能推断所有 Mars NetCDF 读取器都能处理旧格式。