예제 #1
0
def cross_spectrum(clm1, clm2, normalization='4pi', degrees=None, lmax=None,
                   convention='power', unit='per_l', base=10.):
    """
    Return the cross-spectrum of the spherical harmonic coefficients as a
    function of spherical harmonic degree.

    Usage
    -----
    array = cross_spectrum(clm1, clm2, [normalization, degrees, lmax,
                                        convention, unit, base])

    Returns
    -------
    array : ndarray, shape (len(degrees))
        1-D ndarray of the spectrum.

    Parameters
    ----------
    clm1 : ndarray, shape (2, lmax + 1, lmax + 1)
        ndarray containing the first set of spherical harmonic coefficients.
    clm2 : ndarray, shape (2, lmax + 1, lmax + 1)
        ndarray containing the second set of spherical harmonic coefficients.
    normalization : str, optional, default = '4pi'
        '4pi', 'ortho', 'schmidt', or 'unnorm' for geodesy 4pi normalized,
        orthonormalized, Schmidt semi-normalized, or unnormalized coefficients,
        respectively.
    lmax : int, optional, default = len(clm[0,:,0]) - 1.
        Maximum spherical harmonic degree to output.
    degrees : ndarray, optional, default = numpy.arange(lmax+1)
        Array containing the spherical harmonic degrees where the spectrum
        is computed.
    convention : str, optional, default = 'power'
        The type of spectrum to return: 'power' for power spectrum, 'energy'
        for energy spectrum, and 'l2norm' for the l2-norm spectrum.
    unit : str, optional, default = 'per_l'
        If 'per_l', return the total contribution to the spectrum for each
        spherical harmonic degree l. If 'per_lm', return the average
        contribution to the spectrum for each coefficient at spherical
        harmonic degree l. If 'per_dlogl', return the spectrum per log
        interval dlog_a(l).
    base : float, optional, default = 10.
        The logarithm base when calculating the 'per_dlogl' spectrum.

    Description
    -----------
    This function returns either the cross-power spectrum, cross-energy
    spectrum, or l2-cross-norm spectrum. Total cross-power is defined as the
    integral of the clm1 times the conjugate of clm2 over all space, divided
    by the area the functions span. If the mean of the functions is zero,
    this is equivalent to the covariance of the two functions. The total
    cross-energy is the integral of clm1 times the conjugate of clm2 over all
    space and is 4pi times the total power. The l2-cross-norm is the
    sum of clm1 times the conjugate of clm2 over all angular orders as a
    function of spherical harmonic degree.

    The output spectrum can be expresed using one of three units. 'per_l'
    returns the contribution to the total spectrum from all angular orders
    at degree l. 'per_lm' returns the average contribution to the total
    spectrum from a single coefficient at degree l, and is equal to the
    'per_l' spectrum divided by (2l+1). 'per_dlogl' returns the contribution to
    the total spectrum from all angular orders over an infinitessimal
    logarithmic degree band. The contrubution in the band dlog_a(l) is
    spectrum(l, 'per_dlogl')*dlog_a(l), where a is the base, and where
    spectrum(l, 'per_dlogl) is equal to spectrum(l, 'per_l')*l*log(a).
    """
    if normalization.lower() not in ('4pi', 'ortho', 'schmidt', 'unnorm'):
        raise ValueError("The normalization must be '4pi', 'ortho', " +
                         "'schmidt', or 'unnorm'. Input value was {:s}."
                         .format(repr(normalization)))

    if convention.lower() not in ('power', 'energy', 'l2norm'):
        raise ValueError("convention must be 'power', 'energy', or " +
                         "'l2norm'. Input value was {:s}"
                         .format(repr(convention)))

    if unit.lower() not in ('per_l', 'per_lm', 'per_dlogl'):
        raise ValueError("unit must be 'per_l', 'per_lm', or 'per_dlogl'." +
                         "Input value was {:s}".format(repr(unit)))

    if _np.iscomplexobj(clm1) is not _np.iscomplexobj(clm2):
        raise ValueError('clm1 and clm2 must both be either real or ' +
                         'complex. \nclm1 is complex : {:s}\n'
                         .format(repr(_np.iscomplexobj(clm1))) +
                         'clm2 is complex : {:s}'
                         .format(repr(_np.iscomplexobj(clm2))))

    if lmax is None:
        lmax = len(clm1[0, :, 0]) - 1

    if degrees is None:
        degrees = _np.arange(lmax+1)

    if _np.iscomplexobj(clm1):
        array = _np.empty(len(degrees), dtype='complex')
    else:
        array = _np.empty(len(degrees))

    if normalization.lower() == 'unnorm':
        if convention.lower() == 'l2norm':
            raise ValueError("convention can not be set to 'l2norm' when " +
                             "using unnormalized harmonics.")

        for i, l in enumerate(degrees):
            ms = _np.arange(l+1)
            conv = _factorial(l+ms) / (2. * l + 1.) / _factorial(l-ms)

            if _np.iscomplexobj(clm):
                array[i] = (conv[0:l + 1] * clm1[0, l, 0:l + 1] *
                            clm2[0, l, 0:l + 1].conjugate()).real.sum() + \
                           (conv[1:l + 1] * clm1[1, l, 1:l + 1] *
                            clm2[1, l, 1:l + 1].conjugate()).real.sum()
            else:
                conv[1:l + 1] = conv[1:l + 1] / 2.
                array[i] = (conv[0:l + 1] * clm1[0, l, 0:l+1]**2).sum() + \
                           (conv[1:l + 1] * clm2[1, l, 1:l+1]**2).sum()

    else:
        for i, l in enumerate(degrees):
            if _np.iscomplexobj(clm1):
                array[i] = (clm1[0, l, 0:l + 1] *
                            clm2[0, l, 0:l + 1].conjugate()).sum() + \
                           (clm1[1, l, 1:l + 1] *
                            clm2[1, l, 1:l + 1].conjugate()).sum()
            else:
                array[i] = (clm1[0, l, 0:l + 1] * clm2[0, l, 0:l + 1]).sum() \
                           + (clm1[1, l, 1:l + 1] * clm2[1, l, 1:l + 1]).sum()

        if convention.lower() == 'l2norm':
            return array
        else:
            if normalization.lower() == '4pi':
                pass
            elif normalization.lower() == 'schmidt':
                array /= (2. * degrees + 1.)
            elif normalization.lower() == 'ortho':
                array /= (4. * _np.pi)

    if convention.lower() == 'energy':
        array *= 4. * _np.pi

    if unit.lower() == 'per_l':
        pass
    elif unit.lower() == 'per_lm':
        array /= (2. * degrees + 1.)
    elif unit.lower() == 'per_dlogl':
        array *= degrees * _np.log(base)

    return array
