fallback getin 文件来源、key 表初始化和 Mars 无 IOIPSL 条件经过

输入范围

LMDZ.COMMON-6.3\LMDZ.COMMON\libf\misc\ioipsl_getincom.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\ioipsl_getin_p_mod.F90

本页范围

本页深入 ioipsl_getincom.F90 的 fallback getin 实现,覆盖:.def 文件如何被读入内存、key 表如何构建和缓存、keystatus 语义、向量 key 和压缩字段的处理方式,以及 Mars 在 CPP_IOIPSL 未定义时经由 getin_p 调到此 fallback 的完整链路。ioipsl-getin-system 组页给出系统级概述;本页聚焦文件来源和 key 表内部机制。

编译守卫与 fallback 选择

ioipsl_getin_p_mod.F90 的编译守卫决定 getin 的来源:

#ifdef CPP_IOIPSL
USE ioipsl, ONLY: getin        ! IOIPSL 库版本
#else
USE ioipsl_getincom, ONLY: getin ! 本 fallback 版本
#endif

Mars 编译通常不定义 CPP_IOIPSL,因此 getin_p 的每个 specific procedure 在 master 线程上调用的是本页描述的 ioipsl_getincom:getin。动力侧 conf_gcm.F90 直接 USE ioipsl_getincom 或 IOIPSL getin,取决于动力编译选项。

文件来源

初始文件列表

getin_readfilelist(1) 硬编码为 'run.def'nbfiles=1filelistCHARACTER(LEN=100) 的 SAVE 数组,容量 max_files=100

INCLUDEDEF 机制

getin_readdef 遇到 INCLUDEDEF 关键字时,getin_decrypt 将该行所有空格分隔的文件名追加到 filelistgetin_readDO WHILE (current <= nbfiles) 循环会随之读取新增文件,形成深度优先的包含链。

run.def
  INCLUDEDEF phys_mars.def watercloud.def
    → filelist(2) = 'phys_mars.def'
    → filelist(3) = 'watercloud.def'

文件打开与缺失处理

getin_readdefOPEN(UNIT=22, FILE=filelist(current), STATUS="OLD", IOSTAT=io_err) 打开文件。若 io_err /= 0,调用 ipslerr(2,...)(warning 级别)并 RETURN,不终止程序。因此 Mars 运行时若某 .def 文件不存在,该文件被静默跳过,后续 key 查找将使用调用方提供的默认值。

文件解析管线

行读取与注释跳过

getin_skipafew 逐行读取,跳过以 # 开头的注释行。遇到注释行时重置 nb_lastkey = 0,终止向量续行上下文。空行同样终止续行。

key=value 解析

getin_readdefINDEX(READ_str, '=') 分割 key 和 value:

  1. key 提取key_str = TRIM(ADJUSTL(READ_str(1:ptn-1)))
  2. __NNN 向量下标规整:若 key 含 __,将下标部分重格式化为 c_i_fmt = '(I5.5)'(5 位零填充),确保 key__1key__00001 匹配。
  3. value 清洗nocomma 去逗号,cmpblank 压缩多余空白。

压缩字段表示

getin_decrypt 检测 value 中的 * 号(非引号开头时):N*value 表示该值重复 N 次。compline(nb_lines) 存储重复次数(> 0),否则 -1

多值行拆分

若 value 含空格且非引号包裹,getin_decrypt 按空格拆分为多个 TARGET__00001, TARGET__00002, ... 条目。每个条目独立存入 targetlist/fichier/fromfile

向量续行

= 号的非空行被视为上一个 key 的续行。若上一个 key 是标量(nb_lastkey == 1),自动转换为 TARGET__00001 向量表示并继续递增下标。

文本存储层

文件解析结果存入三组并行数组:

数组 类型 说明
targetlist(:) CHARACTER(LEN=l_n) key 名(截断至 l_n=30 字符)
fichier(:) CHARACTER(LEN=100) 原始 value 文本
fromfile(:) INTEGER 来源文件索引(filelist 下标)
compline(:) INTEGER 压缩重复次数(> 0)或 -1

初始分配 i_txtslab=1000 行,getin_alloctxt 按 slab 扩容。nb_lines 跟踪当前行数。

一致性检查

