Information on an SPT-3G D1 data release

This page is linked from the South Pole Telescope homepage and was created using the tools Jupyter Notebook and Quarto.

1 Introduction

This page provides information on a data release relevant to mainly the following two works:

  • Quan et al., SPT-3G D1: Maps of the millimeter-wave sky from 2019 and 2020 observations of the SPT-3G Main field (hereafter Q26)
  • Camphuis et al., SPT-3G D1: CMB temperature and polarization power spectra and cosmology from 2019 and 2020 observations of the SPT-3G Main field (hereafter C25)

The data products made available in this data release are stored in the directory d1_midell_tqu_healpix inside a Globus Collection for SPT-3G public data releases (Collection UUID: 0bf0cefa-568a-4b37-861d-32c9102ce7d6). Information on how to transfer files on Globus can be found in the final section of this documentation.

The data products form three groups: real-data maps, simulated maps, and ancillary products. The directories in d1_midell_tqu_healpix reflect this grouping as follows:

d1_midell_tqu_healpix/
├── ancillary_products  (2.8G)
├── real_data_maps      (2.4T)
└── simulated_maps      (3.3T)

Please do not hesitate to contact Wei Quan (weiquan@uchicago.edu) for any questions on accessing the Globus Collection and/or on using any data products provided in this data release.

The following list shows the different types of data products provided within each group, and more information on each type of data product can be found in a corresponding section.

List of Data Products

from matplotlib import pyplot
import healpy, numpy, os, matplotlib, sys

print("python version:", sys.version)
print("matplotlib version:", matplotlib.__version__)
print("healpy version:", healpy.__version__)
print("numpy version:", numpy.__version__)

matplotlib.rcParams["font.size"] = 14
matplotlib.rcParams["font.family"] = "DeJavu Serif"
matplotlib.rcParams["font.serif"] = ["Times New Roman"]
matplotlib.rcParams["mathtext.fontset"] = "dejavuserif"
matplotlib.rcParams["legend.fontsize"] = 14
matplotlib.rcParams["axes.grid"] = True
matplotlib.rcParams["axes.grid.which"] = "both"
matplotlib.rcParams["grid.linestyle"] = "dotted"
matplotlib.rcParams["grid.linewidth"] = 0.35
matplotlib.rcParams["xtick.direction"] = "in"
matplotlib.rcParams["ytick.direction"] = "in"
matplotlib.rcParams["axes.labelsize"] = 14
matplotlib.rcParams["axes.labelpad"] = 6
matplotlib.rcParams["axes.titlesize"] = 14
matplotlib.rcParams["axes.titlepad"] = 10
matplotlib.rcParams["xtick.labelsize"] = 14
matplotlib.rcParams["ytick.labelsize"] = 14

root_dir = os.path.join(
    "/lcrc/project/SPT3G/globusshare/public_data_releases",
    "d1_midell_tqu_healpix")
real_data_maps_dir = os.path.join(
    root_dir,
    "real_data_maps")
ancillary_products_dir = os.path.join(
    root_dir,
    "ancillary_products")
simulated_maps_dir = os.path.join(
    root_dir,
    "simulated_maps")


def show_map_full_field(
        m, vmin, vmax, title, unit,
        cmap="gray", badcolor="white"):

    pyplot.figure(figsize=(13, 7), num=0, facecolor="white")
    healpy.azeqview(
        m,
        rot=(0, -59.5, 0),
        xsize=1300, ysize=700, reso=3.5, fig=0,
        half_sky=True, lamb=True,
        cmap=cmap, min=vmin, max=vmax,
        badcolor=badcolor,
        title=title, unit=unit)
    pyplot.show()
    pyplot.close()


def show_map_thumbnail(
        m, vmin, vmax, title, unit,
        cmap="gray", badcolor="white"):

    pyplot.figure(figsize=(8, 8), num=0, facecolor="white")
    healpy.gnomview(
        m,
        rot=(32, -51, 0),
        xsize=800, ysize=800, reso=0.2, fig=0,
        cmap=cmap, min=vmin, max=vmax,
        badcolor=badcolor,
        title=title, unit=unit)
    pyplot.show()
    pyplot.close()


def show_1d_functions_of_ell(
        ell, functions, labels, xlims, ylims, ylabel,
        yscale="linear", legend_loc="upper right", legend_ncols=1,
        vlines=[]):

    pyplot.figure(figsize=(8, 5))
    for function, label in zip(functions, labels):
        pyplot.plot(ell, function, label=label, alpha=0.8)
    for vline in vlines:
        pyplot.axvline(vline, color="black", linestyle="dotted")
    pyplot.yscale(yscale)
    pyplot.xlim(left=xlims[0], right=xlims[1])
    pyplot.ylim(bottom=ylims[0], top=ylims[1])
    if not (len(labels) == 1 and labels[0] == ""):
        pyplot.legend(loc=legend_loc, ncol=legend_ncols)
    pyplot.xlabel(r"$\ell$")
    pyplot.ylabel(ylabel)
    pyplot.show()
    pyplot.close()


