calfis_p 动力格点到物理列映射

输入范围

dynphy_lonlat\calfis_p.F: L1-1520(并行物理调用接口)
dynphy_lonlat\mod_interface_dyn_phys.F90: L1-65(index_i/index_j 初始化)
dynphy_lonlat\gr_dyn_fi_p.F: L1-44(动力→物理标量场 scatter)
dynphy_lonlat\gr_fi_dyn_p.F: L1-56(物理→动力标量场 gather)

Mars 运行参与度:条件经过。Mars 并行动力启用物理时(apphys 为真),calfis_p 每次 physics timestep 执行一次完整的 scatter → call_physiq → gather 流程。

例程定位

本页聚焦 calfis_p 中动力网格 (iip1,jjp1) 与物理列 (klon) 之间的数据映射机制。映射由 mod_interface_dyn_physindex_i/index_j 数组驱动,贯穿场 scatter、wind 转换、极点投影和 tendency 回写四个阶段。整体接口结构详见 calfis_p 模块页

物理列索引映射

index_i/index_j 初始化

Init_interface_dyn_phys 在 physics 初始化阶段分配 index_i(klon_mpi)index_j(klon_mpi),按纬圈顺序将每个物理列映射回动力 (i,j) 索引:

k = 1
if (is_north_pole_dyn) then
   index_i(k) = 1;  index_j(k) = 1;  k = 2   ! 北极单列
else
   DO i = ii_begin, iim                        ! 首行部分经度
      index_i(k) = i;  index_j(k) = jj_begin;  k = k+1
   ENDDO
endif

DO j = jj_begin+1, jj_end-1                    ! 中间纬圈:完整经度
   DO i = 1, iim
      index_i(k) = i;  index_j(k) = j;  k = k+1
   ENDDO
ENDDO

if (is_south_pole_dyn) then
   index_i(k) = 1;  index_j(k) = jj_end        ! 南极单列
else
   DO i = 1, ii_end                            ! 末行部分经度
      index_i(k) = i;  index_j(k) = jj_end;  k = k+1
   ENDDO
endif

关键特征:

klon_mpi vs klon_omp

calfis_p 使用两级列数:

  1. klon = klon_mpi:整个 MPI rank 的物理列数,用于 scatter/gather 阶段。
  2. klon = klon_omp:单个 OpenMP 线程负责的列数,用于 call_physiq 调用。

两者通过 offset = klon_omp_begin - 1 连接:线程 t 负责 klon_mpi 数组中 [offset+1, offset+klon_omp] 范围的列。

场 scatter:动力网格 → 物理列

标量场 scatter

每个标量场通过 index_i/index_j 从动力二维数组提取到物理一维列数组:

! 手动展开(压力、温度、tracer 等)
do ig0 = 1, klon
   i = index_i(ig0)
   j = index_j(ig0)
   zpsrf(ig0) = pps(i,j)
enddo

! 批量调用(geopotential、flxw)
CALL gr_dyn_fi_p(nfield, iip1, jjp1, klon, pdyn, pfi)

gr_dyn_fi_p 内部实现完全相同的逻辑:

DO l = 1, nfield
   DO ig = 1, klon
      i = index_i(ig)
      j = index_j(ig)
      pfi(ig,l) = pdyn(i,j,l)
   ENDDO
ENDDO

被 scatter 的场及其转换细节:

动力场 物理列 转换
pps zpsrf 直接索引
pp zplev 直接索引(每层循环)
ppk zpk 直接索引
pteta ztetaztfi 直接索引后 tpot2t_p 转温度
pq zqfi 每 tracer 直接索引
pphi zphi gr_dyn_fi_p 后减 zphis
pphis zphis gr_dyn_fi_p
flxw flxwfi gr_dyn_fi_p

zplay 的派生计算

zplay 不是直接 scatter,而是从 zpk 派生:

pksurcp = ppk(i,j,l) / cpp
zplay(ig0,l) = preff * pksurcp ** unskap    ! unskap = 1/kappa

这是 Exner 函数的逆运算,把 ppk(Exner 值)转换为中层压力。

wind scatter

wind 转换比标量复杂,因为动力网格使用协变分量 (pucov, pvcov),而物理侧使用自然分量 (zufi, zvfi, zrfi)

zufi(自然纬向风)

if (i == 1) then
   zufi(ig0,l) = 0.5 * (pucov(iim,j,l)/cu(iim,j) + pucov(1,j,l)/cu(1,j))
else
   zufi(ig0,l) = 0.5 * (pucov(i-1,j,l)/cu(i-1,j) + pucov(i,j,l)/cu(i,j))
endif

取相邻两个 U 点的协变分量除以 cu(度量因子)得到自然分量,再平均到标量点。i==1 时使用 iim 的 E-W 周期包裹。

zvfi(自然经向风)

zvfi(ig0,l) = 0.5 * (pvcov(i,j-1,l)/cv(i,j-1) + pvcov(i,j,l)/cv(i,j))

取相邻两个 V 点的协变分量除以 cv 后平均。

zrfi(相对涡度):先在动力网格上计算 zrot(协变风分的离散旋度),再用 4 点平均到标量点。极点处 j==1j==jjp1 时置零。

极点 wind 投影

极点处的自然风分量通过对所有经度做 cos/sin 积分获得:

