def test_cmb_map_bandpass(): nside = 32 # pretend for testing that the Dust is CMB model = pysm3.CMBMap(map_IQU="pysm_2/lensed_cmb.fits", nside=nside) freq = 100 * u.GHz expected_map = pysm3.read_map( "pysm_2/lensed_cmb.fits", field=0, nside=nside, unit=u.uK_CMB ).to(u.uK_RJ, equivalencies=u.cmb_equivalencies(freq)) print( "expected_scaling", (1 * u.K_CMB).to_value(u.K_RJ, equivalencies=u.cmb_equivalencies(freq)), ) freqs = np.array([98, 99, 100, 101, 102]) * u.GHz weights = np.ones(len(freqs)) # just checking that the result is reasonably close # to the delta frequency at the center frequency assert_quantity_allclose( expected_map, model.get_emission(freqs, weights)[0], rtol=1e-3 )
def test_precomputed_alms(setup): alms, filename = setup nside = 64 # we assume the original `alms` are in `K_CMB` ref_freq = 40 * u.GHz test_map_K_CMB = hp.alm2map(alms, nside=nside) << u.K_CMB alms_K_RJ = alms.to(u.K_RJ, equivalencies=u.cmb_equivalencies(ref_freq)) filename_K_RJ = filename.replace(".fits", "_RJ.fits") hp.write_alm(filename_K_RJ, alms_K_RJ) precomputed_alms = PrecomputedAlms( filename=filename_K_RJ, nside=nside, input_units="K_RJ", input_reference_frequency=ref_freq, ) m = precomputed_alms.get_emission(23 * u.GHz) assert_quantity_allclose( m, test_map_K_CMB.to(u.K_RJ, equivalencies=u.cmb_equivalencies(23 * u.GHz)) ) freqs = np.array([1, 10, 100]) * u.GHz for freq in freqs: np.testing.assert_allclose( precomputed_alms.get_emission(freq), test_map_K_CMB.to(u.K_RJ, equivalencies=u.cmb_equivalencies(freq)), )
def test_from_cl(tmpdir): nside = 256 lmax = 512 folder = tmpdir.mkdir("cls") filename = os.path.join(str(folder), "cls.fits") input_cl = np.zeros((6, lmax + 1), dtype=np.double) # using healpy old ordering TT, TE, TB, EE, EB, BB # using healpy new ordering TT, EE, BB, TE, TB, EB input_cl[3] = 1e5 * stats.norm.pdf(np.arange(lmax + 1), 250, 30) # EE hp.write_cl(filename, input_cl, overwrite=True) precomputed_alms = PrecomputedAlms( filename=filename, nside=nside, input_units="K_CMB", from_cl=True, from_cl_seed=100, ) freq = 100 * u.GHz m = precomputed_alms.get_emission(freq) m = m.to(u.K_CMB, equivalencies=u.cmb_equivalencies(freq)) cl = hp.anafast(m, lmax=lmax) # anafast returns results in new ordering # TT, EE, BB, TE, EB, TB np.testing.assert_allclose(input_cl[3][200:300], cl[1][200:300], rtol=0.2) np.testing.assert_allclose(0, cl[0], rtol=1e-3) np.testing.assert_allclose(0, cl[2:], rtol=1e-3, atol=1e-4)
def test_cmb_tensor(tmp_path, monkeypatch, tensor_to_scalar): monkeypatch.setattr(utils, "PREDEFINED_DATA_FOLDERS", {"C": [str(tmp_path)]}) nside = 256 lmax = 512 path = tmp_path / "websky" / "0.3" path.mkdir(parents=True) input_cl = np.zeros((6, lmax + 1), dtype=np.double) input_cl[1] = 1e5 * stats.norm.pdf(np.arange(lmax + 1), 250, 30) # EE filename = path / "tensor_cl_r1_nt0.fits" hp.write_cl(filename, input_cl, overwrite=True) cmb_tensor = WebSkyCMBTensor("0.3", nside=nside, tensor_to_scalar=tensor_to_scalar) freq = 100 * u.GHz cmb_tensor_map = cmb_tensor.get_emission(freq) cmb_tensor_map = cmb_tensor_map.to( u.uK_CMB, equivalencies=u.cmb_equivalencies(freq) ) cl = hp.anafast(cmb_tensor_map, use_pixel_weights=True, lmax=lmax) # anafast returns results in new ordering # TT, EE, BB, TE, EB, TB np.testing.assert_allclose( input_cl[5][200:300] * tensor_to_scalar, cl[2][200:300], rtol=0.2 ) np.testing.assert_allclose(0, cl[:2], rtol=1e-3) np.testing.assert_allclose(0, cl[3:], rtol=1e-3, atol=1e-4)
def _load_inverse_variance_map(self, tube, output_units="uK_CMB", band=None): """ Internal function to return a preloaded inverse var map or load one from a from file. By default this just returns None so an inv_var map is computed from a white noise level and a hits map """ survey = self.get_survey(tube) noise_indices = self.get_noise_indices(tube, band) # If the survey has a set of preloaded invariance maps use them if hasattr(survey, "get_ivar_maps") and survey.get_ivar_maps() is not None: ret = np.array(survey.get_ivar_maps())[noise_indices] elif (hasattr(survey, "get_ivar_map_filenames") and survey.get_ivar_map_filenames() is not None): ivar_map_filenames = survey.get_ivar_map_filenames() if ivar_map_filenames is None: return None ivar_map_filenames = [ivar_map_filenames[i] for i in noise_indices] ivar_maps = [] for ivar_map_filename in ivar_map_filenames: ivar_maps.append(self._load_map(ivar_map_filename)) ret = np.array(ivar_maps) else: return None for i in range(self.channel_per_tube): freq = self.tubes[tube][i].center_frequency unit_conv = (1 * u.uK_CMB).to_value( u.Unit(output_units), equivalencies=u.cmb_equivalencies(freq)) ret[i] /= unit_conv**2.0 # divide by square since the default is 1/uK^2 return ret
def test_conversion(self): """ Here we test that the numerical value of the conversion is correct. The mathematical form is ..math:: I_\nu = \frac{2 \nu^2 k T_{\rm RJ}}{c^2} \\ I_\nu = T_{\rm CMB} B^\prime_\nu(T_{\rm CMB, 0}) so, eliminating the flux in this equation: ..math:: T_{\rm RJ} / T_{\rm CMB} = \frac{c^2}{2 \nu^2 k_B}B^\prime_\nu(T_{\rm CMB, 0}) Here we calculate the RHS of this equation and compare it to the ratio of T_RJ and the result of its transformation to T_CMB. """ equiv = {"equivalencies": units.cmb_equivalencies(self.freqs)} rj_from_cmb = self.T_CMB.to(units.K_RJ, **equiv) cmb_from_rj = self.T_RJ.to(units.K_CMB, **equiv) # check that the reverse transformation gives overall transformation of unity. reverse1 = rj_from_cmb.to(units.K_CMB, **equiv) reverse2 = cmb_from_rj.to(units.K_RJ, **equiv) np.testing.assert_almost_equal(1.0, self.T_CMB / reverse1, decimal=6) np.testing.assert_almost_equal(1.0, self.T_RJ / reverse2, decimal=6)
def get_emission( self, freqs: u.GHz, fwhm: [u.arcmin, None] = None, weights=None, output_units=u.uK_RJ, ): """Return map in uK_RJ at given frequency or array of frequencies Parameters ---------- freqs : list or ndarray Frequency or frequencies in GHz at which compute the signal fwhm : float (optional) Smooth the input alms before computing the signal, this can only be used if the class was initialized with `precompute_output_map` to False. output_units : str Output units, as defined in `pysm.convert_units`, by default this is "uK_RJ" as expected by PySM. Returns ------- output_maps : ndarray Output maps array with the shape (num_freqs, 1 or 3 (I or IQU), npix) """ freqs = pysm.utils.check_freq_input(freqs) weights = pysm.utils.normalize_weights(freqs, weights) try: output_map = self.output_map except AttributeError: if fwhm is None: alm = self.alm else: alm = hp.smoothalm(self.alm, fwhm=fwhm.to_value(u.radian), pol=True, inplace=False) output_map = self.compute_output_map(alm) output_units = u.Unit(output_units) assert output_units in [u.uK_RJ, u.uK_CMB] if output_units == u.uK_RJ: convert_to_uK_RJ = (np.ones(len(freqs), dtype=np.double) * u.uK_CMB).to_value( u.uK_RJ, equivalencies=u.cmb_equivalencies(freqs * u.GHz)) if len(freqs) == 1: scaling_factor = convert_to_uK_RJ[0] else: scaling_factor = np.trapz(convert_to_uK_RJ * weights, x=freqs) return output_map.value * scaling_factor << u.uK_RJ elif output_units == output_map.unit: return output_map
def create_dustd1(nside, nus): maps_dust = np.zeros(((len(nus), 3, 12 * nside**2))) sky = pysm3.Sky(nside=nside, preset_strings=['d1']) for i, j in enumerate(nus): maps_dust[i] = sky.get_emission(j * u.GHz).to( getattr(u, 'uK_CMB'), equivalencies=u.cmb_equivalencies(j * u.GHz)) return maps_dust
def test_bandpass_unit_conversion(): freqs = np.array([250, 300, 350]) * u.GHz weights = np.ones(len(freqs)) norm_weights = pysm3.normalize_weights(freqs.value, weights) conversion_factor = pysm3.utils.bandpass_unit_conversion( freqs, weights, "uK_CMB") each_factor = [(1 * u.uK_RJ).to_value(u.uK_CMB, equivalencies=u.cmb_equivalencies(f)) for f in freqs] expected_factor = np.trapz(each_factor * norm_weights, freqs.value) np.testing.assert_allclose(expected_factor, conversion_factor.value)
def setUp(self): nsamples = 50 nu2 = 40.0 nu1 = 20.0 self.freqs = np.linspace(nu1, nu2, nsamples) * u.GHz weights = np.ones(len(self.freqs)) self.Jysr2CMB = np.trapz(weights, x=self.freqs) / np.trapz( (1 * u.K_CMB).to( u.Jy / u.sr, equivalencies=u.cmb_equivalencies(self.freqs) ), x=self.freqs, )
def from_pysm( cls, freq: float, nside: int, preset_strings: List[str] = ["c1"], ) -> Maps: sky = pysm3.Sky(nside=nside, preset_strings=preset_strings) freq_u = freq * u.GHz m = sky.get_emission(freq_u) return cls( CANONICAL_NAME, m.to(u.uK_CMB, equivalencies=u.cmb_equivalencies(freq_u)), name= f"PySM 3 {freq} GHz map with preset {', '.join(preset_strings)}")
def test_cmb_map(): nside = 32 # pretend for testing that the Dust is CMB model = pysm3.CMBMap(map_IQU="pysm_2/lensed_cmb.fits", nside=nside) freq = 100 * u.GHz expected_map = pysm3.read_map( "pysm_2/lensed_cmb.fits", field=(0, 1), nside=nside, unit=u.uK_CMB ).to(u.uK_RJ, equivalencies=u.cmb_equivalencies(freq)) simulated_map = model.get_emission(freq) for pol in [0, 1]: assert_quantity_allclose(expected_map[pol], simulated_map[pol], rtol=1e-5)
def get_noise_realization(nside, instrument, unit='uK_CMB'): """ Generate noise maps for the instrument Parameters ---------- nside: int Desired output healpix nside. instrument: Object that provides the following as a key or an attribute. - **frequency** (required) - **depth_p** (required if ``noise=True``) - **depth_i** (required if ``noise=True``) They can be anything that is convertible to a float numpy array. If only one of ``depth_p`` or ``depth_i`` is provided, the other is inferred assuming that the former is sqrt(2) higher than the latter. unit: str Unit of the output. Only K_CMB and K_RJ (and multiples) are supported. sky: str of pysm3.Sky Sky to observe. It can be a `pysm3.Sky` or a tag to create one. noise: bool If true, add Gaussian, uncorrelated, isotropic noise. Returns ------- observation: array Shape is ``(n_freq, 3, n_pix)``. """ instrument = standardize_instrument(instrument) if not hasattr(instrument, 'depth_i'): instrument.depth_i = instrument.depth_p / np.sqrt(2) if not hasattr(instrument, 'depth_p'): instrument.depth_p = instrument.depth_i * np.sqrt(2) n_freq = len(instrument.frequency) n_pix = hp.nside2npix(nside) res = np.random.normal(size=(n_pix, 3, n_freq)) depth = np.stack( (instrument.depth_i, instrument.depth_p, instrument.depth_p)) depth *= u.arcmin * u.uK_CMB depth = depth.to(getattr(u, unit) * u.arcmin, equivalencies=u.cmb_equivalencies(instrument.frequency * u.GHz)) res *= depth.value / hp.nside2resol(nside, True) return res.T
def test_precomputed_alms_K_CMB(setup): alms, filename = setup nside = 64 test_map = hp.alm2map(alms, nside=nside) << u.K_CMB precomputed_alms = PrecomputedAlms( filename=filename, nside=nside, input_units="K_CMB" ) freqs = np.array([1, 10, 100]) * u.GHz for freq in freqs: np.testing.assert_allclose( precomputed_alms.get_emission(freq), test_map.to(u.K_RJ, equivalencies=u.cmb_equivalencies(freq)), )
def test_from_classes_custominstrument(): cmb = mapsims.SOPrecomputedCMB( num=0, nside=NSIDE, lensed=False, aberrated=False, has_polarization=True, cmb_set=0, cmb_dir="mapsims/tests/data", input_units="uK_CMB", ) # CIB is only at NSIDE 4096, too much memory for testing # cib = so_pysm_models.WebSkyCIB( # websky_version="0.3", nside=NSIDE, interpolation_kind="linear" # ) simulator = mapsims.MapSim( channels="100", nside=NSIDE, unit="uK_CMB", pysm_components_string="SO_d0", pysm_custom_components={"cmb": cmb}, pysm_output_reference_frame="C", instrument_parameters="planck_deltabandpass", ) output_map = simulator.execute(write_outputs=False)["100"] freq = 100.89 * u.GHz expected_map = cmb.get_emission(freq) + so_pysm_models.get_so_models( "SO_d0", nside=NSIDE ).get_emission(freq) fwhm = 9.682 * u.arcmin from mpi4py import MPI map_dist = pysm.MapDistribution( nside=NSIDE, smoothing_lmax=3 * NSIDE - 1, mpi_comm=MPI.COMM_WORLD ) expected_map = pysm.mpi.mpi_smoothing( expected_map.to_value(u.uK_CMB, equivalencies=u.cmb_equivalencies(freq)), fwhm, map_dist, ) assert_quantity_allclose(output_map, expected_map, rtol=1e-3)
def test_precomputed_alms(): alms_filename = get_pkg_data_filename( "data/Planck_bestfit_alm_seed_583_lmax_95_K_CMB.fits.zip") save_name = get_pkg_data_filename( "data/map_nside_32_from_Planck_bestfit_alm_seed_583_K_CMB.fits.zip") nside = 32 # Make an IQU sim precomputed_alms = PrecomputedAlms( alms_filename, nside=nside, input_units="uK_CMB", has_polarization=True, #input_reference_frequency=148*u.GHz ) simulated_map = precomputed_alms.get_emission(148 * u.GHz).to( u.uK_CMB, equivalencies=u.cmb_equivalencies(148 * u.GHz)) expected_map = hp.read_map(save_name, field=(0, 1, 2)) << u.uK_CMB assert simulated_map.shape[0] == 3 assert_quantity_allclose(simulated_map, expected_map)
def signal(self): """ Simulate CO signal """ out = ( hp.ud_grade(map_in=self.planck_templatemap, nside_out=self.target_nside) << u.K_CMB ) if self.include_high_galactic_latitude_clouds: out += self.simulate_high_galactic_latitude_CO() if self.has_polarization: Q_map, U_map = self.simulate_polarized_emission(out) out = np.array([out, Q_map, U_map]) convert_to_uK_RJ = (1 * u.K_CMB).to_value( self.output_units, equivalencies=u.cmb_equivalencies(self.line_frequency) ) return out * convert_to_uK_RJ
def _cmb2jysr(freqs): return (np.ones_like(freqs) * u.K_CMB).to( u.Jy / u.sr, equivalencies=u.cmb_equivalencies(freqs * u.GHz)).value
def test_convert_units(self): a1 = 1 * u.uK_CMB.to(u.uK_RJ, equivalencies=u.cmb_equivalencies(300.0 * u.GHz)) a2 = 1 * u.uK_RJ.to(u.uK_CMB, equivalencies=u.cmb_equivalencies(300.0 * u.GHz)) self.assertAlmostEqual(1.0, a1 * a2) a1 = 1 * u.uK_CMB.to(u.Unit("MJy/sr"), equivalencies=u.cmb_equivalencies(300.0 * u.GHz)) a2 = 1 * u.Unit("MJy/sr").to( u.uK_CMB, equivalencies=u.cmb_equivalencies(300.0 * u.GHz)) self.assertAlmostEqual(1.0, a1 * a2) """Validation against ECRSC tables. https://irsasupport.ipac.caltech.edu/index.php?/Knowledgebase/ Article/View/181/20/what-are-the-intensity-units-of-the-planck -all-sky-maps-and-how-do-i-convert-between-them These tables are based on the following tables: h = 6.626176e-26 erg*s k = 1.380662e-16 erg/L c = 2.997792458e1- cm/s T_CMB = 2.726 The impact of the incorrect CMB temperature is especially impactful and limits some comparison to only ~2/3 s.f. """ freqs = [30, 143, 857] uK_CMB_2_K_RJ = dict() uK_CMB_2_K_RJ[30] = 9.77074e-7 uK_CMB_2_K_RJ[143] = 6.04833e-7 uK_CMB_2_K_RJ[857] = 6.37740e-11 for freq in freqs: self.assertAlmostEqual( uK_CMB_2_K_RJ[freq], 1 * u.uK_CMB.to( u.K_RJ, equivalencies=u.cmb_equivalencies(freq * u.GHz)), ) K_CMB_2_MJysr = dict() K_CMB_2_MJysr[30] = 27.6515 K_CMB_2_MJysr[143] = 628.272 K_CMB_2_MJysr[857] = 22565.1 for freq in freqs: self.assertAlmostEqual( K_CMB_2_MJysr[freq] / (1 * u.K_RJ.to(u.MJy / u.sr, equivalencies=u.cmb_equivalencies(freq * u.GHz))), 1.0, places=4, ) # Note that the MJysr definition seems to match comparatively poorly. The # definitions of h, k, c in the document linked above are in cgs and differ # from those on wikipedia. This may conflict with the scipy constants I use. uK_CMB_2_MJysr = dict() uK_CMB_2_MJysr[30] = 2.7e-5 uK_CMB_2_MJysr[143] = 0.0003800 uK_CMB_2_MJysr[857] = 1.43907e-6 for freq in freqs: self.assertAlmostEqual( uK_CMB_2_MJysr[freq] / (1 * u.uK_CMB.to(u.MJy / u.sr, equivalencies=u.cmb_equivalencies(freq * u.GHz))), 1.0, places=2, )
def make_fg_sims(params): """ Write foreground maps on disk Parameters ---------- params: module contating all the simulation parameters """ parallel = params.parallel nside = params.nside smooth = params.gaussian_smooth root_dir = params.out_dir if root_dir[0] == '.': pwd = os.getcwd() root_dir = root_dir.replace('.', pwd) out_dir = f'{root_dir}/foregrounds/' file_str = params.file_string ch_name = [ 'SO_SAT_27', 'SO_SAT_39', 'SO_SAT_93', 'SO_SAT_145', 'SO_SAT_225', 'SO_SAT_280' ] freqs = sonc.Simons_Observatory_V3_SA_bands() beams = sonc.Simons_Observatory_V3_SA_beams() gaussian_fg = params.gaussian_fg band_int = params.band_int nmc_fg = params.nmc_fg seed_fg = params.seed_fg if not nmc_fg: parallel = False fg_models = params.fg_models components = list(fg_models.keys()) ncomp = len(components) rank = 0 size = 1 if parallel: from mpi4py import MPI comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() if not os.path.exists(out_dir) and rank == 0: os.makedirs(out_dir) if gaussian_fg and nmc_fg: nmc_fg = math.ceil(nmc_fg / size) * size if nmc_fg != params.nmc_fg: print_rnk0(f'WARNING: setting nmc_fg = {nmc_fg}', rank) perrank = nmc_fg // size else: perrank = 1 for ncp, cmp in enumerate(components): if not os.path.exists(out_dir + cmp) and rank == 0: os.makedirs(out_dir + cmp) write_dir = out_dir + cmp + '/' fg_config_file_name = fg_models[cmp] if ('so' in fg_config_file_name) or ('pysm' in fg_config_file_name): fg_config_file_path = os.path.join(os.path.dirname(__file__), 'fg_models/') fg_config_file = f'{fg_config_file_path}/{fg_config_file_name}' else: fg_config_file = f'{fg_config_file_name}' for nmc in range(rank * perrank, (rank + 1) * perrank): if gaussian_fg: nmc_str = str(nmc).zfill(4) if seed_fg: seed_fg_mc = seed_fg + nmc + ncp * 1002 if nmc_fg: if not os.path.exists(out_dir + cmp + '/' + nmc_str): os.makedirs(out_dir + cmp + '/' + nmc_str) write_dir = out_dir + cmp + '/' + nmc_str if cmp == 'dust': Kcmb_to_Krj = (1. * u.uK_CMB).to_value( u.uK_RJ, equivalencies=u.cmb_equivalencies(353. * u.GHz)) A_EE_BB = np.array([56., 28.]) * Kcmb_to_Krj**2. alpha_EE_BB = np.array([-0.32, -0.16]) if cmp == 'synch': Kcmb_to_Krj = (1. * u.uK_CMB).to_value( u.uK_RJ, equivalencies=u.cmb_equivalencies(23. * u.GHz)) A_EE_BB = np.array([9., 1.6]) * Kcmb_to_Krj**2. alpha_EE_BB = np.array([-0.7, -0.93]) fg_temp = make_gaussian_fg(A_EE_BB, alpha_EE_BB, Nside=nside, seed=seed_fg_mc) file_name_Q = f'{cmp}_{nmc_str}_{file_str}_Q.fits' file_name_U = f'{cmp}_{nmc_str}_{file_str}_U.fits' file_path_Q = f'{write_dir}/{file_name_Q}' file_path_U = f'{write_dir}/{file_name_U}' hp.write_map(file_path_Q, fg_temp[1], overwrite=True, dtype=np.float32) hp.write_map(file_path_U, fg_temp[2], overwrite=True, dtype=np.float32) fg_config_file_name = f'{write_dir}/{cmp}_{nmc_str}_gauss.cfg' write_gaussian_config_file(cmp, file_path_Q, file_path_U, fg_config_file_name) fg_config_file = fg_config_file_name sky = pysm3.Sky(nside=nside, component_config=fg_config_file) for nch, chnl in enumerate(ch_name): freq = freqs[nch] fwhm = beams[nch] if band_int: band_file_name = os.path.join( os.path.dirname(__file__), f'datautils/bandpasses/band_{chnl}.txt') bandpass_frequencies, weights = np.loadtxt(band_file_name, unpack=True) bandpass_frequencies = bandpass_frequencies * u.GHz sky_extrap = sky.get_emission(bandpass_frequencies, weights) sky_extrap = sky_extrap * bandpass_unit_conversion( bandpass_frequencies, weights, u.uK_CMB) else: sky_extrap = sky.get_emission(freq * u.GHz) sky_extrap = sky_extrap.to( u.uK_CMB, equivalencies=u.cmb_equivalencies(freq * u.GHz)) if not gaussian_fg: sky_extrap = pysm3.apply_smoothing_and_coord_transform( sky_extrap, rot=hp.Rotator(coord=("G", "C"))) if smooth: sky_extrap_smt = hp.smoothing(sky_extrap, fwhm=np.radians(fwhm / 60.), verbose=False) else: sky_extrap_smt = sky_extrap file_name = f'{chnl}_{cmp}_{file_str}.fits' if nmc_fg: file_name = f'{chnl}_{cmp}_{nmc_str}_{file_str}.fits' file_tot_path = f'{write_dir}/{file_name}' hp.write_map(file_tot_path, sky_extrap_smt, overwrite=True, dtype=np.float32)
def simulate( self, tube, output_units="uK_CMB", seed=None, nsplits=1, mask_value=None, atmosphere=True, hitmap=None, white_noise_rms=None, ): """Create a random realization of the noise power spectrum Parameters ---------- tube : str Specify a tube (for SO: ST0-ST3, LT0-LT6) see the `tubes` attribute output_units : str Output unit supported by PySM.units, e.g. uK_CMB or K_RJ seed : integer or tuple of integers, optional Specify a seed. The seed is converted to a tuple if not already one and appended to (0,0,6,tube_id) to avoid collisions between tubes, with the signal sims and with ACT noise sims, where tube_id is the integer ID of the tube. nsplits : integer, optional Number of splits to generate. The splits will have independent noise realizations, with noise power scaled by a factor of nsplits, i.e. atmospheric noise is assumed to average down with observing time the same way the white noise does. By default, only one split (the coadd) is generated. mask_value : float, optional The value to set in masked (unobserved) regions. By default, it uses the value in default_mask_value, which for healpix is healpy.UNSEEN and for CAR is numpy.nan. atmosphere : bool, optional Whether to include the correlated 1/f from the noise model. This is True by default. If it is set to False, then a pure white noise map is generated from the white noise power in the noise model, and the covariance between arrays is ignored. hitmap : string or map, optional Provide the path to a hitmap to override the default used for the tube. You could also provide the hitmap as an array directly. white_noise_rms : float or tuple of floats, optional Optionally scale the simulation so that the small-scale limit white noise level is white_noise_rms in uK-arcmin (either a single number or a pair for the dichroic array). Returns ------- output_map : ndarray or ndmap Numpy array with the HEALPix or CAR map realization of noise. The shape of the returned array is (2,3,nsplits,)+oshape, where oshape is (npix,) for HEALPix and (Ny,Nx) for CAR. The first dimension of size 2 corresponds to the two different bands within a dichroic tube. See the `band_id` attribute of the Channel class to identify which is the index of a Channel in the array. The second dimension corresponds to independent split realizations of the noise, e.g. it is 1 for full mission. The third dimension corresponds to the three polarization Stokes components I,Q,U The last dimension is the number of pixels """ assert nsplits >= 1 if mask_value is None: mask_value = (default_mask_value["healpix"] if self.healpix else default_mask_value["car"]) # This seed tuple prevents collisions with the signal sims # but we should eventually switch to centralized seed # tracking. if seed is not None: try: iter(seed) except: seed = (seed, ) tube_id = self.tubes[tube][0].tube_id seed = (0, 0, 6, tube_id) + seed np.random.seed(seed) # In the third row we return the correlation coefficient P12/sqrt(P11*P22) # since that can be used straightforwardly when the auto-correlations are re-scaled. ell, ps_T, ps_P, fsky, wnoise_power, weightsMap = self.get_noise_properties( tube, nsplits=nsplits, hitmap=hitmap, white_noise_rms=white_noise_rms, atmosphere=atmosphere, ) if not (atmosphere): if self.apply_beam_correction: raise NotImplementedError( "Beam correction is not currently implemented for pure-white-noise sims." ) # If no atmosphere is requested, we use a simpler/faster method # that generates white noise in real-space. if self.healpix: ashape = (hp.nside2npix(self.nside), ) sel = np.s_[:, None, None, None] pmap = self.pixarea_map else: ashape = self.shape[-2:] sel = np.s_[:, None, None, None, None] pmap = pixell.enmap.enmap(self.pixarea_map, self.wcs) spowr = np.sqrt(wnoise_power[sel] / pmap) output_map = spowr * np.random.standard_normal( (self.channel_per_tube, nsplits, 3) + ashape) output_map[:, :, 1:, :] = output_map[:, :, 1:, :] * np.sqrt(2.0) else: if self.healpix: npix = hp.nside2npix(self.nside) output_map = np.zeros( (self.channel_per_tube, nsplits, 3, npix)) for i in range(nsplits): for i_pol in range(3): output_map[:, i, i_pol] = np.array( hp.synfast( ps_T if i_pol == 0 else ps_P, nside=self.nside, pol=False, new=True, verbose=False, )) else: output_map = pixell.enmap.zeros((2, nsplits, 3) + self.shape, self.wcs) ps_T = pixell.powspec.sym_expand(np.asarray(ps_T), scheme="diag") ps_P = pixell.powspec.sym_expand(np.asarray(ps_P), scheme="diag") # TODO: These loops can probably be vectorized for i in range(nsplits): for i_pol in range(3): output_map[:, i, i_pol] = pixell.curvedsky.rand_map( (self.channel_per_tube, ) + self.shape, self.wcs, ps_T if i_pol == 0 else ps_P, spin=0, ) for i in range(self.channel_per_tube): freq = self.tubes[tube][i].center_frequency if not (self.homogeneous): good = weightsMap[i] != 0 # Normalize on the Effective sky fraction, see discussion in: # https://github.com/simonsobs/mapsims/pull/5#discussion_r244939311 output_map[i, :, :, good] /= np.sqrt(weightsMap[i][good][..., None, None]) output_map[i, :, :, np.logical_not(good)] = mask_value unit_conv = (1 * u.uK_CMB).to_value( u.Unit(output_units), equivalencies=u.cmb_equivalencies(freq)) output_map[i] *= unit_conv return output_map
def get_inverse_variance(self, tube, output_units="uK_CMB", hitmap=None, white_noise_rms=None): """Get the inverse noise variance in each pixel for the requested tube. In the noise model, all the splits and all the I,Q,U components have the same position dependence of the noise variance. Each split just has `nsplits` times the noise power (or `1/nsplits` the inverse noise variance) and the Q,U components have 2x times the noise power (or 1/2 times the inverse noise variance) of the intensity components. The inverse noise variance provided by this function is for the `nsplits=1` intensity component. Two maps are stored in the leading dimension, one for each of the two correlated arrays in the dichroic tube. Parameters ---------- tube : str Specify a tube (for SO: ST0-ST3, LT0-LT6) see the `tubes` attribute output_units : str Output unit supported by PySM.units, e.g. uK_CMB or K_RJ hitmap : string or map, optional Provide the path to a hitmap to override the default used for the tube. You could also provide the hitmap as an array directly. white_noise_rms : float or tuple of floats, optional Optionally scale the simulation so that the small-scale limit white noise level is white_noise_rms in uK-arcmin (either a single number or a pair for the dichroic array). Returns ------- ivar_map : ndarray or ndmap Numpy array with the HEALPix or CAR map of the inverse variance in each pixel. The default units are uK^(-2). This is an extensive quantity that depends on the size of pixels. See the `band_id` attribute of the Channel class to identify which is the index of a Channel in the array. """ ret = self._load_inverse_variance_map(tube, output_units=output_units) if ret is not None: return ret fsky, hitmaps = self._get_requested_hitmaps(tube, hitmap) wnoise_scale = self._get_wscale_factor(white_noise_rms, tube, fsky) sel = np.s_[:, None] if self.healpix else np.s_[:, None, None] whiteNoise = self.get_white_noise_power(tube, sky_fraction=1, units="sr") if whiteNoise is None: raise AssertionError( " Survey white noise level not specified. Cannot generate ivar_map" ) power = whiteNoise[sel] * fsky[sel] * wnoise_scale[:, 0][sel] """ We now have the physical white noise power uK^2-sr and the hitmap ivar = hitmap * pixel_area * fsky / <hitmap> / power """ avgNhits = np.asarray( [self._average(hitmaps[i]) for i in range(self.channel_per_tube)]) ret = hitmaps * self.pixarea_map * fsky[sel] / avgNhits[sel] / power # Convert to desired units for i in range(self.channel_per_tube): freq = self.tubes[tube][i].center_frequency unit_conv = (1 * u.uK_CMB).to_value( u.Unit(output_units), equivalencies=u.cmb_equivalencies(freq)) ret[i] /= unit_conv**2.0 # divide by square since the default is 1/uK^2 return ret
def generate_cmb(self): instr = self.instrument nmc_cmb = self.params.nmc_cmb nside = self.params.nside npix = hp.nside2npix(nside) smooth = self.params.gaussian_smooth parallel = self.params.parallel_mc root_dir = self.sim.base_path output_directory = root_dir / "cmb" file_str = self.params.output_string channels = instr.keys() n_channels = len(channels) seed_cmb = self.params.seed_cmb cmb_ps_file = self.params.cmb_ps_file col_units = [self.params.units, self.params.units, self.params.units] saved_maps = [] if parallel: comm = lbs.MPI_COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() else: comm = None rank, size = 0, 1 if rank == 0: output_directory.mkdir(parents=True, exist_ok=True) if cmb_ps_file: cl_cmb = hp.read_cl(cmb_ps_file) else: datautils_dir = Path(__file__).parent.parent / "datautils" cl_cmb_scalar = hp.read_cl(datautils_dir / "Cls_Planck2018_for_PTEP_2020_r0.fits") cl_cmb_tensor = (hp.read_cl( datautils_dir / "Cls_Planck2018_for_PTEP_2020_tensor_r1.fits") * self.params.cmb_r) cl_cmb = cl_cmb_scalar + cl_cmb_tensor nmc_cmb = math.ceil(nmc_cmb / size) * size if nmc_cmb != self.params.nmc_cmb: log.info(f"setting nmc_cmb = {nmc_cmb}", rank) perrank = nmc_cmb // size if not self.params.save: cmb_map_matrix = np.zeros((n_channels, 3, npix)) else: cmb_map_matrix = None os.environ["PYSM_LOCAL_DATA"] = str(output_directory) for nmc in range(rank * perrank, (rank + 1) * perrank): if seed_cmb: np.random.seed(seed_cmb + nmc) nmc_str = f"{nmc:04d}" nmc_output_directory = output_directory / nmc_str if rank == 0: nmc_output_directory.mkdir(parents=True, exist_ok=True) cmb_temp = hp.synfast(cl_cmb, nside, new=True, verbose=False) file_name = f"cmb_{nmc_str}_{file_str}.fits" cur_map_path = nmc_output_directory / file_name lbs.write_healpix_map_to_file(cur_map_path, cmb_temp, column_units=col_units) saved_maps.append( MbsSavedMapInfo(path=cur_map_path, component="cmb", mc_num=nmc)) sky = pysm3.Sky( nside=nside, component_objects=[ pysm3.CMBMap(nside, map_IQU=Path(nmc_str) / file_name) ], ) for Nchnl, chnl in enumerate(channels): freq = instr[chnl].bandcenter_ghz if self.params.bandpass_int: band = instr[chnl].bandwidth_ghz fmin = freq - band / 2.0 fmax = freq + band / 2.0 fsteps = np.int(np.ceil(fmax - fmin) + 1) bandpass_frequencies = np.linspace(fmin, fmax, fsteps) * u.GHz weights = np.ones(len(bandpass_frequencies)) cmb_map = sky.get_emission(bandpass_frequencies, weights) cmb_map = cmb_map * pysm3.bandpass_unit_conversion( bandpass_frequencies, weights, self.pysm_units) else: cmb_map = sky.get_emission(freq * u.GHz) cmb_map = cmb_map.to(self.pysm_units, equivalencies=u.cmb_equivalencies( freq * u.GHz)) fwhm_arcmin = instr[chnl].fwhm_arcmin if smooth: cmb_map_smt = hp.smoothing(cmb_map, fwhm=np.radians(fwhm_arcmin / 60.0), verbose=False) else: cmb_map_smt = cmb_map if self.params.save: file_name = f"{chnl}_cmb_{nmc_str}_{file_str}.fits" cur_map_path = nmc_output_directory / file_name lbs.write_healpix_map_to_file(cur_map_path, cmb_map_smt, column_units=col_units) saved_maps.append( MbsSavedMapInfo(path=cur_map_path, component="cmb")) else: cmb_map_matrix[Nchnl] = cmb_map_smt return (cmb_map_matrix, saved_maps)
def make_cmb_sims(params): """ Write cmb maps on disk Parameters ---------- params: module contating all the simulation parameters """ nmc_cmb = params.nmc_cmb nside = params.nside smooth = params.gaussian_smooth ch_name = [ 'SO_SAT_27', 'SO_SAT_39', 'SO_SAT_93', 'SO_SAT_145', 'SO_SAT_225', 'SO_SAT_280' ] freqs = sonc.Simons_Observatory_V3_SA_bands() beams = sonc.Simons_Observatory_V3_SA_beams() band_int = params.band_int parallel = params.parallel root_dir = params.out_dir out_dir = f'{root_dir}/cmb/' file_str = params.file_string seed_cmb = params.seed_cmb cmb_ps_file = params.cmb_ps_file rank = 0 size = 1 if params.parallel: from mpi4py import MPI comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() if not os.path.exists(out_dir) and rank == 0: os.makedirs(out_dir) if cmb_ps_file: print(cmb_ps_file) cl_cmb = hp.read_cl(cmb_ps_file) else: cmb_ps_scalar_file = os.path.join(os.path.dirname(__file__), 'datautils/Cls_Planck2018_r0.fits') cl_cmb_scalar = hp.read_cl(cmb_ps_scalar_file) cmb_ps_tensor_r1_file = os.path.join( os.path.dirname(__file__), 'datautils/Cls_Planck2018_tensor_r1.fits') cmb_r = params.cmb_r cl_cmb_tensor = hp.read_cl(cmb_ps_tensor_r1_file) * cmb_r cl_cmb = cl_cmb_scalar + cl_cmb_tensor nmc_cmb = math.ceil(nmc_cmb / size) * size if nmc_cmb != params.nmc_cmb: print_rnk0(f'WARNING: setting nmc_cmb = {nmc_cmb}', rank) perrank = nmc_cmb // size for nmc in range(rank * perrank, (rank + 1) * perrank): if seed_cmb: np.random.seed(seed_cmb + nmc) nmc_str = str(nmc).zfill(4) if not os.path.exists(out_dir + nmc_str): os.makedirs(out_dir + nmc_str) cmb_temp = hp.synfast(cl_cmb, nside, new=True, verbose=False) file_name = f'cmb_{nmc_str}_{file_str}.fits' file_tot_path = f'{out_dir}{nmc_str}/{file_name}' hp.write_map(file_tot_path, cmb_temp, overwrite=True, dtype=np.float32) os.environ["PYSM_LOCAL_DATA"] = f'{out_dir}' sky = pysm3.Sky(nside=nside, component_objects=[ pysm3.CMBMap(nside, map_IQU=f'{nmc_str}/{file_name}') ]) for nch, chnl in enumerate(ch_name): freq = freqs[nch] fwhm = beams[nch] cmb_map = sky.get_emission(freq * u.GHz) cmb_map = cmb_map.to(u.uK_CMB, equivalencies=u.cmb_equivalencies(freq * u.GHz)) if smooth: cmb_map_smt = hp.smoothing(cmb_map, fwhm=np.radians(fwhm / 60.), verbose=False) else: cmb_map_smt = cmb_map file_name = f'{chnl}_cmb_{nmc_str}_{file_str}.fits' file_tot_path = f'{out_dir}{nmc_str}/{file_name}' hp.write_map(file_tot_path, cmb_map_smt, overwrite=True, dtype=np.float32)
def _rj2cmb(freqs): return (np.ones_like(freqs) * u.K_RJ).to(u.K_CMB, equivalencies=u.cmb_equivalencies( freqs * u.GHz)).value
def generate_fg(self): parallel = self.params.parallel_mc instr = self.instrument nside = self.params.nside npix = hp.nside2npix(nside) smooth = self.params.gaussian_smooth root_dir = self.sim.base_path output_directory = root_dir / "foregrounds" file_str = self.params.output_string channels = instr.keys() n_channels = len(channels) fg_models = self.params.fg_models components = fg_models.keys() col_units = [self.params.units, self.params.units, self.params.units] saved_maps = [] if parallel: comm = lbs.MPI_COMM_WORLD rank = comm.Get_rank() else: comm = None rank = 0 if rank == 0 and self.params.save: output_directory.mkdir(parents=True, exist_ok=True) dict_fg = {} if rank != 0: if not self.params.save: return (dict_fg, saved_maps) else: return (None, saved_maps) for cmp in components: cmp_dir = output_directory / cmp if rank == 0 and self.params.save: cmp_dir.mkdir(parents=True, exist_ok=True) fg_config_file_name = fg_models[cmp] if ("lb" in fg_config_file_name) or ("pysm" in fg_config_file_name): fg_config_file_path = Path(__file__).parent / "fg_models" fg_config_file = fg_config_file_path / f"{fg_config_file_name}.cfg" else: fg_config_file = f"{fg_config_file_name}" sky = pysm3.Sky(nside=nside, component_config=fg_config_file) if not self.params.save: fg_map_matrix = np.zeros((n_channels, 3, npix)) for Nchnl, chnl in enumerate(channels): freq = instr[chnl].bandcenter_ghz fwhm_arcmin = instr[chnl].fwhm_arcmin if self.params.bandpass_int: band = instr[chnl].bandwidth_ghz fmin = freq - band / 2.0 fmax = freq + band / 2.0 fsteps = np.int(np.ceil(fmax - fmin) + 1) bandpass_frequencies = np.linspace(fmin, fmax, fsteps) * u.GHz weights = np.ones(len(bandpass_frequencies)) sky_extrap = sky.get_emission(bandpass_frequencies, weights) sky_extrap = sky_extrap * pysm3.bandpass_unit_conversion( bandpass_frequencies, weights, self.pysm_units) else: sky_extrap = sky.get_emission(freq * u.GHz) sky_extrap = sky_extrap.to( self.pysm_units, equivalencies=u.cmb_equivalencies(freq * u.GHz)) if smooth: sky_extrap_smt = hp.smoothing(sky_extrap, fwhm=np.radians(fwhm_arcmin / 60.0), verbose=False) else: sky_extrap_smt = sky_extrap if self.params.save: file_name = f"{chnl}_{cmp}_{file_str}.fits" cur_map_path = cmp_dir / file_name lbs.write_healpix_map_to_file(cur_map_path, sky_extrap_smt, column_units=col_units) saved_maps.append( MbsSavedMapInfo(path=cur_map_path, component="fg", channel=chnl)) else: fg_map_matrix[Nchnl] = sky_extrap_smt if not self.params.save: dict_fg[cmp] = fg_map_matrix if not self.params.save: return (dict_fg, saved_maps) else: return (None, saved_maps)
bpws = nmt.compute_full_master(f0, f0, nmtbin) return nmtbin.get_effective_ells(), bpws[0] # %% # Compute Commander model at 100 GHz, 143 GHz. maps = {} maps["Commander"] = {} EM, T_e = hp.read_map("COM_CompMap_freefree-commander_0256_R2.00.fits", field=(0, 3)) for (key, nu) in FREQS.items(): arr = ff_model_table4_planck150201588(EM, T_e, nu=nu.to( u.Hz).value) * u.uK_RJ maps["Commander"][key] = arr.to(u.uK_CMB, equivalencies=u.cmb_equivalencies(nu)) # %% # Compute Hutschenreuter model at 100 GHz, 143 GHz. maps["Heutschenreuter"] = {} hdu = fits.open("components_w_ff.fits") T_e = hp.read_map("COM_CompMap_freefree-commander_0256_R2.00.fits", field=3) EM = np.exp(hdu[1].data["EPSILON_MEAN"]) for (key, nu) in FREQS.items(): arr = ff_model_table4_planck150201588(EM, T_e, nu=nu.to( u.Hz).value) * u.uK_RJ maps["Heutschenreuter"][key] = arr.to( u.uK_CMB, equivalencies=u.cmb_equivalencies(nu)) # %%
def get_observation(instrument='', sky=None, noise=False, nside=None, unit='uK_CMB'): """ Get a pre-defined instrumental configuration Parameters ---------- instrument: It can be either a `str` (see :func:`get_instrument`) or an object that provides the following as a key or an attribute. - **frequency** (required) - **depth_p** (required if ``noise=True``) - **depth_i** (required if ``noise=True``) They can be anything that is convertible to a float numpy array. If only one of ``depth_p`` or ``depth_i`` is provided, the other is inferred assuming that the former is sqrt(2) higher than the latter. sky: str of pysm3.Sky Sky to observe. It can be a `pysm3.Sky` or a tag to create one. noise: bool If true, add Gaussian, uncorrelated, isotropic noise. nside: int Desired output healpix nside. It is optional if `sky` is a `pysm3.Sky`, and required if it is a `str` or ``None``. unit: str Unit of the output. Only K_CMB and K_RJ (and multiples) are supported. Returns ------- observation: array Shape is ``(n_freq, 3, n_pix)`` """ if isinstance(instrument, str): instrument = get_instrument(instrument) else: instrument = standardize_instrument(instrument) if nside is None: nside = sky.nside elif not isinstance(sky, str): try: assert nside == sky.nside, ( "Mismatch between the value of the nside of the pysm3.Sky " "argument and the one passed in the nside argument.") except AttributeError: raise ValueError("Either provide a pysm3.Sky as sky argument " " or specify the nside argument.") if noise: res = get_noise_realization(nside, instrument, unit) else: res = np.zeros((len(instrument.frequency), 3, hp.nside2npix(nside))) if sky is None or sky == '': return res if isinstance(sky, str): sky = get_sky(nside, sky) for res_freq, freq in zip(res, instrument.frequency): emission = sky.get_emission(freq * u.GHz).to( getattr(u, unit), equivalencies=u.cmb_equivalencies(freq * u.GHz)) res_freq += emission.value return res
def _jysr2cmb(freqs): return (np.ones_like(freqs) * u.Jy / u.sr).to( u.K_CMB, equivalencies=u.cmb_equivalencies(freqs * u.GHz)).value
def _cmb2rj(freqs): return (np.ones_like(freqs) * u.K_CMB).to( u.K_RJ, equivalencies=u.cmb_equivalencies(freqs * u.GHz)).value