Pipelines

TOAST pipelines are standalone Python scripts that apply one or more TOAST operators to existing or simulated data. They typically divide into two major parts:

  1. Creating the TOAST data object
  2. Applying TOAST operators to the data

Experiments and simulations typically require specialized methods for creating the data object. This part is hard to generalize. For the subsequent data processing TOAST provides a number of convenience functions are kept in toast.pipeline_tools.

In [1]:
# Are you using a special reservation for a workshop?
# If so, set it here:
nersc_reservation = "toast3"

# Load common tools for all lessons
import sys
sys.path.insert(0, "..")
from lesson_tools import (
    check_nersc,
    fake_focalplane
)
nersc_host, nersc_repo, nersc_resv = check_nersc(reservation=nersc_reservation)

# Capture C++ output in the jupyter cells
%reload_ext wurlitzer
Running on NERSC machine 'cori'
  with access to repos: mp107, desi, m1759, cosmo
Using default repo mp107
Reservation 'toast3' valid from 2019-10-16T09:00:00 to 2019-10-16T17:00:00
Current time is 2019-10-16T05:48:03.889112
Reservation 'toast3' not currently valid

Brief example

We demonstrate these concepts by writing a very simple pipeline that creates its own observations, fills them with noise and applies a polynomial filter.

In [2]:
%%writefile my_first_pipeline.py

import argparse

import numpy as np

# import lesson_tools

import toast
from toast.mpi import MPI
import toast.pipeline_tools


# Fake focaplane is copied here from lesson_tools to avoid import error

def fake_focalplane(
    samplerate=20,
    epsilon=0,
    net=1,
    fmin=0,
    alpha=1,
    fknee=0.05,
    fwhm=30,
    npix=7,
    fov=3.0
):
    """Create a set of fake detectors.

    This generates 7 pixels (14 dets) in a hexagon layout at the boresight
    and with a made up polarization orientation.

    Args:
        None

    Returns:
        (dict):  dictionary of detectors and their properties.

    """
    from toast.tod import hex_pol_angles_radial, hex_pol_angles_qu, hex_layout

    zaxis = np.array([0, 0, 1.0])
    
    pol_A = hex_pol_angles_qu(npix)
    pol_B = hex_pol_angles_qu(npix, offset=90.0)
    
    dets_A = hex_layout(npix, fov, "", "", pol_A)
    dets_B = hex_layout(npix, fov, "", "", pol_B)
    
    dets = dict()
    for p in range(npix):
        pstr = "{:01d}".format(p)
        for d, layout in zip(["A", "B"], [dets_A, dets_B]):
            props = dict()
            props["quat"] = layout[pstr]["quat"]
            props["epsilon"] = epsilon
            props["rate"] = samplerate
            props["alpha"] = alpha
            props["NET"] = net
            props["fmin"] = fmin
            props["fknee"] = fknee
            props["fwhm_arcmin"] = fwhm
            dname = "{}{}".format(pstr, d)
            dets[dname] = props
    return dets


def parse_arguments(comm):
    """ Declare and parse command line arguments
    
    """
    parser = argparse.ArgumentParser(fromfile_prefix_chars="@")
    
    # pipeline_tools provides arguments to supported operators
    toast.pipeline_tools.add_noise_args(parser)
    toast.pipeline_tools.add_polyfilter_args(parser)
    
    # The pipeline may add its own arguments
    parser.add_argument(
        "--nsample", 
        required=False, 
        default=10000,
        type=np.int,
        help="Length of the simulation",
    )
    
    args = parser.parse_args()
    return args


def create_observations(comm, args):
    """ Create the TOAST data object using `args`
    
    This method will look very different for different pipelines.
    """
    focalplane = fake_focalplane(samplerate=args.sample_rate, fknee=1.0)
    detnames = list(sorted(focalplane.keys()))
    detquats = {x: focalplane[x]["quat"] for x in detnames}
    tod = toast.tod.TODCache(None, detnames, args.nsample, detquats=detquats)
    
    # Write some auxiliary data
    tod.write_times(stamps=np.arange(args.nsample) / args.sample_rate)
    tod.write_common_flags(flags=np.zeros(args.nsample, dtype=np.uint8))
    for d in detnames:
        tod.write_flags(detector=d, flags=np.zeros(args.nsample, dtype=np.uint8))
    
    noise = toast.pipeline_tools.get_analytic_noise(args, comm, focalplane)
    
    observation = {"tod" : tod, "noise" : noise, "name" : "obs-000", "id" : 0}
    
    data = toast.Data(comm)
    data.obs.append(observation)
    
    return data


def main():
    """ The `main` will instantiate and process the data.
    
    """
    mpiworld, procs, rank, comm = toast.pipeline_tools.get_comm()
    args = parse_arguments(comm)
    data = create_observations(comm, args)
    
    mc = 0
    name = "signal"
    tod = data.obs[0]["tod"]
    det = tod.local_dets[0]
    
    # Simulate noise, write out one detector
    toast.pipeline_tools.simulate_noise(args, comm, data, mc, cache_prefix=name)
    tod.local_signal(det, name).tofile("noise.npy")
    
    # Apply polyfilter, write the filtered data
    toast.pipeline_tools.apply_polyfilter(args, comm, data, cache_name=name)
    tod.local_signal(det, name).tofile("filtered.npy")
    

if __name__ == "__main__":
    main()
Writing my_first_pipeline.py
In [3]:
! python3 my_first_pipeline.py --help
<toast.Environment
  Source code version = 2.3.1.dev1428
  Logging level = INFO
  Handling enabled for 0 signals:
  Max threads = 4
  MPI build enabled
  MPI runtime disabled
  Cannot use MPI on NERSC login nodes
>
TOAST INFO: Running serially with one process at 2019-10-16 05:48:05.465294
usage: my_first_pipeline.py [-h] [--noise] [--simulate-noise] [--no-noise]
                            [--no-simulate-noise] [--sample-rate SAMPLE_RATE]
                            [--polyfilter] [--no-polyfilter]
                            [--poly-order POLY_ORDER]
                            [--common-flag-mask COMMON_FLAG_MASK]
                            [--nsample NSAMPLE]

