def get_nvss_map(nside=None): map_filepath = os.path.join(DATA_PATH, 'NVSS', 'nvss_map_n512.fits') mask_filepath = os.path.join(DATA_PATH, 'NVSS', 'nvss_mask_n0512.fits') map, mask = hp.read_map(map_filepath), hp.read_map(mask_filepath) rotator = Rotator(coord=['G', 'C']) map = rotator.rotate_map_pixel(map) mask = rotator.rotate_map_pixel(mask) map, mask = tansform_map_and_mask_to_nside(map, mask, nside=nside) map = get_masked_map(map, mask) return map, mask
def test_rotate_map_polarization_with_spectrum(): """Rotation of reference frame should not change the angular power spectrum. In this test we create a map from a spectrum with a pure EE signal and check that the spectrum of this map and the spectrum of the same map rotated from Galactic to Ecliptic agrees. This test checks if the QU rotation is correct""" nside = 32 cl = np.zeros((6, 96), dtype=np.double) # Set ell=1 for EE to 1 cl[1][2] = 1 gal2ecl = Rotator(coord=["G", "E"]) m_original = hp.synfast(cl, nside=nside, new=True) cl_from_m_original = hp.anafast(m_original) m_rotated = gal2ecl.rotate_map_pixel(m_original) cl_from_m_rotated = hp.anafast(m_rotated) assert np.abs(cl_from_m_rotated - cl_from_m_original).sum() < 1e-2
def get_cmb_temperature_map(nside=None): filename = 'COM_CMB_IQU-smica_2048_R3.00_full.fits' # Read map = hp.read_map(os.path.join(DATA_PATH, 'Planck2018', filename), field=cmb_columns_idx.I_STOKES) mask = hp.read_map(os.path.join(DATA_PATH, 'Planck2018', filename), field=cmb_columns_idx.TMASK) # Rotate rotator = Rotator(coord=['G', 'C']) map = rotator.rotate_map_pixel(map) mask = rotator.rotate_map_pixel(mask) # Adjust map, mask = tansform_map_and_mask_to_nside(map, mask, nside=nside) map = get_masked_map(map, mask) mask = get_masked_map(mask, mask) return map, mask
def test_rotate_dipole_and_back(): """Rotate a smooth signal (dipole) from Galactic to Ecliptic and back""" nside = 64 npix = hp.nside2npix(nside) pix = np.arange(npix) vec = np.array(hp.pix2vec(nside, pix)) # dipole max is at North Pole dip_dir = np.array([0, 0, 1]) m_gal = np.dot(vec.T, dip_dir) gal2ecl = Rotator(coord=["C", "E"]) ecl2gal = Rotator(coord=["E", "C"]) m_ecl = gal2ecl.rotate_map_pixel(m_gal) # Remove 10 deg along equator because dipole signal is so low that relative error is # too large cut_equator_deg = 5 no_equator = hp.query_strip( nside, np.radians(90 + cut_equator_deg), np.radians(90 - cut_equator_deg) ) np.testing.assert_allclose( m_gal[no_equator], ecl2gal.rotate_map_pixel(m_ecl)[no_equator], rtol=1e-3 )
def test_rotate_map_polarization(): """Compare to a rotation from Galactic to Ecliptic of a map of pure Q polarization, the expected value was computed with HEALPix IDL: https://gist.github.com/zonca/401069e1c520e02eaff8cd86149d5900 """ nside = 32 npix = hp.nside2npix(nside) QU_gal = np.zeros((2, npix), dtype=np.double) QU_gal[0, :npix // 2] = 1 gal2ecl = Rotator(coord=["G", "E"]) QU_ecl = gal2ecl.rotate_map_pixel(QU_gal) expected = hp.ma( hp.read_map(os.path.join(path, "data", "justq_gal2ecl.fits.gz"), [0, 1])) expected.mask = expected == 0 for i_pol, pol in enumerate("QU"): assert ((np.abs(expected[i_pol] - QU_ecl[i_pol]) < .05).sum() / np.logical_not(expected[i_pol].mask).sum()) > .9, ( pol + " comparison failed in rotate_map")
def get_cmb_lensing_map(nside=None): folder_path = os.path.join(DATA_PATH, 'Planck2018/COM_Lensing_2048_R2.00') map_path = os.path.join(folder_path, 'dat_klm.fits') mask_path = os.path.join(folder_path, 'mask.fits') # Read klm = hp.read_alm(map_path) map = hp.alm2map(klm, nside) mask = hp.read_map(mask_path) # Rotate rotator = Rotator(coord=['G', 'C']) map = rotator.rotate_map_pixel(map) mask = rotator.rotate_map_pixel(mask) # Adjust mask = nmt.mask_apodization(mask, 0.2, apotype='C1') mask = hp.ud_grade(mask, nside_out=nside) mask[mask < 0.1] = 0 # Visualization purpose map = get_masked_map(map, mask) mask = get_masked_map(mask, mask) return map, mask
def test_rotate_map_polarization(): """Compare to a rotation from Galactic to Ecliptic of a map of pure Q polarization, the expected value was computed with HEALPix IDL: https://gist.github.com/zonca/401069e1c520e02eaff8cd86149d5900 """ nside = 32 npix = hp.nside2npix(nside) QU_gal = np.zeros((2, npix), dtype=np.double) QU_gal[0, : npix // 2] = 1 gal2ecl = Rotator(coord=["G", "E"]) QU_ecl = gal2ecl.rotate_map_pixel(QU_gal) expected = hp.ma( hp.read_map(os.path.join(path, "data", "justq_gal2ecl.fits.gz"), [0, 1]) ) expected.mask = expected == 0 for i_pol, pol in enumerate("QU"): assert ( (np.abs(expected[i_pol] - QU_ecl[i_pol]) < .05).sum() / np.logical_not(expected[i_pol].mask).sum() ) > .9, (pol + " comparison failed in rotate_map")
def test_rotate_dipole_and_back(): """Rotate a smooth signal (dipole) from Galactic to Ecliptic and back""" nside = 64 npix = hp.nside2npix(nside) pix = np.arange(npix) vec = np.array(hp.pix2vec(nside, pix)) # dipole max is at North Pole dip_dir = np.array([0, 0, 1]) m_gal = np.dot(vec.T, dip_dir) gal2ecl = Rotator(coord=["C", "E"]) ecl2gal = Rotator(coord=["E", "C"]) m_ecl = gal2ecl.rotate_map_pixel(m_gal) # Remove 10 deg along equator because dipole signal is so low that relative error is # too large cut_equator_deg = 5 no_equator = hp.query_strip(nside, np.radians(90 + cut_equator_deg), np.radians(90 - cut_equator_deg)) np.testing.assert_allclose(m_gal[no_equator], ecl2gal.rotate_map_pixel(m_ecl)[no_equator], rtol=1e-3)
def nenufar_ant_gain(freq, polar='NW', nside=64, time=None, normalize=True): """ Get NenuFAR elementary antenna gain with respect to the frequency ``freq``, the polarization ``polar`` and convert it to HEALPix representation with a given ``nside`` that defines pixel number. The map is initially in horizontal coordinates, conversion to equatorial coordinates requires to set the ``time`` at which the computation should occur. :param freq: Frequency of the returned antenna gain. Its value must be comprised between 10 and 80 MHz, because available antenna models are constrained to these frequencies. If the frequency value exceeds 80 MHz, an extrapolation is made using polynomial fits. :type freq: `float` or :class:`~astropy.units.Quantity` :param polar: Antenna polarization to take into account (either ``'NW'`` or ``'NE'``). :type polar: `str` :param nside: HEALPix nside parameter, must be a power of 2, less than 2**30 (see :func:`~healpy.pixelfunc.nside2resol` for corresponding angular pixel resolution). :type nside: `int` :param time: Time at which the computation occurs, in order to have the right antenna gain pattern on the sky above NenuFAR. If ``None`` the map is not rotated to equatorial coordinates. :type time: `str` or :class:`~astropy.time.Time` :param normalize: Returns the normalized gain or not. :type normalize: `bool` :returns: Sky map in HEALPix representation of normalized antenna gain. :rtype: :class:`~numpy.ndarray` :Example: Get the antenna gain and plot it (using :class:`~nenupy.astro.hpxsky.HpxSky`): >>> from nenupy.instru import nenufar_ant_gain >>> from nenupy.astro import HpxSky >>> ant_sky = HpxSky(resolution=0.5) >>> ant_sky.skymap = nenufar_ant_gain( freq=60, polar='NW', nside=ant_sky.nside, time='2020-04-01 12:00:00' ) >>> ant_sky.plot() .. image:: ./_images/antgain.png :width: 800 .. seealso:: :class:`~nenupy.beam.hpxbeam.HpxABeam` """ # Parameter checks if not isinstance(freq, Quantity): freq *= u.MHz freq = freq.to(u.MHz).value if polar.lower() not in ['nw', 'ne']: raise ValueError( 'Polar should be either NW or NE' ) polar = polar.upper() # Correspondance between polar/freq and field index in FITS gain_freqs = np.arange(10, 90, 10, dtype=int) count = 0 cols = {} for p in ['NE', 'NW']:#['NW', 'NE']: for f in gain_freqs: cols['{}_{}'.format(p, f)] = count count += 1 antgain_file = join( dirname(__file__), 'NenuFAR_Ant_Hpx.fits', ) if freq < 10: raise ValueError( 'No antenna model < 10 MHz.' ) elif freq > 80: log.warning( 'NenuFAR antenna response is extrapolated > 80 MHz.' ) # Will fit a polynomial along the high end of frequencies freq_to_fit = np.arange(40, 90, 10, dtype=int) gains = np.zeros((freq_to_fit.size, nside2npix(64))) # Construct the gain map (freqs, npix) for i, f in enumerate(freq_to_fit): gains[i, :] = read_map( filename=antgain_file, hdu=1, field=cols['{}_{}'.format(polar, f)], verbose=False, memmap=True, dtype=float ) # Get the polynomial coefficients coeffs = np.polyfit(freq_to_fit, gains, 3) def poly(x, coeffs): """ Retrieve the polynomial from coefficients """ na = np.newaxis order = coeffs.shape[0] poly = np.zeros((x.size, coeffs.shape[1])) for deg in range(order): poly += (x**deg)[:, na] * coeffs[order-deg-1, :][na, :] return poly gain = poly(np.array([freq]), coeffs).ravel() else: # Get Low and High ant gain bounding freq f_low = gain_freqs[gain_freqs <= freq].max() f_high = gain_freqs[gain_freqs >= freq].min() gain_low = read_map( filename=antgain_file, hdu=1, field=cols['{}_{}'.format(polar, f_low)], verbose=False, memmap=True, dtype=float ) gain_high = read_map( filename=antgain_file, hdu=1, field=cols['{}_{}'.format(polar, f_high)], verbose=False, memmap=True, dtype=float ) # Make interpolation if f_low != f_high: gain = gain_low * (f_high - freq)/10. +\ gain_high * (freq - f_low)/10. else: gain = gain_low # Rotate the map to equatorial coordinates if time is not None: if not isinstance(time, Time): time = Time(time) altaz_origin = AltAz( 0*u.deg, 0*u.deg, location=nenufar_loc, obstime=time ) radec_origin = altaz_origin.transform_to(ICRS) rot = Rotator( deg=True, rot=[radec_origin.ra.deg, radec_origin.dec.deg], coord=['C', 'C'], inv=True ) with _HiddenPrints(): gain = rot.rotate_map_alms(gain) # Convert HEALPix map to required nside gain = ud_grade(gain, nside_out=nside) return gain / gain.max() if normalize else gain
def test_rotator_eq(): rot_1 = Rotator(coord=("G", "E")) assert rot_1 == rot_1.get_inverse().get_inverse()
def test_rotate_vector(): gal2ecl = Rotator(coord=("G", "E")) gal_vec = np.array([0.1, -0.1, 1]) gal_vec_in_ecl = gal2ecl(*gal_vec) np.testing.assert_allclose(gal_vec, gal2ecl.I(gal_vec_in_ecl))
def test_rotator_input_lengths_inv(): with pytest.raises(ValueError): Rotator(coord=[("C", "E"), ("E", "G")], rot=[(0, 0, 90), (0, 90, 0)], inv=[True])
def test_rotator_input_type(): with pytest.raises(ValueError): Rotator(coord="CE", rot=[(0, 0, 90)])