dyn1d/parallel_lmdz.F90
路径
LMDZ.MARS\libf\phymars\dyn1d\parallel_lmdz.F90
文件定位
dyn1d/parallel_lmdz.F90 在当前源码树中不是独立实现,而是一行相对路径:
../../../../LMDZ.COMMON/libf/dyn3dpar/parallel_lmdz.F90
它把 1D testphys1d 构建路径中的 parallel_lmdz 解析到公共并行动力运行时模块。目标模块负责 MPI/OpenMP 运行时状态、纬向带分配、halo 交换、broadcast/gather 和并行结束清理。当前 dyn1d 目录中直接使用该模块的源码点是 testphys1d.F90,只导入并调用 init_parallel。
定义的符号
| 符号 | 类型 | 目标行号 | 作用 |
|---|---|---|---|
parallel_lmdz.F90 |
path stub | dyn1d line 1 | 指向 LMDZ.COMMON/libf/dyn3dpar/parallel_lmdz.F90。 |
parallel_lmdz |
module | common line 4 | 保存并行运行状态并提供 MPI/OpenMP 分配、通信和结束入口。 |
using_mpi / using_omp |
logical, save | common lines 13-14 | 记录当前编译/运行是否启用 MPI/OpenMP;init_parallel 按 CPP_MPI、CPP_OMP 设置。 |
mpi_size / mpi_rank |
integer, save | common lines 16-17 | COMM_LMDZ 上的进程数和当前 rank。 |
jj_begin / jj_end / jj_nb |
integer, save | common lines 18-20 | 当前 MPI rank 负责的纬向带起止和数量。 |
ij_begin / ij_end |
integer, save | common lines 21-22 | 当前 rank 的扁平化水平索引范围,按 iip1 和 jj_* 派生。 |
pole_nord / pole_sud |
logical, save | common lines 23-24 | 当前 rank 是否持有北/南极纬向边界。 |
jj_begin_para(:) / jj_end_para(:) / jj_nb_para(:) |
allocatable integer, save | common lines 26-28 | 每个 MPI rank 的纬向分配表。 |
OMP_CHUNK / omp_rank / omp_size |
integer, save | common lines 29-31 | OpenMP 垂直层 chunk 和线程编号/线程数;omp_rank 为 threadprivate。 |
jjb_u 等 dummy 变量 |
integer, save | common lines 36-49 | 为兼容 dyn3d_mem/parallel_lmdz.F90 中的 calfis_loc 编译需求而保留的 u/v 网格占位变量。 |
init_parallel |
subroutine | common line 53 | 初始化 MPI/OpenMP 状态、rank 纬向分配和输出文件。 |
SetDistrib |
subroutine | common line 205 | 用外部传入的 jj_Nb_New 重设每个 rank 的纬向分配。 |
Finalize_parallel |
subroutine | common line 238 | 释放分配表并按 XIOS/OASIS/MPI 条件关闭运行时。 |
Pack_Data / Unpack_Data |
subroutines | common lines 297, 320 | 在二维 field 子块和一维通信 buffer 之间打包/解包。 |
barrier |
subroutine | common line 345 | using_mpi 时调用 MPI_Barrier(COMM_LMDZ)。 |
exchange_hallo |
subroutine | common line 361 | 按上下 halo 行数与极点边界执行相邻 rank 的非阻塞 halo 交换。 |
Gather_Field |
subroutine | common line 494 | 把分布式 ip1jmp1 或 ip1jm 水平场收集到指定 root rank。 |
AllGather_Field |
subroutine | common line 578 | 先 Gather_Field(...,0),再从 rank 0 广播全场。 |
Broadcast_Field |
subroutine | common line 600 | 从指定 rank 广播 field。 |
初始化流程
init_parallel 的源码流程如下:
- 按编译宏设置运行模式:定义
CPP_MPI时using_mpi=.TRUE.,否则.FALSE.;定义CPP_OMP时using_OMP=.TRUE.,否则.FALSE.。 - 调用
InitVampir初始化性能计时钩子。 - 若
using_mpi,用 mod_const_mpi 提供的COMM_LMDZ调MPI_COMM_SIZE和MPI_COMM_RANK;否则强制mpi_size=1、mpi_rank=0。 - 如果
lunout不是标准输入输出单元 5/6,非 root rank 打开lmdz.out_XXXX作为文本输出文件。 - 分配
jj_begin_para/jj_end_para/jj_nb_para(0:mpi_size-1)。 - 将
jjm+1个纬向带尽量均分到所有 rank:每个 rank 先取整数商,前MOD(jjm+1,mpi_size)个 rank 多得一带。 - 若某 rank 的
jj_nb_para(i)<=1,打印错误;MPI 分支下调用MPI_ABORT(COMM_LMDZ,-1,ierr)。 - 由累计纬向带得到每个 rank 的
jj_begin_para(i)和jj_end_para(i),再写入当前 rank 的jj_begin/jj_end/jj_nb。 - 计算扁平水平范围:
ij_begin=(jj_begin-1)*iip1+1,ij_end=jj_end*iip1。 - 设置极点标志:
mpi_rank==0为北极 rank,mpi_rank==mpi_size-1为南极 rank。 - OpenMP 分支中取
omp_size/omp_rank,默认omp_chunk=ceil((llm+1)/omp_size),再用getin('omp_chunk',omp_chunk)允许配置覆盖;非 OpenMP 时设omp_size=1、omp_rank=0。
通信和分布算法
SetDistrib
SetDistrib(jj_Nb_New) 接收一个从 0 到 MPI_Size-1 的纬向带数量数组,直接覆写 jj_Nb_Para,然后从 rank 0 起逐段累加生成新的 jj_begin_para/jj_end_para。最后按当前 mpi_rank 重算 jj_begin/jj_end/jj_nb/ij_begin/ij_end。
复现时要注意:这个例程不重新分配 jj_*_para,调用前必须已经执行过 init_parallel,且 jj_Nb_New 的大小必须与当前 MPI_Size 一致。
exchange_hallo
exchange_hallo(Field,ij,ll,up,down) 只在 using_mpi 为真时工作:
- 先执行全局
barrier,再调用VTb(VThallo)开始 halo 计时。 - 默认允许向上/向下发送和接收。
- 若当前 rank 是北极,禁止向上发送和接收;若是南极,禁止向下发送和接收。
- 若
up==0,禁止向下发送和向上接收;若down==0,禁止向上发送和向下接收。 - 每个启用的方向分配对应 buffer,
Pack_Data把row*iip1*ll个元素打包。 - 用
MPI_ISSEND把北侧/南侧边界发送给mpi_rank-1或mpi_rank+1,用MPI_IRECV接收相邻 rank 的边界。 MPI_WAITALL等待所有请求完成。- 用
Unpack_Data把收到的 buffer 写回Field(ij_begin-up*iip1,1)或Field(ij_end+1,1)位置。 - 调
VTe(VThallo)结束计时,再执行一次barrier。
Pack_Data 和 Unpack_Data 的循环次序是外层垂直层 l=1..ll,内层水平点 i=1..row*iip1,因此通信 buffer 以“每层连续水平行块”的顺序排列。
Gather_Field、AllGather_Field 和 Broadcast_Field
Gather_Field(Field,ij,ll,rank) 支持两种水平数组长度:
ij==ip1jmp1:包含jjm+1纬向行的全水平网格。ij==ip1jm:少一个纬向行,源码用min(jj_end,jjm)避免南边界越界。
每个 rank 先把本地纬向带打包到 Buffer_send。root rank 根据所有 jj_begin_para/jj_end_para 构造 Recv_count 和 displ,调 MPI_GATHERV 后再逐 rank Unpack_Data 回完整 Field。非 root rank 为避免 debug 模式未分配实参报警,会分配一个长度为 1 的 dummy Buffer_Recv。
AllGather_Field 是 Gather_Field(...,0) 加 MPI_BCAST(Field,ij*ll,...);Broadcast_Field 则直接从指定 rank 广播整个 Field。
结束流程
Finalize_parallel 先释放 jj_begin_para/jj_end_para/jj_nb_para。随后按编译宏和 type_ocean 选择关闭路径:
type_ocean == 'couple'且有CPP_XIOS:调用wxios_close()。type_ocean == 'couple'且无 XIOS 但有CPP_COUPLE:调用prism_terminate_proto(ierr);若返回值不是PRISM_Ok,调用abort_gcm。- 非 couple 且有
CPP_XIOS:调用wxios_close()。 - 非 couple 且有
CPP_MPI:若using_mpi,调用MPI_FINALIZE(ierr)。
dyn1d 使用路径
testphys1d.F90 在程序开头导入:
use mod_const_mpi, only: init_const_mpi
use parallel_lmdz, only: init_parallel随后在主流程早期顺序调用:
call init_const_mpi
call init_parallel因此 1D 主程序依赖 mod_const_mpi 先建立 COMM_LMDZ,再由 parallel_lmdz::init_parallel 建立 rank/线程状态。当前 dyn1d 源码未发现直接调用 Finalize_parallel、exchange_hallo、Gather_Field、AllGather_Field 或 Broadcast_Field 的位置;这些例程来自公共并行模块,供完整动力/物理并行路径或被链接的公共代码使用。
编译条件和外部依赖
| 宏/依赖 | 影响 |
|---|---|
CPP_MPI |
引入 mpif.h,启用 MPI_COMM_SIZE/RANK、MPI_ABORT、MPI_Barrier、非阻塞发送接收、MPI_GATHERV、MPI_BCAST 和 MPI_FINALIZE。 |
CPP_OMP |
使用 OpenMP runtime 取线程数和线程号,并设置 omp_chunk。 |
CPP_IOIPSL |
从 IOIPSL 使用 getin;否则使用本地 ioipsl_getincom::getin。 |
CPP_XIOS |
Finalize_parallel 中调用 wxios_close()。 |
CPP_COUPLE / CPP_OMCT |
Finalize_parallel 中走 OASIS/PRISM coupler 结束路径。 |
dimensions.h / paramet.h |
提供 iip1/jjm/llm/ip1jmp1/ip1jm 等网格维度。 |
iniprint.h |
提供 lunout 输出单元。 |
vampir |
提供 InitVampir、VTb、VTe 和 VThallo 计时标识。 |
复现要点
- 当前 dyn1d 文件本身只有一行路径;实际行为以
LMDZ.COMMON/libf/dyn3dpar/parallel_lmdz.F90为准。 init_parallel假设 mod_const_mpi 已经初始化了COMM_LMDZ;在testphys1d中源码顺序确实是先init_const_mpi再init_parallel。- MPI 纬向分配要求每个 rank 至少两条纬向带,否则源码会 abort;这对小网格/多进程组合是硬约束。
- 非 MPI 编译时,
using_mpi=.FALSE.,mpi_size=1,mpi_rank=0,通信例程主体不会执行。 exchange_hallo的up/down为 halo 行数;为 0 时源码会禁用对应方向的发送/接收,不是“交换 0 行后继续通信”。jjb_u/jje_u/...和ijb_u/ije_u/...在本目标文件内没有赋值逻辑;它们是为了公共接口兼容保留的 dummy 状态。
待确认
- 当前 1D 构建默认是否启用
CPP_MPI、CPP_OMP或CPP_XIOS,需要结合 makefile/编译脚本确认;本页只按源码条件分支记录。 - Windows 工作树把 dyn1d 入口呈现为一行路径文本;原始类 Unix 工作树中它可能是符号链接。
Finalize_parallel在testphys1d运行结束路径是否被其他公共代码调用,当前 dyn1d 直接源码未发现调用点。
相关页面
- dyn1d/index.md - dyn1d 子目录索引。
- mod_const_mpi -
COMM_LMDZ和init_const_mpi的来源。 - mod_interface_dyn_phys - 动力-物理接口索引映射,与并行物理分布相关。
- init_testphys1d_mod - 1D 初始化主例程。
- testphys1d.F90 - 1D 主程序。