Skip to content
Snippets Groups Projects
rho_Becke_grid.py 9.64 KiB
import numpy as np
import os
import sys
import h5py
import itertools
from mpl_toolkits.mplot3d import Axes3D


def quant_fill():
  quant = np.empty((35,3))

  quant[0,0]=0;quant[0,1]=0;quant[0,2]=0;

  quant[1,0]=1;quant[1,1]=0;quant[1,2]=0;
  quant[2,0]=0;quant[2,1]=1;quant[2,2]=0;
  quant[3,0]=0;quant[3,1]=0;quant[3,2]=1;

  quant[4,0]=2;quant[4,1]=0;quant[4,2]=0;
  quant[5,0]=0;quant[5,1]=2;quant[5,2]=0;
  quant[6,0]=0;quant[6,1]=0;quant[6,2]=2;
  quant[7,0]=1;quant[7,1]=1;quant[7,2]=0;
  quant[8,0]=1;quant[8,1]=0;quant[8,2]=1;
  quant[9,0]=0;quant[9,1]=1;quant[9,2]=1;

  quant[10,0]=3;quant[10,1]=0;quant[10,2]=0;
  quant[11,0]=0;quant[11,1]=3;quant[11,2]=0;
  quant[12,0]=0;quant[12,1]=0;quant[12,2]=3;
  quant[13,0]=2;quant[13,1]=1;quant[13,2]=0;
  quant[14,0]=2;quant[14,1]=0;quant[14,2]=1;
  quant[15,0]=0;quant[15,1]=2;quant[15,2]=1;
  quant[16,0]=1;quant[16,1]=2;quant[16,2]=0;
  quant[17,0]=1;quant[17,1]=0;quant[17,2]=2;
  quant[18,0]=0;quant[18,1]=1;quant[18,2]=2;
  quant[19,0]=1;quant[19,1]=1;quant[19,2]=1;

  quant[20,0]=4;quant[20,1]=0;quant[20,2]=0;
  quant[21,0]=0;quant[21,1]=4;quant[21,2]=0;
  quant[22,0]=0;quant[22,1]=0;quant[22,2]=4;
  quant[23,0]=3;quant[23,1]=1;quant[23,2]=0;
  quant[24,0]=3;quant[24,1]=0;quant[24,2]=1;
  quant[25,0]=1;quant[25,1]=3;quant[25,2]=0;
  quant[26,0]=0;quant[26,1]=3;quant[26,2]=1;
  quant[27,0]=1;quant[27,1]=0;quant[27,2]=3;
  quant[28,0]=0;quant[28,1]=1;quant[28,2]=3;
  quant[29,0]=2;quant[29,1]=2;quant[29,2]=0;
  quant[30,0]=2;quant[30,1]=0;quant[30,2]=2;
  quant[31,0]=0;quant[31,1]=2;quant[31,2]=2;
  quant[32,0]=2;quant[32,1]=1;quant[32,2]=1;
  quant[33,0]=1;quant[33,1]=2;quant[33,2]=1;
  quant[34,0]=1;quant[34,1]=1;quant[34,2]=2;

  return quant

def calcu_prt_power(num_grid, dpos, max_nlm):
  # 3*nlm(power)*num_grid
  prt_power = np.ones(([3, max_nlm+1] + [num_grid]))
  for a in range(3):
    dpos_a = dpos[:,a]
    for nlm in range(1, max_nlm+1): # nlm=0 are all initiated as ones
      prt_power[a,nlm] = np.power(dpos_a, nlm)

  return prt_power

def calcu_prt_exp(num_grid, dpos, exp_c):
  # len(exp_c)*num_grid
  prt_exp = np.empty(([len(exp_c)] + [num_grid]))
  dpos_norm = np.linalg.norm(dpos, axis=-1)
  for ei in range(len(exp_c)):
    prt_exp[ei] = np.exp(-exp_c[ei]*np.power(dpos_norm,2))

  return prt_exp

