Corrections for Point Sources (corrections.jl)

We need to estimate a correction to the covariance matrix due to insufficiently apodized point source holes in the likelihood mask, and we had generated spectra in signalsim. Now we will estimate a smooth correction from these spectra, by calculating the analytic covariance and then using a Gaussian process to smoothly interpolate the deviation from 1 between the sample covariance and the analytic covariance.

$ julia corrections.jl example.toml 143 143 TT

Configuration

As usual, we set up the various information we'll need later.

configfile, freq1, freq2, spec = ARGS

freqs = [freq1, freq2]
splits = ["1", "2"]
print("$(freqs[1]), $(freqs[2]), $(spec)\n")

# We choose to never do ET, we'll just simulate TE but swap the maps.
transposed = (spec == "ET")
if transposed
    spec = "TE"
    splits = reverse(splits)
    freqs = reverse(freqs)
end

using Plots
using Healpix
using PowerSpectra
using TOML
using UUIDs, JLD2, FileIO
using Statistics
using GaussianProcesses

include("util.jl")

config = TOML.parsefile(configfile)
nside = config["general"]["nside"]
run_name = config["general"]["name"]
simpath = joinpath(config["scratch"], "signalsims",
    "$(freq1)_$(freq2)_$(spec)")
lmax = nside2lmax(nside)
lmax_planck = min(2508, lmax)
767

We'll load the existing simulated spectra from disk.

function readsims(path)
    files = [f for f in readdir(simpath; join=false) if f != "summary.jld2"]
    test_spec_ = load(joinpath(path,files[1]), "cl")
    cl_array = zeros((length(test_spec_), length(files)))
    for (i,f) in enumerate(files)
        cl_array[:,i] .= parent(load(joinpath(path,f), "cl"))
    end
    return cl_array
end


@time cl_array = readsims(simpath)
cl_mean = mean(cl_array, dims=2)
cl_var = var(cl_array, dims=2)
@save joinpath(simpath, "summary.jld2") cl_mean cl_var

signal11, th = signal_and_theory(freq1, freq1, config)
signal12, th = signal_and_theory(freq1, freq2, config)
signal21, th = signal_and_theory(freq2, freq1, config)
signal22, th = signal_and_theory(freq2, freq2, config)
(Dict("TE" => [0.0, 0.0, 5.94969686942062, 2.7509157103624298, 1.474579711071198, 0.8484170459518878, 0.5133839618849767, 0.3259405744091539, 0.21810157931696741, 0.15439615404796334  …  -3.371934880663754e-6, -3.372986717016007e-6, -3.3745077231190236e-6, -3.376587070494609e-6, -3.3790630196926563e-6, -3.38218504386031e-6, -3.385651323499353e-6, -3.389691055273055e-6, -3.3940830169080422e-6, -3.3989262130620895e-6], "ET" => [0.0, 0.0, 5.94969686942062, 2.7509157103624298, 1.474579711071198, 0.8484170459518878, 0.5133839618849767, 0.3259405744091539, 0.21810157931696741, 0.15439615404796334  …  -3.371934880663754e-6, -3.372986717016007e-6, -3.3745077231190236e-6, -3.376587070494609e-6, -3.3790630196926563e-6, -3.38218504386031e-6, -3.385651323499353e-6, -3.389691055273055e-6, -3.3940830169080422e-6, -3.3989262130620895e-6], "EE" => [0.0, 0.0, 1.259572042012656, 0.48455705177483216, 0.24335423862793176, 0.14094070573132123, 0.08980646519341885, 0.06148186807264802, 0.044446533787336574, 0.033457540787315714  …  8.457772862047944e-6, 8.454204955605177e-6, 8.450619967918835e-6, 8.447078141478032e-6, 8.44344905119932e-6, 8.439803028508405e-6, 8.43606004534395e-6, 8.43239035465071e-6, 8.428573937589084e-6, 8.424740954615954e-6], "TT" => [0.0, 0.0, 1303.6832491814655, 588.6901144664341, 326.77976561475975, 205.70604152979342, 141.0400847789298, 102.84165006948633, 78.4819516937242, 62.05775766063433  …  0.00010789386244007327, 0.00010757534769610053, 0.00010725981794470818, 0.00010694525973150039, 0.00010663297428151154, 0.00010632285628185678, 0.00010601590174247024, 0.00010570960313242752, 0.00010540485854981548, 0.00010510376143781316]), Dict("TE" => [0.0, 0.0, 2.7410710061836303, 1.538364618634338, 0.8666585989752018, 0.49257031215634367, 0.28364841670661556, 0.167248294900734, 0.10292206599010562, 0.06757831087917948  …  -2.958512853138577e-6, -2.9598952948706985e-6, -2.961746509944255e-6, -2.9641556705145465e-6, -2.966961037763696e-6, -2.970412085469816e-6, -2.9742069947643985e-6, -2.978574962939165e-6, -2.9832947683479433e-6, -2.988465416274458e-6], "ET" => [0.0, 0.0, 2.7410710061836303, 1.538364618634338, 0.8666585989752018, 0.49257031215634367, 0.28364841670661556, 0.167248294900734, 0.10292206599010562, 0.06757831087917948  …  -2.958512853138577e-6, -2.9598952948706985e-6, -2.961746509944255e-6, -2.9641556705145465e-6, -2.966961037763696e-6, -2.970412085469816e-6, -2.9742069947643985e-6, -2.978574962939165e-6, -2.9832947683479433e-6, -2.988465416274458e-6], "EE" => [0.0, 0.0, 0.03234028781433917, 0.020781792483129162, 0.01083730084967642, 0.004836816993417869, 0.0019374949892939108, 0.0007854228473397264, 0.0003928177640878598, 0.00025154872003633637  …  2.9108320503513206e-6, 2.9116999222465746e-6, 2.912545394219194e-6, 2.9134287172579747e-6, 2.914219474762186e-6, 2.914988006623316e-6, 2.9156542932289212e-6, 2.916388595955796e-6, 2.9169709043795734e-6, 2.9175313873555934e-6], "TT" => [0.0, 0.0, 1064.7171662281169, 504.6062772110218, 286.70425884072733, 183.1500345955494, 126.93664956689628, 93.35938208515364, 71.75877586343385, 57.093419930748745  …  7.920122279308883e-5, 7.890565298286018e-5, 7.861304065330096e-5, 7.832137239398163e-5, 7.803194947381507e-5, 7.774466662370739e-5, 7.746051989729621e-5, 7.717700180727358e-5, 7.689501049525576e-5, 7.661663944786449e-5]))