예제 #2
0
def cross_spectrum(clm1,
                   clm2,
                   normalization='4pi',
                   degrees=None,
                   lmax=None,
                   convention='power',
                   unit='per_l',
                   base=10.):
    """
    Return the cross-spectrum of the spherical harmonic coefficients as a
    function of spherical harmonic degree.

    Usage
    -----
    array = cross_spectrum(clm1, clm2, [normalization, degrees, lmax,
                                        convention, unit, base])

    Returns
    -------
    array : ndarray, shape (len(degrees))
        1-D ndarray of the spectrum.

    Parameters
    ----------
    clm1 : ndarray, shape (2, lmax + 1, lmax + 1)
        ndarray containing the first set of spherical harmonic coefficients.
    clm2 : ndarray, shape (2, lmax + 1, lmax + 1)
        ndarray containing the second set of spherical harmonic coefficients.
    normalization : str, optional, default = '4pi'
        '4pi', 'ortho', 'schmidt', or 'unnorm' for geodesy 4pi normalized,
        orthonormalized, Schmidt semi-normalized, or unnormalized coefficients,
        respectively.
    lmax : int, optional, default = len(clm[0,:,0]) - 1.
        Maximum spherical harmonic degree to output.
    degrees : ndarray, optional, default = numpy.arange(lmax+1)
        Array containing the spherical harmonic degrees where the spectrum
        is computed.
    convention : str, optional, default = 'power'
        The type of spectrum to return: 'power' for power spectrum, 'energy'
        for energy spectrum, and 'l2norm' for the l2-norm spectrum.
    unit : str, optional, default = 'per_l'
        If 'per_l', return the total contribution to the spectrum for each
        spherical harmonic degree l. If 'per_lm', return the average
        contribution to the spectrum for each coefficient at spherical
        harmonic degree l. If 'per_dlogl', return the spectrum per log
        interval dlog_a(l).
    base : float, optional, default = 10.
        The logarithm base when calculating the 'per_dlogl' spectrum.

    Notes
    -----
    This function returns either the cross-power spectrum, cross-energy
    spectrum, or l2-cross-norm spectrum. Total cross-power is defined as the
    integral of the clm1 times the conjugate of clm2 over all space, divided
    by the area the functions span. If the mean of the functions is zero,
    this is equivalent to the covariance of the two functions. The total
    cross-energy is the integral of clm1 times the conjugate of clm2 over all
    space and is 4pi times the total power. The l2-cross-norm is the
    sum of clm1 times the conjugate of clm2 over all angular orders as a
    function of spherical harmonic degree.

    The output spectrum can be expresed using one of three units. 'per_l'
    returns the contribution to the total spectrum from all angular orders
    at degree l. 'per_lm' returns the average contribution to the total
    spectrum from a single coefficient at degree l, and is equal to the
    'per_l' spectrum divided by (2l+1). 'per_dlogl' returns the contribution to
    the total spectrum from all angular orders over an infinitessimal
    logarithmic degree band. The contrubution in the band dlog_a(l) is
    spectrum(l, 'per_dlogl')*dlog_a(l), where a is the base, and where
    spectrum(l, 'per_dlogl) is equal to spectrum(l, 'per_l')*l*log(a).
    """
    if normalization.lower() not in ('4pi', 'ortho', 'schmidt', 'unnorm'):
        raise ValueError("The normalization must be '4pi', 'ortho', " +
                         "'schmidt', or 'unnorm'. Input value was {:s}.".
                         format(repr(normalization)))

    if convention.lower() not in ('power', 'energy', 'l2norm'):
        raise ValueError(
            "convention must be 'power', 'energy', or " +
            "'l2norm'. Input value was {:s}".format(repr(convention)))

    if unit.lower() not in ('per_l', 'per_lm', 'per_dlogl'):
        raise ValueError("unit must be 'per_l', 'per_lm', or 'per_dlogl'." +
                         "Input value was {:s}".format(repr(unit)))

    if _np.iscomplexobj(clm1) is not _np.iscomplexobj(clm2):
        raise ValueError(
            'clm1 and clm2 must both be either real or ' +
            'complex. \nclm1 is complex : {:s}\n'.format(
                repr(_np.iscomplexobj(clm1))) +
            'clm2 is complex : {:s}'.format(repr(_np.iscomplexobj(clm2))))

    if lmax is None:
        lmax = len(clm1[0, :, 0]) - 1

    if degrees is None:
        degrees = _np.arange(lmax + 1)

    if _np.iscomplexobj(clm1):
        array = _np.empty(len(degrees), dtype='complex')
    else:
        array = _np.empty(len(degrees))

    if normalization.lower() == 'unnorm':
        if convention.lower() == 'l2norm':
            raise ValueError("convention can not be set to 'l2norm' when " +
                             "using unnormalized harmonics.")

        for i, l in enumerate(degrees):
            ms = _np.arange(l + 1)
            conv = _factorial(l + ms) / (2. * l + 1.) / _factorial(l - ms)

            if _np.iscomplexobj(clm1):
                array[i] = (conv[0:l + 1] * clm1[0, l, 0:l + 1] *
                            clm2[0, l, 0:l + 1].conjugate()).real.sum() + \
                           (conv[1:l + 1] * clm1[1, l, 1:l + 1] *
                            clm2[1, l, 1:l + 1].conjugate()).real.sum()
            else:
                conv[1:l + 1] = conv[1:l + 1] / 2.
                array[i] = (conv[0:l + 1] * clm1[0, l, 0:l+1]**2).sum() + \
                           (conv[1:l + 1] * clm2[1, l, 1:l+1]**2).sum()

    else:
        for i, l in enumerate(degrees):
            if _np.iscomplexobj(clm1):
                array[i] = (clm1[0, l, 0:l + 1] *
                            clm2[0, l, 0:l + 1].conjugate()).sum() + \
                           (clm1[1, l, 1:l + 1] *
                            clm2[1, l, 1:l + 1].conjugate()).sum()
            else:
                array[i] = (clm1[0, l, 0:l + 1] * clm2[0, l, 0:l + 1]).sum() \
                           + (clm1[1, l, 1:l + 1] * clm2[1, l, 1:l + 1]).sum()

        if convention.lower() == 'l2norm':
            return array
        else:
            if normalization.lower() == '4pi':
                pass
            elif normalization.lower() == 'schmidt':
                array /= (2. * degrees + 1.)
            elif normalization.lower() == 'ortho':
                array /= (4. * _np.pi)

    if convention.lower() == 'energy':
        array *= 4. * _np.pi

    if unit.lower() == 'per_l':
        pass
    elif unit.lower() == 'per_lm':
        array /= (2. * degrees + 1.)
    elif unit.lower() == 'per_dlogl':
        array *= degrees * _np.log(base)

    return array
