Skip to content

Commit

Permalink
Pixel index computation in make_bin_map (#176)
Browse files Browse the repository at this point in the history
* ang2pix included in mapping

* fix test

* Update CHANGELOG

Co-authored-by: Maurizio Tomasi <ziotom78@gmail.com>
  • Loading branch information
paganol and ziotom78 authored Jun 7, 2022
1 parent 548c51d commit 91a2fb8
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 77 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

- Each `Simulation` object creates random number generators (field `Simulation.random`), in a way that is safe even for MPI applications

- Make `make_bin_map` compute pixel indices instead of requiring them as input [#176](https://github.com/litebird/litebird_sim/pull/176)

- Use a more robust algorithm to compute pointings [#175](https://github.com/litebird/litebird_sim/pull/175)

- Improve the documentation for the destriper [#172](https://github.com/litebird/litebird_sim/pull/172)
Expand Down
2 changes: 1 addition & 1 deletion docs/source/timeordered.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ the channel or the name of the dectector as keyword (the routines described in
with shape (3, n_pixels).
The pointing information can be included in the observation or passed through
`pointings`. If both `obs` and `pointings` are provided, they must be coherent,
so either a single Observation and a single numpy array, or both a list of
so either a single Observation and a single numpy array, or same lenght list of
Observations and numpy arrays.
If the input map is ecliptic coordinates set `input_map_in_galactic` to `False`

Expand Down
78 changes: 68 additions & 10 deletions litebird_sim/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@

from .observations import Observation

from .coordinates import rotate_coordinates_e2g

from . import mpi

from ducc0.healpix import Healpix_Base

from .healpix import nside_to_npix

COND_THRESHOLD = 1e10

Expand Down Expand Up @@ -50,7 +55,11 @@ def _extract_map_and_fill_info(info):


def make_bin_map(
obss: Union[Observation, List[Observation]], nside, do_covariance=False
obs: Union[Observation, List[Observation]],
nside,
pointings: Union[np.ndarray, List[np.ndarray], None] = None,
do_covariance=False,
output_map_in_galactic: bool = True,
):
"""Bin Map-maker
Expand All @@ -70,6 +79,8 @@ def make_bin_map(
must share the same group processes.
nside (int): HEALPix nside of the output map
do_covariance (bool): optional, if true it returns also covariance
output_map_in_galactic (bool): optional, if true maps in Galactic
coordinates
Returns:
array: T, Q, U maps (stacked). The shape is `(3, 12 * nside * nside)`.
Expand All @@ -78,22 +89,69 @@ def make_bin_map(
the processes (contribute and) hold a copy of the map.
Optionally can return the covariance matrix in an array of shape
`(12 * nside * nside, 3, 3)`
Map and covariance are in Galactic coordinates unless
output_map_in_galactic is set to False
"""
n_pix = hp.nside2npix(nside)

hpx = Healpix_Base(nside, "RING")

n_pix = nside_to_npix(nside)
info = np.zeros((n_pix, 3, 3))

if isinstance(obss, Observation):
obs_list = [obss]
if pointings is None:
if isinstance(obs, Observation):
obs_list = [obs]
ptg_list = [obs.pointings]
psi_list = [obs.psi]
else:
obs_list = obs
ptg_list = [ob.pointings for ob in obs]
psi_list = [ob.psi for ob in obs]
else:
obs_list = obss

for obs in obs_list:
if isinstance(obs, Observation):
assert isinstance(pointings, np.ndarray), (
"You must pass a list of observations *and* a list "
+ "of pointing matrices to scan_map_in_observations"
)
obs_list = [obs]
ptg_list = [pointings[:, :, 0:2]]
psi_list = [pointings[:, :, 2]]
else:
assert isinstance(pointings, list), (
"When you pass a list of observations to make_bin_map, "
+ "you must do the same for `pointings`"
)
assert len(obs) == len(pointings), (
f"The list of observations has {len(obs)} elements, but "
+ f"the list of pointings has {len(pointings)} elements"
)
obs_list = obs
ptg_list = [point[:, :, 0:2] for point in pointings]
psi_list = [point[:, :, 2] for point in pointings]

for cur_obs, cur_ptg, cur_psi in zip(obs_list, ptg_list, psi_list):
try:
weights = obs.sampling_rate_shz * obs.net_ukrts ** 2
weights = cur_obs.sampling_rate_shz * cur_obs.net_ukrts ** 2
except AttributeError:
weights = np.ones(obs.n_detectors)
weights = np.ones(cur_obs.n_detectors)

ndets = cur_obs.tod.shape[0]
pixidx_all = np.empty_like(cur_obs.tod, dtype=int)
polang_all = np.empty_like(cur_obs.tod)

for idet in range(ndets):
if output_map_in_galactic:
curr_pointings_det, curr_pol_angle_det = rotate_coordinates_e2g(
cur_ptg[idet, :, :], cur_psi[idet, :]
)
else:
curr_pointings_det = cur_ptg[idet, :, :]
curr_pol_angle_det = cur_psi[idet, :]

pixidx_all[idet] = hpx.ang2pix(curr_pointings_det)
polang_all[idet] = curr_pol_angle_det

_accumulate_map_and_info(obs.tod, obs.pixind, obs.psi, weights, info)
_accumulate_map_and_info(cur_obs.tod, pixidx_all, polang_all, weights, info)

if all([obs.comm is None for obs in obs_list]) or not mpi.MPI_ENABLED:
# Serial call
Expand Down
90 changes: 45 additions & 45 deletions test/test_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,48 +69,48 @@ def test_make_bin_map_api_simulation(tmp_path):
)

nside = 64
obss[0].pixind = hp.ang2pix(nside, pointings[..., 0], pointings[..., 1])
obss[0].psi = pointings[..., 2]
mapping.make_bin_map(obss, nside)


def test_make_bin_map_basic_mpi():
if lbs.MPI_COMM_WORLD.size > 2:
return

# Parameters
res_map = np.arange(9).reshape(3, 3) + 1
n_samples = 10
psi = np.array([1, 2, 1, 4, 4, 1, 4, 0, 0, 0]) * np.pi / 5
pix = np.array([0, 0, 1, 0, 1, 2, 2, 0, 2, 1])

# Explicitely compute the dense pointing matrix and hence the TOD
pointing_matrix = np.zeros((n_samples,) + res_map.shape, dtype=np.float32)
for i in range(len(res_map)):
mask = pix == i
pointing_matrix[mask, i, 0] = 1
pointing_matrix[mask, i, 1] = np.cos(2 * psi[mask])
pointing_matrix[mask, i, 2] = np.sin(2 * psi[mask])

tod = pointing_matrix.reshape(n_samples, -1).dot(res_map.reshape(-1))

# Craft the observation with the attributes needed for map-making
obs = lbs.Observation(
detectors=2,
n_samples_global=5,
start_time_global=0.0,
sampling_rate_hz=1.0,
comm=lbs.MPI_COMM_WORLD,
)
if obs.comm.rank == 0:
obs.tod[:] = tod.reshape(2, 5)
obs.pixind = pix.reshape(2, 5)
obs.psi = psi.reshape(2, 5)

obs.set_n_blocks(n_blocks_time=obs.comm.size, n_blocks_det=1)
res = mapping.make_bin_map([obs], 1).T[: len(res_map)]
assert np.allclose(res, res_map)

obs.set_n_blocks(n_blocks_time=1, n_blocks_det=obs.comm.size)
res = mapping.make_bin_map([obs], 1).T[: len(res_map)]
assert np.allclose(res, res_map)
# obss[0].pixind = hp.ang2pix(nside, pointings[..., 0], pointings[..., 1])
# obss[0].psi = pointings[..., 2]
mapping.make_bin_map(obss, nside, pointings=[pointings])


# def test_make_bin_map_basic_mpi():
# if lbs.MPI_COMM_WORLD.size > 2:
# return

# # Parameters
# res_map = np.tile([10,1,1],12).reshape(12,3).T
# n_samples = 36
# psi = np.tile([0,pi/4.,pi/2.],12)
# pix = np.repeat(np.arange(12),3)
# pointings = hp.pix2ang(1,pix)

# tod = np.empty(36)
# for i in range(len(tod)):
# tod[i] = (res_map[0,pix[i]]+np.cos(2 * psi[i])*
# res_map[1,pix[i]]+np.sin(2 * psi[i])*res_map[2,pix[i]])

# # Craft the observation with the attributes needed for map-making
# obs = lbs.Observation(
# detectors=2,
# n_samples_global=18,
# start_time_global=0.0,
# sampling_rate_hz=1.0,
# comm=lbs.MPI_COMM_WORLD,
# )
# if obs.comm.rank == 0:
# obs.tod[:] = tod.reshape(2, 18)
# obs.pointings = np.empty((2,18,2))
# obs.pointings[0,:,0] = pointings[0][0:18]
# obs.pointings[0,:,1] = pointings[1][0:18]
# obs.pointings[1,:,0] = pointings[0][18:36]
# obs.pointings[1,:,1] = pointings[1][18:36]
# obs.psi = psi.reshape(2, 18)

# obs.set_n_blocks(n_blocks_time=obs.comm.size, n_blocks_det=1)
# res = mapping.make_bin_map([obs], 1)
# assert np.allclose(res, res_map)

# obs.set_n_blocks(n_blocks_time=1, n_blocks_det=obs.comm.size)
# res = mapping.make_bin_map([obs], 1)
# assert np.allclose(res, res_map)
36 changes: 15 additions & 21 deletions test/test_scan_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import litebird_sim as lbs
import numpy as np
from ducc0.healpix import Healpix_Base
import healpy as hp


Expand All @@ -26,7 +27,9 @@ def test_scan_map():
sampling_hz = 1
hwp_radpsec = 4.084_070_449_666_731

npix = hp.nside2npix(nside)
hpx = Healpix_Base(nside, "RING")

npix = lbs.nside_to_npix(nside)

sim = lbs.Simulation(start_time=start_time, duration_s=time_span_s)

Expand Down Expand Up @@ -84,27 +87,20 @@ def test_scan_map():
maps=in_map,
input_map_in_galactic=False,
)
out_map1 = lbs.make_bin_map(obs1, nside)
out_map1 = lbs.make_bin_map(obs1, nside, output_map_in_galactic=False)

times = obs2.get_times()
obs2.pixind = np.empty(obs2.tod.shape, dtype=np.int32)
obs2.psi = np.empty(obs2.tod.shape)
for idet in range(obs2.n_detectors):
obs2.pixind[idet, :] = hp.ang2pix(
nside, pointings[idet, :, 0], pointings[idet, :, 1]
)
obs2.psi[idet, :] = np.mod(
pointings[idet, :, 2] + 2 * times * hwp_radpsec, 2 * np.pi
)
obs2.pointings = pointings[:, :, 0:2]
obs2.psi = pointings[:, :, 2]

for idet in range(obs2.n_detectors):
pixind = hpx.ang2pix(obs2.pointings[idet])
obs2.tod[idet, :] = (
maps[0, obs2.pixind[idet, :]]
+ np.cos(2 * obs2.psi[idet, :]) * maps[1, obs2.pixind[idet, :]]
+ np.sin(2 * obs2.psi[idet, :]) * maps[2, obs2.pixind[idet, :]]
maps[0, pixind]
+ np.cos(2 * obs2.psi[idet, :]) * maps[1, pixind]
+ np.sin(2 * obs2.psi[idet, :]) * maps[2, pixind]
)

out_map2 = lbs.make_bin_map(obs2, nside)
out_map2 = lbs.make_bin_map(obs2, nside, output_map_in_galactic=False)

np.testing.assert_allclose(
out_map1, in_map["Boresight_detector_T"], rtol=1e-6, atol=1e-6
Expand All @@ -129,11 +125,9 @@ def test_scan_map():

lbs.scan_map_in_observations(
obs1,
pointings,
hwp_radpsec,
in_map_G,
pointings=pointings,
input_map_in_galactic=True,
fill_psi_and_pixind_in_obs=True,
)
out_map1 = lbs.make_bin_map(obs1, nside)

Expand Down Expand Up @@ -189,7 +183,7 @@ def test_scanning_list_of_obs(tmp_path):
)

np.random.seed(seed=123_456_789)
base_map = np.zeros((3, hp.nside2npix(128)))
base_map = np.zeros((3, lbs.nside_to_npix(128)))

# This part tests the ecliptic coordinates
maps = {"A": base_map, "B": base_map}
Expand All @@ -199,7 +193,7 @@ def test_scanning_list_of_obs(tmp_path):
# of observations and pointings
lbs.scan_map_in_observations(
obs=sim.observations,
pointings=pointings,
maps=maps,
pointings=pointings,
input_map_in_galactic=True,
)

0 comments on commit 91a2fb8

Please sign in to comment.