Let's take a look at the results of the simulations compared to the signal.

if "--plot" ∈ ARGS
    plot(cl_mean[1:lmax_planck+1] .* collect(0:lmax_planck).^2,
        yerr=(sqrt.(cl_var[1:lmax_planck+1])  .* collect(0:lmax_planck).^2),
        label="sims", alpha=0.5)
    plot!(signal12[spec] .* collect(0:length(signal11[spec])-1).^2, xlim=(0,2nside),
        label="injected signal")
end

Next, we'll estimate the analytic signal-only covariance matrix. This requires the masks, the signal spectrum, and some zero arrays for the pixel variances.

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])"

maskfileT₁ = joinpath(config["scratch"], "masks", "$(run_name)_$(mapid1)_maskT.fits")
maskfileP₁ = joinpath(config["scratch"], "masks", "$(run_name)_$(mapid1)_maskP.fits")
maskfileT₂ = joinpath(config["scratch"], "masks", "$(run_name)_$(mapid2)_maskT.fits")
maskfileP₂ = joinpath(config["scratch"], "masks", "$(run_name)_$(mapid2)_maskP.fits")

maskT₁ = readMapFromFITS(maskfileT₁, 1, Float64)
maskP₁ = readMapFromFITS(maskfileP₁, 1, Float64)
maskT₂ = readMapFromFITS(maskfileT₂, 1, Float64)
maskP₂ = readMapFromFITS(maskfileP₂, 1, Float64);

zero_var_component = HealpixMap{Float64, RingOrder}(zeros(nside2npix(nside)))
zero_var = PolarizedHealpixMap(zero_var_component, zero_var_component, zero_var_component);

m1_signal = CovField(mapid1, maskT₁, maskP₁, zero_var)
m2_signal = CovField(mapid2, maskT₂, maskP₂, zero_var)


# convert a Vector (starting from 0) into a SpectralVector (i.e. a 0-indexed vector)
function format_signal(v::Vector{T}, nside) where T
    result = SpectralVector(zeros(T, nside2lmax(nside)+1))
    last_ind = min(length(v), length(result)) - 1
    for ℓ in 0:last_ind
        result[ℓ] = v[ℓ + 1]
    end
    return result
end


spectra = Dict{SpectrumName, SpectralVector{Float64, Vector{Float64}}}(
    (:TT, mapid1, mapid1) => format_signal(signal11["TT"], nside),
    (:TT, mapid1, mapid2) => format_signal(signal12["TT"], nside),
    (:TT, mapid2, mapid1) => format_signal(signal21["TT"], nside),
    (:TT, mapid2, mapid2) => format_signal(signal22["TT"], nside),

    (:EE, mapid1, mapid1) => format_signal(signal11["EE"], nside),
    (:EE, mapid1, mapid2) => format_signal(signal12["EE"], nside),
    (:EE, mapid2, mapid1) => format_signal(signal21["EE"], nside),
    (:EE, mapid2, mapid2) => format_signal(signal22["EE"], nside),

    (:TE, mapid1, mapid1) => format_signal(signal11["TE"], nside),
    (:TE, mapid1, mapid2) => format_signal(signal12["TE"], nside),
    (:TE, mapid2, mapid1) => format_signal(signal21["TE"], nside),
    (:TE, mapid2, mapid2) => format_signal(signal22["TE"], nside),
);