def show_alm_triangle(
        alm, lmax, real=True,
        vmin=None, vmax=None, cmap="Oranges_r",
        xlims=None, ylims=None,
        title="Triangle"):

    import warnings
    warnings.filterwarnings("ignore")

    triangle = numpy.empty((lmax+1, lmax+1))
    triangle[:,:] = numpy.nan
    for l in range(lmax+1):
        for m in range(0, l+1):
            i = healpy.Alm.getidx(lmax, l, m)
            if real:
                triangle[m, l] = alm[i].real
            else:
                triangle[m, l] = alm[i]

    pyplot.figure(figsize=(7, 7))
    if vmin is None:
        vmin = numpy.min(triangle)
    if vmax is None:
        vmax = numpy.max(triangle)
    img = pyplot.imshow(
        triangle, origin="lower", vmin=vmin, vmax=vmax, cmap=cmap)
    if xlims is None:
        xlims = [0, triangle.shape[1]]
    if ylims is None:
        ylims = [0, triangle.shape[0]]
    pyplot.xlim(left=xlims[0], right=xlims[1])
    pyplot.ylim(bottom=ylims[0], top=ylims[1])
    pyplot.grid(False)
    cb = pyplot.colorbar(pad=0.03, shrink=0.8)
    pyplot.grid(True)
    pyplot.xlabel(r"$\ell$")
    pyplot.ylabel(r"$m$")
    pyplot.title(title)
    pyplot.show()
    pyplot.close()
python version: 3.10.1 (main, Jan  8 2024, 15:34:44) [GCC 10.3.0]
matplotlib version: 3.5.1
healpy version: 1.15.0
numpy version: 1.22.1

2 Real-data maps

The directory real_data_maps contains real-data maps. The structure of real_data_maps is shown below:

real_data_maps/
├── full
├── half
├── one_thirtieth
├── pre_null
│   ├── azimuth
│   ├── moon
│   ├── scan
│   ├── sun
│   ├── wafer
│   └── year
└── signflip_noise

Every map file in every directory contains one coadd, a set of weight-removed \(T/Q/U\) maps (HEALPix \(N_{\mathrm{side}} = 8192\), \(\ell_{\mathrm{max}} \sim 16000\)) for one frequency band. The \(Q/U\) maps are stored in accordance with the IAU convention. Details of the definition of a coadd are described in Section III C 1 of Q26. We provide the five types of coadds described in that section (the full, half, one-thirtieth, signflip-noise, and pre-null coadds) in this data release. Information on the files related to each type of coadd can be found below.

As described in Section III B 5 of Q26, we binned the SPT-3G D1 timestreams into two pixelization schemes: the HEALPix pixels with \(N_{side} = 8192\) and the \(0^{\prime}.5625\) square pixels in the ZEA projection. While all the maps initially provided in this data release are the HEALPix maps for simplicity, the \(2^{\prime}.25\) ZEA maps (a rebinned version of the \(0^{\prime}.5625\) maps) used for Ge et al., Cosmology from CMB lensing and delensed \(EE\) power spectra using 2019-2020 SPT-3G polarization data can be made available upon request.

2.1 full coadds

The files in the directory full contain the full coadds, the full-depth coadds made from SPT-3G D1. The file names are based on the following template:

full_{freq}ghz.fits,

where {freq} is 095, 150, or 220.

Each file also contains the associated six weight maps. All the weight maps are normalized by the maximum value of the \(TT\) weight map. Below is part of the output from running the program fitsverify on one file, which shows the map stored in each column.

     Col# Name (Units)       Format
   1 PIXEL                      J
   2 TEMPERATURE (uK)           D
   3 Q_POLARISATION (uK)        D
   4 U_POLARISATION (uK)        D
   5 TT_NORMALIZED (unitless)   D
   6 TQ_NORMALIZED (unitless)   D
   7 TU_NORMALIZED (unitless)   D
   8 QQ_NORMALIZED (unitless)   D
   9 QU_NORMALIZED (unitless)   D
  10 UU_NORMALIZED (unitless)   D

In the following four cells, the 95 GHz full \(T\) map and \(TT\) weight map are loaded and plotted.

full_95_t = healpy.read_map(
    os.path.join(real_data_maps_dir, "full", "full_095ghz.fits"),
    field=0,
    partial=True)
show_map_full_field(
    full_95_t, -140, 140,
    r"95 GHz, $\tt{full}$, $\it{T}$ map", r"$\mathrm{{\mu}K}$",
    cmap="RdBu_r")

full_95_tt = healpy.read_map(
    os.path.join(real_data_maps_dir, "full", "full_095ghz.fits"),
    field=3,
    partial=True)
show_map_full_field(
    full_95_tt, 0, 1,
    r"95 GHz, $\tt{full}$, $\it{TT}$ weight map (normalized)", "Unitless",
    cmap="bone")

2.2 half and one-thirtieth coadds

The files in each of the two directories half and one_thirtieth contain the partial-depth coadds (bundles) made by randomly dividing the full dataset into two and thirty equal-depth bundles, respectively. The file names are based on the following tempates:

half_bundle{n}_{freq}ghz.fits,
one_thirtieth_bundle{m}_{freq}ghz.fits,

where {n} is 0 or 1, and {m} ranges from 0 to 29.

2.3 signflip-noise coadds

The files in the directory signflip_noise contain the 500 signflip-noise coadds. The file names are based on the following template:

signflip_noise_permutation{n}_{freq}ghz.fits,

where {n} ranges from 0 to 499. We use permutation followed by a number to distinguish the different permutations of the signs (\(+1\)s and \(-1\)s).

In the following two cells, the 95 GHz signflip-noise \(U\) map from one permutation is loaded an plotted.

