def _init_log_scheme(self): if log.do_medium: log.deflist([ ('Scheme', 'Hirshfeld'), ('Proatomic DB', self.proatomdb), ]) biblio.cite('hirshfeld1977', 'the use of Hirshfeld partitioning')
def __init__(self, threshold=1e-6, maxiter=128, nvector=6, skip_energy=False, prune_old_states=False): ''' **Optional arguments:** maxiter The maximum number of iterations. When set to None, the SCF loop will go one until convergence is reached. threshold The convergence threshold for the wavefunction skip_energy When set to True, the final energy is not computed. Note that some DIIS variants need to compute the energy anyway. for these methods this option is irrelevant. prune_old_states When set to True, old states are pruned from the history when their coefficient is zero. Pruning starts at the oldest state and stops as soon as a state is encountered with a non-zero coefficient. Even if some newer states have a zero coefficient. ''' biblio.cite('kudin2002', 'the EDIIS method.') DIISSCFSolver.__init__(self, EDIIS2History, threshold, maxiter, nvector, skip_energy, prune_old_states)
def _init_log_scheme(self): if log.do_medium: log.deflist([ ('Scheme', 'Minimal Basis Iterative Stockholder (MBIS)'), ('Convergence threshold', '%.1e' % self._threshold), ('Maximum iterations', self._maxiter), ]) biblio.cite('verstraelen2016', 'the use of MBIS partitioning')
def _init_log_scheme(self): if log.do_medium: log.deflist([ ('Scheme', 'Iterative Stockholder'), ('Convergence threshold', '%.1e' % self._threshold), ('Maximum iterations', self._maxiter), ]) biblio.cite('lillestolen2008', 'the use of Iterative Stockholder partitioning')
def _init_log_scheme(self): if log.do_medium: log.deflist([ ('Scheme', 'Becke'), ('Switching function', 'k=%i' % self._k), ]) biblio.cite('becke1988_multicenter', 'the use of Becke partitioning') biblio.cite('slater1964', 'the Brag-Slater radii used in the Becke partitioning')
def _init_log_scheme(self): if log.do_medium: log.deflist([ ('Scheme', 'Hirshfeld-I'), ('Convergence threshold', '%.1e' % self._threshold), ('Maximum iterations', self._maxiter), ('Proatomic DB', self._proatomdb), ]) biblio.cite('bultinck2007', 'the use of Hirshfeld-I partitioning')
def solve_poisson_becke(density_decomposition): '''Compute the electrostatic potential of a density expanded in real spherical harmonics **Arguments:** density_decomposition A list of cubic splines returned by the method AtomicGrid.get_spherical_decomposition. The returned list of splines is a spherical decomposition of the hartree potential (felt by a particle with the same charge unit as the density). ''' biblio.cite('becke1988_poisson', 'the numerical integration of the Poisson equation') lmax = np.sqrt(len(density_decomposition)) - 1 assert lmax == int(lmax) lmax = int(lmax) result = [] counter = 0 for l in xrange(0, lmax + 1): for m in xrange(-l, l + 1): rho = density_decomposition[counter] rtf = rho.rtransform rgrid = RadialGrid(rtf) radii = rtf.get_radii() # The approach followed here is obtained after substitution of # u = r*V in Eq. (21) in Becke's paper. After this transformation, # the boundary conditions can be implemented such that the output # is more accurate. fy = -4 * np.pi * rho.y fd = -4 * np.pi * rho.dx f = CubicSpline(fy, fd, rtf) b = CubicSpline(2 / radii, -2 / radii**2, rtf) a = CubicSpline(-l * (l + 1) * radii**-2, 2 * l * (l + 1) * radii**-3, rtf) # Derivation of boundary condition at rmax: # Multiply differential equation with r**l and integrate. Using # partial integration and the fact that V(r)=A/r**(l+1) for large # r, we find -(2l+1)A=-4pi*int_0^infty r**2 r**l rho(r) and so # V(rmax) = A/rmax**(l+1) = integrate(r**l rho(r))/(2l+1)/rmax**(l+1) V_rmax = rgrid.integrate( rho.y * radii**l) / radii[-1]**(l + 1) / (2 * l + 1) # Derivation of boundary condition at rmin: # Same as for rmax, but multiply differential equation with r**(-l-1) # and assume that V(r)=B*r**l for small r. V_rmin = rgrid.integrate( rho.y * radii**(-l - 1)) * radii[0]**(l) / (2 * l + 1) bcs = (V_rmin, None, V_rmax, None) v = solve_ode2(b, a, f, bcs, PotentialExtrapolation(l)) result.append(v) counter += 1 return result
def _init_log_scheme(self): if log.do_medium: log.deflist( [ ("Scheme", "Iterative Stockholder"), ("Convergence threshold", "%.1e" % self._threshold), ("Maximum iterations", self._maxiter), ] ) biblio.cite("lillestolen2008", "the use of Iterative Stockholder partitioning")
def _init_log_scheme(self): if log.do_medium: log.deflist([ ('Scheme', 'Becke'), ('Switching function', 'k=%i' % self._k), ]) biblio.cite('becke1988_multicenter', 'the use of Becke partitioning') biblio.cite( 'slater1964', 'the Brag-Slater radii used in the Becke partitioning')
def _log_init(self): if log.do_medium: log('Initialized: %s' % self) log.deflist([ ('Size', self.size), ('Switching function', 'k=%i' % self._k), ]) log.blank() # Cite reference biblio.cite('becke1988_multicenter', 'the multicenter integration scheme used for the molecular integration grid') biblio.cite('cordero2008', 'the covalent radii used for the Becke-Lebedev molecular integration grid')
def _log_init(self): if log.do_high: log('Initialized: %s' % self) log.deflist([ ('Size', self.size), ('Number of radii', self.nsphere), ('Min LL sphere', self._nlls.min()), ('Max LL sphere', self._nlls.max()), ('Radial Transform', self._rgrid.rtransform.to_string()), ]) # Cite reference biblio.cite('lebedev1999', 'the use of Lebedev-Laikov grids (quadrature on a sphere)')
def solve_poisson_becke(density_decomposition): '''Compute the electrostatic potential of a density expanded in real spherical harmonics **Arguments:** density_decomposition A list of cubic splines returned by the method AtomicGrid.get_spherical_decomposition. The returned list of splines is a spherical decomposition of the hartree potential (felt by a particle with the same charge unit as the density). ''' biblio.cite('becke1988_poisson', 'the numerical integration of the Poisson equation') lmax = np.sqrt(len(density_decomposition)) - 1 assert lmax == int(lmax) lmax = int(lmax) result = [] counter = 0 for l in xrange(0, lmax+1): for m in xrange(-l, l+1): rho = density_decomposition[counter] rtf = rho.rtransform rgrid = RadialGrid(rtf) radii = rtf.get_radii() # The approach followed here is obtained after substitution of # u = r*V in Eq. (21) in Becke's paper. After this transformation, # the boundary conditions can be implemented such that the output # is more accurate. fy = -4*np.pi*rho.y fd = -4*np.pi*rho.dx f = CubicSpline(fy, fd, rtf) b = CubicSpline(2/radii, -2/radii**2, rtf) a = CubicSpline(-l*(l+1)*radii**-2, 2*l*(l+1)*radii**-3, rtf) # Derivation of boundary condition at rmax: # Multiply differential equation with r**l and integrate. Using # partial integration and the fact that V(r)=A/r**(l+1) for large # r, we find -(2l+1)A=-4pi*int_0^infty r**2 r**l rho(r) and so # V(rmax) = A/rmax**(l+1) = integrate(r**l rho(r))/(2l+1)/rmax**(l+1) V_rmax = rgrid.integrate(rho.y*radii**l)/radii[-1]**(l+1)/(2*l+1) # Derivation of boundary condition at rmin: # Same as for rmax, but multiply differential equation with r**(-l-1) # and assume that V(r)=B*r**l for small r. V_rmin = rgrid.integrate(rho.y*radii**(-l-1))*radii[0]**(l)/(2*l+1) bcs = (V_rmin, None, V_rmax, None) v = solve_ode2(b, a, f, bcs, PotentialExtrapolation(l)) result.append(v) counter += 1 return result
def _log_init(self): if log.do_high: log('Initialized: %s' % self) log.deflist([ ('Size', self.size), ('Number of radii', self.nsphere), ('Min LL sphere', self._nlls.min()), ('Max LL sphere', self._nlls.max()), ('Radial Transform', self._rgrid.rtransform.to_string()), ('1D Integrator', self._rgrid.int1d), ]) # Cite reference biblio.cite('lebedev1999', 'the use of Lebedev-Laikov grids (quadrature on a sphere)')
def __init__(self, name): """Initialize a LibXCEnergy instance. Parameters ---------- name : str The name of the functional in LibXC, without the ``lda_``, ``gga_`` or ``hyb_gga_`` prefix. (The type of functional is determined by the subclass.) """ name = '%s_%s' % (self.prefix, name) self._name = name self._libxc_wrapper = self.LibXCWrapper(name) biblio.cite('marques2012', 'using LibXC, the library of exchange and correlation functionals') GridObservable.__init__(self, 'libxc_%s' % name)
def __init__(self, name): """Initialize a LibXCEnergy instance. Parameters ---------- name : str The name of the functional in LibXC, without the ``lda_``, ``gga_`` or ``hyb_gga_`` prefix. (The type of functional is determined by the subclass.) """ name = '%s_%s' % (self.prefix, name) self._name = name self._libxc_wrapper = self.LibXCWrapper(name) biblio.cite('lehtola2018', 'using LibXC, the library of exchange and correlation functionals') GridObservable.__init__(self, 'libxc_%s' % name)
def __init__(self, *noccs, **kwargs): r''' **Arguments:** nalpha, nbeta, ... The number of electrons in each channel. **Optional keyword arguments:** temperature Controls the width of the distribution (derivative) eps The error on the sum of the occupation number when searching for the right Fermi level. For each channel, the orbital occupations are assigned with the Fermi distribution: .. math:: n_i = \frac{1}{1 + e^{(\epsilon_i - \mu)/k_B T}} where, for a given set of energy levels, :math:`\{\epsilon_i\}`, the chemical potential, :math:`\mu`, is optimized as to satisfy the following constraint: .. math:: \sum_i n_i = n_\text{occ} where :math:`n_\text{occ}` can be set per (spin) channel. This is only a part of the methodology presented in [rabuck1999]_. ''' temperature = kwargs.pop('temperature', 300) eps = kwargs.pop('eps', 1e-8) if len(kwargs) > 0: raise TypeError('Unknown keyword arguments: %s' % kwargs.keys()) if temperature <= 0: raise ValueError('The temperature must be strictly positive') if eps <= 0: raise ValueError( 'The root-finder threshold (eps) must be strictly positive.') self.temperature = float(temperature) self.eps = eps AufbauOccModel.__init__(self, *noccs) biblio.cite('rabuck1999', 'the Fermi broading method to assign orbital occupations')
def __init__(self, *noccs, **kwargs): r''' **Arguments:** nalpha, nbeta, ... The number of electrons in each channel. **Optional keyword arguments:** temperature Controls the width of the distribution (derivative) eps The error on the sum of the occupation number when searching for the right Fermi level. For each channel, the orbital occupations are assigned with the Fermi distribution: .. math:: n_i = \frac{1}{1 + e^{(\epsilon_i - \mu)/k_B T}} where, for a given set of energy levels, :math:`\{\epsilon_i\}`, the chemical potential, :math:`\mu`, is optimized as to satisfy the following constraint: .. math:: \sum_i n_i = n_\text{occ} where :math:`n_\text{occ}` can be set per (spin) channel. This is only a part of the methodology presented in [rabuck1999]_. ''' temperature = kwargs.pop('temperature', 300) eps = kwargs.pop('eps', 1e-8) if len(kwargs) > 0: raise TypeError('Unknown keyword arguments: %s' % kwargs.keys()) if temperature <= 0: raise ValueError('The temperature must be strictly positive') if eps <= 0: raise ValueError('The root-finder threshold (eps) must be strictly positive.') self.temperature = float(temperature) self.eps = eps AufbauOccModel.__init__(self, *noccs) biblio.cite('rabuck1999', 'the Fermi broading method to assign orbital occupations')
def _log_init(self): if log.do_medium: log('Initialized: %s' % self) log.deflist([ ('Size', self.size), ('Switching function', 'k=%i' % self._k), ]) log.blank() # Cite reference biblio.cite( 'becke1988_multicenter', 'the multicenter integration scheme used for the molecular integration grid' ) biblio.cite( 'cordero2008', 'the covalent radii used for the Becke-Lebedev molecular integration grid' )
def __init__(self, threshold=1e-6, maxiter=128, nvector=6, skip_energy=False, prune_old_states=False): ''' **Optional arguments:** maxiter The maximum number of iterations. When set to None, the SCF loop will go one until convergence is reached. threshold The convergence threshold for the wavefunction skip_energy When set to True, the final energy is not computed. Note that some DIIS variants need to compute the energy anyway. for these methods this option is irrelevant. prune_old_states When set to True, old states are pruned from the history when their coefficient is zero. Pruning starts at the oldest state and stops as soon as a state is encountered with a non-zero coefficient. Even if some newer states have a zero coefficient. ''' biblio.cite('kudin2002', 'the EDIIS method.') DIISSCFSolver.__init__(self, EDIISHistory, threshold, maxiter, nvector, skip_energy, prune_old_states)
def do_dispersion(self): if self.lmax < 3: if log.do_warning: log.warn( 'Skipping the computation of dispersion coefficients because lmax=%i<3' % self.lmax) return if log.do_medium: biblio.cite( 'tkatchenko2009', 'the method to evaluate atoms-in-molecules C6 parameters') biblio.cite('chu2004', 'the reference C6 parameters of isolated atoms') biblio.cite('yan1996', 'the isolated hydrogen C6 parameter') ref_c6s = { # reference C6 values in atomic units 1: 6.499, 2: 1.42, 3: 1392.0, 4: 227.0, 5: 99.5, 6: 46.6, 7: 24.2, 8: 15.6, 9: 9.52, 10: 6.20, 11: 1518.0, 12: 626.0, 13: 528.0, 14: 305.0, 15: 185.0, 16: 134.0, 17: 94.6, 18: 64.2, 19: 3923.0, 20: 2163.0, 21: 1383.0, 22: 1044.0, 23: 832.0, 24: 602.0, 25: 552.0, 26: 482.0, 27: 408.0, 28: 373.0, 29: 253.0, 30: 284.0, 31: 498.0, 32: 354.0, 33: 246.0, 34: 210.0, 35: 162.0, 36: 130.0, 37: 4769.0, 38: 3175.0, 49: 779.0, 50: 659.0, 51: 492.0, 52: 445.0, 53: 385.0, } volumes, new_volumes = self._cache.load('volumes', alloc=self.natom, tags='o') volume_ratios, new_volume_ratios = self._cache.load('volume_ratios', alloc=self.natom, tags='o') c6s, new_c6s = self._cache.load('c6s', alloc=self.natom, tags='o') if new_volumes or new_volume_ratios or new_c6s: self.do_moments() radial_moments = self._cache.load('radial_moments') if log.do_medium: log('Computing atomic dispersion coefficients.') for i in xrange(self.natom): n = self.numbers[i] volumes[i] = radial_moments[i, 3] ref_volume = self.proatomdb.get_record(n, 0).get_moment(3) volume_ratios[i] = volumes[i] / ref_volume if n in ref_c6s: c6s[i] = (volume_ratios[i])**2 * ref_c6s[n] else: c6s[i] = -1 # This is just to indicate that no value is available.
def do_dispersion(self): if self.lmax < 3: if log.do_warning: log.warn('Skipping the computation of dispersion coefficients because lmax=%i<3' % self.lmax) return if log.do_medium: biblio.cite('tkatchenko2009', 'the method to evaluate atoms-in-molecules C6 parameters') biblio.cite('chu2004', 'the reference C6 parameters of isolated atoms') biblio.cite('yan1996', 'the isolated hydrogen C6 parameter') ref_c6s = { # reference C6 values in atomic units 1: 6.499, 2: 1.42, 3: 1392.0, 4: 227.0, 5: 99.5, 6: 46.6, 7: 24.2, 8: 15.6, 9: 9.52, 10: 6.20, 11: 1518.0, 12: 626.0, 13: 528.0, 14: 305.0, 15: 185.0, 16: 134.0, 17: 94.6, 18: 64.2, 19: 3923.0, 20: 2163.0, 21: 1383.0, 22: 1044.0, 23: 832.0, 24: 602.0, 25: 552.0, 26: 482.0, 27: 408.0, 28: 373.0, 29: 253.0, 30: 284.0, 31: 498.0, 32: 354.0, 33: 246.0, 34: 210.0, 35: 162.0, 36: 130.0, 37: 4769.0, 38: 3175.0, 49: 779.0, 50: 659.0, 51: 492.0, 52: 445.0, 53: 385.0, } volumes, new_volumes = self._cache.load('volumes', alloc=self.natom, tags='o') volume_ratios, new_volume_ratios = self._cache.load('volume_ratios', alloc=self.natom, tags='o') c6s, new_c6s = self._cache.load('c6s', alloc=self.natom, tags='o') if new_volumes or new_volume_ratios or new_c6s: self.do_moments() radial_moments = self._cache.load('radial_moments') if log.do_medium: log('Computing atomic dispersion coefficients.') for i in xrange(self.natom): n = self.numbers[i] volumes[i] = radial_moments[i, 3] ref_volume = self.proatomdb.get_record(n, 0).get_moment(3) volume_ratios[i] = volumes[i]/ref_volume if n in ref_c6s: c6s[i] = (volume_ratios[i])**2*ref_c6s[n] else: c6s[i] = -1 # This is just to indicate that no value is available.
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