Example #1
0
 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')
Example #2
0
    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)
Example #3
0
 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')
Example #4
0
 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')
Example #5
0
 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')
Example #6
0
 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')
Example #7
0
 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')
Example #8
0
 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')
Example #9
0
 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')
Example #10
0
 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')
Example #11
0
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
Example #12
0
 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")
Example #13
0
 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')
Example #14
0
 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')
Example #15
0
 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)')
Example #16
0
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
Example #17
0
 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)')
Example #18
0
    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)
Example #19
0
    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)
Example #20
0
    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')
Example #21
0
    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')
Example #22
0
 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'
     )
Example #23
0
    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)
Example #24
0
    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.
Example #25
0
    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.
Example #26
0
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
Example #27
0
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