Beispiel #1
0
def build_cmb_2d(shape, wcs, cl_cmb, dtype=np.float32):
    lmap = enmap.lmap(shape, wcs)
    l = np.sum(lmap**2, 0)**0.5
    cmb = enmap.samewcs(utils.interp(l, np.arange(cl_cmb.shape[-1]), cl_cmb),
                        l).astype(dtype)
    # Rotate [TEB,EB] -> [TQU,TQU]. FIXME: not a perfect match
    R = enmap.queb_rotmat(lmap, spin=2, inverse=True)
    cmb[1:, :] = np.einsum("abyx,bcyx->acyx", R, cmb[1:, :])
    cmb[:, 1:] = np.einsum("abyx,cbyx->acyx", cmb[:, 1:], R)
    return cmb
Beispiel #2
0
def rotate_pol_power(shape,wcs,cov,iau=False,inverse=False):
    """Rotate a 2D power spectrum from TQU to TEB (inverse=False) or
    back (inverse=True). cov is a (3,3,Ny,Nx) 2D power spectrum.
    WARNING: This function is duplicated from orphics.maps to make 
    this module independent. Ideally, it should be implemented in
    enlib.enmap.
    """
    rot = np.zeros((3,3,cov.shape[-2],cov.shape[-1]))
    rot[0,0,:,:] = 1
    prot = enmap.queb_rotmat(enmap.lmap(shape,wcs), inverse=inverse, iau=iau)
    rot[1:,1:,:,:] = prot
    Rt = np.transpose(rot, (1,0,2,3))
    tmp = np.einsum("ab...,bc...->ac...",rot,cov)
    rp2d = np.einsum("ab...,bc...->ac...",tmp,Rt)    
    return rp2d
Beispiel #3
0
 def __init__(self,shape,wcs,groups=None):
     # Symbolic
     self.l1x,self.l1y,self.l2x,self.l2y,self.l1,self.l2 = get_ells()
     self.Lx,self.Ly,self.L = get_Ls()
     if groups is None: groups = [self.Lx*self.Lx,self.Ly*self.Ly,self.Lx*self.Ly]
     self._default_groups = groups
     self.integrands = {}
     self.ul1s = {}
     self.ul2s = {}
     self.ogroups = {}
     self.ogroup_weights = {}
     self.ogroup_symbols = {}
     self.l1funcs = []
     self.l2funcs = []
     # Diagnostic
     self.nfft = 0
     self.nifft = 0
     # Numeric
     self.shape,self.wcs = shape,wcs
     self.modlmap = enmap.modlmap(shape,wcs)
     self.lymap,self.lxmap = enmap.lmap(shape,wcs)
     self.pixarea = np.prod(enmap.pixshape(shape,wcs))
Beispiel #4
0
def integrate(shape,
              wcs,
              feed_dict,
              expr,
              xmask=None,
              ymask=None,
              cache=True,
              validate=True,
              groups=None,
              pixel_units=False):
    """
    Integrate an arbitrary expression after factorizing it.

    Parameters
    ----------

    shape : tuple
        The shape of the array for the geometry of the footprint. Typically 
        (...,Ny,Nx) for Ny pixels in the y-direction and Nx in the x-direction.
    wcs : :obj:`astropy.wcs.wcs.WCS`
        The wcs object completing the specification of the geometry of the footprint.
    feed_dict: dict
        Mapping from names of custom symbols to numpy arrays.
    expr: :obj:`sympy.core.symbol.Symbol` 
        A sympy expression containing recognized symbols (see docs)
    xmask: (Ny,Nx) ndarray,optional
        Fourier space 2D mask for the l1 part of the integral. Defaults to ones.
    ymask:  (Ny,Nx) ndarray, optional
        Fourier space 2D mask for the l2 part of the integral. Defaults to ones.
    cache: boolean, optional
        Whether to store in memory and reuse repeated terms. Defaults to true.
    validate: boolean,optional
        Whether to check that the final expression and the original agree. Defaults to True.
    groups: list,optional 
        Group all terms such that they have common factors of the provided list of 
        expressions to reduce the number of FFTs.
    pixel_units: boolean,optional
        Whether the input is in pixel units or not.

    Returns
    -------

    result : (Ny,Nx) ndarray
        The numerical result of the integration of the expression after factorization.

    """
    # Geometry
    modlmap = enmap.modlmap(shape, wcs)
    lymap, lxmap = enmap.lmap(shape, wcs)
    pixarea = np.prod(enmap.pixshape(shape, wcs))
    feed_dict['L'] = modlmap
    feed_dict['Ly'] = lymap
    feed_dict['Lx'] = lxmap
    shape = shape[-2:]
    ones = np.ones(shape, dtype=np.float32)
    val = 0.
    if xmask is None: xmask = ones
    if ymask is None: ymask = ones

    # Expression
    syms = expr.free_symbols
    l1funcs = []
    l2funcs = []
    for sym in syms:
        strsym = str(sym)
        if strsym[-3:] == "_l1": l1funcs.append(sym)
        elif strsym[-3:] == "_l2": l2funcs.append(sym)

    integrands,ul1s,ul2s, \
        ogroups,ogroup_weights, \
        ogroup_symbols = factorize_2d_convolution_integral(expr,l1funcs=l1funcs,l2funcs=l2funcs,
                                                                         validate=validate,groups=groups)

    def _fft(x):
        return fft(x + 0j)

    def _ifft(x):
        return ifft(x + 0j)

    if cache:
        cached_u1s = []
        cached_u2s = []
        for u1 in ul1s:
            l12d = evaluate(u1, feed_dict) * ones
            cached_u1s.append(_ifft(l12d * xmask))
        for u2 in ul2s:
            l22d = evaluate(u2, feed_dict) * ones
            cached_u2s.append(_ifft(l22d * ymask))

    # For each term, the index of which group it belongs to

    def get_l1l2(term):
        if cache:
            ifft1 = cached_u1s[term['l1index']]
            ifft2 = cached_u2s[term['l2index']]
        else:
            l12d = evaluate(term['l1'], feed_dict) * ones
            ifft1 = _ifft(l12d * xmask)
            l22d = evaluate(term['l2'], feed_dict) * ones
            ifft2 = _ifft(l22d * ymask)
        return ifft1, ifft2

    if ogroups is None:
        for i, term in enumerate(integrands):
            ifft1, ifft2 = get_l1l2(term)
            ot2d = evaluate(term['other'], feed_dict) * ones
            ffft = _fft(ifft1 * ifft2)
            val += ot2d * ffft
    else:
        vals = np.zeros((len(ogroup_symbols), ) + shape, dtype=np.float32) + 0j
        for i, term in enumerate(integrands):
            ifft1, ifft2 = get_l1l2(term)
            gindex = ogroups[i]
            vals[gindex, ...] += ifft1 * ifft2 * ogroup_weights[i]
        for i, group in enumerate(ogroup_symbols):
            ot2d = evaluate(ogroup_symbols[i], feed_dict) * ones
            ffft = _fft(vals[i, ...])
            val += ot2d * ffft

    mul = 1 if pixel_units else 1. / pixarea
    return val * mul
