[1]:
import os

os.environ[
    "OMP_NUM_THREADS"
] = "64"  # for jupyter.nersc.gov otherwise the notebook only uses 2 cores
[2]:
from pathlib import Path
import healpy as hp
import matplotlib.pyplot as plt
import numpy as np
import pymaster as nmt
from astropy.io import fits

%matplotlib inline
[3]:
plt.style.use("seaborn-talk")
[4]:
import pysm3 as pysm
import pysm3.units as u
[5]:
nside = 2048
lmax = 2048
[6]:
comp = "IQU"
[7]:
components = list(enumerate(comp))
components
[7]:
[(0, 'I'), (1, 'Q'), (2, 'U')]
[8]:
spectra_components = ["TT", "EE", "BB"]

change this to True if you want to run namaster on notebook

[9]:
namaster_on_nb = True
[10]:
datadir = Path("data/")
[11]:
proddatadir = Path("production-data") / "dust_gnilc" / "raw"

Setting the inputs

Dust maps

[12]:
dust_varresI = datadir / "COM_CompMap_Dust-GNILC-F353_2048_21p8acm.fits"
[13]:
if not dust_varresI.exists():
    !wget -O $dust_varresI https://portal.nersc.gov/project/cmb/pysm-data/dust_gnilc/inputs/COM_CompMap_Dust-GNILC-F353_2048_21p8acm.fits

Transform maps to double precision for computations

[14]:
I_planck_varres, h = hp.read_map(dust_varresI, dtype=np.float64, h=True)

Maps from the two releases are in different units MJy/sr the former, and K_CMB the latter, we therefore need to perform some conversion to uK_RJ.

[15]:
I_planck_varres <<= u.MJy / u.sr
I_planck_varres = I_planck_varres.to(
    "uK_RJ", equivalencies=u.cmb_equivalencies(353 * u.GHz)
)
[16]:
output_nside = 8192
output_lmax = 2 * output_nside

Amplitude modulation

[17]:
modulate_amp = hp.alm2map(
    hp.read_alm(
        proddatadir / "gnilc_dust_temperature_modulation_alms_lmax768.fits.gz",
    ).astype(np.complex128),
    nside=output_nside,
)

Injecting small scales to Spectral parameters

[18]:
tdfilename = "COM_CompMap_Dust-GNILC-Model-Temperature_2048_R2.01.fits"
tdfile = datadir / tdfilename

if not tdfile.exists():
    !wget -O $tdfile  https://irsa.ipac.caltech.edu/data/Planck/release_2/all-sky-maps/maps/component-maps/foregrounds/$tdfilename

bdfilename = "COM_CompMap_Dust-GNILC-Model-Spectral-Index_2048_R2.01.fits"
bdfile = datadir / bdfilename

if not bdfile.exists():
    !wget -O $bdfile http://pla.esac.esa.int/pla/aio/product-action?MAP.MAP_ID=$bdfilename
[19]:
td = hp.read_map(tdfile, dtype=np.float64)
bd = hp.read_map(bdfile, dtype=np.float64)
[20]:
cltd = hp.anafast(td, use_pixel_weights=True, lmax=lmax)
clbd = hp.anafast(bd, use_pixel_weights=True, lmax=lmax)

cl = {"bd": clbd, "td": cltd}
dust_params = list(cl.keys())
ell = np.arange(lmax + 1)
[21]:
output_ell = np.arange(output_lmax + 1)
[22]:
from scipy.optimize import curve_fit
[22]:
def model(ell, A, gamma):
    out = A * ell**gamma
    out[:2] = 0
    return out
[23]:
ell_fit_low = {"bd": 200, "td": 100}
ell_fit_high = {"bd": 400, "td": 400}

A_fit, gamma_fit, A_fit_std, gamma_fit_std = {}, {}, {}, {}
plt.figure(figsize=(25, 5))