optional arguments:
  -h, --help            show this help message and exit
  --noise               Add simulated noise
  --simulate-noise      Add simulated noise
  --no-noise            Do not add simulated noise
  --no-simulate-noise   Do not add simulated noise
  --sample-rate SAMPLE_RATE
                        Detector sample rate (Hz)
  --polyfilter          Apply polynomial filter
  --no-polyfilter       Do not apply polynomial filter
  --poly-order POLY_ORDER
                        Polynomial order for the polyfilter
  --common-flag-mask COMMON_FLAG_MASK
                        Common flag mask
  --nsample NSAMPLE     Length of the simulation
In [4]:
! python3 my_first_pipeline.py --simulate-noise --polyfilter --poly-order 6 --nsample 10000
<toast.Environment
  Source code version = 2.3.1.dev1428
  Logging level = INFO
  Handling enabled for 0 signals:
  Max threads = 4
  MPI build enabled
  MPI runtime disabled
  Cannot use MPI on NERSC login nodes
>
TOAST INFO: Running serially with one process at 2019-10-16 05:48:07.197869
TOAST INFO: Creating noise model:  0.00 seconds (1 calls)
TOAST INFO: Simulating noise
TOAST INFO: Simulate noise:  0.04 seconds (1 calls)
TOAST INFO: Polyfiltering signal
TOAST INFO: Polynomial filtering:  0.08 seconds (1 calls)
In [5]:
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np

noise = np.fromfile("noise.npy")
filtered = np.fromfile("filtered.npy")
plt.plot(noise, '.', label="noise")
plt.plot(noise - filtered, '-', label="polynomial")
plt.legend()
Out[5]:
<matplotlib.legend.Legend at 0x2aaaf0dfa048>

Satellite simulation pipeline

pipelines/toast_satellite_sim.py

In [6]:
! toast_satellite_sim.py --help
<toast.Environment
  Source code version = 2.3.1.dev1428
  Logging level = INFO
  Handling enabled for 0 signals:
  Max threads = 4
  MPI build enabled
  MPI runtime disabled
  Cannot use MPI on NERSC login nodes
>
TOAST INFO: Running serially with one process at 2019-10-16 05:48:09.518620
usage: toast_satellite_sim.py [-h] [--group-size GROUP_SIZE] [--day-maps]
                              [--no-day-maps] [--season-maps]
                              [--no-season-maps] [--nside NSIDE]
                              [--coord COORD] [--single-precision-pointing]
                              [--no-single-precision-pointing]
                              [--common-flag-mask COMMON_FLAG_MASK] [--flush]
                              [--tidas TIDAS] [--spt3g SPT3G] [--dipole]
                              [--no-dipole] [--dipole-mode DIPOLE_MODE]
                              [--dipole-solar-speed-kms DIPOLE_SOLAR_SPEED_KMS]
                              [--dipole-solar-gal-lat-deg DIPOLE_SOLAR_GAL_LAT_DEG]
                              [--dipole-solar-gal-lon-deg DIPOLE_SOLAR_GAL_LON_DEG]
                              [--pysm-model PYSM_MODEL] [--pysm-apply-beam]
                              [--no-pysm-apply-beam]
                              [--pysm-precomputed-cmb-K_CMB PYSM_PRECOMPUTED_CMB_K_CMB]
                              [--MC-start MC_START] [--MC-count MC_COUNT]
                              [--noise] [--simulate-noise] [--no-noise]
                              [--no-simulate-noise]
                              [--sample-rate SAMPLE_RATE]
                              [--start-time START_TIME]
                              [--spin-period-min SPIN_PERIOD_MIN]
                              [--spin-angle-deg SPIN_ANGLE_DEG]
                              [--prec-period-min PREC_PERIOD_MIN]
                              [--prec-angle-deg PREC_ANGLE_DEG]
                              [--obs-time-h OBS_TIME_H] [--gap-h GAP_H]
                              [--obs-num OBS_NUM] [--hwp-rpm HWP_RPM]
                              [--hwp-step-deg HWP_STEP_DEG]
                              [--hwp-step-time-s HWP_STEP_TIME_S]
                              [--outdir OUTDIR] [--debug]
                              [--madam-prefix MADAM_PREFIX]
                              [--madam-iter-max MADAM_ITER_MAX]
                              [--madam-precond-width MADAM_PRECOND_WIDTH]
                              [--madam-precond-width-min MADAM_PRECOND_WIDTH_MIN]
                              [--madam-precond-width-max MADAM_PRECOND_WIDTH_MAX]
                              [--madam-baseline-length MADAM_BASELINE_LENGTH]
                              [--madam-baseline-order MADAM_BASELINE_ORDER]
                              [--madam-noisefilter]
                              [--madam-parfile MADAM_PARFILE]
                              [--madam-allreduce] [--no-madam-allreduce]
                              [--madam-concatenate-messages]
                              [--no-madam-concatenate-messages] [--destripe]
                              [--no-destripe] [--binmap] [--no-binmap]
                              [--hits] [--no-hits] [--wcov] [--no-wcov]
                              [--wcov-inv] [--no-wcov-inv] [--conserve-memory]
                              [--no-conserve-memory] [--zip] [--no-zip]
                              [--madam] [--no-madam] [--focalplane FOCALPLANE]
                              [--gain GAIN]

Simulate satellite boresight pointing and make a map.

