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 版本
#endifMars 编译通常不定义 CPP_IOIPSL,因此 getin_p 的每个 specific procedure 在 master 线程上调用的是本页描述的 ioipsl_getincom:getin。动力侧 conf_gcm.F90 直接 USE ioipsl_getincom 或 IOIPSL getin,取决于动力编译选项。
文件来源
初始文件列表
getin_read 将 filelist(1) 硬编码为 'run.def',nbfiles=1。filelist 是 CHARACTER(LEN=100) 的 SAVE 数组,容量 max_files=100。
INCLUDEDEF 机制
当 getin_readdef 遇到 INCLUDEDEF 关键字时,getin_decrypt 将该行所有空格分隔的文件名追加到 filelist。getin_read 的 DO WHILE (current <= nbfiles) 循环会随之读取新增文件,形成深度优先的包含链。
run.def
INCLUDEDEF phys_mars.def watercloud.def
→ filelist(2) = 'phys_mars.def'
→ filelist(3) = 'watercloud.def'
文件打开与缺失处理
getin_readdef 用 OPEN(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_readdef 用 INDEX(READ_str, '=') 分割 key 和 value:
- key 提取:
key_str = TRIM(ADJUSTL(READ_str(1:ptn-1)))。 __NNN向量下标规整:若 key 含__,将下标部分重格式化为c_i_fmt = '(I5.5)'(5 位零填充),确保key__1和key__00001匹配。- 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 时:
- 打印两个值的来源和文本。
- 将先出现的
targetlist(line)置为空白(' '),保留后出现的值。 - 打印
"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=200 个 t_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(懒初始化),在 targetlist 中 get_findkey(i_tab=2) 查找;找不到则试 TARGET__NNN 下标形式 |
| ④ 写表 | get_wdb |
新建 key_tab(nb_keys+1) 条目,在对应 typed memory 池中分配并写入值 |
| ⑤ 读表 | get_rdb |
校验 keytype 匹配,从 typed memory 池复制值;压缩字段则单值填充整个数组 |
懒初始化
getin_read 由 SAVE 变量 allread 守卫,仅在首次调用时执行:分配文本、key 表和四组 typed memory → 读取 run.def 及所有 INCLUDEDEF 文件 → getin_checkcohe → allread=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_fil 按 get_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_in、keymemlen = 1。
向量 key 索引格式
向量 key 使用 TARGET__NNNNN 格式,NNNNN 为 5 位零填充整数(c_i_fmt = '(I5.5)')。get_fil 查找时先尝试 target 原名,失败后尝试 target//__NNN(it 从 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.F90 将 getin 包装为并行安全版本。每个 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_para,is_master 来自 mod_phys_lmdz_para。因此 getin_p 的前提是物理侧并行基础设施已初始化。
Mars 无 IOIPSL 条件经过
Mars 编译不定义 CPP_IOIPSL 时:
ioipsl_getin_p_mod.F90使用USE ioipsl_getincom, ONLY: getin。- 物理配置(
phymars/conf_phys.F等)调用getin_p。 - master 线程触发
getin→get_findkey(1)查 key_tab → 首次 miss →getin_read懒初始化(读run.def+ INCLUDEDEF 文件) →get_fil解析 →get_wdb写表 →bcast。 - 后续同 key 调用命中 key_tab →
get_rdb直接读 typed memory →bcast。
Mars 动力侧(conf_gcm.F90)可能使用 IOIPSL getin 或 ioipsl_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 | 默认值打印上限 |