|
| 1 | +# This test needs to be run with: |
| 2 | + |
| 3 | +# mpirun -np X python test_smoothing_noise_pol_mpi.py |
| 4 | + |
| 5 | +from mpi4py import MPI |
| 6 | + |
| 7 | +import numpy as np |
| 8 | + |
| 9 | +import healpy as hp |
| 10 | + |
| 11 | +import libsharp |
| 12 | + |
| 13 | +mpi = True |
| 14 | +rank = MPI.COMM_WORLD.Get_rank() |
| 15 | + |
| 16 | +nside = 256 |
| 17 | +npix = hp.nside2npix(nside) |
| 18 | + |
| 19 | +np.random.seed(100) |
| 20 | +input_map = np.random.normal(size=(3, npix)) |
| 21 | +fwhm_deg = 10 |
| 22 | +lmax = 512 |
| 23 | + |
| 24 | +nrings = 4 * nside - 1 # four missing pixels |
| 25 | + |
| 26 | +if rank == 0: |
| 27 | + print("total rings", nrings) |
| 28 | + |
| 29 | +n_mpi_processes = MPI.COMM_WORLD.Get_size() |
| 30 | +rings_per_process = nrings // n_mpi_processes + 1 |
| 31 | +# ring indices are 1-based |
| 32 | + |
| 33 | +ring_indices_emisphere = np.arange(2*nside, dtype=np.int32) + 1 |
| 34 | +local_ring_indices = ring_indices_emisphere[rank::n_mpi_processes] |
| 35 | + |
| 36 | +# to improve performance, simmetric rings north/south need to be in the same rank |
| 37 | +# therefore we use symmetry to create the full ring indexing |
| 38 | + |
| 39 | +if local_ring_indices[-1] == 2 * nside: |
| 40 | + # has equator ring |
| 41 | + local_ring_indices = np.concatenate( |
| 42 | + [local_ring_indices[:-1], |
| 43 | + nrings - local_ring_indices[::-1] + 1] |
| 44 | + ) |
| 45 | +else: |
| 46 | + # does not have equator ring |
| 47 | + local_ring_indices = np.concatenate( |
| 48 | + [local_ring_indices, |
| 49 | + nrings - local_ring_indices[::-1] + 1] |
| 50 | + ) |
| 51 | + |
| 52 | +print("rank", rank, "n_rings", len(local_ring_indices)) |
| 53 | + |
| 54 | +if not mpi: |
| 55 | + local_ring_indices = None |
| 56 | +grid = libsharp.healpix_grid(nside, rings=local_ring_indices) |
| 57 | + |
| 58 | +# returns start index of the ring and number of pixels |
| 59 | +startpix, ringpix, _, _, _ = hp.ringinfo(nside, local_ring_indices.astype(np.int64)) |
| 60 | + |
| 61 | +local_npix = grid.local_size() |
| 62 | + |
| 63 | +def expand_pix(startpix, ringpix, local_npix): |
| 64 | + """Turn first pixel index and number of pixel in full array of pixels |
| 65 | +
|
| 66 | + to be optimized with cython or numba |
| 67 | + """ |
| 68 | + local_pix = np.empty(local_npix, dtype=np.int64) |
| 69 | + i = 0 |
| 70 | + for start, num in zip(startpix, ringpix): |
| 71 | + local_pix[i:i+num] = np.arange(start, start+num) |
| 72 | + i += num |
| 73 | + return local_pix |
| 74 | + |
| 75 | +local_pix = expand_pix(startpix, ringpix, local_npix) |
| 76 | + |
| 77 | +local_map = input_map[:, local_pix] |
| 78 | + |
| 79 | +local_hitmap = np.zeros(npix) |
| 80 | +local_hitmap[local_pix] = 1 |
| 81 | +hp.write_map("hitmap_{}.fits".format(rank), local_hitmap, overwrite=True) |
| 82 | + |
| 83 | +print("rank", rank, "npix", npix, "local_npix", local_npix, "local_map len", len(local_map), "unique pix", len(np.unique(local_pix))) |
| 84 | + |
| 85 | +local_m_indices = np.arange(rank, lmax + 1, MPI.COMM_WORLD.Get_size(), dtype=np.int32) |
| 86 | +if not mpi: |
| 87 | + local_m_indices = None |
| 88 | + |
| 89 | +order = libsharp.packed_real_order(lmax, ms=local_m_indices) |
| 90 | +local_nl = order.local_size() |
| 91 | +print("rank", rank, "local_nl", local_nl, "mval", order.mval()) |
| 92 | + |
| 93 | +mpi_comm = MPI.COMM_WORLD if mpi else None |
| 94 | + |
| 95 | +# map2alm |
| 96 | +# maps in libsharp are 3D, 2nd dimension is IQU, 3rd is pixel |
| 97 | + |
| 98 | +alm_sharp_I = libsharp.analysis(grid, order, |
| 99 | + np.ascontiguousarray(local_map[0].reshape((1, 1, -1))), |
| 100 | + spin=0, comm=mpi_comm) |
| 101 | +alm_sharp_P = libsharp.analysis(grid, order, |
| 102 | + np.ascontiguousarray(local_map[1:].reshape((1, 2, -1))), |
| 103 | + spin=2, comm=mpi_comm) |
| 104 | + |
| 105 | +beam = hp.gauss_beam(fwhm=np.radians(fwhm_deg), lmax=lmax, pol=True) |
| 106 | + |
| 107 | +print("Smooth") |
| 108 | +# smooth in place (zonca implemented this function) |
| 109 | +order.almxfl(alm_sharp_I, np.ascontiguousarray(beam[:, 0:1])) |
| 110 | +order.almxfl(alm_sharp_P, np.ascontiguousarray(beam[:, (1, 2)])) |
| 111 | + |
| 112 | +# alm2map |
| 113 | + |
| 114 | +new_local_map_I = libsharp.synthesis(grid, order, alm_sharp_I, spin=0, comm=mpi_comm) |
| 115 | +new_local_map_P = libsharp.synthesis(grid, order, alm_sharp_P, spin=2, comm=mpi_comm) |
| 116 | + |
| 117 | +# Transfer map to first process for writing |
| 118 | + |
| 119 | +local_full_map = np.zeros(input_map.shape, dtype=np.float64) |
| 120 | +local_full_map[0, local_pix] = new_local_map_I |
| 121 | +local_full_map[1:, local_pix] = new_local_map_P |
| 122 | + |
| 123 | +output_map = np.zeros(input_map.shape, dtype=np.float64) if rank == 0 else None |
| 124 | +mpi_comm.Reduce(local_full_map, output_map, root=0, op=MPI.SUM) |
| 125 | + |
| 126 | +if rank == 0: |
| 127 | + # hp.write_map("sharp_smoothed_map.fits", output_map, overwrite=True) |
| 128 | + # hp_smoothed = hp.alm2map(hp.map2alm(input_map, lmax=lmax), nside=nside) # transform only |
| 129 | + hp_smoothed = hp.smoothing(input_map, fwhm=np.radians(fwhm_deg), lmax=lmax) |
| 130 | + std_diff = (hp_smoothed-output_map).std() |
| 131 | + print("Std of difference between libsharp and healpy", std_diff) |
| 132 | + # hp.write_map( |
| 133 | + # "healpy_smoothed_map.fits", |
| 134 | + # hp_smoothed, |
| 135 | + # overwrite=True |
| 136 | + # ) |
| 137 | + assert std_diff < 1e-5 |
0 commit comments