def calc_E_kin_bound(orbs): """ Computes the kinetic energy contribution from the bound electrons Inputs: - orbs (object) : the orbitals object Returns: - E_kin_bound (float) : kinetic energy """ # compute the grad^2 component grad2_orbs = mathtools.laplace(orbs.eigfuncs, orbs._xgrid) # compute the (l+1/2)^2 component l_arr = np.array([(l + 0.5)**2.0 for l in range(config.lmax)]) lhalf_orbs = np.einsum("j,ijkl->ijkl", l_arr, orbs.eigfuncs) # add together and multiply by eigfuncs*exp(-3x) prefac = np.exp(-3.0 * orbs._xgrid) * orbs.eigfuncs kin_orbs = prefac * (grad2_orbs - lhalf_orbs) # multiply and sum over occupation numbers e_kin_dens = np.einsum("ijk,ijkl->l", orbs.occnums, kin_orbs) # integrate over sphere E_kin_bound = -0.5 * mathtools.int_sphere(e_kin_dens, orbs._xgrid) return E_kin_bound
def E_xc(density, xgrid, xfunc, cfunc): """ Wrapper function which computes the exchange and correlation energies """ # initialize the _E_xc dict (leading underscore to distinguish from function name) _E_xc = {} # get the total density dens_tot = np.sum(density, axis=0) # compute the exchange energy ex_libxc = calc_xc(density, xgrid, xfunc, "e_xc") _E_xc["x"] = mathtools.int_sphere(ex_libxc * dens_tot, xgrid) # compute the correlation energy ec_libxc = calc_xc(density, xgrid, cfunc, "e_xc") _E_xc["c"] = mathtools.int_sphere(ec_libxc * dens_tot, xgrid) # sum to get the total xc potential _E_xc["xc"] = _E_xc["x"] + _E_xc["c"] return _E_xc
def calc_E_en(density, xgrid): """ Computes the electron-nuclear energy E_en = \int dr v_en(r) n(r) Inputs: - density (np array) : density """ # sum the density over the spin axes to get the total density dens_tot = np.sum(density, axis=0) # compute the integral v_en = Potential.calc_v_en(xgrid) E_en = mathtools.int_sphere(dens_tot * v_en, xgrid) return E_en
def calc_E_ha(density, xgrid): """ Computes the Hartree energy E_ha = 1/2 \int dr \int dr' n(r)n(r')/|r-r'| Uses the pre-computed hartree potential E_ha = 1/2 /int dr n(r) v_ha(r) Inputs: - density (np array) : the density object - pot (object) : the potential object Returns: - E_ha (float) : the hartree energy """ # sum density over spins to get total density dens_tot = np.sum(density, axis=0) # compute the integral v_ha = Potential.calc_v_ha(density, xgrid) E_ha = 0.5 * mathtools.int_sphere(dens_tot * v_ha, xgrid) return E_ha
def check_conv(self, E_free, pot, dens, iscf): """ Computes the changes in energy, integrated density and integrated potential and checks if they satisfy the proscribed convergence parameters Parameters ---------- E_free : float the total free energy v_s : ndarray the KS potential dens : ndarray the electronic density iscf : int the iteration number Returns ------- conv_vals : dict Dictionary of convergence parameters as follows: {'conv_energy' : float, 'conv_rho' : ndarray, 'conv_pot' : ndarray, 'complete' : bool} """ conv_vals = {} # first update the energy, potential and density attributes self._energy[0] = E_free self._potential[0] = pot self._density[0] = dens # compute the change in energy conv_vals["dE"] = abs( (self._energy[0] - self._energy[1]) / self._energy[0]) # compute the change in potential dv = np.abs(self._potential[0] - self._potential[1]) # compute the norm norm_v = mathtools.int_sphere(np.abs(self._potential[0]), self._xgrid) conv_vals["dpot"] = mathtools.int_sphere(dv, self._xgrid) / norm_v # compute the change in density dn = np.abs(self._density[0] - self._density[1]) # integrate over sphere to return a number # note we add a small constant to avoid errors if there are no electrons in one spin channel conv_vals["drho"] = mathtools.int_sphere( dn, self._xgrid) / (config.nele + 1e-3) # reset the energy, potential and density attributes self._energy[1] = E_free self._potential[1] = pot self._density[1] = dens # see if the convergence criteria are satisfied conv_energy = False conv_rho = False conv_pot = False conv_vals["complete"] = False if iscf > 3: if conv_vals["dE"] < config.conv_params["econv"]: conv_energy = True if np.all(conv_vals["drho"] < config.conv_params["nconv"]): conv_rho = True if np.all(conv_vals["dpot"] < config.conv_params["vconv"]): conv_pot = True if conv_energy and conv_rho and conv_pot: conv_vals["complete"] = True return conv_vals