在这一篇中将结合前两篇中介绍的底层数据结构和FFT实现,介绍第二篇中提到的CalculateHartreeSB子程序中分块算法是如何实现的,并与第一篇的计算流程进行对照。
在这一子程序中首先定义了一些数组用于存储实空间和倒空间中不同类型的网格数据:
complex*16, dimension(DSBX%k1, DSBX%k2, DSBX%k3) :: rhoG_DSBX_short
complex*16, dimension(DSBX%k1, DSBX%k2, DSBX%k3) :: potG_DSBX_short
real*8, dimension(DSBX%n1, DSBX%n2, DSBX%n3) :: rhoR_DSBX_short
real*8, dimension(DSBX%n1, DSBX%n2, DSBX%n3) :: potR_DSBX_short
complex*16, dimension(SGBX%k1, SGBX%k2, SGBX%k3) :: rhoG_SGBX
real*8, dimension(SGBX%n1, SGBX%n2, SGBX%n3) :: potR_SGBX
real*8, dimension(SGBX%n1, SGBX%n2, SGBX%n3) :: rhoR_SGBX
real*8, dimension(SSBX%n1, SSBX%n2, SSBX%n3) :: potR_SSBX
complex*16, dimension(SSBX%k1, SSBX%k2, SSBX%k3) :: potG_SSBX
real*8, dimension(DSBX%n1, DSBX%n2, DSBX%n3) :: potR_DSBX_long
complex*16, dimension(DSBX%k1, DSBX%k2, DSBX%k3) :: potG_DSBX_long
这些数组的命名基本是遵循自然语言,此处不再赘述。使用这些数组和已经预先顶的DGBX、DSBX、SGBX和SSBX四个BOX类型的变量,下面将分段介绍计算流程中每一步对应的子程序调用,注意到子程序中用到的输入和输出数组rhoIn和potential是以DSBX网格形式存储的。
(1)短程库仑势的计算
- \rho^\text{s}(\boldsymbol{r}^\text{s})\rightarrow\rho^\text{s}(\boldsymbol{G}^\text{s})
- \phi^\text{s}_\text{S}(\boldsymbol{r}^\text{s})=V_\text{s}(\boldsymbol{G}^\text{s})\rho^\text{s}(\boldsymbol{G}^\text{s})
- \phi_\text{S}^\text{s}(\boldsymbol{G}^\text{s})\rightarrow\phi_\text{S}^\text{s}(\boldsymbol{r}^\text{s})
这部分计算对应的调用为:
call FFT_NEW( DSBX%FFT_STATE, rhoIn, rhoG_DSBX_short )
potG_DSBX_short = rhoG_DSBX_short * htkernel_short
call FFT_NEW( DSBX%FFT_STATE, potG_DSBX_short, potR_DSBX_short )
这里的htkernel_short对应V_\text{s}(\boldsymbol{G}^\text{s})。
(2)长程库仑势的计算(一)
- \rho^\text{s}_\text{m}(\boldsymbol{G}^\text{s})=m(\boldsymbol{G}^\text{s})\rho^\text{s}(\boldsymbol{G}^\text{s})
- \rho^\text{s}_\text{m}(\boldsymbol{G}^\text{s})\rightarrow\rho'^\text{s}_\text{m}(\boldsymbol{G}^\text{s})
- \rho'^\text{s}_\text{m}(\boldsymbol{G}^\text{s})\rightarrow\rho'^\text{s}_\text{m}(\boldsymbol{r}'^\text{s})
- \rho'^\text{s}_\text{m}(\boldsymbol{r}'^\text{s})\rightarrow\rho'_\text{m}(\boldsymbol{r}')
这部分对应的调用为:
rhoG_DSBX_short = rhoG_DSBX_short * maskG_DSBX
call FFT_NEW( DSBX%FFT_STATE, rhoG_DSBX_short, rhoR_DSBX_short )
call DSBX_2_SGBX( rhoR_DSBX_short, rhoR_SGBX )
这里的maskG_DSBX对应m(\boldsymbol{G}^\text{s})。注意到这里的计算流程是\rho^\text{s}_\text{m}(\boldsymbol{G}^\text{s})\rightarrow\rho^\text{s}_\text{m}(\boldsymbol{r}^\text{s})\rightarrow\rho'_\text{m}(\boldsymbol{r}'),与第一篇中不同,但是效果是相同的。
(3)长程库仑势的计算(二)
- \rho'_\text{m}(\boldsymbol{r}')\rightarrow\rho'_\text{m}(\boldsymbol{G})
- \phi'_\text{L}(\boldsymbol{G})=\rho'_\text{m}(\boldsymbol{G})\cdot[V_\text{L}(\boldsymbol{G})/m(\boldsymbol{G})]
- \phi'_\text{L}(\boldsymbol{G})\rightarrow\phi'_\text{L}(\boldsymbol{r}')
这部分对应的调用为:
if (SGBX%active_proc == .TRUE. ) then
CALL FFT_NEW( SGBX%FFT_STATE, rhoR_SGBX, rhoG_SGBX )
rhoG_SGBX = rhoG_SGBX * htkernel_long / maskG_SGBX
CALL FFT_NEW( SGBX%FFT_STATE, rhoG_SGBX, potR_SGBX )
end if
这里的htkernel_long对应的是V_\text{L}(\boldsymbol{G})。注意到由于这部分计算是对SGBX网格进行全局FFT,因此只有第一个网格(ctive_proc标记为.TRUE.)进行相关计算。
(4)长程库仑势的计算(三)
- \phi'_\text{L}(\boldsymbol{r}')\rightarrow\phi'^\text{s}_\text{L}(\boldsymbol{r}'^\text{s})
- W'^\text{s}(\boldsymbol{r}'^\text{s})=M'(\boldsymbol{r}'^\text{s})\phi'^\text{s}_\text{L}(\boldsymbol{r}'^\text{s})
- W'^\text{s}(\boldsymbol{r}'^\text{s})\rightarrow W'^\text{s}(\boldsymbol{G}^\text{s})
这部分对应的调用为:
call SGBX_2_SSBX( potR_SGBX, potR_SSBX )
call GetBufferData( SSBX, potR_SSBX )
potR_SSBX = potR_SSBX * maskX_SSBX
call FFT_NEW( SSBX%FFT_STATE, potR_SSBX, potG_SSBX )
这里的maskX_SSBX对应M'(\boldsymbol{r}'^\text{s})。注意到这里还调用了一个GetBufferData子程序(位于DataExchange_FillBuffer.f90文件中DataExchange_FillBuffer),其作用是将每个小块与附近26个小块交换核心部分数据以填充缓冲区。
(5)长程库仑势的计算(四)
- W'^\text{s}(\boldsymbol{G}^\text{s})\rightarrow W^\text{s}(\boldsymbol{G}^\text{s})
- W^\text{s}(\boldsymbol{G}^\text{s})\rightarrow W^\text{s}(\boldsymbol{r}^\text{s})
- \phi^\text{s}_\text{L}(\boldsymbol{r}^\text{s} )=W^\text{s} (\boldsymbol{r}^\text{s})/M(\boldsymbol{r}^\text{s} )
这部分对应的调用为:
call SSBX_2_DSBX( potG_SSBX, potG_DSBX_long )
call FFT_NEW( DSBX%FFT_STATE, potG_DSBX_long, potR_DSBX_long )
potR_DSBX_long = potR_DSBX_long / maskX_DSBX
这里的maskX_DSBX对应M(\boldsymbol{r}^\text{s})。
(6)将短程与长程库仑势相加
- \phi^\text{s}(\boldsymbol{r}^\text{s})=\phi_\text{S}^\text{s}(\boldsymbol{r}^\text{s})+\phi_\text{L}^\text{s}(\boldsymbol{r}^\text{s})
这部分对应的代码为:
potential = potR_DSBX_short + potR_DSBX_long
可以看到,CalculateHartreeSB子程序中的计算过程与第一篇中讨论的计算流程基本上是一一对应的。至此基本上完成了对于SBFFT代码的初步学习。
写这一系列的博客的目的是学习SBFFT代码的Fortran实现,下一步的工作是将这一算法移植到AMD公司的GPU计算平台ROCm。当然这里的讨论还是很初步的,没有涉及到比较细节的内容,此外关于数据输入/输出、mask函数的生成等问题也没有涉及。在移植工作中还可能需要针对GPU的特性对数据结构进行一定的改进。