def _init_log_scheme(self): if log.do_medium: log.deflist([ ('Scheme', 'Hirshfeld'), ('Proatomic DB', self.proatomdb), ]) log.cite('hirshfeld1977', 'the use of Hirshfeld partitioning')
def converge_scf_oda(ham, maxiter=128, threshold=1e-6, debug=False): '''Minimize the energy of the wavefunction with optimal-damping SCF **Arguments:** ham A Hamiltonian instance. **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 debug Make debug plots with matplotlib of the linear interpolation **Raises:** NoSCFConvergence if the convergence criteria are not met within the specified number of iterations. ''' log.cite('cances2001', 'using the optimal damping algorithm (ODA) SCF') if isinstance(ham.system.wfn, RestrictedWFN): return converge_scf_oda_cs(ham, maxiter, threshold, debug) elif isinstance(ham.system.wfn, UnrestrictedWFN): return converge_scf_oda_os(ham, maxiter, threshold, debug) else: raise NotImplementedError
def __init__(self, prefix, name): self.exchange = name.startswith('x') name = '%s_%s' % (prefix, name) self._name = name self._libxc_wrapper = LibXCWrapper(name) log.cite('marques2012', 'using LibXC, the library of exchange and correlation functionals') Observable.__init__(self, 'libxc_%s' % name)
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. ''' log.cite('kudin2002', 'the EDIIS method.') DIISSCFSolver.__init__(self, EDIIS2History, threshold, maxiter, nvector, skip_energy, prune_old_states)
def setup_weights(system, grid, dens=None, near=None, far=None): '''Define a weight function for the ESPCost **Arguments:** system The system for which the weight function must be defined 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). ''' weights = np.ones(grid.shape) # combine three possible mask functions if dens is not None: log.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(system.natom): pair = near.get(system.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(system.coordinates[i], grid, r0, gamma, weights) if far is not None: r0, gamma = far multiply_far_mask(system.coordinates, grid, r0, gamma, weights) # double that weight goes to zero at non-periodic edges return weights
def _init_log_scheme(self): if log.do_medium: log.deflist([ ('Scheme', 'Iterative Stockholder'), ('Convergence threshold', '%.1e' % self._threshold), ('Maximum iterations', self._maxiter), ]) log.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), ]) log.cite('becke1988_multicenter', 'the use of Becke partitioning') log.cite('slater1964', 'the Brag-Slater radii used in the Becke partitioning')
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), ]) log.cite('verstraelen2016', 'the use of MBIS partitioning')
def __init__(self, prefix, name): self.exchange = name.startswith('x') name = '%s_%s' % (prefix, name) self._name = name self._libxc_wrapper = LibXCWrapper(name) log.cite( 'marques2012', 'using LibXC, the library of exchange and correlation functionals') Observable.__init__(self, 'libxc_%s' % name)
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), ]) log.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). ''' log.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_medium: log('Initialized: %s' % self) log.deflist([ ('Size', self.size), ('Switching function', 'k=%i' % self._k), ]) log.blank() # Cite reference log.cite('becke1988_multicenter', 'the multicenter integration scheme used for the molecular integration grid') log.cite('cordero2008', 'the covalent radii used for the Becke-Lebedev molecular integration grid')
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). ''' log.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 log.cite('lebedev1999', 'the use of Lebedev-Laikov grids (quadrature on a sphere)')
def __init__(self, name): ''' **Arguments:** name 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) log.cite('marques2012', '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) log.cite('rabuck1999', 'the Fermi broading method to assign orbital occupations')
def __init__(self, name): ''' **Arguments:** name 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) log.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) log.cite( 'marques2012', 'using LibXC, the library of exchange and correlation functionals') GridObservable.__init__(self, 'libxc_%s' % name)
def converge_scf_ediis(ham, maxiter=128, threshold=1e-6, nvector=6, prune_old_states=False, scf_step='regular'): '''Minimize the energy of the wavefunction with the EDIIS algorithm **Arguments:** ham A Hamiltonian instance. **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 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. scf_step The type of SCF step to take after the interpolated states was create from the DIIS history. This can be 'regular', 'oda2' or 'oda3'. **Raises:** NoSCFConvergence if the convergence criteria are not met within the specified number of iterations. **Returns:** the number of iterations ''' log.cite('kudin2002', 'using the energy DIIS SCF algorithm') if isinstance(ham.system.wfn, RestrictedWFN): return converge_scf_ediis_cs(ham, maxiter, threshold, nvector, prune_old_states, scf_step) else: raise NotImplementedError
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) log.cite('rabuck1999', 'the Fermi broading method to assign orbital occupations')
def converge_scf_cdiis(ham, maxiter=128, threshold=1e-6, nvector=6, prune_old_states=False, skip_energy=False, scf_step='regular'): '''Minimize the energy of the wavefunction with the CDIIS algorithm **Arguments:** ham A Hamiltonian instance. **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 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. skip_energy When set to True, the final energy is not computed. scf_step The type of SCF step to take after the interpolated states was create from the DIIS history. This can be 'regular', 'oda2' or 'oda3'. **Raises:** NoSCFConvergence if the convergence criteria are not met within the specified number of iterations. **Returns:** the number of iterations ''' log.cite('pulay1980', 'using the commutator DIIS SCF algorithm') if isinstance(ham.system.wfn, RestrictedWFN): return converge_scf_cdiis_cs(ham, maxiter, threshold, nvector, prune_old_states, skip_energy, scf_step) else: raise NotImplementedError
def converge_scf_cdiis_cs(ham, maxiter=128, threshold=1e-6, nvector=6, prune_old_states=False, skip_energy=False, scf_step='regular'): '''Minimize the energy of the closed-shell wavefunction with CDIIS **Arguments:** ham A Hamiltonian instance. **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 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. skip_energy When set to True, the final energy is not computed. scf_step The type of SCF step to take after the interpolated states was create from the DIIS history. This can be 'regular', 'oda2' or 'oda3'. **Raises:** NoSCFConvergence if the convergence criteria are not met within the specified number of iterations. **Returns:** the number of iterations ''' log.cite('pulay1980', 'the use of the commutator DIIS method') return converge_scf_diis_cs(ham, PulayDIISHistory, maxiter, threshold, nvector, prune_old_states, skip_energy, scf_step)
def converge_scf_ediis_cs(ham, maxiter=128, threshold=1e-6, nvector=6, prune_old_states=False, scf_step='regular'): '''Minimize the energy of the closed-shell wavefunction with EDIIS **Arguments:** ham A Hamiltonian instance. **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 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. scf_step The type of SCF step to take after the interpolated states was create from the DIIS history. This can be 'regular', 'oda2' or 'oda3'. **Raises:** NoSCFConvergence if the convergence criteria are not met within the specified number of iterations. **Returns:** the number of iterations ''' log.cite('kudin2002', 'the use of the EDIIS method.') return converge_scf_diis_cs(ham, EnergyDIISHistory, maxiter, threshold, nvector, prune_old_states, scf_step)
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. ''' log.cite('pulay1980', 'the commutator DIIS SCF algorithm') DIISSCFSolver.__init__(self, CDIISHistory, 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: log.cite( 'tkatchenko2009', 'the method to evaluate atoms-in-molecules C6 parameters') log.cite('chu2004', 'the reference C6 parameters of isolated atoms') log.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: log.cite('tkatchenko2009', 'the method to evaluate atoms-in-molecules C6 parameters') log.cite('chu2004', 'the reference C6 parameters of isolated atoms') log.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_populations() self.do_moments() radial_moments = self._cache.load('radial_moments') populations = self._cache.load('populations') if log.do_medium: log('Computing atomic dispersion coefficients.') for i in xrange(self.natom): n = self.numbers[i] volumes[i] = radial_moments[i,2]/populations[i] ref_volume = self.proatomdb.get_record(n, 0).get_moment(3)/n 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 __call__(self, orb, select, **kwargs): '''Localizes the orbitals using a unitary transformation to rotate the AO/MO coefficient matrix. The orbitals are optimized by minimizing an objective function. This works only for restricted orbitals. **Arguments:** orb The AO/MO coefficients. An Expansion instance. select The orbital block to be localised (str). Any of ``occ`` (occupied orbitals), ``virt`` (virtual orbitals) **Keywords:** :maxiter: (int) maximum number of iterations for localization (default 2000) :threshold: (float) localization threshold for objective function (default 1e-6) :levelshift: level shift of Hessian (float) (default 1e-8) :stepsearch: step search options (dictionary) containing: * method: step search method used (str). One of ``trust-region`` (default), ``None``, ``backtracking`` * alpha: scaling factor for Newton step (float), used in ``backtracking`` and ``None`` method (default 0.75) * c1: parameter used in ``backtracking`` (float) (default 1e-4) * minalpha: minimum step length used in ``backracking`` (float) (default 1e-6) * maxiterouter: maximum number of search steps (int) (default 10) * maxiterinner: maximum number of optimization steps in each search step (int) (used only in ``pcg``, default 500) * maxeta: upper bound for estimated vs actual change in ``trust-region`` (float) (default 0.75) * mineta: lower bound for estimated vs actual change in ``trust-region`` (float) (default 0.25) * upscale: scaling factor to increase trustradius in ``trust-region`` (float) (default 2.0) * downscale: scaling factor to decrease trustradius in ``trust-region`` (float) and scaling factor in ``backtracking`` (default 0.25) * trustradius: initial trustradius (float) (default 0.75) * maxtrustradius: maximum trustradius (float) (default 0.75) * threshold: trust-region optimization threshold, only used in ``pcg`` (float) (default 1e-8) * optimizer: optimizes step to boundary of trustradius (str). One of ``pcg``, ``dogleg``, ``ddl`` (default ddl) ''' if log.do_medium: log('Performing localization of %s block' % (select)) log.cite('pipek1989', 'the Pipek-Mezey localization scheme') # # Assign default keyword arguements # names = [] def _helper(x, y): names.append(x) return kwargs.get(x, y) maxiter = _helper('maxiter', 2000) thresh = _helper('threshold', 1e-6) lshift = _helper('levelshift', 1e-8) stepsearch = _helper('stepsearch', dict({})) stepsearch.setdefault('method', 'trust-region') stepsearch.setdefault('minalpha', 1e-6) stepsearch.setdefault('alpha', 1.0) stepsearch.setdefault('c1', 0.0001) stepsearch.setdefault('maxiterouter', 10) stepsearch.setdefault('maxiterinner', 500) stepsearch.setdefault('maxeta', 0.75) stepsearch.setdefault('mineta', 0.25) stepsearch.setdefault('upscale', 2.0) stepsearch.setdefault('downscale', 0.25) stepsearch.setdefault('trustradius', 0.75) stepsearch.setdefault('maxtrustradius', 0.75) stepsearch.setdefault('threshold', 1e-8) stepsearch.setdefault('optimizer', 'ddl') for name, value in kwargs.items(): if name not in names: raise ValueError("Unknown keyword argument %s" % name) if value < 0: raise ValueError('Illegal value for %s: %s' % (name, value)) # # Update information about localization block # self.update_locblock(select) if log.do_medium: log('%3s %12s %10s' % ('Iter', 'D(ObjectiveFunction)', 'Steplength')) # # Initialize step search # stepsearch_ = RStepSearch(self.lf, **stepsearch) # # Calculate initial objective function # self.solve_model(orb) objfct_ref = self.compute_objective_function() maxThresh = True maxIter = True it = 0 while maxThresh and maxIter: # # Update population matrix for new orbitals # self.compute_population_matrix(orb) # # Calculate orbital gradient and diagonal approximation to the Hessian # kappa, gradient, hessian = self.orbital_rotation_step(lshift) # # Apply steps search to orbital rotation step 'kappa' and perform # orbital rotation # stepsearch_( self, None, None, orb, **{ 'kappa': kappa, 'gradient': gradient, 'hessian': hessian }) # # update objective function # objfct = self.compute_objective_function() it += 1 # # Print localization progress # if log.do_medium: log('%4i %14.8f' % (it, abs(objfct - objfct_ref))) # # Check convergence # maxThresh = abs(objfct - objfct_ref) > thresh maxIter = it < maxiter # # Prepare for new iteration # objfct_ref = objfct if maxThresh and not maxIter: if log.do_medium: log(' ') log('Warning: Orbital localization not converged in %i iteration' % (it - 1)) log(' ') else: if log.do_medium: log(' ') log('Orbital localization converged in %i iteration' % (it - 1)) log(' ')
def __call__(self, orb, select, **kwargs): '''Localizes the orbitals using a unitary transformation to rotate the AO/MO coefficient matrix. The orbitals are optimized by minimizing an objective function. This works only for restricted orbitals. **Arguments:** orb The AO/MO coefficients. An Expansion instance. select The orbital block to be localised (str). Any of ``occ`` (occupied orbitals), ``virt`` (virtual orbitals) **Keywords:** :maxiter: (int) maximum number of iterations for localization (default 2000) :threshold: (float) localization threshold for objective function (default 1e-6) :levelshift: level shift of Hessian (float) (default 1e-8) :stepsearch: step search options (dictionary) containing: * method: step search method used (str). One of ``trust-region`` (default), ``None``, ``backtracking`` * alpha: scaling factor for Newton step (float), used in ``backtracking`` and ``None`` method (default 0.75) * c1: parameter used in ``backtracking`` (float) (default 1e-4) * minalpha: minimum step length used in ``backracking`` (float) (default 1e-6) * maxiterouter: maximum number of search steps (int) (default 10) * maxiterinner: maximum number of optimization steps in each search step (int) (used only in ``pcg``, default 500) * maxeta: upper bound for estimated vs actual change in ``trust-region`` (float) (default 0.75) * mineta: lower bound for estimated vs actual change in ``trust-region`` (float) (default 0.25) * upscale: scaling factor to increase trustradius in ``trust-region`` (float) (default 2.0) * downscale: scaling factor to decrease trustradius in ``trust-region`` (float) and scaling factor in ``backtracking`` (default 0.25) * trustradius: initial trustradius (float) (default 0.75) * maxtrustradius: maximum trustradius (float) (default 0.75) * threshold: trust-region optimization threshold, only used in ``pcg`` (float) (default 1e-8) * optimizer: optimizes step to boundary of trustradius (str). One of ``pcg``, ``dogleg``, ``ddl`` (default ddl) ''' if log.do_medium: log('Performing localization of %s block' %(select)) log.cite('pipek1989', 'the Pipek-Mezey localization scheme') # # Assign default keyword arguements # names = [] def _helper(x,y): names.append(x) return kwargs.get(x,y) maxiter = _helper('maxiter', 2000) thresh = _helper('threshold', 1e-6) lshift = _helper('levelshift', 1e-8) stepsearch = _helper('stepsearch', dict({})) stepsearch.setdefault('method', 'trust-region') stepsearch.setdefault('minalpha', 1e-6) stepsearch.setdefault('alpha', 1.0) stepsearch.setdefault('c1', 0.0001) stepsearch.setdefault('maxiterouter', 10) stepsearch.setdefault('maxiterinner', 500) stepsearch.setdefault('maxeta', 0.75) stepsearch.setdefault('mineta', 0.25) stepsearch.setdefault('upscale', 2.0) stepsearch.setdefault('downscale', 0.25) stepsearch.setdefault('trustradius', 0.75) stepsearch.setdefault('maxtrustradius', 0.75) stepsearch.setdefault('threshold', 1e-8) stepsearch.setdefault('optimizer', 'ddl') for name, value in kwargs.items(): if name not in names: raise ValueError("Unknown keyword argument %s" % name) if value < 0: raise ValueError('Illegal value for %s: %s' %(name, value)) # # Update information about localization block # self.update_locblock(select) if log.do_medium: log('%3s %12s %10s' %('Iter', 'D(ObjectiveFunction)', 'Steplength')) # # Initialize step search # stepsearch_ = RStepSearch(self.lf, **stepsearch) # # Calculate initial objective function # self.solve_model(orb) objfct_ref = self.compute_objective_function() maxThresh = True maxIter = True it = 0 while maxThresh and maxIter: # # Update population matrix for new orbitals # self.compute_population_matrix(orb) # # Calculate orbital gradient and diagonal approximation to the Hessian # kappa, gradient, hessian = self.orbital_rotation_step(lshift) # # Apply steps search to orbital rotation step 'kappa' and perform # orbital rotation # stepsearch_(self, None, None, orb, **{'kappa': kappa, 'gradient': gradient, 'hessian': hessian }) # # update objective function # objfct = self.compute_objective_function() it += 1 # # Print localization progress # if log.do_medium: log('%4i %14.8f' %(it, abs(objfct-objfct_ref))) # # Check convergence # maxThresh = abs(objfct-objfct_ref)>thresh maxIter = it<maxiter # # Prepare for new iteration # objfct_ref = objfct if maxThresh and not maxIter: if log.do_medium: log(' ') log('Warning: Orbital localization not converged in %i iteration' %(it-1)) log(' ') else: if log.do_medium: log(' ') log('Orbital localization converged in %i iteration' %(it-1)) log(' ')
def setup_weights(system, grid, dens=None, near=None, far=None): '''Define a weight function for the ESPCost **Arguments:** system The system for which the weight function must be defined 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). ''' weights = np.ones(grid.shape) # combine three possible mask functions if dens is not None: log.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(system.natom): pair = near.get(system.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(system.coordinates[i], grid, r0, gamma, weights) if far is not None: r0, gamma = far multiply_far_mask(system.coordinates, grid, r0, gamma, weights) # double that weight goes to zero at non-periodic edges return weights