mod_phys_lmdz_mpi_transfert MPI transfer 核心
输入范围
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\mod_phys_lmdz_mpi_transfert.F90
本页范围
本页展开 mod_phys_lmdz_mpi_transfert.F90(1953 个物理行,END MODULE 在第 1952 行)的全部 8 个泛型接口、94 个 specific procedures 和 18 个内部引擎子程序。mpi-data-transfer 是组页概览;phys-transfer-para-wrapper 覆盖上层合并层;本页专注 MPI 底层实现细节。
模块结构
MODULE mod_phys_lmdz_mpi_transfert
CONTAINS
! 94 specific procedures + 18 _gen engine subroutines
END MODULE模块不 USE 其他 transfer 模块。每个 specific procedure 按需 USE mod_phys_lmdz_mpi_data(is_mpi_root/is_using_mpi/mpi_master/COMM_LMDZ_PHY 等)或 USE mod_grid_phy_lmdz(klon_glo/klon_mpi/nbp_lon/jj_nb/ii_begin/is_north_pole_dyn/is_south_pole_dyn)。
8 个泛型接口总览
| 接口 | 过程数 | 类型 | 最大维度 | MPI 调用 | 引擎 |
|---|---|---|---|---|---|
bcast_mpi |
22 | c, i/i1-i6, r/r1-r6, l/l1-l6 | 6D | MPI_BCAST |
cgen/igen/rgen/lgen |
scatter_mpi |
12 | i/i1-i3, r/r1-r3, l/l1-l3 | 4D | MPI_SCATTERV |
igen/rgen/lgen |
gather_mpi |
12 | i/i1-i3, r/r1-r3, l/l1-l3 | 4D | MPI_GATHERV |
igen/rgen/lgen |
scatter2D_mpi |
12 | i/i1-i3, r/r1-r3, l/l1-l3 | 5D→4D | 组合 | — |
gather2D_mpi |
12 | i/i1-i3, r/r1-r3, l/l1-l3 | 4D→5D | 组合 | — |
reduce_sum_mpi |
10 | i/i1-i4, r/r1-r4 | 4D | MPI_REDUCE |
igen/rgen |
grid1dTo2d_mpi |
12 | i/i1-i3, r/r1-r3, l/l1-l3 | 5D | 无(纯本地) | igen/rgen/lgen |
grid2dTo1d_mpi |
12 | i/i1-i3, r/r1-r3, l/l1-l3 | 5D | 无(纯本地) | igen/rgen/lgen |
总计 94 个 specific procedures + 18 个 _gen 引擎子程序。
类型覆盖差异
- character:仅
bcast_mpi_c(1 个),无 scatter/gather/reduce。 - logical:无
reduce_sum_mpi(MPI 不提供MPI_SUM对MPI_LOGICAL的定义)。 - integer/real:覆盖全部 8 个接口。
维度后缀约定
_i = scalar integer,_i1 = 1D,_i2 = 2D,_i3 = 3D。bcast 额外支持 _i4/_i5/_i6(到 6D)。reduce_sum 额外支持 _i4(到 4D)。_c 仅 character scalar。
bcast_mpi 实现
标量包装模式
标量过程(bcast_mpi_i/bcast_mpi_r/bcast_mpi_l)将标量值复制到 var_tmp(1) 后调用引擎,广播后再复制回来:
SUBROUTINE bcast_mpi_i(var)
USE mod_phys_lmdz_mpi_data, ONLY : is_mpi_root
INTEGER,INTENT(INOUT) :: Var
INTEGER :: var_tmp(1)
IF (is_mpi_root) var_tmp(1)=var
CALL bcast_mpi_igen(Var_tmp,1)
var=var_tmp(1)
END SUBROUTINE数组过程(bcast_mpi_i1~bcast_mpi_i6)直接传 Var 和 size(Var) 给引擎,利用 Fortran 数组内存连续性。
引擎子程序
SUBROUTINE bcast_mpi_igen(var,nb)
USE mod_phys_lmdz_mpi_data
#ifdef CPP_MPI
INCLUDE 'mpif.h'
#endif
IF (.not.is_using_mpi) RETURN
#ifdef CPP_MPI
CALL MPI_BCAST(Var,nb,MPI_INTEGER,mpi_master,COMM_LMDZ_PHY,ierr)
#endif
END SUBROUTINE| 引擎 | MPI datatype | 额外说明 |
|---|---|---|
bcast_mpi_cgen |
MPI_CHARACTER |
参数 nb=len(Var1) |
bcast_mpi_igen |
MPI_INTEGER |
— |
bcast_mpi_rgen |
MPI_REAL_LMDZ |
自定义类型,初始化为 MPI_REAL8 |
bcast_mpi_lgen |
MPI_LOGICAL |
— |
所有引擎:root = mpi_master,comm = COMM_LMDZ_PHY,IF (.not.is_using_mpi) RETURN 快速跳过。
scatter_mpi 实现
引擎结构
scatter 引擎(scatter_mpi_igen/rgen/lgen)三步:
- root 打包:
IF (is_mpi_root)遍历所有 rank,用klon_mpi_para_nb/begin/end从VarIn(klon_glo,dimsize)抽取连续块到VarTmp(dimsize*klon_glo),同时计算counts(rank)=nb*dimsize和displs(rank)=Index-1。 - MPI 调用:
MPI_SCATTERV(VarTmp,counts,displs,MPI_*,VarOut,klon_mpi*dimsize,MPI_*,mpi_master,COMM_LMDZ_PHY,ierr)。 - 非 MPI 退化:
IF (.not.is_using_mpi)在打包前直接VarOut(:,:)=VarIn(:,:)+RETURN。
dimsize 计算
specific procedure 将额外维度展平为 dimsize:
| 后缀 | dimsize |
|---|---|
_i(scalar) |
1 |
_i1(1D extra) |
Size(VarOut,2) |
_i2(2D extra) |
Size(VarOut,2)*Size(VarOut,3) |
_i3(3D extra) |
Size(VarOut,2)*Size(VarOut,3)*Size(VarOut,4) |
scatter 从 VarOut 额外维度取 dimsize(因为 root 端 VarIn 有 klon_glo 首维,各 rank 端 VarOut 有 klon_mpi 首维)。
VarTmp 临时数组
VarTmp 声明为 DIMENSION(dimsize*klon_glo),是栈上自动数组。打包循环按 i=1..dimsize 外层、rank 内层的方式,将全局列连续拷贝到按 rank 排列的 buffer 中。
gather_mpi 实现
引擎结构
gather 引擎(gather_mpi_igen/rgen/lgen)三步,顺序与 scatter 不同:
- root 计算 displs/counts:
IF (is_mpi_root)预计算counts/displs(所有 rank 都参与 GATHERV,但只有 root 的 displs/counts 有意义)。 - 非 MPI 退化:
IF (.not.is_using_mpi)→VarOut(:,:)=VarIn(:,:)+RETURN。注意gather_rgen/gather_lgen中此检查位于 displs 计算之后,与scatter_*gen和gather_igen不同(后者先检查非 MPI 再打包)。 - MPI 调用 + root 解包:
MPI_GATHERV(VarIn,klon_mpi*dimsize,MPI_*,VarTmp,counts,displs,MPI_*,mpi_master,COMM_LMDZ_PHY,ierr),root 再将VarTmp按klon_mpi_para_begin/end解包到VarOut(klon_glo,dimsize)。
dimsize 计算
gather 从 VarIn 额外维度取 dimsize(Size(VarIn,2) 等),与 scatter 取 VarOut 相反。这是因为 gather 方向是 local→global,VarIn 在各 rank 端有 klon_mpi 首维。
scatter2D_mpi / gather2D_mpi 组合
这两个接口不直接调用 MPI 原语,而是组合 grid 转换和 1D scatter/gather:
scatter2D_mpi 流程
VarIn(nbp_lon*jj_nb, ...) -- grid2dTo1d_glo --> Var_tmp(klon_glo, ...) -- scatter_mpi --> VarOut(klon_mpi, ...)
- 分配栈临时数组
Var_tmp(klon_glo, ...),额外维度从VarOut取。 - 调用
grid2dTo1d_glo(VarIn, Var_tmp)(mod_grid_phy_lmdz),将 2D 经纬网格全局字段转为 1D 列表示。 - 调用
scatter_mpi(Var_tmp, VarOut)分发到各 rank。
gather2D_mpi 流程
VarIn(klon_mpi, ...) -- gather_mpi --> Var_tmp(klon_glo, ...) -- grid1dTo2d_glo --> VarOut(nbp_lon*jj_nb, ...)
- 分配栈临时数组
Var_tmp(klon_glo, ...),额外维度从VarOut取(size(VarOut,3)等)。 - 调用
gather_mpi(VarIn, Var_tmp)收集到 root。 - 调用
grid1dTo2d_glo(Var_tmp, VarOut)将 1D 列转回 2D 经纬网格。
grid1dTo2d_glo/grid2dTo1d_glo 来自 mod_grid_phy_lmdz,操作的是全局列数 klon_glo 和全局 2D 网格 nbp_lon*nbp_lat(注意与 _mpi 版本的区别:_glo 用 nbp_lat,_mpi 用 jj_nb)。
reduce_sum_mpi 实现
标量包装
标量过程(reduce_sum_mpi_i/reduce_sum_mpi_r)使用 VarIn_tmp(1)/VarOut_tmp(1) 包装标量为数组:
SUBROUTINE reduce_sum_mpi_i(VarIn, VarOut)
INTEGER,INTENT(IN) :: VarIn
INTEGER,INTENT(OUT) :: VarOut
INTEGER :: VarIn_tmp(1), VarOut_tmp(1)
VarIn_tmp(1)=VarIn
CALL reduce_sum_mpi_igen(VarIn_tmp,VarOut_tmp,1)
VarOut=VarOut_tmp(1)
END SUBROUTINE引擎
SUBROUTINE reduce_sum_mpi_igen(VarIn,VarOut,nb)
IF (.not.is_using_mpi) THEN
VarOut(:)=VarIn(:)
RETURN
ENDIF
#ifdef CPP_MPI
CALL MPI_REDUCE(VarIn,VarOut,nb,MPI_INTEGER,MPI_SUM,mpi_master,COMM_LMDZ_PHY,ierr)
#endif
END SUBROUTINE- 操作:
MPI_SUM。 - root:
mpi_master,结果只在 root 有效。 - 仅 integer 和 real 两个引擎(
reduce_sum_mpi_igen/reduce_sum_mpi_rgen),无 logical。 - 数组过程使用
SIZE(VarIn)作为nb,将多维数组视为连续内存。 reduce_sum_mpi_*最大到 4D(_i4/_r4)。
grid1dTo2d_mpi / grid2dTo1d_mpi 实现
这两个接口是纯本地网格转换,不涉及 MPI 通信,没有 IF (.not.is_using_mpi) 守卫。
grid1dTo2d_mpi 引擎
将本 rank 的 1D 列数组 VarIn(klon_mpi,dimsize) 映射到局部 2D 块 VarOut(nbp_lon*jj_nb,dimsize):
- 极点行清零:
VarOut(1:nbp_lon,:)=0(北极行),VarOut(nbp_lon*(jj_nb-1)+1:nbp_lon*jj_nb,:)=0(南极行)。logical 引擎用.FALSE.。 - 计算偏移:
offset=ii_begin;若is_north_pole_dyn则Offset=nbp_lon(跳过北极复制行)。 - 主体复制:
VarOut(ij+offset-1,i)=VarIn(ij,i)。 - 北极处理:
IF (is_north_pole_dyn)→ 将VarIn(1,i)复制到VarOut(1:nbp_lon,i)(北极所有经度共享同一值)。 - 南极处理:
IF (is_south_pole_dyn)→ 将VarIn(klon_mpi,i)复制到VarOut(nbp_lon*(jj_nb-1)+1:nbp_lon*jj_nb,i)。
grid2dTo1d_mpi 引擎
反向操作,将局部 2D 块 VarIn(nbp_lon*jj_nb,dimsize) 提取为 1D 列 VarOut(klon_mpi,dimsize):
- 计算偏移:
offset=ii_begin;若is_north_pole_dyn则offset=nbp_lon。 - 主体提取:
VarOut(ij,i)=VarIn(ij+offset-1,i)。 - 北极处理:
IF (is_north_pole_dyn)→VarOut(1,i)=VarIn(1,i)(从 2D 北极行首元素读取)。
注意:grid2dTo1d_mpi 不处理南极——南极值已在主体提取中通过 offset 正确读取。
_mpi 与 _glo 版本对比
| 属性 | grid1dTo2d_mpi |
grid1dTo2d_glo(mod_grid_phy_lmdz) |
|---|---|---|
| 1D 首维 | klon_mpi(本 rank) |
klon_glo(全局) |
| 2D 首维 | nbp_lon*jj_nb(本 rank 局部) |
nbp_lon*nbp_lat(全局) |
| 偏移来源 | ii_begin + is_north_pole_dyn |
固定 nbp_lon |
| 极点条件 | 条件(is_north/south_pole_dyn) |
无条件(总是复制两极) |
| 用途 | scatter2D_mpi/gather2D_mpi 组合 | 全局字段↔︎1D 列转换 |
CPP_MPI 双重守卫
所有引擎子程序使用编译期 + 运行期双重守卫:
#ifdef CPP_MPI
INCLUDE 'mpif.h'
#endif
IF (.not.is_using_mpi) RETURN
#ifdef CPP_MPI
CALL MPI_BCAST(...)
#endif| 场景 | CPP_MPI 定义 |
is_using_mpi |
行为 |
|---|---|---|---|
| MPI 构建 + MPI 运行 | 是 | .TRUE. |
执行 MPI 调用 |
| MPI 构建 + 单进程 | 是 | .TRUE. |
执行 MPI 调用(mpi_size=1) |
| 非 MPI 构建 | 否 | .FALSE. |
所有引擎空操作或直拷 |
is_using_mpi 在 init_phys_lmdz_mpi_data 中根据 CPP_MPI 宏设置:定义时 .TRUE.,否则 .FALSE.。
MPI_REAL_LMDZ
INTEGER,SAVE :: MPI_REAL_LMDZ ! MPI_REAL8在 init_phys_lmdz_mpi_data 中初始化为 MPI_REAL8。所有 real 类型的 MPI 调用使用 MPI_REAL_LMDZ 而非直接 MPI_REAL 或 MPI_DOUBLE_PRECISION,提供精度抽象层。若需切换单/双精度,只需修改此处赋值。
依赖数据汇总
| 来源模块 | 符号 | 用途 |
|---|---|---|
mod_phys_lmdz_mpi_data |
is_using_mpi |
运行时 MPI 开关 |
mod_phys_lmdz_mpi_data |
is_mpi_root |
root 判断 |
mod_phys_lmdz_mpi_data |
mpi_master |
MPI root rank |
mod_phys_lmdz_mpi_data |
COMM_LMDZ_PHY |
物理侧 communicator |
mod_phys_lmdz_mpi_data |
MPI_REAL_LMDZ |
real datatype |
mod_phys_lmdz_mpi_data |
mpi_size |
进程数 |
mod_phys_lmdz_mpi_data |
klon_mpi_para_nb/begin/end |
各 rank 列分布表 |
mod_phys_lmdz_mpi_data |
klon_mpi |
本 rank 列数 |
mod_phys_lmdz_mpi_data |
ii_begin |
本 rank 经向起始 |
mod_phys_lmdz_mpi_data |
is_north_pole_dyn |
本 rank 拥有北极 |
mod_phys_lmdz_mpi_data |
is_south_pole_dyn |
本 rank 拥有南极 |
mod_grid_phy_lmdz |
klon_glo |
全局列数 |
mod_grid_phy_lmdz |
nbp_lon |
经度点数 |
mod_grid_phy_lmdz |
jj_nb |
本 rank 纬度行数 |
Mars 运行参与度
条件经过:Mars 物理侧使用并行 transfer 时经过。MPI 构建 + mpi_size>1 时执行 MPI 调用;
非 MPI 构建或单进程时由 .not.is_using_mpi 短路或走 _para 上层退化。
Mars 物理过程通过 phys-transfer-para-wrapper 的 bcast/scatter/gather 泛型间接调用本模块。直接调用 *_mpi 接口的场景主要出现在 _para wrapper 内部的 !$OMP MASTER 区域。