parallel physics grid distribution
输入范围:physics_distribution_mod.F90、mod_grid_phy_lmdz.F90、mod_phys_lmdz_para.F90,并确认 mod_phys_lmdz_mpi_data.F90、mod_phys_lmdz_omp_data.F90、inigeomphy_mod.F90、Mars iniphysiq_mod.F90 和 dimphy.F90 调用边界。 Mars 运行参与度:条件经过。Mars 3D 物理初始化从 iniphysiq_mod -> inigeomphy -> init_physics_distribution 进入本链;Mars 1D/testphys 路径也直接调用 init_physics_distribution(regular_lonlat,4,1,1,1,...) 来建立单列物理维度。
源码范围
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\physics_distribution_mod.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\mod_grid_phy_lmdz.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\mod_phys_lmdz_para.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\mod_phys_lmdz_mpi_data.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\mod_phys_lmdz_omp_data.F90
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\dynphy_lonlat\inigeomphy_mod.F90
LMDZ.MARS\libf\dynphy_lonlat\phymars\iniphysiq_mod.F90
LMDZ.MARS\libf\phymars\dimphy.F90
职责
本页说明物理列分布初始化的完整约定:上游把动力经纬网格规模、当前 MPI rank 的物理列数和 communicator 传入 COMMON;COMMON 先写全局物理网格状态,再生成 MPI rank 与 OpenMP thread 的列切片,最后把每个 OpenMP 线程看到的 klon/klev 写入 Mars physics 的 dimphy。
这条链不是字段传输本身。实际 scatter/gather/bcast/reduce 在 MPI data/transfer、OMP data/transfer 和 phys-transfer-para-wrapper 中实现;本页只解释这些传输例程依赖的分布表和局部维度如何建立。
入口和参数
physics_distribution_mod:init_physics_distribution 定义在 physics_distribution_mod.F90:9-31。
| 参数 | 方向 | 维度/类型 | 含义 |
|---|---|---|---|
grid_type |
in | integer | 物理网格类型;Mars lon-lat 路径传 regular_lonlat。 |
nvertex |
in | integer | cell 顶点数;regular lon-lat 路径传 4。 |
nbp |
in | integer | 当前 MPI rank 的物理列数。 |
nbp_lon |
in | integer | 全局 regular 网格经向点数。 |
nbp_lat |
in | integer | 全局 regular 网格纬向点数;Mars 3D 从 inigeomphy 传 jjm+1。 |
nbp_lev |
in | integer | 物理垂直层数。 |
communicator |
in | integer | 物理侧 MPI communicator,写入 COMM_LMDZ_PHY。 |
Mars 3D 入口在 iniphysiq_mod.F90:57-64 调用 inigeomphy。COMMON inigeomphy_mod.F90:82-87 再按两种形态调用本入口:
3D: init_physics_distribution(regular_lonlat, 4, nbp, iim, jjm+1, nlayer, communicator)
1D: init_physics_distribution(regular_lonlat, 4, 1, 1, 1, nlayer, communicator)
Mars phymars/dyn1d/init_testphys1d_mod.F90 也直接调用同一入口,并在有/无 MPI communicator 的两种分支下传 COMM_LMDZ 或 1。
总体调用链
Mars iniphysiq
-> COMMON inigeomphy
-> init_physics_distribution
-> init_grid_phy_lmdz
-> init_phys_lmdz_para
-> init_phys_lmdz_mpi_data
-> OMP parallel:
-> init_phys_lmdz_omp_data
-> klon_loc = klon_omp
-> is_master = is_mpi_root .and. is_omp_root
-> Test_transfert
-> OMP parallel:
-> init_dimphy(klon_omp, nbp_lev)
-> init_interface_dyn_phys
-> build global physical geometry arrays
-> OMP parallel:
local slice = klon_mpi_begin + klon_omp_begin/end
-> init_geometry(klon_omp, ...)
-> init_vertical_layers(...)
-> Mars OMP parallel:
phys_state_var_init(klon_omp, ...)
ini_fillgeom(klon_omp, ...)
conf_phys(klon_omp, ...)
init_grid_phy_lmdz 必须先于 init_phys_lmdz_para,因为 Test_transfert 会使用 mod_grid_phy_lmdz:klon_glo/nbp_lev 构造全局测试数组。
全局网格状态
mod_grid_phy_lmdz 的 init_grid_phy_lmdz 在 mod_grid_phy_lmdz.F90:39-59 保存:
| 状态 | 含义 |
|---|---|
grid_type |
regular_lonlat 或 unstructured。 |
nvertex |
每个 cell 顶点数。 |
nbp_lon, nbp_lat, nbp_lev |
全局经向、纬向、垂直层规模。 |
klon_glo |
全局物理列数,去掉两极重复经度。 |
klon_glo 的源码公式为:
if nbp_lon * nbp_lat == 1:
klon_glo = 1
else:
klon_glo = nbp_lon * nbp_lat - 2 * (nbp_lon - 1)
mod_phys_lmdz_mpi_data.F90:68-74 用等价形式重算局部变量:
1D: klon_glo = 1
3D: klon_glo = nbp_lon * (nbp_lat - 2) + 2
两者在 3D regular lon-lat 情况下相同:每个极点从 nbp_lon 个重复经度压缩为一个物理列。
MPI rank 级分布
mod_phys_lmdz_para:Init_phys_lmdz_para 在 mod_phys_lmdz_para.F90:19-41 先调用 Init_phys_lmdz_mpi_data(nbp,nbp_lon,nbp_lat,communicator)。
mod_phys_lmdz_mpi_data.F90:47-182 做以下事情:
- 根据
CPP_MPI设置is_using_mpi;无 MPI 时令mpi_size=1、mpi_rank=0。 - MPI 构建下用
MPI_COMM_SIZE/RANK读取COMM_LMDZ_PHY的 size/rank。 - 用
MPI_ALLGATHER(nbp,...)收集每个 rank 的物理列数;无 MPI 时distrib(:)=nbp。 klon_mpi_para_nb(:)=distrib(:)。- 由前缀和构造
klon_mpi_para_begin/end。 - 把 1D 物理列区间映射回二维 regular lon-lat 线性区间
ij_para_begin/end:- rank 0 从
ij=1开始,保留北极物理列。 - 其他 rank 用
klon_mpi_para_begin(i)+nbp_lon-1把去重后的物理列重新嵌入带极点重复经度的 2D 线性网格。
- rank 0 从
- 推导
ii_para_begin/end、jj_para_begin/end、ij_para_nb、jj_para_nb。 - 当前 rank 写入
ii_begin/end、jj_begin/end、ij_begin/end、klon_mpi_begin/end、klon_mpi。
极点所有权由 rank 位置决定:
| 标志 | 条件 | 含义 |
|---|---|---|
is_north_pole_dyn |
mpi_rank == 0 |
北极物理列在第一个 rank。 |
is_south_pole_dyn |
mpi_rank == mpi_size-1 |
南极物理列在最后一个 rank。 |
is_mpi_root |
mpi_rank == 0 |
MPI root,和 OMP root 合成 is_master。 |
这套表供 transfer、XIOS、debug 写场、calfis_p 周边逻辑和 geometry 切片使用。nbp 必须已经由上游动力/物理接口按当前 rank 算好,本模块不重新决定负载平衡。
OpenMP thread 级分布
Init_phys_lmdz_para 随后进入 OpenMP parallel 区,在每个线程调用 Init_phys_lmdz_omp_data(klon_mpi)。
mod_phys_lmdz_omp_data.F90:25-106 的分配规则是平均切分当前 rank 的 klon_mpi:
for thread i = 0 .. omp_size-1:
klon_omp_para_nb(i) = klon_mpi / omp_size
if i < mod(klon_mpi, omp_size):
klon_omp_para_nb(i) += 1
klon_omp_para_begin(0) = 1
klon_omp_para_end(0) = klon_omp_para_nb(0)
for i = 1 .. omp_size-1:
begin(i) = end(i-1) + 1
end(i) = begin(i) + nb(i) - 1
每个线程写入自己的 threadprivate 状态:
| 状态 | 来源 | 含义 |
|---|---|---|
omp_rank, omp_size |
OpenMP runtime 或串行 fallback | 当前线程编号和线程总数。 |
klon_omp |
klon_omp_para_nb(omp_rank) |
当前线程物理列数。 |
klon_omp_begin/end |
klon_omp_para_begin/end(omp_rank) |
当前线程在 rank 内的列区间。 |
is_omp_root |
omp_rank == 0 |
当前 rank 的 OMP root 线程。 |
is_north_pole_phy |
is_north_pole_dyn .and. omp_rank == 0 |
北极列由第一个 rank 的第一个线程拥有。 |
is_south_pole_phy |
is_south_pole_dyn .and. omp_rank == omp_size-1 |
南极列由最后一个 rank 的最后一个线程拥有。 |
mod_phys_lmdz_para 在同一 parallel 区内设置 klon_loc=klon_omp,并把 is_master 设为 is_mpi_root .AND. is_omp_root。
dimphy 局部维度
physics_distribution_mod.F90:28-30 再开一个 OpenMP parallel 区,按线程调用 Mars dimphy:Init_dimphy(klon_omp, nbp_lev)。
Mars dimphy.F90:1-29 中:
| 状态 | 线程属性 | 初始化 |
|---|---|---|
klon |
THREADPRIVATE |
每个线程写自己的 klon0,即 klon_omp。 |
klev, klevp1, klevm1 |
THREADPRIVATE |
OMP MASTER 赋值后 BARRIER;parallel 区内线程获得一致垂直层状态。 |
因此 Mars physics 中大量 USE dimphy, ONLY: klon/klev 的例程看到的是当前 OpenMP 子域维度,而不是全局 klon_glo 或 rank 级 klon_mpi。
几何局部切片
inigeomphy_mod.F90 在初始化分布后构造全局物理几何数组,再进入 OpenMP parallel 区把全局列切给每个线程。关键索引在 inigeomphy_mod.F90:224-234:
offset = klon_mpi_begin - 1
local global slice = offset + klon_omp_begin : offset + klon_omp_end
airefi(:) = airefi_glo(local slice)
airefi_outputs = aire_glo(local slice)
cufi/cvfi = cufi_glo/cvfi_glo(local slice)
lonfi/latfi = lonfi_glo/latfi_glo(local slice)
bounds = bounds_glo(local slice, :)
ind_cell_glo_fi = local global indices
随后 inigeomphy_mod.F90:237-241 调用 init_geometry(klon_omp,...) 和 init_geometry_cell_area_for_outputs(klon_omp,...),F90:244-248 调用 init_vertical_layers。这说明分布初始化不仅决定 transfer 维度,也决定 Mars physics 看到的经纬度、面积、边界和垂直层状态。
复现顺序
复现 Mars 3D 物理初始化时,最小顺序如下:
1. 上游动力/并行层决定每个 rank 的 nbp 和 communicator。
2. iniphysiq 调用 inigeomphy(ii,jj,nlayer,nbp,communicator,...)
3. inigeomphy 调用 init_physics_distribution:
a. init_grid_phy_lmdz 写全局网格和 klon_glo
b. init_phys_lmdz_mpi_data 收集 nbp,建立 rank 级 klon_mpi 表
c. init_phys_lmdz_omp_data 切分 klon_mpi,建立 thread 级 klon_omp 表
d. Init_dimphy 写 Mars physics 的 klon/klev
4. inigeomphy 根据 klon_mpi_begin + klon_omp_begin/end 截取几何数组。
5. Mars iniphysiq 在 OMP parallel COPYIN 后调用 phys_state_var_init、ini_fillgeom、conf_phys,均使用 klon_omp。
常见误读
klon_glo、klon_mpi、klon_omp不是同一个维度。klon_glo是全局物理列,klon_mpi是当前 rank 列数,klon_omp是当前线程列数。dimphy:klon在 Mars physics 中等于当前线程的klon_omp,不是klon_glo。mod_grid_phy_lmdz的Grid1Dto2D_glo/Grid2Dto1D_glo处理全局列和二维 lon-lat 的极点展开/压缩;它不负责 MPI/OMP 切分。calfis_p的动力格点到物理列 scatter/gather 是手写路径,不能直接等同于mod_phys_lmdz_transfert_para的通用 transfer。COMM_LMDZ_PHY来自传入的communicator;并行动力中其他路径也可能使用COMM_LMDZ。两者是否相同取决于上游初始化。
输出与副作用
| 模块 | 写入状态 |
|---|---|
mod_grid_phy_lmdz |
grid_type/nvertex/nbp_lon/nbp_lat/nbp_lev/klon_glo。 |
mod_phys_lmdz_mpi_data |
MPI rank、size、root、pole 标志、rank 级二维/一维分布表、COMM_LMDZ_PHY。 |
mod_phys_lmdz_omp_data |
OMP rank、size、thread 级列分布和 pole 标志。 |
mod_phys_lmdz_para |
klon_loc/is_master/is_sequential/is_parallel。 |
dimphy |
Mars physics 当前线程的 klon/klev/klevp1/klevm1。 |
geometry_mod、regular_lonlat_mod、vertical_layers_mod |
后续由 inigeomphy 用分布结果写入局部几何、regular grid 和垂直层。 |
本链没有外部文件 I/O。Init_phys_lmdz_mpi_data、Init_phys_lmdz_omp_data 和 Test_transfert 会向 lunout 打印分布和 checksum 诊断。
待确认
- Mars 并行配置中
COMM_LMDZ_PHY与动力侧COMM_LMDZ是否总是一致,需要结合init_const_mpi/init_parallel和具体 run script 实测。 Test_transfert在非MESOSCALE构建中每次初始化都会运行;这是否在生产输出中可接受属于运行配置/历史设计问题,本页只记录源码事实。