optional arguments:
  -h, --help            show this help message and exit
  --group-size GROUP_SIZE
                        Size of a process group assigned to an observation
  --day-maps            Enable daily maps
  --no-day-maps         Disable daily maps
  --season-maps         Enable season maps
  --no-season-maps      Disable season maps
  --nside NSIDE         Healpix NSIDE
  --coord COORD         Sky coordinate system [C,E,G]
  --single-precision-pointing
                        Use single precision for pointing in memory.
  --no-single-precision-pointing
                        Use single precision for pointing in memory.
  --common-flag-mask COMMON_FLAG_MASK
                        Common flag mask
  --flush               Flush every print statement.
  --tidas TIDAS         Output TIDAS export path
  --spt3g SPT3G         Output SPT3G export path
  --dipole              Add simulated dipole
  --no-dipole           Do not add simulated dipole
  --dipole-mode DIPOLE_MODE
                        Dipole mode is 'total', 'orbital' or 'solar'
  --dipole-solar-speed-kms DIPOLE_SOLAR_SPEED_KMS
                        Solar system speed [km/s]
  --dipole-solar-gal-lat-deg DIPOLE_SOLAR_GAL_LAT_DEG
                        Solar system speed galactic latitude [degrees]
  --dipole-solar-gal-lon-deg DIPOLE_SOLAR_GAL_LON_DEG
                        Solar system speed galactic longitude[degrees]
  --pysm-model PYSM_MODEL
                        Comma separated models for on-the-fly PySM simulation,
                        e.g. "s1,d6,f1,a2"
  --pysm-apply-beam     Convolve sky with detector beam
  --no-pysm-apply-beam  Do not convolve sky with detector beam.
  --pysm-precomputed-cmb-K_CMB PYSM_PRECOMPUTED_CMB_K_CMB
                        Precomputed CMB map for PySM in K_CMBit overrides any
                        model defined in pysm_model"
  --MC-start MC_START   First Monte Carlo noise realization
  --MC-count MC_COUNT   Number of Monte Carlo noise realizations
  --noise               Add simulated noise
  --simulate-noise      Add simulated noise
  --no-noise            Do not add simulated noise
  --no-simulate-noise   Do not add simulated noise
  --sample-rate SAMPLE_RATE
                        Detector sample rate (Hz)
  --start-time START_TIME
                        The overall start time of the simulation
  --spin-period-min SPIN_PERIOD_MIN
                        The period (in minutes) of the rotation about the spin
                        axis
  --spin-angle-deg SPIN_ANGLE_DEG
                        The opening angle (in degrees) of the boresight from
                        the spin axis
  --prec-period-min PREC_PERIOD_MIN
                        The period (in minutes) of the rotation about the
                        precession axis
  --prec-angle-deg PREC_ANGLE_DEG
                        The opening angle (in degrees) of the spin axis from
                        the precession axis
  --obs-time-h OBS_TIME_H
                        Number of hours in one science observation
  --gap-h GAP_H         Cooler cycle time in hours between science obs
  --obs-num OBS_NUM     Number of complete observations
  --hwp-rpm HWP_RPM     The rate (in RPM) of the HWP rotation
  --hwp-step-deg HWP_STEP_DEG
                        For stepped HWP, the angle in degrees of each step
  --hwp-step-time-s HWP_STEP_TIME_S
                        For stepped HWP, the time in seconds between steps
  --outdir OUTDIR       Output directory
  --debug               Write diagnostics
  --madam-prefix MADAM_PREFIX
                        Output map prefix
  --madam-iter-max MADAM_ITER_MAX
                        Maximum number of CG iterations in Madam
  --madam-precond-width MADAM_PRECOND_WIDTH
                        Width of the Madam band preconditioner
  --madam-precond-width-min MADAM_PRECOND_WIDTH_MIN
                        Minimum width of the Madam band preconditioner
  --madam-precond-width-max MADAM_PRECOND_WIDTH_MAX
                        Maximum width of the Madam band preconditioner
  --madam-baseline-length MADAM_BASELINE_LENGTH
                        Destriping baseline length (seconds)
  --madam-baseline-order MADAM_BASELINE_ORDER
                        Destriping baseline polynomial order
  --madam-noisefilter   Destripe with the noise filter enabled
  --madam-parfile MADAM_PARFILE
                        Madam parameter file
  --madam-allreduce     Use the allreduce commucation pattern in Madam
  --no-madam-allreduce  Do not use the allreduce commucation pattern in Madam
  --madam-concatenate-messages
                        Use the alltoallv commucation pattern in Madam
  --no-madam-concatenate-messages
                        Use the point-to-point commucation pattern in Madam
  --destripe            Write destriped maps [default]
  --no-destripe         Do not write destriped maps
  --binmap              Write binned maps [default]
  --no-binmap           Do not write binned maps
  --hits                Write hit maps [default]
  --no-hits             Do not write hit maps
  --wcov                Write white noise covariance [default]
  --no-wcov             Do not write white noise covariance
  --wcov-inv            Write inverse white noise covariance [default]
  --no-wcov-inv         Do not write inverse white noise covariance
  --conserve-memory     Conserve memory when staging libMadam buffers
                        [default]
  --no-conserve-memory  Do not conserve memory when staging libMadam buffers
  --zip                 Compress the map outputs
  --no-zip              Do not compress the map outputs
  --madam               Use libmadam for map-making
  --no-madam            Do not use libmadam for map-making [default]
  --focalplane FOCALPLANE
                        Pickle file containing a dictionary of detector
                        properties. The keys of this dict are the detector
                        names, and each value is also a dictionary with keys
                        "quat" (4 element ndarray), "fwhm" (float, arcmin),
                        "fknee" (float, Hz), "alpha" (float), and "NET"
                        (float). For optional plotting, the key "color" can
                        specify a valid matplotlib color string.
  --gain GAIN           Calibrate the input timelines with a set of gains from
                        aFITS file containing 3 extensions:HDU named DETECTORS
                        : table with list of detector names in a column named
                        DETECTORSHDU named TIME: table with common timestamps
                        column named TIMEHDU named GAINS: 2D image of floats
                        with one row per detector and one column per value.

main without the profiling commands:

