Source code for harmonia.algorithms.integration

"""
Numerical integration (:mod:`~harmonia.algorithms.integration`)
===========================================================================

Integrate numerically in specified coordinate systems.

.. warning::

    Quadrature integration of spherical functions may suffer from poor
    convergence.


**Spherical integrals**

.. autosummary::

    angular_integral
    radial_integral
    pixelated_angular_integral

|

"""
import healpy as hp
import numpy as np
from scipy.integrate import dblquad, quad


def _angular_integrand(phi, theta, func, complex_part):
    r"""Evaluate the angular integrand with the Jacobian factor
    :math:`\sin\theta`.

    Notes
    -----
    Angular arguments are in reverse order for outward double integration.

    Parameters
    ----------
    phi, theta: float, array_like
        Angular coordinates in radians.
    func : callable
        Angular function to be integrated.
    complex_part : {'real', 'imag'}
        Real or imaginary part.

    Returns
    -------
    float, array_like
        Real or imaginary part of the complex angular integrand value.

    """
    if complex_part.lower() == 'real':
        return np.abs(np.sin(theta)) * np.real(func(theta, phi))

    if complex_part.lower() == 'imag':
        return np.abs(np.sin(theta)) * np.imag(func(theta, phi))

    raise ValueError("`complex_part` is neither 'real' nor 'imag'.")


def _radial_integrand(r, func):
    r"""Evaluate the radial integrand with the Jacobian factor :math:`r^2`.

    Parameters
    ----------
    r: float, array_like
        Radial coordinate.
    func : callable
        Radial function to be integrated.

    Returns
    -------
    float, array_like
        Radial integrand value.

    """
    return r**2 * func(r)


[docs]def angular_integral(angular_func): r"""Compute the full spherical angular integral. Notes ----- Arguments :math:`(\theta, \phi)` of `angular_func` must be in radians in the domain :math:`[0, \pi] \times [0, 2\pi]`. Parameters ---------- angular_func : callable Angular function to be integrated. Returns ------- complex Angular integral value. """ theta_range = (0., np.pi) phi_range = (0., 2*np.pi) integral_real, _ = dblquad( _angular_integrand, *theta_range, *phi_range, args=(angular_func, 'real') ) integral_imag, _ = dblquad( _angular_integrand, *theta_range, *phi_range, args=(angular_func, 'imag') ) return integral_real + 1j*integral_imag
[docs]def radial_integral(radial_func, rmax): """Compute the radial integral up to the given maximum radius. Parameters ---------- radial_func : callable Radial function to be integrated. rmax : float Upper radial limit ``rmax > 0``. Returns ------- integral : float Radial integral value. """ integral, _ = quad(_radial_integrand, 0., rmax, args=(radial_func,)) return integral
[docs]def pixelated_angular_integral(angular_func, nside): r"""Compute the full spherical angular integral with pixelation. Notes ----- Arguments :math:`(\theta, \phi)` of `angular_func` must be in radians in the domain :math:`[0, \pi] \times [0, 2\pi]`. Parameters ---------- angular_func : callable Angular function to be integrated. nside : int 'NSIDE' parameter for `healpy` pixelation. Returns ------- complex Angular integral value. """ num_pixel = hp.nside2npix(nside) theta, phi = hp.pix2ang(nside, ipix=range(num_pixel)) pixel_area = 4 * np.pi / num_pixel pixel_value = angular_func(theta, phi) return pixel_area * np.sum(pixel_value)