def test_cmb_map_bandpass(): nside = 32 # pretend for testing that the Dust is CMB model = pysm.CMBMap(map_IQU="pysm_2/lensed_cmb.fits", nside=nside) freq = 100 * u.GHz expected_map = pysm.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_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 __init__( self, filename, input_units="uK_CMB", input_reference_frequency=None, nside=None, target_shape=None, target_wcs=None, precompute_output_map=True, has_polarization=True, map_dist=None, ): """Generic component based on Precomputed Alms Load a set of Alms from a FITS file and generate maps at the requested resolution and frequency assuming the CMB black body spectrum. A single set of Alms is used for all frequencies requested by PySM, consider that PySM expects the output of components to be in uK_RJ. See more details at https://so-pysm-models.readthedocs.io/en/latest/so_pysm_models/models.html Parameters ---------- filename : string Path to the input Alms in FITS format input_units : string Input unit strings as defined by pysm.convert_units, e.g. K_CMB, uK_RJ, MJysr input_reference_frequency: float If input units are K_RJ or Jysr, the reference frequency nside : int HEALPix NSIDE of the output maps precompute_output_map : bool If True (default), Alms are transformed into a map in the constructor, if False, the object only stores the Alms and generate the map at each call of the signal method, this is useful to generate maps convolved with different beams has_polarization : bool whether or not to simulate also polarization maps Default: True """ super().__init__(nside=nside, map_dist=map_dist) self.shape = target_shape self.wcs = target_wcs self.filename = filename self.input_units = u.Unit(input_units) self.has_polarization = has_polarization alm = np.complex128( hp.read_alm(self.filename, hdu=(1, 2, 3) if self.has_polarization else 1)) self.equivalencies = (None if input_reference_frequency is None else u.cmb_equivalencies(input_reference_frequency)) if precompute_output_map: self.output_map = self.compute_output_map(alm) else: self.alm = alm
def get_emission(self, freqs: u.GHz, fwhm: [u.arcmin, None] = None, weights=None) -> 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) """ try: nfreqs = len(freqs) except TypeError: nfreqs = 1 freqs = freqs.reshape((1, )) 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) convert_to_uK_RJ = (np.ones(len(freqs), dtype=np.double) * u.uK_CMB).to_value( u.uK_RJ, equivalencies=u.cmb_equivalencies(freqs)) if nfreqs == 1: scaling_factor = convert_to_uK_RJ[0] else: scaling_factor = np.trapz(convert_to_uK_RJ * weights, x=freqs.value) return output_map.value * scaling_factor << u.uK_RJ
def test_bandpass_unit_conversion(): freqs = np.array([250, 300, 350]) * u.GHz weights = np.ones(len(freqs)) norm_weights = pysm.normalize_weights(freqs.value, weights) conversion_factor = pysm.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 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 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 pysm.Sky Sky to observe. It can be a `pysm.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 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 test_precomputed_alms(): alms_filename = get_pkg_data_filename( "data/fullskyUnlensedUnabberatedCMB_alm_set00_00000.fits.zip") save_name = get_pkg_data_filename("data/test_cmb_map.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_quantity_allclose(simulated_map, expected_map) assert simulated_map.shape[0] == 3
def get_emission(self, freqs: u.GHz, weights=None) -> u.uK_RJ: nu = freqs.to(u.GHz) weights = normalize_weights(freqs, weights) if nu.isscalar: nu = nu.reshape(1) filename = utils.get_data_from_url(self.get_filename()) m = self.read_map(filename, field=0, unit=u.uK_CMB) weights = (weights * u.uK_CMB).to_value( u.uK_RJ, equivalencies=u.cmb_equivalencies(nu)) is_thermal = self.sz_type == "thermal" output = (get_sz_emission_numba(nu.value, weights, m.value, is_thermal) << u.uK_RJ) # the output of out is always 2D, (IQU, npix) return output
def test_cmb_map(): nside = 32 # pretend for testing that the Dust is CMB model = pysm.CMBMap(map_IQU="pysm_2/lensed_cmb.fits", nside=nside) freq = 100 * u.GHz expected_map = pysm.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_cmb_realization(nside, cl_path, beams, frequencies, seed=100): with h5py.File(f"{cl_path}", 'r') as f: cl_total = np.swapaxes(f['lensed_scalar'][...], 0, 1) cmb = hp.synfast(cl_total, nside, new=True, verbose=False) cmb = [hp.smoothing(cmb, fwhm=b / 60. * np.pi/180., verbose=False)[1:] * u.uK_CMB for b in beams] return np.array([c.to(u.uK_RJ, equivalencies=u.cmb_equivalencies(f)) for c, f in zip(cmb, frequencies)])
def _rj2cmb(freqs): return (np.ones_like(freqs) * u.K_RJ).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
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 pysm.Sky Sky to observe. It can be a `pysm.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 `pysm.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 pysm.Sky " "argument and the one passed in the nside argument.") except AttributeError: raise ValueError("Either provide a pysm.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 main(cfg_path: Path, log_level: int): logging.basicConfig(stream=sys.stdout, level=log_level, datefmt='%Y-%m-%d %H:%M', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') with open(cfg_path) as f: cfg = yaml.load(f, Loader=yaml.FullLoader) freqs = old_np.array(cfg['frequencies']) * u. GHz nside = cfg['nside'] components = cfg['skymodel']['args'] sensitivities = cfg['sensitivities'] nmc = cfg['monte_carlo'] beams = cfg['fwhm'] outpath = cfg['hdf5_path'] half_mission_noise = cfg['half_mission_noise'] cosmo_path = cfg['cosmo_path'] if half_mission_noise: sensitivities = [s * np.sqrt(2.) for s in sensitivities] logging.info(f""" Frequencies: {freqs!s} Nside: {nside:04d} Components: {components!s} Sensitivities: {sensitivities!s} Number of Monte Carlo Simulations: {nmc:05d} """) # Generate sky signal sky = pysm.Sky(nside=nside, **components) fgnd = (sky.get_emission(f) for f in freqs) fgnd = (hp.smoothing(s, fwhm=b / 60. * np.pi / 180., verbose=False)[None, 1:, ...] for b, s in zip(beams, fgnd)) fgnd = np.concatenate(list(fgnd)) # Make noise generator sens = np.array(sensitivities) * u.uK_CMB sens = np.array([w.to(u.uK_RJ, equivalencies=u.cmb_equivalencies(f)) for w, f in zip(sens, freqs)]) noise_generator = WhiteNoise(sens=sens) cov = noise_generator.get_pix_var_map(nside) logging.info(f"Output path: {outpath}") with h5py.File(outpath, 'a') as f: f.attrs.update({'config': yaml.dump(cfg)}) maps = f.require_group('maps') monte_carlo = maps.require_group('monte_carlo') components = maps.require_group('components') data_dset = monte_carlo.require_dataset('data', shape=(nmc, len(freqs), 2, hp.nside2npix(nside)), dtype=np.float32) cov_dset = monte_carlo.require_dataset('cov', shape=(nmc, len(freqs), 2, hp.nside2npix(nside)), dtype=np.float32) cov_dset[...] = cov.astype(np.float32) for imc in np.arange(nmc)[::2]: logging.info(f"Working on CMB MC: {imc:04d}") cmb = get_cmb_realization(nside, cosmo_path, beams, freqs, seed=imc) for j in range(imc, imc + 2): logging.info(f"Working on noise MC: {j:04d}") data = fgnd + cmb + noise_generator.map(nside, seed=j) logging.debug(f"Data shape: {data.shape!r}") data_dset[j] = data