def from_dm(cls, center, number, pseudo_number, obasis, dm_full, energy, agspec='fine'): '''Construct a proatom record from a single-atom density matrix **Arguments:** center The position of the nuclues (needed for spherical average) number The atomic number pseudo_number The effective core charges obasis The orbital basis dm_full The spin-summed density matrix object energy The electronic energy of the atom **Optional arguments:** agspec A specifications of the atomic grid. This can either be an instance of the AtomicGridSpec object, or the first argument of its constructor. ''' if len(center) != 3: raise TypeError('Center should be a vector with three elements.') if not isinstance(agspec, AtomicGridSpec): agspec = AtomicGridSpec(agspec) # Compute the density and the gradient on a grid atgrid = AtomicGrid(number, pseudo_number, center, agspec) rho_all = obasis.compute_grid_density_dm(dm_full, atgrid.points) grad_all = obasis.compute_grid_gradient_dm(dm_full, atgrid.points) rho, deriv = atgrid.get_spherical_average(rho_all, grads=[grad_all]) # Derive the number of electrions and the charge overlap = dm_full.new() obasis.compute_overlap(overlap) nel = overlap.contract_two('ab,ba', dm_full) assert abs(nel - int(np.round(nel))) < 1e-4 # only integer nel are supported nel = int(np.round(nel)) charge = pseudo_number - nel # Create object return cls(number, charge, energy, atgrid.rgrid, rho, deriv, pseudo_number)
def from_system(cls, system, agspec='fine'): '''Construct a proatom record from a system and an atomic grid **Arguments:** sys The one-atom system. **Optional arguments:** agspec A specifications of the atomic grid. This can either be an instance of the AtomicGridSpec object, or the first argument of its constructor. ''' if system.natom != 1: raise ValueError('Can only construct a proatom record from a one-atom system.') if not isinstance(agspec, AtomicGridSpec): agspec = AtomicGridSpec(agspec) # Compute the density and the gradient on a grid atgrid = AtomicGrid(system.numbers[0], system.pseudo_numbers[0], system.coordinates[0], agspec) rho_all = system.compute_grid_density(atgrid.points) grad_all = system.compute_grid_gradient(atgrid.points) rrgrad_all = ((atgrid.points-atgrid.center)*grad_all).sum(axis=1) # Compute spherical averages rho = atgrid.get_spherical_average(rho_all) deriv = atgrid.get_spherical_average(rrgrad_all)/atgrid.rgrid.rtransform.get_radii() # Derive the number of electrions try: nel = system.wfn.nel except NotImplementedError: nel = int(np.round(atgrid.rgrid.integrate(rho))) # Get all the other information from the atom energy = system.extra['energy'] try: homo_energy = system.wfn.homo_energy except AttributeError: homo_energy = None number = system.numbers[0] pseudo_number = system.pseudo_numbers[0] charge = pseudo_number - nel # Create object return cls(number, charge, energy, homo_energy, atgrid.rgrid, rho, deriv, pseudo_number)
def __init__(self, centers, numbers, pseudo_numbers=None, agspec='medium', k=3, random_rotate=True, mode='discard'): ''' **Arguments:** centers An array (N, 3) with centers for the atom-centered grids. numbers An array (N,) with atomic numbers. **Optional arguments:** pseudo_numbers An array (N,) with effective core charges. When not given, this defaults to ``numbers``. agspec A specifications of the atomic grid. This can either be an instance of the AtomicGridSpec object, or the first argument of its constructor. k The order of the switching function in Becke's weighting scheme. random_rotate Flag to control random rotation of spherical grids. mode Select one of the following options regarding atomic subgrids: * ``'discard'`` (the default) means that all information about subgrids gets discarded. * ``'keep'`` means that a list of subgrids is kept, including the integration weights of the local grids. * ``'only'`` means that only the subgrids are constructed and that the computation of the molecular integration weights (based on the Becke partitioning) is skipped. ''' natom, centers, numbers, pseudo_numbers = typecheck_geo( centers, numbers, pseudo_numbers) self._centers = centers self._numbers = numbers self._pseudo_numbers = pseudo_numbers # check if the mode argument is valid if mode not in ['discard', 'keep', 'only']: raise ValueError( 'The mode argument must be \'discard\', \'keep\' or \'only\'.') # transform agspec into a usable format if not isinstance(agspec, AtomicGridSpec): agspec = AtomicGridSpec(agspec) self._agspec = agspec # assign attributes self._k = k self._random_rotate = random_rotate self._mode = mode # allocate memory for the grid size = sum( agspec.get_size(self.numbers[i], self.pseudo_numbers[i]) for i in xrange(natom)) points = np.zeros((size, 3), float) weights = np.zeros(size, float) self._becke_weights = np.ones(size, float) # construct the atomic grids if mode != 'discard': atgrids = [] else: atgrids = None offset = 0 if mode != 'only': # More recent covalent radii are used than in the original work of Becke. # No covalent radius is defined for elements heavier than Curium and a # default value of 3.0 Bohr is used for heavier elements. cov_radii = np.array([(periodic[n].cov_radius or 3.0) for n in self.numbers]) # The actual work: if log.do_medium: log('Preparing Becke-Lebedev molecular integration grid.') pb = log.progress(natom) for i in xrange(natom): atsize = agspec.get_size(self.numbers[i], self.pseudo_numbers[i]) atgrid = AtomicGrid(self.numbers[i], self.pseudo_numbers[i], self.centers[i], agspec, random_rotate, points[offset:offset + atsize]) if mode != 'only': atbecke_weights = self._becke_weights[offset:offset + atsize] becke_helper_atom(points[offset:offset + atsize], atbecke_weights, cov_radii, self.centers, i, self._k) weights[offset:offset + atsize] = atgrid.weights * atbecke_weights if mode != 'discard': atgrids.append(atgrid) offset += atsize pb() # finish IntGrid.__init__(self, points, weights, atgrids) # Some screen info self._log_init()
def __init__(self, system, agspec='medium', k=3, random_rotate=True, mode='discard'): ''' **Arguments:** system The System object for which the molecular grid must be made. Alternatively, this may also be a tuple (centers, numbers, pseudo_numbers). **Optional arguments:** agspec A specifications of the atomic grid. This can either be an instance of the AtomicGridSpec object, or the first argument of its constructor. k The order of the switching function in Becke's weighting scheme. random_rotate Flag to control random rotation of spherical grids. mode Select one of the following options regarding atomic subgrids: * ``'discard'`` (the default) means that all information about subgrids gets discarded. * ``'keep'`` means that a list of subgrids is kept, including the integration weights of the local grids. * ``'only'`` means that only the subgrids are constructed and that the computation of the molecular integration weights (based on the Becke partitioning) is skipped. ''' if isinstance(system, System): self._centers = system.coordinates.copy() self._numbers = system.numbers.copy() self._pseudo_numbers = system.pseudo_numbers.copy() natom = system.natom else: self._centers, self._numbers, self._pseudo_numbers = system natom = len(self.centers) # check if the mode argument is valid if mode not in ['discard', 'keep', 'only']: raise ValueError('The mode argument must be \'discard\', \'keep\' or \'only\'.') # transform agspec into a usable format if not isinstance(agspec, AtomicGridSpec): agspec = AtomicGridSpec(agspec) self._agspec = agspec # assign attributes self._k = k self._random_rotate = random_rotate self._mode = mode # allocate memory for the grid size = sum(agspec.get_size(self.numbers[i], self.pseudo_numbers[i]) for i in xrange(natom)) points = np.zeros((size, 3), float) weights = np.zeros(size, float) log.mem.announce(points.nbytes + weights.nbytes) # construct the atomic grids if mode != 'discard': atgrids = [] else: atgrids = None offset = 0 if mode != 'only': # More recent covalent radii are used than in the original work of Becke. cov_radii = np.array([periodic[n].cov_radius for n in self.numbers]) # The actual work: if log.do_medium: log('Preparing Becke-Lebedev molecular integration grid.') pb = log.progress(natom) for i in xrange(natom): atsize = agspec.get_size(self.numbers[i], self.pseudo_numbers[i]) atgrid = AtomicGrid( self.numbers[i], self.pseudo_numbers[i], self.centers[i], agspec, random_rotate, points[offset:offset+atsize]) if mode != 'only': weights[offset:offset+atsize] = atgrid.weights becke_helper_atom( points[offset:offset+atsize], weights[offset:offset+atsize], cov_radii, self.centers, i, self._k) if mode != 'discard': atgrids.append(atgrid) offset += atsize pb() # finish IntGrid.__init__(self, points, weights, atgrids) # Some screen info self._log_init()