signflip_noise_95_t = healpy.read_map(
    os.path.join(
        real_data_maps_dir,
        "signflip_noise",
        "signflip_noise_permutation421_095ghz.fits"),
    field=2,
    partial=True)
show_map_full_field(
    signflip_noise_95_t, -35, 35,
    r"95 GHz, $\tt{signflip-noise}$, $\it{U}$ map (IAU convention)",
    r"$\mathrm{{\mu}K}$",
    cmap="RdBu_r")

2.4 pre-null coadds

The files in the directory pre_null in turn are organized into six directories, each of which has the files that contain the coadds related to one of the six splits (azimuth, year, sun, moon, scan, and wafer) described in Section VII A 1 of Q26. The file names are based on the following template:

{split}_{half}_bundle{n}_{freq}.fits,

where {split} is one of the six names of the splits, {half} is the name for one of the two parts for that split, and {n} ranges from 0 to 24.

The following table shows the meanings of the two {half}s for each split.

Names Meaning
azimuth, near The observations whose median azimuth is in the 180\(^\circ\) range of azimuth centered at 150\(^\circ\)
azimuth, far The observations whose median azimuth is in the other 180\(^\circ\) range
year, 2019 The observations taken in 2019
year, 2020 The observations taken in 2020
sun (moon), below The observations taken when the Sun (moon) was below the horizon
sun (moon), above The observations taken when the Sun (moon) was above the horizon
scan, left The timestreams acquired during the left-going telescope scans of every observation
scan, right The timestreams acquired during the right-going telescope scans of every observation
wafer, large Every observation’s timestreams from the group of five wafers that have relatively large elevation-dependent gain changes
wafer, small Every observation’s timestreams from the group of five wafers that have relatively small elevation-dependent gain changes

In the following two cells, the 95 GHz \(T\) map from bundle 4 of the near half of the azimuth split is loaded and plotted.

az_near_bundle0_95_t = healpy.read_map(
    os.path.join(
        real_data_maps_dir,
        "pre_null/azimuth",
        "azimuth_near_bundle00_095ghz.fits"),
    field=0,
    partial=True)
show_map_full_field(
    az_near_bundle0_95_t, -140, 140,
    r"95 GHz, $\tt{pre-null}$, $\it{T}$ map "+
        "(the 'near' half of the azimuth split, bundle 0)",
    r"$\mathrm{{\mu}K}$",
    cmap="RdBu_r")

The 25 null bundles that we produced to calculate the average null spectra for each split are the differences between the 25 bundles in one half and those in the other half. For example, the 95 GHz \(T\) map in null bundle 4 from the azimuth split is equal to \((T_{\mathrm{near},4} - T_{\mathrm{far},4}) / 2\), where \(T_{\mathrm{near},4}\) is the \(T\) map from azimuth_near_bundle04_095ghz.fits, and \(T_{\mathrm{far},4}\) is from azimuth_far_bundle04_095ghz.fits.

Back to List of Data Products

3 Simulated maps

The directory simulated_maps contains the mock-observation input and output maps described in Section III D of Q26. The structure of simulated_maps is shown below:

simulated_maps/
├── input_maps
└── output_maps
    ├── masking_no
    └── masking_yes

As is the case with the map files in real_data_maps, every map file in simulated_maps contains one coadd (HEALPix \(N_{\mathrm{side}} = 8192\), \(\ell_{\mathrm{max}}=16000\)) for one frequency band, and the \(Q/U\) maps are stored in accordance with the IAU convention.

3.1 Input maps

The files in the directory input_maps contain the 500 realizations of input sky maps and the theory spectra associated with the realizations.

The names of the files storing the input sky maps are based on the following template:

input_maps_realization{rlz}_{freq}ghz.fits,

where {rlz} ranges from 0 to 499.

In the following two cells, one realization of the sky at 220 GHz is loaded and plotted. The footprint of each input sky map is slightly larger (\({\sim}10\%\)) than the footprint of the SPT-3G Main field. In addition, these maps had very-large angular-scale information (\(\ell<100\)) removed when they were generated.

mock_input_220_tqu = healpy.read_map(
    os.path.join(
        simulated_maps_dir,
        "input_maps",
        "input_maps_realization421_220ghz.fits"),
    field=(0, 1, 2),
    partial=True)
for s, m in zip("TQU", mock_input_220_tqu):
    if s == "T":
        lim = 300
        title = r"220 GHz, mock observation input, $\it{T}$ map"
    else:
        lim = 10
        title = r"220 GHz, mock observation input, "
        title += "$\it{}$ map (IAU convention)".format(s)
    show_map_full_field(
        m, -1*lim, lim,
        title,
        r"$\mathrm{{\mu}K}$",
        cmap="gray")

The theory spectra can be reconstructed by using the files storing the spectra of the CMB and the sum of all the foregrounds and the files storing the beams.

First, the names of the files storing the CMB spectra are based on the following template:

input_cl_{anisotropy}.txt,

where {anisotropy} is tt, ee, bb, or te.

Second, the names of the files storing the foreground spectra are based on a similar template:

input_cl_foregrounds_{freq1}ghz{freq2}ghz_{anisotropy}.txt,

where each of {freq1} and {freq2} is 095, 150, or 220, and {anisotropy} is tt, ee, or bb. (The te, tb, and eb spectra are expected to be zero.)

