def get_bamc(self): """Binned additive MC correction, with crude error bars. This compares the reconstruction on the simulations to the FFP10 input lensing spectrum. Note: the approximate error corrections to the additive MC correction variance follows Appendix C of https://arxiv.org/abs/1807.06210, check this for more details on its validity. """ assert self.k1[0] == 'p' and self.k2[ 0] == 'p' and self.ksource == 'p', (self.k1, self.k2, self.ksource) ss2 = 2 * self.parfile.qcls_ss.get_sim_stats_qcl( self.k1, self.parfile.mc_sims_var, k2=self.k2).mean() cl_pred = utils.camb_clfile( os.path.join( self.cls_path, 'FFP10_wdipole_lenspotentialCls.dat'))['pp'][:len(ss2)] qc_norm = utils.cli( self.parfile.qresp_dd.get_response(self.k1, self.ksource) * self.parfile.qresp_dd.get_response(self.k2, self.ksource)) bp_stats = utils.stats(self.nbins) bp_n1 = self.get_n1() for i, idx in utils.enumerate_progress(self.parfile.mc_sims_var, label='collecting BP stats'): dd = self.parfile.qcls_dd.get_sim_qcl(self.k1, idx, k2=self.k2) bp_stats.add( self._get_binnedcl(qc_norm * (dd - ss2) - cl_pred) - bp_n1) NMF = len(self.parfile.qcls_dd.mc_sims_mf) NB = len(self.parfile.mc_sims_var) return bp_stats.mean(), bp_stats.sigmas_on_mean() * np.sqrt( (1. + 1. + 2. / NMF + 2 * NB / (float(NMF * NMF))))
def get_bmmc(self, mc_sims_dd=None, mc_sims_ss=None): """Binned multiplicative MC correction. This compares the reconstruction on the simulations to the FFP10 input lensing spectrum. """ assert self.k1[0] == 'p' and self.k2[ 0] == 'p' and self.ksource == 'p', (self.k1, self.k2, self.ksource) if mc_sims_dd is None: mc_sims_dd = self.parfile.mc_sims_var if mc_sims_ss is None: mc_sims_ss = self.parfile.mc_sims_var dd = self.parfile.qcls_dd.get_sim_stats_qcl(self.k1, mc_sims_dd, k2=self.k2).mean() ss = self.parfile.qcls_ss.get_sim_stats_qcl(self.k1, mc_sims_ss, k2=self.k2).mean() cl_pred = utils.camb_clfile( os.path.join(self.cls_path, 'FFP10_wdipole_lenspotentialCls.dat'))['pp'] qc_resp = self.parfile.qresp_dd.get_response( self.k1, self.ksource) * self.parfile.qresp_dd.get_response( self.k2, self.ksource) bps = self._get_binnedcl( utils.cli(qc_resp) * (dd - 2 * ss) - cl_pred[:len(dd)]) - self.get_n1() return 1. / (1 + bps / self.fid_bandpowers)
def get_n1(self, k1=None, k2=None, unnormed=False): """Returns analytical N1 lensing bias. This uses the analyical approximation to the QE pair filtering as input. """ k1 = self.k1 if k1 is None else k1 k2 = self.k2 if k2 is None else k2 assert k1 == k2, 'check signs for qe' 's of different spins' assert self.ksource[0] == 'p', 'check aniso source spectrum' # This implementation accepts 2 different qes but pairwise identical filtering on each qe leg. assert np.all(self.parfile.qcls_dd.qeA.f2map1.ivfs.get_ftl() == self.parfile.qcls_dd.qeA.f2map2.ivfs.get_ftl()) assert np.all(self.parfile.qcls_dd.qeA.f2map1.ivfs.get_fel() == self.parfile.qcls_dd.qeA.f2map2.ivfs.get_fel()) assert np.all(self.parfile.qcls_dd.qeA.f2map1.ivfs.get_fbl() == self.parfile.qcls_dd.qeA.f2map2.ivfs.get_fbl()) assert np.all(self.parfile.qcls_dd.qeB.f2map1.ivfs.get_ftl() == self.parfile.qcls_dd.qeB.f2map2.ivfs.get_ftl()) assert np.all(self.parfile.qcls_dd.qeB.f2map1.ivfs.get_fel() == self.parfile.qcls_dd.qeB.f2map2.ivfs.get_fel()) assert np.all(self.parfile.qcls_dd.qeB.f2map1.ivfs.get_fbl() == self.parfile.qcls_dd.qeB.f2map2.ivfs.get_fbl()) ivfsA = self.parfile.qcls_dd.qeA.f2map1.ivfs ivfsB = self.parfile.qcls_dd.qeB.f2map1.ivfs ftlA = ivfsA.get_ftl() felA = ivfsA.get_fel() fblA = ivfsA.get_fbl() ftlB = ivfsB.get_ftl() felB = ivfsB.get_fel() fblB = ivfsB.get_fbl() clpp_fid = utils.camb_clfile( os.path.join(self.cls_path, 'FFP10_wdipole_lenspotentialCls.dat'))['pp'] qc_resp = self.parfile.qresp_dd.get_response( k1, self.ksource) * self.parfile.qresp_dd.get_response( k2, self.ksource) n1pp = self.parfile.n1_dd.get_n1(k1, self.ksource, clpp_fid, ftlA, felA, fblA, len(qc_resp) - 1, kB=k2, ftlB=ftlB, felB=felB, fblB=fblB) return self._get_binnedcl(utils.cli(qc_resp) * n1pp) if not unnormed else n1pp
'data', 'cls') #--- definition of simulation and inverse-variance filtered simulation libraries: lmax_ivf = 2048 lmin_ivf = 100 # We will use in the QE only CMB modes between lmin_ivf and lmax_ivf lmax_qlm = 4096 # We will calculate lensing estimates until multipole lmax_qlm. nside = 2048 # Healpix resolution of the data and sims. nlev_t = 35. # Filtering noise level in temperature (here also used for the noise simulations generation). nlev_p = 55. # Filtering noise level in polarization (here also used for the noise simulations generation). nsims = 300 # Total number of simulations to consider. transf = hp.gauss_beam(5. / 60. / 180. * np.pi, lmax=lmax_ivf) * hp.pixwin(nside)[:lmax_ivf + 1] #: CMB transfer function. Here a 5' Gaussian beam and healpix pixel window function. cl_unl = utils.camb_clfile( os.path.join(cls_path, 'FFP10_wdipole_lenspotentialCls.dat')) cl_len = utils.camb_clfile( os.path.join(cls_path, 'FFP10_wdipole_lensedCls.dat')) #: Fiducial unlensed and lensed power spectra used for the analysis. cl_weight = utils.camb_clfile( os.path.join(cls_path, 'FFP10_wdipole_lensedCls.dat')) cl_weight['bb'] *= 0. #: CMB spectra entering the QE weights (the spectra multplying the inverse-variance filtered maps in the QE legs) libdir_pixphas = os.path.join(TEMP, 'pix_phas_nside%s' % nside) pix_phas = phas.pix_lib_phas(libdir_pixphas, 3, (hp.nside2npix(nside), )) #: Noise simulation T, Q, U random phases instance. sims = maps_utils.sim_lib_shuffle( maps.cmb_maps_nlev(planck2018_sims.cmb_len_ffp10(),
lmin_ivf = 100 lmax_qlm = 4096 # We will calculate lensing estimates until multipole lmax_qlm. nside = 2048 # Healpix resolution of the data and sims. fwhm = 7.1 # Pixel size of the survey, in arc-miniutes. int_survey = 6. # (delta T)/T intensity of the survey, in microkelvin. nlev_t = fwhm * int_survey # Set by the above two parameters and determines # Gaussian noise standard deviation. Also used for noise processing. nlev_p = 55. # Setting in case polarization data is provided. In this script no calculation # with polarization is done, so this is just a place holder. transf = hp.gauss_beam(fwhm / 60. / 180. * np.pi, lmax=lmax_ivf) * hp.pixwin(nside)[:lmax_ivf + 1] # CMB transfer function. Here a Gaussian beam and healpix pixel window function. Used in smoothing. cl_unl = utils.camb_clfile(os.path.join(cls_path, 'CAMB_lenspotentialCls.dat')) cl_len = utils.camb_clfile(os.path.join(cls_path, 'CAMB_lensedCls.dat')) # Fiducial unlensed and lensed power spectra used for the analysis. cl_weight = cl_len cl_weight['bb'] *= 0 # CMB spectra entering the QE weights (the spectra multplying the inverse-variance filtered maps in the QE legs) libdir_pixphas = os.path.join(TEMP, 'pix_phas_nside%s' % nside) pix_phas = phas.pix_lib_phas(libdir_pixphas, 3, (hp.nside2npix(nside), )) # Noise simulation T, Q, U random phases instance. lib = maps.cmb_maps_nlev(datamaps.websky_lensed(), transf, nlev_t, nlev_p,
def __init__(self, k1, k2, parfile, btype, ksource='p'): lmaxphi = 2048 cls_path = os.path.join( os.path.dirname(os.path.abspath(plancklens.__file__)), 'data', 'cls') if ksource == 'p': kswitch = (np.arange(0, lmaxphi + 1, dtype=float) * (np.arange(1, lmaxphi + 2)))**2 / (2. * np.pi) * 1e7 if k1[0] == 'p' and k2[0] == 'p': clpp_fid = utils.camb_clfile( os.path.join( cls_path, 'FFP10_wdipole_lenspotentialCls.dat'))['pp'][:lmaxphi + 1] elif k1[0] == 'x' and k2[0] == 'x': clpp_fid = np.ones(lmaxphi + 1, dtype=float) else: assert 0, 'not implemented' else: kswitch = np.ones(lmaxphi + 1, dtype=float) clpp_fid = np.ones(lmaxphi + 1, dtype=float) clkk_fid = clpp_fid * kswitch qc_resp = parfile.qresp_dd.get_response( k1, ksource)[:lmaxphi + 1] * parfile.qresp_dd.get_response( k2, ksource)[:lmaxphi + 1] bin_lmins, bin_lmaxs, bin_centers = get_blbubc(btype) vlpp_inv = qc_resp * (2 * np.arange(lmaxphi + 1) + 1) * (0.5 * parfile.qcls_dd.fsky1234) vlpp_inv *= utils.cli(kswitch)**2 vlpp_den = [ np.sum(clkk_fid[slice(lmin, lmax + 1)]**2 * vlpp_inv[slice(lmin, lmax + 1)]) for lmin, lmax in zip(bin_lmins, bin_lmaxs) ] fid_bandpowers = np.ones( len(bin_centers )) # We will renormalize that as soon as l_av is calculated. def _get_bil( i, L ): # Bin i window function to be applied to cLpp-like arrays as just described ret = (fid_bandpowers[i] / vlpp_den[i]) * vlpp_inv[L] * clkk_fid[L] * kswitch[L] ret *= (L >= bin_lmins[i]) * (L <= bin_lmaxs[i]) return ret lav = np.zeros(len(bin_centers)) for i, (lmin, lmax) in enumerate(zip(bin_lmins, bin_lmaxs)): w_lav = 1. / np.arange(lmin, lmax + 1)**2 / np.arange( lmin + 1, lmax + 2)**2 lav[i] = np.sum( np.arange(lmin, lmax + 1) * w_lav * _get_bil(i, np.arange(lmin, lmax + 1))) / np.sum( w_lav * _get_bil(i, np.arange(lmin, lmax + 1))) self.k1 = k1 self.k2 = k2 self.ksource = ksource self.parfile = parfile self.fid_bandpowers = np.interp(lav, np.arange(lmaxphi + 1, dtype=float), clkk_fid) self.bin_lmins = bin_lmins self.bin_lmaxs = bin_lmaxs self.bin_lavs = lav self.nbins = len(bin_centers) self.vlpp_den = vlpp_den self.vlpp_inv = vlpp_inv self.clkk_fid = clkk_fid self.kswitch = kswitch self.cls_path = cls_path
def get_ps_data(self, lmin_ss_s4=100, lmax_ss_s4=2048, mc_sims_ss=None, mc_sims_ds=None): ks4 = 'stt' twolpo = 2 * np.arange(lmax_ss_s4 + 1) + 1. filt = np.ones(lmax_ss_s4 + 1, dtype=float) filt[:lmax_ss_s4] *= 0. dd_ptsrc = self.parfile.qcls_dd.get_sim_stats_qcl( ks4, self.parfile.mc_sims_var).mean()[:lmax_ss_s4 + 1] ds_ptsrc = self.parfile.qcls_ds.get_sim_stats_qcl( ks4, self.parfile.mc_sims_bias if mc_sims_ds is None else mc_sims_ds).mean()[:lmax_ss_s4 + 1] ss_ptsrc = self.parfile.qcls_ss.get_sim_stats_qcl( ks4, self.parfile.mc_sims_bias if mc_sims_ss is None else mc_sims_ss).mean()[:lmax_ss_s4 + 1] dat_ptsrc = self.parfile.qcls_dd.get_sim_qcl(ks4, -1)[:lmax_ss_s4 + 1] # This simple PS implementation accepts only identical filtering on each four legs. assert np.all(self.parfile.qcls_dd.qeA.f2map1.ivfs.get_ftl() == self.parfile.qcls_dd.qeA.f2map2.ivfs.get_ftl()) assert np.all(self.parfile.qcls_dd.qeB.f2map1.ivfs.get_ftl() == self.parfile.qcls_dd.qeB.f2map2.ivfs.get_ftl()) assert np.all(self.parfile.qcls_dd.qeA.f2map1.ivfs.get_ftl() == self.parfile.qcls_dd.qeB.f2map1.ivfs.get_ftl()) ftl = self.parfile.qcls_dd.qeA.f2map1.ivfs.get_ftl() qc_resp_ptsrc = nhl.get_nhl(ks4, ks4, {}, {'tt': ftl}, len(ftl) - 1, len(ftl) - 1, lmax_out=lmax_ss_s4)[0]**2 s4_band_norm = 4.0 / np.sum(4.0 * (twolpo[lmin_ss_s4:lmax_ss_s4 + 1] * qc_resp_ptsrc[lmin_ss_s4:lmax_ss_s4 + 1])) s4_cl_dat = s4_band_norm * twolpo * (dat_ptsrc - 4. * ds_ptsrc + 2. * ss_ptsrc) s4_cl_check = s4_band_norm * twolpo * (dd_ptsrc - 2. * ss_ptsrc) s4_cl_systs = s4_band_norm * twolpo * (4. * ds_ptsrc - 4. * ss_ptsrc) # phi-induced PS estimator N1 clpp_fid = utils.camb_clfile( os.path.join(self.cls_path, 'FFP10_wdipole_lenspotentialCls.dat'))['pp'] s4_cl_clpp_n1 = s4_band_norm * twolpo * self.get_n1( k1=ks4, k2=ks4, unnormed=True)[:lmax_ss_s4 + 1] s4_cl_clpp_prim = s4_band_norm * twolpo * self.parfile.qresp_dd.get_response( ks4, self.ksource)[:lmax_ss_s4 + 1]**2 * clpp_fid[:lmax_ss_s4 + 1] s4_band_dat = np.sum((s4_cl_dat - s4_cl_clpp_prim - s4_cl_clpp_n1)[lmin_ss_s4:lmax_ss_s4 + 1]) s4_band_check = np.sum((s4_cl_check - s4_cl_clpp_prim - s4_cl_clpp_n1)[lmin_ss_s4:lmax_ss_s4 + 1]) s4_band_syst = np.abs(np.sum(s4_cl_systs[lmin_ss_s4:lmax_ss_s4 + 1])) Cs2s2 = (s4_cl_dat - s4_cl_clpp_prim - s4_cl_clpp_n1) * utils.cli(twolpo) / s4_band_norm Cs2s2 *= utils.cli(qc_resp_ptsrc[:lmax_ss_s4 + 1]) # reconstucted PS power (with correct normalization) s4_band_sim_stats = [] for i, idx in utils.enumerate_progress(self.parfile.mc_sims_var): ts4_cl = s4_band_norm * twolpo[: lmax_ss_s4 + 1] * \ (self.parfile.qcls_dd.get_sim_qcl(ks4, idx)[:lmax_ss_s4 + 1] - 2. * ss_ptsrc) s4_band_sim_stats.append( np.sum((ts4_cl - s4_cl_clpp_prim - s4_cl_clpp_n1)[lmin_ss_s4:lmax_ss_s4 + 1])) print("ptsrc stats:") print(' fit range = [' + str(lmin_ss_s4) + ', ' + str(lmax_ss_s4) + ']') print(' sim avg has amplitude of ' + ('%.3g +- %0.3g (stat), discrepant from zero at %.3f sigma.' % (s4_band_check, np.std(s4_band_sim_stats) / np.sqrt(len(self.parfile.mc_sims_var)), s4_band_check / np.std(s4_band_sim_stats) * np.sqrt(len(self.parfile.mc_sims_var))))) print(' dat has amplitude of ' + ('%.3g +- %0.3g (stat), signif of %.3f sigma.' % (s4_band_dat, np.std(s4_band_sim_stats), s4_band_dat / np.sqrt(np.var(s4_band_sim_stats))))) qc_resp = self.parfile.qresp_dd.get_response(self.k1, self.ksource) \ * self.parfile.qresp_dd.get_response(self.k2, self.ksource) # PS spectrum response to ks4, using qe.key- source key symmetry of response functions. qlss = self.parfile.qresp_dd.get_response( ks4, self.k1[0]) * self.parfile.qresp_dd.get_response( ks4, self.k2[0]) # Correction to apply to estimated spectrum : pp_cl_ps = s4_band_dat * utils.cli(qc_resp) * qlss return s4_band_dat, s4_band_check, s4_band_syst, s4_band_sim_stats, Cs2s2, pp_cl_ps
beam_fwhm = 6. lmax_qlm = lmax_ivf if ksource in ['p', 'f']: qe_keys = [ksource + 'tt', ksource+'_p', ksource] qe_keys_lab = [ (r'$\hat\phi^{%s}$' if ksource == 'p' else 'f')%l for l in ['TT', 'P.', 'MV']] elif ksource in ['a', 'a_p', 'stt']: qe_keys = [ksource] qe_keys_lab = [ksource] else: assert 0 transf = hp.gauss_beam(beam_fwhm / 60. / 180. * np.pi, lmax=lmax_ivf) cls_len = utils.camb_clfile(os.path.join(cls_path, 'FFP10_wdipole_lensedCls.dat')) cls_weight = utils.camb_clfile(os.path.join(cls_path, 'FFP10_wdipole_lensedCls.dat')) fal_sepTP = {'tt': utils.cli(cls_len['tt'][:lmax_ivf + 1] + (nlev_t / 60. / 180. * np.pi) ** 2 / transf ** 2), 'ee': utils.cli(cls_len['ee'][:lmax_ivf + 1] + (nlev_p / 60. / 180. * np.pi) ** 2 / transf ** 2), 'bb': utils.cli(cls_len['bb'][:lmax_ivf + 1] + (nlev_p / 60. / 180. * np.pi) ** 2 / transf ** 2)} cls_ivfs_sepTP = {'tt':fal_sepTP['tt'].copy(), 'ee':fal_sepTP['ee'].copy(), 'bb':fal_sepTP['bb'].copy(), 'te':cls_len['te'][:lmax_ivf + 1] * fal_sepTP['tt'] * fal_sepTP['ee']} cls_dat = { 'tt': (cls_len['tt'][:lmax_ivf + 1] + (nlev_t / 60. /180. * np.pi) ** 2 / transf ** 2), 'ee': (cls_len['ee'][:lmax_ivf + 1] + (nlev_p / 60. /180. * np.pi) ** 2 / transf ** 2), 'bb': (cls_len['bb'][:lmax_ivf + 1] + (nlev_p / 60. /180. * np.pi) ** 2 / transf ** 2),
def test_w(): cls_path = os.path.join( os.path.dirname(os.path.abspath(plancklens.__file__)), 'data', 'cls') lmax_ivf = 500 lmin_ivf = 100 nlev_t = 35. nlev_p = 35. * np.sqrt(2.) beam_fwhm = 6. lmax_qlm = lmax_ivf for ksource in ['p', 'f']: if ksource in ['p', 'f']: qe_keys = [ksource + 'tt', ksource + '_p', ksource] elif ksource in ['a', 'a_p', 'stt']: qe_keys = [ksource] else: assert 0 transf = hp.gauss_beam(beam_fwhm / 60. / 180. * np.pi, lmax=lmax_ivf) cls_len = utils.camb_clfile( os.path.join(cls_path, 'FFP10_wdipole_lensedCls.dat')) cls_weight = utils.camb_clfile( os.path.join(cls_path, 'FFP10_wdipole_lensedCls.dat')) fal_sepTP = { 'tt': utils.cli(cls_len['tt'][:lmax_ivf + 1] + (nlev_t / 60. / 180. * np.pi)**2 / transf**2), 'ee': utils.cli(cls_len['ee'][:lmax_ivf + 1] + (nlev_p / 60. / 180. * np.pi)**2 / transf**2), 'bb': utils.cli(cls_len['bb'][:lmax_ivf + 1] + (nlev_p / 60. / 180. * np.pi)**2 / transf**2) } cls_ivfs_sepTP = { 'tt': fal_sepTP['tt'].copy(), 'ee': fal_sepTP['ee'].copy(), 'bb': fal_sepTP['bb'].copy(), 'te': cls_len['te'][:lmax_ivf + 1] * fal_sepTP['tt'] * fal_sepTP['ee'] } cls_dat = { 'tt': (cls_len['tt'][:lmax_ivf + 1] + (nlev_t / 60. / 180. * np.pi)**2 / transf**2), 'ee': (cls_len['ee'][:lmax_ivf + 1] + (nlev_p / 60. / 180. * np.pi)**2 / transf**2), 'bb': (cls_len['bb'][:lmax_ivf + 1] + (nlev_p / 60. / 180. * np.pi)**2 / transf**2), 'te': np.copy(cls_len['te'][:lmax_ivf + 1]) } fal_jtTP = utils.cl_inverse(cls_dat) cls_ivfs_jtTP = utils.cl_inverse(cls_dat) for cls in [fal_sepTP, fal_jtTP, cls_ivfs_sepTP, cls_ivfs_jtTP]: for cl in cls.values(): cl[:max(1, lmin_ivf)] *= 0. for qe_key in qe_keys: NG, NC, NGC, NCG = nhl.get_nhl(qe_key, qe_key, cls_weight, cls_ivfs_sepTP, lmax_ivf, lmax_ivf, lmax_out=lmax_qlm) RG, RC, RGC, RCG = qresp.get_response(qe_key, lmax_ivf, ksource, cls_weight, cls_len, fal_sepTP, lmax_qlm=lmax_qlm) if qe_key[1:] in ['tt', '_p']: assert np.allclose(NG[1:], RG[1:], rtol=1e-6), qe_key assert np.allclose(NC[2:], RC[2:], rtol=1e-6), qe_key assert np.all(NCG == 0.) and np.all(NGC == 0.) # for these keys assert np.all(RCG == 0.) and np.all(RGC == 0.) NG, NC, NGC, NCG = nhl.get_nhl(ksource, ksource, cls_weight, cls_ivfs_jtTP, lmax_ivf, lmax_ivf, lmax_out=lmax_qlm) RG, RC, RGC, RCG = qresp.get_response(ksource, lmax_ivf, ksource, cls_weight, cls_len, fal_jtTP, lmax_qlm=lmax_qlm) assert np.allclose(NG[1:], RG[1:], rtol=1e-6), ksource assert np.allclose(NC[2:], RC[2:], rtol=1e-6), ksource assert np.all(NCG == 0.) and np.all(NGC == 0.) # for these keys assert np.all(RCG == 0.) and np.all(RGC == 0.)
def get_N0(beam_fwhm=1.4, nlev_t: float or np.ndarray = 5., nlev_p=None, lmax_CMB: dict or int = 3000, lmin_CMB=100, lmax_out=None, cls_len: dict or None = None, cls_weight: dict or None = None, joint_TP=True, ksource='p'): r"""Example function to calculates reconstruction noise levels for a bunch of quadratic estimators Args: beam_fwhm: beam fwhm in arcmin nlev_t: T white noise level in uK-arcmin (an array of size lmax_CMB can be passed for scale-dependent noise level) nlev_p: P white noise level in uK-arcmin (defaults to root(2) nlevt) (can also be an array) lmax_CMB: max. CMB multipole used in the QE (use a dict with 't' 'e' 'b' keys instead of int to set different CMB lmaxes) lmin_CMB: min. CMB multipole used in the QE lmax_out: max lensing 'L' multipole calculated cls_len: CMB spectra entering the sky response to the anisotropy (defaults to FFP10 lensed CMB spectra) cls_weight: CMB spectra entering the QE weights (defaults to FFP10 lensed CMB spectra) joint_TP: if True include calculation of the N0s for the GMV estimator (incl. joint T and P filtering) ksource: anisotropy source to consider (defaults to 'p', lensing) Returns: N0s array for the lensing gradient and curl modes for the T-only, P-onl and (G)MV estimators Prompted by AL """ if nlev_p is None: nlev_p = nlev_t * np.sqrt(2) if not isinstance(lmax_CMB, dict): lmaxs_CMB = {s: lmax_CMB for s in ['t', 'e', 'b']} else: lmaxs_CMB = lmax_CMB print("Seeing lmax's:") for s in lmaxs_CMB.keys(): print(s + ': ' + str(lmaxs_CMB[s])) lmax_ivf = np.max(list(lmaxs_CMB.values())) lmin_ivf = lmin_CMB lmax_qlm = lmax_out or lmax_ivf cls_path = os.path.join( os.path.dirname(os.path.abspath(plancklens.__file__)), 'data', 'cls') cls_len = cls_len or utils.camb_clfile( os.path.join(cls_path, 'FFP10_wdipole_lensedCls.dat')) cls_weight = cls_weight or utils.camb_clfile( os.path.join(cls_path, 'FFP10_wdipole_lensedCls.dat')) # We consider here TT, Pol-only and the GMV comb if joint_TP is set qe_keys = [ksource + 'tt', ksource + '_p'] if not joint_TP: qe_keys.append(ksource) # Simple white noise model. Can feed here something more fancy if desired transf = hp.gauss_beam(beam_fwhm / 60. / 180. * np.pi, lmax=lmax_ivf) Noise_L_T = (nlev_t / 60. / 180. * np.pi)**2 / transf**2 Noise_L_P = (nlev_p / 60. / 180. * np.pi)**2 / transf**2 # Data power spectra cls_dat = { 'tt': (cls_len['tt'][:lmax_ivf + 1] + Noise_L_T), 'ee': (cls_len['ee'][:lmax_ivf + 1] + Noise_L_P), 'bb': (cls_len['bb'][:lmax_ivf + 1] + Noise_L_P), 'te': np.copy(cls_len['te'][:lmax_ivf + 1]) } for s in cls_dat.keys(): cls_dat[s][min(lmaxs_CMB[s[0]], lmaxs_CMB[s[1]]) + 1:] *= 0. # (C+N)^{-1} filter spectra # For independent T and P filtering, this is really just 1/ (C+ N), diagonal in T, E, B space fal_sepTP = {spec: utils.cli(cls_dat[spec]) for spec in ['tt', 'ee', 'bb']} # Spectra of the inverse-variance filtered maps # In general cls_ivfs = fal * dat_cls * fal^t, with a matrix product in T, E, B space cls_ivfs_sepTP = utils.cls_dot([fal_sepTP, cls_dat, fal_sepTP], ret_dict=True) # For joint TP filtering, fals is matrix inverse fal_jtTP = utils.cl_inverse(cls_dat) # since cls_dat = fals, cls_ivfs = fals. If the data spectra do not match the filter, this must be changed: cls_ivfs_jtTP = utils.cls_dot([fal_jtTP, cls_dat, fal_jtTP], ret_dict=True) for cls in [fal_sepTP, fal_jtTP, cls_ivfs_sepTP, cls_ivfs_jtTP]: for cl in cls.values(): cl[:max(1, lmin_ivf)] *= 0. N0s = {} N0_curls = {} for qe_key in qe_keys: # This calculates the unormalized QE gradient (G), curl (C) variances and covariances: # (GC and CG is zero for most estimators) NG, NC, NGC, NCG = nhl.get_nhl(qe_key, qe_key, cls_weight, cls_ivfs_sepTP, lmax_ivf, lmax_ivf, lmax_out=lmax_qlm) # Calculation of the G to G, C to C, G to C and C to G QE responses (again, cross-terms are typically zero) RG, RC, RGC, RCG = qresp.get_response(qe_key, lmax_ivf, ksource, cls_weight, cls_len, fal_sepTP, lmax_qlm=lmax_qlm) # Gradient and curl noise terms N0s[qe_key] = utils.cli(RG**2) * NG N0_curls[qe_key] = utils.cli(RC**2) * NC if joint_TP: NG, NC, NGC, NCG = nhl.get_nhl(ksource, ksource, cls_weight, cls_ivfs_jtTP, lmax_ivf, lmax_ivf, lmax_out=lmax_qlm) RG, RC, RGC, RCG = qresp.get_response(ksource, lmax_ivf, ksource, cls_weight, cls_len, fal_jtTP, lmax_qlm=lmax_qlm) N0s[ksource] = utils.cli(RG**2) * NG N0_curls[ksource] = utils.cli(RC**2) * NC return N0s, N0_curls