workspace_signal = CovarianceWorkspace(m1_signal, m2_signal, m1_signal, m2_signal);

@time C = coupledcov(Symbol(spec), Symbol(spec), workspace_signal, spectra)

symspec = (spec == "EE") ? :M⁺⁺ : Symbol(spec)
mask₁ = (spec[1] == 'T') ? maskT₁ : maskP₁
mask₂ = (spec[2] == 'T') ? maskT₂ : maskP₂
@time 𝐌 = mcm(symspec, mask₁, mask₂)
C_decoupled = decouple_covmat(C, 𝐌, 𝐌)
768×768 PowerSpectra.SpectralArray{Float64, 2, Matrix{Float64}} with indices 0:767×0:767:
     1.68222e5       661.633       …   7.56077e-5    7.80722e-5
   661.633         12300.7             1.48415e-5    9.78514e-6
    -5.27774e5     -1722.57           -0.000231177  -0.000238227
  -275.604        -54746.8            -6.24081e-5   -4.04927e-5
 33485.7              63.8839          2.36566e-6    9.7496e-7
   274.778          2661.79        …  -3.00607e-6   -2.36023e-6
  7624.72             51.1977          1.48637e-6    1.3182e-6
   187.203           865.944          -3.04772e-7   -2.90548e-7
   264.239            19.0491         -3.85263e-7   -4.06246e-7
   113.23             59.8835         -3.20625e-7   -2.59605e-7
     ⋮                             ⋱                
     0.000151648       2.00745e-5     -1.27006e-9   -1.19048e-9
     0.000140257       2.35652e-5  …  -2.48684e-9   -1.27128e-9
     0.000155043       2.18833e-5     -1.50434e-9   -2.48796e-9
     0.00014011        2.31245e-5     -4.95303e-8   -1.50747e-9
     0.000156289       2.27197e-5     -2.09576e-9   -4.95174e-8
     0.000159278       2.50791e-5     -2.35794e-7   -2.15994e-9
     0.000174445       1.22594e-5  …  -2.2741e-9    -2.35708e-7
     7.56077e-5        1.48415e-5      1.98377e-6   -2.57731e-9
     7.80722e-5        9.78514e-6     -2.57731e-9    1.98288e-6

Once a covariance matrix has been estimated, it needs to be corrected for mode-coupling just like the spectra. The covariance is possibly transposed, but we only care about the diagonal.

correction = SpectralVector(cl_var[:,1] ./ diag(parent(C_decoupled)))
768-element PowerSpectra.SpectralVector{Float64, Vector{Float64}} with indices 0:767:
 0.5085376068387355
 0.6276401494475211
 0.4598645614839412
 0.5910420663000585
 0.8939125978283793
 0.7087940102599101
 1.1491497212592383
 0.8982054699336028
 1.1372377130239266
 1.0526959365593267
 ⋮
 1.27757174042366
 1.2141564109948548
 1.2793711133043584
 1.4521272200022348
 1.1625663440691654
 1.2003946266076808
 1.0368921791953223
 1.023613251148139
 1.0021097983081189

Now it's finally time to fit a smooth interpolation over the noisy correction we just estimated.

lmin_cut, lmax_cut = get_plic_ellrange(Symbol(spec), freq1, freq2)
correction[lmax_cut:end] .= 1.0

fit_ells = intersect((lmin_cut-20):(lmax_cut), 0:lmax)
u = correction[fit_ells] .- 1
t = 1.0 * collect(fit_ells)
758-element Vector{Float64}:
  10.0
  11.0
  12.0
  13.0
  14.0
  15.0
  16.0
  17.0
  18.0
  19.0
   ⋮
 759.0
 760.0
 761.0
 762.0
 763.0
 764.0
 765.0
 766.0
 767.0

We use a really generic Gaussian kernel.

se = SE(0.0, 0.0)
m = MeanZero()
gp = GP(t,u,m,se)

using Optim
optimize!(gp)   # Optimise the hyperparameters
correction_gp = predict_y(gp, float.(0:(lmax_cut+20)))[1];
correction_gp[1:(lmin_cut-21)] .= 0.0

correction_path = joinpath(config["scratch"], "point_source_corrections")
mkpath(correction_path)

if transposed
    revspec = reverse(spec)
    correction_file = joinpath(correction_path, "$(freq1)_$(freq2)_$(revspec)_corr.dat")
else
    correction_file = joinpath(correction_path, "$(freq1)_$(freq2)_$(spec)_corr.dat")
end
writedlm(correction_file, correction_gp)

This smooth curve will be applied to the diagonal of the covariance matrices later.

if "--plot" ∈ ARGS
    plot(correction_gp)
end