示例#1
0
def FieldType(ns):
    """
    Construct and return a `Field`:
    a tuple of (`DataSource`, `Painter`, `Transfer`)

    Notes
    -----
    *   the default Painter is set to `DefaultPainter`
    *   the default Transfer chain is set to 
        [`NormalizeDC`, `RemoveDC`, `AnisotropicCIC`]

    Parameters
    ----------
    fields_dict : OrderedDict
        an ordered dictionary where the keys are Plugin names
        and the values are instantiated Plugins

    Returns
    -------
    field : list
        list of (DataSource, Painter, Transfer)
    """
    # define the default Painter and Transfer
    default_painter = Painter.create("DefaultPainter")
    default_transfer = [Transfer.create(x) for x in ['NormalizeDC', 'RemoveDC', 'AnisotropicCIC']]

    # start with a default option for (DataSource, Painter, Transfer)
    field = [None, default_painter, default_transfer]

    # set the DataSource
    if 'DataSource' not in ns:
        raise ValueError("exactly one `DataSource` per field must be specified")
    field[0] = getattr(ns, 'DataSource')

    # set the Painter
    if 'Painter' in ns:
        field[1] = getattr(ns, 'Painter')

    # set the Transfer
    if 'Transfer' in ns:
        field[2] = getattr(ns, 'Transfer')

    return field
