1 2 3# Evaluate GTO's starting from a symbolic representation 4# see qmc_algorithms/Wavefunctions/GaussianOrbitals.ipynb 5 6from sympy import * 7from collections import namedtuple, defaultdict 8import numpy as np 9import pdb 10 11CG_basis = namedtuple('CG_basis',['orbtype','nbasis','zeta','contraction_coeff']) 12 13class GTO: 14 def __init__(self, basis=None, center=[0.0, 0.0, 0.0]): 15 x,y,z = symbols('x y z') 16 alpha = Symbol('alpha', positive=True, real=True) 17 r = Symbol('r',real=True,nonnegative=True) 18 i,j,k = symbols('i j k',integer=True) 19 N = Symbol('N') 20 self.N = N 21 norm1 = factorial(i)*factorial(j)*factorial(k) 22 norm2 = factorial(2*i)*factorial(2*j)*factorial(2*k) 23 norm_sym = (2*alpha/pi)**(3/S(4)) * sqrt((8*alpha)**(i+j+k)*norm1/norm2) 24 25 gto_sym_raw = N * x**i * y**j * z**k * exp(-alpha *r**2) 26 27 gto_sym = gto_sym_raw.subs(N, norm_sym) 28 29 self.alpha = alpha 30 self.x = x; self.y = y; self.z = z 31 self.r = r 32 self.i = i; self.j = j; self.k = k 33 self.gto_sym = gto_sym 34 self.compute_grad() 35 36 self.ijk = get_ijk_by_type() 37 38 self.basis = basis 39 self.center = center 40 41 42 def compute_grad(self): 43 r2 = self.x**2 + self.y**2 + self.z**2 44 gto_xyz = self.gto_sym.subs(self.r**2, r2) 45 #print gto_xyz 46 self.grad = [0]*3 47 self.grad[0] = diff(gto_xyz, self.x).subs(r2, self.r**2) 48 self.grad[1] = diff(gto_xyz, self.y).subs(r2, self.r**2) 49 self.grad[2] = diff(gto_xyz, self.z).subs(r2, self.r**2) 50 lap = diff(gto_xyz, self.x, 2) + \ 51 diff(gto_xyz, self.y, 2) + \ 52 diff(gto_xyz, self.z, 2) 53 # Need to expand to avoid NaN's 54 self.lap = expand(lap.subs(r2, self.r**2)) 55 56 self.hess = [0]*6 57 self.hess[0] = diff(diff(gto_xyz,self.x),self.x).subs(r2,self.r**2) 58 self.hess[1] = diff(diff(gto_xyz,self.x),self.y).subs(r2,self.r**2) 59 self.hess[2] = diff(diff(gto_xyz,self.x),self.z).subs(r2,self.r**2) 60 self.hess[3] = diff(diff(gto_xyz,self.y),self.y).subs(r2,self.r**2) 61 self.hess[4] = diff(diff(gto_xyz,self.y),self.z).subs(r2,self.r**2) 62 self.hess[5] = diff(diff(gto_xyz,self.z),self.z).subs(r2,self.r**2) 63 64 self.ghess = [0]*10 65 self.ghess[0] = diff(diff(diff(gto_xyz,self.x),self.x),self.x).subs(r2,self.r**2) 66 self.ghess[1] = diff(diff(diff(gto_xyz,self.x),self.x),self.y).subs(r2,self.r**2) 67 self.ghess[2] = diff(diff(diff(gto_xyz,self.x),self.x),self.z).subs(r2,self.r**2) 68 self.ghess[3] = diff(diff(diff(gto_xyz,self.x),self.y),self.y).subs(r2,self.r**2) 69 self.ghess[4] = diff(diff(diff(gto_xyz,self.x),self.y),self.z).subs(r2,self.r**2) 70 self.ghess[5] = diff(diff(diff(gto_xyz,self.x),self.z),self.z).subs(r2,self.r**2) 71 self.ghess[6] = diff(diff(diff(gto_xyz,self.y),self.y),self.y).subs(r2,self.r**2) 72 self.ghess[7] = diff(diff(diff(gto_xyz,self.y),self.y),self.z).subs(r2,self.r**2) 73 self.ghess[8] = diff(diff(diff(gto_xyz,self.y),self.z),self.z).subs(r2,self.r**2) 74 self.ghess[9] = diff(diff(diff(gto_xyz,self.z),self.z),self.z).subs(r2,self.r**2) 75 76 77 def set_basis(self, basis): 78 self.basis = basis 79 80 def make_subs_list(self, i, j, k, x, y, z, alpha): 81 subs_list = {self.i:i, self.j:j, self.k:k} 82 r2 = x**2 + y**2 + z**2 83 r_val = sqrt(r2) 84 subs_list[self.r] = r_val 85 subs_list[self.r**2] = r2 86 subs_list[self.alpha] = alpha 87 subs_list[self.x] = x 88 subs_list[self.y] = y 89 subs_list[self.z] = z 90 return subs_list 91 def make_subs_ijk_list(self,i,j,k): 92 subs_list = {self.i:i, self.j:j, self.k:k} 93 return subs_list 94 95 def eval_single_v(self, i, j, k, x, y, z, alpha): 96 xc = x - self.center[0] 97 yc = y - self.center[1] 98 zc = z - self.center[2] 99 sl1 = self.make_subs_list(i,j,k,xc,yc,zc,alpha) 100 v = self.gto_sym.subs(sl1).evalf() 101 return v 102 103 def eval_single_vgl(self, i, j, k, x, y, z, alpha): 104 xc = x - self.center[0] 105 yc = y - self.center[1] 106 zc = z - self.center[2] 107 sl1 = self.make_subs_list(i,j,k,xc,yc,zc,alpha) 108 v = self.gto_sym.subs(sl1).evalf() 109 g = [grad.subs(sl1).evalf() for grad in self.grad] 110 lap = self.lap.subs(sl1).evalf() 111 return v,g,lap 112 113 def eval_single_vgh(self, i, j, k, x, y, z, alpha): 114 xc = x - self.center[0] 115 yc = y - self.center[1] 116 zc = z - self.center[2] 117 #This is to deal with the sympy "feature" encountered, explained below... 118 expsub=self.make_subs_ijk_list(i,j,k) 119 sl1 = self.make_subs_list(i,j,k,xc,yc,zc,alpha) 120 v = self.gto_sym.subs(sl1).evalf() 121 g = [grad.subs(sl1).evalf() for grad in self.grad] 122 #Since we are taking derivatives of x^i*y^j*z^k, derivaties of the GTO basis functions 123 #will reduce the exponents on the cartesian tensor terms. Depending on how sympy 124 #tries to evaluate the terms, it can end up trying to evaluate things like y^(j-1). If 125 #j=0 and y=0; this will results in nan or inf, even though the properly evaluated term will have 126 #compensating terms to cancel out this divergence. 127 # 128 #Thus, we evaluate the expression with i,j,k specified. Then we simplify this expression, allowing divergent terms 129 #to be taken care of. Then we do the normal subs(sl1).evalf(). 130 h = [simplify(hess.subs(expsub)).subs(sl1).evalf() for hess in self.hess] 131 return v,g,h 132 133 def eval_single_gradhess(self, i, j, k, x, y, z, alpha): 134 xc = x - self.center[0] 135 yc = y - self.center[1] 136 zc = z - self.center[2] 137 sl1 = self.make_subs_list(i,j,k,xc,yc,zc,alpha) 138 #see eval_single_vgh for why this trick is employed. 139 expsub=self.make_subs_ijk_list(i,j,k) 140 gh = [simplify(ghess.subs(expsub)).subs(sl1).evalf() for ghess in self.ghess] 141 return gh 142 143 def eval_contraction_v(self, x, y, z, basis): 144 vals = [] 145 # Expand each basis type by the angular momentum state 146 angular_list = self.ijk[basis.orbtype] 147 for i,j,k,name in angular_list: 148 val = 0.0 149 for idx in range(basis.nbasis): 150 val += basis.contraction_coeff[idx] * self.eval_single_v(i,j,k,x,y,z,basis.zeta[idx]) 151 vals.append(val) 152 153 return vals 154 155 def eval_contraction_vgl(self, x, y, z, basis): 156 vals = [] 157 grads = [] 158 laps = [] 159 160 angular_list = self.ijk[basis.orbtype] 161 for i,j,k,name in angular_list: 162 val = 0.0 163 grad = [0.0, 0.0, 0.0] 164 lap = 0.0 165 for idx in range(basis.nbasis): 166 c = basis.contraction_coeff[idx] 167 v,g,l = self.eval_single_vgl(i,j,k,x,y,z,basis.zeta[idx]) 168 val += c*v 169 lap += c*l 170 grad = [c*g[m] + grad[m] for m in range(3)] 171 vals.append(val) 172 grads.append(grad) 173 laps.append(lap) 174 return vals, grads, laps 175 176 def eval_contraction_vgh(self, x, y, z, basis): 177 vals = [] 178 grads = [] 179 hesses = [] 180 181 angular_list = self.ijk[basis.orbtype] 182 for i,j,k,name in angular_list: 183 val = 0.0 184 grad = [0.0, 0.0, 0.0] 185 hess = [0.0,0.0,0.0,0.0,0.0,0.0] 186 for idx in range(basis.nbasis): 187 c = basis.contraction_coeff[idx] 188 v,g,h = self.eval_single_vgh(i,j,k,x,y,z,basis.zeta[idx]) 189 val += c*v 190 grad = [c*g[m] + grad[m] for m in range(3)] 191 hess = [c*h[m] + hess[m] for m in range(6)] 192 vals.append(val) 193 grads.append(grad) 194 hesses.append(hess) 195 return vals, grads, hesses 196 197 def eval_contraction_gradhess(self, x, y, z, basis): 198 gradhesses = [] 199 angular_list = self.ijk[basis.orbtype] 200 for i,j,k,name in angular_list: 201 ghess = [0.0 for m in range(10)] 202 for idx in range(basis.nbasis): 203 c = basis.contraction_coeff[idx] 204 gh = self.eval_single_gradhess(i,j,k,x,y,z,basis.zeta[idx]) 205 ghess = [c*gh[m] + ghess[m] for m in range(10)] 206 gradhesses.append(ghess) 207 return gradhesses 208 209 def eval_v(self, x, y, z): 210 vs = [] 211 for basis in self.basis: 212 v = self.eval_contraction_v(x, y, z, basis) 213 vs.extend(v) 214 return vs 215 216 def eval_vgl(self, x, y, z): 217 vs = [] 218 grads = [] 219 lapls = [] 220 for basis in self.basis: 221 v,g,l = self.eval_contraction_vgl(x, y, z, basis) 222 vs.extend(v) 223 grads.extend(g) 224 lapls.extend(l) 225 return vs, grads, lapls 226 227 def eval_vgh(self, x, y, z): 228 vs = [] 229 grads = [] 230 hesses = [] 231 for basis in self.basis: 232 v,g,hess = self.eval_contraction_vgh(x, y, z, basis) 233 vs.extend(v) 234 grads.extend(g) 235 hesses.extend(hess) 236 return vs, grads, hesses 237 238 def eval_gradhess(self,x,y,z): 239 gradhesses = [] 240 for basis in self.basis: 241 ghess = self.eval_contraction_gradhess(x, y, z, basis) 242 gradhesses.extend(ghess) 243 return gradhesses 244 245# generated from qmcpack src/Numerics/codegen/read_order.py 246# Only part of the function included for now 247def get_ijk(): 248 ijk = [] 249 # S 250 ijk.append( (0,0,0,"S") ) 251 # P 252 ijk.append( (1,0,0,"X") ) 253 ijk.append( (0,1,0,"Y") ) 254 ijk.append( (0,0,1,"Z") ) 255 # D 256 ijk.append( (2,0,0,"XX") ) 257 ijk.append( (0,2,0,"YY") ) 258 ijk.append( (0,0,2,"ZZ") ) 259 ijk.append( (1,1,0,"XY") ) 260 ijk.append( (1,0,1,"XZ") ) 261 ijk.append( (0,1,1,"YZ") ) 262 263 return ijk 264 265def get_ijk_by_type(): 266 ijk_list = get_ijk() 267 268 by_type = defaultdict(list) 269 for i,j,k,name in ijk_list: 270 L = i + j + k 271 by_type[L].append((i,j,k,name)) 272 273 return by_type 274 275def get_ijk_inverse_index(basis_set): 276 ijk_list = get_ijk_by_type() 277 by_index = list() 278 for basis in basis_set: 279 angular_list = ijk_list[basis.orbtype] 280 for angular_info in angular_list: 281 by_index.append( (basis, angular_info) ) 282 return by_index 283 284 285 286# Collection of atoms with different types and positions 287class GTO_centers: 288 def __init__(self, pos_list, elements, basis_sets): 289 self.gto_list = [] 290 for pos, element in zip(pos_list, elements): 291 gto = GTO(basis_sets[element], pos) 292 self.gto_list.append(gto) 293 294 def eval_v(self, x, y, z): 295 vs = [] 296 for gto in self.gto_list: 297 v = gto.eval_v(x, y, z) 298 vs.extend(v) 299 return vs 300 301 def eval_vgl(self, x, y, z): 302 vs = [] 303 grads = [] 304 laps = [] 305 for gto in self.gto_list: 306 v,g,l = gto.eval_vgl(x,y,z) 307 vs.extend(v) 308 grads.extend(g) 309 laps.extend(l) 310 311 return vs, grads, laps 312 313 def eval_vgh(self, x, y, z): 314 vs = [] 315 grads = [] 316 hesses = [] 317 for gto in self.gto_list: 318 v,g,h = gto.eval_vgh(x,y,z) 319 vs.extend(v) 320 grads.extend(g) 321 hesses.extend(h) 322 323 return vs, grads, hesses 324 325 def eval_gradhess(self, x, y, z): 326 ghesses = [] 327 count=0 328 for gto in self.gto_list: 329 gh = gto.eval_gradhess(x,y,z) 330 ghesses.extend(gh) 331 return ghesses 332 333def get_center_and_ijk_by_index(pos_list, elements, basis_sets): 334 index = [] 335 for pos_idx, (pos, element) in enumerate(zip(pos_list, elements)): 336 index_for_one = get_ijk_inverse_index(basis_sets[element]) 337 for basis,angular_info in index_for_one: 338 index.append((pos_idx, basis, angular_info)) 339 return index 340 341class MolecularOrbital: 342 def __init__(self, gto, MO_matrix): 343 self.gto = gto 344 self.MO_matrix = MO_matrix 345 346 def eval_v(self, x, y, z): 347 ao_vals = self.gto.eval_v(x, y, z) 348 mo_vals = np.dot(self.MO_matrix, ao_vals) 349 return mo_vals 350 351 def eval_v_one_MO(self, x, y, z, mo_idx): 352 ao_vals = self.gto.eval_v(x, y, z) 353 mo_val = np.dot(self.MO_matrix[mo_idx, :], ao_vals) 354 return mo_val 355 356 def eval_vgl(self, x, y, z): 357 ao_v, ao_g, ao_l = self.gto.eval_vgl(x, y, z) 358 mo_v = np.dot(self.MO_matrix, ao_v) 359 mo_g = np.dot(self.MO_matrix, ao_g) 360 mo_l = np.dot(self.MO_matrix, ao_l) 361 362 return mo_v, mo_g, mo_l 363 364 def eval_vgl_one_MO(self, x, y, z, mo_idx): 365 ao_vals, ao_g, ao_l = self.gto.eval_vgl(x, y, z) 366 mo_val = np.dot(self.MO_matrix[mo_idx, :], ao_vals) 367 mo_g = np.dot(self.MO_matrix[mo_idx, :], ao_g) 368 mo_l = np.dot(self.MO_matrix[mo_idx, :], ao_l) 369 return mo_val, mo_g, mo_l 370 371 372 373if __name__ == '__main__': 374 gto = GTO() 375