def gal2equ(in_file, out_file, smooth, eulers=None): e2g = np.array([[-0.054882486, -0.993821033, -0.096476249], [ 0.494116468, -0.110993846, 0.862281440], [-0.867661702, -0.000346354, 0.497154957]]) # intrinsic rotation g2e = np.linalg.inv(e2g) eps = 23.452294 - 0.0130125 - 1.63889E-6 + 5.02778E-7 eps = eps * np.pi / 180. e2q = np.array([[1., 0. , 0. ], [0., np.cos( eps ), -1. * np.sin( eps )], [0., np.sin( eps ), np.cos( eps ) ]]) g2q = np.dot(e2q , g2e) psi = np.arctan2(g2q[1,2],g2q[0,2]) theta = np.arccos(g2q[2,2]) phi = np.arctan2(g2q[2,1],-g2q[2,0]) # deduced from zyz rotation matrix fwhm = smooth*((2*np.pi)/360) alms = hp.read_alm(in_file) hp.smoothalm(alms, fwhm=fwhm) if eulers == None: hp.rotate_alm(alms, phi, theta, psi) # reverse rotation order -> extrinsic rotation print('Euler angles (zyz) = ', str(np.rad2deg(phi)), str(np.rad2deg(theta)), str(np.rad2deg(psi))) else: eulers = np.deg2rad(eulers) hp.rotate_alm(alms, eulers[0], eulers[1], eulers[2]) print('Euler angles (zyz) = ', str(np.rad2deg(eulers[0])), str(np.rad2deg(eulers[1])), str(np.rad2deg(eulers[2]))) print(e2q) hp.write_alm(out_file, alms)
def rotate_map_to_axis(m, ra, dec, nest=False, method="direct"): """Rotate a sky map to place a given line of sight on the +z axis. Parameters ---------- m : np.ndarray The input HEALPix array. ra : float Right ascension of axis in radians. To specify the axis in geocentric coordinates, supply ra=(lon + gmst), where lon is the geocentric longitude and gmst is the Greenwich mean sidereal time in radians. dec : float Declination of axis in radians. To specify the axis in geocentric coordinates, supply dec=lat, where lat is the geocentric latitude in radians. nest : bool, default=False Indicates whether the input sky map is in nested rather than ring-indexed HEALPix coordinates (default: ring). method : 'direct' or 'fft' Select whether to use spherical harmonic transformation ('fft') or direct coordinate transformation ('direct') Returns ------- m_rotated : np.ndarray The rotated HEALPix array. """ npix = len(m) nside = hp.npix2nside(npix) theta = 0.5 * np.pi - dec phi = ra if method == "fft": if nest: m = hp.reorder(m, n2r=True) alm = hp.map2alm(m) hp.rotate_alm(alm, -phi, -theta, 0.0) ret = hp.alm2map(alm, nside, verbose=False) if nest: ret = hp.reorder(ret, r2n=True) elif method == "direct": R = hp.Rotator(rot=np.asarray([0, theta, -phi]), deg=False, inv=False, eulertype="Y") theta, phi = hp.pix2ang(nside, np.arange(npix), nest=nest) ipix = hp.ang2pix(nside, *R(theta, phi), nest=nest) ret = m[ipix] else: raise ValueError("Unrecognized method: {0}".format(method)) return ret
def test_rotate_alm(self): almigc = hp.map2alm(self.mapiqu) alms = [almigc[0], almigc[0:2], almigc, np.vstack(almigc)] for i in alms: o = deepcopy(i) hp.rotate_alm(o, 0.1, 0.2, 0.3) hp.rotate_alm(o, -0.3, -0.2, -0.1) np.testing.assert_allclose(i, o, rtol=1e-6)
def test_rotate_alm(self): almigc = hp.map2alm(self.mapiqu) alms = [almigc[0], almigc[0:2], almigc, np.vstack(almigc)] for i in alms: o = deepcopy(i) hp.rotate_alm(o, 0.1, 0.2, 0.3) hp.rotate_alm(o, -0.3, -0.2, -0.1) # FIXME: rtol=1e-6 works here, except on Debian with Python 3.4. np.testing.assert_allclose(i, o, rtol=1e-5)
def rotate_map(map,iepoch,oepoch,isys,osys,ns): #ns=healpy.npix2nside(map.size) alm=healpy.map2alm(map) phi,theta,psi=coord_v_convert.py_coordsys2euler_zyz(iepoch,oepoch,isys,osys) #healpy.sphtfunc.rotate_alm(alm,phi,theta,psi) healpy.rotate_alm(alm,phi,theta,psi) out_map=healpy.alm2map(alm,nside=ns) return out_map
def test_rotate_alm2(self): # Test rotate_alm against the Fortran library lmax = 64 nalm = hp.Alm.getsize(lmax) alm = np.zeros([3, nalm], dtype=np.complex) for i in range(3): for ell in range(lmax + 1): for m in range(ell): ind = hp.Alm.getidx(lmax, ell, m) alm[i, ind] = (i + 1) * 10 + ell + 1j * m psi = np.pi / 3. theta = 0.5 phi = 0.01 hp.rotate_alm(alm, psi, theta, phi) ref_0_0_0 = 0.00000000000 + 0.00000000000j ref_0_21_0 = -64.0056622444 + 0.00000000000j ref_0_21_21 = -3.19617408364 + 2.00219590117j ref_0_42_0 = 87.8201360825 + 0.00000000000j ref_0_42_21 = -6.57242309702 + 50.1128079361j ref_0_42_42 = 0.792592362074 - .928452597766j ref_0_63_0 = -49.6732554742 + 0.00000000000j ref_0_63_21 = -51.2812623888 - 61.6289129316j ref_0_63_42 = -9.32823219430 + 79.0787993482j ref_0_63_63 = -.157204566965 + 0.324692958700j ref_1_0_0 = 0.00000000000 + 0.00000000000j ref_1_21_0 = -85.5520809077 + 0.00000000000j ref_1_21_21 = -3.57384285749 + 2.93255811219j ref_1_42_0 = 107.541172254 + 0.00000000000j ref_1_42_21 = -2.77944941833 + 57.1015322415j ref_1_42_42 = 0.794212854046 - 1.10982745343j ref_1_63_0 = -60.7153303746 + 0.00000000000j ref_1_63_21 = -61.0915123767 - 65.9943878923j ref_1_63_42 = -4.86354653261 + 86.5277253196j ref_1_63_63 = -.147165377786 + 0.360474777237j ref = np.array([ ref_0_0_0, ref_0_21_0, ref_0_21_21, ref_0_42_0, ref_0_42_21, ref_0_42_42, ref_0_63_0, ref_0_63_21, ref_0_63_42, ref_0_63_63, ref_1_0_0, ref_1_21_0, ref_1_21_21, ref_1_42_0, ref_1_42_21, ref_1_42_42, ref_1_63_0, ref_1_63_21, ref_1_63_42, ref_1_63_63 ]) mine = [] for i in [0, 1]: for ell in range(0, lmax + 1, 21): for m in range(0, ell + 1, 21): ind = hp.Alm.getidx(lmax, ell, m) mine.append(alm[i, ind]) mine = np.array(ref) np.testing.assert_allclose(ref, mine, rtol=1e-10)
def test_rotate_alm_rotmatrix(self): """rotate_alm also support rotation matrix instead of angles""" lmax = 32 nalm = hp.Alm.getsize(lmax) alm = np.zeros([3, nalm], dtype=np.complex) alm[0, 1] = 1 alm[1, 2] = 1 alm_rotated_angles = alm.copy() angles = hp.rotator.coordsys2euler_zyz(coord=["G", "E"]) hp.rotate_alm(alm_rotated_angles, *angles) gal2ecl = hp.Rotator(coord=["G", "E"]) hp.rotate_alm(alm, matrix=gal2ecl.mat) np.testing.assert_allclose(alm_rotated_angles, alm)
def test_rotate_map_polarization_alms(): lmax = 64 path = os.path.dirname(os.path.realpath(__file__)) map1 = hp.read_map( os.path.join(path, "data", "wmap_band_iqumap_r9_7yr_W_v4_udgraded32.fits"), (0, 1, 2), ) # do the rotation with hp.rotate_alm angles = hp.rotator.coordsys2euler_zyz(coord=["G", "E"]) alm = hp.map2alm(map1, lmax=lmax, use_pixel_weights=True) hp.rotate_alm(alm, *angles) rotated_map1 = hp.alm2map(alm, nside=hp.get_nside(map1), lmax=lmax) # do the rotation with hp.Rotator gal2ecl = hp.Rotator(coord=["G", "E"]) rotate_map1_rotate_map_alms = gal2ecl.rotate_map_alms(map1, lmax=lmax) np.testing.assert_allclose(rotate_map1_rotate_map_alms, rotated_map1, rtol=1e-5)
def rotate_alm(alm, psi, theta, phi, lmax=None, method="auto", nthread=0, inplace=False): """Rotate the given alm[...,:] via the zyz rotations given by psi, theta and phi. The underlying implementation is provided by ducc0 or healpy. This is controlled with the "method" argument, which can be "ducc0", "healpy" or "auto". For "auto" it uses ducc0 if available, otherwise healpy. The resulting alm is returned. If inplace=True, then the input alm will be modified in place (but still returned). The number of threads to use is controlled with the nthread argument. If this is 0 (the default), then the number of threads is given by the value of the OMP_NUM_THREADS variable.""" if not inplace: alm = alm.copy() if lmax is None: lmax = sharp.nalm2lmax(alm.shape[-1]) if method == "auto": method = utils.first_importable("ducc0", "healpy") if method == "ducc0": import ducc0 try: nthread = nthread or int(os.environ['OMP_NUM_THREADS']) except (KeyError, ValueError): nthread = 0 for I in utils.nditer(alm.shape[:-1]): alm[I] = ducc0.sht.rotate_alm(alm[I], lmax=lmax, psi=psi, theta=theta, phi=phi) elif method == "healpy": import healpy for I in utils.nditer(alm.shape[:-1]): healpy.rotate_alm(alm[I], lmax=lmax, psi=psi, theta=theta, phi=phi) elif method is None: raise ValueError("No rotate_alm implementations found") else: raise ValueError("Unrecognized rotate_alm implementation '%s'" % str(method)) return alm
def offset_beam_ghost(az_off=0, el_off=0, polang=0, lmax=100, fwhm=200, hwp_freq=25., pol_only=True): ''' Script that scans LCDM realization of sky with a detector on the boresight that has no main beam but a full-amplitude ghost at the specified offset eam. The signal is then binned using the boresight pointing and compared to a map made by a symmetric Gaussian beam that has been rotated away from the boresight. Results should agree. Keyword arguments --------- az_off : float, Azimuthal location of detector relative to boresight (default : 0.) el_off : float, Elevation location of detector relative to boresight (default : 0.) polang : float, Detector polarization angle in degrees (defined for unrotated detector as offset from meridian) (default : 0) lmax : int, Maximum multipole number, (default : 200) fwhm : float, The beam FWHM used in this analysis in arcmin (default : 100) hwp_freq : float, HWP spin frequency (continuous mode) (default : 25.) pol_only : bool, Set unpolarized sky signal to zero (default : True) ''' # Load up alm and blm ell, cls = get_cls() np.random.seed(30) alm = hp.synalm(cls, lmax=lmax, new=True, verbose=True) # uK if pol_only: alm = (alm[0] * 0., alm[1], alm[2]) # init scan strategy and instrument mlen = 240 # mission length ss = ScanStrategy( mlen, # mission duration in sec. sample_rate=1, # sample rate in Hz location='spole') # South pole instrument # create single detector on boresight ss.create_focal_plane(nrow=1, ncol=1, fov=0, no_pairs=True, polang=polang, lmax=lmax, fwhm=fwhm, scatter=True) try: beam = ss.beams[0][0] # set main beam to zero beam.amplitude = 0. # create Gaussian beam (would be done by code anyway otherwise) beam.gen_gaussian_blm() #explicitely set offset to zero beam.az = 0. beam.el = 0. beam.polang = 0. # create full-amplitude ghost beam.create_ghost(az=az_off, el=el_off, polang=polang, amplitude=1.) ghost = beam.ghosts[0] ghost.gen_gaussian_blm() except IndexError as e: if ss.mpi_rank != 0: pass else: raise e # Start instrument rotated (just to make things complicated) rot_period = ss.mlen ss.set_instr_rot(period=rot_period, start_ang=45) # Set HWP rotation ss.set_hwp_mod(mode='stepped', freq=1 / 20., start_ang=45, angles=[34, 12, 67]) # calculate tod in one go (beam is symmetric so mmax=2 suffices) ss.partition_mission() ss.scan_instrument_mpi(alm, binning=False, nside_spin=512, max_spin=2, verbose=2) # Store the tod and pixel indices made with ghost try: tod_ghost = ss.tod.copy() pix_ghost = ss.pix.copy() except AttributeError as e: if ss.mpi_rank != 0: pass else: raise e # now repeat with asymmetric beam and no detector offset # set offsets to zero such that tods are generated using # only the boresight pointing. try: beam = ss.beams[0][0] beam.amplitude = 1. beam.gen_gaussian_blm() # Convert beam spin modes to E and B modes and rotate them blm = beam.blm blmI = blm[0].copy() blmE, blmB = tools.spin2eb(blm[1], blm[2]) # Rotate blm to match centroid. # Note that rotate_alm uses the ZYZ euler convention. # Note that we include polang here as first rotation. q_off = ss.det_offset(az_off, el_off, polang) ra, dec, pa = ss.quat2radecpa(q_off) # convert between healpy and math angle conventions phi = np.radians(ra) theta = np.radians(90 - dec) psi = np.radians(-pa) # rotate blm hp.rotate_alm([blmI, blmE, blmB], psi, theta, phi, lmax=lmax, mmax=lmax) # convert beam coeff. back to spin representation. blmm2, blmp2 = tools.eb2spin(blmE, blmB) beam.blm = (blmI, blmm2, blmp2) # kill ghost ghost.dead = True # spinmaps will still be created, so make as painless as possible ghost.lmax = 1 ghost.mmax = 0 except IndexError as e: if ss.mpi_rank != 0: pass else: raise e # reset instr. rot and hwp modulation ss.reset_instr_rot() ss.reset_hwp_mod() ss.scan_instrument_mpi(alm, binning=False, nside_spin=512, verbose=2, max_spin=lmax) # now we use all spin modes # Figure comparing the raw detector timelines for the two versions # For subpixel offsets, the bottom plot shows you that sudden shifts # in the differenced tods are due to the pointing for the symmetric # case hitting a different pixel than the boresight pointing. if ss.mpi_rank == 0: plt.figure() gs = gridspec.GridSpec(5, 9) ax1 = plt.subplot(gs[:2, :6]) ax2 = plt.subplot(gs[2:4, :6]) ax3 = plt.subplot(gs[-1, :6]) ax4 = plt.subplot(gs[:, 6:]) samples = np.arange(tod_ghost.size) ax1.plot(samples, ss.tod, label='Asymmetric Gaussian', linewidth=0.7) ax1.plot(samples, tod_ghost, label='Ghost', linewidth=0.7, alpha=0.5) ax1.legend() ax1.tick_params(labelbottom='off') sigdiff = ss.tod - tod_ghost ax2.plot(samples, sigdiff, ls='None', marker='.', markersize=2.) ax2.tick_params(labelbottom='off') ax3.plot(samples, (pix_ghost - ss.pix).astype(bool).astype(int), ls='None', marker='.', markersize=2.) ax1.set_ylabel(r'Signal [$\mu K_{\mathrm{CMB}}$]') ax2.set_ylabel(r'asym-sym. [$\mu K_{\mathrm{CMB}}$]') ax3.set_xlabel('Sample number') ax3.set_ylabel('different pixel?') ax3.set_ylim([-0.25, 1.25]) ax3.set_yticks([0, 1]) ax4.hist(sigdiff, 128, label='Difference') ax4.set_xlabel(r'Difference [$\mu K_{\mathrm{CMB}}$]') ax4.tick_params(labelleft='off') plt.savefig('../scratch/img/tods_ghost.png') plt.close()
def rotate_map_to_axis(m, ra, dec, nest=False, method='direct'): """Rotate a sky map to place a given line of sight on the +z axis. Parameters ---------- m : np.ndarray The input HEALPix array. ra : float Right ascension of axis in radians. To specify the axis in geocentric coordinates, supply ra=(lon + gmst), where lon is the geocentric longitude and gmst is the Greenwich mean sidereal time in radians. dec : float Declination of axis in radians. To specify the axis in geocentric coordinates, supply dec=lat, where lat is the geocentric latitude in radians. nest : bool, default=False Indicates whether the input sky map is in nested rather than ring-indexed HEALPix coordinates (default: ring). method : 'direct' or 'fft' Select whether to use spherical harmonic transformation ('fft') or direct coordinate transformation ('direct') Returns ------- m_rotated : np.ndarray The rotated HEALPix array. """ npix = len(m) nside = hp.npix2nside(npix) theta = 0.5 * np.pi - dec phi = ra if method == 'fft': if nest: m = hp.reorder(m, n2r=True) alm = hp.map2alm(m) hp.rotate_alm(alm, -phi, -theta, 0.0) ret = hp.alm2map(alm, nside, verbose=False) if nest: ret = hp.reorder(ret, r2n=True) elif method == 'direct': R = hp.Rotator(rot=np.asarray([0, theta, -phi]), deg=False, inv=False, eulertype='Y') theta, phi = hp.pix2ang(nside, np.arange(npix), nest=nest) ipix = hp.ang2pix(nside, *R(theta, phi), nest=nest) ret = m[ipix] else: raise ValueError('Unrecognized method: {0}'.format(method)) return ret
def test_rotate_alm2(self): # Test rotate_alm against the Fortran library lmax = 64 nalm = hp.Alm.getsize(lmax) alm = np.zeros([3, nalm], dtype=np.complex) for i in range(3): for ell in range(lmax + 1): for m in range(ell): ind = hp.Alm.getidx(lmax, ell, m) alm[i, ind] = (i + 1) * 10 + ell + 1j * m psi = np.pi / 3. theta = 0.5 phi = 0.01 hp.rotate_alm(alm, psi, theta, phi) ref_0_0_0 = 0.00000000000 + 0.00000000000j ref_0_21_0 = -64.0056622444 + 0.00000000000j ref_0_21_21 = -3.19617408364 + 2.00219590117j ref_0_42_0 = 87.8201360825 + 0.00000000000j ref_0_42_21 = -6.57242309702 + 50.1128079361j ref_0_42_42 = 0.792592362074 - .928452597766j ref_0_63_0 = -49.6732554742 + 0.00000000000j ref_0_63_21 = -51.2812623888 - 61.6289129316j ref_0_63_42 = -9.32823219430 + 79.0787993482j ref_0_63_63 = -.157204566965 + 0.324692958700j ref_1_0_0 = 0.00000000000 + 0.00000000000j ref_1_21_0 = -85.5520809077 + 0.00000000000j ref_1_21_21 = -3.57384285749 + 2.93255811219j ref_1_42_0 = 107.541172254 + 0.00000000000j ref_1_42_21 = -2.77944941833 + 57.1015322415j ref_1_42_42 = 0.794212854046 - 1.10982745343j ref_1_63_0 = -60.7153303746 + 0.00000000000j ref_1_63_21 = -61.0915123767 - 65.9943878923j ref_1_63_42 = -4.86354653261 + 86.5277253196j ref_1_63_63 = -.147165377786 + 0.360474777237j ref = np.array( [ ref_0_0_0, ref_0_21_0, ref_0_21_21, ref_0_42_0, ref_0_42_21, ref_0_42_42, ref_0_63_0, ref_0_63_21, ref_0_63_42, ref_0_63_63, ref_1_0_0, ref_1_21_0, ref_1_21_21, ref_1_42_0, ref_1_42_21, ref_1_42_42, ref_1_63_0, ref_1_63_21, ref_1_63_42, ref_1_63_63, ] ) mine = [] for i in [0, 1]: for ell in range(0, lmax + 1, 21): for m in range(0, ell + 1, 21): ind = hp.Alm.getidx(lmax, ell, m) mine.append(alm[i, ind]) mine = np.array(ref) np.testing.assert_allclose(ref, mine, rtol=1e-10)
def test_rotate_alm_complex64(self): lmax = 32 nalm = hp.Alm.getsize(lmax) alm = np.zeros([3, nalm], dtype=np.complex64) with pytest.raises(ValueError): hp.rotate_alm(alm, 0.1, 0.2, 0.3)
b = cm.seismic # b.set_under("w") # hp.mollview(gmapa, cmap=b, title='Planck Kappa mapa (galaktični sistem)', cbar=True, xsize=1400) #izris mape #ct.mollaxes() hp.graticule(coord=('E')) # plt.show() #alm_check=hp.map2alm(gmapa,lmax=lmax) #preveritev, če vrne map2alm vsaj podobne koeficiente kot v originalnih podatkih (vrne renormalizirane, ker vrnejo isto obliko mape z renormaliziranimi vrednostmi) b, l, aomega = ct.eq2gal(0, 90) hp.rotate_alm(galm, psi=l, theta=b, phi=aomega, lmax=lmax) emapa = hp.alm2map(galm, nside=64) #kreacija mape iz ealm koeficientov #emapa=emapa*10**-2 #renormalizacija (še ne pravilna) b = cm.seismic # b.set_under("w") # hp.mollview(emapa, cmap=b, title='Planck Kappa mapa (ekvatorialni sistem)', cbar=True, xsize=1400) #izris mape ct.mollaxes() hp.graticule(coord=('E')) # plt.show()
def test_offset_beam_pol(self): mlen = 20 # mission length sample_rate = 10 location='spole' lmax = self.lmax fwhm = 300 nside_spin = 256 #polang = 30 #az_off = 20 #el_off = 40 polang = 90 az_off = 20 el_off = 0 alm = (self.alm[0]*0., self.alm[1], self.alm[2]) ss = ScanStrategy(mlen, sample_rate=sample_rate, location=location) # Create single detector. ss.create_focal_plane(nrow=1, ncol=1, fov=0, no_pairs=True, polang=polang, lmax=lmax, fwhm=fwhm) # Move detector away from boresight. ss.beams[0][0].az = az_off ss.beams[0][0].el = el_off # Start instrument rotated. rot_period = ss.mlen ss.set_instr_rot(period=rot_period, start_ang=45) #ss.set_hwp_mod(mode='stepped', freq=1/20., start_ang=45, # angles=[34, 12, 67]) ss.partition_mission() ss.scan_instrument_mpi(alm, binning=False, nside_spin=nside_spin, max_spin=2, interp=True) # Store the tod and pixel indices made with symmetric beam. tod_sym = ss.tod.copy() # Now repeat with asymmetric beam and no detector offset. # Set offsets to zero such that tods are generated using # only the boresight pointing. ss.beams[0][0].az = 0 ss.beams[0][0].el = 0 ss.beams[0][0].polang = 0 # Convert beam spin modes to E and B modes and rotate them # create blm again, scan_instrument_mpi detetes blms when done ss.beams[0][0].gen_gaussian_blm() blm = ss.beams[0][0].blm blmI = blm[0].copy() blmE, blmB = tools.spin2eb(blm[1], blm[2]) # Rotate blm to match centroid. # Note that rotate_alm uses the ZYZ euler convention. # Note that we include polang here as first rotation. q_off = ss.det_offset(az_off, el_off, polang) ra, dec, pa = ss.quat2radecpa(q_off) # We need to to apply these changes to the angles. phi = np.radians(ra) theta = np.radians(90 - dec) psi = np.radians(-pa) print('angles', psi, theta, phi) # rotate blm hp.rotate_alm([blmI, blmE, blmB], psi, theta, phi, lmax=lmax, mmax=lmax) # convert beam coeff. back to spin representation. blmm2, blmp2 = tools.eb2spin(blmE, blmB) ss.beams[0][0].blm = (blmI, blmm2, blmp2) ss.reset_instr_rot() ss.reset_hwp_mod() ss.scan_instrument_mpi(alm, binning=False, nside_spin=nside_spin, max_spin=lmax, interp=True) # TODs must agree at least at 2% per sample. print('tod_sym', tod_sym[::10]) print('ss.tod', ss.tod[::10]) np.testing.assert_equal(np.abs(ss.tod - tod_sym) < 0.02 * np.std(tod_sym), np.full(tod_sym.size, True))
def rotate_alm(alm, iepoch, oepoch, isys, osys): phi, theta, psi = py_coordsys2euler_zyz(iepoch, oepoch, isys, osys) hp.rotate_alm(alm, phi, theta, psi) return alm
def offset_beam_interp(az_off=0, el_off=0, polang=0, lmax=100, fwhm=200, hwp_freq=25., pol_only=True): ''' Script that scans LCDM realization of sky with a symmetric Gaussian beam that has been rotated away from the boresight. This means that the beam is highly asymmetric. Scanning using interpolation. Keyword arguments ----------------- az_off : float, Azimuthal location of detector relative to boresight (default : 0.) el_off : float, Elevation location of detector relative to boresight (default : 0.) polang : float, Detector polarization angle in degrees (defined for unrotated detector as offset from meridian) (default : 0) lmax : int, Maximum multipole number, (default : 200) fwhm : float, The beam FWHM used in this analysis [arcmin] (default : 100) hwp_freq : float, HWP spin frequency (continuous mode) (default : 25.) pol_only : bool, Set unpolarized sky signal to zero (default : True) ''' # Load up alm and blm ell, cls = get_cls() np.random.seed(30) alm = hp.synalm(cls, lmax=lmax, new=True, verbose=True) # uK if pol_only: alm = (alm[0]*0., alm[1], alm[2]) # init scan strategy and instrument mlen = 240 # mission length ss = ScanStrategy(mlen, # mission duration in sec. sample_rate=1, # sample rate in Hz location='spole') # South pole instrument # create single detector ss.create_focal_plane(nrow=1, ncol=1, fov=0, no_pairs=True, polang=polang, lmax=lmax, fwhm=fwhm, scatter=True) # move detector away from boresight try: ss.beams[0][0].az = az_off ss.beams[0][0].el = el_off except IndexError as e: if ss.mpi_rank != 0: pass else: raise e # Start instrument rotated (just to make things complicated) rot_period = ss.mlen ss.set_instr_rot(period=rot_period, start_ang=45) # Set HWP rotation ss.set_hwp_mod(mode='stepped', freq=1/20., start_ang=45, angles=[34, 12, 67]) # calculate tod in one go (beam is symmetric so mmax=2 suffices) ss.partition_mission() ss.scan_instrument_mpi(alm, binning=False, nside_spin=512, max_spin=2, interp=True) # Store the tod made with symmetric beam try: tod_sym = ss.tod.copy() except AttributeError as e: if ss.mpi_rank != 0: pass else: raise e # now repeat with asymmetric beam and no detector offset # set offsets to zero such that tods are generated using # only the boresight pointing. try: ss.beams[0][0].az = 0 ss.beams[0][0].el = 0 ss.beams[0][0].polang = 0 # Convert beam spin modes to E and B modes and rotate them # create blm again, scan_instrument_mpi detetes blms when done ss.beams[0][0].gen_gaussian_blm() blm = ss.beams[0][0].blm blmI = blm[0].copy() blmE, blmB = tools.spin2eb(blm[1], blm[2]) # Rotate blm to match centroid. # Note that rotate_alm uses the ZYZ euler convention. # Note that we include polang here as first rotation. q_off = ss.det_offset(az_off, el_off, polang) ra, dec, pa = ss.quat2radecpa(q_off) # convert between healpy and math angle conventions phi = np.radians(ra) theta = np.radians(90 - dec) psi = np.radians(-pa) # rotate blm hp.rotate_alm([blmI, blmE, blmB], psi, theta, phi, lmax=lmax, mmax=lmax) # convert beam coeff. back to spin representation. blmm2, blmp2 = tools.eb2spin(blmE, blmB) ss.beams[0][0].blm = (blmI, blmm2, blmp2) except IndexError as e: if ss.mpi_rank != 0: pass else: raise e ss.reset_instr_rot() ss.reset_hwp_mod() # Now we use all spin modes. ss.scan_instrument_mpi(alm, binning=False, nside_spin=512, max_spin=lmax, interp=True) if ss.mpi_rank == 0: plt.figure() gs = gridspec.GridSpec(5, 9) ax1 = plt.subplot(gs[:2, :6]) ax2 = plt.subplot(gs[2:4, :6]) ax4 = plt.subplot(gs[:, 6:]) samples = np.arange(tod_sym.size) ax1.plot(samples, ss.tod, label='Asymmetric Gaussian', linewidth=0.7) ax1.plot(samples, tod_sym, label='Symmetric Gaussian', linewidth=0.7, alpha=0.5) ax1.legend() ax1.tick_params(labelbottom='off') sigdiff = ss.tod - tod_sym ax2.plot(samples, sigdiff,ls='None', marker='.', markersize=2.) ax2.tick_params(labelbottom='off') ax1.set_ylabel(r'Signal [$\mu K_{\mathrm{CMB}}$]') ax2.set_ylabel(r'asym-sym. [$\mu K_{\mathrm{CMB}}$]') ax2.set_xlabel('Sample number') ax4.hist(sigdiff, 128, label='Difference') ax4.set_xlabel(r'Difference [$\mu K_{\mathrm{CMB}}$]') ax4.tick_params(labelleft='off') plt.savefig('../scratch/img/tods_interp.png', dpi=250) plt.close()
progress("%s TQU read" % name) imap = np.array(healpy.read_map(ifile, fields)).astype(dtype) nside = healpy.npix2nside(imap.shape[-1]) progress("%s TQU mask" % name) imap[healpy.mask_bad(imap)] = 0 progress("%s TQU scale" % name) imap *= runit nside = healpy.npix2nside(imap.shape[-1]) lmax = args.lmax or 3*nside progress("%s TQU alm2map" % name) alm = curvedsky.map2alm_healpix(imap, lmax=lmax) del imap # work around healpix bug progress("%s TQU rotate_alm" % name) alm = alm.astype(np.complex128,copy=False) healpy.rotate_alm(alm, euler[0], euler[1], euler[2]) alm = alm.astype(ctype,copy=False) progress("%s TQU map2alm" % name) curvedsky.alm2map_cyl(alm, omap) del alm ofile = ifile[:-5] + "_map.fits" progress("%s TQU write %s" % (name, ofile)) enmap.write_map(ofile, omap) def get_pixsize_rect(shape, wcs): """Return the exact pixel size in steradians for the rectangular cylindrical projection given by shape, wcs. Returns area[ny], where ny = shape[-2] is the number of rows in the image. All pixels on the same row have the same area.""" ymin = enmap.sky2pix(shape, wcs, [-np.pi/2,0])[0] ymax = enmap.sky2pix(shape, wcs, [ np.pi/2,0])[0] y = np.arange(shape[-2])
L.debug("Projecting") res = curvedsky.alm2map_pos(alm, pmap) if args.rot and ncomp==3: L.debug("Rotating polarization vectors") res[1:3] = enmap.rotate_pol(res[1:3], psi) else: # We will project directly onto target map if possible if args.rot: L.debug("Rotating alms") s1,s2 = args.rot.split(",") if s1 != s2: # Note: rotate_alm does not actually modify alm # if it is single precision alm = alm.astype(np.complex128,copy=False) if s1 == "gal" and (s2 == "equ" or s2 == "cel"): healpy.rotate_alm(alm, euler[0], euler[1], euler[2]) elif s2 == "gal" and (s1 == "equ" or s1 == "cel"): healpy.rotate_alm(alm,-euler[2],-euler[1],-euler[0]) else: raise NotImplementedError alm = alm.astype(ctype,copy=False) L.debug("Projecting") res = enmap.zeros((len(alm),)+shape[-2:], wcs, dtype) res = curvedsky.alm2map(alm, res) if len(args.templates) > 1: oname = args.odir + "/" + os.path.basename(tfile) else: oname = args.ofile if args.oslice: res = eval("res"+args.oslice) L.info("Writing " + oname)