def test_el_steps(self): scs = ScanStrategy(duration=200) scs.set_el_steps(10, steps=np.arange(5)) nsteps = int(np.ceil(scs.mlen / float(scs.step_dict['period']))) self.assertEqual(nsteps, 20) for step in range(12): el = next(scs.el_step_gen) scs.step_dict['step'] = el self.assertEqual(el, step % 5) self.assertEqual(scs.step_dict['step'], el) scs.step_dict['remainder'] = 100 scs.reset_el_steps() self.assertEqual(scs.step_dict['step'], 0) self.assertEqual(scs.step_dict['remainder'], 0) for step in range(nsteps): el = next(scs.el_step_gen) self.assertEqual(el, step % 5) scs.reset_el_steps() self.assertEqual(next(scs.el_step_gen), 0) self.assertEqual(next(scs.el_step_gen), 1)
def test_init_detpair2(self): ''' Check if function works with only A beam. ''' mmax = 3 nside = 16 scs = ScanStrategy() beam_a = Beam(fwhm=0., btype='Gaussian', mmax=mmax) beam_b = None init_spinmaps_opts = dict(max_spin=5, nside_spin=nside, verbose=False) scs.init_detpair(self.alm, beam_a, beam_b=beam_b, **init_spinmaps_opts) # Test for correct shapes. # Note empty lists evaluate to False self.assertFalse(scs.spinmaps['ghosts']) func, func_c = scs.spinmaps['main_beam']['maps'] self.assertEqual(func.shape, (mmax + 1, 12 * nside ** 2)) self.assertEqual(func_c.shape, (2 * mmax + 1, 12 * nside ** 2))
def test_init_spinmaps_old_new(self): # Test if spinmaps with are consistent between old and new # implementation of HWP. def rand_alm(lmax): alm = np.empty(hp.Alm.getsize(lmax), dtype=np.complex128) alm[:] = np.random.randn(hp.Alm.getsize(lmax)) alm += 1j * np.random.randn(hp.Alm.getsize(lmax)) # Make m=0 modes real. alm[:lmax+1] = np.real(alm[:lmax+1]) return alm lmax = 4 alm = tuple([rand_alm(lmax) for i in range(3)]) blmI = np.array([0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=np.complex128) blmm2 = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0], dtype=np.complex128) blmp2 = np.zeros_like(blmm2) blm = (blmI, blmm2, blmp2) nside = 32 max_spin = 3 spinmaps_old = ScanStrategy._init_spinmaps(alm, blm, max_spin, nside, symmetric=False, hwp_mueller=None) hwp_mueller = np.asarray([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1]]) spinmaps_new = ScanStrategy._init_spinmaps(alm, blm, max_spin, nside, symmetric=False, hwp_mueller=hwp_mueller) np.testing.assert_almost_equal(spinmaps_old['s0a0']['maps'], spinmaps_new['s0a0']['maps']) zero_map = np.zeros(hp.nside2npix(nside)) np.testing.assert_almost_equal(spinmaps_old['s2a4']['maps'][0], zero_map) np.testing.assert_almost_equal(spinmaps_old['s2a4']['maps'][1], zero_map) np.testing.assert_almost_equal(spinmaps_old['s2a4']['maps'][2], zero_map) np.testing.assert_almost_equal(spinmaps_old['s2a4']['maps'][3], zero_map) np.testing.assert_almost_equal(spinmaps_old['s2a4']['maps'][4], zero_map) np.testing.assert_almost_equal(spinmaps_old['s2a4']['maps'][6], zero_map) np.testing.assert_almost_equal(spinmaps_new['s2a4']['maps'][0], zero_map) np.testing.assert_almost_equal(spinmaps_new['s2a4']['maps'][1], zero_map) np.testing.assert_almost_equal(spinmaps_new['s2a4']['maps'][2], zero_map) np.testing.assert_almost_equal(spinmaps_new['s2a4']['maps'][3], zero_map) np.testing.assert_almost_equal(spinmaps_new['s2a4']['maps'][4], zero_map) np.testing.assert_almost_equal(spinmaps_new['s2a4']['maps'][6], zero_map) np.testing.assert_almost_equal(spinmaps_old['s2a4']['maps'][5], spinmaps_new['s2a4']['maps'][5])
def test_init_detpair(self): ''' Check if spinmaps are correctly created. ''' mmax = 3 nside = 16 scs = ScanStrategy(duration=1, sample_rate=10) beam_a = Beam(fwhm=0., btype='Gaussian', mmax=mmax) beam_b = Beam(fwhm=0., btype='Gaussian', mmax=mmax) init_spinmaps_opts = dict(max_spin=5, nside_spin=nside) scs.init_detpair(self.alm, beam_a, beam_b=beam_b, **init_spinmaps_opts) # We expect a spinmaps attribute (dict) with # main_beam key that contains a list of [func, func_c] # where func has shape (mmax + 1, 12nside**2) and # func_c has shape (2 mmax + 1, 12nside**2). # We expect an empty list for the ghosts. # Note empty lists evaluate to False self.assertFalse(scs.spinmaps['ghosts']) func = scs.spinmaps['main_beam']['s0a0']['maps'] func_c = scs.spinmaps['main_beam']['s2a4']['maps'] self.assertEqual(func.shape, (mmax + 1, 12 * nside ** 2)) self.assertEqual(func_c.shape, (2 * mmax + 1, 12 * nside ** 2)) # Since we have a infinitely narrow Gaussian the convolved # maps should just match the input (up to healpix quadrature # wonkyness). input_map = hp.alm2map(self.alm, nside, verbose=False) # I, Q, U zero_map = np.zeros_like(input_map[0]) np.testing.assert_array_almost_equal(input_map[0], func[0], decimal=6) # s = 2 Pol map should be Q \pm i U np.testing.assert_array_almost_equal(input_map[1] + 1j * input_map[2], func_c[mmax + 2], decimal=6) # Test if rest of maps are zero. for i in range(1, mmax + 1): np.testing.assert_array_almost_equal(zero_map, func[i], decimal=6) for i in range(1, 2 * mmax + 1): if i == mmax + 2: continue print(i) np.testing.assert_array_almost_equal(zero_map, func_c[i], decimal=6)
def test_spinmaps_complex(self): # Test if spinmaps_complex returns to spinmaps_real # in case where sky and beam B-modes are zero. def rand_alm(lmax): alm = np.empty(hp.Alm.getsize(lmax), dtype=np.complex128) alm[:] = np.random.randn(hp.Alm.getsize(lmax)) alm += 1j * np.random.randn(hp.Alm.getsize(lmax)) # Make m=0 modes real. alm[:lmax+1] = np.real(alm[:lmax+1]) return alm lmax = 10 almE, almB = tuple([rand_alm(lmax) for i in range(2)]) blmE, blmB = tuple([rand_alm(lmax) for i in range(2)]) spin_values = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] nside = 32 spinmaps = ScanStrategy._spinmaps_complex(almE, almB*0, blmE, blmB*0, spin_values, nside) for spin in range(6): sidx_pos = spin + 5 sidx_neg = 5 - spin np.testing.assert_almost_equal(spinmaps[sidx_pos], np.conj(spinmaps[sidx_neg]))
def test_init_no_sample_rate(self): # Test if we can also init without specifying mlen. scs = ScanStrategy(duration=5, num_samples=100) # nsamp = mlen * sample_rate self.assertEqual(scs.mlen, 5) self.assertEqual(scs.fsamp, 20) self.assertEqual(scs.nsamp, 100)
def test_init_zero_duration(self): # Sample rate should be zero scs = ScanStrategy(duration=0, sample_rate=10) # nsamp = mlen * sample_rate self.assertEqual(scs.mlen, 0) self.assertEqual(scs.fsamp, 0) self.assertEqual(scs.nsamp, 0)
def test_init(self): scs = ScanStrategy(duration=200, sample_rate=10) self.assertEqual(scs.mlen, 200) self.assertEqual(scs.fsamp, 10.) self.assertEqual(scs.nsamp, 2000) self.assertRaises(AttributeError, setattr, scs, 'fsamp', 1) self.assertRaises(AttributeError, setattr, scs, 'mlen', 1) self.assertRaises(AttributeError, setattr, scs, 'nsamp', 1)
def test_init(self): scs = ScanStrategy(duration=200, sample_rate=10) self.assertEqual(scs.mlen, 200) self.assertEqual(scs.fsamp, 10.) self.assertEqual(scs.nsamp, 2000) # Test if we are unable to change scan parameters after init. self.assertRaises(AttributeError, setattr, scs, 'fsamp', 1) self.assertRaises(AttributeError, setattr, scs, 'mlen', 1) self.assertRaises(AttributeError, setattr, scs, 'nsamp', 1)
def test_chunks(self): '''Test the _chunk2idx function. ''' mlen = 100 # so 1000 samples chunksize = 30 rot_period = 1.2 # Note, seconds. scs = ScanStrategy(duration=mlen, sample_rate=10) scs.partition_mission(chunksize=chunksize) self.assertEqual(len(scs.chunks), int(np.ceil(scs.nsamp / float(chunksize)))) # Take single chunk and subdivide it and check whether we # can correctly access a chunk-sized array. scs.set_instr_rot(period=rot_period) for chunk in scs.chunks: scs.rotate_instr() subchunks = scs.subpart_chunk(chunk) chunklen = chunk['end'] - chunk['start'] # Start with zero array, let every subchunk add ones # to its slice, then test if resulting array is one # everywhere. arr = np.zeros(chunklen, dtype=int) for subchunk in subchunks: self.assertEqual(subchunk['cidx'], chunk['cidx']) self.assertTrue(subchunk['start'] >= chunk['start']) self.assertTrue(subchunk['end'] <= chunk['end']) qidx_start, qidx_end = scs._chunk2idx(**subchunk) arr[qidx_start:qidx_end] += 1 np.testing.assert_array_equal(arr, np.ones_like(arr))
def test_interpolate(self): ''' Compare interpoted TOD to raw for extremely bandlimited input such that should agree relatively well. ''' mlen = 60 mmax = 2 ra0 = -10 dec0 = -57.5 fwhm = 100 nside = 128 az_throw = 10 scs = ScanStrategy(duration=mlen, sample_rate=10, location='spole') # Create a 1 x 1 square grid of Gaussian beams. scs.create_focal_plane(nrow=1, ncol=1, fov=4, lmax=self.lmax, fwhm=fwhm) # Generate timestreams, bin them and store as attributes. scs.scan_instrument_mpi(self.alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=2., nside_spin=nside, max_spin=mmax, binning=False) tod_raw = scs.tod.copy() scs.scan_instrument_mpi(self.alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=2., nside_spin=nside, max_spin=mmax, reuse_spinmaps=False, interp=True, binning=False) np.testing.assert_array_almost_equal(tod_raw, scs.tod, decimal=0)
def test_preview_pointing_input(self): # Test if scan_instrument_mpi works with preview_pointing # option set. scs = ScanStrategy(duration=1, sample_rate=10, location='spole') # Should raise error if alm is None with preview_pointing not set. alm = None with self.assertRaises(TypeError): scs.scan_instrument_mpi(alm, verbose=0, preview_pointing=False) # Should not raise error if alm is provided and preview_pointing set. alm = self.alm scs.scan_instrument_mpi(alm, verbose=0, preview_pointing=True)
def test_init_err(self): # Test if init raises erorrs when user does not # provide enough info. with self.assertRaises(ValueError): ScanStrategy(duration=5) with self.assertRaises(ValueError): ScanStrategy(num_samples=5) with self.assertRaises(ValueError): ScanStrategy(sample_rate=5) # Or if nsamp = mlen * sample_rate is not satisfied. with self.assertRaises(ValueError): ScanStrategy(duration=10, sample_rate=20, num_samples=100) # Or when sample_rate is zero or negative. with self.assertRaises(ValueError): ScanStrategy(sample_rate=0, duration=10) with self.assertRaises(ValueError): ScanStrategy(sample_rate=-2, duration=10)
def test_scan_ghosts_map(self): ''' Perform a (low resolution) scan with two detectors, compare map to detector + ghost. ''' mlen = 10 * 60 rot_period = 120 mmax = 2 ra0 = -10 dec0 = -57.5 fwhm = 200 nside = 256 az_throw = 10 scs = ScanStrategy(duration=mlen, sample_rate=10, location='spole') # Create two Gaussian (main) beams. beam_opts = dict(az=0, el=0, polang=28, fwhm=fwhm, lmax=self.lmax, symmetric=True) ghost_opts = dict(az=0, el=0, polang=28, fwhm=fwhm, lmax=self.lmax, symmetric=True, amplitude=1) scs.add_to_focal_plane(Beam(**beam_opts)) scs.add_to_focal_plane(Beam(**ghost_opts)) # Allocate and assign parameters for mapmaking. scs.allocate_maps(nside=nside) # Set HWP rotation. scs.set_hwp_mod(mode='continuous', freq=3.) # Generate timestreams, bin them and store as attributes. scs.scan_instrument_mpi(self.alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=2., binning=False, nside_spin=nside, max_spin=mmax, save_tod=True) tod = scs.data(scs.chunks[0], beam=scs.beams[0][0], data_type='tod') tod += scs.data(scs.chunks[0], beam=scs.beams[1][0], data_type='tod') tod = tod.copy() # Solve for the maps. maps, cond = scs.solve_for_map(fill=np.nan) # To supress warnings cond[~np.isfinite(cond)] = 10 # Repeat with single beam + ghost. scs.remove_from_focal_plane(scs.beams[1][0]) scs.beams[0][0].create_ghost(**ghost_opts) scs.reset_hwp_mod() scs.scan_instrument_mpi(self.alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=2., binning=False, nside_spin=nside, max_spin=mmax, save_tod=True) tod_w_ghost = scs.data(scs.chunks[0], beam=scs.beams[0][0], data_type='tod') # Sum TOD of two beams must match TOD of single beam + ghost. np.testing.assert_array_almost_equal(tod, tod_w_ghost, decimal=10) # Maps must match. maps_w_ghost, cond_w_ghost = scs.solve_for_map(fill=np.nan) # To supress warnings cond_w_ghost[~np.isfinite(cond_w_ghost)] = 10 np.testing.assert_array_almost_equal(maps[0, cond < 2.5], maps_w_ghost[0, cond_w_ghost < 2.5], decimal=10) np.testing.assert_array_almost_equal(maps[1, cond < 2.5], maps_w_ghost[1, cond_w_ghost < 2.5], decimal=10) np.testing.assert_array_almost_equal(maps[2, cond < 2.5], maps_w_ghost[2, cond_w_ghost < 2.5], decimal=10)
def test_scan_spole_hwp_mueller(self): ''' Perform a (low resolution) scan with a HWP mueller matrix specified and see if TOD make sense. ''' mlen = 10 * 60 rot_period = 120 mmax = 2 ra0=-10 dec0=-57.5 fwhm = 200 nside = 128 az_throw = 10 polang = 20. ces_opts = dict(ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=2.) scs = ScanStrategy(duration=mlen, sample_rate=10, location='spole') # Create a 1 x 1 square grid of Gaussian beams. scs.create_focal_plane(nrow=1, ncol=1, fov=4, lmax=self.lmax, fwhm=fwhm, polang=polang) beam = scs.beams[0][0] hwp_mueller = np.asarray([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1]]) beam.hwp_mueller = hwp_mueller scs.init_detpair(self.alm, beam, nside_spin=nside, max_spin=mmax) scs.partition_mission() chunk = scs.chunks[0] ces_opts.update(chunk) # Populate boresight. scs.constant_el_scan(**ces_opts) # Turn on HWP scs.set_hwp_mod(mode='continuous', freq=1., start_ang=0) scs.rotate_hwp(**chunk) tod, pix, nside_out, pa, hwp_ang = scs.scan(beam, return_tod=True, return_point=True, **chunk) # Construct TOD manually. polang = beam.polang maps_sm = np.asarray(hp.alm2map(self.alm, nside, verbose=False, fwhm=np.radians(beam.fwhm / 60.))) np.testing.assert_almost_equal(maps_sm[0], scs.spinmaps['main_beam']['s0a0']['maps'][0]) q = np.real(scs.spinmaps['main_beam']['s2a4']['maps'][mmax + 2]) u = np.imag(scs.spinmaps['main_beam']['s2a4']['maps'][mmax + 2]) np.testing.assert_almost_equal(maps_sm[1], q) np.testing.assert_almost_equal(maps_sm[2], u) tod_man = maps_sm[0][pix] tod_man += (maps_sm[1][pix] \ * np.cos(2 * np.radians(pa - polang - 2 * hwp_ang))) tod_man += (maps_sm[2][pix] \ * np.sin(2 * np.radians(pa - polang - 2 * hwp_ang))) np.testing.assert_almost_equal(tod, tod_man)
def test_scan_spole_bin(self): ''' Perform a (low resolution) scan, bin and compare to input. ''' mlen = 10 * 60 rot_period = 120 mmax = 2 ra0 = -10 dec0 = -57.5 fwhm = 200 nside = 256 az_throw = 10 scs = ScanStrategy(duration=mlen, sample_rate=10, location='spole') # Create a 1 x 2 square grid of Gaussian beams. scs.create_focal_plane(nrow=1, ncol=2, fov=4, lmax=self.lmax, fwhm=fwhm) # Allocate and assign parameters for mapmaking. scs.allocate_maps(nside=nside) # set instrument rotation. scs.set_instr_rot(period=rot_period, angles=[68, 113, 248, 293]) # Set elevation stepping. scs.set_el_steps(rot_period, steps=[0, 2, 4]) # Set HWP rotation. scs.set_hwp_mod(mode='continuous', freq=3.) # Generate timestreams, bin them and store as attributes. scs.scan_instrument_mpi(self.alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=2., nside_spin=nside, max_spin=mmax) # Solve for the maps. maps, cond = scs.solve_for_map(fill=np.nan) alm = hp.smoothalm(self.alm, fwhm=np.radians(fwhm / 60.), verbose=False) maps_raw = np.asarray(hp.alm2map(self.alm, nside, verbose=False)) cond[~np.isfinite(cond)] = 10 np.testing.assert_array_almost_equal(maps_raw[0, cond < 2.5], maps[0, cond < 2.5], decimal=10) np.testing.assert_array_almost_equal(maps_raw[1, cond < 2.5], maps[1, cond < 2.5], decimal=10) np.testing.assert_array_almost_equal(maps_raw[2, cond < 2.5], maps[2, cond < 2.5], decimal=10)
def test_scan_spole(self): ''' Perform a (low resolution) scan and see if TOD make sense. ''' mlen = 10 * 60 rot_period = 120 mmax = 2 ra0 = -10 dec0 = -57.5 fwhm = 200 nside = 256 az_throw = 10 polang = 20. ces_opts = dict(ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=2.) scs = ScanStrategy(duration=mlen, sample_rate=10, location='spole') # Create a 1 x 1 square grid of Gaussian beams. scs.create_focal_plane(nrow=1, ncol=1, fov=4, lmax=self.lmax, fwhm=fwhm, polang=polang) beam = scs.beams[0][0] scs.init_detpair(self.alm, beam, nside_spin=nside, max_spin=mmax) scs.partition_mission() chunk = scs.chunks[0] ces_opts.update(chunk) # Populate boresight. scs.constant_el_scan(**ces_opts) # Test without returning anything (default behaviour). scs.scan(beam, **chunk) tod = scs.scan(beam, return_tod=True, **chunk) self.assertEqual(tod.size, chunk['end'] - chunk['start']) pix, nside_out, pa, hwp_ang = scs.scan(beam, return_point=True, **chunk) self.assertEqual(pix.size, tod.size) self.assertEqual(nside, nside_out) self.assertEqual(pa.size, tod.size) self.assertEqual(hwp_ang, 0) # Turn on HWP scs.set_hwp_mod(mode='continuous', freq=1., start_ang=0) scs.rotate_hwp(**chunk) tod2, pix2, nside_out2, pa2, hwp_ang2 = scs.scan(beam, return_tod=True, return_point=True, **chunk) np.testing.assert_almost_equal(pix, pix2) np.testing.assert_almost_equal(pix, pix2) np.testing.assert_almost_equal(pa, pa2) self.assertTrue(np.any(np.not_equal(tod, tod2)), True) self.assertEqual(nside_out, nside_out2) self.assertEqual(hwp_ang2.size, tod.size) # Construct TOD manually. polang = beam.polang maps_sm = np.asarray( hp.alm2map(self.alm, nside, verbose=False, fwhm=np.radians(beam.fwhm / 60.))) np.testing.assert_almost_equal(maps_sm[0], scs.spinmaps['main_beam']['maps'][0][0]) q = np.real(scs.spinmaps['main_beam']['maps'][1][mmax + 2]) u = np.imag(scs.spinmaps['main_beam']['maps'][1][mmax + 2]) np.testing.assert_almost_equal(maps_sm[1], q) np.testing.assert_almost_equal(maps_sm[2], u) tod_man = maps_sm[0][pix] tod_man += (maps_sm[1][pix] \ * np.cos(-2 * np.radians(pa + polang + 2 * hwp_ang2))) tod_man += (maps_sm[2][pix] \ * np.sin(-2 * np.radians(pa + polang + 2 * hwp_ang2))) np.testing.assert_almost_equal(tod2, tod_man)
def single_detector(nsamp=1000, lmax=700, fwhm=30., ra0=-10, dec0=-57.5, az_throw=50, scan_speed=2.8, rot_period=4.5 * 60 * 60, mmax=5, nside_spin=512): ''' Generates a timeline for a set of individual detectors scanning the sky. The spatial response of these detectors is described by a 1) Gaussian, 2) an elliptical Gaussian and, 3) a beam map derived by physical optics. Arguments --------- nsamp : int (default : 1000) The length of the generated timestreams in number of samples ''' # Load up alm ell, cls = get_cls() np.random.seed(39) alm = hp.synalm(cls, lmax=lmax, new=True, verbose=True) # uK # create Beam properties and pickle (this is just to test load_focal_plane) import tempfile import shutil import pickle opj = os.path.join blm_dir = os.path.abspath( opj(os.path.dirname(__file__), '../tests/test_data/example_blms')) po_file = opj(blm_dir, 'blm_hp_X1T1R1C8A_800_800.npy') eg_file = opj(blm_dir, 'blm_hp_eg_X1T1R1C8A_800_800.npy') tmp_dir = tempfile.mkdtemp() beam_file = opj(tmp_dir, 'beam_opts.pkl') beam_opts = dict( az=0, el=0, polang=0., btype='Gaussian', name='X1T1R1C8', fwhm=32.2, lmax=800, mmax=800, amplitude=1., po_file=po_file, eg_file=eg_file, deconv_q=True, # blm are SH coeff from hp.alm2map normalize=True) with open(beam_file, 'wb') as handle: pickle.dump(beam_opts, handle, protocol=pickle.HIGHEST_PROTOCOL) # init scan strategy and instrument ss = ScanStrategy( nsamp / 10., # mission duration in sec. sample_rate=10, # 10 Hz sample rate location='spole') # South pole instrument ss.load_focal_plane(tmp_dir, no_pairs=True) # remove tmp dir and contents shutil.rmtree(tmp_dir) # init plot fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) # Generate timestreams with Gaussian beams and plot them ss.scan_instrument_mpi(alm, verbose=1, ra0=ra0, dec0=dec0, az_throw=az_throw, nside_spin=nside_spin, max_spin=mmax, binning=False) ax1.plot(ss.tod, label='sym. Gaussian', linewidth=0.7) gauss_tod = ss.tod.copy() # Generate timestreams with elliptical Gaussian beams and plot them ss.beams[0][0].btype = 'EG' ss.scan_instrument_mpi(alm, verbose=1, ra0=ra0, dec0=dec0, az_throw=az_throw, nside_spin=nside_spin, max_spin=mmax, binning=False) ax1.plot(ss.tod, label='ell. Gaussian', linewidth=0.7) eg_tod = ss.tod.copy() # Generate timestreams with Physical Optics beams and plot them ss.beams[0][0].btype = 'PO' ss.scan_instrument_mpi(alm, verbose=1, ra0=ra0, dec0=dec0, az_throw=az_throw, nside_spin=nside_spin, max_spin=mmax, binning=False) ax1.plot(ss.tod, label='PO', linewidth=0.7) po_tod = ss.tod.copy() ax2.plot(eg_tod - gauss_tod, label='EG - G', linewidth=0.7) ax2.plot(po_tod - gauss_tod, label='PO - G', linewidth=0.7) ax1.legend() ax2.legend() ax1.set_ylabel(r'Signal [$\mu K_{\mathrm{CMB}}$]') ax2.set_ylabel(r'Difference [$\mu K_{\mathrm{CMB}}$]') ax2.set_xlabel('Sample number') fig.savefig('../scratch/img/sdet_tod.png') plt.close()
def test_ghosts(lmax=700, mmax=5, fwhm=43, ra0=-10, dec0=-57.5, az_throw=50, scan_speed=2.8, rot_period=4.5 * 60 * 60, hwp_mode=None): ''' Similar test to `scan_bicep`, but includes reflected ghosts Simulates a 24h BICEP2-like scan strategy using a random LCDM realisation and a 3 x 3 grid of Gaussian beams pairs. Bins tods into maps and compares to smoothed input maps (no pair- differencing). MPI-enabled. Keyword arguments --------- lmax : int, bandlimit (default : 700) mmax : int, assumed azimuthal bandlimit beams (symmetric in this example so 2 would suffice) (default : 5) fwhm : float, The beam FWHM in arcmin (default : 40) ra0 : float, Ra coord of centre region (default : -10) dec0 : float, (default : -57.5) Ra coord of centre region az_throw : float, Scan width in azimuth (in degrees) (default : 50) scan_speed : float, Scan speed in deg/s (default : 1) rot_period : float, The instrument rotation period in sec (default : 600) hwp_mode : str, None HWP modulation mode, either "continuous", "stepped" or None. Use freq of 1 or 1/10800 Hz respectively (default : None) ''' mlen = 24 * 60 * 60 # hardcoded mission length # Create LCDM realization ell, cls = get_cls() np.random.seed(25) # make sure all MPI ranks use the same seed alm = hp.synalm(cls, lmax=lmax, new=True, verbose=True) # uK b2 = ScanStrategy( mlen, # mission duration in sec. sample_rate=12.01, # sample rate in Hz location='spole') # Instrument at south pole # Create a 3 x 3 square grid of Gaussian beams b2.create_focal_plane(nrow=3, ncol=3, fov=5, lmax=lmax, fwhm=fwhm) # Create reflected ghosts for every detector # We create two ghosts per detector. They overlap # but have different fwhm. First ghost is just a # scaled down version of the main beam, the second # has a much wider Gaussian shape. # After this initialization, the code takes # the ghosts into account without modifications b2.create_reflected_ghosts(b2.beams, amplitude=0.01, ghost_tag='ghost_1', dead=False) b2.create_reflected_ghosts(b2.beams, amplitude=0.01, fwhm=100, ghost_tag='ghost_2', dead=False) # calculate tods in two chunks b2.partition_mission(0.5 * b2.nsamp) # Allocate and assign parameters for mapmaking b2.allocate_maps(nside=256) # set instrument rotation b2.set_instr_rot(period=rot_period, angles=[68, 113, 248, 293]) # Set HWP rotation if hwp_mode == 'continuous': b2.set_hwp_mod(mode='continuous', freq=1.) elif hwp_mode == 'stepped': b2.set_hwp_mod(mode='stepped', freq=1 / (3 * 60 * 60.)) # Generate timestreams, bin them and store as attributes b2.scan_instrument_mpi(alm, verbose=1, ra0=ra0, dec0=dec0, az_throw=az_throw, nside_spin=256, max_spin=mmax) # Solve for the maps maps, cond = b2.solve_for_map(fill=np.nan) # Plotting if b2.mpi_rank == 0: print('plotting results') cart_opts = dict( rot=[ra0, dec0, 0], lonra=[-min(0.5 * az_throw, 90), min(0.5 * az_throw, 90)], latra=[-min(0.375 * az_throw, 45), min(0.375 * az_throw, 45)], unit=r'[$\mu K_{\mathrm{CMB}}$]') # plot rescanned maps plot_iqu(maps, '../scratch/img/', 'rescan_ghost', sym_limits=[250, 5, 5], plot_func=hp.cartview, **cart_opts) # plot smoothed input maps nside = hp.get_nside(maps[0]) hp.smoothalm(alm, fwhm=np.radians(fwhm / 60.), verbose=False) maps_raw = hp.alm2map(alm, nside, verbose=False) plot_iqu(maps_raw, '../scratch/img/', 'raw_ghost', sym_limits=[250, 5, 5], plot_func=hp.cartview, **cart_opts) # plot difference maps for arr in maps_raw: # replace stupid UNSEEN crap arr[arr == hp.UNSEEN] = np.nan diff = maps_raw - maps plot_iqu(diff, '../scratch/img/', 'diff_ghost', sym_limits=[1e+1, 1e-1, 1e-1], plot_func=hp.cartview, **cart_opts) # plot condition number map cart_opts.pop('unit', None) plot_map(cond, '../scratch/img/', 'cond_ghost', min=2, max=5, unit='condition number', plot_func=hp.cartview, **cart_opts) # plot input spectrum cls[3][cls[3] <= 0.] *= -1. dell = ell * (ell + 1) / 2. / np.pi plt.figure() for i, label in enumerate(['TT', 'EE', 'BB', 'TE']): plt.semilogy(ell, dell * cls[i], label=label) plt.legend() plt.ylabel(r'$D_{\ell}$ [$\mu K^2_{\mathrm{CMB}}$]') plt.xlabel(r'Multipole [$\ell$]') plt.savefig('../scratch/img/cls_ghost.png') plt.close()
def scan_atacama(lmax=700, mmax=5, fwhm=40, mlen=48 * 60 * 60, nrow=3, ncol=3, fov=5.0, ra0=[-10, 170], dec0=[-57.5, 0], el_min=45., cut_el_min=False, az_throw=50, scan_speed=1, rot_period=0, hwp_mode='continuous'): ''' Simulates 48h of an atacama-based telescope with a 3 x 3 grid of Gaussian beams pairs. Prefers to scan the bicep patch but will try to scan the ABS_B patch if the first is not visible. Keyword arguments --------- lmax : int bandlimit (default : 700) mmax : int assumed azimuthal bandlimit beams (symmetric in this example so 2 would suffice) (default : 5) fwhm : float The beam FWHM in arcmin (default : 40) mlen : int The mission length [seconds] (default : 48 * 60 * 60) nrow : int Number of detectors along row direction (default : 3) ncol : int Number of detectors along column direction (default : 3) fov : float The field of view in degrees (default : 5.0) ra0 : float, array-like Ra coord of centre region (default : [-10., 85.]) dec0 : float, array-like Ra coord of centre region (default : [-57.5, 0.]) el_min : float Minimum elevation range [deg] (default : 45) cut_el_min: bool If True, excludes timelines where el would be less than el_min az_throw : float Scan width in azimuth (in degrees) (default : 10) scan_speed : float Scan speed in deg/s (default : 1) rot_period : float The instrument rotation period in sec (default : 600) hwp_mode : str, None HWP modulation mode, either "continuous", "stepped" or None. Use freq of 1 or 1/10800 Hz respectively (default : continuous) ''' # hardcoded mission length # Create LCDM realization ell, cls = get_cls() np.random.seed(25) # make sure all MPI ranks use the same seed alm = hp.synalm(cls, lmax=lmax, new=True, verbose=True) # uK ac = ScanStrategy( mlen, # mission duration in sec. sample_rate=12.01, # sample rate in Hz location='atacama') # Instrument at south pole # Create a 3 x 3 square grid of Gaussian beams ac.create_focal_plane(nrow=nrow, ncol=ncol, fov=fov, lmax=lmax, fwhm=fwhm) # calculate tods in two chunks ac.partition_mission(0.5 * ac.mlen * ac.fsamp) # Allocate and assign parameters for mapmaking ac.allocate_maps(nside=256) # set instrument rotation ac.set_instr_rot(period=rot_period) # Set HWP rotation if hwp_mode == 'continuous': ac.set_hwp_mod(mode='continuous', freq=1.) elif hwp_mode == 'stepped': ac.set_hwp_mod(mode='stepped', freq=1 / (3 * 60 * 60.)) # Generate timestreams, bin them and store as attributes ac.scan_instrument_mpi(alm, verbose=2, ra0=ra0, dec0=dec0, az_throw=az_throw, nside_spin=256, el_min=el_min, cut_el_min=cut_el_min, create_memmap=True) # Solve for the maps maps, cond = ac.solve_for_map(fill=np.nan) # Plotting if ac.mpi_rank == 0: print('plotting results') img_out_path = '../scratch/img/' moll_opts = dict(unit=r'[$\mu K_{\mathrm{CMB}}$]') # plot rescanned maps plot_iqu(maps, img_out_path, 'rescan_atacama', sym_limits=[250, 5, 5], plot_func=hp.mollview, **moll_opts) # plot smoothed input maps nside = hp.get_nside(maps[0]) hp.smoothalm(alm, fwhm=np.radians(fwhm / 60.), verbose=False) maps_raw = hp.alm2map(alm, nside, verbose=False) plot_iqu(maps_raw, img_out_path, 'raw_atacama', sym_limits=[250, 5, 5], plot_func=hp.mollview, **moll_opts) # plot difference maps for arr in maps_raw: # replace stupid UNSEEN crap arr[arr == hp.UNSEEN] = np.nan diff = maps_raw - maps plot_iqu(diff, img_out_path, 'diff_atacama', sym_limits=[1e-6, 1e-6, 1e-6], plot_func=hp.mollview, **moll_opts) # plot condition number map moll_opts.pop('unit', None) plot_map(cond, img_out_path, 'cond_atacama', min=2, max=5, unit='condition number', plot_func=hp.mollview, **moll_opts) # plot input spectrum cls[3][cls[3] <= 0.] *= -1. dell = ell * (ell + 1) / 2. / np.pi plt.figure() for i, label in enumerate(['TT', 'EE', 'BB', 'TE']): plt.semilogy(ell, dell * cls[i], label=label) plt.legend() plt.ylabel(r'$D_{\ell}$ [$\mu K^2_{\mathrm{CMB}}$]') plt.xlabel(r'Multipole [$\ell$]') plt.savefig('../scratch/img/cls_atacama.png') plt.close() print("Results written to {}".format(os.path.abspath(img_out_path)))
def test_satellite_scan(lmax=700, mmax=2, fwhm=43, ra0=-10, dec0=-57.5, az_throw=50, scan_speed=2.8, hwp_mode=None, alpha=45., beta=45., alpha_period=5400., beta_period=600., delta_az=0., delta_el=0., delta_psi=0., jitter_amp=1.0): ''' Simulates a satellite scan strategy using a random LCDM realisation and a 3 x 3 grid of Gaussian beams pairs. Bins tods into maps and compares to smoothed input maps (no pair- differencing). MPI-enabled. Keyword arguments --------- lmax : int, bandlimit (default : 700) mmax : int, assumed azimuthal bandlimit beams (symmetric in this example so 2 would suffice) (default : 2) fwhm : float, The beam FWHM in arcmin (default : 40) ra0 : float, Ra coord of centre region (default : -10) dec0 : float, (default : -57.5) Ra coord of centre region az_throw : float, Scan width in azimuth (in degrees) (default : 50) scan_speed : float, Scan speed in deg/s (default : 1) hwp_mode : str, None HWP modulation mode, either "continuous", "stepped" or None. Use freq of 1 or 1/10800 Hz respectively (default : None) ''' print('Simulating a satellite...') mlen = 3 * 24 * 60 * 60 # hardcoded mission length # Create LCDM realization ell, cls = get_cls() np.random.seed(25) # make sure all MPI ranks use the same seed alm = hp.synalm(cls, lmax=lmax, new=True, verbose=True) # uK sat = ScanStrategy( mlen, # mission duration in sec. external_pointing=True, # Telling code to use non-standard scanning sample_rate=12.01, # sample rate in Hz location='space') # Instrument at south pole # Create a 3 x 3 square grid of Gaussian beams sat.create_focal_plane(nrow=7, ncol=7, fov=15, lmax=lmax, fwhm=fwhm) # calculate tods in two chunks sat.partition_mission(0.5 * sat.nsamp) # Allocate and assign parameters for mapmaking sat.allocate_maps(nside=256) scan_opts = dict(q_bore_func=sat.satellite_scan, ctime_func=sat.satellite_ctime, q_bore_kwargs=dict(), ctime_kwargs=dict()) # Generate timestreams, bin them and store as attributes sat.scan_instrument_mpi(alm, verbose=1, ra0=ra0, dec0=dec0, az_throw=az_throw, nside_spin=256, max_spin=mmax, **scan_opts) # Solve for the maps maps, cond, proj = sat.solve_for_map(fill=np.nan, return_proj=True) # Plotting if sat.mpi_rank == 0: print('plotting results') cart_opts = dict(unit=r'[$\mu K_{\mathrm{CMB}}$]') # plot rescanned maps plot_iqu(maps, '../scratch/img/', 'rescan_satellite', sym_limits=[250, 5, 5], plot_func=hp.mollview, **cart_opts) # plot smoothed input maps nside = hp.get_nside(maps[0]) hp.smoothalm(alm, fwhm=np.radians(fwhm / 60.), verbose=False) maps_raw = hp.alm2map(alm, nside, verbose=False) plot_iqu(maps_raw, '../scratch/img/', 'raw_satellite', sym_limits=[250, 5, 5], plot_func=hp.mollview, **cart_opts) # plot difference maps for arr in maps_raw: # replace stupid UNSEEN crap arr[arr == hp.UNSEEN] = np.nan diff = maps_raw - maps plot_iqu(diff, '../scratch/img/', 'diff_satellite', sym_limits=[1e-6, 1e-6, 1e-6], plot_func=hp.mollview, **cart_opts) # plot condition number map cart_opts.pop('unit', None) plot_map(cond, '../scratch/img/', 'cond_satellite', min=2, max=5, unit='condition number', plot_func=hp.mollview, **cart_opts) plot_map(proj[0], '../scratch/img/', 'hits_satellite', unit='Hits', plot_func=hp.mollview, **cart_opts)
def idea_jon(): nside_spin = 512 ra0 = 0 dec0 = -90 az_throw = 10 max_spin = 5 fwhm = 32.2 scan_opts = dict(verbose=1, ra0=ra0, dec0=dec0, az_throw=az_throw, nside_spin=nside_spin, max_spin=max_spin, binning=True) lmax = 800 alm = tools.gauss_blm(1e-5, lmax, pol=False) ell = np.arange(lmax + 1) fl = np.sqrt((2 * ell + 1) / 4. / np.pi) hp.almxfl(alm, fl, mmax=None, inplace=True) fm = (-1)**(hp.Alm.getlm(lmax)[1]) alm *= fm alm = tools.get_copol_blm(alm) # create Beam properties and pickle (this is just to test load_focal_plane) import tempfile import shutil import pickle opj = os.path.join blm_dir = os.path.abspath( opj(os.path.dirname(__file__), '../tests/test_data/example_blms')) po_file = opj(blm_dir, 'blm_hp_X1T1R1C8A_800_800.npy') eg_file = opj(blm_dir, 'blm_hp_eg_X1T1R1C8A_800_800.npy') tmp_dir = tempfile.mkdtemp() beam_file = opj(tmp_dir, 'beam_opts.pkl') beam_opts = dict(az=0, el=0, polang=0., btype='Gaussian', name='X1T1R1C8', fwhm=fwhm, lmax=800, mmax=800, amplitude=1., po_file=po_file, eg_file=eg_file) with open(beam_file, 'wb') as handle: pickle.dump(beam_opts, handle, protocol=pickle.HIGHEST_PROTOCOL) # init scan strategy and instrument ss = ScanStrategy( 1., # mission duration in sec. sample_rate=10000, location='spole') ss.allocate_maps(nside=1024) ss.load_focal_plane(tmp_dir, no_pairs=True) # remove tmp dir and contents shutil.rmtree(tmp_dir) ss.set_el_steps(0.01, steps=np.linspace(-10, 10, 100)) # Generate maps with Gaussian beams ss.scan_instrument_mpi(alm, **scan_opts) ss.reset_el_steps() # Solve for the maps maps_g, cond_g = ss.solve_for_map(fill=np.nan) # Generate maps with elliptical Gaussian beams ss.allocate_maps(nside=1024) ss.beams[0][0].btype = 'EG' ss.scan_instrument_mpi(alm, **scan_opts) ss.reset_el_steps() # Solve for the maps maps_eg, cond_eg = ss.solve_for_map(fill=np.nan) # Generate map with Physical Optics beams and plot them ss.allocate_maps(nside=1024) ss.beams[0][0].btype = 'PO' ss.scan_instrument_mpi(alm, **scan_opts) ss.reset_el_steps() # Solve for the maps maps_po, cond_po = ss.solve_for_map(fill=np.nan) # Plotting print('plotting results') cart_opts = dict( #rot=[ra0, dec0, 0], lonra=[-min(0.5 * az_throw, 10), min(0.5 * az_throw, 10)], latra=[-min(0.375 * az_throw, 10), min(0.375 * az_throw, 10)], unit=r'[$\mu K_{\mathrm{CMB}}$]') # plot smoothed input maps nside = hp.get_nside(maps_g[0]) hp.smoothalm(alm, fwhm=np.radians(fwhm / 60.), verbose=False) maps_raw = hp.alm2map(alm, nside, verbose=False) plot_iqu(maps_raw, '../scratch/img/', 'raw_delta', sym_limits=[1, 1, 1], plot_func=hp.cartview, **cart_opts)
def scan(lmax=500, nside=512, mmax=2): '''Time scanning single detector.''' os.environ["OMP_NUM_THREADS"] = "10" import numpy as np import healpy as hp from beamconv import ScanStrategy from beamconv import Beam ndays_range = np.logspace(np.log10(0.001), np.log10(50), 15) lmax = lmax nside = nside freq = 100. timings = np.ones((ndays_range.size), dtype=float) timings_cpu = np.ones((ndays_range.size), dtype=float) S = ScanStrategy(duration=24 * 3600, sample_rate=freq) beam_opts = dict(az=1, el=1, polang=10., fwhm=40, btype='Gaussian', lmax=lmax, symmetric=mmax == 0) beam = Beam(**beam_opts) S.add_to_focal_plane(beam) alm = np.zeros((3, hp.Alm.getsize(lmax=lmax)), dtype=np.complex128) print('init detpair...') S.init_detpair(alm, beam, beam_b=None, nside_spin=nside, max_spin=mmax, verbose=True) print('...done') spinmaps = copy.deepcopy(S.spinmaps) # Calculate q_bore for 0.1 day of scanning and reuse this. const_el_opts = dict(az_throw=50., scan_speed=10., dec0=-70.) S.partition_mission() print('cons_el_scan...') const_el_opts.update(dict(start=0, end=int(24 * 3600 * 100 * 0.1))) S.constant_el_scan(**const_el_opts) print('...done') q_bore_day = S.q_bore ctime_day = S.ctime for nidx, ndays in enumerate(ndays_range): duration = ndays * 24 * 3600 nsamp = duration * freq S = ScanStrategy(duration=duration, sample_rate=freq, external_pointing=True) S.add_to_focal_plane(beam) S.partition_mission() q_bore = np.repeat(q_bore_day, np.ceil(10 * ndays), axis=0) ctime = np.repeat(q_bore_day, np.ceil(10 * ndays)) def q_bore_func(start=None, end=None, q_bore=None): return q_bore[int(start):int(end), :] def ctime_func(start=None, end=None, ctime=None): return ctime[int(start):int(end)] S.spinmaps = spinmaps t0 = time.time() t0c = time.clock() S.scan_instrument_mpi(alm, binning=False, reuse_spinmaps=True, interp=False, q_bore_func=q_bore_func, ctime_func=ctime_func, q_bore_kwargs={'q_bore': q_bore}, ctime_kwargs={'ctime': ctime}, start=0, end=int(nsamp), cidx=0) t1 = time.time() t1c = time.clock() print t1 - t0 print t1c - t0c timings[nidx] = t1 - t0 timings_cpu[nidx] = t1c - t0c np.save( './scan_timing_lmax{}_nside{}_mmax{}.npy'.format(lmax, nside, mmax), timings) np.save( './scan_timing_cpu_lmax{}_nside{}_mmax{}.npy'.format( lmax, nside, mmax), timings_cpu) np.save('./scan_ndays_lmax{}_nside{}_mmax{}.npy'.format(lmax, nside, mmax), ndays_range)
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 test_preview_pointing(self): # With preview_pointing set, expect correct proj matrix, # but vec vector should be zero. mlen = 6 * 60 rot_period = 30 step_period = rot_period * 2 mmax = 2 ra0=-10 dec0=-57.5 fwhm = 10 nside_out = 32 az_throw = 10 scan_speed = 2 # deg / s. scs = ScanStrategy(duration=mlen, sample_rate=10, location='spole') # Create a 1 x 2 square grid of Gaussian beams. scs.create_focal_plane(nrow=1, ncol=2, fov=2, lmax=self.lmax, fwhm=fwhm) # Allocate and assign parameters for mapmaking. scs.allocate_maps(nside=nside_out) # set instrument rotation. scs.set_instr_rot(period=rot_period, angles=[68, 113, 248, 293]) # Set elevation stepping. scs.set_el_steps(step_period, steps=[0, 1, 2]) # Set HWP rotation. scs.set_hwp_mod(mode='continuous', freq=3.) # First run with preview_pointing set alm = None preview_pointing = True # Generate timestreams, bin them and store as attributes. scs.scan_instrument_mpi(alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=scan_speed, nside_spin=nside_out, max_spin=mmax, preview_pointing=preview_pointing) # Vec should be zero np.testing.assert_array_equal(scs.vec, np.zeros((3, 12 * nside_out ** 2))) # Save for comparison vec_prev = scs.vec proj_prev = scs.proj # Now run again in default way. # Create new dest arrays. scs.allocate_maps(nside=nside_out) scs.reset_instr_rot() scs.reset_hwp_mod() scs.reset_el_steps() alm = self.alm preview_pointing = False # Generate timestreams, bin them and store as attributes. scs.scan_instrument_mpi(alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=scan_speed, nside_spin=nside_out, max_spin=mmax, preview_pointing=preview_pointing) # Vec should not be zero now. np.testing.assert_equal(np.any(scs.vec), True) # Proj should be identical. np.testing.assert_array_almost_equal(scs.proj, proj_prev, decimal=9) # Run one more time with a ghost. Ghost should not change proj. # Create new dest arrays. scs.allocate_maps(nside=nside_out) alm = self.alm preview_pointing = False scs.reset_instr_rot() scs.reset_hwp_mod() scs.reset_el_steps() ghost_opts = dict(az=10, el=10, polang=28, fwhm=fwhm, lmax=self.lmax, symmetric=True, amplitude=1) scs.beams[0][0].create_ghost(**ghost_opts) # Generate timestreams, bin them and store as attributes. scs.scan_instrument_mpi(alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=scan_speed, nside_spin=nside_out, max_spin=mmax, preview_pointing=preview_pointing) # Vec should not be zero now. np.testing.assert_equal(np.any(scs.vec), True) # Proj should be identical. np.testing.assert_array_almost_equal(scs.proj, proj_prev, decimal=9)
def Scan_maps(nside, alms, lmax, Freq, Own_Stack=True, ideal_hwp=False): ''' Scanning simulation, store the output map, the blm, the tod, and the condition number in the output directory. --------- nside: int the nside of the map alms : array-like array of alm arrays that share lmax and mmax. For each frequency we have three healpy alm array lmax: int The bandlmit. Freq: array-like frequency at which one want to compute the sky Keyword arguments ----------------- ideal_hwp : bool If True: it is considered an ideal HWP, if Flase: it is considered a real HWP. (default : False) ''' po_file = opj(blm_dir, 'pix0000_90_hwpproj_v5_f1p6_6p0mm_mfreq_lineard_hdpe.npy') #eg_file = opj(blm_dir, 'blm_hp_eg_X1T1R1C8A_800_800.npy') beam_file = 'pix0000_90_hwpproj_v5_f1p6_6p0mm_mfreq_lineard_hdpe.pkl' hwp = Beam().hwp() if Own_Stack: thicknesses = np.array([0.427, 4.930, 0.427]) indices = np.array([[1., 1.], [1.02, 1.02], [1., 1.]]) losses = np.array([[0., 0.], [1e-4, 1e-4], [0., 0.]]) angles = np.array([0., 0., 0.]) hwp.stack_builder(thicknesses=thicknesses, indices=indices, losses=losses, angles=angles) else: hwp.choose_HWP_model('SPIDER_95') beam_opts = dict( az=0, el=0, polang=0., btype='PO', fwhm=32.2, lmax=lmax, mmax=4, amplitude=1., po_file=po_file, #eg_file=eg_file, deconv_q=True, # blm are SH coeff from hp.alm2map normalize=True, hwp=hwp) with open(beam_file, 'wb') as handle: pickle.dump(beam_opts, handle, protocol=pickle.HIGHEST_PROTOCOL) beam = Beam(**beam_opts) ### SCAN OPTIONS # duration = nsamp/sample_rate nsamp = 864000 fsamp = 10 mlen = nsamp / fsamp lmax = lmax mmax = 4 ra0 = -10 dec0 = -57.5 az_throw = 50 scan_speed = 2.8 rot_period = 4.5 * 60 * 60 nside_spin = nside # scan the given sky and return the results as maps; filefolder = opj(dir_out, 'Output_maps/') for freq, alm in zip(Freq, alms): ss = ScanStrategy(mlen, sample_rate=fsamp, location='atacama') # Add the detectors to the focal plane; 9 detector pairs used in this case ss.create_focal_plane(nrow=4, ncol=4, fov=3, **beam_opts) # Use a half-wave plate. Other options one can use is to add an elevation # pattern or a periodic instrument rotation as, for example: # ss.set_instr_rot(period=rot_period, angles=[68, 113, 248, 293]) # and ss.set_el_steps(step_period, steps=[-4, -3, -2, -1, 0, 1, 2, 3, 4, 4]) ss.set_hwp_mod(mode='continuous', freq=1.) # scan the given sky and return the results as maps; ss.allocate_maps(nside=nside) if ideal_hwp: ss.scan_instrument_mpi(alm, verbose=1, ra0=ra0, dec0=dec0, az_throw=az_throw, nside_spin=nside_spin, max_spin=mmax, binning=True, hwp_status='ideal') else: ss.scan_instrument_mpi(alm, verbose=1, ra0=ra0, dec0=dec0, az_throw=az_throw, nside_spin=nside_spin, max_spin=mmax, binning=True) tod = ss.tod maps, cond = ss.solve_for_map(fill=np.nan) blm = np.asarray(beam.blm).copy() # We need to divide out sqrt(4pi / (2 ell + 1)) to get # correctly normlized spherical harmonic coeffients. ell = np.arange(hp.Alm.getlmax(blm[0].size)) q_ell = np.sqrt(4. * np.pi / (2 * ell + 1)) blm[0] = hp.almxfl(blm[0], 1 / q_ell) blm[1] = hp.almxfl(blm[1], 1 / q_ell) blm[2] = hp.almxfl(blm[2], 1 / q_ell) # print(np.shape(alm)) # print(np.shape(alms)) # print(np.shape(maps)) # print(np.shape(cond)) # maps = [maps[0], maps[1], maps[2]] hp.write_map(opj(dir_out, 'Output_maps/Bconv_' + str(freq) + 'GHz.fits'), maps, overwrite=True) hp.write_map(opj(dir_out, 'Output_maps/Cond_Numb_' + str(freq) + 'GHz.fits'), cond, overwrite=True) np.save(os.path.join(dir_out, 'blms/blm_' + str(freq) + 'GHz.npy'), blm) np.save(os.path.join(dir_out, 'tods/tod_' + str(freq) + 'GHz.npy'), tod)
def test_cross_talk(self): '''Test if the cross-talk is performing as it should.''' mlen = 10 * 60 rot_period = 120 mmax = 2 ra0 = -10 dec0 = -57.5 fwhm = 200 nside = 256 az_throw = 10 scs = ScanStrategy(duration=mlen, sample_rate=10, location='spole') # Single pair. scs.create_focal_plane(nrow=1, ncol=1, fov=0, lmax=self.lmax, fwhm=fwhm) # Allocate and assign parameters for mapmaking. scs.allocate_maps(nside=nside) # set instrument rotation. scs.set_instr_rot(period=rot_period, angles=[12, 14, 248, 293]) # Set elevation stepping. scs.set_el_steps(rot_period, steps=[0, 2, 4, 8, 10]) # Set HWP rotation. scs.set_hwp_mod(mode='stepped', freq=3.) beam_a, beam_b = scs.beams[0] scs.init_detpair(self.alm, beam_a, beam_b=beam_b, nside_spin=nside, verbose=False) # Generate timestreams, bin them and store as attributes. scs.scan_instrument_mpi(self.alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=2., max_spin=mmax, reuse_spinmaps=True, save_tod=True, binning=False, ctalk=0.0) tod_a = scs.data(scs.chunks[0], beam=beam_a, data_type='tod').copy() tod_b = scs.data(scs.chunks[0], beam=beam_b, data_type='tod').copy() # Redo with cross-talk ctalk = 0.5 scs.reset_instr_rot() scs.reset_hwp_mod() scs.reset_el_steps() scs.scan_instrument_mpi(self.alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=2., max_spin=mmax, reuse_spinmaps=True, save_tod=True, binning=False, ctalk=ctalk) tod_ac = scs.data(scs.chunks[0], beam=beam_a, data_type='tod') tod_bc = scs.data(scs.chunks[0], beam=beam_b, data_type='tod') np.testing.assert_array_almost_equal(tod_ac, tod_a + ctalk * tod_b) np.testing.assert_array_almost_equal(tod_bc, tod_b + ctalk * tod_a) # Redo with less cross-talk ctalk = 0.000001 scs.reset_instr_rot() scs.reset_hwp_mod() scs.reset_el_steps() scs.scan_instrument_mpi(self.alm, verbose=0, ra0=ra0, dec0=dec0, az_throw=az_throw, scan_speed=2., max_spin=mmax, reuse_spinmaps=True, save_tod=True, binning=False, ctalk=ctalk) tod_acs = scs.data(scs.chunks[0], beam=beam_a, data_type='tod') tod_bcs = scs.data(scs.chunks[0], beam=beam_b, data_type='tod') np.testing.assert_array_almost_equal(tod_acs, tod_a + ctalk * tod_b) np.testing.assert_array_almost_equal(tod_bcs, tod_b + ctalk * tod_a) np.testing.assert_raises(AssertionError, np.testing.assert_array_equal, tod_ac, tod_acs) np.testing.assert_raises(AssertionError, np.testing.assert_array_equal, tod_bc, tod_bcs)
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 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()
def t_lmax(): '''Time init_detpair as function of lmax, mmax.''' os.environ["OMP_NUM_THREADS"] = "1" import numpy as np import healpy as hp from beamconv import ScanStrategy from beamconv import Beam scan_opts = dict(duration=3600, sample_rate=100) mmax_range = np.array([0, 2, 5, 8], dtype=int) lmax_range = np.logspace(np.log10(500), np.log10(3000), 8, dtype=int) nsides = np.ones_like(lmax_range) * np.nan nside_range = 2**np.arange(15) timings = np.ones((mmax_range.size, lmax_range.size)) * np.nan timings_cpu = np.ones((mmax_range.size, lmax_range.size)) * np.nan alm = np.zeros((3, hp.Alm.getsize(lmax=4000)), dtype=np.complex128) S = ScanStrategy(**scan_opts) for lidx, lmax in enumerate(lmax_range): nside = nside_range[np.digitize(0.5 * lmax, nside_range)] nsides[lidx] = nside for midx, mmax in enumerate(mmax_range): beam_opts = dict(az=0, el=0, polang=0, fwhm=40, btype='Gaussian', lmax=lmax, symmetric=mmax == 0) beam = Beam(**beam_opts) beam.blm t0 = time.time() t0c = time.clock() S.init_detpair(alm, beam, beam_b=None, nside_spin=nside, max_spin=mmax, verbose=False) t1 = time.time() t1c = time.clock() print('{}, {}, {}: {}'.format(lmax, mmax, nside, t1 - t0)) print('{}, {}, {}: {}'.format(lmax, mmax, nside, t1c - t0c)) timings[midx, lidx] = t1 - t0 timings_cpu[midx, lidx] = t1c - t0c np.save('./timings.npy', timings) np.save('./timings_cpu.npy', timings_cpu) np.save('./lmax_range.npy', lmax_range) np.save('./mmax_range.npy', mmax_range) np.save('./nsides.npy', nsides)