示例#2
0
def compute_bianchi_poles(comm,
                          max_ell,
                          catalog,
                          Nmesh,
                          factor_hexadecapole=False,
                          paintbrush='cic'):
    """
    Use the algorithm detailed in Bianchi et al. 2015 to compute and return the 3D 
    power spectrum multipoles (`ell = [0, 2, 4]`) from one input field, which contains 
    non-trivial survey geometry.
    
    The estimator uses the FFT algorithm outlined in Bianchi et al. 2015
    (http://adsabs.harvard.edu/abs/2015arXiv150505341B) to compute
    the monopole, quadrupole, and hexadecapole
    
    Parameters
    ----------
    comm : MPI.Communicator
        the communicator to pass to the ParticleMesh object
    max_ell : int, {0, 2, 4}
        the maximum multipole number to compute up to (inclusive)
    catalog : :class:`~nbodykit.fkp.FKPCatalog`
        the FKP catalog object that manipulates the data and randoms DataSource
        objects and paints the FKP density
    Nmesh : int
        the number of cells (per side) in the gridded mesh
    factor_hexadecapole : bool, optional
        if `True`, use the factored expression for the hexadecapole (ell=4) from
        eq. 27 of Scoccimarro 2015 (1506.02729); default is `False`
    paintbrush : str, {'cic', 'tsc'}
        the density assignment kernel to use when painting, either `cic` (2nd order) 
        or `tsc` (3rd order); default is `cic`
    
    Returns
    -------
    pm : ParticleMesh
        the mesh object used to do painting, FFTs, etc
    result : list of arrays
        list of 3D complex arrays holding power spectrum multipoles; respectively, 
        if `ell_max=0,2,4`, the list holds the monopole only, monopole and quadrupole, 
        or the monopole, quadrupole, and hexadecapole
    stats : dict
        dict holding the statistics of the input fields, as returned
        by the `FKPPainter` painter
    
    References
    ----------
    * Bianchi, Davide et al., `Measuring line-of-sight-dependent Fourier-space clustering using FFTs`,
      MNRAS, 2015
    * Scoccimarro, Roman, `Fast estimators for redshift-space clustering`, Phys. Review D, 2015
    """
    from pmesh.pm import ParticleMesh, RealField

    rank = comm.rank
    bianchi_transfers = []

    # the painting kernel transfer
    if paintbrush == 'cic':
        transfer = Transfer.create('AnisotropicCIC')
    elif paintbrush == 'tsc':
        transfer = Transfer.create('AnisotropicTSC')
    else:
        raise ValueError("valid `paintbrush` values are: ['cic', 'tsc']")

    # determine which multipole values we are computing
    if max_ell not in [0, 2, 4]:
        raise ValueError(
            "valid values for the maximum multipole number are [0, 2, 4]")
    ells = numpy.arange(0, max_ell + 1, 2)

    # determine kernels needed to compute quadrupole
    if max_ell > 0:

        # the (i,j) index values for each kernel
        k2 = [(0, 0), (1, 1), (2, 2), (0, 1), (0, 2), (1, 2)]

        # the amplitude of each kernel term
        a2 = [1.] * 3 + [2.] * 3
        bianchi_transfers.append((a2, k2))

    # determine kernels needed to compute hexadecapole
    if max_ell > 2 and not factor_hexadecapole:

        # the (i,j,k) index values for each kernel
        k4 = [(0, 0, 0), (1, 1, 1), (2, 2, 2), (0, 0, 1), (0, 0, 2), (1, 1, 0),
              (1, 1, 2), (2, 2, 0), (2, 2, 1), (0, 1, 1), (0, 2, 2), (1, 2, 2),
              (0, 1, 2), (1, 0, 2), (2, 0, 1)]

        # the amplitude of each kernel term
        a4 = [1.] * 3 + [4.] * 6 + [6.] * 3 + [12.] * 3
        bianchi_transfers.append((a4, k4))

    # load the data/randoms, setup boxsize, etc from the FKPCatalog
    with catalog:

        # the mean coordinate offset of the input data
        offset = catalog.mean_coordinate_offset

        # initialize the particle mesh
        pm = ParticleMesh(BoxSize=catalog.BoxSize,
                          Nmesh=[Nmesh] * 3,
                          dtype='f4',
                          comm=comm)

        # paint the FKP density field to the mesh (paints: data - randoms, essentially)
        real, stats = catalog.paint(pm, paintbrush=paintbrush)

    # save the painted density field for later
    density = real.copy()
    if rank == 0: logger.info('%s painting done' % paintbrush)

    # FFT density field and apply the paintbrush window transfer kernel
    complex = real.r2c()
    transfer(pm, complex)
    if rank == 0: logger.info('ell = 0 done; 1 r2c completed')

    # monopole A0 is just the FFT of the FKP density field
    volume = pm.BoxSize.prod()
    A0 = complex[:] * volume  # normalize with a factor of volume

    # store the A0, A2, A4 arrays here
    result = []
    result.append(A0)

    # the real-space grid points
    # the grid is properly offset from [-L/2, L/2] to the original positions in space
    # this is the grid used when applying Bianchi kernels
    cell_size = pm.BoxSize / pm.Nmesh
    xgrid = [(ri + 0.5) * cell_size[i] + offset[i]
             for i, ri in enumerate(pm.r)]

    # loop over the higher order multipoles (ell > 0)
    start = time.time()
    for iell, ell in enumerate(ells[1:]):

        # temporary array to hold sum of all of the terms in Fourier space
        Aell_sum = numpy.zeros_like(complex)

        # loop over each kernel term for this multipole
        for amp, integers in zip(*bianchi_transfers[iell]):

            # reset the realspace mesh to the original FKP density
            real[:] = density[:]

            # apply the real-space Bianchi kernel
            if rank == 0:
                logger.debug("applying real-space Bianchi transfer for %s..." %
                             str(integers))
            apply_bianchi_kernel(real, xgrid, *integers)
            if rank == 0: logger.debug('...done')

            # do the real-to-complex FFT
            if rank == 0: logger.debug("performing r2c...")
            real.r2c(out=complex)
            if rank == 0: logger.debug('...done')

            # apply the Fourier-space Bianchi kernel
            if rank == 0:
                logger.debug(
                    "applying Fourier-space Bianchi transfer for %s..." %
                    str(integers))
            apply_bianchi_kernel(complex, pm.k, *integers)
            if rank == 0: logger.debug('...done')

            # and this contribution to the total sum
            Aell_sum[:] += amp * complex[:] * volume

        # apply the paintbrush window transfer function and save
        transfer(pm, Aell_sum)
        result.append(Aell_sum)
        del Aell_sum  # delete temp array since appending to list makes copy

        # log the total number of FFTs computed for each ell
        if rank == 0:
            args = (ell, len(bianchi_transfers[iell][0]))
            logger.info('ell = %d done; %s r2c completed' % args)

    # density array no longer needed
    del density

    # summarize how long it took
    stop = time.time()
    if rank == 0:
        logger.info("higher order multipoles computed in elapsed time %s" %
                    timer(start, stop))
        if factor_hexadecapole:
            logger.info("using factorized hexadecapole estimator for ell=4")

    # proper normalization: same as equation 49 of Scoccimarro et al. 2015
    norm = 1.0 / stats['A_ran']

    # reuse memory for output arrays
    P0 = result[0]
    if max_ell > 0:
        P2 = result[1]
    if max_ell > 2:
        P4 = numpy.empty_like(P2) if factor_hexadecapole else result[2]

    # calculate the power spectrum multipoles, slab-by-slab to save memory
    for islab in range(len(P0)):

        # save arrays for reuse
        P0_star = (P0[islab]).conj()
        if max_ell > 0: P2_star = (P2[islab]).conj()

        # hexadecapole
        if max_ell > 2:

            # see equation 8 of Bianchi et al. 2015
            if not factor_hexadecapole:
                P4[islab, ...] = norm * 9. / 8. * P0[islab] * (
                    35. * (P4[islab]).conj() - 30. * P2_star + 3. * P0_star)
            # see equation 48 of Scoccimarro et al; 2015
            else:
                P4[islab, ...] = norm * 9. / 8. * (
                    35. * P2[islab] * P2_star + 3. * P0[islab] * P0_star -
                    5. / 3. *
                    (11. * P0[islab] * P2_star + 7. * P2[islab] * P0_star))

        # quadrupole: equation 7 of Bianchi et al. 2015
        if max_ell > 0:
            P2[islab,
               ...] = norm * 5. / 2. * P0[islab] * (3. * P2_star - P0_star)

        # monopole: equation 6 of Bianchi et al. 2015
        P0[islab, ...] = norm * P0[islab] * P0_star

    return pm, result, stats