After a CMB realization and a foreground realization were generated using the method described in Section III D 2, the two maps were combined and smoothed by a beam. The names of the files storing the beams are based on the following template:

input_beam_bl_{freq}ghz.txt.

The beams used to smooth the simulated maps are not the latest beams. The beams that we recommend to be used when analyzing the real-data maps are described in the section below on the generally applicable ancillary products.

In the following two cells, the \(TT/EE/BB\) theory at 220 GHz are loaded and plotted.

l = numpy.arange(16001)
cl2dl = l*(l+1) / (2*numpy.pi)
input_maps_dir = os.path.join(simulated_maps_dir, "input_maps")

input_cl = {}

for anisotropy in ["tt", "ee", "bb"]:
    _, cl_cmb = numpy.loadtxt(
        os.path.join(
            input_maps_dir,
            "input_cl_cmb_{}.txt".format(anisotropy)),
        unpack=True)
    _, cl_foregrounds = numpy.loadtxt(
        os.path.join(
            input_maps_dir,
            "input_cl_foregrounds_220ghz220ghz_{}.txt".format(anisotropy)),
        unpack=True)
    _, bl = numpy.loadtxt(
        os.path.join(
            input_maps_dir,
            "input_beam_bl_220ghz.txt"),
        unpack=True)
    input_cl[anisotropy] = [
        cl_cmb * bl**2,
        cl_foregrounds * bl**2,
        (cl_cmb+cl_foregrounds) * bl**2]
for anisotropy, loc in zip(
    ["tt", "ee", "bb"], ["lower left", "upper right", "upper right"]):

    show_1d_functions_of_ell(
        l,
        [input_cl[anisotropy][0]*cl2dl,
         input_cl[anisotropy][1]*cl2dl,
         input_cl[anisotropy][2]*cl2dl,
         input_cl[anisotropy][2]*cl2dl/bl**2],
        ["Comp = CMB",
         "Comp = Foregrounds",
         "Comp = Total",
         "Comp = Total w/o Beam"],
        [0, 16000], [1e-6, 1e4],
        r"$D_{{\ell,\mathrm{{Comp}}}}^{{{}}}\/\/[{{\mathrm{{\mu}}K}}^2]$".format(
            anisotropy.upper()),
        yscale="log", legend_loc=loc)

3.2 Output maps

The files in the directory output_maps contain two sets of mock-observation output maps. The first set is the standard set, which comprises the output maps produced with the masked timestream high-pass filter (this was the setting used for the real-data timestreams). This set is contained in the directory masking_yes. The second set corresponds to the output maps produced with the masking turned off in the filtering process. We produced these maps to study the effect of the masking, namely the “filtering artifacts” described in Section IV A 1 of C25 and Section III B 2 of Q26. This set is contained in the directory masking_no. While we produced the first set of output maps for all the 500 realizations of input sky maps, we produced the second set of output maps for only the first 110 realizations.

3.2.1 With the masked high-pass filter

The names of the files storing the first set of output maps are based on the following template:

output_maps_masking_yes_realization{rlz}_{freq}ghz.fits,

where {rlz} ranges from 0 to 499.

In the following two cells, the input and output 220GHz \(T\) map from one realization are loaded, and both the full extent and a thumbnail of a small region of each map are shown.

mock_in = os.path.join(
    simulated_maps_dir,
    "input_maps",
    "input_maps_realization421_220ghz.fits")
mock_out = os.path.join(
    simulated_maps_dir,
    "output_maps/masking_yes",
    "output_maps_masking_yes_realization421_220ghz.fits")

mock_in, mock_out = [
    healpy.read_map(mock, field=0, partial=True)
    for mock in [mock_in, mock_out]]
for mock, label in zip([mock_in, mock_out], ["input", "output"]):
    show_map_full_field(
        mock, -300, 300,
        r"220 GHz, mock observation {}, $\it{{T}}$ map".format(label),
        r"$\mathrm{{\mu}K}$",
        cmap="RdBu_r")
    show_map_thumbnail(
        mock, -300, 300,
        r"220 GHz, mock observation {}, $\it{{T}}$ map".format(label),
        r"$\mathrm{{\mu}K}$",
        cmap="RdBu_r")

In the following three cells, the \(TT\) spectra of these two maps are loaded and plotted. The spectra were calculated using the program \(\texttt{PolSpice}\) with the following command:

spice
  -mapfile {maps_path}
  -weightfile ancillary_products/pixel_mask_apodized_borders_objects.fits
  -nlmax 16000 -thetamax 30 -apodizetype 1 -apodizesigma 30
  -clfile {cl_path} -fits_out YES
  -verbosity 2,

where {maps_path} is the path represented by either the variable mock_in or the variable mock_out defined above, and {cl_path} is shown in cell below where the spectra are loaded.

cl_in = healpy.read_cl(os.path.join(
    simulated_maps_dir,
    "input_maps",
    ".input_cls_realization421_220ghz_tonly.fits"))
cl_out = healpy.read_cl(os.path.join(
    simulated_maps_dir,
    "output_maps/masking_yes",
    ".output_cls_masking_yes_realization421_220ghz_tonly.fits"))
l = numpy.arange(16001)
cl2dl = l*(l+1) / (2*numpy.pi)
show_1d_functions_of_ell(
    l, [cl_in*cl2dl, cl_out*cl2dl], ["Input", "Output"],
    [0, 16000], [1e1, 1e4], r"$D_{\ell}^{TT}\/\/[{\mathrm{{\mu}K}}^2]$",
    yscale="log", legend_loc="upper right")