예제 #3
0
def convert(coeffs_in, normalization_in=None, normalization_out=None,
            csphase_in=None, csphase_out=None, lmax=None):
        """
        Convert an array of spherical harmonic coefficients to a different
        normalization convention.

        Usage
        -----
        coeffs_out = convert(coeffs_in, [normalization_in, normalization_out,
                                         csphase_in, csphase_out, lmax])

        Returns
        -------
        coeffs_out : ndarray, size (2, lmax+1, lmax+1)
            An array of spherical harmonic coefficients with the new
            normalization convention.

        Parameters
        ----------
        coeffs_in : ndarray
            The array of imput spherical harmonic coefficients.
        normalization_in : str, optional, default = None
            Normalization of the output coefficients: '4pi', 'ortho'
            'schmidt', or 'unnorm', for geodesy 4pi normalized,
            orthonormalized, Schmidt semi-normalized, or unnormalized
            coefficients, respectively.
        normalization_out : str, optional, default = None
            Normalization of the output coefficients: '4pi', 'ortho'
            'schmidt', or 'unnorm', for geodesy 4pi normalized,
            orthonormalized, Schmidt semi-normalized, or unnormalized
            coefficients, respectively.
        csphase_in : int, optional, default = None
            Condon-Shortley phase convention of the input coefficients: 1 to
            exclude the phase factor, or -1 to include it.
        csphase_out : int, optional, default = None
            Condon-Shortley phase convention of the output coefficients: 1 to
            exclude the phase factor, or -1 to include it.
        lmax : int, optional, default = coeffs.shape[1] - 1
            Maximum spherical harmonic degree to output. If lmax is larger than
            that of the input coefficients, the output array will be zero
            padded.

        Description
        -----------
        This routine will convert an array of spherical harmonic coefficients
        to a different normalization convention and different Condon-Shortley
        phase convention. Optionally, a different maximum spherical harmonic
        degree can be specified. If this degree is smaller than that of the
        input coefficients, the input coefficients will be truncated. If this
        degree is larger than the input coefficients, then the output
        coefficients will be zero padded.
        """

        # check argument consistency
        if normalization_in is not None:
            if type(normalization_in) != str:
                raise ValueError('normalization_in must be a string. ' +
                                 'Input type was {:s}'
                                 .format(str(type(normalization_in))))
            if normalization_in.lower() not in ('4pi', 'ortho', 'schmidt',
                                                'unnorm'):
                raise ValueError(
                    "normalization_in must be '4pi', 'ortho', 'schmidt', or " +
                    "'unnorm'. Provided value was {:s}"
                    .format(repr(normalization_in))
                    )
            if normalization_out is None:
                raise ValueError("normalization_in and normalization_out " +
                                 "must both be specified.")
        if normalization_out is not None:
            if type(normalization_out) != str:
                raise ValueError('normalization_out must be a string. ' +
                                 'Input type was {:s}'
                                 .format(str(type(normalization_out))))
            if normalization_out.lower() not in ('4pi', 'ortho', 'schmidt',
                                                 'unnorm'):
                raise ValueError(
                    "normalization_out must be '4pi', 'ortho', 'schmidt', or" +
                    " 'unnorm'. Provided value was {:s}"
                    .format(repr(normalization_out))
                    )
            if normalization_in is None:
                raise ValueError("normalization_in and normalization_out " +
                                 "must both be specified.")
        if csphase_in is not None:
            if csphase_in != 1 and csphase_in != -1:
                raise ValueError(
                    "csphase_in must be 1 or -1. Input value was {:s}"
                    .format(repr(csphase_in)))
            if csphase_out is None:
                raise ValueError("csphase_in and csphase_out must both be " +
                                 "specified.")
        if csphase_out is not None:
            if csphase_out != 1 and csphase_out != -1:
                raise ValueError(
                    "csphase_out must be 1 or -1. Input value was {:s}"
                    .format(repr(csphase_out)))
            if csphase_in is None:
                raise ValueError("csphase_in and csphase_out must both be " +
                                 "specified.")

        lmaxin = coeffs_in.shape[1] - 1

        if ((normalization_in == 'unnorm' or normalization_out ==
                'unnorm') and lmaxin > 85):
            _warnings.warn("Conversions with unnormalized coefficients are " +
                           "stable only for degrees less than or equal to " +
                           "85. lmax of the input coefficients will be " +
                           "truncated after degree 85. The spherical " +
                           "harmonic degree of the input coefficients was " +
                           "{:d}.".format(lmaxin), category=RuntimeWarning)
            lmaxin = 85

        if lmax is None:
            lmaxout = lmaxin
        else:
            lmaxout = lmax

        lconv = min(lmaxin, lmaxout)
        degrees = _np.arange(lconv + 1)

        if _np.iscomplexobj(coeffs_in):
            coeffs = _np.zeros((2, lmaxout+1, lmaxout+1), dtype=complex)
        else:
            coeffs = _np.zeros((2, lmaxout+1, lmaxout+1))

        coeffs[:, :lconv+1, :lconv+1] = coeffs_in[:, :lconv+1, :lconv+1]

        if normalization_in == normalization_out:
            pass
        elif normalization_in == '4pi' and normalization_out == 'schmidt':
            for l in degrees:
                coeffs[:, l, :l+1] *= _np.sqrt(2. * l + 1.)
        elif normalization_in == '4pi' and normalization_out == 'ortho':
            coeffs *= _np.sqrt(4. * _np.pi)
        elif normalization_in == '4pi' and normalization_out == 'unnorm':
            for l in degrees:
                ms = _np.arange(l+1)
                conv = (2. * l + 1.) * _factorial(l-ms) / _factorial(l+ms)
                if not _np.iscomplexobj(coeffs):
                    conv[1:] *= 2.
                coeffs[:, l, :l+1] *= _np.sqrt(conv)
        elif normalization_in == 'schmidt' and normalization_out == '4pi':
            for l in degrees:
                coeffs[:, l, :l+1] /= _np.sqrt(2. * l + 1.)
        elif normalization_in == 'schmidt' and normalization_out == 'ortho':
            for l in degrees:
                coeffs[:, l, :l+1] *= _np.sqrt(4. * _np.pi / (2. * l + 1.))
        elif normalization_in == 'schmidt' and normalization_out == 'unnorm':
            for l in degrees:
                ms = _np.arange(l+1)
                conv = _factorial(l-ms) / _factorial(l+ms)
                if not _np.iscomplexobj(coeffs):
                    conv[1:] *= 2.
                coeffs[:, l, :l+1] *= _np.sqrt(conv)
        elif normalization_in == 'ortho' and normalization_out == '4pi':
            coeffs /= _np.sqrt(4. * _np.pi)
        elif normalization_in == 'ortho' and normalization_out == 'schmidt':
            for l in degrees:
                coeffs[:, l, :l+1] *= _np.sqrt((2. * l + 1.) / (4. * _np.pi))
        elif normalization_in == 'ortho' and normalization_out == 'unnorm':
            for l in degrees:
                ms = _np.arange(l+1)
                conv = (2. * l + 1.) * _factorial(l-ms) \
                    / 4. / _np.pi / _factorial(l+ms)
                if not _np.iscomplexobj(coeffs):
                    conv[1:] *= 2.
                coeffs[:, l, :l+1] *= _np.sqrt(conv)
        elif normalization_in == 'unnorm' and normalization_out == '4pi':
            for l in degrees:
                ms = _np.arange(l+1)
                conv = _factorial(l+ms) / (2. * l + 1.) / _factorial(l-ms)
                if not _np.iscomplexobj(coeffs):
                    conv[1:] /= 2.
                coeffs[:, l, :l+1] *= _np.sqrt(conv)
        elif normalization_in == 'unnorm' and normalization_out == 'schmidt':
            for l in degrees:
                ms = _np.arange(l+1)
                conv = _factorial(l+ms) / _factorial(l-ms)
                if not _np.iscomplexobj(coeffs):
                    conv[1:] /= 2.
                coeffs[:, l, :l+1] *= _np.sqrt(conv)
        elif normalization_in == 'unnorm' and normalization_out == 'ortho':
            for l in degrees:
                ms = _np.arange(l+1)
                conv = 4. * _np.pi * _factorial(l+ms) / (2. * l + 1.) / \
                    _factorial(l-ms)
                if not _np.iscomplexobj(coeffs):
                    conv[1:] /= 2.
                coeffs[:, l, :l+1] *= _np.sqrt(conv)

        if csphase_in != csphase_out:
            for m in degrees:
                if m % 2 == 1:
                    coeffs[:, m:lconv+1, m] = - coeffs[:, m:lconv+1, m]

        return coeffs