def main():
    env = Environment.get()

    mpiworld, procs, rank, comm = get_comm()
    args, comm, groupsize = parse_arguments(comm, procs)

    # Parse options                                                                                          

    if comm.world_rank == 0:
        os.makedirs(args.outdir, exist_ok=True)

    focalplane, gain, detweights = load_focalplane(args, comm)

    data = create_observations(args, comm, focalplane, groupsize)

    expand_pointing(args, comm, data)

    localpix, localsm, subnpix = get_submaps(args, comm, data)

    signalname = None
    skyname = simulate_sky_signal(
        args, comm, data, [focalplane], subnpix, localsm, "signal"
    )
    if skyname is not None:
        signalname = skyname

    diponame = simulate_dipole(args, comm, data, "signal")
    if diponame is not None:
        signalname = diponame

    # Mapmaking                                                                                         

    if not args.use_madam:
        # THIS BRANCH WILL SOON BE REPLACED WITH THE NATIVE MAPMAKER
    else:
        # Initialize madam parameters                                                                                          

        madampars = setup_madam(args)

        # Loop over Monte Carlos                                                                                         

        firstmc = args.MC_start
        nmc = args.MC_count

        for mc in range(firstmc, firstmc + nmc):
            # create output directory for this realization
            outpath = os.path.join(args.outdir, "mc_{:03d}".format(mc))

            simulate_noise(args, comm, data, mc, "tot_signal", overwrite=True)

            # add sky signal
            add_signal(args, comm, data, "tot_signal", signalname)

            if gain is not None:
                op_apply_gain = OpApplyGain(gain, name="tot_signal")
                op_apply_gain.exec(data)

            apply_madam(args, comm, data, madampars, outpath, detweights, "tot_signal")

Ground simulation pipeline

pipelines/toast_ground_sim.py

In [7]:
! toast_ground_sim.py --help
<toast.Environment
  Source code version = 2.3.1.dev1428
  Logging level = INFO
  Handling enabled for 0 signals:
  Max threads = 4
  MPI build enabled
  MPI runtime disabled
  Cannot use MPI on NERSC login nodes
>
TOAST INFO: Running serially with one process at 2019-10-16 05:48:11.249650
usage: toast_ground_sim.py [-h] [--group-size GROUP_SIZE] [--day-maps]
                           [--no-day-maps] [--season-maps] [--no-season-maps]
                           [--debug] [--no-debug] [--scan-rate SCAN_RATE]
                           [--scan-accel SCAN_ACCEL]
                           [--sun-angle-min SUN_ANGLE_MIN] --schedule SCHEDULE
                           [--weather WEATHER] [--timezone TIMEZONE]
                           [--sample-rate SAMPLE_RATE] [--coord COORD]
                           [--split-schedule SPLIT_SCHEDULE] [--sort-schedule]
                           [--no-sort-schedule] [--hwp-rpm HWP_RPM]
                           [--hwp-step-deg HWP_STEP_DEG]
                           [--hwp-step-time-s HWP_STEP_TIME_S] [--nside NSIDE]
                           [--single-precision-pointing]
                           [--no-single-precision-pointing]
                           [--common-flag-mask COMMON_FLAG_MASK] [--flush]
                           [--polyfilter] [--no-polyfilter]
                           [--poly-order POLY_ORDER] [--groundfilter]
                           [--no-groundfilter] [--ground-order GROUND_ORDER]
                           [--atmosphere] [--simulate-atmosphere]
                           [--no-atmosphere] [--no-simulate-atmosphere]
                           [--focalplane-radius-deg FOCALPLANE_RADIUS_DEG]
                           [--atm-verbosity ATM_VERBOSITY]
                           [--atm-lmin-center ATM_LMIN_CENTER]
                           [--atm-lmin-sigma ATM_LMIN_SIGMA]
                           [--atm-lmax-center ATM_LMAX_CENTER]
                           [--atm-lmax-sigma ATM_LMAX_SIGMA]
                           [--atm-gain ATM_GAIN] [--atm-zatm ATM_ZATM]
                           [--atm-zmax ATM_ZMAX] [--atm-xstep ATM_XSTEP]
                           [--atm-ystep ATM_YSTEP] [--atm-zstep ATM_ZSTEP]
                           [--atm-nelem-sim-max ATM_NELEM_SIM_MAX]
                           [--atm-wind-dist ATM_WIND_DIST]
                           [--atm-z0-center ATM_Z0_CENTER]
                           [--atm-z0-sigma ATM_Z0_SIGMA]
                           [--atm-T0-center ATM_T0_CENTER]
                           [--atm-T0-sigma ATM_T0_SIGMA]
                           [--atm-cache ATM_CACHE] [--noise]
                           [--simulate-noise] [--no-noise]
                           [--no-simulate-noise] [--gainscrambler]
                           [--no-gainscrambler] [--gain-sigma GAIN_SIGMA]
                           [--madam-prefix MADAM_PREFIX]
                           [--madam-iter-max MADAM_ITER_MAX]
                           [--madam-precond-width MADAM_PRECOND_WIDTH]
                           [--madam-precond-width-min MADAM_PRECOND_WIDTH_MIN]
                           [--madam-precond-width-max MADAM_PRECOND_WIDTH_MAX]
                           [--madam-baseline-length MADAM_BASELINE_LENGTH]
                           [--madam-baseline-order MADAM_BASELINE_ORDER]
                           [--madam-noisefilter]
                           [--madam-parfile MADAM_PARFILE] [--madam-allreduce]
                           [--no-madam-allreduce]
                           [--madam-concatenate-messages]
                           [--no-madam-concatenate-messages] [--destripe]
                           [--no-destripe] [--binmap] [--no-binmap] [--hits]
                           [--no-hits] [--wcov] [--no-wcov] [--wcov-inv]
                           [--no-wcov-inv] [--conserve-memory]
                           [--no-conserve-memory] [--input-map INPUT_MAP]
                           [--pysm-model PYSM_MODEL] [--pysm-apply-beam]
                           [--no-pysm-apply-beam]
                           [--pysm-precomputed-cmb-K_CMB PYSM_PRECOMPUTED_CMB_K_CMB]
                           [--ground-map GROUND_MAP]
                           [--ground-nside GROUND_NSIDE]
                           [--ground-fwhm-deg GROUND_FWHM_DEG]
                           [--ground-lmax GROUND_LMAX]
                           [--ground-scale GROUND_SCALE]
                           [--ground-power GROUND_POWER] [--simulate-ground]
                           [--no-simulate-ground] [--tidas TIDAS]
                           [--spt3g SPT3G] [--MC-start MC_START]
                           [--MC-count MC_COUNT] [--outdir OUTDIR]
                           [--focalplane FOCALPLANE] --freq FREQ