getin_read 在所有文件读取完成后调用 getin_checkcohe。该子程序对 targetlist 做 O(n²) 逐对比较,发现重复 key 时:

  1. 打印两个值的来源和文本。
  2. 先出现targetlist(line) 置为空白(' '),保留后出现的值。
  3. 打印 "We will keep only the last value"

这意味着后读入的文件覆盖先读入的同名 key,同一文件内后面的行覆盖前面的行。

key 表结构

t_key 类型

TYPE :: t_key
  CHARACTER(LEN=l_n) :: keystr         ! key 名(30 字符)
  INTEGER :: keystatus                 ! 1=文件, 2=默认, 3=部分默认
  INTEGER :: keytype                   ! k_i=1, k_r=2, k_c=3, k_l=4
  INTEGER :: keycompress               ! >0: 压缩重复次数; <0: 未压缩
  INTEGER :: keyfromfile               ! 来源文件索引
  INTEGER :: keymemstart               ! typed memory 起始位置
  INTEGER :: keymemlen                 ! typed memory 占用长度
END TYPE t_key

类型常量

常量 含义
k_i 1 INTEGER
k_r 2 REAL
k_c 3 CHARACTER
k_l 4 LOGICAL

typed memory 池

四组一维 SAVE 数组分别存储各类型值:

数组 类型 初始大小
i_mem(:) INTEGER memslabs=200
r_mem(:) REAL memslabs=200
c_mem(:) CHARACTER(LEN=100) memslabs=200
l_mem(:) LOGICAL memslabs=200

i_mempos/r_mempos/c_mempos/l_mempos 跟踪各池当前末尾位置。getin_allocmem 按需扩容,每次增加 MAX(memslabs, len_wanted) 个元素,使用 tmp 数组复制旧数据。

key_tab 分配

getin_allockeys 初始分配 memslabs=200t_key 条目。空间不足时按 memslabs 增量扩容。key_tab(:)%keycompress 初始化为 -1

getin 调用流(首次 vs 缓存命中)

每个 typed specific procedure(getinis/getini1d/getini2d/getinrs/getinr1d/getinr2d/getincs/getinc1d/getinc2d/getinls/getinl1d/getinl2d)遵循相同模式。以 getinis 为例:

SUBROUTINE getinis (target, ret_val)
  CALL get_findkey (1, target, pos)     ! ① 在 key_tab 中查找
  tmp_ret_val(1) = ret_val              ! ② 保存调用方默认值
  IF (pos < 0) THEN
    CALL get_fil (target, status, fileorig, i_val=tmp_ret_val)  ! ③ 从文件文本解析
    CALL get_wdb (target, status, fileorig, 1, i_val=tmp_ret_val) ! ④ 写入 key 表
  ELSE
    CALL get_rdb (pos, 1, target, i_val=tmp_ret_val)  ! ⑤ 从 key 表读取
  ENDIF
  ret_val = tmp_ret_val(1)              ! ⑥ 回写
END SUBROUTINE getinis

流程阶段

阶段 子程序 说明
① 查找 get_findkey(i_tab=1) 线性扫描 key_tab(1:nb_keys)%keystr,匹配返回 pos,不匹配返回 -1
③ 文件解析 get_fil 调用 getin_read(懒初始化),在 targetlistget_findkey(i_tab=2) 查找;找不到则试 TARGET__NNN 下标形式
④ 写表 get_wdb 新建 key_tab(nb_keys+1) 条目,在对应 typed memory 池中分配并写入值
⑤ 读表 get_rdb 校验 keytype 匹配,从 typed memory 池复制值;压缩字段则单值填充整个数组

懒初始化

getin_readSAVE 变量 allread 守卫,仅在首次调用时执行:分配文本、key 表和四组 typed memory → 读取 run.def 及所有 INCLUDEDEF 文件 → getin_checkcoheallread=1。后续调用直接返回。

keystatus 语义

get_fil 根据解析结果设置 status

keystatus 含义 设置条件
1 值来自 .def 文件 所有请求的元素都在 targetlist 中找到,且非 "def"/"default"
2 使用调用方默认值 所有元素都未在文件中找到,或值为 "def"/"default"
3 部分默认 部分元素找到、部分使用默认值(仅向量 key)