예제 #4
0
def convert(coeffs_in,
            normalization_in=None,
            normalization_out=None,
            csphase_in=None,
            csphase_out=None,
            lmax=None):
    """
        Convert an array of spherical harmonic coefficients to a different
        normalization convention.

        Usage
        -----
        coeffs_out = convert(coeffs_in, [normalization_in, normalization_out,
                                         csphase_in, csphase_out, lmax])

        Returns
        -------
        coeffs_out : ndarray, size (2, lmax+1, lmax+1)
            An array of spherical harmonic coefficients with the new
            normalization convention.

        Parameters
        ----------
        coeffs_in : ndarray
            The array of imput spherical harmonic coefficients.
        normalization_in : str, optional, default = None
            Normalization of the output coefficients: '4pi', 'ortho'
            'schmidt', or 'unnorm', for geodesy 4pi normalized,
            orthonormalized, Schmidt semi-normalized, or unnormalized
            coefficients, respectively.
        normalization_out : str, optional, default = None
            Normalization of the output coefficients: '4pi', 'ortho'
            'schmidt', or 'unnorm', for geodesy 4pi normalized,
            orthonormalized, Schmidt semi-normalized, or unnormalized
            coefficients, respectively.
        csphase_in : int, optional, default = None
            Condon-Shortley phase convention of the input coefficients: 1 to
            exclude the phase factor, or -1 to include it.
        csphase_out : int, optional, default = None
            Condon-Shortley phase convention of the output coefficients: 1 to
            exclude the phase factor, or -1 to include it.
        lmax : int, optional, default = coeffs.shape[1] - 1
            Maximum spherical harmonic degree to output. If lmax is larger than
            that of the input coefficients, the output array will be zero
            padded.

        Description
        -----------
        This routine will convert an array of spherical harmonic coefficients
        to a different normalization convention and different Condon-Shortley
        phase convention. Optionally, a different maximum spherical harmonic
        degree can be specified. If this degree is smaller than that of the
        input coefficients, the input coefficients will be truncated. If this
        degree is larger than the input coefficients, then the output
        coefficients will be zero padded.
        """

    # check argument consistency
    if normalization_in is not None:
        if type(normalization_in) != str:
            raise ValueError(
                'normalization_in must be a string. ' +
                'Input type was {:s}'.format(str(type(normalization_in))))
        if normalization_in.lower() not in ('4pi', 'ortho', 'schmidt',
                                            'unnorm'):
            raise ValueError(
                "normalization_in must be '4pi', 'ortho', 'schmidt', or " +
                "'unnorm'. Provided value was {:s}".format(
                    repr(normalization_in)))
        if normalization_out is None:
            raise ValueError("normalization_in and normalization_out " +
                             "must both be specified.")
    if normalization_out is not None:
        if type(normalization_out) != str:
            raise ValueError(
                'normalization_out must be a string. ' +
                'Input type was {:s}'.format(str(type(normalization_out))))
        if normalization_out.lower() not in ('4pi', 'ortho', 'schmidt',
                                             'unnorm'):
            raise ValueError(
                "normalization_out must be '4pi', 'ortho', 'schmidt', or" +
                " 'unnorm'. Provided value was {:s}".format(
                    repr(normalization_out)))
        if normalization_in is None:
            raise ValueError("normalization_in and normalization_out " +
                             "must both be specified.")
    if csphase_in is not None:
        if csphase_in != 1 and csphase_in != -1:
            raise ValueError(
                "csphase_in must be 1 or -1. Input value was {:s}".format(
                    repr(csphase_in)))
        if csphase_out is None:
            raise ValueError("csphase_in and csphase_out must both be " +
                             "specified.")
    if csphase_out is not None:
        if csphase_out != 1 and csphase_out != -1:
            raise ValueError(
                "csphase_out must be 1 or -1. Input value was {:s}".format(
                    repr(csphase_out)))
        if csphase_in is None:
            raise ValueError("csphase_in and csphase_out must both be " +
                             "specified.")

    lmaxin = coeffs_in.shape[1] - 1

    if ((normalization_in == 'unnorm' or normalization_out == 'unnorm')
            and lmaxin > 85):
        _warnings.warn("Conversions with unnormalized coefficients are " +
                       "stable only for degrees less than or equal to " +
                       "85. lmax of the input coefficients will be " +
                       "truncated after degree 85. The spherical " +
                       "harmonic degree of the input coefficients was " +
                       "{:d}.".format(lmaxin),
                       category=RuntimeWarning)
        lmaxin = 85

    if lmax is None:
        lmaxout = lmaxin
    else:
        lmaxout = lmax

    lconv = min(lmaxin, lmaxout)
    degrees = _np.arange(lconv + 1)

    if _np.iscomplexobj(coeffs_in):
        coeffs = _np.zeros((2, lmaxout + 1, lmaxout + 1), dtype=complex)
    else:
        coeffs = _np.zeros((2, lmaxout + 1, lmaxout + 1))

    coeffs[:, :lconv + 1, :lconv + 1] = coeffs_in[:, :lconv + 1, :lconv + 1]

    if normalization_in == normalization_out:
        pass
    elif normalization_in == '4pi' and normalization_out == 'schmidt':
        for l in degrees:
            coeffs[:, l, :l + 1] *= _np.sqrt(2. * l + 1.)
    elif normalization_in == '4pi' and normalization_out == 'ortho':
        coeffs *= _np.sqrt(4. * _np.pi)
    elif normalization_in == '4pi' and normalization_out == 'unnorm':
        for l in degrees:
            ms = _np.arange(l + 1)
            conv = (2. * l + 1.) * _factorial(l - ms) / _factorial(l + ms)
            if not _np.iscomplexobj(coeffs):
                conv[1:] *= 2.
            coeffs[:, l, :l + 1] *= _np.sqrt(conv)
    elif normalization_in == 'schmidt' and normalization_out == '4pi':
        for l in degrees:
            coeffs[:, l, :l + 1] /= _np.sqrt(2. * l + 1.)
    elif normalization_in == 'schmidt' and normalization_out == 'ortho':
        for l in degrees:
            coeffs[:, l, :l + 1] *= _np.sqrt(4. * _np.pi / (2. * l + 1.))
    elif normalization_in == 'schmidt' and normalization_out == 'unnorm':
        for l in degrees:
            ms = _np.arange(l + 1)
            conv = _factorial(l - ms) / _factorial(l + ms)
            if not _np.iscomplexobj(coeffs):
                conv[1:] *= 2.
            coeffs[:, l, :l + 1] *= _np.sqrt(conv)
    elif normalization_in == 'ortho' and normalization_out == '4pi':
        coeffs /= _np.sqrt(4. * _np.pi)
    elif normalization_in == 'ortho' and normalization_out == 'schmidt':
        for l in degrees:
            coeffs[:, l, :l + 1] *= _np.sqrt((2. * l + 1.) / (4. * _np.pi))
    elif normalization_in == 'ortho' and normalization_out == 'unnorm':
        for l in degrees:
            ms = _np.arange(l + 1)
            conv = (2. * l + 1.) * _factorial(l-ms) \
                / 4. / _np.pi / _factorial(l+ms)
            if not _np.iscomplexobj(coeffs):
                conv[1:] *= 2.
            coeffs[:, l, :l + 1] *= _np.sqrt(conv)
    elif normalization_in == 'unnorm' and normalization_out == '4pi':
        for l in degrees:
            ms = _np.arange(l + 1)
            conv = _factorial(l + ms) / (2. * l + 1.) / _factorial(l - ms)
            if not _np.iscomplexobj(coeffs):
                conv[1:] /= 2.
            coeffs[:, l, :l + 1] *= _np.sqrt(conv)
    elif normalization_in == 'unnorm' and normalization_out == 'schmidt':
        for l in degrees:
            ms = _np.arange(l + 1)
            conv = _factorial(l + ms) / _factorial(l - ms)
            if not _np.iscomplexobj(coeffs):
                conv[1:] /= 2.
            coeffs[:, l, :l + 1] *= _np.sqrt(conv)
    elif normalization_in == 'unnorm' and normalization_out == 'ortho':
        for l in degrees:
            ms = _np.arange(l + 1)
            conv = 4. * _np.pi * _factorial(l+ms) / (2. * l + 1.) / \
                _factorial(l-ms)
            if not _np.iscomplexobj(coeffs):
                conv[1:] /= 2.
            coeffs[:, l, :l + 1] *= _np.sqrt(conv)

    if csphase_in != csphase_out:
        for m in degrees:
            if m % 2 == 1:
                coeffs[:, m:lconv + 1, m] = -coeffs[:, m:lconv + 1, m]

    return coeffs