Simulate ground-based boresight pointing. Simulate atmosphere and make maps
for some number of noise Monte Carlos.

optional arguments:
  -h, --help            show this help message and exit
  --group-size GROUP_SIZE
                        Size of a process group assigned to an observation
  --day-maps            Enable daily maps
  --no-day-maps         Disable daily maps
  --season-maps         Enable season maps
  --no-season-maps      Disable season maps
  --debug               Enable extra debugging outputs
  --no-debug            Disable extra debugging outputs
  --scan-rate SCAN_RATE
                        Scanning rate [deg / s]
  --scan-accel SCAN_ACCEL
                        Scanning rate change [deg / s^2]
  --sun-angle-min SUN_ANGLE_MIN
                        Minimum azimuthal distance between the Sun and the
                        bore sight [deg]
  --schedule SCHEDULE   Comma-separated list CES schedule files (from
                        toast_ground_schedule.py)
  --weather WEATHER     Comma-separated list of TOAST weather files for every
                        schedule. Repeat the same file if the schedules share
                        observing site.
  --timezone TIMEZONE   Offset to apply to MJD to separate days [hours]
  --sample-rate SAMPLE_RATE
                        Detector sample rate (Hz)
  --coord COORD         Sky coordinate system [C,E,G]
  --split-schedule SPLIT_SCHEDULE
                        Only use a subset of the schedule. The argument is a
                        string of the form "[isplit],[nsplit]" and only
                        observations that satisfy scan modulo nsplit == isplit
                        are included
  --sort-schedule       Reorder the observing schedule so that observations of
                        thesame patch are consecutive. This will reduce the
                        sky area observed by individual process groups.
  --no-sort-schedule    Do not reorder the observing schedule so that
                        observations of thesame patch are consecutive.
  --hwp-rpm HWP_RPM     The rate (in RPM) of the HWP rotation
  --hwp-step-deg HWP_STEP_DEG
                        For stepped HWP, the angle in degrees of each step
  --hwp-step-time-s HWP_STEP_TIME_S
                        For stepped HWP, the time in seconds between steps
  --nside NSIDE         Healpix NSIDE
  --single-precision-pointing
                        Use single precision for pointing in memory.
  --no-single-precision-pointing
                        Use single precision for pointing in memory.
  --common-flag-mask COMMON_FLAG_MASK
                        Common flag mask
  --flush               Flush every print statement.
  --polyfilter          Apply polynomial filter
  --no-polyfilter       Do not apply polynomial filter
  --poly-order POLY_ORDER
                        Polynomial order for the polyfilter
  --groundfilter        Apply ground filter
  --no-groundfilter     Do not apply ground filter
  --ground-order GROUND_ORDER
                        Ground template order
  --atmosphere          Add simulated atmoshere
  --simulate-atmosphere
                        Add simulated atmoshere
  --no-atmosphere       Do not add simulated atmosphere
  --no-simulate-atmosphere
                        Do not add simulated atmosphere
  --focalplane-radius-deg FOCALPLANE_RADIUS_DEG
                        Override focal plane radius [deg]
  --atm-verbosity ATM_VERBOSITY
                        Atmospheric sim verbosity level
  --atm-lmin-center ATM_LMIN_CENTER
                        Kolmogorov turbulence dissipation scale center
  --atm-lmin-sigma ATM_LMIN_SIGMA
                        Kolmogorov turbulence dissipation scale sigma
  --atm-lmax-center ATM_LMAX_CENTER
                        Kolmogorov turbulence injection scale center
  --atm-lmax-sigma ATM_LMAX_SIGMA
                        Kolmogorov turbulence injection scale sigma
  --atm-gain ATM_GAIN   Atmospheric gain factor.
  --atm-zatm ATM_ZATM   atmosphere extent for temperature profile
  --atm-zmax ATM_ZMAX   atmosphere extent for water vapor integration
  --atm-xstep ATM_XSTEP
                        size of volume elements in X direction
  --atm-ystep ATM_YSTEP
                        size of volume elements in Y direction
  --atm-zstep ATM_ZSTEP
                        size of volume elements in Z direction
  --atm-nelem-sim-max ATM_NELEM_SIM_MAX
                        controls the size of the simulation slices
  --atm-wind-dist ATM_WIND_DIST
                        Maximum wind drift to simulate without discontinuity
  --atm-z0-center ATM_Z0_CENTER
                        central value of the water vapor distribution
  --atm-z0-sigma ATM_Z0_SIGMA
                        sigma of the water vapor distribution
  --atm-T0-center ATM_T0_CENTER
                        central value of the temperature distribution
  --atm-T0-sigma ATM_T0_SIGMA
                        sigma of the temperature distribution
  --atm-cache ATM_CACHE
                        Atmosphere cache directory
  --noise               Add simulated noise
  --simulate-noise      Add simulated noise
  --no-noise            Do not add simulated noise
  --no-simulate-noise   Do not add simulated noise
  --gainscrambler       Add simulated noise
  --no-gainscrambler    Do not add simulated noise
  --gain-sigma GAIN_SIGMA
                        Simulated gain fluctuation amplitude
  --madam-prefix MADAM_PREFIX
                        Output map prefix
  --madam-iter-max MADAM_ITER_MAX
                        Maximum number of CG iterations in Madam
  --madam-precond-width MADAM_PRECOND_WIDTH
                        Width of the Madam band preconditioner
  --madam-precond-width-min MADAM_PRECOND_WIDTH_MIN
                        Minimum width of the Madam band preconditioner
  --madam-precond-width-max MADAM_PRECOND_WIDTH_MAX
                        Maximum width of the Madam band preconditioner
  --madam-baseline-length MADAM_BASELINE_LENGTH
                        Destriping baseline length (seconds)
  --madam-baseline-order MADAM_BASELINE_ORDER
                        Destriping baseline polynomial order
  --madam-noisefilter   Destripe with the noise filter enabled
  --madam-parfile MADAM_PARFILE
                        Madam parameter file
  --madam-allreduce     Use the allreduce commucation pattern in Madam
  --no-madam-allreduce  Do not use the allreduce commucation pattern in Madam
  --madam-concatenate-messages
                        Use the alltoallv commucation pattern in Madam
  --no-madam-concatenate-messages
                        Use the point-to-point commucation pattern in Madam
  --destripe            Write destriped maps [default]
  --no-destripe         Do not write destriped maps
  --binmap              Write binned maps [default]
  --no-binmap           Do not write binned maps
  --hits                Write hit maps [default]
  --no-hits             Do not write hit maps
  --wcov                Write white noise covariance [default]
  --no-wcov             Do not write white noise covariance
  --wcov-inv            Write inverse white noise covariance [default]
  --no-wcov-inv         Do not write inverse white noise covariance
  --conserve-memory     Conserve memory when staging libMadam buffers
                        [default]
  --no-conserve-memory  Do not conserve memory when staging libMadam buffers
  --input-map INPUT_MAP
                        Input map for signal
  --pysm-model PYSM_MODEL
                        Comma separated models for on-the-fly PySM simulation,
                        e.g. "s1,d6,f1,a2"
  --pysm-apply-beam     Convolve sky with detector beam
  --no-pysm-apply-beam  Do not convolve sky with detector beam.
  --pysm-precomputed-cmb-K_CMB PYSM_PRECOMPUTED_CMB_K_CMB
                        Precomputed CMB map for PySM in K_CMBit overrides any
                        model defined in pysm_model"
  --ground-map GROUND_MAP
                        Fixed ground template map
  --ground-nside GROUND_NSIDE
                        Ground template resolution
  --ground-fwhm-deg GROUND_FWHM_DEG
                        Ground template smoothing in degrees
  --ground-lmax GROUND_LMAX
                        Ground template expansion order
  --ground-scale GROUND_SCALE
                        Ground template RMS at el=45 deg
  --ground-power GROUND_POWER
                        Exponential for suppressing ground pick-up at higher
                        observing elevation
  --simulate-ground     Enable simulating ground pickup.
  --no-simulate-ground  Disable simulating ground pickup.
  --tidas TIDAS         Output TIDAS export path
  --spt3g SPT3G         Output SPT3G export path
  --MC-start MC_START   First Monte Carlo noise realization
  --MC-count MC_COUNT   Number of Monte Carlo noise realizations
  --outdir OUTDIR       Output directory
  --focalplane FOCALPLANE
                        Pickle file containing a dictionary of detector
                        properties. The keys of this dict are the detector
                        names, and each value is also a dictionary with keys
                        "quat" (4 element ndarray), "fwhm" (float, arcmin),
                        "fknee" (float, Hz), "alpha" (float), and "NET"
                        (float).
  --freq FREQ           Comma-separated list of frequencies with identical
                        focal planes. They override the bandpasses in the
                        focalplane for the purpose of scaling the atmospheric
                        signal but not for simulating the sky signal.