show_1d_functions_of_ell(
    l, [cl_out/cl_in], [""],
    [0, 16000], [0.0, 1.0],
    r"$C_{\ell,\/\mathrm{output}}^{TT}\//\/C_{\ell,\/\mathrm{input}}^{TT}$"
    " [unitless]",
    yscale="linear", legend_loc="lower left")

The ratio shown in the second plot includes not only the filter transfer function but also mainly two other effects: the effect due to the bilinear interpolation used to generate mock timestream samples from the input map pixel values and the effect due to binning the filtered timestreams.

The latter effect is well described by \(P_{\ell}^2\), where \(P_{\ell}\) is the pixel window function, and more information on the pixel window function associated with the SPT-3G Main field can be found in the section below on the generally applicable ancillary products.

As described in Section III D 2, we model the former effect as \(P_{\ell}^4\). However, we do not expect this model to be exact. More information on this point can be found in the section below on the generally applicable ancillary products (in the part where the transfer function is described).

As a result, we model the ratio shown in the second plot as follows:

\(C_{\ell,output}^{TT}\,/\,C_{\ell,output}^{TT} \approx P_{\ell}^4 \times F_{\ell} \times P_{\ell}^2\),

where \(F_{\ell}\) is the filter transfer function.

The sum of a mock-observation output map and a signflip-noise map described above can be used as a simulation of a full-depth real-data map (the full maps described above). It is true that the effect of the bilinear interpolation present in the mock-observation output map does not apply to a real-data map, but if needed, this effect can by corrected for before the mock-observation output map is combined with the noise map.

3.2.2 Without the masking

The names of the files storing the second set of output maps are based on the following template:

output_maps_masking_no_realization{rlz}_{freq}ghz.fits,

where {rlz} ranges from 0 to 109.

In the following two cells, an output \(T\) map from this second set and the corresponding map from the first set are loaded, and the difference map is plotted.

mock_out_masking_yes, mock_out_masking_no = [
    healpy.read_map(
        os.path.join(
            simulated_maps_dir,
            "output_maps/masking_{}".format(yn),
            "output_maps_masking_{}_realization022_220ghz.fits".format(yn)),
        field=0,
        partial=True)
    for yn in ["yes", "no"]]

mask = healpy.read_map(
    os.path.join(
        ancillary_products_dir,
        "generally_applicable",
        "pixel_mask_apodized_borders_objects.fits"),
    field=0,
    partial=True)

mock_out_diff = mock_out_masking_yes - mock_out_masking_no
mock_out_diff *= mask
show_map_thumbnail(
    mock_out_diff, -30, 30,
    r"220 GHz, mock observation, $\it{{T}}$ map difference".format(label),
    r"$\mathrm{{\mu}K}$",
    cmap="RdBu_r")

Back to List of Data Products

4 Ancillary products

The directory ancillary_products contains data products that can be useful when analyzing the real-data and simulated-data maps described above. The structure of ancillary_products is shown below:

ancillary_products/
├── generally_applicable
└── specific_to_c25

The directory specific_to_c25 contains data products that can be useful if a user is interested in analyzing the temperature and \(E\)-mode polarization anisotropies of the maps up to \(\ell = 4000\) in a way similar to the method used in C25, In the C25 method, the pixels at the locations of the high-S/N objects are inpainted, certain \(a_{{\ell}m}\) coefficients of the maps are downweighted when \(C_{\ell}\)’s are calculated, and the effects of the timestream filtering are modeled as a combination of multiplicative and additive biases.

On the other hand, the directory generally_applicable contains data products that can be useful when a user is interested in analyzing the maps up to higher \(\ell\)’s and/or modeling the effects of the timestream filtering simply as an multiplicative bias.

4.1 Generally applicable products

The directory generally_applicable contains binary and apodized pixel masks, the HEALPix pixel window function associated with the SPT-3G Main field, a beam for each frequency band and each of several types of spectral energy densities (SEDs) of sky signals. and a filter transfer function.

4.1.1 Pixel masks

We provide four pixel masks that can be useful for purposes including calculating power spectra of the maps and locating the high-S/N objects (emissive sources and galaxy clusters) masked in the timestream high-pass filter. The masking in the high-pass filter is described in Section III B 2 of Q26. The file names storing the four masks and the types of the masks are listed in the following table:

File name Mask type
pixel_mask_binary_borders_only.fits A binary mask that highlights all the pixels where the weights in the full \(TT\) weight map are larger than 10% of the median weight for each frequency band. We constructed a binary mask using the weight map for each frequency band and used the intersection of three binary masks as the final binary survey mask.)
pixel_mask_apodized_borders_only.fits An apodized version of the binary mask above that has Gaussian tapers near the borders
pixel_mask_binary_borders_objects.fits A binary mask that highlights the survey area used and covers the pixels containing the high-S/N objects. The radius of the disk covering each object is the same as the radius used in the masking in the timestream high-pass filter.
pixel_mask_apodized_borders_objects.fits An apodized version of the binary mask above that has Gaussian tapers near both the borders and the disks.

More information on these masks can be found in Section III C of C25. In the following two cells, the four masks are loaded and plotted. For the two masks that involve the high-S/N objects, thumbnails of a small region containing the highest-S/N object are also shown.