예제 #5
0
def mag_spectrum(clm,
                 a,
                 r,
                 potential=False,
                 normalization='schmidt',
                 degrees=None,
                 lmax=None,
                 convention='power',
                 unit='per_l',
                 base=10.):
    """
    Return the spectrum of the magnetic field as a function of spherical
    harmonic degree.

    Usage
    -----
    array = mag_spectrum(clm, a, r, [potential, normalization, degrees, lmax,
                                     convention, unit, base])

    Returns
    -------
    array : ndarray, shape (len(degrees))
        1-D ndarray of the spectrum.

    Parameters
    ----------
    clm : ndarray, shape (2, lmax + 1, lmax + 1)
        ndarray containing the spherical harmonic coefficients.
    a : float
        The reference radius of the spherical harmonic coefficients.
    r : float
        The radius at which the spectrum is evaluated.
    potential : bool, optional, default = False
        If True, calculate the spectrum of the magnetic potential. Otherwise,
        calculate the spectrum of the magnetic intensity (default).
    normalization : str, optional, default = 'schmidt'
        '4pi', 'ortho', 'schmidt', or 'unnorm' for geodesy 4pi normalized,
        orthonormalized, Schmidt semi-normalized, or unnormalized coefficients,
        respectively.
    lmax : int, optional, default = len(clm[0,:,0]) - 1.
        Maximum spherical harmonic degree to output.
    degrees : ndarray, optional, default = numpy.arange(lmax+1)
        Array containing the spherical harmonic degrees where the spectrum
        is computed.
    convention : str, optional, default = 'power'
        The type of spectrum to return: 'power' for power spectrum, 'energy'
        for energy spectrum, and 'l2norm' for the l2-norm spectrum.
    unit : str, optional, default = 'per_l'
        If 'per_l', return the total contribution to the spectrum for each
        spherical harmonic degree l. If 'per_lm', return the average
        contribution to the spectrum for each coefficient at spherical
        harmonic degree l. If 'per_dlogl', return the spectrum per log
        interval dlog_a(l).
    base : float, optional, default = 10.
        The logarithm base when calculating the 'per_dlogl' spectrum.

    Notes
    -----
    This function returns either the power spectrum, energy spectrum, or
    l2-norm spectrum of a global magnetic field. Total power is defined as the
    integral of the function squared over all space, divided by the area the
    function spans. If the mean of the function is zero, this is equivalent to
    the variance of the function. The total energy is the integral of the
    function squared over all space and is 4pi times the total power. The
    l2-norm is simply the sum of the magnitude of the spherical harmonic
    coefficients squared. The default behaviour of this function is to return
    the Lowes-Mauersberger spectrum, which is the total power of the magnetic
    field strength as a function of spherical harmonic degree (see below).

    The magnetic potential and magnetic field are defined respectively by the
    two equations

        U = a**2 \sum_{l, m}^L (a/r)**(l+1) glm Ylm,
        B = - \Del U.

    Here, a is the reference radius of the spherical harmonic coefficients, r
    is the radius at which the function is evalulated, and L is the maximum
    spherical harmonic degree of the expansion. If the optional parameter
    potential is set to True, the spectrum of the magnetic potential
    will be calculated. Otherwise, the default behavior is to calculate the
    spectrum of the magnetic field intensity.

    The output spectrum can be expresed using one of three units. 'per_l'
    returns the contribution to the total spectrum from all angular orders
    at degree l. 'per_lm' returns the average contribution to the total
    spectrum from a single coefficient at degree l, and is equal to the
    'per_l' spectrum divided by (2l+1). 'per_dlogl' returns the contribution to
    the total spectrum from all angular orders over an infinitessimal
    logarithmic degree band. The contrubution in the band dlog_a(l) is
    spectrum(l, 'per_dlogl')*dlog_a(l), where a is the base, and where
    spectrum(l, 'per_dlogl) is equal to spectrum(l, 'per_l')*l*log(a).

    When no optional parameters are specified, the Lowes-Mauersberger power
    spectrum is calculated. Explicitly, this corrresponds to convention =
    'power', unit = 'per_l', and potential = False.
    """
    if normalization.lower() not in ('4pi', 'ortho', 'schmidt', 'unnorm'):
        raise ValueError("The normalization must be '4pi', 'ortho', " +
                         "'schmidt', or 'unnorm'. Input value was {:s}.".
                         format(repr(normalization)))

    if convention.lower() not in ('power', 'energy', 'l2norm'):
        raise ValueError(
            "convention must be 'power', 'energy', or " +
            "'l2norm'. Input value was {:s}".format(repr(convention)))

    if unit.lower() not in ('per_l', 'per_lm', 'per_dlogl'):
        raise ValueError("unit must be 'per_l', 'per_lm', or 'per_dlogl'." +
                         "Input value was {:s}".format(repr(unit)))

    if lmax is None:
        lmax = len(clm[0, :, 0]) - 1

    if degrees is None:
        degrees = _np.arange(lmax + 1)

    array = _np.empty(len(degrees))

    if normalization.lower() == 'unnorm':
        if convention.lower() == 'l2norm':
            raise ValueError("convention can not be set to 'l2norm' when " +
                             "using unnormalized harmonics.")

        for i, l in enumerate(degrees):
            ms = _np.arange(l + 1)
            conv = _factorial(l + ms) / (2. * l + 1.) / _factorial(l - ms)

            if _np.iscomplexobj(clm):
                array[i] = (conv[0:l + 1] * clm[0, l, 0:l + 1] *
                            clm[0, l, 0:l + 1].conjugate()).real.sum() + \
                           (conv[1:l + 1] * clm[1, l, 1:l + 1] *
                            clm[1, l, 1:l + 1].conjugate()).real.sum()
            else:
                conv[1:l + 1] = conv[1:l + 1] / 2.
                array[i] = (conv[0:l + 1] * clm[0, l, 0:l+1]**2).sum() + \
                           (conv[1:l + 1] * clm[1, l, 1:l+1]**2).sum()

    else:
        for i, l in enumerate(degrees):
            if _np.iscomplexobj(clm):
                array[i] = (clm[0, l, 0:l + 1] *
                            clm[0, l, 0:l + 1].conjugate()).real.sum() + \
                           (clm[1, l, 1:l + 1] *
                            clm[1, l, 1:l + 1].conjugate()).real.sum()
            else:
                array[i] = (clm[0, l, 0:l+1]**2).sum() + \
                           (clm[1, l, 1:l+1]**2).sum()

        if convention.lower() == 'l2norm':
            return array
        else:
            if normalization.lower() == '4pi':
                pass
            elif normalization.lower() == 'schmidt':
                array /= (2. * degrees + 1.)
            elif normalization.lower() == 'ortho':
                array /= (4. * _np.pi)

    if convention.lower() == 'energy':
        array *= 4. * _np.pi

    if unit.lower() == 'per_l':
        pass
    elif unit.lower() == 'per_lm':
        array /= (2. * degrees + 1.)
    elif unit.lower() == 'per_dlogl':
        array *= degrees * _np.log(base)

    if potential is True:
        array *= (a**2) * (a / r)**(2 * degrees + 2)
    else:
        array *= (a / r)**(2 * degrees + 4) * (degrees + 1) * (2 * degrees + 1)

    return array