示例#3
0
def compute_bianchi_poles(comm, max_ell, catalog, Nmesh, factor_hexadecapole=False, paintbrush='cic'):
    """
    Use the algorithm detailed in Bianchi et al. 2015 to compute and return the 3D 
    power spectrum multipoles (`ell = [0, 2, 4]`) from one input field, which contains 
    non-trivial survey geometry.
    
    The estimator uses the FFT algorithm outlined in Bianchi et al. 2015
    (http://adsabs.harvard.edu/abs/2015arXiv150505341B) to compute
    the monopole, quadrupole, and hexadecapole
    
    Parameters
    ----------
    comm : MPI.Communicator
        the communicator to pass to the ParticleMesh object
    max_ell : int, {0, 2, 4}
        the maximum multipole number to compute up to (inclusive)
    catalog : :class:`~nbodykit.fkp.FKPCatalog`
        the FKP catalog object that manipulates the data and randoms DataSource
        objects and paints the FKP density
    Nmesh : int
        the number of cells (per side) in the gridded mesh
    factor_hexadecapole : bool, optional
        if `True`, use the factored expression for the hexadecapole (ell=4) from
        eq. 27 of Scoccimarro 2015 (1506.02729); default is `False`
    paintbrush : str, {'cic', 'tsc'}
        the density assignment kernel to use when painting, either `cic` (2nd order) 
        or `tsc` (3rd order); default is `cic`
    
    Returns
    -------
    pm : ParticleMesh
        the mesh object used to do painting, FFTs, etc
    result : list of arrays
        list of 3D complex arrays holding power spectrum multipoles; respectively, 
        if `ell_max=0,2,4`, the list holds the monopole only, monopole and quadrupole, 
        or the monopole, quadrupole, and hexadecapole
    stats : dict
        dict holding the statistics of the input fields, as returned
        by the `FKPPainter` painter
    
    References
    ----------
    * Bianchi, Davide et al., `Measuring line-of-sight-dependent Fourier-space clustering using FFTs`,
      MNRAS, 2015
    * Scoccimarro, Roman, `Fast estimators for redshift-space clustering`, Phys. Review D, 2015
    """
    from pmesh.pm import ParticleMesh, RealField
    
    rank = comm.rank
    bianchi_transfers = []

    # the painting kernel transfer
    if paintbrush == 'cic':
        transfer = Transfer.create('AnisotropicCIC')
    elif paintbrush == 'tsc':
        transfer = Transfer.create('AnisotropicTSC')
    else:
        raise ValueError("valid `paintbrush` values are: ['cic', 'tsc']")

    # determine which multipole values we are computing
    if max_ell not in [0, 2, 4]:
        raise ValueError("valid values for the maximum multipole number are [0, 2, 4]")
    ells = numpy.arange(0, max_ell+1, 2)
    
    # determine kernels needed to compute quadrupole
    if max_ell > 0:
        
        # the (i,j) index values for each kernel
        k2 = [(0, 0), (1, 1), (2, 2), (0, 1), (0, 2), (1, 2)]
        
        # the amplitude of each kernel term
        a2 = [1.]*3 + [2.]*3
        bianchi_transfers.append((a2, k2))
    
    # determine kernels needed to compute hexadecapole
    if max_ell > 2 and not factor_hexadecapole:
        
        # the (i,j,k) index values for each kernel
        k4 = [(0, 0, 0), (1, 1, 1), (2, 2, 2), (0, 0, 1), (0, 0, 2),
             (1, 1, 0), (1, 1, 2), (2, 2, 0), (2, 2, 1), (0, 1, 1),
             (0, 2, 2), (1, 2, 2), (0, 1, 2), (1, 0, 2), (2, 0, 1)]
        
        # the amplitude of each kernel term
        a4 = [1.]*3 + [4.]*6 + [6.]*3 + [12.]*3
        bianchi_transfers.append((a4, k4))
    
    # load the data/randoms, setup boxsize, etc from the FKPCatalog
    with catalog:
        
        # the mean coordinate offset of the input data
        offset = catalog.mean_coordinate_offset
        
        # initialize the particle mesh
        pm = ParticleMesh(BoxSize=catalog.BoxSize, Nmesh=[Nmesh]*3, dtype='f4', comm=comm)
        
        # paint the FKP density field to the mesh (paints: data - randoms, essentially)
        real, stats = catalog.paint(pm, paintbrush=paintbrush)

    # save the painted density field for later
    density = real.copy()
    if rank == 0: logger.info('%s painting done' %paintbrush)
    
    # FFT density field and apply the paintbrush window transfer kernel
    complex = real.r2c()
    transfer(pm, complex)
    if rank == 0: logger.info('ell = 0 done; 1 r2c completed')
        
    # monopole A0 is just the FFT of the FKP density field
    volume = pm.BoxSize.prod()
    A0 = complex[:]*volume # normalize with a factor of volume
    
    # store the A0, A2, A4 arrays here
    result = []
    result.append(A0)
    
    # the real-space grid points
    # the grid is properly offset from [-L/2, L/2] to the original positions in space
    # this is the grid used when applying Bianchi kernels
    cell_size = pm.BoxSize / pm.Nmesh
    xgrid = [(ri+0.5)*cell_size[i] + offset[i] for i, ri in enumerate(pm.r)]
    
    # loop over the higher order multipoles (ell > 0)
    start = time.time()
    for iell, ell in enumerate(ells[1:]):
        
        # temporary array to hold sum of all of the terms in Fourier space
        Aell_sum = numpy.zeros_like(complex)
        
        # loop over each kernel term for this multipole
        for amp, integers in zip(*bianchi_transfers[iell]):
                        
            # reset the realspace mesh to the original FKP density
            real[:] = density[:]
        
            # apply the real-space Bianchi kernel
            if rank == 0: logger.debug("applying real-space Bianchi transfer for %s..." %str(integers))
            apply_bianchi_kernel(real, xgrid, *integers)
            if rank == 0: logger.debug('...done')
    
            # do the real-to-complex FFT
            if rank == 0: logger.debug("performing r2c...")
            real.r2c(out=complex)
            if rank == 0: logger.debug('...done')
            
            # apply the Fourier-space Bianchi kernel
            if rank == 0: logger.debug("applying Fourier-space Bianchi transfer for %s..." %str(integers))
            apply_bianchi_kernel(complex, pm.k, *integers)
            if rank == 0: logger.debug('...done')
            
            # and this contribution to the total sum
            Aell_sum[:] += amp*complex[:]*volume
            
        # apply the paintbrush window transfer function and save
        transfer(pm, Aell_sum)
        result.append(Aell_sum); del Aell_sum # delete temp array since appending to list makes copy
        
        # log the total number of FFTs computed for each ell
        if rank == 0: 
            args = (ell, len(bianchi_transfers[iell][0]))
            logger.info('ell = %d done; %s r2c completed' %args)
        
    # density array no longer needed
    del density
    
    # summarize how long it took
    stop = time.time()
    if rank == 0:
        logger.info("higher order multipoles computed in elapsed time %s" %timer(start, stop))
        if factor_hexadecapole:
            logger.info("using factorized hexadecapole estimator for ell=4")
    
    # proper normalization: same as equation 49 of Scoccimarro et al. 2015 
    norm = 1.0 / stats['A_ran']
    
    # reuse memory for output arrays
    P0 = result[0]
    if max_ell > 0: 
        P2 = result[1]
    if max_ell > 2:
        P4 = numpy.empty_like(P2) if factor_hexadecapole else result[2]
        
    # calculate the power spectrum multipoles, slab-by-slab to save memory
    for islab in range(len(P0)):

        # save arrays for reuse
        P0_star = (P0[islab]).conj()
        if max_ell > 0: P2_star = (P2[islab]).conj()

        # hexadecapole
        if max_ell > 2:
            
            # see equation 8 of Bianchi et al. 2015
            if not factor_hexadecapole:
                P4[islab, ...] = norm * 9./8. * P0[islab] * (35.*(P4[islab]).conj() - 30.*P2_star + 3.*P0_star)
            # see equation 48 of Scoccimarro et al; 2015
            else:
                P4[islab, ...] = norm * 9./8. * ( 35.*P2[islab]*P2_star + 3.*P0[islab]*P0_star - 5./3.*(11.*P0[islab]*P2_star + 7.*P2[islab]*P0_star) )
        
        # quadrupole: equation 7 of Bianchi et al. 2015
        if max_ell > 0:
            P2[islab, ...] = norm * 5./2. * P0[islab] * (3.*P2_star - P0_star)

        # monopole: equation 6 of Bianchi et al. 2015
        P0[islab, ...] = norm * P0[islab] * P0_star
        
    return pm, result, stats