Beispiel #5
0
def kfilter_map(m, apo, kx_cut, ky_cut, unpixwin=True, legacy_steve=False):
    r"""Apply a k-space filter on a map.

    By default, we do not reproduce the output of Steve's code. We do offer
    this functionality: set the optional flag `legacy_steve=True` to offset
    the mask and the map by one pixel in each dimension.

    You should filter both in temperature and polarization.

    Parameters
    ----------
    m : enmap
        Input map which this function will filter.
    apo : enmap
        This map is a smooth tapering of the edges of the map, to be multiplied
        into the map prior to filtering. The filtered map is divided by the
        nonzero pixels of this map at the end. This is required because
        maps of actual data are unlikely to be periodic, which will induce
        ringing when one applies a k-space filter. To solve this, we taper the
        edges of the map to zero prior to filtering.

        See :py:func:`nawrapper.ps.rectangular_apodization`
    kx_cut : float
        We cut modes with wavenumber :math:`|k_x| < k_x^{\mathrm{cut}}`.
    ky_cut : float
        We cut modes with wavenumber :math:`|k_y| < k_y^{\mathrm{cut}}`.
    unpixwin : bool
        Correct for the CAR pixel window if True.
    legacy_steve : bool
        Use a slightly different filter if True, to reproduce Steve's pipeline.
        Steve's k-space filter as of June 2019 had a bug where two k-space
        modes (the most positive cut mode in x and most negative cut mode in y)
        were not cut. This has a very small effect on the spectrum, but for
        reproducibility purposes we offer this behavior. By default we do not
        use this. To reproduce Steve's code behavior you should set `legacy_steve=True`.

    Returns
    -------
    result : enmap
        The map with the specified k-space filter applied.

    """
    alm = enmap.fft(m * apo, normalize=True)

    if unpixwin:  # remove pixel window in Fourier space
        wy, wx = enmap.calc_window(m.shape)
        alm /= wy[:, np.newaxis]
        alm /= wx[np.newaxis, :]

    ly, lx = enmap.lmap(alm.shape, alm.wcs)
    kfilter_x = np.abs(lx) >= kx_cut
    kfilter_y = np.abs(ly) >= ky_cut

    if legacy_steve:  # Steve's kspace filter appears to do this
        cut_x_k = np.unique(lx[(np.abs(lx) <= kx_cut)])
        cut_y_k = np.unique(ly[(np.abs(ly) <= ky_cut)])
        # keep most negative kx and most positive ky
        kfilter_x[np.isclose(lx, cut_x_k[0])] = True
        kfilter_y[np.isclose(ly, cut_y_k[-1])] = True

    result = enmap.ifft(alm * kfilter_x * kfilter_y, normalize=True).real
    result[apo > 0.0] = result[apo > 0.0] / apo[apo > 0.0]
    return result