예제 #6
0
def surprise(expectation, outcome, k_max=None):
    if not _numpy.issubdtype(expectation.dtype, _numpy.float):
        raise ValueError("expectation must be of float type")
    if not _numpy.issubdtype(outcome.dtype, _numpy.integer):
        raise ValueError("outcome must be of integer type")
    
    if k_max is None:
        k_max = outcome.max()*2
    max_outcome = outcome.max()
    surprise_pix = _numpy.zeros(expectation.shape)
    k_mask = outcome < k_max
    # surprise_pix[k_mask] =
    # -_numpy.log(expectation[k_mask]**outcome[k_mask]
    #  *_numpy.exp(-expectation[k_mask])
    # / _factorial(outcome[k_mask]))
    surprise_pix[k_mask] = -(outcome[k_mask]*_numpy.log(expectation[k_mask])
                             + -expectation[k_mask]
                             - _numpy.log(_factorial(outcome[k_mask])))
    surprise_pix[~k_mask] = (-(expectation[~k_mask] - outcome[~k_mask])**2
                             / (2*expectation[~k_mask]))
    surprise = surprise_pix.sum()

    
    surprise_expectation_pix = _numpy.zeros(surprise_pix.shape)
    surprise_expectation = 0
    for k in range(max_outcome):
        if k < k_max:
            fac_k = _factorial(k)
            this_exp = (expectation**k*_numpy.exp(-expectation)/fac_k
                        * _numpy.log(expectation**k
                                     * _numpy.exp(-expectation)
                                     / fac_k))
            this_exp[_numpy.isnan(this_exp)] = 0

            surprise_expectation_pix -= this_exp
        else:
            this_exp = (_numpy.exp(-(expectation - k)**2 / (2 * expectation))
                        * (-(expectation - k)**2 / (2 * expectation)))
            surprise_expectation_pix -= this_exp

    surprise_expectation = surprise_expectation_pix.sum()

    surprise_variance_pix = _numpy.zeros(surprise_pix.shape)
    for k in range(max_outcome):
        if k < k_max:
            fac_k = _factorial(k)
            surprise_variance_pix += (
                expectation**k * _numpy.exp(-expectation) / fac_k
                * (k*_numpy.log(expectation)
                   - expectation
                   - _numpy.log(fac_k))**2)
        else:
            surprise_variance_pix += (
                _numpy.exp(-(expectation - k)**2 / (2*expectation))
                * (-(expectation - k)**2 / (2*expectation))**2)
    surprise_variance_pix -= surprise_expectation_pix**2
    surprise_variance = surprise_variance_pix.sum()

    score = abs(surprise - surprise_expectation) / surprise_variance

    return score