main without profiling

def main():
    mpiworld, procs, rank, comm = get_comm()

    args, comm = parse_arguments(comm)

    # Initialize madam parameters                                                                                          

    madampars = setup_madam(args)

    # Load and broadcast the schedule file                                                                                         

    schedules = load_schedule(args, comm)

    # Load the weather and append to schedules                                                                                         

    load_weather(args, comm, schedules)

    # load or simulate the focalplane                                                                                          

    detweights = load_focalplanes(args, comm, schedules)

    # Create the TOAST data object to match the schedule.  This will                                                                                         
    # include simulating the boresight pointing                                                                                         

    data, telescope_data = create_observations(args, comm, schedules)

    # Split the communicator for day and season mapmaking                                                                                          

    time_comms = get_time_communicators(args, comm, data)

    # Expand boresight quaternions into detector pointing weights and                                                                                          
    # pixel numbers                                                                                          

    expand_pointing(args, comm, data)

    # Purge the pointing if we are NOT going to export the                                                                                         
    # data to a TIDAS volume                                                                                         
    if (args.tidas is None) and (args.spt3g is None):
        for ob in data.obs:
            tod = ob["tod"]
            tod.free_radec_quats()

    # Prepare auxiliary information for distributed map objects                                                                                          

    _, localsm, subnpix = get_submaps(args, comm, data)

    if args.pysm_model:
        focalplanes = [s.telescope.focalplane.detector_data for s in schedules]
        signalname = simulate_sky_signal(
            args, comm, data, focalplanes, subnpix, localsm, "signal"
        )
    else:
        signalname = scan_sky_signal(args, comm, data, localsm, subnpix, "signal")

    # Set up objects to take copies of the TOD at appropriate times                                                                                          

    totalname, totalname_freq = setup_sigcopy(args)

    # Loop over Monte Carlos

    firstmc = args.MC_start
    nsimu = args.MC_count

    freqs = [float(freq) for freq in args.freq.split(",")]
    nfreq = len(freqs)

    for mc in range(firstmc, firstmc + nsimu):

        simulate_atmosphere(args, comm, data, mc, totalname)

        # Loop over frequencies with identical focal planes and identical                                                                                          
        # atmospheric noise                                                                                         

        for ifreq, freq in enumerate(freqs):
            # Make a copy of the atmosphere so we can scramble the gains and apply                                                                                         
            # frequency-dependent scaling

            copy_signal(args, comm, data, totalname, totalname_freq)

            scale_atmosphere_by_frequency(
                args, comm, data, freq=freq, mc=mc, cache_name=totalname_freq
            )

            update_atmospheric_noise_weights(args, comm, data, freq, mc)

            # Add previously simulated sky signal to the atmospheric noise                                                                                          

            add_signal(args, comm, data, totalname_freq, signalname, purge=(nsimu == 1))

            mcoffset = ifreq * 1000000

            simulate_noise(args, comm, data, mc + mcoffset, totalname_freq)

            simulate_sss(args, comm, data, mc + mcoffset, totalname_freq)

            scramble_gains(args, comm, data, mc + mcoffset, totalname_freq)

            if (mc == firstmc) and (ifreq == 0):
                # For the first realization and frequency, optionally                                                                                                                                                                                                                 
                # export the timestream data

                output_tidas(args, comm, data, totalname)
                output_spt3g(args, comm, data, totalname)

            outpath = setup_output(args, comm, mc + mcoffset, freq)

            # Bin and destripe maps                                                                                          

            apply_madam(
                args,
                comm,
                data,
                madampars,
                outpath,
                detweights,
                totalname_freq,
                freq=freq,
                time_comms=time_comms,
                telescope_data=telescope_data,
                first_call=(mc == firstmc),
            )

            if args.apply_polyfilter or args.apply_groundfilter:

                # Filter signal                                                                                          

                apply_polyfilter(args, comm, data, totalname_freq)

                apply_groundfilter(args, comm, data, totalname_freq)

                # Bin filtered maps                                                                                          

                apply_madam(
                    args,
                    comm,
                    data,
                    madampars,
                    outpath,
                    detweights,
                    totalname_freq,
                    freq=freq,
                    time_comms=time_comms,
                    telescope_data=telescope_data,
                    first_call=False,
                    extra_prefix="filtered",
                    bin_only=True,
                )

