def binned_map(data, npix, subnpix, out="."):
    """Make a binned map

    This function should exist in toast, but all the pieces do.  If we are
    doing MCs we break these operations into two pieces and only generate
    the noise weighted map each realization.

    """
    # The global MPI communicator
    cworld = data.comm.comm_world

    # construct distributed maps to store the covariance,
    # noise weighted map, and hits
    invnpp = tm.DistPixels(data, nnz=6, dtype=np.float64)
    hits = tm.DistPixels(data, nnz=1, dtype=np.int64)
    zmap = tm.DistPixels(data, nnz=3, dtype=np.float64)

    invnpp.data.fill(0.0)
    hits.data.fill(0)
    zmap.data.fill(0.0)

    start = MPI.Wtime()
    if cworld.rank == 0:
        print("Accumulating hits and N_pp'^-1 ...", flush=True)

    # Setting detweights to None gives uniform weighting.
    build_invnpp = tm.OpAccumDiag(detweights=None,
                                  invnpp=invnpp,
                                  hits=hits,
                                  zmap=zmap,
                                  name="signal",
                                  pixels="pixels",
                                  weights="weights",
                                  common_flag_name="flags_common",
                                  common_flag_mask=1)
    build_invnpp.exec(data)

    invnpp.allreduce()
    hits.allreduce()
    zmap.allreduce()

    cworld.barrier()
    stop = MPI.Wtime()
    elapsed = stop - start
    if cworld.rank == 0:
        print("Building hits and N_pp^-1 took {:.3f} s".format(elapsed),
              flush=True)

    if cworld.rank == 0:
        print("Writing hits and N_pp'^-1 ...", flush=True)

    hits.write_healpix_fits(os.path.join(out, "hits.fits"))
    invnpp.write_healpix_fits(os.path.join(out, "invnpp.fits"))

    start = stop
    if cworld.rank == 0:
        print("Inverting N_pp'^-1 ...", flush=True)

    # invert it
    tm.covariance_invert(invnpp, 1.0e-3)

    cworld.barrier()
    stop = MPI.Wtime()
    elapsed = stop - start
    if cworld.rank == 0:
        print("Inverting N_pp^-1 took {:.3f} s".format(elapsed), flush=True)

    if cworld.rank == 0:
        print("Writing N_pp' ...", flush=True)
    invnpp.write_healpix_fits(os.path.join(out, "npp.fits"))

    start = stop

    if cworld.rank == 0:
        print("Computing binned map ...", flush=True)

    tm.covariance_apply(invnpp, zmap)

    cworld.barrier()
    stop = MPI.Wtime()
    elapsed = stop - start
    if cworld.rank == 0:
        print("Computing binned map took {:.3f} s".format(elapsed), flush=True)
    start = stop

    if cworld.rank == 0:
        print("Writing binned map ...", flush=True)
    zmap.write_healpix_fits(os.path.join(out, "binned.fits"))
    if cworld.rank == 0:
        print("Binned map done", flush=True)

    return
def build_npp(args, comm, data, localsm, subnpix, detweights, flag_name,
              common_flag_name):
    """ Build pixel-pixel noise covariance matrices.

    """
    if not args.skip_bin:

        if comm.comm_world.rank == 0:
            print('Preparing distributed map', flush=args.flush)
        start0 = MPI.Wtime()
        start = start0
        autotimer = timing.auto_timer()

        npix = 12 * args.nside**2

        # construct distributed maps to store the covariance,
        # noise weighted map, and hits

        invnpp = tm.DistPixels(comm=comm.comm_world,
                               size=npix,
                               nnz=6,
                               dtype=np.float64,
                               submap=subnpix,
                               local=localsm)
        invnpp.data.fill(0.0)

        hits = tm.DistPixels(comm=comm.comm_world,
                             size=npix,
                             nnz=1,
                             dtype=np.int64,
                             submap=subnpix,
                             local=localsm)
        hits.data.fill(0)

        zmap = tm.DistPixels(comm=comm.comm_world,
                             size=npix,
                             nnz=3,
                             dtype=np.float64,
                             submap=subnpix,
                             local=localsm)

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        if comm.comm_world.rank == 0:
            print(' - distobjects initialized in {:.3f} s'
                  ''.format(stop - start),
                  flush=args.flush)
        start = stop

        invnpp_group = None
        hits_group = None
        zmap_group = None
        if comm.comm_group.size < comm.comm_world.size:
            invnpp_group = tm.DistPixels(comm=comm.comm_group,
                                         size=npix,
                                         nnz=6,
                                         dtype=np.float64,
                                         submap=subnpix,
                                         local=localsm)
            invnpp_group.data.fill(0.0)

            hits_group = tm.DistPixels(comm=comm.comm_group,
                                       size=npix,
                                       nnz=1,
                                       dtype=np.int64,
                                       submap=subnpix,
                                       local=localsm)
            hits_group.data.fill(0)

            zmap_group = tm.DistPixels(comm=comm.comm_group,
                                       size=npix,
                                       nnz=3,
                                       dtype=np.float64,
                                       submap=subnpix,
                                       local=localsm)

            comm.comm_group.barrier()
            stop = MPI.Wtime()
            if comm.comm_group.rank == 0:
                print(' - group distobjects initialized in {:.3f} s'
                      ''.format(stop - start),
                      flush=args.flush)
            start = stop

        # compute the hits and covariance once, since the pointing and noise
        # weights are fixed.

        build_invnpp = tm.OpAccumDiag(detweights=detweights,
                                      invnpp=invnpp,
                                      hits=hits,
                                      flag_name=flag_name,
                                      common_flag_name=common_flag_name,
                                      common_flag_mask=args.common_flag_mask)

        build_invnpp.exec(data)

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        if comm.comm_world.rank == 0:
            print(' - distobjects accumulated in {:.3f} s'
                  ''.format(stop - start),
                  flush=args.flush)
        start = stop

        invnpp.allreduce()
        if not args.skip_hits:
            hits.allreduce()

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        if comm.comm_world.rank == 0:
            print(' - distobjects reduced in {:.3f} s'.format(stop - start),
                  flush=args.flush)
        start = stop

        if invnpp_group is not None:
            build_invnpp_group = tm.OpAccumDiag(
                detweights=detweights,
                invnpp=invnpp_group,
                hits=hits_group,
                flag_name=flag_name,
                common_flag_name=common_flag_name,
                common_flag_mask=args.common_flag_mask)

            build_invnpp_group.exec(data)

            comm.comm_group.barrier()
            stop = MPI.Wtime()
            if comm.comm_group.rank == 0:
                print(' - group distobjects accumulated in {:.3f} s'
                      ''.format(stop - start),
                      flush=args.flush)
            start = stop

            invnpp_group.allreduce()
            if not args.skip_hits:
                hits_group.allreduce()

            comm.comm_group.barrier()
            stop = MPI.Wtime()
            if comm.comm_group.rank == 0:
                print(' - group distobjects reduced in {:.3f} s'
                      ''.format(stop - start),
                      flush=args.flush)
            start = stop

        if not args.skip_hits:
            fn = '{}/hits.fits'.format(args.outdir)
            if args.zip:
                fn += '.gz'
            hits.write_healpix_fits(fn)
            comm.comm_world.barrier()
            stop = MPI.Wtime()
            if comm.comm_world.rank == 0:
                print(' - Writing hit map to {} took {:.3f} s'
                      ''.format(fn, stop - start),
                      flush=args.flush)
            start = stop
        del hits

        if hits_group is not None:
            if not args.skip_hits:
                fn = '{}/hits_group_{:04}.fits'.format(args.outdir, comm.group)
                if args.zip:
                    fn += '.gz'
                hits_group.write_healpix_fits(fn)
                comm.comm_group.barrier()
                stop = MPI.Wtime()
                if comm.comm_group.rank == 0:
                    print(' - Writing group hit map to {} took {:.3f} s'
                          ''.format(fn, stop - start),
                          flush=args.flush)
                start = stop
            del hits_group

        if not args.skip_hits:
            fn = '{}/invnpp.fits'.format(args.outdir)
            if args.zip:
                fn += '.gz'
            invnpp.write_healpix_fits(fn)
            comm.comm_world.barrier()
            stop = MPI.Wtime()
            if comm.comm_world.rank == 0:
                print(' - Writing N_pp^-1 to {} took {:.3f} s'
                      ''.format(fn, stop - start),
                      flush=args.flush)
            start = stop

        if not args.skip_hits:
            if invnpp_group is not None:
                fn = '{}/invnpp_group_{:04}.fits'.format(
                    args.outdir, comm.group)
                if args.zip:
                    fn += '.gz'
                invnpp_group.write_healpix_fits(fn)
                comm.comm_group.barrier()
                stop = MPI.Wtime()
                if comm.comm_group.rank == 0:
                    print(' - Writing group N_pp^-1 to {} took {:.3f} s'
                          ''.format(fn, stop - start),
                          flush=args.flush)
                start = stop

        # invert it
        tm.covariance_invert(invnpp, 1.0e-3)

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        if comm.comm_world.rank == 0:
            print(' - Inverting N_pp^-1 took {:.3f} s'.format(stop - start),
                  flush=args.flush)
        start = stop

        if not args.skip_hits:
            fn = '{}/npp.fits'.format(args.outdir)
            if args.zip:
                fn += '.gz'
            invnpp.write_healpix_fits(fn)
            comm.comm_world.barrier()
            stop = MPI.Wtime()
            if comm.comm_world.rank == 0:
                print(' - Writing N_pp to {} took {:.3f} s'
                      ''.format(fn, stop - start),
                      flush=args.flush)
            start = stop

        if invnpp_group is not None:
            tm.covariance_invert(invnpp_group, 1.0e-3)

            comm.comm_group.barrier()
            stop = MPI.Wtime()
            if comm.comm_group.rank == 0:
                print(' - Inverting group N_pp^-1 took {:.3f} s'
                      ''.format(stop - start),
                      flush=args.flush)
            start = stop

            if not args.skip_hits:
                fn = '{}/npp_group_{:04}.fits'.format(args.outdir, comm.group)
                if args.zip:
                    fn += '.gz'
                invnpp_group.write_healpix_fits(fn)
                comm.comm_group.barrier()
                stop = MPI.Wtime()
                if comm.comm_group.rank == 0:
                    print(' - Writing group N_pp to {} took {:.3f} s'.format(
                        fn, stop - start),
                          flush=args.flush)
                start = stop

        stop = MPI.Wtime()
        if comm.comm_group.rank == 0:
            print('Building Npp took {:.3f} s'.format(stop - start0),
                  flush=args.flush)

    return invnpp, zmap, invnpp_group, zmap_group, flag_name, common_flag_name
def bin_maps(args, comm, data, rootname, zmap, invnpp, zmap_group,
             invnpp_group, detweights, totalname_freq, flag_name,
             common_flag_name, outpath):
    """ Use TOAST facilities to bin stored signal.

    """
    if not args.skip_bin:
        if comm.comm_world.rank == 0:
            print('Binning unfiltered maps', flush=args.flush)
        start0 = MPI.Wtime()
        start = start0
        autotimer = timing.auto_timer()

        # Bin a map using the toast facilities

        zmap.data.fill(0.0)
        build_zmap = tm.OpAccumDiag(detweights=detweights,
                                    zmap=zmap,
                                    name=totalname_freq,
                                    flag_name=flag_name,
                                    common_flag_name=common_flag_name,
                                    common_flag_mask=args.common_flag_mask)
        build_zmap.exec(data)
        zmap.allreduce()

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        if comm.comm_world.rank == 0:
            print(' - Building noise weighted map took {:.3f} s'
                  ''.format(stop - start),
                  flush=args.flush)
        start = stop

        tm.covariance_apply(invnpp, zmap)

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        if comm.comm_world.rank == 0:
            print(' - Computing {} map took {:.3f} s'
                  ''.format(rootname, stop - start),
                  flush=args.flush)
        start = stop

        fn = os.path.join(outpath, rootname + '.fits')
        if args.zip:
            fn += '.gz'
        zmap.write_healpix_fits(fn)

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        if comm.comm_world.rank == 0:
            print(' - Writing {} map to {} took {:.3f} s'
                  ''.format(rootname, fn, stop - start),
                  flush=args.flush)

        if zmap_group is not None:

            zmap_group.data.fill(0.0)
            build_zmap_group = tm.OpAccumDiag(
                detweights=detweights,
                zmap=zmap_group,
                name=totalname_freq,
                flag_name=flag_name,
                common_flag_name=common_flag_name,
                common_flag_mask=args.common_flag_mask)
            build_zmap_group.exec(data)
            zmap_group.allreduce()

            comm.comm_group.barrier()
            stop = MPI.Wtime()
            if comm.comm_group.rank == 0:
                print(' - Building group noise weighted map took '
                      '{:.3f} s'.format(stop - start),
                      flush=args.flush)
            start = stop

            tm.covariance_apply(invnpp_group, zmap_group)

            comm.comm_group.barrier()
            stop = MPI.Wtime()
            elapsed = stop - start
            if comm.comm_group.rank == 0:
                print(' - Computing {} map took {:.3f} s'
                      ''.format(rootname, stop - start),
                      flush=args.flush)
            start = stop

            fn = os.path.join(
                outpath, '{}_group_{:04}.fits'
                ''.format(rootname, comm.group))
            if args.zip:
                fn += '.gz'
            zmap_group.write_healpix_fits(fn)

            comm.comm_group.barrier()
            stop = MPI.Wtime()
            if comm.comm_group.rank == 0:
                print(' - Writing group {} map to {} took '
                      '{:.3f} s'.format(rootname, fn, stop - start),
                      flush=args.flush)

        stop = MPI.Wtime()
        if comm.comm_world.rank == 0:
            print('Mapmaking took {:.3f} s'
                  ''.format(stop - start0),
                  flush=args.flush)

    return
Exemple #4
0
def main():
    # We are going to group our processes in a single group.  This is fine
    # if we have fewer processes than detectors.  Otherwise we should group
    # them in a reasonable size that is smaller than the number of detectors
    # and which divides evenly into the total number of processes.

    comm = toast.Comm(world=MPI.COMM_WORLD, groupsize=MPI.COMM_WORLD.size)

    # Make a fake focalplane.  Plot it just for fun (don't waste time on this
    # for very large runs though).
    fp = fake_focalplane()
    if comm.comm_world.rank == 0:
        outfile = "custom_example_focalplane.png"
        set_backend()
        tt.plot_focalplane(fp, 6.0, 6.0, outfile)

    # Read in 2 boresight files
    borefiles = [
        "../data/custom_example_boresight_1.txt",
        "../data/custom_example_boresight_2.txt"
    ]

    # Set up the distributed data
    rate = 100.0
    data = create_observations(comm, rate, fp, borefiles)

    # Configure the healpix pixelization we will use for map-making and
    # also the "submap" resolution, which sets granularity of the locally
    # stored pieces of the sky.
    map_nside = 512
    map_npix = 12 * map_nside**2
    sub_nside = 4
    sub_npix = 12 * sub_nside**2

    # Compute a pointing matrix with healpix pixels and weights.
    pointing = tt.OpPointingHpix(nside=map_nside, nest=True, mode="IQU",
        pixels="pixels", weights="weights")
    pointing.exec(data)

    # Compute the locally hit submaps
    local_submaps = pixel_dist(data, sub_npix)

    # Sources of simulated data:  scan from a symmetric beam convolved sky
    # and then add some simulated noise.

    signalmap = tm.DistPixels(comm=comm.comm_world, size=map_npix, nnz=3, 
        dtype=np.float64, submap=sub_npix, local=local_submaps)
    signalmap.read_healpix_fits("../data/custom_example_sky.fits")

    scanmap = tt.OpSimScan(distmap=signalmap, pixels='pixels', 
        weights='weights', out="sim")
    scanmap.exec(data)

    nse = tt.OpSimNoise(out="sim", realization=0)
    nse.exec(data)

    # Accumulate the hits and inverse diagonal pixel covariance, as well as the
    # noise weighted map.  Here we simply use inverse noise weighting.

    detweights = {}
    for d in fp.keys():
        net = fp[d]["NET"]
        detweights[d] = 1.0 / (rate * net * net)
        
    invnpp = tm.DistPixels(comm=comm.comm_world, size=map_npix, nnz=6, 
        dtype=np.float64, submap=sub_npix, local=local_submaps)

    hits = tm.DistPixels(comm=comm.comm_world, size=map_npix, nnz=1, 
        dtype=np.int64, submap=sub_npix, local=local_submaps)
    
    zmap = tm.DistPixels(comm=comm.comm_world, size=map_npix, nnz=3, 
        dtype=np.float64, submap=sub_npix, local=local_submaps)

    invnpp.data.fill(0.0)
    hits.data.fill(0)
    zmap.data.fill(0.0)

    build_invnpp = tm.OpAccumDiag(detweights=detweights, invnpp=invnpp, 
        hits=hits, zmap=zmap, name="sim")
    build_invnpp.exec(data)

    invnpp.allreduce()
    hits.allreduce()
    zmap.allreduce()

    # Write these products out

    hits.write_healpix_fits("custom_example_hits.fits")
    invnpp.write_healpix_fits("custom_example_invnpp.fits")
    zmap.write_healpix_fits("custom_example_zmap.fits")

    # Invert the covariance and write
    
    tm.covariance_invert(invnpp, 1.0e-3)
    invnpp.write_healpix_fits("custom_example_npp.fits")

    # Apply covariance to make the binned map

    tm.covariance_apply(invnpp, zmap)
    zmap.write_healpix_fits("custom_example_binned.fits")

    MPI.Finalize()
    return
Exemple #5
0
def main():

    if MPI.COMM_WORLD.rank == 0:
        print("Running with {} processes".format(MPI.COMM_WORLD.size),
              flush=True)

    global_start = MPI.Wtime()

    parser = argparse.ArgumentParser(
        description="Read existing data and make a simple map.",
        fromfile_prefix_chars="@",
    )

    parser.add_argument(
        "--groupsize",
        required=False,
        type=int,
        default=0,
        help="size of processor groups used to distribute "
        "observations",
    )

    parser.add_argument(
        "--hwprpm",
        required=False,
        type=float,
        default=0.0,
        help="The rate (in RPM) of the HWP rotation",
    )

    parser.add_argument(
        "--samplerate",
        required=False,
        default=100.0,
        type=np.float,
        help="Detector sample rate (Hz)",
    )

    parser.add_argument("--outdir",
                        required=False,
                        default="out",
                        help="Output directory")

    parser.add_argument("--nside",
                        required=False,
                        type=int,
                        default=64,
                        help="Healpix NSIDE")

    parser.add_argument(
        "--subnside",
        required=False,
        type=int,
        default=8,
        help="Distributed pixel sub-map NSIDE",
    )

    parser.add_argument("--coord",
                        required=False,
                        default="E",
                        help="Sky coordinate system [C,E,G]")

    parser.add_argument(
        "--baseline",
        required=False,
        type=float,
        default=60.0,
        help="Destriping baseline length (seconds)",
    )

    parser.add_argument(
        "--noisefilter",
        required=False,
        default=False,
        action="store_true",
        help="Destripe with the noise filter enabled",
    )

    parser.add_argument(
        "--madam",
        required=False,
        default=False,
        action="store_true",
        help="If specified, use libmadam for map-making",
    )

    parser.add_argument("--madampar",
                        required=False,
                        default=None,
                        help="Madam parameter file")

    parser.add_argument(
        "--polyorder",
        required=False,
        type=int,
        help="Polynomial order for the polyfilter",
    )

    parser.add_argument(
        "--wbin_ground",
        required=False,
        type=float,
        help="Ground template bin width [degrees]",
    )

    parser.add_argument(
        "--flush",
        required=False,
        default=False,
        action="store_true",
        help="Flush every print statement.",
    )

    parser.add_argument("--tidas",
                        required=False,
                        default=None,
                        help="Input TIDAS volume")

    parser.add_argument("--tidas_detgroup",
                        required=False,
                        default=None,
                        help="TIDAS detector group")

    parser.add_argument("--spt3g",
                        required=False,
                        default=None,
                        help="Input SPT3G data directory")

    parser.add_argument(
        "--spt3g_prefix",
        required=False,
        default=None,
        help="SPT3G data frame file prefix",
    )

    parser.add_argument(
        "--common_flag_mask",
        required=False,
        default=0,
        type=np.uint8,
        help="Common flag mask",
    )

    parser.add_argument(
        "--debug",
        required=False,
        default=False,
        action="store_true",
        help="Write data distribution info and focalplane plot",
    )

    args = timing.add_arguments_and_parse(parser, timing.FILE(noquotes=True))
    # args = parser.parse_args(sys.argv)

    autotimer = timing.auto_timer("@{}".format(timing.FILE()))

    if (args.tidas is not None) and (args.spt3g is not None):
        raise RuntimeError("Cannot read two datasets!")

    if (args.tidas is None) and (args.spt3g is None):
        raise RuntimeError("No dataset specified!")

    if args.tidas is not None:
        if not tt.tidas_available:
            raise RuntimeError("TIDAS not found- cannot load")

    if args.spt3g is not None:
        if not tt.spt3g_available:
            raise RuntimeError("SPT3G not found- cannot load")

    groupsize = args.groupsize
    if groupsize == 0:
        groupsize = MPI.COMM_WORLD.size

    # Pixelization

    nside = args.nside
    npix = 12 * args.nside * args.nside
    subnside = args.subnside
    if subnside > nside:
        subnside = nside
    subnpix = 12 * subnside * subnside

    # This is the 2-level toast communicator.

    if MPI.COMM_WORLD.size % groupsize != 0:
        if MPI.COMM_WORLD.rank == 0:
            print(
                "WARNING:  process groupsize does not evenly divide into "
                "total number of processes",
                flush=True,
            )
    comm = toast.Comm(world=MPI.COMM_WORLD, groupsize=groupsize)

    # Create output directory

    mtime = MPI.Wtime()

    if comm.comm_world.rank == 0:
        if not os.path.isdir(args.outdir):
            os.makedirs(args.outdir)

    mtime = elapsed(comm.comm_world, mtime, "Creating output directory")

    # The distributed timestream data

    data = None

    if args.tidas is not None:
        if args.tidas_detgroup is None:
            raise RuntimeError("you must specify the detector group")
        data = tds.load_tidas(
            comm,
            comm.group_size,
            args.tidas,
            "r",
            args.tidas_detgroup,
            tds.TODTidas,
            group_dets=args.tidas_detgroup,
            distintervals="chunks",
        )

    if args.spt3g is not None:
        if args.spt3g_prefix is None:
            raise RuntimeError("you must specify the frame file prefix")
        data = s3g.load_spt3g(
            comm,
            comm.group_size,
            args.spt3g,
            args.spt3g_prefix,
            s3g.obsweight_spt3g,
            s3g.TOD3G,
        )

    mtime = elapsed(comm.comm_world, mtime, "Distribute data")

    # In debug mode, print out data distribution information

    if args.debug:
        handle = None
        if comm.comm_world.rank == 0:
            handle = open("{}_distdata.txt".format(args.outdir), "w")
        data.info(handle)
        if comm.comm_world.rank == 0:
            handle.close()
        mtime = elapsed(comm.comm_world, mtime,
                        "Dumping debug data distribution")
        if comm.comm_world.rank == 0:
            outfile = "{}_focalplane.png".format(args.outdir)
            set_backend()
            # Just plot the dets from the first TOD
            temptod = data.obs[0]["tod"]
            # FIXME: change this once we store det info in the metadata.
            dfwhm = {x: 10.0 for x in temptod.detectors}
            tt.plot_focalplane(temptod.detoffset(),
                               10.0,
                               10.0,
                               outfile,
                               fwhm=dfwhm)
        comm.comm_world.barrier()
        mtime = elapsed(comm.comm_world, mtime, "Plotting debug focalplane")

    # Compute pointing matrix

    pointing = tt.OpPointingHpix(nside=args.nside,
                                 nest=True,
                                 mode="IQU",
                                 hwprpm=args.hwprpm)
    pointing.exec(data)

    mtime = elapsed(comm.comm_world, mtime, "Expand pointing")

    # Mapmaking.

    # FIXME:  We potentially have a different noise model for every
    # observation.  We need to have both spt3g and tidas format Noise
    # classes which read the information from disk.  Then the mapmaking
    # operators need to get these noise weights from each observation.
    detweights = {d: 1.0 for d in data.obs[0]["tod"].detectors}

    if not args.madam:
        if comm.comm_world.rank == 0:
            print("Not using Madam, will only make a binned map!", flush=True)

        # Filter data if desired

        if args.polyorder:
            polyfilter = tt.OpPolyFilter(
                order=args.polyorder, common_flag_mask=args.common_flag_mask)
            polyfilter.exec(data)
            mtime = elapsed(comm.comm_world, mtime, "Polynomial filtering")

        if args.wbin_ground:
            groundfilter = tt.OpGroundFilter(
                wbin=args.wbin_ground, common_flag_mask=args.common_flag_mask)
            groundfilter.exec(data)
            mtime = elapsed(comm.comm_world, mtime,
                            "Ground template filtering")

        # Compute pixel space distribution

        lc = tm.OpLocalPixels()
        localpix = lc.exec(data)
        if localpix is None:
            raise RuntimeError(
                "Process {} has no hit pixels. Perhaps there are fewer "
                "detectors than processes in the group?".format(
                    comm.comm_world.rank))
        localsm = np.unique(np.floor_divide(localpix, subnpix))
        mtime = elapsed(comm.comm_world, mtime, "Compute local submaps")

        # construct distributed maps to store the covariance,
        # noise weighted map, and hits

        mtime = MPI.Wtime()
        invnpp = tm.DistPixels(
            comm=comm.comm_world,
            size=npix,
            nnz=6,
            dtype=np.float64,
            submap=subnpix,
            local=localsm,
        )
        hits = tm.DistPixels(
            comm=comm.comm_world,
            size=npix,
            nnz=1,
            dtype=np.int64,
            submap=subnpix,
            local=localsm,
        )
        zmap = tm.DistPixels(
            comm=comm.comm_world,
            size=npix,
            nnz=3,
            dtype=np.float64,
            submap=subnpix,
            local=localsm,
        )

        # compute the hits and covariance.

        invnpp.data.fill(0.0)
        hits.data.fill(0)

        build_invnpp = tm.OpAccumDiag(
            detweights=detweights,
            invnpp=invnpp,
            hits=hits,
            common_flag_mask=args.common_flag_mask,
        )
        build_invnpp.exec(data)

        invnpp.allreduce()
        hits.allreduce()
        mtime = elapsed(comm.comm_world, mtime, "Building hits and N_pp^-1")

        hits.write_healpix_fits("{}_hits.fits".format(args.outdir))
        invnpp.write_healpix_fits("{}_invnpp.fits".format(args.outdir))
        mtime = elapsed(comm.comm_world, mtime, "Writing hits and N_pp^-1")

        # invert it
        tm.covariance_invert(invnpp, 1.0e-3)
        mtime = elapsed(comm.comm_world, mtime, "Inverting N_pp^-1")

        invnpp.write_healpix_fits("{}_npp.fits".format(args.outdir))
        mtime = elapsed(comm.comm_world, mtime, "Writing N_pp")

        zmap.data.fill(0.0)
        build_zmap = tm.OpAccumDiag(zmap=zmap,
                                    detweights=detweights,
                                    common_flag_mask=args.common_flag_mask)
        build_zmap.exec(data)
        zmap.allreduce()
        mtime = elapsed(comm.comm_world, mtime, "Building noise weighted map")

        tm.covariance_apply(invnpp, zmap)
        mtime = elapsed(comm.comm_world, mtime, "Computing binned map")

        zmap.write_healpix_fits(os.path.join(args.outdir, "binned.fits"))
        mtime = elapsed(comm.comm_world, mtime, "Writing binned map")

    else:
        # Set up MADAM map making.

        pars = {}
        pars["temperature_only"] = "F"
        pars["force_pol"] = "T"
        pars["kfirst"] = "T"
        pars["concatenate_messages"] = "T"
        pars["write_map"] = "T"
        pars["write_binmap"] = "T"
        pars["write_matrix"] = "T"
        pars["write_wcov"] = "T"
        pars["write_hits"] = "T"
        pars["nside_cross"] = nside // 2
        pars["nside_submap"] = subnside

        if args.madampar is not None:
            pat = re.compile(r"\s*(\S+)\s*=\s*(\S+(\s+\S+)*)\s*")
            comment = re.compile(r"^#.*")
            with open(args.madampar, "r") as f:
                for line in f:
                    if comment.match(line) is None:
                        result = pat.match(line)
                        if result is not None:
                            key, value = result.group(1), result.group(2)
                            pars[key] = value

        pars["base_first"] = args.baseline
        pars["nside_map"] = nside
        if args.noisefilter:
            pars["kfilter"] = "T"
        else:
            pars["kfilter"] = "F"
        pars["fsample"] = args.samplerate

        madam = tm.OpMadam(params=pars,
                           detweights=detweights,
                           common_flag_mask=args.common_flag_mask)
        madam.exec(data)
        mtime = elapsed(comm.comm_world, mtime, "Madam mapmaking")

    comm.comm_world.barrier()
    stop = MPI.Wtime()
    dur = stop - global_start
    if comm.comm_world.rank == 0:
        print("Total Time:  {:.2f} seconds".format(dur), flush=True)
    return
Exemple #6
0
def main():

    if MPI.COMM_WORLD.rank == 0:
        print("Running with {} processes".format(MPI.COMM_WORLD.size),
            flush=True)

    global_start = MPI.Wtime()

    parser = argparse.ArgumentParser( description="Simulate satellite "
        "boresight pointing and make a noise map.", fromfile_prefix_chars="@" )

    parser.add_argument( "--groupsize", required=False, type=int, default=0,
        help="size of processor groups used to distribute observations" )

    parser.add_argument( "--samplerate", required=False, type=float,
        default=40.0, help="Detector sample rate (Hz)" )

    parser.add_argument( "--starttime", required=False, type=float,
        default=0.0, help="The overall start time of the simulation" )

    parser.add_argument( "--spinperiod", required=False, type=float,
        default=10.0, help="The period (in minutes) of the rotation about the "
        "spin axis" )
    parser.add_argument( "--spinangle", required=False, type=float,
        default=30.0, help="The opening angle (in degrees) of the boresight "
        "from the spin axis" )

    parser.add_argument( "--precperiod", required=False, type=float,
        default=50.0, help="The period (in minutes) of the rotation about the "
        "precession axis" )
    parser.add_argument( "--precangle", required=False, type=float,
        default=65.0, help="The opening angle (in degrees) of the spin axis "
        "from the precession axis" )

    parser.add_argument( "--hwprpm", required=False, type=float,
        default=0.0, help="The rate (in RPM) of the HWP rotation" )
    parser.add_argument( "--hwpstep", required=False, default=None,
        help="For stepped HWP, the angle in degrees of each step" )
    parser.add_argument( "--hwpsteptime", required=False, type=float,
        default=0.0, help="For stepped HWP, the the time in seconds between "
        "steps" )

    parser.add_argument( "--obs", required=False, type=float, default=1.0,
        help="Number of hours in one science observation" )
    parser.add_argument( "--gap", required=False, type=float, default=0.0,
        help="Cooler cycle time in hours between science obs" )
    parser.add_argument( "--numobs", required=False, type=int, default=1,
        help="Number of complete observations" )

    parser.add_argument( "--outdir", required=False, default="out",
        help="Output directory" )
    parser.add_argument( "--debug", required=False, default=False,
        action="store_true", help="Write diagnostics" )

    parser.add_argument( "--nside", required=False, type=int, default=64,
        help="Healpix NSIDE" )
    parser.add_argument( "--subnside", required=False, type=int, default=4,
        help="Distributed pixel sub-map NSIDE" )

    parser.add_argument( "--baseline", required=False, type=float,
        default=60.0, help="Destriping baseline length (seconds)" )
    parser.add_argument( "--noisefilter", required=False, default=False,
        action="store_true", help="Destripe with the noise filter enabled" )

    parser.add_argument( "--madam", required=False, default=False,
        action="store_true", help="If specified, use libmadam for map-making" )
    parser.add_argument( "--madampar", required=False, default=None,
        help="Madam parameter file" )

    parser.add_argument('--flush',
                        required=False, default=False, action='store_true',
                        help='Flush every print statement.')

    parser.add_argument( "--MC_start", required=False, type=int, default=0,
        help="First Monte Carlo noise realization" )
    parser.add_argument( "--MC_count", required=False, type=int, default=1,
        help="Number of Monte Carlo noise realizations" )

    parser.add_argument( "--fp", required=False, default=None,
        help="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." )

    parser.add_argument('--tidas',
                        required=False, default=None,
                        help='Output TIDAS export path')

    parser.add_argument('--input_map', required=False,
                        help='Input map for signal')
    parser.add_argument('--input_pysm_model', required=False,
                        help='Comma separated models for on-the-fly PySM '
                        'simulation, e.g. s3,d6,f1,a2"')
    parser.add_argument('--apply_beam', required=False, action='store_true',
                        help='Apply beam convolution to input map with gaussian '
                        'beam parameters defined in focalplane')

    parser.add_argument('--input_dipole', required=False,
                        help='Simulate dipole, possible values are '
                        'total, orbital, solar')
    parser.add_argument('--input_dipole_solar_speed_kms', required=False,
                        help='Solar system speed [km/s]', type=float,
                        default=369.0)
    parser.add_argument('--input_dipole_solar_gal_lat_deg', required=False,
                        help='Solar system speed galactic latitude [degrees]',
                        type=float, default=48.26)
    parser.add_argument('--input_dipole_solar_gal_lon_deg', required=False,
                        help='Solar system speed galactic longitude[degrees]',
                        type=float, default=263.99)

    args = timing.add_arguments_and_parse(parser, timing.FILE(noquotes=True))

    autotimer = timing.auto_timer("@{}".format(timing.FILE()))

    if args.tidas is not None:
        if not tt.tidas_available:
            raise RuntimeError("TIDAS not found- cannot export")

    groupsize = args.groupsize
    if groupsize == 0:
        groupsize = MPI.COMM_WORLD.size

    # This is the 2-level toast communicator.

    if MPI.COMM_WORLD.size % groupsize != 0:
        if MPI.COMM_WORLD.rank == 0:
            print("WARNING:  process groupsize does not evenly divide into "
                "total number of processes", flush=True)
    comm = toast.Comm(world=MPI.COMM_WORLD, groupsize=groupsize)

    # get options

    hwpstep = None
    if args.hwpstep is not None:
        hwpstep = float(args.hwpstep)

    npix = 12 * args.nside * args.nside

    subnside = args.subnside
    if subnside > args.nside:
        subnside = args.nside
    subnpix = 12 * subnside * subnside

    start = MPI.Wtime()

    fp = None

    # Load focalplane information

    if comm.comm_world.rank == 0:
        if args.fp is None:
            # in this case, create a fake detector at the boresight
            # with a pure white noise spectrum.
            fake = {}
            fake["quat"] = np.array([0.0, 0.0, 1.0, 0.0])
            fake["fwhm"] = 30.0
            fake["fknee"] = 0.0
            fake["alpha"] = 1.0
            fake["NET"] = 1.0
            fake["color"] = "r"
            fp = {}
            fp["bore"] = fake
        else:
            with open(args.fp, "rb") as p:
                fp = pickle.load(p)
    fp = comm.comm_world.bcast(fp, root=0)

    stop = MPI.Wtime()
    elapsed = stop - start
    if comm.comm_world.rank == 0:
        print("Create focalplane:  {:.2f} seconds".format(stop-start),
            flush=True)
    start = stop

    if args.debug:
        if comm.comm_world.rank == 0:
            outfile = "{}_focalplane.png".format(args.outdir)
            set_backend()
            tt.plot_focalplane(fp, 10.0, 10.0, outfile)

    # Since we are simulating noise timestreams, we want
    # them to be contiguous and reproducible over the whole
    # observation.  We distribute data by detector within an
    # observation, so ensure that our group size is not larger
    # than the number of detectors we have.

    if groupsize > len(fp.keys()):
        if comm.comm_world.rank == 0:
            print("process group is too large for the number of detectors",
                flush=True)
            comm.comm_world.Abort()

    # Detector information from the focalplane

    detectors = sorted(fp.keys())
    detquats = {}
    detindx = None
    if "index" in fp[detectors[0]]:
        detindx = {}

    for d in detectors:
        detquats[d] = fp[d]["quat"]
        if detindx is not None:
            detindx[d] = fp[d]["index"]

    # Distribute the observations uniformly

    groupdist = toast.distribute_uniform(args.numobs, comm.ngroups)

    # Compute global time and sample ranges of all observations

    obsrange = tt.regular_intervals(args.numobs, args.starttime, 0,
        args.samplerate, 3600*args.obs, 3600*args.gap)

    # Create the noise model used for all observations

    fmin = {}
    fknee = {}
    alpha = {}
    NET = {}
    rates = {}
    for d in detectors:
        rates[d] = args.samplerate
        fmin[d] = fp[d]["fmin"]
        fknee[d] = fp[d]["fknee"]
        alpha[d] = fp[d]["alpha"]
        NET[d] = fp[d]["NET"]

    noise = tt.AnalyticNoise(rate=rates, fmin=fmin, detectors=detectors,
        fknee=fknee, alpha=alpha, NET=NET)

    mem_counter = tt.OpMemoryCounter()

    # The distributed timestream data

    data = toast.Data(comm)

    # Every process group creates its observations

    group_firstobs = groupdist[comm.group][0]
    group_numobs = groupdist[comm.group][1]

    for ob in range(group_firstobs, group_firstobs + group_numobs):
        tod = tt.TODSatellite(
            comm.comm_group,
            detquats,
            obsrange[ob].samples,
            firsttime=obsrange[ob].start,
            rate=args.samplerate,
            spinperiod=args.spinperiod,
            spinangle=args.spinangle,
            precperiod=args.precperiod,
            precangle=args.precangle,
            detindx=detindx,
            detranks=comm.group_size
        )

        obs = {}
        obs["name"] = "science_{:05d}".format(ob)
        obs["tod"] = tod
        obs["intervals"] = None
        obs["baselines"] = None
        obs["noise"] = noise
        obs["id"] = ob

        data.obs.append(obs)

    stop = MPI.Wtime()
    elapsed = stop - start
    if comm.comm_world.rank == 0:
        print("Read parameters, compute data distribution:  "
            "{:.2f} seconds".format(stop-start), flush=True)
    start = stop

    # we set the precession axis now, which will trigger calculation
    # of the boresight pointing.

    for ob in range(group_numobs):
        curobs = data.obs[ob]
        tod = curobs["tod"]

        # Get the global sample offset from the original distribution of
        # intervals
        obsoffset = obsrange[group_firstobs + ob].first

        # Constantly slewing precession axis
        degday = 360.0 / 365.25
        precquat = tt.slew_precession_axis(nsim=tod.local_samples[1],
            firstsamp=obsoffset, samplerate=args.samplerate,
            degday=degday)

        tod.set_prec_axis(qprec=precquat)

    stop = MPI.Wtime()
    elapsed = stop - start
    if comm.comm_world.rank == 0:
        print("Construct boresight pointing:  "
            "{:.2f} seconds".format(stop-start), flush=True)
    start = stop

    # make a Healpix pointing matrix.

    pointing = tt.OpPointingHpix(nside=args.nside, nest=True, mode="IQU",
        hwprpm=args.hwprpm, hwpstep=hwpstep, hwpsteptime=args.hwpsteptime)
    pointing.exec(data)

    comm.comm_world.barrier()
    stop = MPI.Wtime()
    elapsed = stop - start
    if comm.comm_world.rank == 0:
        print("Pointing generation took {:.3f} s".format(elapsed), flush=True)
    start = stop

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

    signalname = "signal"
    if args.input_pysm_model:
        simulate_sky_signal(args, comm, data, mem_counter,
                                         [fp], subnpix, localsm, signalname=signalname)

    if args.input_dipole:
        print("Simulating dipole")
        op_sim_dipole = tt.OpSimDipole(mode=args.input_dipole,
                solar_speed=args.input_dipole_solar_speed_kms,
                solar_gal_lat=args.input_dipole_solar_gal_lat_deg,
                solar_gal_lon=args.input_dipole_solar_gal_lon_deg,
                out=signalname,
                keep_quats=True,
                keep_vel=False,
                subtract=False,
                coord="G",
                freq=0,  # we could use frequency for quadrupole correction
                flag_mask=255, common_flag_mask=255)
        op_sim_dipole.exec(data)

    # Mapmaking.  For purposes of this simulation, we use detector noise
    # weights based on the NET (white noise level).  If the destriping
    # baseline is too long, this will not be the best choice.

    detweights = {}
    for d in detectors:
        net = fp[d]["NET"]
        detweights[d] = 1.0 / (args.samplerate * net * net)

    if not args.madam:
        if comm.comm_world.rank == 0:
            print("Not using Madam, will only make a binned map!", flush=True)

        # get locally hit pixels
        lc = tm.OpLocalPixels()
        localpix = lc.exec(data)

        # find the locally hit submaps.
        localsm = np.unique(np.floor_divide(localpix, subnpix))

        # construct distributed maps to store the covariance,
        # noise weighted map, and hits

        invnpp = tm.DistPixels(comm=comm.comm_world, size=npix, nnz=6,
            dtype=np.float64, submap=subnpix, local=localsm)
        hits = tm.DistPixels(comm=comm.comm_world, size=npix, nnz=1,
            dtype=np.int64, submap=subnpix, local=localsm)
        zmap = tm.DistPixels(comm=comm.comm_world, size=npix, nnz=3,
            dtype=np.float64, submap=subnpix, local=localsm)

        # compute the hits and covariance once, since the pointing and noise
        # weights are fixed.

        invnpp.data.fill(0.0)
        hits.data.fill(0)

        build_invnpp = tm.OpAccumDiag(detweights=detweights, invnpp=invnpp,
            hits=hits)
        build_invnpp.exec(data)

        invnpp.allreduce()
        hits.allreduce()

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        elapsed = stop - start
        if comm.comm_world.rank == 0:
            print("Building hits and N_pp^-1 took {:.3f} s".format(elapsed),
                flush=True)
        start = stop

        hits.write_healpix_fits("{}_hits.fits".format(args.outdir))
        invnpp.write_healpix_fits("{}_invnpp.fits".format(args.outdir))

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        elapsed = stop - start
        if comm.comm_world.rank == 0:
            print("Writing hits and N_pp^-1 took {:.3f} s".format(elapsed),
                flush=True)
        start = stop

        # invert it
        tm.covariance_invert(invnpp, 1.0e-3)

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        elapsed = stop - start
        if comm.comm_world.rank == 0:
            print("Inverting N_pp^-1 took {:.3f} s".format(elapsed),
                flush=True)
        start = stop

        invnpp.write_healpix_fits("{}_npp.fits".format(args.outdir))

        comm.comm_world.barrier()
        stop = MPI.Wtime()
        elapsed = stop - start
        if comm.comm_world.rank == 0:
            print("Writing N_pp took {:.3f} s".format(elapsed),
                flush=True)
        start = stop

        # in debug mode, print out data distribution information
        if args.debug:
            handle = None
            if comm.comm_world.rank == 0:
                handle = open("{}_distdata.txt".format(args.outdir), "w")
            data.info(handle)
            if comm.comm_world.rank == 0:
                handle.close()

            comm.comm_world.barrier()
            stop = MPI.Wtime()
            elapsed = stop - start
            if comm.comm_world.rank == 0:
                print("Dumping debug data distribution took "
                    "{:.3f} s".format(elapsed), flush=True)
            start = stop

        mcstart = start

        # Loop over Monte Carlos

        firstmc = int(args.MC_start)
        nmc = int(args.MC_count)

        for mc in range(firstmc, firstmc+nmc):
            # create output directory for this realization
            outpath = "{}_{:03d}".format(args.outdir, mc)
            if comm.comm_world.rank == 0:
                if not os.path.isdir(outpath):
                    os.makedirs(outpath)

            comm.comm_world.barrier()
            stop = MPI.Wtime()
            elapsed = stop - start
            if comm.comm_world.rank == 0:
                print("Creating output dir {:04d} took {:.3f} s".format(mc,
                    elapsed), flush=True)
            start = stop

            # clear all signal data from the cache, so that we can generate
            # new noise timestreams.
            tod.cache.clear("tot_signal_.*")

            # simulate noise

            nse = tt.OpSimNoise(out="tot_signal", realization=mc)
            nse.exec(data)

            # add sky signal
            add_sky_signal(args, comm, data, totalname="tot_signal", signalname=signalname)

            if mc == firstmc:
                # For the first realization, optionally export the
                # timestream data to a TIDAS volume.
                if args.tidas is not None:
                    from toast.tod.tidas import OpTidasExport
                    tidas_path = os.path.abspath(args.tidas)
                    export = OpTidasExport(tidas_path, name="tot_signal")
                    export.exec(data)

            comm.comm_world.barrier()
            stop = MPI.Wtime()
            elapsed = stop - start
            if comm.comm_world.rank == 0:
                print("  Noise simulation {:04d} took {:.3f} s".format(mc,
                    elapsed), flush=True)
            start = stop

            zmap.data.fill(0.0)
            build_zmap = tm.OpAccumDiag(zmap=zmap, name="tot_signal",
                                        detweights=detweights)
            build_zmap.exec(data)
            zmap.allreduce()

            comm.comm_world.barrier()
            stop = MPI.Wtime()
            elapsed = stop - start
            if comm.comm_world.rank == 0:
                print("  Building noise weighted map {:04d} took {:.3f} s".format(
                    mc, elapsed), flush=True)
            start = stop

            tm.covariance_apply(invnpp, zmap)

            comm.comm_world.barrier()
            stop = MPI.Wtime()
            elapsed = stop - start
            if comm.comm_world.rank == 0:
                print("  Computing binned map {:04d} took {:.3f} s".format(mc,
                    elapsed), flush=True)
            start = stop

            zmap.write_healpix_fits(os.path.join(outpath, "binned.fits"))

            comm.comm_world.barrier()
            stop = MPI.Wtime()
            elapsed = stop - start
            if comm.comm_world.rank == 0:
                print("  Writing binned map {:04d} took {:.3f} s".format(mc,
                    elapsed), flush=True)
            elapsed = stop - mcstart
            if comm.comm_world.rank == 0:
                print("  Mapmaking {:04d} took {:.3f} s".format(mc, elapsed),
                    flush=True)
            start = stop

    else:

        # Set up MADAM map making.

        pars = {}

        cross = args.nside // 2

        pars[ "temperature_only" ] = "F"
        pars[ "force_pol" ] = "T"
        pars[ "kfirst" ] = "T"
        pars[ "concatenate_messages" ] = "T"
        pars[ "write_map" ] = "T"
        pars[ "write_binmap" ] = "T"
        pars[ "write_matrix" ] = "T"
        pars[ "write_wcov" ] = "T"
        pars[ "write_hits" ] = "T"
        pars[ "nside_cross" ] = cross
        pars[ "nside_submap" ] = subnside

        if args.madampar is not None:
            pat = re.compile(r"\s*(\S+)\s*=\s*(\S+(\s+\S+)*)\s*")
            comment = re.compile(r"^#.*")
            with open(args.madampar, "r") as f:
                for line in f:
                    if comment.match(line) is None:
                        result = pat.match(line)
                        if result is not None:
                            key, value = result.group(1), result.group(2)
                            pars[key] = value

        pars[ "base_first" ] = args.baseline
        pars[ "nside_map" ] = args.nside
        if args.noisefilter:
            pars[ "kfilter" ] = "T"
        else:
            pars[ "kfilter" ] = "F"
        pars[ "fsample" ] = args.samplerate

        # Loop over Monte Carlos

        firstmc = int(args.MC_start)
        nmc = int(args.MC_count)

        for mc in range(firstmc, firstmc+nmc):
            # clear all total signal data from the cache, so that we can generate
            # new noise timestreams.
            tod.cache.clear("tot_signal_.*")

            # simulate noise

            nse = tt.OpSimNoise(out="tot_signal", realization=mc)
            nse.exec(data)

            # add sky signal
            add_sky_signal(args, comm, data, totalname="tot_signal", signalname=signalname)

            comm.comm_world.barrier()
            stop = MPI.Wtime()
            elapsed = stop - start
            if comm.comm_world.rank == 0:
                print("Noise simulation took {:.3f} s".format(elapsed),
                    flush=True)
            start = stop

            # create output directory for this realization
            pars[ "path_output" ] = "{}_{:03d}".format(args.outdir, mc)
            if comm.comm_world.rank == 0:
                if not os.path.isdir(pars["path_output"]):
                    os.makedirs(pars["path_output"])

            # in debug mode, print out data distribution information
            if args.debug:
                handle = None
                if comm.comm_world.rank == 0:
                    handle = open(os.path.join(pars["path_output"],
                        "distdata.txt"), "w")
                data.info(handle)
                if comm.comm_world.rank == 0:
                    handle.close()

            madam = tm.OpMadam(params=pars, detweights=detweights,
                name="tot_signal")
            madam.exec(data)

            comm.comm_world.barrier()
            stop = MPI.Wtime()
            elapsed = stop - start
            if comm.comm_world.rank == 0:
                print("Mapmaking took {:.3f} s".format(elapsed), flush=True)

    comm.comm_world.barrier()
    stop = MPI.Wtime()
    elapsed = stop - global_start
    if comm.comm_world.rank == 0:
        print("Total Time:  {:.2f} seconds".format(elapsed), flush=True)