def calcu_prt(grid, num_grid, quant, pos_c, typ_c, exp_c):
  dpos = np.subtract(grid, pos_c)
  dpos[dpos == 0] = 1e-8
  # calculate exponent term for pos_c
  prt_exp = calcu_prt_exp(num_grid, dpos, exp_c)

  # precalculate all possible polynomial terms for pos_c
  max_nlm = max([q for t in typ_c for q in quant[t]])
  prt_power  = calcu_prt_power(num_grid, dpos, max_nlm)
  
  # calculate primitives
  prt_c = np.empty(([len(typ_c)] + [num_grid]))
  for ti in range(len(typ_c)):
    t = typ_c[ti]
    prt_c[ti] = prt_power[0,quant[t,0]] * prt_power[1,quant[t,1]] * prt_power[2,quant[t,2]] * prt_exp[ti]

  # calculate gradients of primitives (grdrho(x)=[nx/(xi-xA)-2epi*(xi-xA)]*rho)
  grdprt_c = np.empty(([len(typ_c),3] + [num_grid]))
  grd = np.empty(([len(typ_c),3] + [num_grid]))
  for dim in range(3):
    for ti in range(len(typ_c)):
      t = typ_c[ti]
      tmp = np.divide(quant[t,dim], dpos[:,dim])
      grd[ti,dim] = np.subtract(tmp, 2 * exp_c[ti] * dpos[:,dim])
      grdprt_c[ti,dim] = grd[ti,dim] * prt_c[ti]

  '''
  # calculate sec gradients of primitives grd2rho(x)
  grd2prt_c = np.empty(([len(typ_c),3,3] + [num_grid]))
  for dim1 in range(3):
    for dim2 in range(3):
      for ti in range(len(typ_c)):
        t = typ_c[ti]
        tmp = -np.divide(quant[t,dim], np.power(dpos[:,dim],2))
        if dim1 == dim2:
          grd2prt_c[ti,dim1,dim2] = np.add(np.subtract(tmp, 2 * exp_c[ti]) * prt_c[ti], grd[ti,dim2] * grdprt_c[ti,dim1])
        else:
          grd2prt_c[ti,dim1,dim2] = grd[ti,dim2] * grdprt_c[ti,dim1]
  '''

  return prt_c, grdprt_c #, grd2prt_c

def sel_shell(unoccmo, hf_occ, num_orb, sel_orb, noe):
  if sel_orb == 'all':
    mat = np.ones((unoccmo))
  else:
    sel_orb = int(sel_orb)
    mat = np.zeros((unoccmo))
    mat[hf_occ-sel_orb:] = np.ones((sel_orb+unoccmo-hf_occ))
  '''  
  for i in range(unoccmo):
    c = 0
    for j in range(unoccmo, num_orb):
      c += 1 / (noe[j] - noe[i])
    mat[i] *= c
  '''
  #mat = mat / (noe[unoccmo] - noe[unoccmo-1])

  print(mat)
  return mat