mask_paths = [
    "pixel_mask_binary_borders_only.fits",
    "pixel_mask_apodized_borders_only.fits"]
mask_names = [
    "Binary border mask",
    "Apodized border mask"]

for mask_path, mask_name in zip(mask_paths, mask_names):
    mask = healpy.read_map(
        os.path.join(
            ancillary_products_dir,
            "generally_applicable",
            mask_path),
        field=0,
        partial=True)

    show_map_full_field(mask, 0, 1, mask_name, "Unitless", cmap="Oranges_r")

mask_paths = [
    "pixel_mask_binary_borders_objects.fits",
    "pixel_mask_apodized_borders_objects.fits"]
mask_names = [
    "Binary border-and-object mask",
    "Apodized border-and-object mask"]

for mask_path, mask_name in zip(mask_paths, mask_names):
    mask = healpy.read_map(
        os.path.join(
            ancillary_products_dir,
            "generally_applicable",
            mask_path),
        field=0,
        partial=True)

    show_map_full_field(mask, 0, 1, mask_name, "Unitless", cmap="Oranges_r")
    print()
    show_map_thumbnail(mask, 0, 1, mask_name, "Unitless", cmap="Oranges_r")
    print()

4.1.2 Pixel window function

We provide the pixel window function specific to the region covered by the SPT-3G Main field, which is stored in the following file:

pixel_window_function.txt.

Because pixels in different regions of the surface do not have the identical shape in HEALPix, the pixel window function provided by healpy, which is a full-sky average, is different from the one specific to SPT-3G. However, the difference is only \(\sim 1\%\) at \(\ell = 10000\). In the following two cells, the SPT-3G pixel window function, \(P_{\ell}\), is loaded and compared with the one provided by healpy. The SPT-3G pixel window function was calculated using the pixel mask stored in pixel_mask_apodized_borders_only.fits.

ell, pixwin_spt3g = numpy.loadtxt(
    os.path.join(
        ancillary_products_dir,
        "generally_applicable",
        "pixel_window_function.txt"),
    unpack=True)
pixwin_avg = healpy.pixwin(nside=8192, lmax=16000)
show_1d_functions_of_ell(
    ell,
    [pixwin_spt3g, pixwin_avg],
    ["SPT-3G Main field", "Full-sky average"],
    [0, 16000], [0.79, 1.01],
    r"$P_{\ell}$"" [unitless]")

4.1.3 Beams

We provide beam products for \(\ell \leq 16000\) for each of the three frequency bands and each of four types of astrophysical signals with different spectral energy densities (SEDs). These beam products were used in Chaubal et al., SPT-3G D1: A Measurement of Secondary Cosmic Microwave Background Anisotropy Power.