예제 #7
0
def mag_spectrum(clm, a, r, potential=False, normalization='schmidt',
                 degrees=None, lmax=None, convention='power', unit='per_l',
                 base=10.):
    """
    Return the spectrum of the magnetic field as a function of spherical
    harmonic degree.

    Usage
    -----
    array = mag_spectrum(clm, a, r, [potential, normalization, degrees, lmax,
                                     convention, unit, base])

    Returns
    -------
    array : ndarray, shape (len(degrees))
        1-D ndarray of the spectrum.

    Parameters
    ----------
    clm : ndarray, shape (2, lmax + 1, lmax + 1)
        ndarray containing the spherical harmonic coefficients.
    a : float
        The reference radius of the spherical harmonic coefficients.
    r : float
        The radius at which the spectrum is evaluated.
    potential : bool, optional, default = False
        If True, calculate the spectrum of the magnetic potential. Otherwise,
        calculate the spectrum of the magnetic intensity (default).
    normalization : str, optional, default = 'schmidt'
        '4pi', 'ortho', 'schmidt', or 'unnorm' for geodesy 4pi normalized,
        orthonormalized, Schmidt semi-normalized, or unnormalized coefficients,
        respectively.
    lmax : int, optional, default = len(clm[0,:,0]) - 1.
        Maximum spherical harmonic degree to output.
    degrees : ndarray, optional, default = numpy.arange(lmax+1)
        Array containing the spherical harmonic degrees where the spectrum
        is computed.
    convention : str, optional, default = 'power'
        The type of spectrum to return: 'power' for power spectrum, 'energy'
        for energy spectrum, and 'l2norm' for the l2-norm spectrum.
    unit : str, optional, default = 'per_l'
        If 'per_l', return the total contribution to the spectrum for each
        spherical harmonic degree l. If 'per_lm', return the average
        contribution to the spectrum for each coefficient at spherical
        harmonic degree l. If 'per_dlogl', return the spectrum per log
        interval dlog_a(l).
    base : float, optional, default = 10.
        The logarithm base when calculating the 'per_dlogl' spectrum.

    Description
    -----------
    This function returns either the power spectrum, energy spectrum, or
    l2-norm spectrum of a global magnetic field. Total power is defined as the
    integral of the function squared over all space, divided by the area the
    function spans. If the mean of the function is zero, this is equivalent to
    the variance of the function. The total energy is the integral of the
    function squared over all space and is 4pi times the total power. The
    l2-norm is simply the sum of the magnitude of the spherical harmonic
    coefficients squared. The default behaviour of this function is to return
    the Lowes-Mauersberger spectrum, which is the total power of the magnetic
    field strength as a function of spherical harmonic degree (see below).

    The magnetic potential and magnetic field are defined respectively by the
    two equations

        U = a**2 \sum_{l, m}^L (a/r)**(l+1) glm Ylm,
        B = - \Del U.

    Here, a is the reference radius of the spherical harmonic coefficients, r
    is the radius at which the function is evalulated, and L is the maximum
    spherical harmonic degree of the expansion. If the optional parameter
    potential is set to True, the spectrum of the magnetic potential
    will be calculated. Otherwise, the default behavior is to calculate the
    spectrum of the magnetic field intensity.

    The output spectrum can be expresed using one of three units. 'per_l'
    returns the contribution to the total spectrum from all angular orders
    at degree l. 'per_lm' returns the average contribution to the total
    spectrum from a single coefficient at degree l, and is equal to the
    'per_l' spectrum divided by (2l+1). 'per_dlogl' returns the contribution to
    the total spectrum from all angular orders over an infinitessimal
    logarithmic degree band. The contrubution in the band dlog_a(l) is
    spectrum(l, 'per_dlogl')*dlog_a(l), where a is the base, and where
    spectrum(l, 'per_dlogl) is equal to spectrum(l, 'per_l')*l*log(a).

    When no optional parameters are specified, the Lowes-Mauersberger power
    spectrum is calculated. Explicitly, this corrresponds to convention =
    'power', unit = 'per_l', and potential = False.
    """
    if normalization.lower() not in ('4pi', 'ortho', 'schmidt', 'unnorm'):
        raise ValueError("The normalization must be '4pi', 'ortho', " +
                         "'schmidt', or 'unnorm'. Input value was {:s}."
                         .format(repr(normalization)))

    if convention.lower() not in ('power', 'energy', 'l2norm'):
        raise ValueError("convention must be 'power', 'energy', or " +
                         "'l2norm'. Input value was {:s}"
                         .format(repr(convention)))

    if unit.lower() not in ('per_l', 'per_lm', 'per_dlogl'):
        raise ValueError("unit must be 'per_l', 'per_lm', or 'per_dlogl'." +
                         "Input value was {:s}".format(repr(unit)))

    if lmax is None:
        lmax = len(clm[0, :, 0]) - 1

    if degrees is None:
        degrees = _np.arange(lmax+1)

    array = _np.empty(len(degrees))

    if normalization.lower() == 'unnorm':
        if convention.lower() == 'l2norm':
            raise ValueError("convention can not be set to 'l2norm' when " +
                             "using unnormalized harmonics.")

        for i, l in enumerate(degrees):
            ms = _np.arange(l+1)
            conv = _factorial(l+ms) / (2. * l + 1.) / _factorial(l-ms)

            if _np.iscomplexobj(clm):
                array[i] = (conv[0:l + 1] * clm[0, l, 0:l + 1] *
                            clm[0, l, 0:l + 1].conjugate()).real.sum() + \
                           (conv[1:l + 1] * clm[1, l, 1:l + 1] *
                            clm[1, l, 1:l + 1].conjugate()).real.sum()
            else:
                conv[1:l + 1] = conv[1:l + 1] / 2.
                array[i] = (conv[0:l + 1] * clm[0, l, 0:l+1]**2).sum() + \
                           (conv[1:l + 1] * clm[1, l, 1:l+1]**2).sum()

    else:
        for i, l in enumerate(degrees):
            if _np.iscomplexobj(clm):
                array[i] = (clm[0, l, 0:l + 1] *
                            clm[0, l, 0:l + 1].conjugate()).real.sum() + \
                           (clm[1, l, 1:l + 1] *
                            clm[1, l, 1:l + 1].conjugate()).real.sum()
            else:
                array[i] = (clm[0, l, 0:l+1]**2).sum() + \
                           (clm[1, l, 1:l+1]**2).sum()

        if convention.lower() == 'l2norm':
            return array
        else:
            if normalization.lower() == '4pi':
                pass
            elif normalization.lower() == 'schmidt':
                array /= (2. * degrees + 1.)
            elif normalization.lower() == 'ortho':
                array /= (4. * _np.pi)

    if convention.lower() == 'energy':
        array *= 4. * _np.pi

    if unit.lower() == 'per_l':
        pass
    elif unit.lower() == 'per_lm':
        array /= (2. * degrees + 1.)
    elif unit.lower() == 'per_dlogl':
        array *= degrees * _np.log(base)

    if potential is True:
        array *= (a**2) * (a / r)**(2 * degrees + 2)
    else:
        array *= (a / r)**(2 * degrees + 4) * (degrees + 1) * (2 * degrees + 1)

    return array