Here is a full working example of the ground simulation pipeline

First we need an observing schedule. This one is for one patch and covers 24 hours:

In [8]:
! toast_ground_schedule.py \
    --site-lat "-22.958064" \
    --site-lon "-67.786222" \
    --site-alt 5200 \
    --site-name Atacama \
    --telescope LAT \
    --start "2020-01-01 00:00:00" \
    --stop "2020-01-02 00:00:00" \
    --patch-coord C \
    --patch small_patch,1,40,-40,10 \
    --el-min 45 \
    --el-max 60 \
    --out schedule.txt

! cat schedule.txt
TOAST INFO: Adding patch "small_patch"
TOAST INFO: Center-and-width format
TOAST INFO: Global timer: toast_ground_schedule:  0.13 seconds (1 calls)
#Site            Telescope        Latitude [deg] Longitude [deg]   Elevation [m]
 Atacama         LAT                     -22.958         -67.786          5200.0
#Start time UTC       Stop time UTC        Start MJD      Stop MJD       Patch name                          Az min   Az max   El       R/S   Sun el1  Sun az1  Sun el2  Sun az2  Moon el1 Moon az1 Moon el2 Moon az2 Phase Pass  Sub
 2020-01-01 02:00:00  2020-01-01 02:14:30    58849.083333   58849.093403 small_patch                           217.44   234.41    59.64 S       -30.65   221.82   -32.81   218.83    19.11   267.74    15.85   266.47  0.31     0   0
 2020-01-01 02:14:40  2020-01-01 02:29:10    58849.093519   58849.103588 small_patch                           217.53   238.45    59.64 S       -32.83   218.80   -34.85   215.60    15.81   266.45    12.55   265.18  0.31     0   1
 2020-01-01 02:29:20  2020-01-01 02:43:50    58849.103704   58849.113773 small_patch                           219.01   239.50    59.64 S       -34.87   215.56   -36.73   212.15    12.52   265.17     9.28   263.90  0.31     0   2
 2020-01-01 02:44:00  2020-01-01 02:58:00    58849.113889   58849.123611 small_patch                           222.66   238.93    59.64 S       -36.75   212.11   -38.38   208.61     9.24   263.89     6.13   262.65  0.31     0   3
 2020-01-01 02:59:40  2020-01-01 03:12:55    58849.124769   58849.133970 small_patch                           227.28   240.30    49.46 S       -38.56   208.18   -39.92   204.65     5.76   262.51     2.86   261.32  0.31     1   0
 2020-01-01 03:13:05  2020-01-01 03:26:20    58849.134086   58849.143287 small_patch                           226.82   242.20    49.46 S       -39.94   204.61   -41.12   200.91     2.83   261.31     0.05   260.11  0.31     1   1
 2020-01-01 03:26:30  2020-01-01 03:39:45    58849.143403   58849.152604 small_patch                           227.33   242.53    49.46 S       -41.13   200.86   -42.12   197.01     0.02   260.09    -2.42   258.86  0.31     1   2
 2020-01-01 03:39:55  2020-01-01 03:52:40    58849.152720   58849.161574 small_patch                           229.43   241.58    49.46 S       -42.13   196.96   -42.90   193.12    -2.45   258.85    -5.97   257.64  0.31     1   3
 2020-01-01 20:44:20  2020-01-01 20:57:35    58849.864120   58849.873322 small_patch                           117.99   129.78    45.23 R        33.02   256.67    30.06   255.82    67.82    42.00    69.66    34.59  0.38     2   0
 2020-01-01 20:57:45  2020-01-01 21:11:00    58849.873438   58849.882639 small_patch                           117.12   131.14    45.23 R        30.02   255.81    27.07   254.94    69.68    34.49    71.16    25.84  0.38     2   1
 2020-01-01 21:11:10  2020-01-01 21:24:25    58849.882755   58849.891956 small_patch                           117.33   131.42    45.23 R        27.04   254.93    24.10   254.02    71.17    25.73    72.21    15.90  0.38     2   2
 2020-01-01 21:24:35  2020-01-01 21:37:20    58849.892072   58849.900926 small_patch                           119.23   130.71    45.23 R        24.06   254.01    21.25   253.12    72.22    15.77    72.71     5.50  0.38     2   3
 2020-01-01 21:39:00  2020-01-01 21:52:45    58849.902083   58849.911632 small_patch                           119.49   133.93    55.47 R        20.89   253.00    17.87   251.99    72.74     4.13    72.61   352.79  0.38     3   0
 2020-01-01 21:52:55  2020-01-01 22:06:40    58849.911748   58849.921296 small_patch                           118.79   136.62    55.47 R        17.84   251.98    14.84   250.93    72.61   352.65    71.87   341.84  0.38     3   1
 2020-01-01 22:06:50  2020-01-01 22:20:35    58849.921412   58849.930961 small_patch                           119.55   137.47    55.47 R        14.80   250.92    11.83   249.83    71.86   341.71    70.59   331.99  0.38     3   2
 2020-01-01 22:20:45  2020-01-01 22:34:00    58849.931076   58849.940278 small_patch                           122.69   137.09    55.47 R        11.80   249.82     8.96   248.72    70.57   331.88    68.92   323.76  0.38     3   3

