Signal Sims (signalsim.jl)
This script runs signal-only simulations, and then computes spectra from those simulations after applying and then correcting for the mode-coupling of the mask. The point-source holes in the Planck likelihood masks are insufficiently apodized, which violates an assumption in the estimation of analytic covariance matrices. We wish to correct for these, so we generate a bunch of simulations and compare the analytic and sample covariance.
$ julia signalsim.jl example.toml 143 143 TT 10
Configuration
We set up various information we'll need later.
configfile, freq1, freq2, spec, nsims = ARGS
using TOML
using Plots
using Healpix
using JLD2, UUIDs # for saving sim arrays
include("util.jl")
config = TOML.parsefile(configfile)
nside = config["general"]["nside"]
run_name = config["general"]["name"]
spectrapath = joinpath(config["scratch"], "rawspectra")
lmax = nside2lmax(nside)
lmax_planck = min(2508, lmax)
splits = "1", "2" # planck never uses any other splits
("1", "2")
Next, we prepare the input signal used to generate these simulations.
signal11, th = signal_and_theory(freq1, freq1, config)
signal12, th = signal_and_theory(freq1, freq2, config)
signal22, th = signal_and_theory(freq2, freq2, config)
signal = Dict(("1", "1") => signal11, ("1", "2") => signal12, ("2", "2") => signal22)
Dict{Tuple{String, String}, Dict{String, Vector{Float64}}} with 3 entries:
("1", "1") => Dict("TE"=>[0.0, 0.0, 5.9497, 2.75092, 1.47458, 0.848417, 0.513…
("2", "2") => Dict("TE"=>[0.0, 0.0, 5.9497, 2.75092, 1.47458, 0.848417, 0.513…
("1", "2") => Dict("TE"=>[0.0, 0.0, 5.9497, 2.75092, 1.47458, 0.848417, 0.513…
We'll store it in a nice array for access later, when simulating.
𝐂 = zeros(2, 2, lmax+1)
X, Y = spec
inds = 1:(lmax_planck+1)
𝐂[1,1,inds] .= signal[splits[1], splits[1]][X * X][inds]
𝐂[1,2,inds] .= signal[splits[1], splits[2]][X * Y][inds]
𝐂[2,1,inds] .= signal[splits[1], splits[2]][X * Y][inds]
𝐂[2,2,inds] .= signal[splits[2], splits[2]][Y * Y][inds];
Next, we pre-allocate some structures that we'll re-use between simulation iterations.
m1 = PolarizedHealpixMap{Float64, RingOrder}(nside)
m2 = PolarizedHealpixMap{Float64, RingOrder}(nside)
a1 = [Alm(lmax, lmax) for i in 1:3]
a2 = [Alm(lmax, lmax) for i in 1:3]
X, Y = Symbol(spec[1]), Symbol(spec[2])
run_name = config["general"]["name"]
masktype1 = (X == :T) ? "T" : "P"
masktype2 = (Y == :T) ? "T" : "P"
mapid1 = "P$(freq1)hm$(splits[1])"
mapid2 = "P$(freq2)hm$(splits[2])"
"P143hm2"
We'll apply the likelihood masks to the simulations.
maskfile1 = joinpath(config["scratch"], "masks", "$(run_name)_$(mapid1)_mask$(masktype1).fits")
maskfile2 = joinpath(config["scratch"], "masks", "$(run_name)_$(mapid2)_mask$(masktype2).fits")
mask1 = readMapFromFITS(maskfile1, 1, Float64)
mask2 = readMapFromFITS(maskfile2, 1, Float64)
786432-element Healpix.HealpixMap{Float64, Healpix.RingOrder, Vector{Float64}}:
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
⋮
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
Next, we generate the mode-coupling matrix.
if spec == "EE"
@time M = mcm(:EE_BB, map2alm(mask1), map2alm(mask2))
else
@time M = mcm(Symbol(X,Y), map2alm(mask1), map2alm(mask2))
end
768×768 PowerSpectra.SpectralArray{Float64, 2, Matrix{Float64}} with indices 0:767×0:767:
0.357017 0.000339814 0.117123 … 5.82069e-8 6.06314e-8
0.000113271 0.403866 0.000583344 6.33065e-8 2.91225e-8
0.0234246 0.000350006 0.40473 3.81753e-8 3.99319e-8
0.000118934 0.0396167 0.000479359 4.59394e-8 3.06201e-8
0.00554131 0.000281181 0.0425235 3.50858e-8 3.82861e-8
7.35616e-5 0.00889793 0.000390126 … 4.13823e-8 3.08056e-8
0.000819861 0.000186638 0.0101736 3.36894e-8 3.68819e-8
5.24848e-5 0.00145335 0.000267823 3.92369e-8 3.07226e-8
0.000190965 0.000135968 0.00189866 3.38087e-8 3.60675e-8
3.89563e-5 0.000503389 0.000183988 3.84517e-8 3.14946e-8
⋮ ⋱
4.42576e-11 1.24982e-10 2.24952e-10 0.000462722 0.00145018
4.02641e-11 1.33532e-10 2.07558e-10 … 0.0034615 0.000462718
4.47635e-11 1.21595e-10 2.26e-10 0.000559024 0.00346148
4.07986e-11 1.36792e-10 2.03646e-10 0.0159192 0.000559021
4.64303e-11 1.22919e-10 2.22768e-10 0.000701606 0.0159192
4.1147e-11 1.34284e-10 1.99117e-10 0.0537571 0.000701603
4.30947e-11 1.18671e-10 2.14969e-10 … 0.000740051 0.053757
3.79693e-11 1.23887e-10 1.24512e-10 0.395218 0.00074005
3.94993e-11 5.69169e-11 1.30071e-10 0.000739086 0.395218
We write a function that does one iteration of this signal-only simulation and then estimates spectra from it.
# map T,E,B => 1,2,3
channelindex(X) = findfirst(first(X), "TEB")
function sim_iteration(𝐂, m1, m2, a1, a2, M, spec::String)
# get indices of the spectrum
c₁, c₂ = channelindex(spec[1]), channelindex(spec[2])
# zero out alms
for i in 1:3
fill!(a1[i].alm, 0.0)
fill!(a2[i].alm, 0.0)
end
# synthesize polarized spectrum into m1
synalm!(𝐂, [a1[c₁], a2[c₂]])
alm2map!(a1, m1)
alm2map!(a2, m2)
# same signal, but different masks
mask!(m1, mask1, mask1)
mask!(m2, mask2, mask2)
# subtract monopole if TT
if spec[1] == 'T'
monopole, dipole = fitdipole(m1.i * mask1)
subtract_monopole_dipole!(m1.i, monopole, dipole)
end
if spec[2] == 'T'
monopole, dipole = fitdipole(m2.i * mask2)
subtract_monopole_dipole!(m2.i, monopole, dipole)
end
# apply pixel weights and then map2alm
Healpix.applyFullWeights!(m1)
Healpix.applyFullWeights!(m2)
map2alm!(m1, a1; niter=0)
map2alm!(m2, a2; niter=0)
if spec == "EE"
pCl_EE = SpectralVector(alm2cl(a1[c₁], a2[c₂]))
pCl_BB = SpectralVector(zeros(length(pCl_EE)))
@spectra Cl_EE, Cl_BB = M \ [pCl_EE; pCl_BB]
return Cl_EE
end
# otherwise easy mode coupling
pCl_XY = SpectralVector(alm2cl(a1[c₁], a2[c₂]))
return M \ pCl_XY
end
sim_iteration (generic function with 1 method)
Let's take a look at a simulated spectrum. Note the deviation at $\ell > 2n_{\mathrm{side}}$. This is due to pixelization, and is unimportant. For example, in the full Planck analysis we would expect to start seeing these problems at $\ell \sim 2n_{\mathrm{side}} = 4096$.
if "--plot" in ARGS
@time simTT = sim_iteration(𝐂, m1, m2, a1, a2, M, "TT")
plot(simTT .* eachindex(simTT).^2, label="sim")
plot!(signal12["TT"][1:(lmax_planck+1)] .* eachindex(0:lmax_planck).^2,
xlim=(0,lmax_planck), label="input")
end
This script generates many simulations. We'll run this for nsims
iterations and then save the spectra to disk.
simpath = joinpath(config["scratch"], "signalsims", "$(freq1)_$(freq2)_$(spec)")
mkpath(simpath)
for sim_index in 1:parse(Int,nsims)
@time cl = sim_iteration(𝐂, m1, m2, a1, a2, M, spec)
@save "$(simpath)/$(uuid4()).jld2" cl=cl
end
0.884230 seconds (10.64 k allocations: 31.740 MiB)
0.890548 seconds (10.64 k allocations: 31.740 MiB)
0.944940 seconds (10.64 k allocations: 31.740 MiB)
0.926235 seconds (10.64 k allocations: 31.740 MiB)
0.923195 seconds (10.64 k allocations: 31.740 MiB)
0.927628 seconds (10.64 k allocations: 31.740 MiB)
0.930783 seconds (10.64 k allocations: 31.740 MiB)
1.149752 seconds (10.64 k allocations: 31.740 MiB, 19.04% gc time)
0.938677 seconds (10.64 k allocations: 31.740 MiB)
0.976039 seconds (10.64 k allocations: 31.740 MiB)