keystatus=2 时打印 "USING DEFAULT BEHAVIOUR FOR <target>"。部分默认时逐个打印 "USING DEFAULTS : <target>__<n> = <value>",超过 max_msgs=15 条后缩写为 "USING DEFAULTS ..."

值解码

get_filget_qtyp 返回的类型解码:

类型 解码方式
INTEGER READ(str, FMT='(I< len >)')
REAL READ(str, FMT=*)
CHARACTER 直接截取 str(1:len_str)
LOGICAL 搜索 "tru"/"y".TRUE."fal"/"n".FALSE.

IOSTAT 非零时调用 ipslerr(3,...) fatal 终止。

压缩字段展开

compline(pos) > 0 时,get_fil 将解析到的单值 i_cmpval/r_cmpval 填充到所有 found(it) == .FALSE. 的位置。get_rdb 中压缩字段也使用单值填充:i_val(1:size_of_in) = i_mem(key_tab(pos)%keymemstart)

get_wdb 在写入时判断压缩条件:MINVAL(val) == MAXVAL(val)size_of_in > compress_lim=20,满足则 keycompress = size_of_inkeymemlen = 1

向量 key 索引格式

向量 key 使用 TARGET__NNNNN 格式,NNNNN 为 5 位零填充整数(c_i_fmt = '(I5.5)')。get_fil 查找时先尝试 target 原名,失败后尝试 target//__NNNit 从 1 到 nb_to_ret)。

l_n=30 限制 key 名最长 30 字符。向量 key 的基名部分截断至 l_n - n_d_fmt - 2 = 30 - 5 - 2 = 23 字符,为 __NNNNN 后缀预留空间。

并行 wrapper(getin_p

ioipsl_getin_p_mod.F90getin 包装为并行安全版本。每个 specific procedure 遵循:

!$OMP BARRIER
IF (is_master) THEN
  CALL getin(VarIn, VarOut)
ENDIF
CALL bcast(VarOut)
specific procedure 类型 维度 bcast 方式
getincs_p CHARACTER scalar bcast(VarOut)
getinc1d_p CHARACTER 1D 逐元素 bcast(VarOut(i))
getinis_p INTEGER scalar bcast(VarOut)
getini1d_p INTEGER 1D bcast(VarOut)
getini2d_p INTEGER 2D bcast(VarOut)
getinrs_p REAL scalar bcast(VarOut)
getinr1d_p REAL 1D bcast(VarOut)
getinr2d_p REAL 2D bcast(VarOut)
getinls_p LOGICAL scalar bcast(VarOut)
getinl1d_p LOGICAL 1D bcast(VarOut)
getinl2d_p LOGICAL 2D bcast(VarOut)

character 1D 逐元素广播是因为 Fortran CHARACTER(LEN=*) 数组在内存中不连续,不能直接整体 bcast。

bcast 来自 mod_phys_lmdz_transfert_parais_master 来自 mod_phys_lmdz_para。因此 getin_p 的前提是物理侧并行基础设施已初始化。

Mars 无 IOIPSL 条件经过

Mars 编译不定义 CPP_IOIPSL 时:

  1. ioipsl_getin_p_mod.F90 使用 USE ioipsl_getincom, ONLY: getin
  2. 物理配置(phymars/conf_phys.F 等)调用 getin_p
  3. master 线程触发 getinget_findkey(1) 查 key_tab → 首次 miss → getin_read 懒初始化(读 run.def + INCLUDEDEF 文件) → get_fil 解析 → get_wdb 写表 → bcast
  4. 后续同 key 调用命中 key_tab → get_rdb 直接读 typed memory → bcast

Mars 动力侧(conf_gcm.F90)可能使用 IOIPSL getinioipsl_getincom getin,取决于动力编译路径,但 fallback 行为一致。

关键常量汇总

常量 含义
max_files 100 最大文件数
l_n 30 key 名最大长度
i_txtslab 1000 文本 slab 初始大小
memslabs 200 key_tab 和 typed memory slab 大小
compress_lim 20 压缩阈值(元素数超过此值才允许压缩)
n_d_fmt 5 向量下标格式宽度
max_msgs 15 默认值打印上限

相关页面