Then we need a focalplane

In [9]:
! toast_fake_focalplane.py \
    --minpix 100 \
    --out focalplane \
    --fwhm 30 \
    --fwhm_sigma 0.05 \
    --fov 10 \
    --psd_fknee 5e-2 \
    --psd_NET 1e-3 \
    --psd_alpha 1 \
    --psd_fmin 1e-5 \
    --bandcenter_ghz 100 \
    --bandcenter_sigma 0.01 \
    --bandwidth_ghz 10 \
    --bandwidth_sigma 0.1
TOAST INFO: using 127 pixels (254 detectors)

And of course we need the weather file

In [10]:
! [[ ! -e weather_Atacama.fits ]] && wget http://portal.nersc.gov/project/cmb/toast_data/example_data/weather_Atacama.fits
--2019-10-16 05:48:16--  http://portal.nersc.gov/project/cmb/toast_data/example_data/weather_Atacama.fits
Resolving portal.nersc.gov (portal.nersc.gov)... 128.55.201.128
Connecting to portal.nersc.gov (portal.nersc.gov)|128.55.201.128|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://portal.nersc.gov/project/cmb/toast_data/example_data/weather_Atacama.fits [following]
--2019-10-16 05:48:16--  https://portal.nersc.gov/project/cmb/toast_data/example_data/weather_Atacama.fits
Connecting to portal.nersc.gov (portal.nersc.gov)|128.55.201.128|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2180160 (2.1M) [image/fits]
Saving to: ‘weather_Atacama.fits’

weather_Atacama.fit 100%[===================>]   2.08M  --.-KB/s    in 0.009s  

2019-10-16 05:48:16 (241 MB/s) - ‘weather_Atacama.fits’ saved [2180160/2180160]

Now write a parameter file that uses the above inputs and simulates sky signal (using PySM), atmosphere and instrument noise

In [11]:
%%writefile toast_ground_sim.par
--weather
weather_Atacama.fits
--scan-rate
1
--scan-accel
3
--schedule
schedule.txt
--sample-rate
30
--coord
C
--hwp-rpm
2
--nside
512
--common-flag-mask
1
--polyfilter
--poly-order
5
--groundfilter
--ground-order
3
--atmosphere
--atm-lmin-center
0.1
--atm-lmax-center
30
--atm-gain
3e-5
--atm-zmax
1000
--atm-xstep
30
--atm-ystep
30
--atm-zstep
30
--atm-nelem-sim-max
10000
--atm-wind-dist
5000
--atm-cache
atm_cache
--noise
--gainscrambler
--gain-sigma
0.05
--madam-prefix
groundsim000
--madam-iter-max
200
--madam-precond-width
100
--madam-baseline-length
1
--madam-noisefilter
--madam-allreduce
--destripe
--binmap
--hits
--wcov
--ground-nside
512
--ground-fwhm-deg
3
--ground-lmax
512
--ground-scale
1e-3
--simulate-ground
--focalplane
focalplane_127.pkl
--freq
100
--pysm-model
s1,d1,f1,a1
--pysm-apply-beam
Writing toast_ground_sim.par

With the PySM part (last three lines) the simulation will take about 20 minutes on a single Cori Haswell node. If you remove them, it will run in about 4 minutes.

Finally, instead of submitting the job interactively with srun, we write an actual script and submit it.

In [12]:
%%writefile toast_ground_sim.slrm
#!/bin/bash
#SBATCH --partition=regular
#SBATCH --time=02:00:00
#SBATCH --nodes=1
#SBATCH --job-name=toast_ground_sim
#SBATCH --licenses=SCRATCH
#SBATCH --constraint=haswell
#SBATCH --account=mp107

ulimit -c unlimited

export MALLOC_MMAP_THRESHOLD_=131072
export PYTHONSTARTUP=""
export PYTHONNOUSERSITE=1
export HOME=/global/cscratch1/sd/keskital

export OMP_NUM_THREADS=2
export OMP_PLACES=threads
export OMP_PROC_BIND=spread

let nnode=1
let ntask_node=32/$OMP_NUM_THREADS
let ntask=$nnode*$ntask_node
let ncore=2*$OMP_NUM_THREADS

srun -n $ntask -c $ncore --cpu_bind=cores \
    toast_ground_sim.py \
    @toast_ground_sim.par \
    >& toast_ground_sim.log
Writing toast_ground_sim.slrm

The sbatch command below is commented out so it does not get executed every time this notebook runs.

In [13]:
# ! sbatch toast_ground_sim.slrm

Here we examine the intensity part of the simulated maps.

In [14]:
import healpy as hp
try:
    binned = hp.read_map("out/00000000/100/groundsim000_100_telescope_all_time_all_bmap.fits")
    destriped = hp.read_map("out/00000000/100/groundsim000_100_telescope_all_time_all_map.fits")
    filtered = hp.read_map("out/00000000/100/groundsim000_filtered_100_telescope_all_time_all_bmap.fits")

    rot = [40, -40]
    reso = 10
    cmap = "coolwarm"
    amp = 0.02
    unit = "K"
    plt.figure(figsize=[12, 8])
    hp.gnomview(binned, rot=rot, reso=reso, cmap=cmap, min=-amp, max=amp, sub=[1, 3, 1], title="binned", unit=unit)
    hp.gnomview(destriped, rot=rot, reso=reso, cmap=cmap, min=-amp, max=amp, sub=[1, 3, 2], title="destriped", unit=unit)
    hp.gnomview(filtered, rot=rot, reso=reso, cmap=cmap, min=-amp, max=amp, sub=[1, 3, 3], title="filtered", unit=unit)
except:
    print("Maps are not available.  `Either toast_ground_sim.slrm` failed or you did not submit it yet.")
Maps are not available.  `Either toast_ground_sim.slrm` failed or you did not submit it yet.
In [ ]: