Source code for pynao.prod_log

from __future__ import print_function, division
import numpy as np
from numpy import einsum
from pynao.m_dipole_ni import dipole_ni
from pynao.m_overlap_ni import overlap_ni
from pynao.m_overlap_am import overlap_am
from pynao import ao_matelem_c
from pynao.ao_log import ao_log


[docs]def overlap_check(prod_log, overlap_funct=overlap_ni, **kvargs): """ Computes the allclose(), mean absolute error and maximal error of the overlap reproduced by the (local) vertex. """ from pynao.m_ao_matelem import ao_matelem_c me = ao_matelem_c(prod_log.rr, prod_log.pp).init_one_set(prod_log.ao_log) sp2mom0,sp2mom1 = prod_log.comp_moments() mael,mxel,acl=[],[],[] R0 = np.array([0.0,0.0,0.0]) for sp, [vertex,mom0] in enumerate(zip(prod_log.sp2vertex, sp2mom0)): oo_ref = overlap_funct(me,sp,R0,sp,R0,**kvargs) oo = np.einsum('pjk,p->jk', vertex, mom0) ac = np.allclose(oo_ref, oo, atol=prod_log.tol_loc*10, rtol=prod_log.tol_loc) mae = abs(oo_ref-oo).sum()/oo.size mxe = abs(oo_ref-oo).max() acl.append(ac); mael.append(mae); mxel.append(mxe) if not ac: print('overlap check:', sp, mae, mxe, prod_log.tol_loc) return mael, mxel, acl
[docs]def dipole_check(sv, prod_log, dipole_funct=dipole_ni, **kvargs): """ Computes the allclose(), mean absolute error and maximal error of the dipoles reproduced by the (local) vertex. """ from pynao.m_ao_matelem import ao_matelem_c from pynao.m_ao_log import comp_moments me = ao_matelem_c(prod_log.ao_log.rr, prod_log.ao_log.pp) me.init_one_set(prod_log.ao_log) sp2mom0,sp2mom1 = comp_moments(prod_log) mael,mxel,acl=[],[],[] for atm, [sp, coord] in enumerate(zip(sv.atom2sp, sv.atom2coord)): dip_moms = np.einsum('j,k->jk', sp2mom0[sp],coord)+sp2mom1[sp] koo2dipme = np.einsum('pab,pc->cab', prod_log.sp2vertex[sp],dip_moms) dipme_ref = dipole_funct(me,sp,coord,sp,coord, **kvargs) ac = np.allclose(dipme_ref, koo2dipme, atol=prod_log.tol_loc*10, rtol=prod_log.tol_loc) mae = abs(koo2dipme-dipme_ref).sum()/koo2dipme.size mxe = abs(koo2dipme-dipme_ref).max() acl.append(ac); mael.append(mae); mxel.append(mxe) if not ac: print('dipole check:', sp, mae, mxe, prod_log.tol_loc) return mael,mxel,acl
[docs]class prod_log(ao_log): """prod_log object. Holder of (local) product functions and vertices. Input parameters: ao_log: ao_log class (pynao.ao_log) or pyscf Mole object (pyscf.gto.mole) holder of the numerical orbitals tol: float tolerance to keep the linear combinations Output parameters: For each specie returns a set of radial functions defining a product basis. These functions are sufficient to represent the products of original atomic orbitals via a product vertex coefficients. """ def __init__(self, **kw): if 'auxmol' in kw: self.init_prod_log_df(**kw) elif 'ao_log' in kw: self.init_prod_log_dp(**kw) else: print(__name__, kw.keys()) raise RuntimeError('unknown init method')
[docs] def init_prod_log_df(self, **kw): """ Initializes the radial functions from pyscf """ from pyscf.df.incore import aux_e2 sv = kw['nao'] ao_log.__init__(self, ao_log=sv.ao_log) auxmol = self.auxmol = kw['auxmol'] rcut_tol = kw['rcut_tol'] if 'rcut_tol' in kw else 1e-7 self.init_ao_log_gto(gto=auxmol, **kw) # j3c = aux_e2(sv.mol, auxmol, intor='cint3c2e_sph', aosym='s1') # nao = sv.mol.nao_nr() # naoaux = auxmol.nao_nr() # j3c = j3c.reshape(nao,nao,naoaux) # print(nao, naoaux) return self
[docs] def init_prod_log_dp(self, **kw): """ Builds linear combinations of the original orbital products """ from scipy.sparse import csr_matrix from pynao.m_local_vertex import local_vertex_c ao_log.__init__(self, **kw) self.ao_log = kw['ao_log'] tol_loc = self.tol_loc = kw['tol_loc'] self.sp2nmult = np.zeros((self.nspecies), dtype=np.int64) # constructor of local vertices lvc = local_vertex_c(kw['ao_log']) # radial orbitals: list of numpy arrays self.psi_log = [] # radial orbitals times r**j: list of numpy arrays self.psi_log_rl = [] # list of numpy arrays containing the maximal radii self.sp_mu2rcut = [] # list of numpy arrays containing the angular momentum of the radial function self.sp_mu2j = [] # list of numpy arrays containing the starting index for each radial multiplett self.sp_mu2s = [] # list of numpy arrays containing the vertex coefficients <mu,a,b> self.sp2vertex = [] # list of numpy arrays containing the inverse vertex coefficients # <p,a,b> L^p_ab defined by F^p(r) = L^p_ab f^a(r) f^b(r) self.sp2lambda = [] # going to be list of sparse matrices with dimension (nprod,norbs**2) or # <mu,ab> . This is a derivative of the sp2vertex self.sp2vertex_csr = [] # this is a future list of matrices (<mu|ab><ab|nu>)^-1. This is a # derivative of the sp2vertex self.sp2inv_vv = [] # number of orbitals per specie self.sp2norbs = np.zeros(self.nspecies, dtype=int) for sp, no in enumerate(lvc.ao1.sp2norbs): ldp = lvc.get_local_vertex(sp) mu2jd = [] for j, evs in enumerate(ldp['j2eva']): for domi, ev in enumerate(evs): if ev>tol_loc: mu2jd.append([j,domi]) nmult=len(mu2jd) mu2j = np.array([jd[0] for jd in mu2jd], dtype=np.int32) mu2s = np.array([0] + [sum(2*mu2j[0:mu+1]+1) for mu in range(nmult)], dtype=np.int64) mu2rcut = np.array([self.ao_log.sp2rcut[sp]]*nmult, dtype=np.float64) self.sp2nmult[sp]=nmult self.sp_mu2j.append(mu2j) self.sp_mu2rcut.append(mu2rcut) self.sp_mu2s.append(mu2s) self.sp2norbs[sp] = mu2s[-1] mu2ff = np.zeros((nmult, lvc.nr)) for mu,[j,domi] in enumerate(mu2jd): mu2ff[mu,:] = ldp['j2xff'][j][domi,:] self.psi_log.append(mu2ff) mu2ff = np.zeros((nmult, lvc.nr)) for mu, [j, domi] in enumerate(mu2jd): mu2ff[mu,:] = ldp['j2xff'][j][domi,:]/lvc.rr**j self.psi_log_rl.append(mu2ff) npf= sum(2*mu2j+1) # count number of product functions mu2ww = np.zeros((npf,no,no)) for [j, domi], s in zip(mu2jd, mu2s): mu2ww[s:s+2*j+1,:,:] = ldp['j2xww'][j][domi,0:2*j+1,:,:] self.sp2vertex.append(mu2ww) self.sp2vertex_csr.append(csr_matrix(mu2ww.reshape([npf,no**2]))) v_csr = self.sp2vertex_csr[sp] self.sp2inv_vv.append(np.linalg.inv((v_csr*v_csr.transpose()).todense())) #mu2iww = np.zeros((npf,no,no)) # rigorous way of doing it (but does not work) #for [j,domi],s in zip(mu2jd,mu2s): mu2iww[s:s+2*j+1,:,:] = ldp['j2xww_inv'][j][domi,0:2*j+1,:,:] #self.sp2lambda.append(mu2iww) # lazy way of finding lambda mu2iww = np.array(self.sp2inv_vv[sp]*self.sp2vertex_csr[sp]).reshape([npf,no,no]) self.sp2lambda.append(mu2iww) self.jmx = np.amax(np.array( [max(mu2j) for mu2j in self.sp_mu2j], dtype=np.int32)) self.sp2rcut = np.array([np.amax(rcuts) for rcuts in self.sp_mu2rcut]) # Not necessary, Python should get rid og the temporary variables # at the end of the function call # del v_csr, mu2iww, mu2ww, mu2ff # maybe unnecessary return self
[docs] def load_prod_log_dp(self, ao_log, sp2charge, tol_loc=1e-5, fname="local2functs_vertex.hdf5"): """ Builds linear combinations of the original orbital products Testing the calculations using same kernel than from fortran """ from scipy.sparse import csr_matrix from pynao.m_local_vertex import local_vertex_c import h5py self.ao_log = ao_log self.init_log_mesh(ao_log.rr, ao_log.pp) self.nspecies = ao_log.nspecies self.tol_loc = tol_loc self.rr,self.pp,self.nr = ao_log.rr,ao_log.pp,ao_log.nr self.interp_rr = ao_log.interp_rr self.sp2nmult = np.zeros((ao_log.nspecies), dtype=np.int64) # constructor of local vertices lvc = local_vertex_c(ao_log) # radial orbitals: list of numpy arrays self.psi_log = [] # radial orbitals times r**j: list of numpy arrays self.psi_log_rl = [] # list of numpy arrays containing the maximal radii self.sp_mu2rcut = [] # list of numpy arrays containing the angular momentum of the radial function self.sp_mu2j = [] # list of numpy arrays containing the starting index for each radial multiplett self.sp_mu2s = [] # list of numpy arrays containing the vertex coefficients <mu,a,b> self.sp2vertex = [] # list of numpy arrays containing the inverse vertex coefficients # <p,a,b> L^p_ab defined by F^p(r) = L^p_ab f^a(r) f^b(r) self.sp2lambda = [] # going to be list of sparse matrices with dimension (nprod,norbs**2) or # <mu,ab> . This is a derivative of the sp2vertex self.sp2vertex_csr = [] # this is a future list of matrices (<mu|ab><ab|nu>)^-1. This is a # derivative of the sp2vertex self.sp2inv_vv = [] # number of orbitals per specie self.sp2norbs = np.zeros(self.nspecies, dtype=int) # copy of nuclear charges from atomic orbitals self.sp2charge = ao_log.sp2charge print(__name__, "load prod log from: ", fname) File = h5py.File(fname, "r") for isp, atm_nb in enumerate(sp2charge): val = File["specie_{0}".format(atm_nb)] mu2j = val["sp_local2functs/mu2j"].value nmult = mu2j.shape[0] mu2s = np.array([0]+[sum(2*mu2j[0:mu+1]+1) for mu in range(nmult)], dtype=np.int64) mu2rcut = np.array([val["sp_local2functs/rcut"].value[0]]*nmult, dtype=np.float64) self.sp2nmult[isp]=nmult self.sp_mu2j.append(mu2j) self.sp_mu2rcut.append(mu2rcut) self.sp_mu2s.append(mu2s) self.sp2norbs[isp] = mu2s[-1] self.psi_log.append(val["sp_local2functs/ir_mu2v"].value) mu2ww = val["vertex"].value no = mu2ww.shape[1] npf= sum(2*mu2j+1) # count number of product functions if npf != mu2ww.shape[0]: raise ValueError("mismatch npf and mu2ww.shape[0]") self.sp2vertex.append(mu2ww) self.sp2vertex_csr.append(csr_matrix(mu2ww.reshape([npf,no**2]))) v_csr = self.sp2vertex_csr[isp] self.sp2inv_vv.append( np.linalg.inv((v_csr*v_csr.transpose()).todense())) # lazy way of finding lambda mu2iww = np.array(self.sp2inv_vv[isp]*self.sp2vertex_csr[isp]).reshape([npf,no,no]) self.sp2lambda.append(mu2iww) self.psi_log_rl = [] for mu2ff, mu2j in zip(self.psi_log, self.sp_mu2j): mu2ff_rl = np.zeros_like(mu2ff) for mu,(ff,j) in enumerate(zip(mu2ff,mu2j)): mu2ff_rl[mu,:] = ff/self.rr**j self.psi_log_rl.append(mu2ff_rl) self.jmx = np.amax(np.array( [max(mu2j) for mu2j in self.sp_mu2j], dtype=np.int32)) self.sp2rcut = np.array([np.amax(rcuts) for rcuts in self.sp_mu2rcut]) return self
[docs] def overlap_check(self, overlap_funct=overlap_ni, **kvargs): """ Recompute the overlap between orbitals using the product vertex and scalar moments of product functions """ return overlap_check(self, overlap_funct=overlap_ni, **kvargs)
[docs] def hartree_pot(self, **kvargs): """ Compute Hartree potential of the radial orbitals and return another ao_log_c storage with these potentials. """ from pynao.m_ao_log_hartree import ao_log_hartree as ext return ext(self, **kvargs)
[docs] def lambda_check_coulomb(self): """ Check the equality (p|q)<q,cd> = [p,ab] <ab|q>(q|r)<r|cd> """ me = ao_matelem_c(self) mael,mxel=[],[] for sp,[pab,lab] in enumerate(zip(self.sp2vertex, self.sp2lambda)): pq = me.coulomb_am(sp,np.zeros(3), sp, np.zeros(3)) pcd_ref = einsum('pq,qab->pab', pq, pab) abcd = einsum('abq,qcd->abcd', einsum('pab,pq->abq', pab,pq), pab) pcd = einsum('lab,abcd->lcd', lab, abcd) mael.append(abs(pcd-pcd_ref).sum()/pcd.size); mxel.append(abs(pcd-pcd_ref).max()) return mael,mxel
[docs] def lambda_check_overlap(self, overlap_funct=overlap_am, **kvargs): """ Check the equality (p) = [p,ab] S^ab, i.e. scalar moments are recomputed with inversed vertex from the ao's overlap """ from pynao.m_ao_log import comp_moments me = ao_matelem_c(self.ao_log) sp2mom0,sp2mom1 = comp_moments(self) mael,mxel=[],[] for sp, [lab, mom0_ref] in enumerate(zip(self.sp2lambda, sp2mom0)): ab = overlap_funct(me,sp,np.zeros(3),sp,np.zeros(3),**kvargs) mom0 = einsum('lab,ab->l', lab,ab) mael.append((abs(mom0-mom0_ref)).sum()/mom0.size); mxel.append((abs(mom0-mom0_ref)).max()) return mael,mxel
if __name__=='__main__': from pynao.m_system_vars import system_vars_c from pynao.m_prod_log import prod_log as prod_log_c import matplotlib.pyplot as plt sv = system_vars_c(label='siesta') prod_log = prod_log_c(sv.ao_log, tol_loc=1e-4) print(dir(prod_log)) print(prod_log.sp2nmult, prod_log.sp2norbs) print(prod_log.overlap_check()) print(prod_log.lambda_check_coulomb()) sp = 0 for mu,[ff,j] in enumerate(zip(prod_log.psi_log[sp], prod_log.sp_mu2j[sp])): if j==0: plt.plot(prod_log.rr, ff/abs(ff).max(), "--", label=str(mu)+" j="+str(j)) else: plt.plot(prod_log.rr, ff/abs(ff).max(), label=str(mu)+" j="+str(j)) plt.legend() plt.xlim(0.0,6.0) plt.show()