def calcu_grid(fwfn, grid, sel_orb, hf_occ):

  num_grid = len(grid)

  # load wave functions from h5py
  with h5py.File(fwfn, 'r') as hf:
    typ_orb, num_orb, num_prt, num_nuc = hf['BASIC'][:]
    pos = hf['POS'][:]
    cassign = hf['CASSIGN'][:]
    tassign = hf['TASSIGN'][:]
    exp = hf['EXP'][:]
    noon = hf['NOON'][:]
    noe = hf['NOE'][:]
    nocoef = hf['NOCOEF'][:]

  # assign positions to primitives -> a nested center-type list
  # e.g. [1,1,1,1,2,2,2] [1,1,2,3,1,1,2] -> [[1,1,2,3],[1,1,2]]
  # same with nested center-exponet list
  ct_list = [[] for _ in range(len(set(cassign)))]
  ce_list = [[] for _ in range(len(set(cassign)))]
  #print(len(cassign))
  #print(len(tassign)) 
  for i in range(len(cassign)):
    ct_list[cassign[i]-1].append(tassign[i]-1) # tassign[i]-1 to correspond to quant_fill()
    ce_list[cassign[i]-1].append(exp[i])
  
   
  # fill the type with quantum number -> 35 * [n,m,l]
  quant = quant_fill().astype(int)
  
  # check the nonzero occ
  if np.min(noon) == 0:
    unoccmo = next(list(noon).index(o) for o in list(noon) if o==0)
  else:
    unoccmo = num_orb
  
  # select only the shell
  shell = sel_shell(unoccmo, hf_occ, num_orb, sel_orb, noe)
  
  # calculate for every center(atom) the primitive (dpd: atom)
  prt = np.empty(([0] + [num_grid]), dtype=np.float32) # initiate a matrix with unknown dim of concatenate axis for center(atom)
  grdprt = np.empty(([0,3] + [num_grid]), dtype=np.float32)
  #grd2prt = np.empty(([0,3,3] + [num_grid]))
  for c in range(num_nuc):
    #prt_c, grdprt_c, grd2prt_c = calcu_prt(grid, num_grid, quant, pos[c], ct_list[c], ce_list[c]) # this is primitives per atom
    prt_c, grdprt_c = calcu_prt(grid, num_grid, quant, pos[c], ct_list[c], ce_list[c])
    #prt, grdprt, grd2prt = np.append(prt, prt_c, axis=0), np.append(grdprt, grdprt_c, axis=0), np.append(grd2prt, grd2prt_c, axis=0)
    prt, grdprt = np.append(prt, prt_c, axis=0), np.append(grdprt, grdprt_c, axis=0)

  np.save('prt.npy', prt)
  np.save('grdprt.npy', grdprt)

  print('Finish prt and grdprt calculations!')

  # summation of the primitives based on nocoef and no
  # prt in dimension num_prt * num_grid
  # grdprt in dimension num_prt * 3 * num_grid
  nowfn = np.empty([num_orb] + [num_grid])
  norho = np.empty([unoccmo] + [num_grid])
  nokin = np.empty([unoccmo] + [num_grid])
  nogrdrho = np.empty([unoccmo,3] + [num_grid])
  nogrd2rho = np.empty([unoccmo,3,3] + [num_grid])
  
  # for nonzero occupied no
  for n in range(unoccmo):
    # rho & wfn #
    prtwfn = np.multiply(nocoef[n][:,np.newaxis], prt)
    nowfn[n] = np.sum(prtwfn, axis=0)
    norho[n] = shell[n] * noon[n] * np.power(nowfn[n], 2)
    # grd  #
    grdprtwfn = np.multiply(nocoef[n][:,np.newaxis,np.newaxis], grdprt)
    nogrdrho[n] = 2 * shell[n] * noon[n] * (np.multiply(np.sum(prtwfn, axis=0), np.sum(grdprtwfn, axis=0)))
    # kin #
    nokin[n] = shell[n] * noon[n] * np.sum(np.power(np.sum(grdprtwfn, axis=0), 2), axis=0)
    '''
    # grd2 #
    grd2prtwfn = np.multiply(nocoef[n][:,np.newaxis,np.newaxis,np.newaxis], grd2prt)
    tmp1 = np.multiply(np.sum(prtwfn, axis=0), np.sum(grd2prtwfn, axis=0))
    grdwfn = np.sum(grdprtwfn, axis=0)
    tmp2 = np.empty([3,3] + [num_grid])
    for dim1 in range(3):
      for dim2 in range(3):
        tmp2[dim1,dim2] = grdwfn[dim1] * grdwfn[dim2]
    nogrd2rho[n] = 2 * shell[n] * noon[n] * np.add(tmp1, tmp2)
    '''

  # for zero occupied no
  for n in range(unoccmo, num_orb):
    # wfn #
    prtwfn = np.multiply(nocoef[n][:,np.newaxis], prt)
    nowfn[n] = np.sum(prtwfn, axis=0)
          
  del prtwfn
  del grdprtwfn
  #del grd2prtwfn
  
  rho = np.sum(norho, axis=0)
  del norho
  grdrho = np.sum(nogrdrho, axis=0)
  del nogrdrho
  kin = np.sum(nokin, axis=0)
  del nokin
  grd2rho = np.sum(nogrd2rho, axis=0)
  del nogrd2rho

  
  nrm_grdrho = np.linalg.norm(grdrho, axis=0)

  
  # calculate grd2rho eigen values
  dig_grd2rho = np.empty([num_grid] + [3])
  for i in range(num_grid):
    w, _ = np.linalg.eig(grd2rho[:,:,i])
    dig_grd2rho[i] = np.sort(w)
  
  '''
  rg_grdrho = []
  for i in range(num_grid):     
    rg_grdrho.append(grdrho[:,i])
  rg_grdrho = np.array(rg_grdrho)
  '''
  
  
  # save grid points and their rho, gradrho, grad2rho
  np.save('rho-' + sys.argv[1].split('/')[-1].split('.')[0] + '.npy', rho)
  np.save('nrmgrdrho-' + sys.argv[1].split('/')[-1].split('.')[0] + '.npy', nrm_grdrho)
  np.save('kin-' + sys.argv[1].split('/')[-1].split('.')[0] + '.npy', kin)
  np.save('diglaprho-' + sys.argv[1].split('/')[-1].split('.')[0] + '.npy', dig_grd2rho)

  np.savetxt('rho.dat', rho)
  np.savetxt('nrmgrdrho.dat', nrm_grdrho)
  np.savetxt('kin.dat', kin)
  np.savetxt('diglaprho.dat', dig_grd2rho)

  # plot 3d scattering of grid
  # plot3d(rg_grid)

  return rho, nowfn.T


def main():  
  fwfn = sys.argv[1]
  fgrid = sys.argv[2]
  fwgt = sys.argv[3]
  sel_orb = sys.argv[4]
  hf_occ = int(sys.argv[5]) # in casci cases, we need to know lumo from hf occupation

  with h5py.File(fwfn, 'r') as hf:
    _, _, _, num_nuc = hf['BASIC'][:]

  # load grids and weights
  grid = np.load(fgrid)
  wgt = np.load(fwgt)

  # calculate rho grad lap on each grid
  rho, wfn = calcu_grid(fwfn, grid, sel_orb, hf_occ)

  # sum up all of them based on weight
  wrho = np.sum(np.multiply(wgt, rho))
    
  print('Integrated rho of of system' + ': ' + str(wrho))

  # save grid points and their NO wfn
  np.savez(sys.argv[1].split('/')[-1].split('.')[0] + '.npz', GRID=grid, WFN=wfn, WGT=wgt)

if __name__ == "__main__":
  main()