The four SEDs are the following:

  • the CMB (the derivative of the 2.7 K blackbody SED)
  • radio sources (a falling power-law SED with the spectral index \(\alpha = -0.7\)
  • dusty sources (a modified blackbody with the parameters \(\beta = 1.8\) and \(T = 25\) K
  • the thermal Sunyaev-Zel’dovich effect

For each SED, we provide the beam (\(B_\ell\)) for each of the three frequency bands and the covariance matrix of the three \(B_{\ell}\)’s.

The \(B_\ell\)’s are sampled at \(\Delta \ell = 1\) and normalized to 1 at \(\ell = 800\). They are stored in the following files:

beam_bl_{sed}_{freq}ghz.txt,

where {sed} is cmb, radio, dusty, or tsz, and {freq} is 095, 150, or 220.

In the following cell, all the \(B_{\ell}\)’s are loaded and plotted.

for band in [95, 150, 220]:

    bl = {}
    for sed in ["cmb", "radio", "dusty", "tsz"]:

        _, bl[sed] = numpy.loadtxt(
            os.path.join(
                ancillary_products_dir,
                "generally_applicable",
                "beam_c26_{}_bl_{:03d}ghz.txt".format(sed, band)),
            unpack=True)

    show_1d_functions_of_ell(
        numpy.arange(16001),
        list(bl.values()),
        ["SED = {}".format(sed) for sed in bl.keys()],
        [0, 16000], [0.0, 1.1],
        r"$B_{{\ell,\mathrm{{SED}}}}^\mathrm{{{{{}}}\,GHz}}$ [unitless]".format(band),
        yscale="linear", legend_loc="upper right")

The covariance matrix for each SED is equal to the sum of a component common to all the SEDs and a component specific to that SED. The matrices are stored in the following files:

beam_c26_{sed}_cov_3bands.npz.

The common component is also provided on its own in the following file:

beam_c26_raw_cov_3bands.npz.

Each file is a numpy file containing three arrays. One array has the key cov and stores the covariance matrix itself. Another array has the key ell and shows the \(\ell\)’s at which the covariance matrix is sampled (\(\Delta\ell = 60\)). The other array has the key bands and shows the ordering of the frequency bands in the covariance matrix. In each covariance matrix (except for the raw matrix), the component specific to the SED can be extracted by subtracting the common component (the raw matrix).

To build covariance matrices for a particular \(\ell\) range, one can follow these steps:

  1. trim the elements of each matrix beyond the interested \(\ell\) range,
  2. decompose the trimmed matrix into its eigenvectors and keep only the significant ones (for most analyses, we find 30 eigenvectors to be more than sufficient),
  3. upsample the models through linear interpolation to \(\Delta\ell = 1\)
  4. form the dot product between the array of eigenvectors and the transpose of the array

To use the beam covariance matrices on power spectra with multiple SEDs, one must first multiply the total power spectrum by the beam covariance matrix (or its eigenmodes) and then add the product of each component power spectrum times the SED-specific portion of the covariance matrix (or its eigenmodes), i.e.,

\((\sum_{\mathrm{SED}} C_\ell^{\mathrm{SED}}) \Sigma_{\ell,\ell^\prime}^{\mathrm{common}} + \sum_{\mathrm{SEDs}} (C_\ell^{\mathrm{SED}} \Sigma_{\ell,\ell^\prime}^{\mathrm{SED}})\),

where \(\sum_\mathrm{SED}\) is a summation over multiple SEDs, and \(\Sigma_{\ell,\ell^\prime}^{\mathrm{common}}\) is the common component exising in all the covariance matrices (the raw matrix), and \(\Sigma_{\ell,\ell^\prime}^{\mathrm{SED}}\) is the component specific to an SED (an sed matrix minus the raw matrix).

In the following cell, one covariance matrix is loaded, and its basic information is shown.

cov = numpy.load(os.path.join(
    ancillary_products_dir,
    "generally_applicable",
    "beam_c26_cmb_cov_3bands.npz"))

print(list(cov.keys()))
print(cov["band"])
print(cov["ell"])
print(cov["cov"].shape)
['ell', 'band', 'cov']
['95' '150' '220']
[   30.    90.   150.   210.   270.   330.   390.   450.   510.   570.
   630.   690.   750.   810.   870.   930.   990.  1050.  1110.  1170.
  1230.  1290.  1350.  1410.  1470.  1530.  1590.  1650.  1710.  1770.
  1830.  1890.  1950.  2010.  2070.  2130.  2190.  2250.  2310.  2370.
  2430.  2490.  2550.  2610.  2670.  2730.  2790.  2850.  2910.  2970.
  3030.  3090.  3150.  3210.  3270.  3330.  3390.  3450.  3510.  3570.
  3630.  3690.  3750.  3810.  3870.  3930.  3990.  4050.  4110.  4170.
  4230.  4290.  4350.  4410.  4470.  4530.  4590.  4650.  4710.  4770.
  4830.  4890.  4950.  5010.  5070.  5130.  5190.  5250.  5310.  5370.
  5430.  5490.  5550.  5610.  5670.  5730.  5790.  5850.  5910.  5970.
  6030.  6090.  6150.  6210.  6270.  6330.  6390.  6450.  6510.  6570.
  6630.  6690.  6750.  6810.  6870.  6930.  6990.  7050.  7110.  7170.
  7230.  7290.  7350.  7410.  7470.  7530.  7590.  7650.  7710.  7770.
  7830.  7890.  7950.  8010.  8070.  8130.  8190.  8250.  8310.  8370.
  8430.  8490.  8550.  8610.  8670.  8730.  8790.  8850.  8910.  8970.
  9030.  9090.  9150.  9210.  9270.  9330.  9390.  9450.  9510.  9570.
  9630.  9690.  9750.  9810.  9870.  9930.  9990. 10050. 10110. 10170.
 10230. 10290. 10350. 10410. 10470. 10530. 10590. 10650. 10710. 10770.
 10830. 10890. 10950. 11010. 11070. 11130. 11190. 11250. 11310. 11370.
 11430. 11490. 11550. 11610. 11670. 11730. 11790. 11850. 11910. 11970.
 12030. 12090. 12150. 12210. 12270. 12330. 12390. 12450. 12510. 12570.
 12630. 12690. 12750. 12810. 12870. 12930. 12990. 13050. 13110. 13170.
 13230. 13290. 13350. 13410. 13470. 13530. 13590. 13650. 13710. 13770.
 13830. 13890. 13950. 14010. 14070. 14130. 14190. 14250. 14310. 14370.
 14430. 14490. 14550. 14610. 14670. 14730. 14790. 14850. 14910. 14970.
 15030. 15090. 15150. 15210. 15270. 15330. 15390. 15450. 15510. 15570.
 15630. 15690. 15750. 15810. 15870. 15930. 15990.]
(801, 801)

4.1.4 Filter transfer function

We provide a filter transfer function for \(\ell \leq 16000\), which can be used for all three frequency bands. The function is stored in the following file:

filter_transfer_function.txt

We obtained this function, \(F_{\ell}\), by azimuthally (in 2D Fourier space) binning a function of \(\ell_x\) and \(\ell_y\) as follows:

\(F_{\ell} = \frac{1}{2\pi} \int_{0}^{2\pi} H(\ell_x - 300)\,\,e^{-2(\ell_x/13000)^6} d\phi_\ell\),

where \(\phi_\ell = \mathrm{tan}(\ell_y/\ell_x)\). (We implemented this integral numerically using 2D numpy arrays.) As described in Section IV B of Q26, the timestream filtering is well approximated by this azimuthal binning.

In the following two cells, this function is loaded and plotted.

_, fl = numpy.loadtxt(
    os.path.join(
        ancillary_products_dir,
        "generally_applicable",
        "filter_transfer_function.txt"),
    unpack=True)
show_1d_functions_of_ell(
    numpy.arange(16001), [fl],
    [""], [0, 16000], [0.0, 1.0],
    r"$F_\ell$ [unitless]",
    yscale="linear", legend_loc="upper right")

Up to \(\ell \approx 6000\), the dominant filtering effect is due to the high-pass filter, and the function monotonically increases. Using the average ratio \(C_{\ell,\mathrm{output}}^{TT} / C_{\ell,\mathrm{input}}^{TT}\) obtained from the 500 realizations of 220 GHz input and output \(T\) maps as the reference (and dividing the ratio by \(P_{\ell}^6\) to correct for the effects of the bilinear interpolation and binning as described in the section above on the mock-observation output maps), the analytic form of \(F_{\ell}\) shown above deviates from the reference by \(\sim 3\%\) in the \(\ell\) range from 400 to 1000 and \(\sim 1\%\) in the \(\ell\) range from 1000 to 6000.

A main cause of the increasing deviation near the high-pass cutoff is that the analytic form does not model the fact that the high-pass filter cutoff values used for different detectors in the full detector array were slightly different from 300 as described in Section VII A 3 of Q25 (the nonzero expectated null spectra for the wafer split).

In the following two cells, the reference function obtained from the mock observations is loaded and compared with the analytic form.

_, pixwin = numpy.loadtxt(
    os.path.join(
        ancillary_products_dir,
        "generally_applicable",
        "pixel_window_function.txt"),
    unpack=True)

_, avg_ratio_mock = numpy.loadtxt(
    os.path.join(
        ancillary_products_dir, 
        "generally_applicable",
        ".average_cl_ratio_output_to_input_500realizations_220ghz_tonly.txt"),
    unpack=True)
show_1d_functions_of_ell(
    numpy.arange(16001),
    [avg_ratio_mock/pixwin**6, fl],
    ["Reference from mock observations", "Analytic form"],
    [0, 6000], [0.0, 1.0],
    r"$F_{\ell}$ [unitless]",
    yscale="linear", legend_loc="lower right")

show_1d_functions_of_ell(
    numpy.arange(16001),
    [fl/(avg_ratio_mock/pixwin**6)],
    [""],
    [0, 6000], [0.97, 1.03],
    r"$F_{\ell,\mathrm{flat}}/F_{\ell,\mathrm{mock}}$"
    " [unitless]",
    yscale="linear", legend_loc="lower right")

Beyond \(\ell \approx 6000\), the dominant filtering effect is due to the low-pass filter. The deviation of the analytic \(F_{\ell}\) from the reference function grows up to 20% at \(\ell = 16000\). However, at high-\(\ell\), our investigations have shown that the analytic form is in fact a more accurate representation of the effect of the low-pass filter than the reference function. This is because we do not expect the modeling of the effect of the bilinear interpolation used to generate mock timestreams from input map pixel values as \(P_{\ell}^4\) to be exact, and we attribute the difference between the analytic \(F_{\ell}\) and the reference function obtained from the mock observations to the inaccuracy in the modeling of the bilinear interpolation.

In the following cell, the different between the two functions at high-\(\ell\) is shown.

show_1d_functions_of_ell(
    numpy.arange(16001),
    [fl/(avg_ratio_mock/pixwin**6)],
    [""],
    [0, 16000], [0.8, 1.2],
    r"$F_{\ell,\mathrm{flat}}/F_{\ell,\mathrm{mock}}$"
    " [unitless]",
    yscale="linear", legend_loc="lower right")

5 Globus transfers

In this section, we provide information on how to transfer files using Globus tools.

Primary access to the data products described above will be through the Globus file transfer system. To use this, you will either need a Globus account, or you can log in to Globus with an institutional or group identity. If you do not have an academic affiliation or a Globus ID, you can use your GitHub, Google, or ORCID identity.

The standard way to access Globus is through the web-based file transfer application. which can be accessed (1) directly on the file transfer web app or (2) through the Globus Connect Personal app.

  1. If you access the web app above directly, and you are not already logged in to Globus, this will redirect to a login page where you can select an organizational identification to use. (If you have a Globus ID and want to use that, select “Globus ID” from the dropdown menu.) Once on the page, you will need to identify a destination for the file transfer, which will need to be a machine with a Globus endpoint installed that you have permission to use. Normally this will be a computing cluster or central storage location for a group or institution.

  2. If you do not have access to a Globus endpoint on such a machine described in (1), or if you want to download the data products directly to your personal device, you will want to install the Globus Connect Personal app (scroll down and look for the appropriate “INSTALL NOW” button for your operating system) on your device. Once you have installed the app, clicking on the “g” icon (in the top or bottom screen menu, for Mac or Windows) and selecting “Web: Transfer Files” will take you to the file transfer web app, with one of the endpoints already selected to be your personal device.

Once you have logged in to Globus and are on the file transfer web app page, use the “Search” prompt in the box next to the word “Collection” to search for “SPT-3G Public Data Release” or “0bf0cefa-568a-4b37-861d-32c9102ce7d6”. Once in the SPT-3G Collection page, use the file navigator to navigate to the directory d1_midell_tqu_healpix, and select the files you wish to transfer. Then, if you are in the two-pane mode, select your destination Globus endpoint in the other pane, select the directory where you want the data products to be transferred, and hit the “Start” button above the left (SPT-3G D1) pane.

Advanced users may wish to access the data products through the Globus command-line tools. Instructions for installing the tools can be found on the linked page, and instructions for using the tools can be found at the CLI QuickStart Guide.

Back to List of Data Products