def main(): args = parse_args() fn_h5, grp_name = parse_h5(args.output, 'output') # check if the group is already present (and not empty) in the output file if check_output(fn_h5, grp_name, args.overwrite): return # Load the system sys = System.from_file(args.wfn) # Define a list of optional arguments for the WPartClass: WPartClass = wpart_schemes[args.scheme] kwargs = dict((key, val) for key, val in vars(args).iteritems() if key in WPartClass.options) # Load the proatomdb if args.atoms is not None: proatomdb = ProAtomDB.from_file(args.atoms) proatomdb.normalize() kwargs['proatomdb'] = proatomdb else: proatomdb = None # Run the partitioning agspec = AtomicGridSpec(args.grid) molgrid = BeckeMolGrid(sys, agspec, mode='only') sys.update_grid(molgrid) # for the grid to be written to the output wpart = wpart_schemes[args.scheme](sys, molgrid, **kwargs) names = wpart.do_all() write_part_output(fn_h5, grp_name, wpart, names, args)
def getBeckeGrid(self, resolution='fine', new=False, **kwargs): """ coarse, medium, fine, veryfine, ultrafine and insane """ if not hasattr(self, 'molecule'): qtk.exit("no molecule structure found") if new or not hasattr(self, 'grid'): molecule = self.molecule coord = np.array(np.atleast_2d(molecule.R * 1.8897261245650618)) self.grid = BeckeMolGrid(coord, molecule.Z.astype(int), molecule.Z, resolution) mol_str = [] for i in range(molecule.N): atm_str = [molecule.type_list[i]] for j in range(3): atm_str.append(str(molecule.R[i, j])) mol_str.append(' '.join(atm_str)) mol_str = '; '.join(mol_str) if 'gto_kwargs' in kwargs: mol = gto.Mole(**kwargs['gto_kwargs']) else: mol = gto.Mole() #mol.build(atom=mol_str, basis=self.setting['basis_set']) if hasattr(self, 'basis_name'): basis = self.basis_name self.basisFormat() mol.build(atom=mol_str, basis=self.pybasis) self.mol = mol del_list = ['_phi', '_psi', '_dphi', '_dpsi', '_rho', '_drho'] for p in del_list: if hasattr(self, p): delattr(self, p)
def __init__(self, coordinates, numbers, pseudo_numbers, specs='medium', k=3, rotate=False): """Initialize class. Parameters ---------- coordinates : np.ndarray, shape=(M, 3) Cartesian coordinates of `M` atoms in the molecule. numbers : np.ndarray, shape=(M,) Atomic number of `M` atoms in the molecule. pseudo_numbers : np.ndarray, shape=(M,) Pseudo-number of `M` atoms in the molecule. specs : str, optional Specification of grid. Either choose from ['coarse', 'medium', 'fine', 'veryfine', 'ultrafine', 'insane'] or provide a string of 'rname:rmin:rmax:nrad:nang' format. Here 'rname' denotes the type of radial grid and can be chosen from ['linear', 'exp', 'power'], 'rmin' and 'rmax' specify the first and last radial grid points in angstrom, 'nrad' specify the number of radial grid points, and 'nang' specify the number of angular Lebedev-Laikov grid. The 'nang' can be chosen from (6, 14, 26, 38, 50, 74, 86, 110, 146, 170, 194, 230, 266, 302, 350, 434, 590, 770, 974, 1202, 1454, 1730, 2030, 2354, 2702, 3074, 3470, 3890, 4334, 4802, 5294, 5810). k : int, optional The order of the switching function in Becke's weighting scheme. rotate : bool, optional Whether to randomly rotate spherical grids. """ self._coordinates = coordinates self._numbers = numbers self._pseudo_numbers = pseudo_numbers self._k = k self._rotate = rotate self.specs = specs self._grid = BeckeMolGrid(self.coordinates, self.numbers, self.pseudo_numbers, agspec=self.specs, k=k, random_rotate=rotate, mode='keep')
def from_molecule(cls, mol, scheme=None, grid=None, spin="ab", **kwargs): if grid is None: grid = BeckeMolGrid(mol.coordinates, mol.numbers, mol.pseudo_numbers, agspec="fine", random_rotate=False, mode='keep') else: check_molecule_grid(mol, grid) # compute molecular electron density dens = mol.compute_density(grid.points, spin=spin) if mol.pesudo_numbers is None: pesudo_numbers = mol.numbers else: pesudo_numbers = mol.pesudo_numbers return cls(mol.coordinates, mol.numbers, pesudo_numbers, dens, grid, scheme, **kwargs)
def main(): args = parse_args() fn_h5, grp_name = parse_h5(args.output, 'output') # check if the group is already present (and not empty) in the output file if check_output(fn_h5, grp_name, args.overwrite): return # Load the system mol = IOData.from_file(args.wfn) # Define a list of optional arguments for the WPartClass: WPartClass = wpart_schemes[args.scheme] kwargs = dict((key, val) for key, val in vars(args).iteritems() if key in WPartClass.options) # Load the proatomdb if args.atoms is not None: proatomdb = ProAtomDB.from_file(args.atoms) proatomdb.normalize() kwargs['proatomdb'] = proatomdb else: proatomdb = None # Run the partitioning agspec = AtomicGridSpec(args.grid) grid = BeckeMolGrid(mol.coordinates, mol.numbers, mol.pseudo_numbers, agspec, mode='only') dm_full = mol.get_dm_full() moldens = mol.obasis.compute_grid_density_dm(dm_full, grid.points, epsilon=args.epsilon) dm_spin = mol.get_dm_spin() if dm_spin is not None: kwargs['spindens'] = mol.obasis.compute_grid_density_dm( dm_spin, grid.points, epsilon=args.epsilon) wpart = wpart_schemes[args.scheme](mol.coordinates, mol.numbers, mol.pseudo_numbers, grid, moldens, **kwargs) keys = wpart.do_all() if args.slow: # ugly hack for the slow analysis involving the AIM overlap operators. wpart_slow_analysis(wpart, mol) keys = list(wpart.cache.iterkeys(tags='o')) write_part_output(fn_h5, grp_name, wpart, keys, args)
def getBeckeGrid(self, resolution='fine', new=False, **kwargs): """ coarse, medium, fine, veryfine, ultrafine and insane """ if not hasattr(self, 'molecule'): qtk.exit("no molecule structure found") if new or not hasattr(self, 'grid'): molecule = self.molecule coord = np.array(np.atleast_2d(molecule.R*1.8897261245650618)) self.grid = BeckeMolGrid(coord, molecule.Z.astype(int), molecule.Z, resolution) mol_str = [] for i in range(molecule.N): atm_str = [molecule.type_list[i]] for j in range(3): atm_str.append(str(molecule.R[i,j])) mol_str.append(' '.join(atm_str)) mol_str = '; '.join(mol_str) if 'gto_kwargs' in kwargs: mol = gto.Mole(**kwargs['gto_kwargs']) else: mol = gto.Mole() #mol.build(atom=mol_str, basis=self.setting['basis_set']) if hasattr(self, 'basis_name'): basis = self.basis_name self.basisFormat() mol.build(atom=mol_str, basis=self.pybasis) self.mol = mol del_list = ['_phi', '_psi', '_dphi', '_dpsi', '_rho', '_drho'] for p in del_list: if hasattr(self, p): delattr(self, p)
def __init__(self, molecule, **kwargs): if not ht_found: qtk.exit("horton module not found.") if not ps_found: qtk.exit("pyscf module not found.") if not xc_found: print xcpath print xc_found qtk.exit("libxc not found.") if 'wf_convergence' not in kwargs: kwargs['wf_convergence'] = 1e-06 if 'kinetic_functional' not in kwargs: kwargs['kinetic_functional'] = 'LLP' if 'aufbau' in kwargs and kwargs['aufbau']: self.aufbau = True self.orbitals = [] else: self.aufbau = False GaussianBasisInput.__init__(self, molecule, **kwargs) self.setting.update(kwargs) self.backup() if 'dft_setting' not in kwargs: if not self.aufbau: kf = self.setting['kinetic_functional'] if kf == 'LLP': self.setting['dft_setting'] = { 'K': { 1.0: 'XC_GGA_K_LLP' }, } else: self.setting['dft_setting'] = {'K': {1.0: 'XC_GGA_K_VW'}} ss = self.setting['theory'] sdft = self.setting['dft_setting'] if ss == 'pbe': sdft['X'] = 'XC_GGA_X_PBE' sdft['C'] = 'XC_GGA_C_PBE' elif ss == 'blyp': sdft['X'] = 'XC_GGA_X_B88' sdft['C'] = 'XC_GGA_C_LYP' dft = self.setting['dft_setting'] for k, v in dft.iteritems(): if type(dft[k]) is str: dft[k] = xc_dict[v] elif type(dft[k]) is dict: for key, value in dft[k].iteritems(): if type(value) is str: dft[k][key] = xc_dict[value] mol_str = [] for i in range(molecule.N): atm_str = [molecule.type_list[i]] for j in range(3): atm_str.append(str(molecule.R[i, j])) mol_str.append(' '.join(atm_str)) mol_str = '; '.join(mol_str) mol = gto.Mole() mol.build(atom=mol_str, basis=self.setting['basis_set']) ig = ps.scf.hf.get_init_guess(mol, key='atom') ovl = mol.intor_symmetric("cint1e_ovlp_sph") kin = mol.intor_symmetric("cint1e_kin_sph") ext = mol.intor_symmetric("cint1e_nuc_sph") # 4D array, can be contracted by numpy.tensordot (td) # nao x nao integrated density Coulomb kernel can be computed via # int_vee_rho = td(dm, vee, axes=([0,1], [0,1])) # the final electron-electron cam be computed via # Vee = 0.5 * trace(dm, int_vee_rho) nao = mol.nao_nr() vee = mol.intor( "cint2e_sph", comp=1, hermi=1, ).reshape(nao, nao, nao, nao) coord = np.array(np.atleast_2d(molecule.R * 1.8897261245650618)) grid = BeckeMolGrid(coord, molecule.Z.astype(int), molecule.Z) self.molecule = molecule self.mol = mol self.ovl = ovl self.kin = kin self.ext = ext self.vee = vee self.nao = nao self.dv = self.normalize(sqrt(diag(ig))) self.initial_guess = copy.deepcopy(self.dv) self.ig = ig self.grid = grid self.update(self.dv) self.old_deltadv = None self.new_deltadv = None self.old_E = None self.dv_list = [] self.psi_list = [] self.dpsi_list = [] self.optimizers = { 'tnc': opt.fmin_tnc, 'ncg': opt.fmin_ncg, 'cg': opt.fmin_cg, 'bfgs': opt.fmin_bfgs, 'l_bfgs_b': opt.fmin_l_bfgs_b, 'simplex': opt.fmin, } self.optimizer_settings = { 'tnc': { 'xtol': 0.0, 'pgtol': 0.0, 'maxfun': 1000 }, 'simplex': { 'xtol': 1E-10, 'ftol': 1E-10, 'maxfun': 1000 }, } self.orbitals = []
class MolecularGrid(object): """Becke-Lebedev molecular grid for numerical integrations.""" def __init__(self, coordinates, numbers, pseudo_numbers, specs='medium', k=3, rotate=False): """Initialize class. Parameters ---------- coordinates : np.ndarray, shape=(M, 3) Cartesian coordinates of `M` atoms in the molecule. numbers : np.ndarray, shape=(M,) Atomic number of `M` atoms in the molecule. pseudo_numbers : np.ndarray, shape=(M,) Pseudo-number of `M` atoms in the molecule. specs : str, optional Specification of grid. Either choose from ['coarse', 'medium', 'fine', 'veryfine', 'ultrafine', 'insane'] or provide a string of 'rname:rmin:rmax:nrad:nang' format. Here 'rname' denotes the type of radial grid and can be chosen from ['linear', 'exp', 'power'], 'rmin' and 'rmax' specify the first and last radial grid points in angstrom, 'nrad' specify the number of radial grid points, and 'nang' specify the number of angular Lebedev-Laikov grid. The 'nang' can be chosen from (6, 14, 26, 38, 50, 74, 86, 110, 146, 170, 194, 230, 266, 302, 350, 434, 590, 770, 974, 1202, 1454, 1730, 2030, 2354, 2702, 3074, 3470, 3890, 4334, 4802, 5294, 5810). k : int, optional The order of the switching function in Becke's weighting scheme. rotate : bool, optional Whether to randomly rotate spherical grids. """ self._coordinates = coordinates self._numbers = numbers self._pseudo_numbers = pseudo_numbers self._k = k self._rotate = rotate self.specs = specs self._grid = BeckeMolGrid(self.coordinates, self.numbers, self.pseudo_numbers, agspec=self.specs, k=k, random_rotate=rotate, mode='keep') @classmethod def from_molecule(cls, molecule, specs='medium', k=3, rotate=False): """Initialize the class given an instance of Molecule. Parameters ---------- molecule : instance of Molecule Instance of Molecule class. specs : str, optional Specification of grid. Either choose from ['coarse', 'medium', 'fine', 'veryfine', 'ultrafine', 'insane'] or provide a string of 'rname:rmin:rmax:nrad:nang' format. Here 'rname' denotes the type of radial grid and can be chosen from ['linear', 'exp', 'power'], 'rmin' and 'rmax' specify the first and last radial grid points in angstrom, 'nrad' specify the number of radial grid points, and 'nang' specify the number of angular Lebedev-Laikov grid. The 'nang' can be chosen from (6, 14, 26, 38, 50, 74, 86, 110, 146, 170, 194, 230, 266, 302, 350, 434, 590, 770, 974, 1202, 1454, 1730, 2030, 2354, 2702, 3074, 3470, 3890, 4334, 4802, 5294, 5810). k : int, optional The order of the switching function in Becke's weighting scheme. rotate : bool, optional Whether to randomly rotate spherical grids. """ if not isinstance(molecule, Molecule): raise TypeError( 'Argument molecule should be an instance of Molecule class.') coords, nums, pnums = molecule.coordinates, molecule.numbers, molecule.pseudo_numbers return cls(coords, nums, pnums, specs, k, rotate) @classmethod def from_file(cls, fname, specs='medium', k=3, rotate=False): """Initialize the class given an instance of Molecule. Parameters ---------- fname : str Path to molecule's files. specs : str, optional Specification of grid. Either choose from ['coarse', 'medium', 'fine', 'veryfine', 'ultrafine', 'insane'] or provide a string of 'rname:rmin:rmax:nrad:nang' format. Here 'rname' denotes the type of radial grid and can be chosen from ['linear', 'exp', 'power'], 'rmin' and 'rmax' specify the first and last radial grid points in angstrom, 'nrad' specify the number of radial grid points, and 'nang' specify the number of angular Lebedev-Laikov grid. The 'nang' can be chosen from (6, 14, 26, 38, 50, 74, 86, 110, 146, 170, 194, 230, 266, 302, 350, 434, 590, 770, 974, 1202, 1454, 1730, 2030, 2354, 2702, 3074, 3470, 3890, 4334, 4802, 5294, 5810). k : int, optional The order of the switching function in Becke's weighting scheme. rotate : bool, optional Whether to randomly rotate spherical grids. """ mol = Molecule.from_file(fname) return cls.from_molecule(mol, specs, k, rotate) def __getattr__(self, item): return getattr(self._grid, item) @property def center(self): """Cartesian coordinates of atomic centers.""" return self._coordinates @property def coordinates(self): """Cartesian coordinates of atomic centers.""" return self._coordinates @property def numbers(self): """Atomic number of atomic centers.""" return self._numbers @property def pseudo_numbers(self): """Pseudo atomic number of atomic centers.""" return self._pseudo_numbers @property def npoints(self): """Number of grid points.""" return self._grid.points.shape[0] @property def points(self): """Cartesian coordinates of grid points.""" return self._grid.points @property def weights(self): """Integration weight of grid points.""" return self._grid.weights def integrate(self, *value): """Integrate the property evaluated on the grid points. Parameters ---------- value : np.ndarray Property value evaluated on the grid points. """ # temporary hack because of HORTON if not isinstance(value, np.ndarray): temp = value[0] for item in value[1:]: if item is not None: temp = temp * item value = temp if value.ndim != 1: raise ValueError('Argument value should be a 1D array.') if value.shape != (self.npoints, ): raise ValueError('Argument value should have ({0},) shape!'.format( self.npoints)) return self._grid.integrate(value) def compute_spherical_average(self, value): """Compute spherical average of given value evaluated on the grid points. Note: This method only works for atomic systems with one nuclear center. Parameters ---------- value : np.ndarray Property value evaluated on the grid points. """ if len(self.numbers) != 1: raise NotImplementedError( 'This method only works for systems with one atom!') if value.ndim != 1: raise ValueError('Argument value should be a 1D array.') if value.shape != (self.npoints, ): raise ValueError('Argument value should have ({0},) shape!'.format( self.npoints)) return self._grid.get_spherical_average(value)
class GaussianBasisOutput(GenericQMOutput): def __init__(self, output=None, **kwargs): GenericQMOutput.__init__(self, output, **kwargs) def copy(self): if hasattr(self, 'mol'): del self.mol if hasattr(self, 'grid'): del self.grid return copy.deepcopy(self) def mo_g09_nwchem(self): mo = self.mo_vectors ind = np.arange(len(mo[0])) itr = 0 # hard coded reordering for d and f orbitals order = { 'd': [0, 3, 4, 1, 5, 2], 'f': [0, 4, 5, 3, 9, 6, 1, 8, 7, 2], } while itr < len(mo[0]): bStr = self.basis[itr]['type'] if len(bStr) > 2: key = bStr[0] if key in order.keys(): ind_itr = ind[itr: itr+len(order[key])] ind_itr = ind_itr[order[key]] for i in range(len(order[bStr[0]])): ind[itr + i] = ind_itr[i] itr += len(order[bStr[0]]) - 1 else: qtk.exit("basis reordering for %s orbital " % key\ + "not implemented yet") itr += 1 return mo[:, ind] def basisFormat(): if not hasattr(self, 'basis'): qtk.exit("no basis found") pass def getBeckeGrid(self, resolution='fine', new=False, **kwargs): """ coarse, medium, fine, veryfine, ultrafine and insane """ if not hasattr(self, 'molecule'): qtk.exit("no molecule structure found") if new or not hasattr(self, 'grid'): molecule = self.molecule coord = np.array(np.atleast_2d(molecule.R*1.8897261245650618)) self.grid = BeckeMolGrid(coord, molecule.Z.astype(int), molecule.Z, resolution) mol_str = [] for i in range(molecule.N): atm_str = [molecule.type_list[i]] for j in range(3): atm_str.append(str(molecule.R[i,j])) mol_str.append(' '.join(atm_str)) mol_str = '; '.join(mol_str) if 'gto_kwargs' in kwargs: mol = gto.Mole(**kwargs['gto_kwargs']) else: mol = gto.Mole() #mol.build(atom=mol_str, basis=self.setting['basis_set']) if hasattr(self, 'basis_name'): basis = self.basis_name mol.build(atom=mol_str, basis=basis) self.mol = mol del_list = ['_phi', '_psi', '_dphi', '_dpsi', '_rho', '_drho'] for p in del_list: if hasattr(self, p): delattr(self, p) def getPhi(self, cartesian=True, resolution='fine', new=False, **kwargs): if 'gridpoints' in kwargs: new = True if new or not hasattr(self, '_phi'): self.getBeckeGrid(resolution, new, **kwargs) coords = self.grid.points if 'gridpoints' not in kwargs: grid_coords = None else: grid_coords = np.array(kwargs['gridpoints']).astype(float) if cartesian: mode = "GTOval_cart" else: mode = "GTOval_sph" self._phi = self.mol.eval_gto(mode, coords).T norm = np.dot(self._phi * self.grid.weights, self._phi.T) if grid_coords is not None: self._phi = self.mol.eval_gto(mode, grid_coords).T self._phi = self._phi / np.sqrt(np.diag(norm))[:, np.newaxis] else: self._phi = self._phi / np.sqrt(np.diag(norm))[:, np.newaxis] return self._phi def getDPhi(self, cartesian=True, resolution='fine', new=False, **kwargs): if 'gridpoints' in kwargs: new = True if new or not hasattr(self, '_dphi'): self.getBeckeGrid(resolution, new, **kwargs) if 'gridpoints' not in kwargs: coords = self.grid.points else: coords = np.array(kwargs['gridpoints']).astype(float) if cartesian: mode = "GTOval_ip_cart" else: mode = "GTOval_ip_sph" self._dphi = self.mol.eval_gto(mode, coords, comp=3).T return self._dphi def getPsi(self, cartesian=True, resolution='fine', new=False, **kwargs): if 'gridpoints' in kwargs: new = True if new or not hasattr(self, '_psi'): self.getPhi(cartesian, resolution, new, **kwargs) if not hasattr(self, 'mo_vectors'): qtk.exit('mo_vectors not found') mo = self.mo_vectors if hasattr(self, 'program'): if self.program == 'gaussian': mo = self.mo_g09_nwchem() self._psi = np.dot(mo, self._phi) return self._psi def getDPsi(self, cartesian=True, resolution='fine', new=False, **kwargs): if 'gridpoints' in kwargs: new = True if new or not hasattr(self, '_dpsi'): self.getDPhi(cartesian, resolution, new, **kwargs) if not hasattr(self, 'mo_vectors'): qtk.exit('mo_vectors not found') mo = self.mo_vectors if hasattr(self, 'program'): if self.program == 'gaussian': mo = self.mo_g09_nwchem() self._dpsi = np.dot(mo, np.swapaxes(self._dphi, 0, 1)) return self._dpsi def getRho(self, cartesian=True, resolution='fine', new=False, occupation=None, **kwargs): if 'gridpoints' in kwargs: new = True if new or not hasattr(self, '_rho'): self.getPsi(cartesian, resolution, new, **kwargs) if not hasattr(self, 'occupation'): qtk.exit("occupation number not found") if occupation is None: occ = np.array(self.occupation) else: occ = np.array(occupation) self._rho = np.sum(self._psi**2 * occ[:, np.newaxis], axis = 0) return self._rho def getDRho(self, cartesian=True, resolution='fine', new=False, occupation=None, **kwargs): if 'gridpoints' in kwargs: new = True if new or not hasattr(self, '_drho'): if not hasattr(self, '_psi'): _psi = self.getPsi(cartesian, resolution, new, **kwargs) if not hasattr(self, '_dpsi'): _dpsi = self.getDPsi(cartesian, resolution, new, **kwargs) if not hasattr(self, 'occupation'): qtk.exit("occupation number not found") self._psi, self._dpsi = _psi, _dpsi if occupation is None: occ = np.array(self.occupation) else: occ = np.array(occupation) self._drho = 2 * np.sum( self._psi[..., np.newaxis] * self._dpsi \ * occ[:, np.newaxis, np.newaxis], axis = 0 ) return self._drho def freeRho(self, coord, gridpoints, **kwargs): assert self.molecule.N == 1 new = self.copy() new.molecule.R[0] = np.array(coord) / 1.8897261245650618 if 'spin' not in kwargs: if new.molecule.getValenceElectrons() % 2 != 0: kw = {'gto_kwargs': {'spin': 1}} else: kw = {} else: kw = {'gto_kwargs': {'spin': kwargs['spin']}} if type(gridpoints[0][0]) is not float: pl = np.array(gridpoints).astype(float) else: pl = gridpoints return new.getRho(gridpoints = pl, **kw) def _e_setting(self, gridpoints, **kwargs): new = self.copy() kw = {} if 'spin' not in kwargs: if new.molecule.getValenceElectrons() % 2 != 0: kw['gto_kwargs'] = {'spin': 1} else: kw['gto_kwargs'] = {'spin': spin} if 'resolution' in kwargs: kw['resolution'] = kwargs['resolution'] if 'cartesian' in kwargs: kw['cartesian'] = kwargs['cartesian'] occ = np.array(new.occupation) gridpoints = np.atleast_2d(gridpoints).astype(float) return new, occ, kw, gridpoints def e_kin(self, gridpoints, **kwargs): """ kinetic energy density """ new, occ, kw, gridpoints = self._e_setting(gridpoints, **kwargs) dpsi = new.getDPsi(gridpoints = gridpoints, **kw) dpsi2 = np.linalg.norm(dpsi, axis=-1) ** 2 return (0.5 * dpsi2 * occ[:, np.newaxis]).sum(axis=0) def e_ext(self, gridpoints, **kwargs): """ external potential energy density """ new, occ, kw, gridpoints = self._e_setting(gridpoints, **kwargs) ext = np.zeros(len(gridpoints)) for I in range(self.molecule.N): RI = self.molecule.R[I] * 1.8897261245650618 ZI = self.molecule.Z[I] R_list = gridpoints - RI ext -= ZI / np.linalg.norm(R_list, axis=1) psi = new.getPsi(gridpoints = gridpoints, **kw) return (psi * occ[:, np.newaxis]).sum(axis=0) * ext def e_xc(self, gridpoints, dft='pbe', **kwargs): new, occ, kw, gridpoints = self._e_setting(gridpoints, **kwargs) xc_list = [xc_dict[f] for f in qtk.setting.libxc_dict[dft]] drho = new.getDRho(gridpoints=gridpoints, new=True, **kw) sigma = np.sum(drho ** 2, axis = 1) rho = new.getRho(gridpoints = gridpoints, **kw) x = libxc_exc(rho, sigma, len(rho), xc_list[0]) c = libxc_exc(rho, sigma, len(rho), xc_list[1]) return x, c def e_coulomb(self, gridpoints, **kwargs): new, occ, kw, gridpoints = self._e_setting(gridpoints, **kwargs) kernel = self.coulombKernel(gridpoints / 1.8897261245650618, **kwargs) rho = new.getRho(gridpoints = gridpoints, **kw) return 0.5 * rho * kernel def epsilon(self, gridpoints, dft='pbe', **kwargs): new, occ, kw, gridpoints = self._e_setting(gridpoints, **kwargs) kin = self.e_kin(gridpoints, **kw) ext = self.e_ext(gridpoints, **kw) x, c = self.e_xc(gridpoints, **kw) coulomb = self.e_coulomb(gridpoints, **kw) return kin + ext + x + c + coulomb def getDipole(self, cartesian=True, resolution='fine', unit='debye'): if not hasattr(self, 'molecule'): qtk.exit('molecule structure not found') if not hasattr(self, '_rho'): self.getRho(cartesian, resolution) pQ = np.array( [sum(self.molecule.Z * self.molecule.R[:,i]) for i in range(3)] ) pQ = pQ * 1.8897259885789 pq = np.array( [self.grid.integrate(self._rho * self.grid.points[:,i]) for i in range(3)] ) pq = pq mu = pQ - pq if unit == 'debye': return mu / 0.393430307 else: return mu def keMatrix(self): return keMatrix(self.basis) def densityMatrix(self): return densityMatrix(self) def veMatrix(self, coord = None, Z = None): if coord is None: coord = self.molecule.R if Z is None: Z = self.molecule.Z return veMatrix(self.basis, coord, Z) def eeKernel(self, coord=None, batch_size=1): if coord is None: coord = self.molecule.R else: coord = np.atleast_2d(coord).astype(float) out = [] itr = 1 for chunk in np.array_split(coord, batch_size): if itr > 1: qtk.progress("eeKernel", "processing batch: %d\n" % itr) itr += 1 out.append(eeKernel(self.basis, chunk)) return np.concatenate(out) def coulombKernel(self, coord = None, **kwargs): k = self.eeKernel(coord, **kwargs) mo = self.mo_vectors # out = np.diagonal( # np.einsum('is,jl,kls->kij', mo, mo, k), # axis1=1, axis2=2, # ).sum(1) # # diagonal: np.einsum('is,il, kls->ki', mo, mo, k) # or np.einsum('ii->i', a) for diagonal # sum diagonal: np.einsum('is,il, kls->k', mo, mo, k) # or np.einsum('ii', a) for trace out = np.einsum('is,il, kls->k', mo, mo, k) return out def eeMatrix(self): if not hasattr(self, '_eeMatrix'): self._eeMatrix = eeMatrix(self.basis) return self._eeMatrix def EJ(self): ee = 2*self.eeMatrix() td = np.tensordot mo = self.mo_vectors out = td(mo, ee, axes=(1, 0)) out = td(mo, out, axes=(1, 1)) out = td(mo, out, axes=(1, 2)) out = td(mo, out, axes=(1, 3)) occ = int(np.sum(self.occupation) / 2.) EJ = [out[a,a,b,b] for a in range(occ) for b in range(occ)] return sum(EJ) def EX(self): ex = -np.swapaxes(self.eeMatrix(), 1,2) td = np.tensordot mo = self.mo_vectors out = td(mo, ex, axes=(1, 0)) out = td(mo, out, axes=(1, 1)) out = td(mo, out, axes=(1, 2)) out = td(mo, out, axes=(1, 3)) occ = int(np.sum(self.occupation) / 2.) EX = [out[a,a,b,b] for a in range(occ) for b in range(occ)] return sum(EX) def EK(self): km = self.keMatrix() dm = self.densityMatrix() return np.trace(np.dot(dm, km)) def Eext(self): vext = self.veMatrix() dm = self.densityMatrix() return np.trace(np.dot(dm, vext))