技术背景
在数学和物理学领域,总是充满了各种连续的函数模型。而当我们用现代计算机的技术去处理这些问题的时候,事实上是无法直接处理连续模型的,绝大多数的情况下都要转化成一个离散的模型再进行数值的计算。比如计算数值的积分,计算数值的二阶导数(海森矩阵)等等。这里我们所介绍的打格点的算法,正是一种典型的离散化方法。这个对空间做离散化的方法,可以在很大程度上简化运算量。比如在分子动力学模拟中,计算近邻表的时候,如果不采用打格点的方法,那么就要针对整个空间所有的原子进行搜索,计算出来距离再判断是否近邻。而如果采用打格点的方法,我们只需要先遍历一遍原子对齐进行打格点的离散化,之后再计算近邻表的时候,只需要计算三维空间下邻近的27个格子中的原子是否满足近邻条件即可。在这篇文章中,我们主要探讨如何用GPU来实现打格点的算法。
打格点算法实现
我们先来用一个例子说明一下什么叫打格点。对于一个给定所有原子坐标的系统,也就是已知了[x,y,z],我们需要得到的是这些原子所在的对应的格子位置[nx,ny,nz]。我们先看一下在CPU上的实现方案,是一个遍历一次的算法:
# cuda_grid.py from numba import jit from numba import cuda import numpy as np def grid_by_cpu(crd, rxyz, atoms, grids): """Transform coordinates [x,y,z] into grids [nx,ny,nz]. Args: crd(list): The 3-D coordinates of atoms. rxyz(list): The list includes xmin,ymin,zmin,grid_num. atoms(int): The total number of atoms. grids(list): The transformed grids matrix. """ for i in range(atoms): grids[i][0] = int((crd[i][0]-rxyz[0])/rxyz[3]) grids[i][1] = int((<strong style="color:transparent">本文来源gao@daima#com搞(%代@#码@网&</strong>crd[i][1]-rxyz[1])/rxyz[3]) grids[i][2] = int((crd[i][2]-rxyz[2])/rxyz[3]) return grids if __name__=='__main__': np.random.seed(1) atoms = 4 grid_size = 0.1 crd = np.random.random((atoms,3)).astype(np.float32) xmin = min(crd[:,0]) ymin = min(crd[:,1]) zmin = min(crd[:,2]) xmax = max(crd[:,0]) ymax = max(crd[:,1]) zmax = max(crd[:,2]) xgrids = int((xmax-xmin)/grid_size)+1 ygrids = int((ymax-ymin)/grid_size)+1 zgrids = int((zmax-zmin)/grid_size)+1 rxyz = np.array([xmin,ymin,zmin,grid_size], dtype=np.float32) grids = np.ones_like(crd)*(-1) grids = grids.astype(np.float32) grids_cpu = grid_by_cpu(crd, rxyz, atoms, grids) print (crd) print (grids_cpu) import matplotlib.pyplot as plt plt.figure() plt.plot(crd[:,0], crd[:,1], 'o', color='red') for grid in range(ygrids+1): plt.plot([xmin,xmin+grid_size*xgrids], [ymin+grid_size*grid,ymin+grid_size*grid], color='black') for grid in range(xgrids+1): plt.plot([xmin+grid_size*grid,xmin+grid_size*grid], [ymin,ymin+grid_size*ygrids], color='black') plt.savefig('Atom_Grids.png')
输出结果如下,
$ python3 cuda_grid.py
[[4.17021990e-01 7.20324516e-01 1.14374816e-04]
[3.02332580e-01 1.46755889e-01 9.23385918e-02]
[1.86260208e-01 3.45560730e-01 3.96767467e-01]
[5.38816750e-01 4.19194520e-01 6.85219526e-01]]
[[2. 5. 0.]
[1. 0. 0.]
[0. 1. 3.]
[3. 2. 6.]]