for ii, pol in enumerate(dust_params):
    plt.subplot(131 + ii)
    xdata = np.arange(ell_fit_low[pol], ell_fit_high[pol])
    ydata = cl[pol][xdata]
    (A_fit[pol], gamma_fit[pol]), cov = curve_fit(model, xdata, ydata)

    A_fit_std[pol], gamma_fit_std[pol] = np.sqrt(np.diag(cov))
    plt.loglog(ell, cl[pol], label=" Anafast $C_\ell$")

    plt.plot(
        ell[ell_fit_low[pol] // 2 : ell_fit_high[pol] * 2],
        model(
            ell[ell_fit_low[pol] // 2 : ell_fit_high[pol] * 2],
            A_fit[pol],
            gamma_fit[pol],
        ),
        label="model fit",
    )

    plt.axvline(
        ell_fit_low[pol],
        linestyle="--",
        color="black",
        label="$ \ell={} $".format(ell_fit_low[pol]),
    )
    plt.axvline(
        ell_fit_high[pol],
        linestyle="--",
        color="gray",
        label="$ \ell={} $".format(ell_fit_high[pol]),
    )
    plt.legend()
    plt.grid()
    plt.title(
        f"{pol}   spectrum for dust   " + r"$\gamma_{fit}=$" + f"{gamma_fit[pol]:.2f}"
    )

    plt.ylabel("$ C_\ell $")
    plt.xlabel(("$\ell$"))
    plt.xlim(2, lmax)
../_images/preprocess-templates_gnilc_dust_spectralindex_Tdust_29_0.png
  • We inject smaller angular scales to the maps by extrapolating the power law fitted from the GNILC spectral parameter maps

  • Smaller angular scales are modulated similarly as the intensity map in pol tens formalism.

  • the multipoles where the fit is performed are different given the observed spectra . In any case we don’t fit beyond \(\ell=400\), which is consistent with the TT analysis above

  • given the fact that we inject smaller angular scales with a steeper spectral index than TT

    \[\gamma_{\beta} = -1.96, \gamma_{Td} = -2.47, \gamma_{TT}= -1.29\]

we don’t expect to injecti small scale noise when rescaling at frequencies orders of magnitude lower or larger than the reference one ( 353 GHz).

[24]:
def sigmoid(x, x0, width, power=4):
    """Sigmoid function given start point and width
    Parameters
    ----------
    x : array
        input x axis
    x0 : float
        value of x where the sigmoid starts (not the center)
    width : float
        width of the transition region in unit of x
    power : float
        tweak the steepness of the curve
    Returns
    -------
    sigmoid : array
        sigmoid, same length of x"""
    return 1.0 / (1 + np.exp(-power * (x - x0 - width / 2) / width))

Small scales

[25]:
# filter small scales
small_scales_input_cl = [
    1
    * model(output_ell, A_fit[pol], gamma_fit[pol])
    * (sigmoid(output_ell, ell_fit_high[pol], ell_fit_high[pol] / 10))
    for pol in dust_params
]

names = ["beta", "Td"]

for name, each in zip(names, small_scales_input_cl):
    hp.write_cl(
        proddatadir / f"gnilc_dust_small_scales_{name}_cl_lmax{output_lmax}_2023.06.06.fits",
        each,
        dtype=np.complex128,
        overwrite=True,
    )
    pysm.utils.add_metadata(
        [proddatadir / f"gnilc_dust_small_scales_{name}_cl_lmax{output_lmax}_2023.06.06.fits"],
        unit="K**2" if name == "Td" else "",
    )


np.random.seed(777)
bd_ss_alm = hp.synalm(small_scales_input_cl[0], lmax=output_lmax)
np.random.seed(888)
td_ss_alm = hp.synalm(small_scales_input_cl[1], lmax=output_lmax)


bd_ss = hp.alm2map(bd_ss_alm, nside=output_nside)
td_ss = hp.alm2map(td_ss_alm, nside=output_nside)


bd_ss *= modulate_amp
td_ss *= modulate_amp
/tmp/ipykernel_265480/3589521984.py:2: RuntimeWarning: divide by zero encountered in power
  out = A * ell ** gamma
[27]:
plt.loglog(small_scales_input_cl[0], label="beta small scale spectrum 2023.06.06")
plt.loglog(hp.read_cl("production-data/obsolete/dust_gnilc/pysm_3.4.0b8/gnilc_dust_small_scales_beta_cl_lmax16384.fits.gz"),
           label="beta in pysm 3.4.0beta8")
plt.loglog(small_scales_input_cl[1], label="Td small scale spectrum 2023.06.06")
plt.loglog(hp.read_cl("production-data/obsolete/dust_gnilc/pysm_3.4.0b8/gnilc_dust_small_scales_Td_cl_lmax16384.fits.gz"),
           label="Td in pysm 3.4.0beta8")
plt.legend()
plt.grid();
/global/homes/z/zonca/condanamaster2/lib/python3.8/site-packages/numpy/core/_asarray.py:102: ComplexWarning: Casting complex values to real discards the imaginary part
  return array(a, dtype, copy=False, order=order)
/global/homes/z/zonca/condanamaster2/lib/python3.8/site-packages/numpy/core/_asarray.py:102: ComplexWarning: Casting complex values to real discards the imaginary part
  return array(a, dtype, copy=False, order=order)
../_images/preprocess-templates_gnilc_dust_spectralindex_Tdust_34_1.png
[ ]:

Large scales

[26]:
largescale_lmax = 1024
[27]:
alm_bd = hp.map2alm(bd, lmax=largescale_lmax, use_pixel_weights=True)
alm_td = hp.map2alm(td, lmax=largescale_lmax, use_pixel_weights=True)

sig_func = sigmoid(ell, x0=ell_fit_high["bd"], width=ell_fit_high["bd"] / 10)
bd_LS_alm = hp.almxfl(alm_bd, np.sqrt(1.0 - sig_func))
td_LS_alm = hp.almxfl(alm_td, np.sqrt(1.0 - sig_func))

if False: # do not generate large scales again

    for name, alm in zip(names, [bd_LS_alm, td_LS_alm]):

        hp.write_alm(
            proddatadir
            / f"gnilc_dust_largescale_template_{name}_alm_nside{nside}_lmax{largescale_lmax:d}.fits",
            alm,
            lmax=largescale_lmax,
            out_dtype=np.complex64,
            overwrite=True,
        )

    pysm.utils.add_metadata(
        [
            proddatadir
            / f"gnilc_dust_largescale_template_beta_alm_nside{nside}_lmax{largescale_lmax:d}.fits"
        ],
        coord="G",
        unit="",
    )
    pysm.utils.add_metadata(
        [
            proddatadir
            / f"gnilc_dust_largescale_template_Td_alm_nside{nside}_lmax{largescale_lmax:d}.fits"
        ],
        coord="G",
        unit="K",
    )

bd_ls = hp.alm2map(bd_LS_alm, nside=output_nside)
td_ls = hp.alm2map(td_LS_alm, nside=output_nside)

Final map

[28]:
bd_out = bd_ss + bd_ls
td_out = td_ss + td_ls
[29]:
# hp.write_map(
#     datadir / f"gnilc_dust_beta_map_nside{output_nside}.fits",
#     bd_out,
#     dtype=np.float32,
#     coord="G",
#     column_units="",
#     overwrite=True,
# )
[30]:
# hp.write_map(
#     datadir / f"gnilc_dust_Td_map_nside{output_nside}.fits",
#     td_out,
#     dtype=np.float32,
#     coord="G",
#     column_units="K",
#     overwrite=True,
# )
[31]:
cl_out = {
    "bd": hp.anafast(bd_out, use_pixel_weights=True, lmax=lmax),
    "td": hp.anafast(td_out, use_pixel_weights=True, lmax=lmax),
}
[32]:
for ii, pol in enumerate(dust_params):
    plt.loglog(ell, cl[pol], alpha=0.5, color="C%d" % ii)
    plt.loglog(ell, cl_out[pol], label=f" {pol}   ", color="C%d" % ii)

    plt.legend()
    plt.grid(True)
    plt.plot(
        ell[100:][2:],
        model(ell[100:], A_fit[pol], gamma_fit[pol])[2:],
        "--",
        color="red",
    )
    plt.axvline(ell_fit_high[pol], linestyle="--", color="gray")
    plt.axvline(100, linestyle="--", color="gray")

    plt.ylabel("$ C_\ell $")
    plt.xlabel(("$\ell$"))
    # plt.xlim(350, 500 )
    plt.xlim(2, lmax)
../_images/preprocess-templates_gnilc_dust_spectralindex_Tdust_44_0.png
[33]:
lat = 35
cm = plt.cm.RdBu
plt.figure(figsize=(15, 8))
hp.gnomview(
    bd_out,
    title="Bd  w/ small scales ",
    rot=[0, lat],
    reso=3.75,
    cmap=cm,
    xsize=320,
    sub=234,
    min=1.2,
    max=1.9,
)
hp.gnomview(
    bd_ss, title="small scales ", rot=[0, lat], reso=3.75, xsize=320, cmap=cm, sub=232
)
hp.gnomview(
    I_planck_varres,
    title=" GNILC I MAP  ",
    rot=[0, lat],
    reso=3.75,
    cmap=cm,
    xsize=320,
    sub=233,
)

hp.gnomview(
    (bd),
    title=" Bd  GNILC  ",
    rot=[0, lat],
    reso=3.75,
    xsize=320,
    cmap=cm,
    sub=235,
    min=1.2,
    max=1.9,
)
hp.gnomview(
    bd_ls,
    title="  Bd large scales ",
    rot=[0, lat],
    reso=3.75,
    cmap=cm,
    xsize=320,
    sub=231,
    min=1.2,
    max=1.9,
)

plt.figure(figsize=(15, 8))
hp.gnomview(
    td_out,
    title="Td  w/ small scales ",
    rot=[0, lat],
    reso=3.75,
    xsize=320,
    cmap=cm,
    sub=234,
    min=15,
    max=25,
)
hp.gnomview(
    td_ss, title="small scales ", rot=[0, lat], reso=3.75, xsize=320, sub=232, cmap=cm
)
hp.gnomview(
    (modulate_amp),
    title=" modulation  ",
    rot=[0, lat],
    reso=3.75,
    xsize=320,
    cmap=cm,
    sub=233,
)

hp.gnomview(
    (td),
    title=" Td  GNILC  ",
    rot=[0, lat],
    reso=3.75,
    xsize=320,
    sub=235,
    cmap=cm,
    min=15,
    max=25,
)
hp.gnomview(
    td_ls,
    title="  Td large scales ",
    rot=[0, lat],
    reso=3.75,
    xsize=320,
    cmap=cm,
    sub=231,
    min=15,
    max=25,
)
../_images/preprocess-templates_gnilc_dust_spectralindex_Tdust_45_0.png
../_images/preprocess-templates_gnilc_dust_spectralindex_Tdust_45_1.png

Galactic plane fix

As in the templates, we replace the inner part of the galactic plane with the actual input data

[ ]:
templates_galplane_fix_mask = hp.read_map(
    proddatadir / "gnilc_dust_galplane.fits.gz", 3
)
[ ]:
hp.mollview(templates_galplane_fix_mask)
[ ]:
for each in [td, bd]:
    each[templates_galplane_fix_mask == 1] = 0
[39]:
if False: # do not generate galplane fix again

    hp.write_map(
        proddatadir / "gnilc_dust_beta_Td_galplane.fits.gz",
        [bd, td],
        fits_IDL=False,
        overwrite=True,
        coord="G",
        column_names=["BETA", "TD"],
        column_units=["", "K"],
        dtype=np.float32,
    )
[ ]: