def apply_to(self, coordinates, numbers): """Construct a GOBasis object for the given molecular geometry **Arguments:** coordinates A (N, 3) float numpy array with Cartesian coordinates of the atoms. numbers A (N,) numpy vector with the atomic numbers. Note that the geometry specified by the arguments may also contain ghost atoms. """ natom, coordinates, numbers = typecheck_geo(coordinates, numbers, need_pseudo_numbers=False) shell_map = [] nprims = [] shell_types = [] alphas = [] con_coeffs = [] def get_basis(i, n): """Look up the basis for a given atom""" basis = self.index_map.get(i) if basis is not None: return basis basis = self.element_map.get(n) if basis is not None: return basis if self.default is None: raise KeyError('Could not find basis for atom %i.' % i) else: return self.default def translate_basis(basis_x, n): """Translate the first argument into a GOBasisAtom instance""" if isinstance(basis_x, basestring): basis_fam = go_basis_families.get(basis_x.lower()) if basis_fam is None: raise ValueError('Unknown basis family: %s' % basis_x) basis_atom = basis_fam.get(n) if basis_atom is None: raise ValueError('The basis family %s does not contain element %i.' % (basis_x, n)) return basis_atom elif isinstance(basis_x, GOBasisFamily): basis_atom = basis_x.get(n) if basis_atom is None: raise ValueError('The basis family %s does not contain element %i.' % (basis_x.name, n)) return basis_atom elif isinstance(basis_x, GOBasisAtom): return basis_x else: raise ValueError('Can not interpret %s as an atomic basis function.' % basis_x) # Loop over the atoms and fill in all the lists for i in xrange(natom): n = numbers[i] basis_x = get_basis(i, n) basis_atom = translate_basis(basis_x, n) basis_atom.extend(i, shell_map, nprims, shell_types, alphas, con_coeffs, self.pure) # Return the Gaussian basis object. return GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs)
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 setup_weights(coordinates, numbers, grid, dens=None, near=None, far=None): '''Define a weight function for the ESPCost **Arguments:** coordinates An array with shape (N, 3) containing atomic coordinates. numbers A vector with shape (N,) containing atomic numbers. grid A UniformGrid object. **Optional arguments:** dens The density-based criterion. This is a three-tuple with rho, lnrho0 and sigma. rho is the atomic or the pro-atomic electron density on the same grid as the ESP data. lnrho0 and sigma are parameters defined in JCTC, 3, 1004 (2007), DOI:10.1021/ct600295n. The weight function takes the form:: exp(-sigma*(ln(rho) - lnrho0)**2) Note that the density, rho, should not contain depletions in the atomic cores, as is often encountered with pseudo-potential computations. In that case it is recommended to construct a promolecular density as input for this option. near Exclude points near the nuclei. This is a dictionary with as items (number, (R0, gamma)). far Exclude points far away. This is a two-tuple: (R0, gamma). ''' natom, coordinates, numbers = typecheck_geo(coordinates, numbers, need_pseudo_numbers=False) weights = np.ones(grid.shape) # combine three possible mask functions if dens is not None: biblio.cite('hu2007', 'for the ESP fitting weight function') rho, lnrho0, sigma = dens assert (rho.shape == grid.shape).all() multiply_dens_mask(rho, lnrho0, sigma, weights) if near is not None: for i in xrange(natom): pair = near.get(numbers[i]) if pair is None: pair = near.get(0) if pair is None: continue r0, gamma = pair if r0 > 5*angstrom: raise ValueError('The wnear radius is excessive. Please keep it below 5 angstrom.') multiply_near_mask(coordinates[i], grid, r0, gamma, weights) if far is not None: r0, gamma = far multiply_far_mask(coordinates, grid, r0, gamma, weights) # double that weight goes to zero at non-periodic edges return weights
def setup_weights(coordinates, numbers, grid, dens=None, near=None, far=None): '''Define a weight function for the ESPCost **Arguments:** coordinates An array with shape (N, 3) containing atomic coordinates. numbers A vector with shape (N,) containing atomic numbers. grid A UniformGrid object. **Optional arguments:** dens The density-based criterion. This is a three-tuple with rho, lnrho0 and sigma. rho is the atomic or the pro-atomic electron density on the same grid as the ESP data. lnrho0 and sigma are parameters defined in JCTC, 3, 1004 (2007), DOI:10.1021/ct600295n. The weight function takes the form:: exp(-sigma*(ln(rho) - lnrho0)**2) Note that the density, rho, should not contain depletions in the atomic cores, as is often encountered with pseudo-potential computations. In that case it is recommended to construct a promolecular density as input for this option. near Exclude points near the nuclei. This is a dictionary with as items (number, (R0, gamma)). far Exclude points far away. This is a two-tuple: (R0, gamma). ''' natom, coordinates, numbers = typecheck_geo(coordinates, numbers, need_pseudo_numbers=False) weights = np.ones(grid.shape) # combine three possible mask functions if dens is not None: biblio.cite('hu2007', 'for the ESP fitting weight function') rho, lnrho0, sigma = dens assert (rho.shape == grid.shape).all() multiply_dens_mask(rho, lnrho0, sigma, weights) if near is not None: for i in xrange(natom): pair = near.get(numbers[i]) if pair is None: pair = near.get(0) if pair is None: continue r0, gamma = pair if r0 > 5 * angstrom: raise ValueError( 'The wnear radius is excessive. Please keep it below 5 angstrom.' ) multiply_near_mask(coordinates[i], grid, r0, gamma, weights) if far is not None: r0, gamma = far multiply_far_mask(coordinates, grid, r0, gamma, weights) # double that weight goes to zero at non-periodic edges return weights
def __init__(self, coordinates, numbers, pseudo_numbers, grid, moldens, spindens, local, lmax): ''' **Arguments:** coordinates An array (N, 3) with centers for the atom-centered grids. numbers An array (N,) with atomic numbers. pseudo_numbers An array (N,) with effective charges. When set to None, this defaults to``numbers.astype(float)``. grid The integration grid moldens The spin-summed electron density on the grid. spindens The spin difference density on the grid. (Can be None) local Whether or not to use local (non-periodic) subgrids for atomic integrals. lmax The maximum angular momentum in multipole expansions. ''' # Init base class JustOnceClass.__init__(self) # Some type checking for first three arguments natom, coordinates, numbers, pseudo_numbers = typecheck_geo(coordinates, numbers, pseudo_numbers) self._natom = natom self._coordinates = coordinates self._numbers = numbers self._pseudo_numbers = pseudo_numbers # Assign remaining arguments as attributes self._grid = grid self._moldens = moldens self._spindens = spindens self._local = local self._lmax = lmax # Caching stuff, to avoid recomputation of earlier results self._cache = Cache() # Initialize the subgrids if local: self._init_subgrids() # Some screen logging self._init_log_base() self._init_log_scheme() self._init_log_memory() if log.do_medium: log.blank()
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, coordinates, numbers, pseudo_numbers, grid, moldens, spindens, local, lmax): ''' **Arguments:** coordinates An array (N, 3) with centers for the atom-centered grids. numbers An array (N,) with atomic numbers. pseudo_numbers An array (N,) with effective charges. When set to None, this defaults to``numbers.astype(float)``. grid The integration grid moldens The spin-summed electron density on the grid. spindens The spin difference density on the grid. (Can be None) local Whether or not to use local (non-periodic) subgrids for atomic integrals. lmax The maximum angular momentum in multipole expansions. ''' # Init base class JustOnceClass.__init__(self) # Some type checking for first three arguments natom, coordinates, numbers, pseudo_numbers = typecheck_geo(coordinates, numbers, pseudo_numbers) self._natom = natom self._coordinates = coordinates self._numbers = numbers self._pseudo_numbers = pseudo_numbers # Assign remaining arguments as attributes self._grid = grid self._moldens = moldens self._spindens = spindens self._local = local self._lmax = lmax # Caching stuff, to avoid recomputation of earlier results self._cache = Cache() # Initialize the subgrids if local: self._init_subgrids() # Some screen logging self._init_log_base() self._init_log_scheme() self._init_log_memory() if log.do_medium: log.blank()