mod_phys_lmdz_mpi_transfert.F90
源码基线:LMDZ.COMMON-6.3\LMDZ.COMMON。 Mars 运行参与度:条件经过。Mars physics 通过上层 mod_phys_lmdz_transfert_para 泛型进入本模块;MPI 构建且物理侧 MPI 分布启用时执行真实 MPI 通信。
源码范围
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\mod_phys_lmdz_mpi_transfert.F90
当前文件有 1953 个物理行,END MODULE mod_phys_lmdz_mpi_transfert 在第 1952 行,末尾还有一个空行。模块只定义接口和过程,不保存模块级状态;所有通信状态来自 mod_phys_lmdz_mpi_data,网格规模和 1D/2D 转换规模来自 mod_grid_phy_lmdz。
模块职责
mod_phys_lmdz_mpi_transfert 是物理侧 MPI rank 间 transfer 的底层实现。它提供:
- root 到所有 rank 的广播:
bcast_mpi - 全局 1D physical-column 字段到各 rank 局部字段:
scatter_mpi - 各 rank 局部字段到全局 1D physical-column 字段:
gather_mpi - 全局 lon-lat 2D 字段和局部 physical-column 字段之间的组合 scatter/gather:
scatter2D_mpi、gather2D_mpi - rank 间求和:
reduce_sum_mpi - 本 rank 局部 1D physical-column 字段和局部 2D lon-lat 块之间的转换:
grid1dTo2d_mpi、grid2dTo1d_mpi
本模块不直接处理 OpenMP 线程内数据分发。完整 MPI+OMP 顺序由 mod_phys_lmdz_transfert_para 组合。
接口总览
| 泛型接口 | 行号 | specific procedures | 类型覆盖 | 最大数组 rank | 底层动作 |
|---|---|---|---|---|---|
bcast_mpi |
7-12 | 22 | character scalar;integer/real/logical scalar 到 6D |
6D | MPI_BCAST |
scatter_mpi |
14-18 | 12 | integer/real/logical scalar 到 4D |
4D | root 打包 + MPI_SCATTERV |
gather_mpi |
21-25 | 12 | integer/real/logical scalar 到 4D |
4D | MPI_GATHERV + root 解包 |
scatter2D_mpi |
27-31 | 12 | integer/real/logical,input 比 output 多一个 lon-lat 维 |
5D -> 4D | grid2dTo1d_glo + scatter_mpi |
gather2D_mpi |
33-37 | 12 | integer/real/logical,output 比 input 多一个 lon-lat 维 |
4D -> 5D | gather_mpi + grid1dTo2d_glo |
reduce_sum_mpi |
39-42 | 10 | integer/real scalar 到 4D |
4D | MPI_REDUCE(..., MPI_SUM, ...) |
grid1dTo2d_mpi |
44-48 | 12 | integer/real/logical,1D local columns 到局部 2D block |
4D -> 5D | 本地复制和极点展开 |
grid2dTo1d_mpi |
50-54 | 12 | integer/real/logical,局部 2D block 到 1D local columns |
5D -> 4D | 本地复制和北极首点提取 |
全文件共 94 个 specific procedures,另有 18 个 _gen 引擎过程:
- 4 个
bcast_mpi_*gen - 3 个
scatter_mpi_*gen - 3 个
gather_mpi_*gen - 2 个
reduce_sum_mpi_*gen - 3 个
grid1dTo2d_mpi_*gen - 3 个
grid2dTo1d_mpi_*gen
类型和维度边界
character只支持bcast_mpi_c,使用len(Var1)作为广播长度。logical没有reduce_sum_mpi,因为MPI_SUM对逻辑值没有物理含义。bcast_mpi_i6/r6/l6是 6D 数组广播,不是 7D。reduce_sum_mpi_i4/r4是 4D 数组求和。grid1dTo2d_mpi_*3的输入是(:,:,:,:),输出是(:,:,:,:,:);grid2dTo1d_mpi_*3正好相反。
bcast_mpi
标量 integer/real/logical 先包装为长度 1 的数组,再进入对应 _gen 引擎:
IF (is_mpi_root) var_tmp(1)=var
CALL bcast_mpi_igen(Var_tmp,1)
var=var_tmp(1)数组版本直接把 Var 和 SIZE(Var) 传给引擎。引擎统一模式如下:
IF (.not.is_using_mpi) RETURN
CALL MPI_BCAST(Var, nb, MPI_*, mpi_master, COMM_LMDZ_PHY, ierr)real 版本使用 MPI_REAL_LMDZ,而不是硬编码 MPI_REAL;该 datatype 在 mod_phys_lmdz_mpi_data 初始化。
scatter_mpi
scatter_mpi_* 从 root rank 上的全局 1D physical-column 字段 VarIn(klon_glo, dimsize) 分发到各 rank 的 VarOut(klon_mpi, dimsize)。
specific procedure 根据输出额外维度计算 dimsize:
| 后缀 | dimsize |
|---|---|
| 无后缀 | 1 |
*1 |
SIZE(VarOut,2) |
*2 |
SIZE(VarOut,2)*SIZE(VarOut,3) |
*3 |
SIZE(VarOut,2)*SIZE(VarOut,3)*SIZE(VarOut,4) |
引擎逻辑:
- 若
is_using_mpi为 false,直接VarOut(:,:)=VarIn(:,:)并返回。 - root rank 为每个 rank 计算
counts(rank)=klon_mpi_para_nb(rank)*dimsize和displs(rank)。 - root 将
VarIn(klon_mpi_para_begin(rank):klon_mpi_para_end(rank), i)按 rank 顺序打包到一维VarTmp(dimsize*klon_glo)。 - 调用
MPI_SCATTERV,每个 rank 接收klon_mpi*dimsize个元素。
关键点是 SCATTERV 的块长由每个 rank 的物理列数决定,不是等长分配。
gather_mpi
gather_mpi_* 与 scatter 方向相反:各 rank 提供 VarIn(klon_mpi, dimsize),root 得到 VarOut(klon_glo, dimsize)。
specific procedure 根据输入额外维度计算 dimsize:
| 后缀 | dimsize |
|---|---|
| 无后缀 | 1 |
*1 |
SIZE(VarIn,2) |
*2 |
SIZE(VarIn,2)*SIZE(VarIn,3) |
*3 |
SIZE(VarIn,2)*SIZE(VarIn,3)*SIZE(VarIn,4) |
引擎逻辑:
- root 计算
counts/displs。 - 非 MPI 路径直接
VarOut(:,:)=VarIn(:,:)。 - 调用
MPI_GATHERV(VarIn, klon_mpi*dimsize, ..., VarTmp, counts, displs, ...)。 - root 按
klon_mpi_para_begin/end将VarTmp解包回全局列顺序。
scatter2D_mpi 和 gather2D_mpi
这两个接口本身不直接调用 MPI 原语,而是组合全局 1D/2D 网格转换和普通 scatter/gather。
scatter2D_mpi:
VarIn(global 2D lon-lat, extra dims)
-> grid2dTo1d_glo
Var_tmp(klon_glo, extra dims)
-> scatter_mpi
VarOut(klon_mpi, extra dims)
gather2D_mpi:
VarIn(klon_mpi, extra dims)
-> gather_mpi
Var_tmp(klon_glo, extra dims)
-> grid1dTo2d_glo
VarOut(global 2D lon-lat, extra dims)
这里使用的是 mod_grid_phy_lmdz 的 _glo 转换,处理全局 klon_glo 和全局 lon-lat 网格;不要和本文件后半段的 _mpi 局部转换混淆。
reduce_sum_mpi
reduce_sum_mpi_i/r 标量版本同样先包装为长度 1 的数组。数组版本用 SIZE(VarIn) 作为 nb,把多维数组视为连续内存。
引擎逻辑:
IF (.not.is_using_mpi) THEN
VarOut(:)=VarIn(:)
RETURN
ENDIF
CALL MPI_REDUCE(VarIn, VarOut, nb, MPI_*, MPI_SUM, mpi_master, COMM_LMDZ_PHY, ierr)输出只在 root rank 上有效。若后续所有 ranks 都需要结果,上层调用必须再执行 broadcast;本模块的 reduce 引擎不会自动广播结果。
局部 1D/2D 转换
grid1dTo2d_mpi 和 grid2dTo1d_mpi 是本 rank 内的局部网格转换,不做 MPI 调用,也不检查 is_using_mpi。
grid1dTo2d_mpi_*gen
输入:VarIn(klon_mpi, dimsize)。 输出:VarOut(nbp_lon*jj_nb, dimsize)。
流程:
- 先把北极行和南极行清零;logical 版本置
.FALSE.。 - 默认
offset=ii_begin;若本 rank 拥有北极,改为offset=nbp_lon,跳过 2D 北极复制行。 - 主体循环将
VarIn(ij,i)写到VarOut(ij+offset-1,i)。 - 若
is_north_pole_dyn,把VarIn(1,i)复制到北极所有经度。 - 若
is_south_pole_dyn,把VarIn(klon_mpi,i)复制到南极所有经度。
grid2dTo1d_mpi_*gen
输入:VarIn(nbp_lon*jj_nb, dimsize)。 输出:VarOut(klon_mpi, dimsize)。
流程:
- 默认
offset=ii_begin;若本 rank 拥有北极,改为offset=nbp_lon。 - 主体循环从
VarIn(ij+offset-1,i)读回VarOut(ij,i)。 - 若
is_north_pole_dyn,额外令VarOut(1,i)=VarIn(1,i)。
南极没有单独覆盖步骤,因为它在主体循环中已经按当前 rank 的局部范围读回。
编译和运行守卫
MPI 引擎都使用两层守卫:
- 编译期:
#ifdef CPP_MPI才INCLUDE 'mpif.h'并编译 MPI 调用。 - 运行期:
IF (.not.is_using_mpi)返回或退化为直接复制。
因此非 MPI 构建下,本模块仍可被上层编译依赖引用;真正行为由 is_using_mpi 和上层 wrapper 控制。
依赖状态
| 来源 | 符号 | 用途 |
|---|---|---|
mod_phys_lmdz_mpi_data |
is_using_mpi, is_mpi_root, mpi_master, mpi_size |
决定是否调用 MPI、root rank 和 rank 数。 |
mod_phys_lmdz_mpi_data |
COMM_LMDZ_PHY, MPI_REAL_LMDZ |
MPI communicator 和 real datatype。 |
mod_phys_lmdz_mpi_data |
klon_mpi, klon_mpi_para_nb/begin/end |
当前 rank 和所有 ranks 的 physical-column 分布。 |
mod_phys_lmdz_mpi_data |
ii_begin, jj_nb, is_north_pole_dyn, is_south_pole_dyn |
局部 1D/2D 转换的偏移和极点处理。 |
mod_grid_phy_lmdz |
klon_glo, nbp_lon |
全局列数、经度数和全局 1D/2D 转换规模。 |
复现要点
- 初始化
mod_phys_lmdz_mpi_data后再调用本模块,否则klon_mpi、分布表和 communicator 未定义。 - scatter/gather 的
dimsize来自非物理列维度;物理列维必须始终是第一维。 - 需要全局 lon-lat 2D 字段时使用
scatter2D_mpi/gather2D_mpi,而不是手工调用grid*_glo后再调用 scatter/gather,除非你明确要拆开调试。 grid1dTo2d_mpi/grid2dTo1d_mpi是局部块转换;它们不产生全局场。- reduce 只在 root rank 上给出有效结果;若所有 ranks 后续都读结果,上层要配合
bcast_mpi或bcast。
待确认
- 单进程但
CPP_MPI定义时,is_using_mpi仍可能为 true;实际是否走 MPI 调用取决于init_phys_lmdz_mpi_data的初始化路径和 communicator。当前页面只按源码控制流说明。 - 自动数组
VarTmp(dimsize*klon_glo)的栈空间需求取决于平台和编译选项,源码未提供显式保护。