! 北极
z1(i) = (rlonu(i)-rlonu(i-1)) * pvcov(i,1,l) / cv(i,1)
zcos(i) = COS(rlonv(i)) * z1(i)
zsin(i) = SIN(rlonv(i)) * z1(i)
zufi(1,l) = SSUM(iim, zcos, 1) / pi    ! U = <v*cos(lon)>/pi
zvfi(1,l) = SSUM(iim, zsin, 1) / pi    ! V = <v*sin(lon)>/pi
zrfi(1,l) = 0.

这与 vly 中极点 dyq 的 sin/cos 投影是相同的数学操作:将极点矢量投影到最低两个 Fourier 模态。南极使用 jjm 行和 klon 列。

MPI wind 边界交换

physics 调用后,calfis_p 执行一次手写 MPI 交换:

! rank > 0 发送前 iim 列给 rank-1
MPI_ISSEND(du_send, iim*llm, ..., MPI_Rank-1, tag=401, ...)
! rank < size-1 从 rank+1 接收
MPI_IRECV(du_recv, iim*llm, ..., MPI_Rank+1, tag=401, ...)

交换的是物理 tendency zdufi/zdvfi 的首 iim 列。目的是构造扩展数组 zdufi2/zdvfi2

zdufi2(1:klon, l) = zdufi(1:klon, l)
zdufi2(klon+1:klon+iim, l) = du_recv(1:iim, l)

扩展后的数组支持 V wind tendency 回写时需要跨纬带平均:pdvfi 使用 zdvfi2(ig0)zdvfi2(ig0+iim) 做平均,后者属于下一纬带。如果下一纬带在另一个 rank 上,没有这次交换就无法获取。

场 gather:物理列 → 动力网格

标量场 gather

gr_fi_dyn_p 实现物理列到动力网格的反向映射:

do ig = 1, klon
   i = index_i(ig)
   j = index_j(ig)
   pdyn(i,j,ifield) = pfi(ig,ifield)
   if (i == 1) pdyn(im,j,ifield) = pdyn(i,j,ifield)    ! E-W 周期端点
enddo

i==1 时,同时将值写入 im 位置(E-W 周期端点),确保周期一致性。

极点处理:

if (pole_nord) then
   do i = 1, im
      pdyn(i,1,ifield) = pdyn(1,1,ifield)    ! 极点所有经度同值
   enddo
endif

tendency 回写

tendency 回写不都使用 gr_fi_dyn_p。各场使用不同的回写策略:

pdpsfi(surface pressure tendency):使用 gr_fi_dyn_p

CALL gr_fi_dyn_p(1, klon, iip1, jjp1, zdpsrf, pdpsfi)

pdhfi(potential temperature tendency):手动回写,不使用 zdtfi 而是 (zteta-pteta)/dtphys

pdhfi(i,j,l) = (zteta(ig0,l) - pteta(i,j,l)) / dtphys

这避免了中间转换的精度损失。t2tpot_p 先将物理温度转回位温 zteta,再与原始 pteta 差商。极点整行用极点单列值扩展。i==1 时补 iip1 周期端点。

pdqfi(tracer tendency):先整层清零,再按 iq 手动回写:

pdqfi(:,:,l,:) = 0.
DO iq = 1, nqtot
   DO ig0 = kstart, kend
      i = index_i(ig0);  j = index_j(ig0)
      pdqfi(i,j,l,iq) = zdqfi(ig0,l,iq)
      if (i==1) pdqfi(iip1,j,l,iq) = zdqfi(ig0,l,iq)
   ENDDO
   ! 极点扩展
ENDDO

清零确保 niadv 未映射的 tracer 位为零。i==1 补周期端点。

pdufi(zonal wind tendency):使用扩展数组 zdufi2 的相邻平均:

if (i /= iim) then
   pdufi(i,j,l) = 0.5 * (zdufi2(ig0,l) + zdufi2(ig0+1,l)) * cu(i,j)
endif
if (i == 1) then
   pdufi(iim,j,l) = 0.5 * (zdufi2(ig0,l) + zdufi2(ig0+iim-1,l)) * cu(iim,j)
   pdufi(iip1,j,l) = 0.5 * (zdufi2(ig0,l) + zdufi2(ig0+1,l)) * cu(i,j)
endif

物理 tendency 平均到 U 点后乘 cu 恢复协变分量。i==1 时用 E-W 周期包裹。极点行置零。

pdvfi(meridional wind tendency):使用跨纬带平均:

pdvfi(i,j,l) = 0.5 * (zdvfi2(ig0,l) + zdvfi2(ig0+iim,l)) * cv(i,j)

ig0+iim 跳到下一纬带,这就是前面 MPI 交换的目的。极点附近用投影方式回写:

! 北极
pdvfi(i,1,l) = zdufi(1,l)*COS(rlonv(i)) + zdvfi(1,l)*SIN(rlonv(i))
pdvfi(i,1,l) = 0.5 * (pdvfi(i,1,l) + zdvfi(i+1,l)) * cv(i,1)

先将极点自然风 tendency 投影到各经度方向,再与邻近纬带平均后乘 cv

边界清零

回写前,本 rank 起始纬圈 jj_begin 的 tendency 全部清零。若不是南极 rank,jj_end 也清零:

pdhfi(:,jj_begin,l) = 0;  pdqfi(:,jj_begin,l,:) = 0
pdufi(:,jj_begin,l) = 0;  pdvfi(:,jj_begin,l) = 0
pdpsfi(:,jj_begin) = 0

这防止旧值残留。leapfrog_p 后续通过 jj_Nb_Physic_bis halo 合并处理边界一致性。

复现要点

相关页面

待确认