write_field 调试写场接口
源码基线:LMDZ.COMMON-6.3\LMDZ.COMMON。 Mars 运行参与度:条件经过。write_field 是旧式调试 dump 工具,不是 Mars 标准 history/diagfi 主输出;但 Mars/COMMON 动力、guide、并行动力和物理侧调试路径可通过它把中间场写到独立 .nc 或 .field 文件。
源码范围
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\misc\write_field.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\write_field_phy.F90
关联边界:
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dyn3dpar\write_field_p.F90
dyn3dpar/write_field_p.F90 不在本页范围内,但它是并行动力场写入 misc/write_field 的直接 wrapper,本页在并行边界中记录其行为。
模块和符号
| 文件 | 行数 | 模块 | 主要符号 | 作用 |
|---|---|---|---|---|
misc/write_field.F90 |
326 | write_field |
generic WriteField, GetFieldIndex, WriteField_gen, CreateNewField, write_field1D/2D/3D, int2str |
旧式字段 dump 底座,支持 NetCDF .nc 和格式化文本 .field 两套路径。 |
phy_common/write_field_phy.F90 |
37 | write_field_phy |
WriteField_phy |
将物理列局地数组 gather 到全局物理网格,再转成 (nbp_lon, nbp_lat, ll) 并调用 WriteField。 |
dyn3dpar/write_field_p.F90 |
73 | write_field_p |
generic WriteField_p, write_field1D_p/2D_p/3D_p |
并行动力 wrapper:Gather_Field 后只在 MPI_Rank==0 调 WriteField。 |
write_field 模块状态
write_field.F90 维护一个最多 100 个字段的本地表:
| 状态 | 类型 | 含义 |
|---|---|---|
MaxWriteField |
integer parameter = 100 | 最多登记字段数。 |
FieldId(MaxWriteField) |
integer, save | 每个字段对应的 NetCDF file id。 |
FieldVarId(MaxWriteField) |
integer, save | 每个字段对应的 NetCDF var id。 |
FieldIndex(MaxWriteField) |
integer, save | 当前字段已写出的 iter 下标。 |
FieldName(MaxWriteField) |
character(len=255) | 字段名表。 |
NbField |
integer, save = 0 | 已登记字段数。 |
源码没有显式检查 NbField > MaxWriteField,也没有检查 NetCDF status 返回值;调试复现时若文件没生成或字段过多,不能期待它像正式 I/O wrapper 那样报错。
generic WriteField 的 NetCDF 路径
WriteField 是一个 generic interface,按输入数组 rank 分派到:
| 分派例程 | 输入 | 维度传给 WriteField_gen |
|---|---|---|
WriteField3d(name, Field(:,:,:)) |
3D real | (dimx, dimy, dimz) |
WriteField2d(name, Field(:,:)) |
2D real | (dimx, dimy, 1) |
WriteField1d(name, Field(:)) |
1D real | (dimx, 1, 1) |
共同流程:
WriteField(...)
-> shape(Field)
-> WriteField_gen(name, Field, dimx, dimy, dimz)
-> GetFieldIndex(name)
-> if new name: CreateNewField(name, dimx, dimy, dimz)
-> else: FieldIndex(index) += 1
-> NF_PUT_VARA_DOUBLE(file_id, var_id, start=(1,1,1,iter), count=(dimx,dimy,dimz,1), Field)
-> NF_SYNC(file_id)
首次写某个 name 时,CreateNewField 会创建 name.nc,定义 4 个维度:
| 维度 | 长度 |
|---|---|
X |
dimx |
Y |
dimy |
Z |
dimz |
iter |
NF_UNLIMITED |
变量名使用字段名本身,类型为 NF_DOUBLE,维度顺序为 (X,Y,Z,iter)。后续同名写入不会重新定义文件,而是把 iter 下标加 1 后追加一帧。
格式化 .field 路径
同一模块还定义了带下划线的 write_field1D, write_field2D, write_field3D。这组例程不是 generic WriteField 的分派目标,而是单独公开的格式化文本 dump:
| 例程 | 输出文件 | 格式 |
|---|---|---|
write_field1D(name, Field(:)) |
name.field |
每项写 i ---> value,每 4 列换行。 |
write_field2D(name, Field(:,:)) |
name.field |
每项写 (j,i) ---> value,纬向循环外加分隔线。 |
write_field3D(name, Field(:,:,:)) |
name.field |
每项写 (k,j,i) ---> value,逐层/逐行分隔。 |
这组文本输出每次 status='replace' 打开文件,会覆盖旧文件;NetCDF generic WriteField 则按字段名维持 iter 无限维追加。
WriteField_phy 物理列桥接
phy_common/write_field_phy.F90 定义:
SUBROUTINE WriteField_phy(name, Field, ll)
Field(klon_omp, ll)
流程:
Gather(Field, New_Field)把本地 OMP/MPI 物理列收集到New_Field(klon_glo,ll)。- 进入
!$OMP MASTER区。 - 若
is_mpi_root,调用Grid1Dto2D_glo(New_Field, Field_2D),把全局 1D 物理列转成Field_2d(nbp_lon,nbp_lat,ll)。 - root 调用
WriteField(name, Field_2d),走misc/write_field.F90的 NetCDF dump 路径。
它依赖:
| use 模块 | 符号 | 作用 |
|---|---|---|
mod_phys_lmdz_para |
klon_omp, is_mpi_root, Gather |
物理侧并行列数、root 判定和 gather。 |
mod_grid_phy_lmdz |
nbp_lon, nbp_lat, klon_glo, Grid1Dto2D_glo |
全局物理网格尺寸和 1D 到 2D 映射。 |
write_field |
WriteField |
写出最终 NetCDF dump。 |
WriteField_phy 不做 XIOS context、field 或正式诊断注册;它只是把物理列调试场转成全局 lon-lat 后交给旧式 dump 底座。
并行动力 wrapper 边界
dyn3dpar/write_field_p.F90 定义 generic WriteField_p。1D/2D/3D 版本都遵循:
copy local Field into New_Field
Gather_Field(New_Field, horizontal_size, vertical_size, 0)
if MPI_Rank == 0:
WriteField(name, New_Field)
这解释了并行动力中 WriteField_p 的行为:它先把分布式动力数组 gather 到 root,再复用串行 WriteField。因此并行 debug dump 的输出文件也只由 rank 0 写,文件名仍是 name.nc。
调用关系和 Mars 边界
源码搜索显示几类使用方式:
dyn3d/guide_mod.F90和dyn3dpar/guide_p_mod.F90有活动CALL writefield(...)/CALL writefield_p(...),用于 guide/nudging 调试输出。dyn3d/leapfrog.F、MARSphymars/leapfrog_nogcm.F、多个并行动力算子中有许多注释掉的WriteField/WriteField_p调试调用。dyn3dpar/caldyn_p.F中存在活动WriteField_p调用,会输出多个动力中间量;是否在生产构建中保留这些调用,需要结合编译配置和运行实践确认。phy_common/write_field_phy.F90提供物理列桥接,但 Mars 标准 physics 诊断通常仍应优先看diagfi/stats、history 或 XIOS 页面。
所以本页是“调试写场”页,不应替代正式输出链:history-xios-getin-boundary、restart-io 和 XIOS/write_field 输出封装组页。
复现检查表
| 目标 | 检查 |
|---|---|
想得到 name.nc |
调用 generic WriteField(name, array) 或 WriteField_p/WriteField_phy;首次调用会创建 name.nc,后续同名追加 iter。 |
| 想得到可读文本 | 调用带下划线的 write_field1D/2D/3D;输出 name.field,每次覆盖。 |
| 并行动力 dump 缺文件 | 确认是否在 MPI_Rank==0,以及 Gather_Field 前并行分布是否匹配当前数组。 |
| 物理列 dump 形状异常 | 确认 klon_omp, klon_glo, nbp_lon, nbp_lat 和 Grid1Dto2D_glo 已初始化。 |
| NetCDF 文件损坏或无变量 | 源码不检查 NF_* status,需外部用 NetCDF 工具验证。 |
| 字段超过 100 个 | MaxWriteField=100,源码未见越界保护。 |
待确认
dyn3dpar/caldyn_p.F中活动WriteField_p调用是否属于当前 Mars 生产构建保留路径,需要结合实际编译选项和运行日志确认。WriteField_gen使用NF_PUT_VARA_DOUBLE写 Fortranreal数组,实际精度和 NetCDF 库接口绑定需运行或编译器接口确认。.field文本路径中部分局部 allocatable 变量未使用;本页按无副作用处理。