def calculate_exx_integrals(self): # Find core states: core = [] lmax = 0 for l, ch in enumerate(self.aea.channels): for n, phi_g in enumerate(ch.phi_ng): if l >= len(self.waves_l) or (l < len(self.waves_l) and n + l + 1 not in self.waves_l[l].n_n): core.append((l, phi_g)) if l > lmax: lmax = l lmax = max(lmax, len(self.waves_l) - 1) G_LLL = make_gaunt(lmax) # Calculate core contribution to EXX energy: self.exxcc = 0.0 j1 = 0 for l1, phi1_g in core: f = 1.0 for l2, phi2_g in core[j1:]: n_g = phi1_g * phi2_g for l in range((l1 + l2) % 2, l1 + l2 + 1, 2): G = (G_LLL[l1 ** 2 : (l1 + 1) ** 2, l2 ** 2 : (l2 + 1) ** 2, l ** 2 : (l + 1) ** 2] ** 2).sum() vr_g = self.rgd.poisson(n_g, l) e = f * self.rgd.integrate(vr_g * n_g, -1) / 4 / pi self.exxcc -= e * G f = 2.0 j1 += 1 self.log("EXX (core-core):", self.exxcc, "Hartree") # Calculate core-valence contribution to EXX energy: nj = sum(len(waves) for waves in self.waves_l) ni = sum(len(waves) * (2 * l + 1) for l, waves in enumerate(self.waves_l)) self.exxcv_ii = np.zeros((ni, ni)) i1 = 0 for l1, waves1 in enumerate(self.waves_l): for phi1_g in waves1.phi_ng: i2 = 0 for l2, waves2 in enumerate(self.waves_l): for phi2_g in waves2.phi_ng: X_mm = self.exxcv_ii[i1 : i1 + 2 * l1 + 1, i2 : i2 + 2 * l2 + 1] if (l1 + l2) % 2 == 0: for lc, phi_g in core: n_g = phi1_g * phi_g for l in range((l1 + lc) % 2, max(l1, l2) + lc + 1, 2): vr_g = self.rgd.poisson(phi2_g * phi_g, l) e = self.rgd.integrate(vr_g * n_g, -1) / (4 * pi) for mc in range(2 * lc + 1): for m in range(2 * l + 1): G_L = G_LLL[:, lc ** 2 + mc, l ** 2 + m] X_mm += ( np.outer(G_L[l1 ** 2 : (l1 + 1) ** 2], G_L[l2 ** 2 : (l2 + 1) ** 2]) * e ) i2 += 2 * l2 + 1 i1 += 2 * l1 + 1
def calculate_exx(self, s=None): if s is None: self.exx = sum(self.calculate_exx(s) for s in range(self.nspins)) / self.nspins return self.exx states = [] lmax = 0 for ch in self.channels: l = ch.l for n, phi_g in enumerate(ch.phi_ng): f = ch.f_n[n] if f > 0 and ch.s == s: states.append((l, f * self.nspins / 2.0 / (2 * l + 1), phi_g)) if l > lmax: lmax = l G_LLL = make_gaunt(lmax) exx = 0.0 j1 = 0 for l1, f1, phi1_g in states: f = 1.0 for l2, f2, phi2_g in states[j1:]: n_g = phi1_g * phi2_g for l in range((l1 + l2) % 2, l1 + l2 + 1, 2): G = (G_LLL[l1**2:(l1 + 1)**2, l2**2:(l2 + 1)**2, l**2:(l + 1)**2]**2).sum() vr_g = self.rgd.poisson(n_g, l) e = f * self.rgd.integrate(vr_g * n_g, -1) / 4 / pi exx -= e * G * f1 * f2 f = 2.0 j1 += 1 return exx
def calculate_exx(self, s=None): if s is None: self.exx = sum(self.calculate_exx(s) for s in range(self.nspins)) / self.nspins return self.exx states = [] lmax = 0 for ch in self.channels: l = ch.l for n, phi_g in enumerate(ch.phi_ng): f = ch.f_n[n] if f > 0 and ch.s == s: states.append( (l, f * self.nspins / 2.0 / (2 * l + 1), phi_g)) if l > lmax: lmax = l G_LLL = make_gaunt(lmax) exx = 0.0 j1 = 0 for l1, f1, phi1_g in states: f = 1.0 for l2, f2, phi2_g in states[j1:]: n_g = phi1_g * phi2_g for l in range((l1 + l2) % 2, l1 + l2 + 1, 2): G = (G_LLL[l1**2:(l1 + 1)**2, l2**2:(l2 + 1)**2, l**2:(l + 1)**2]**2).sum() vr_g = self.rgd.poisson(n_g, l) e = f * self.rgd.integrate(vr_g * n_g, -1) / 4 / pi exx -= e * G * f1 * f2 f = 2.0 j1 += 1 return exx
def constructX(gen): """Construct the X_p^a matrix for the given atom. The X_p^a matrix describes the valence-core interactions of the partial waves. """ # initialize attributes uv_j = gen.vu_j # soft valence states * r: lv_j = gen.vl_j # their repective l quantum numbers Nvi = 0 for l in lv_j: Nvi += 2 * l + 1 # total number of valence states (including m) # number of core and valence orbitals (j only, i.e. not m-number) Njcore = gen.njcore Njval = len(lv_j) # core states * r: uc_j = gen.u_j[:Njcore] r, dr, N, beta = gen.r, gen.dr, gen.N, gen.beta # potential times radius vr = np.zeros(N) # initialize X_ii matrix X_ii = np.zeros((Nvi, Nvi)) # make gaunt coeff. list lmax = max(gen.l_j[:Njcore] + gen.vl_j) gaunt = make_gaunt(lmax=lmax) # sum over core states for jc in range(Njcore): lc = gen.l_j[jc] # sum over first valence state index i1 = 0 for jv1 in range(Njval): lv1 = lv_j[jv1] # electron density 1 times radius times length element n1c = uv_j[jv1] * uc_j[jc] * dr n1c[1:] /= r[1:] # sum over second valence state index i2 = 0 for jv2 in range(Njval): lv2 = lv_j[jv2] # electron density 2 n2c = uv_j[jv2] * uc_j[jc] * dr n2c[1:] /= r[1:] # sum expansion in angular momenta for l in range(min(lv1, lv2) + lc + 1): # Int density * potential * r^2 * dr: hartree(l, n2c, beta, N, vr) nv = np.dot(n1c, vr) # expansion coefficients A_mm = X_ii[i1:i1 + 2 * lv1 + 1, i2:i2 + 2 * lv2 + 1] for mc in range(2 * lc + 1): for m in range(2 * l + 1): G1c = gaunt[lv1**2:(lv1 + 1)**2, lc**2 + mc, l**2 + m] G2c = gaunt[lv2**2:(lv2 + 1)**2, lc**2 + mc, l**2 + m] A_mm += nv * np.outer(G1c, G2c) i2 += 2 * lv2 + 1 i1 += 2 * lv1 + 1 # pack X_ii matrix X_p = pack2(X_ii) return X_p
def atomic_exact_exchange(atom, type = 'all'): """Returns the exact exchange energy of the atom defined by the instantiated AllElectron object 'atom' """ gaunt = make_gaunt(lmax=max(atom.l_j)) # Make gaunt coeff. list Nj = len(atom.n_j) # The total number of orbitals # determine relevant states for chosen type of exchange contribution if type == 'all': nstates = mstates = range(Nj) else: Njcore = core_states(atom.symbol) # The number of core orbitals if type == 'val-val': nstates = mstates = range(Njcore, Nj) elif type == 'core-core': nstates = mstates = range(Njcore) elif type == 'val-core': nstates = range(Njcore,Nj) mstates = range(Njcore) else: raise RuntimeError('Unknown type of exchange: ', type) # Arrays for storing the potential (times radius) vr = np.zeros(atom.N) vrl = np.zeros(atom.N) # do actual calculation of exchange contribution Exx = 0.0 for j1 in nstates: # angular momentum of first state l1 = atom.l_j[j1] for j2 in mstates: # angular momentum of second state l2 = atom.l_j[j2] # joint occupation number f12 = .5 * atom.f_j[j1] / (2. * l1 + 1) * \ atom.f_j[j2] / (2. * l2 + 1) # electron density times radius times length element nrdr = atom.u_j[j1] * atom.u_j[j2] * atom.dr nrdr[1:] /= atom.r[1:] # potential times radius vr[:] = 0.0 # L summation for l in range(l1 + l2 + 1): # get potential for current l-value hartree(l, nrdr, atom.beta, atom.N, vrl) # take all m1 m2 and m values of Gaunt matrix of the form # G(L1,L2,L) where L = {l,m} G2 = gaunt[l1**2:(l1+1)**2, l2**2:(l2+1)**2, l**2:(l+1)**2]**2 # add to total potential vr += vrl * np.sum(G2) # add to total exchange the contribution from current two states Exx += -.5 * f12 * np.dot(vr, nrdr) # double energy if mixed contribution if type == 'val-core': Exx *= 2. # return exchange energy return Exx
def constructX(gen): """Construct the X_p^a matrix for the given atom. The X_p^a matrix describes the valence-core interactions of the partial waves. """ # initialize attributes uv_j = gen.vu_j # soft valence states * r: lv_j = gen.vl_j # their repective l quantum numbers Nvi = 0 for l in lv_j: Nvi += 2 * l + 1 # total number of valence states (including m) # number of core and valence orbitals (j only, i.e. not m-number) Njcore = gen.njcore Njval = len(lv_j) # core states * r: uc_j = gen.u_j[:Njcore] r, dr, N, beta = gen.r, gen.dr, gen.N, gen.beta # potential times radius vr = np.zeros(N) # initialize X_ii matrix X_ii = np.zeros((Nvi, Nvi)) # make gaunt coeff. list lmax = max(gen.l_j[:Njcore] + gen.vl_j) gaunt = make_gaunt(lmax=lmax) # sum over core states for jc in range(Njcore): lc = gen.l_j[jc] # sum over first valence state index i1 = 0 for jv1 in range(Njval): lv1 = lv_j[jv1] # electron density 1 times radius times length element n1c = uv_j[jv1] * uc_j[jc] * dr n1c[1:] /= r[1:] # sum over second valence state index i2 = 0 for jv2 in range(Njval): lv2 = lv_j[jv2] # electron density 2 n2c = uv_j[jv2] * uc_j[jc] * dr n2c[1:] /= r[1:] # sum expansion in angular momenta for l in range(min(lv1, lv2) + lc + 1): # Int density * potential * r^2 * dr: hartree(l, n2c, r, vr) nv = np.dot(n1c, vr) # expansion coefficients A_mm = X_ii[i1:i1 + 2 * lv1 + 1, i2:i2 + 2 * lv2 + 1] for mc in range(2 * lc + 1): for m in range(2 * l + 1): G1c = gaunt[lv1**2:(lv1 + 1)**2, lc**2 + mc, l**2 + m] G2c = gaunt[lv2**2:(lv2 + 1)**2, lc**2 + mc, l**2 + m] A_mm += nv * np.outer(G1c, G2c) i2 += 2 * lv2 + 1 i1 += 2 * lv1 + 1 # pack X_ii matrix X_p = pack2(X_ii) return X_p
def atomic_exact_exchange(atom, type='all'): """Returns the exact exchange energy of the atom defined by the instantiated AllElectron object 'atom' """ gaunt = make_gaunt(lmax=max(atom.l_j)) # Make gaunt coeff. list Nj = len(atom.n_j) # The total number of orbitals # determine relevant states for chosen type of exchange contribution if type == 'all': nstates = mstates = range(Nj) else: Njcore = core_states(atom.symbol) # The number of core orbitals if type == 'val-val': nstates = mstates = range(Njcore, Nj) elif type == 'core-core': nstates = mstates = range(Njcore) elif type == 'val-core': nstates = range(Njcore, Nj) mstates = range(Njcore) else: raise RuntimeError('Unknown type of exchange: ', type) # Arrays for storing the potential (times radius) vr = np.zeros(atom.N) vrl = np.zeros(atom.N) # do actual calculation of exchange contribution Exx = 0.0 for j1 in nstates: # angular momentum of first state l1 = atom.l_j[j1] for j2 in mstates: # angular momentum of second state l2 = atom.l_j[j2] # joint occupation number f12 = .5 * atom.f_j[j1] / (2. * l1 + 1) * \ atom.f_j[j2] / (2. * l2 + 1) # electron density times radius times length element nrdr = atom.u_j[j1] * atom.u_j[j2] * atom.dr nrdr[1:] /= atom.r[1:] # potential times radius vr[:] = 0.0 # L summation for l in range(l1 + l2 + 1): # get potential for current l-value hartree(l, nrdr, atom.r, vrl) # take all m1 m2 and m values of Gaunt matrix of the form # G(L1,L2,L) where L = {l,m} G2 = gaunt[l1**2:(l1 + 1)**2, l2**2:(l2 + 1)**2, l**2:(l + 1)**2]**2 # add to total potential vr += vrl * np.sum(G2) # add to total exchange the contribution from current two states Exx += -.5 * f12 * np.dot(vr, nrdr) # double energy if mixed contribution if type == 'val-core': Exx *= 2. # return exchange energy return Exx
def calculate_exx_integrals(self): # Find core states: core = [] lmax = 0 for l, ch in enumerate(self.aea.channels): for n, phi_g in enumerate(ch.phi_ng): if (l >= len(self.waves_l) or (l < len(self.waves_l) and n + l + 1 not in self.waves_l[l].n_n)): core.append((l, phi_g)) if l > lmax: lmax = l lmax = max(lmax, len(self.waves_l) - 1) G_LLL = make_gaunt(lmax) # Calculate core contribution to EXX energy: self.exxcc = 0.0 j1 = 0 for l1, phi1_g in core: f = 1.0 for l2, phi2_g in core[j1:]: n_g = phi1_g * phi2_g for l in range((l1 + l2) % 2, l1 + l2 + 1, 2): G = (G_LLL[l1**2:(l1 + 1)**2, l2**2:(l2 + 1)**2, l**2:(l + 1)**2]**2).sum() vr_g = self.rgd.poisson(n_g, l) e = f * self.rgd.integrate(vr_g * n_g, -1) / 4 / pi self.exxcc -= e * G f = 2.0 j1 += 1 self.log('EXX (core-core):', self.exxcc, 'Hartree') # Calculate core-valence contribution to EXX energy: ni = sum( len(waves) * (2 * l + 1) for l, waves in enumerate(self.waves_l)) self.exxcv_ii = np.zeros((ni, ni)) i1 = 0 for l1, waves1 in enumerate(self.waves_l): for phi1_g in waves1.phi_ng: i2 = 0 for l2, waves2 in enumerate(self.waves_l): for phi2_g in waves2.phi_ng: X_mm = self.exxcv_ii[i1:i1 + 2 * l1 + 1, i2:i2 + 2 * l2 + 1] if (l1 + l2) % 2 == 0: for lc, phi_g in core: n_g = phi1_g * phi_g for l in range((l1 + lc) % 2, max(l1, l2) + lc + 1, 2): vr_g = self.rgd.poisson(phi2_g * phi_g, l) e = (self.rgd.integrate(vr_g * n_g, -1) / (4 * pi)) for mc in range(2 * lc + 1): for m in range(2 * l + 1): G_L = G_LLL[:, lc**2 + mc, l**2 + m] X_mm += np.outer( G_L[l1**2:(l1 + 1)**2], G_L[l2**2:(l2 + 1)**2]) * e i2 += 2 * l2 + 1 i1 += 2 * l1 + 1