示例#1
0
文件: poisson.py 项目: kumrud/horton
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
示例#2
0
    def get_spline(self, number, parameters=0, combine='linear'):
        '''Construct a proatom spline.

           **Arguments:** See ``get_rho`` method.
        '''
        rho, deriv = self.get_rho(number, parameters, combine, do_deriv=True)
        return CubicSpline(rho, deriv, self.get_rgrid(number).rtransform)
示例#3
0
def get_cosine_spline():
    # Construct a simple spline for the function cos(x)+1 in the range 0,pi
    rtf = LinearRTransform(0.0, np.pi, 100)
    x = rtf.get_radii()
    y = np.cos(x) + 1
    d = -np.sin(x)
    return CubicSpline(y, d, rtf)
示例#4
0
def solve_ode2(b, a, f, bcs, extrapolation=None):
    '''Solve a second order ODE.

       **Arguments:**

       b, a, f
            Cubic splines for the given functions in the second order ODE. (See
            build_neumann for details.) These cubic splines must have identical
            RTransform objects.

       bcs
            The boundary conditions (See build_neumann for details.)

       **Optional arguments:**

       extrapolation
            The extrapolation object for the returned cubic spline.

       **Returns:** a cubic spline object with the solution that uses the same
       RTransform object as the input functions a, b and f.
    '''
    # Parse args.
    rtf = b.rtransform
    if rtf.to_string() != a.rtransform.to_string():
        raise ValueError('The RTransform objects of b and a do not match.')
    if rtf.to_string() != f.rtransform.to_string():
        raise ValueError('The RTransform objects of b and f do not match.')

    # Transform the given functions to the linear coordinate.
    j1 = rtf.get_deriv()
    j2 = rtf.get_deriv2()
    j3 = rtf.get_deriv3()
    j1sq = j1 * j1
    by_new = j1 * b.y - j2 / j1
    bd_new = j2 * b.y + j1sq * b.dx + (j2 * j2 - j1 * j3) / j1sq
    ay_new = a.y * j1sq
    ad_new = (a.dx * j1sq + 2 * a.y * j2) * j1
    fy_new = f.y * j1sq
    fd_new = (f.dx * j1sq + 2 * f.y * j2) * j1

    # Transform the boundary conditions
    new_bcs = (
        bcs[0],
        None if bcs[1] is None else bcs[1] * j1[0],
        bcs[2],
        None if bcs[3] is None else bcs[3] * j1[-1],
    )

    # Call the equation builder.
    coeffs, rhs = build_ode2(by_new, bd_new, ay_new, ad_new, fy_new, fd_new,
                             new_bcs)
    solution = spsolve(csc_matrix(coeffs), rhs)
    uy_new = solution[::2]
    ud_new = solution[1::2]

    # Transform solution back to the original coordinate.
    uy_orig = uy_new.copy()  # A copy of is needed to obtain contiguous arrays.
    ud_orig = ud_new / j1

    return CubicSpline(uy_orig, ud_orig, rtf, extrapolation)
示例#5
0
    def get_wcor_fit_funcs(self, index):
        # skip wcor if the element is not listed among those who need a correction:
        if self.numbers[index] not in self.wcor_numbers:
            return []

        center = self.coordinates[index]
        atom_nbasis = self.hebasis.get_atom_nbasis(index)
        rtf = self.get_rgrid(index).rtransform
        splines = []
        for j0 in xrange(atom_nbasis):
            rho0 = self.hebasis.get_basis_rho(index, j0)
            splines.append(CubicSpline(rho0, rtransform=rtf))
            for j1 in xrange(j0 + 1):
                rho1 = self.hebasis.get_basis_rho(index, j1)
                splines.append(CubicSpline(rho0 * rho1, rtransform=rtf))
        return [(center, splines)]
示例#6
0
    def get_proatom_spline(self, index, *args, **kwargs):
        # Get the radial density
        rho, deriv = self.get_proatom_rho(index, *args, **kwargs)

        # Double check and fix if needed
        rho, deriv = self.fix_proatom_rho(index, rho, deriv)

        # Make a spline
        rtf = self.get_rgrid(index).rtransform
        return CubicSpline(rho, deriv, rtf)
示例#7
0
文件: atgrid.py 项目: kumrud/horton
    def get_spherical_decomposition(self, *args, **kwargs):
        r'''Returns the decomposition of the product of the given functions in spherical harmonics

           **Arguments:**

           data1, data2, ...
                All arguments must be arrays with the same size as the number
                of grid points. The arrays contain the functions, evaluated
                at the grid points.

           **Optional arguments:**

           lmax=0
                The maximum angular momentum to consider when computing
                multipole moments.

           **Remark**

           A series of radial functions is returned as CubicSpline objects.
           These radial functions are defined as:

           .. math::
                f_{\ell m}(r) = \int f(\mathbf{r}) Y_{\ell m}(\mathbf{r}) d \Omega

           where the integral is carried out over angular degrees of freedom and
           :math:`Y_{\ell m}` are real spherical harmonics. The radial functions
           can be used to reconstruct the original integrand as follows:

           .. math::
                f(\mathbf{r}) = \sum_{\ell m} f_{\ell m}(|\mathbf{r}|) Y_{\ell m}(\mathbf{r})

           One can also derive the pure multipole moments by integrating these
           radial functions as follows:

           .. math::
                Q_{\ell m} = \sqrt{\frac{4\pi}{2\ell+1}} \int_0^\infty f_{\ell m}(r) r^l r^2 dr

           (These multipole moments are equivalent to the ones obtained with
           option ``mtype==2`` of the ``integrate`` method).
        '''
        lmax = kwargs.pop('lmax', 0)
        if lmax < 0:
            raise ValueError('lmax can not be negative.')
        if len(kwargs) > 0:
            raise TypeError('Unexpected keyword argument: %s' %
                            kwargs.popitem()[0])

        angular_ints = self.integrate(*args,
                                      center=self.center,
                                      mtype=4,
                                      lmax=lmax,
                                      segments=self.nlls)
        angular_ints /= self.integrate(segments=self.nlls).reshape(-1, 1)

        results = []
        counter = 0
        lmaxs = self.lmaxs
        for l in xrange(0, lmax + 1):
            mask = lmaxs < 2 * l
            for m in xrange(-l, l + 1):
                f = angular_ints[:, counter].copy()
                f *= np.sqrt(
                    4 * np.pi *
                    (2 * l + 1))  # proper norm for spherical harmonics
                f[mask] = 0  # mask out unreliable results
                results.append(CubicSpline(f,
                                           rtransform=self.rgrid.rtransform))
                counter += 1

        return results
示例#8
0
def get_exp_spline():
    rtf = LinearRTransform(0.0, 20.0, 100)
    x = rtf.get_radii()
    y = np.exp(-0.2 * x)
    d = -0.2 * np.exp(-0.2 * x)
    return CubicSpline(y, d, rtf)