def euclid_nzs(num_dens): ''' euclid num density = 30 arcmin^-2 = 108,000 deg^-2 In steradians: A steradian is (180/π)^2 square degrees, or 3282.8 deg^2 So Euclid number density = 108,000 * 3282.8 = 354,543,086 galaxies per steradian ''' nz = 1000 # zmin , zmax = 0., 3. # z = np.linspace(zmin, zmax, nz) pz = ccl.PhotoZGaussian(sigma_z0=0.05) dNdz_true = ccl.dNdzSmail(alpha=1.3, beta=1.5, z0=0.65) dNdz_obs = ccl.dNdz_tomog(z=z, zmin=zmin, zmax=zmax, pz_func=pz, dNdz_func=dNdz_true) # scale to the given number density dNdz_obs = dNdz_obs / dNdz_obs.sum() * num_dens nzs = [] for i in range(nbins): # calculate the number density of galaxies per steradian per bin zmin_i, zmax_i = i * (2. / nbins), (i + 1) * (2. / nbins) mask = (z > zmin_i) & (z < zmax_i) nzs.append(dNdz_obs[mask].sum()) return nzs
def euclid_nzs(num_dens): """ Calculate the (AVERAGE?!) number density of sub-sample galaxies per redshift bin num_dens = 354,543,086 galaxies per steradian Euclid num density = 30 arcmin^-2 = 108,000 deg^-2 (arcmin^2 to deg^2 = 60^2) In steradians: A steradian is (180/π)^2 square degrees, or 3282.8 deg^2 So Euclid number density = 108,000 * 3282.8 = 354,543,086 galaxies per steradian Returns nzs --- list --- (AVERAGE?!) number density of galaxies in redshift bin """ # zmin , zmax = 0., 3. # z = np.linspace(zmin, zmax, nz) pz = ccl.PhotoZGaussian(sigma_z0=0.05) dNdz_true = ccl.dNdzSmail(alpha=1.3, beta=1.5, z0=0.65) dNdz_obs = ccl.dNdz_tomog(z=z, zmin=zmin, zmax=zmax, pz_func=pz, dNdz_func=dNdz_true) # scale to the given number density of 30 per arcmin squared dNdz_obs = dNdz_obs / dNdz_obs.sum() * num_dens nzs = [] for i in range(nbins): # calculate the number density of galaxies per steradian per bin zmin_i, zmax_i = i * (2. / nbins), (i + 1) * (2. / nbins) mask = (z > zmin_i) & (z < zmax_i) nzs.append(dNdz_obs[mask].sum()) return nzs
def euclid_nzs(num_dens): """ Calculate the (AVERAGE?!) number density of sub-sample galaxies per redshift bin num_dens = 354,543,086 galaxies per steradian Euclid num density = 30 arcmin^-2 = 108,000 deg^-2 (arcmin^2 to deg^2 = 60^2) In steradians: A steradian is (180/π)^2 square degrees, or 3282.8 deg^2 So Euclid number density = 108,000 * 3282.8 = 354,543,086 galaxies per steradian Returns nzs --- list --- (AVERAGE?!) number density of galaxies in redshift bin """ # integrate over whole z range and # scale to the given number density of 30 per arcmin squared dNdz_obs = ccl.dNdz_tomog(z=z, zmin=zmin, zmax=zmax, pz_func=pz, dNdz_func = dNdz_true) dNdz_obs = dNdz_obs/dNdz_obs.sum() * num_dens nzs = [] for i in range(nbins): # calculate the number density of galaxies per steradian per bin zmin_i, zmax_i = i*(2./nbins), (i+1)*(2./nbins) mask = (z>zmin_i)&(z<zmax_i) nzs.append(dNdz_obs[mask].sum()) return nzs
def euclid_ccl(Omega_c, sigma8=0.83): """ Generate C_ell as function of ell for a given Omega_c and Sigma8 Inputs Omega_c -- float: CDM density Sigma_8 -- float: sigma_8 Assumed global variables z -- np.array: samples of z ells -- np.array: samples of ell dNdz_true -- ccl.dNdzSmail: dNdz distribution pz -- ccl.PhotoZGaussian: PhotoZ error nbins -- the amount of tomographic redshift bins Outputs Cls -- np.array, shape (nbins*(nbins-1)/2 + nbins, len(ell)): Cross/Auto correlation shear spectra for the tomographic bins dNdzs -- np.array, shape (nbins,len(z): dNdz per redshift bin, for all redshifts """ cosmo_fid = ccl.Cosmology(Omega_c=Omega_c, Omega_b=0.045, h=0.71, sigma8=sigma8, n_s=0.963) dNdzs = np.zeros((nbins, z.size)) shears = [] for i in range(nbins): # edges of 1 equal width redshift bins, between 0 and 2 zmin, zmax = i * (2. / nbins), (i + 1) * (2. / nbins) # generate dNdz per bin dNdzs[i, :] = ccl.dNdz_tomog(z=z, zmin=zmin, zmax=zmax, pz_func=pz, dNdz_func=dNdz_true) # calculate the shear per bin gal_shapes = ccl.WeakLensingTracer(cosmo_fid, dndz=(z, dNdzs[i, :])) shears.append(gal_shapes) print('Shears calculated, calculating power spectra now...') # calculate nbin*(nbin+1)/2 = 1 spectra from the shears Cls = [] for i in range(nbins): for j in range(0, i + 1): Cls.append(ccl.angular_cl(cosmo_fid, shears[i], shears[j], ells)) return np.array(Cls), dNdzs
def euclid_ccl(Omega_c, sigma8): """ Generate C_ell as function of ell for a given Omega_c and Sigma8 Assumes a redshift distribution given by z^alpha * exp(z/z0)^beta with alpha=1.3, beta = 1.5 and z0 = 0.65 Assumes photo-z error is Gaussian with a bias is 0.05(1+z) """ cosmo_fid = ccl.Cosmology(Omega_c=Omega_c, Omega_b=0.045, h=0.71, sigma8=sigma8, n_s=0.963) ell = np.logspace(np.log10(100), np.log10(6000), 10) pz = ccl.PhotoZGaussian(sigma_z0=0.05) dNdz_true = ccl.dNdzSmail(alpha=1.3, beta=1.5, z0=0.65) dNdzs = np.zeros((nbins, z.size)) shears = [] for i in range(nbins): # edges of nbins equal width redshift bins, between 0 and 2 zmin, zmax = i * (2. / nbins), (i + 1) * (2. / nbins) # generate dNdz per bin dNdzs[i, :] = ccl.dNdz_tomog(z=z, zmin=zmin, zmax=zmax, pz_func=pz, dNdz_func=dNdz_true) # calculate the shear per bin gal_shapes = ccl.WeakLensingTracer(cosmo_fid, dndz=(z, dNdzs[i, :])) shears.append(gal_shapes) # calculate nbin*(nbin+1)/2 spectra from the shears Cls = [] bin_indices = [ ] # list of length nbin*(nbin+1)/2 containing tuples with the indices of the bins for i in range(nbins): for j in range(0, i + 1): bin_indices.append((i, j)) Cls.append(ccl.angular_cl(cosmo_fid, shears[i], shears[j], ell)) return ell, np.array(Cls), dNdzs, bin_indices
def euclid_ccl(Omega_c, Omega_b=0.045, sigma8=0.83, n_s=0.963, h=0.71, mps='halofit'): """ Generate C_ell as function of ell for a given Omega_c and Sigma8 Inputs Omega_c -- float: CDM density Sigma_8 -- float: sigma_8 mps -- string: model to use for matter power spectrum Assumed global variables z -- np.array: samples of z ells -- np.array: samples of ell dNdz_true -- ccl.dNdzSmail: dNdz distribution pz -- ccl.PhotoZGaussian: PhotoZ error nbins -- the amount of tomographic redshift bins Outputs Cls -- np.array, shape (nbins*(nbins-1)/2 + nbins, len(ell)): Cross/Auto correlation shear spectra for the tomographic bins dNdzs -- np.array, shape (nbins,len(z): dNdz per redshift bin, for all redshifts """ cosmo_fid = ccl.Cosmology(Omega_c=Omega_c, Omega_b=Omega_b, h=h, sigma8=sigma8, n_s=n_s, matter_power_spectrum=mps) dNdzs = np.zeros((nbins, z.size)) shears = [] for i in range(nbins): # edges of 2 redshift bins zmin_b, zmax_b = zbins[i] print (f'Redshift bin {i}: {zmin_b} - {zmax_b}') # generate dNdz per bin dNdzs[i,:] = ccl.dNdz_tomog(z=z, zmin=zmin_b, zmax=zmax_b, pz_func=pz, dNdz_func = dNdz_true) # calculate the shear per bin gal_shapes = ccl.WeakLensingTracer(cosmo_fid, dndz=(z, dNdzs[i,:])) shears.append(gal_shapes) # calculate nbin*(nbin+1)/2 = 3 spectra from the shears Cls = [] for i in range(nbins): for j in range(0,i+1): Cls.append(ccl.angular_cl(cosmo_fid, shears[i], shears[j], ells)) return np.array(Cls), dNdzs
def euclid_nzs(num_dens): ''' Calculate integrated number density per bin, scale to given num_dens Euclid num density = 30 arcmin^-2 ''' nz = 1000 zmin , zmax = 0., 3. z = np.linspace(zmin, zmax,nz) pz = ccl.PhotoZGaussian(sigma_z0=0.05) dNdz_true = ccl.dNdzSmail(alpha = 1.3, beta = 1.5, z0=0.65) dNdz_obs = ccl.dNdz_tomog(z=z, zmin=zmin, zmax=zmax, pz_func=pz, dNdz_func = dNdz_true) # scale to the given number density dNdz_obs = dNdz_obs*num_dens/dNdz_obs.sum() nzs = [] for i in range(10): zmin_i, zmax_i = i*.2 , (i+1)*.2 mask = (z>zmin_i)&(z<zmax_i) nzs.append(dNdz_obs[mask].sum()) #*num_dens) return nzs
def check_lsst_specs(cosmo): """ Check that lsst_specs functions can be run. """ # Types of scale factor input (scalar, list, array) a_scl = 0.5 a_lst = [0.2, 0.4, 0.6, 0.8, 1.] a_arr = np.linspace(0.2, 1., 5) # Types of redshift input z_scl = 0.5 z_lst = [0., 0.5, 1., 1.5, 2.] z_arr = np.array(z_lst) # p(z) function for dNdz_tomog def pz1(z_ph, z_s, args): return np.exp(- (z_ph - z_s)**2. / 2.) # Lambda function p(z) function for dNdz_tomog pz2 = lambda z_ph, z_s, args: np.exp(-(z_ph - z_s)**2. / 2.) # PhotoZFunction classes PZ1 = ccl.PhotoZFunction(pz1) PZ2 = ccl.PhotoZFunction(pz2) # bias_clustering assert_( all_finite(ccl.bias_clustering(cosmo, a_scl)) ) assert_( all_finite(ccl.bias_clustering(cosmo, a_lst)) ) assert_( all_finite(ccl.bias_clustering(cosmo, a_arr)) ) # dNdz_tomog, PhotoZFunction # sigmaz_clustering assert_( all_finite(ccl.sigmaz_clustering(z_scl)) ) assert_( all_finite(ccl.sigmaz_clustering(z_lst)) ) assert_( all_finite(ccl.sigmaz_clustering(z_arr)) ) # sigmaz_sources assert_( all_finite(ccl.sigmaz_sources(z_scl)) ) assert_( all_finite(ccl.sigmaz_sources(z_lst)) ) assert_( all_finite(ccl.sigmaz_sources(z_arr)) ) # dNdz_tomog zmin = 0. zmax = 1. assert_( all_finite(ccl.dNdz_tomog(z_scl, 'nc', zmin, zmax, PZ1)) ) assert_( all_finite(ccl.dNdz_tomog(z_lst, 'nc', zmin, zmax, PZ1)) ) assert_( all_finite(ccl.dNdz_tomog(z_arr, 'nc', zmin, zmax, PZ1)) ) assert_( all_finite(ccl.dNdz_tomog(z_scl, 'nc', zmin, zmax, PZ2)) ) assert_( all_finite(ccl.dNdz_tomog(z_lst, 'nc', zmin, zmax, PZ2)) ) assert_( all_finite(ccl.dNdz_tomog(z_arr, 'nc', zmin, zmax, PZ2)) ) assert_( all_finite(ccl.dNdz_tomog(z_scl, 'wl_fid', zmin, zmax, PZ1)) ) assert_( all_finite(ccl.dNdz_tomog(z_lst, 'wl_fid', zmin, zmax, PZ1)) ) assert_( all_finite(ccl.dNdz_tomog(z_arr, 'wl_fid', zmin, zmax, PZ1)) ) assert_( all_finite(ccl.dNdz_tomog(z_scl, 'wl_fid', zmin, zmax, PZ2)) ) assert_( all_finite(ccl.dNdz_tomog(z_lst, 'wl_fid', zmin, zmax, PZ2)) ) assert_( all_finite(ccl.dNdz_tomog(z_arr, 'wl_fid', zmin, zmax, PZ2)) ) # Argument checking of dNdz_tomog # Wrong dNdz_type assert_raises(ValueError, ccl.dNdz_tomog, z_scl, 'nonsense', zmin, zmax, PZ1) # Wrong function type assert_raises(TypeError, ccl.dNdz_tomog, z_scl, 'nc', zmin, zmax, pz1) assert_raises(TypeError, ccl.dNdz_tomog, z_scl, 'nc', zmin, zmax, z_arr) assert_raises(TypeError, ccl.dNdz_tomog, z_scl, 'nc', zmin, zmax, None)
def check_redshifts(cosmo): """ Check that redshift functions can be run and produce finite values. """ # Types of scale factor input (scalar, list, array) a_scl = 0.5 a_lst = [0.2, 0.4, 0.6, 0.8, 1.] a_arr = np.linspace(0.2, 1., 5) # Types of redshift input z_scl = 0.5 z_lst = [0., 0.5, 1., 1.5, 2.] z_arr = np.array(z_lst) # p(z) function for dNdz_tomog def pz1(z_ph, z_s, args): return np.exp(-(z_ph - z_s)**2. / 2.) # Lambda function p(z) function for dNdz_tomog pz2 = lambda z_ph, z_s, args: np.exp(-(z_ph - z_s)**2. / 2.) # PhotoZFunction classes PZ1 = ccl.PhotoZFunction(pz1) PZ2 = ccl.PhotoZFunction(pz2) PZ3 = ccl.PhotoZGaussian(sigma_z0=0.1) # dNdz (in terms of true redshift) function for dNdz_tomog def dndz1(z, args): return z**1.24 * np.exp(-(z / 0.51)**1.01) # dNdzFunction classes dNdZ1 = ccl.dNdzFunction(dndz1) dNdZ2 = ccl.dNdzSmail(alpha=1.24, beta=1.01, z0=0.51) # Check that dNdz_tomog is finite with the various combinations # of PhotoZ and dNdz functions zmin = 0. zmax = 1. assert_(all_finite(ccl.dNdz_tomog(z_scl, zmin, zmax, PZ1, dNdZ1))) assert_(all_finite(ccl.dNdz_tomog(z_lst, zmin, zmax, PZ1, dNdZ1))) assert_(all_finite(ccl.dNdz_tomog(z_arr, zmin, zmax, PZ1, dNdZ1))) assert_(all_finite(ccl.dNdz_tomog(z_scl, zmin, zmax, PZ2, dNdZ1))) assert_(all_finite(ccl.dNdz_tomog(z_lst, zmin, zmax, PZ2, dNdZ1))) assert_(all_finite(ccl.dNdz_tomog(z_arr, zmin, zmax, PZ2, dNdZ1))) assert_(all_finite(ccl.dNdz_tomog(z_scl, zmin, zmax, PZ3, dNdZ1))) assert_(all_finite(ccl.dNdz_tomog(z_lst, zmin, zmax, PZ3, dNdZ1))) assert_(all_finite(ccl.dNdz_tomog(z_arr, zmin, zmax, PZ3, dNdZ1))) assert_(all_finite(ccl.dNdz_tomog(z_scl, zmin, zmax, PZ1, dNdZ2))) assert_(all_finite(ccl.dNdz_tomog(z_lst, zmin, zmax, PZ1, dNdZ2))) assert_(all_finite(ccl.dNdz_tomog(z_arr, zmin, zmax, PZ1, dNdZ2))) assert_(all_finite(ccl.dNdz_tomog(z_scl, zmin, zmax, PZ2, dNdZ2))) assert_(all_finite(ccl.dNdz_tomog(z_lst, zmin, zmax, PZ2, dNdZ2))) assert_(all_finite(ccl.dNdz_tomog(z_arr, zmin, zmax, PZ2, dNdZ2))) assert_(all_finite(ccl.dNdz_tomog(z_scl, zmin, zmax, PZ3, dNdZ2))) assert_(all_finite(ccl.dNdz_tomog(z_lst, zmin, zmax, PZ3, dNdZ2))) assert_(all_finite(ccl.dNdz_tomog(z_arr, zmin, zmax, PZ3, dNdZ2))) # Wrong function type assert_raises(TypeError, ccl.dNdz_tomog, z_scl, zmin, zmax, pz1, z_arr) assert_raises(TypeError, ccl.dNdz_tomog, z_scl, zmin, zmax, z_arr, dNdZ1) assert_raises(TypeError, ccl.dNdz_tomog, z_scl, zmin, zmax, None, None)
def test_redshift_analytic(): """ Compare the redshift functions to an analytic toy case. """ # Redshift input z_lst = [0., 0.5, 1., 1.5, 2.] # p(z) function for which we can calculate and analytic dNdz_tomog # when pairs with the toy dndz_ana below def pz_ana(z_ph, z_s, args): return (np.exp(- (z_ph - z_s)**2. / 2. / 0.1**2) / np.sqrt(2. * np.pi) / 0.1) # PhotoZFunction class PZ_ana = ccl.PhotoZFunction(pz_ana) # Introduce an unrealistic, simple true function for dNdz for which # we can calculate dNdz_tomog analytically for comparison. def dndz_ana(z, args): if ((z>=0.) and (z<=1.0)): return 1.0 else: return 0. # import math erf and erfc functions: from math import erf, erfc # Return the analytic result for dNdz_tomog using # dndz_ana with a Gaussian p(z,z'), to which we compare. # This is obtained by analytically evaluating (in pseudo-latex): # \frac{ dndz_ana(z) \int_{zpmin}^{zpmax} dz_p pz_ana(z_p, z_s)} # {\int_{0}^{1} dz_s dndz_ana(z_s) \int_{zpmin}^{zpmax} dz_p # pz_ana(z_p, z_s)} # (which we did using Wolfram Mathematica) def dNdz_tomog_analytic(z, sigz, zmin, zmax): if ( (z>=0.) and (z<=1.0) ): return (erf((z-zmin) / np.sqrt(2.)/sigz) - erf((z-zmax)/ np.sqrt(2.)/sigz)) / (-1. + (-np.exp(-(zmax-1)**2 / 2. / sigz**2) + np.exp(-zmax**2 / 2. / sigz**2) + np.exp(-(zmin-1)**2 / 2. / sigz**2) - np.exp( -zmin**2 / 2 /sigz**2)) * np.sqrt(2. / np.pi)*sigz + erf( (zmax-1) / np.sqrt(2.) / sigz) + erfc( (zmin-1.)/np.sqrt(2.)/sigz) + zmax * (erf(zmax/ np.sqrt(2.)/sigz) - erf( (zmax-1.) / np.sqrt(2.)/ sigz)) + zmin * ( erf( (zmin-1.)/np.sqrt(2.) / sigz) - erf(zmin/np.sqrt(2.)/sigz))) else: return 0. # dNdzFunction class dNdZ_ana = ccl.dNdzFunction(dndz_ana) # Check that for the analytic case introduced above, we get the # correct value. zmin = 0. zmax = 1. # math erf funcs are not vectorized so loop over z values for z in z_lst: assert_allclose(ccl.dNdz_tomog(z, zmin, zmax, PZ_ana, dNdZ_ana), dNdz_tomog_analytic(z, 0.1, zmin, zmax), rtol=TOLERANCE)
def test_redshift_numerical(): """ Compare the redshift functions to a high precision integral. """ # Redshift input z_lst = [0., 0.5, 1., 1.5, 2.] # p(z) function for dNdz_tomog def pz1(z_ph, z_s, args): return np.exp(- (z_ph - z_s)**2. / 2.) # Lambda function p(z) function for dNdz_tomog pz2 = lambda z_ph, z_s, args: np.exp(-(z_ph - z_s)**2. / 2.) # Set up a function equivalent to the PhotoZGaussian def pz3(z_ph, z_s, sigz): sig = sigz*(1.+ z_s) return (np.exp(- (z_ph - z_s)**2. / 2. / sig**2) / np.sqrt(2. *np.pi) / sig) # PhotoZFunction classes PZ1 = ccl.PhotoZFunction(pz1) PZ2 = ccl.PhotoZFunction(pz2) PZ3 = ccl.PhotoZGaussian(sigma_z0=0.1) # dNdz (in terms of true redshift) function for dNdz_tomog def dndz1(z, args): return z**1.24 * np.exp(- (z / 0.51)**1.01) # dNdzFunction classes dNdZ1 = ccl.dNdzFunction(dndz1) dNdZ2 = ccl.dNdzSmail(alpha = 1.24, beta = 1.01, z0 = 0.51) # Do the integral in question directly in numpy at high precision zmin = 0. zmax = 1. zp = np.linspace(zmin, zmax, 10000) zs = np.linspace(0., 5., 10000) # Assume any dNdz does not extend # above z=5 denom_zp_1 =np.asarray([np.trapz(pz1(zp, z, []), zp) for z in zs]) denom_zp_2 =np.asarray([np.trapz(pz2(zp, z, []), zp) for z in zs]) denom_zp_3 =np.asarray([np.trapz(pz3(zp, z, 0.1), zp) for z in zs]) np_dndz_1 = ([ dndz1(z, []) * np.trapz(pz1(zp, z, []), zp) / np.trapz(dndz1(zs, []) * denom_zp_1, zs) for z in z_lst]) np_dndz_2 = ([ dndz1(z, []) * np.trapz(pz2(zp, z, []), zp) / np.trapz(dndz1(zs, []) * denom_zp_2, zs) for z in z_lst]) np_dndz_3 = ([ dndz1(z, []) * np.trapz(pz3(zp, z, 0.1), zp) / np.trapz(dndz1(zs, []) * denom_zp_3, zs) for z in z_lst]) # Check that for the analytic case introduced above, we get the # correct value. for i in range(0, len(z_lst)): assert_allclose(ccl.dNdz_tomog(z_lst[i], zmin, zmax, PZ1, dNdZ1), np_dndz_1[i], rtol=TOLERANCE) assert_allclose(ccl.dNdz_tomog(z_lst[i], zmin, zmax, PZ1, dNdZ2), np_dndz_1[i], rtol=TOLERANCE) assert_allclose(ccl.dNdz_tomog(z_lst[i], zmin, zmax, PZ2, dNdZ1), np_dndz_2[i], rtol=TOLERANCE) assert_allclose(ccl.dNdz_tomog(z_lst[i], zmin, zmax, PZ2, dNdZ2), np_dndz_2[i], rtol=TOLERANCE) assert_allclose(ccl.dNdz_tomog(z_lst[i], zmin, zmax, PZ3, dNdZ1), np_dndz_3[i], rtol=TOLERANCE) assert_allclose(ccl.dNdz_tomog(z_lst[i], zmin, zmax, PZ3, dNdZ2), np_dndz_3[i], rtol=TOLERANCE)
# ############### # Can also compute C_ell for galaxy counts, galaxy lensing and CMB lensing # for autocorrelations or any cross-correlation z_pz = np.linspace(0.3, 3., 3) # Define the edges of the photo-z bins. # array([0.3 , 1.65, 3. ]) AKA 2 bins pz = ccl.PhotoZGaussian(sigma_z0=0.05) def dndz(z,args) : return z**2*np.exp(-(z/0.5)**1.5) redshift_dist=ccl.dNdzFunction(dndz) # galaxy counts dNdz_nc = [ccl.dNdz_tomog(z=z, zmin=z_pz[zi], zmax=z_pz[zi+1], pz_func=pz , dNdz_func=redshift_dist) for zi in range(0, len(z_pz)-1)] # galaxy lensing dNdz_len = [ccl.dNdz_tomog(z=z, zmin=z_pz[zi], zmax=z_pz[zi+1], pz_func=pz , dNdz_func=redshift_dist) for zi in range(0, len(z_pz)-1)] # assume linear bias bias = 2.*np.ones(len(z)) gal_counts = ([ccl.NumberCountsTracer(cosmo, has_rsd=False, dndz=(z, dNdz_nc[zi]), bias=(z, bias)) for zi in range(0, len(z_pz)-1)]) gal_lens = ([ccl.WeakLensingTracer(cosmo, dndz=(z, dNdz_len[zi])) for zi in range(0, len(z_pz)-1)]) # tracer objects for CMB lensing cmb_lens = [ccl.CMBLensingTracer(cosmo, z_source=1089.)]