Пример #1
0
def main():
    env = Environment.get()
    log = Logger.get()
    gt = GlobalTimers.get()
    gt.start("toast_satellite_sim (total)")
    timer0 = Timer()
    timer0.start()

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

    # Parse options

    tmr = Timer()
    tmr.start()

    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:
        if comm.world_rank == 0:
            log.info("Not using Madam, will only make a binned map")

        npp, zmap = init_binner(args,
                                comm,
                                data,
                                detweights,
                                subnpix=subnpix,
                                localsm=localsm)

        # Loop over Monte Carlos

        firstmc = args.MC_start
        nmc = args.MC_count

        for mc in range(firstmc, firstmc + nmc):
            mctmr = Timer()
            mctmr.start()

            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:
                timer = Timer()
                timer.start()
                op_apply_gain = OpApplyGain(gain, name="tot_signal")
                op_apply_gain.exec(data)
                if comm.world_rank == 0:
                    timer.report_clear("  Apply gains {:04d}".format(mc))

            if mc == firstmc:
                # For the first realization, optionally export the
                # timestream data.  If we had observation intervals defined,
                # we could pass "use_interval=True" to the export operators,
                # which would ensure breaks in the exported data at
                # acceptable places.
                output_tidas(args, comm, data, "tot_signal")
                output_spt3g(args, comm, data, "tot_signal")

            apply_binner(args, comm, data, npp, zmap, detweights, outpath,
                         "tot_signal")

            if comm.world_rank == 0:
                mctmr.report_clear("  Map-making {:04d}".format(mc))
    else:

        # Initialize madam parameters

        madampars = setup_madam(args)

        # in debug mode, print out data distribution information
        if args.debug:
            handle = None
            if comm.world_rank == 0:
                handle = open(os.path.join(args.outdir, "distdata.txt"), "w")
            data.info(handle)
            if comm.world_rank == 0:
                handle.close()
            if comm.comm_world is not None:
                comm.comm_world.barrier()
            if comm.world_rank == 0:
                tmr.report_clear("Dumping data distribution")

        # Loop over Monte Carlos

        firstmc = args.MC_start
        nmc = args.MC_count

        for mc in range(firstmc, firstmc + nmc):
            mctmr = Timer()
            mctmr.start()

            # 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)

            if comm.comm_world is not None:
                comm.comm_world.barrier()
            if comm.world_rank == 0:
                tmr.report_clear("  Apply gains {:04d}".format(mc))

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

            if comm.comm_world is not None:
                comm.comm_world.barrier()
            if comm.world_rank == 0:
                mctmr.report_clear("  Map-making {:04d}".format(mc))

    gt.stop_all()
    if comm.comm_world is not None:
        comm.comm_world.barrier()
    tmr.stop()
    tmr.clear()
    tmr.start()
    alltimers = gather_timers(comm=comm.comm_world)
    if comm.world_rank == 0:
        out = os.path.join(args.outdir, "timing")
        dump_timing(alltimers, out)
        tmr.stop()
        tmr.report("Gather and dump timing info")
        timer0.report_clear("toast_satellite_sim.py")
    return
Пример #2
0
    def load_frames(self):
        log = Logger.get()
        rank = 0
        if self.mpicomm is not None:
            rank = self.mpicomm.rank

        # Timestamps
        self.cache.create(self.TIMESTAMP_NAME, np.float64,
                          (self.local_samples[1], ))

        # Boresight pointing
        self.cache.create("boresight_radec", np.float64,
                          (self.local_samples[1], 4))
        self.cache.create("boresight_azel", np.float64,
                          (self.local_samples[1], 4))
        self.cache.create(self.HWP_ANGLE_NAME, np.float64,
                          (self.local_samples[1], ))

        # Common flags
        self.cache.create(self.COMMON_FLAG_NAME, np.uint8,
                          (self.local_samples[1], ))

        # Telescope position and velocity
        self.cache.create(self.POSITION_NAME, np.float64,
                          (self.local_samples[1], 3))
        self.cache.create(self.VELOCITY_NAME, np.float64,
                          (self.local_samples[1], 3))

        # Detector data and flags
        for det in self.local_dets:
            name = "{}_{}".format(self.SIGNAL_NAME, det)
            self.cache.create(name, np.float64, (self.local_samples[1], ))
            name = "{}_{}".format(self.FLAG_NAME, det)
            self.cache.create(name, np.uint8, (self.local_samples[1], ))

        timer = Timer()
        for ffile in self._file_names:
            fnf = self._file_nframes[ffile]
            frame_offsets = self._frame_sample_offs[ffile]
            frame_sizes = self._frame_sizes[ffile]
            if rank == 0:
                log.debug("Loading {} frames from {}".format(fnf, ffile))
            # Loop over all frames- only the root process will actually
            # read data from disk.
            if rank == 0:
                gfile = core3g.G3File(ffile)
            else:
                gfile = [None] * fnf

            timer.clear()
            timer.start()
            for fdata, frame_offset, frame_size in zip(gfile, frame_offsets,
                                                       frame_sizes):
                is_scan = True
                if rank == 0:
                    if fdata.type != core3g.G3FrameType.Scan:
                        is_scan = False
                if self.mpicomm is not None:
                    is_scan = self.mpicomm.bcast(is_scan, root=0)
                if not is_scan:
                    continue

                frame_to_tod(
                    self,
                    frame_offset,
                    frame_size,
                    frame_data=fdata,
                    all_flavors=self._all_flavors,
                )
                if self.mpicomm is not None:
                    self.mpicomm.barrier()
            timer.stop()
            if rank == 0:
                log.debug("Translated frames in {}s".format(timer.seconds()))
            del gfile
        return
Пример #3
0
def main():
    env = Environment.get()
    log = Logger.get()
    gt = GlobalTimers.get()
    gt.start("toast_satellite_sim (total)")
    timer0 = Timer()
    timer0.start()

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

    # Parse options

    tmr = Timer()
    tmr.start()

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

    focalplane, gain, detweights = load_focalplane(args, comm)
    if comm.world_rank == 0:
        tmr.report_clear("Load focalplane")

    data = create_observations(args, comm, focalplane, groupsize)
    if comm.world_rank == 0:
        tmr.report_clear("Create observations")

    pipeline_tools.expand_pointing(args, comm, data)
    if comm.world_rank == 0:
        tmr.report_clear("Expand pointing")

    signalname = None
    if args.pysm_model:
        skyname = pipeline_tools.simulate_sky_signal(args, comm, data,
                                                     [focalplane], "signal")
    else:
        skyname = pipeline_tools.scan_sky_signal(args, comm, data, "signal")
    if skyname is not None:
        signalname = skyname
    if comm.world_rank == 0:
        tmr.report_clear("Simulate sky signal")

    # NOTE: Conviqt could use different input file names for different
    # Monte Carlo indices, but the operator would need to be invoked within
    # the Monte Carlo loop.
    skyname = pipeline_tools.apply_conviqt(
        args,
        comm,
        data,
        "signal",
        mc=args.MC_start,
    )
    if skyname is not None:
        signalname = skyname
    if comm.world_rank == 0:
        tmr.report_clear("Apply beam convolution")

    diponame = pipeline_tools.simulate_dipole(args, comm, data, "signal")
    if diponame is not None:
        signalname = diponame
    if comm.world_rank == 0:
        tmr.report_clear("Simulate dipole")

    # in debug mode, print out data distribution information
    if args.debug:
        handle = None
        if comm.world_rank == 0:
            handle = open(os.path.join(args.outdir, "distdata.txt"), "w")
        data.info(handle)
        if comm.world_rank == 0:
            handle.close()
        if comm.comm_world is not None:
            comm.comm_world.barrier()
        if comm.world_rank == 0:
            tmr.report_clear("Dumping data distribution")

    # in debug mode, print out data distribution information
    if args.debug:
        handle = None
        if comm.world_rank == 0:
            handle = open(os.path.join(args.outdir, "distdata.txt"), "w")
        data.info(handle)
        if comm.world_rank == 0:
            handle.close()
        if comm.comm_world is not None:
            comm.comm_world.barrier()
        if comm.world_rank == 0:
            tmr.report_clear("Dumping data distribution")

    # Mapmaking.

    if args.use_madam:
        # Initialize madam parameters
        madampars = pipeline_tools.setup_madam(args)
        if comm.comm_world is not None:
            comm.comm_world.barrier()
        if comm.world_rank == 0:
            tmr.report_clear("Initialize madam map-making")

    # Loop over Monte Carlos

    firstmc = args.MC_start
    nmc = args.MC_count

    for mc in range(firstmc, firstmc + nmc):
        mctmr = Timer()
        mctmr.start()

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

        pipeline_tools.simulate_noise(args,
                                      comm,
                                      data,
                                      mc,
                                      "tot_signal",
                                      overwrite=True)
        if comm.comm_world is not None:
            comm.comm_world.barrier()
        if comm.world_rank == 0:
            tmr.report_clear("    Simulate noise {:04d}".format(mc))

        # add sky signal
        pipeline_tools.add_signal(args, comm, data, "tot_signal", signalname)
        if comm.comm_world is not None:
            comm.comm_world.barrier()
        if comm.world_rank == 0:
            tmr.report_clear("    Add sky signal {:04d}".format(mc))

        if gain is not None:
            op_apply_gain = OpApplyGain(gain, name="tot_signal")
            op_apply_gain.exec(data)
            if comm.comm_world is not None:
                comm.comm_world.barrier()
            if comm.world_rank == 0:
                tmr.report_clear("    Apply gains {:04d}".format(mc))

        if mc == firstmc:
            # For the first realization, optionally export the
            # timestream data.  If we had observation intervals defined,
            # we could pass "use_interval=True" to the export operators,
            # which would ensure breaks in the exported data at
            # acceptable places.
            pipeline_tools.output_tidas(args, comm, data, "tot_signal")
            pipeline_tools.output_spt3g(args, comm, data, "tot_signal")
            if comm.comm_world is not None:
                comm.comm_world.barrier()
            if comm.world_rank == 0:
                tmr.report_clear("    Write TOD snapshot {:04d}".format(mc))

        if args.use_madam:
            pipeline_tools.apply_madam(args, comm, data, madampars, outpath,
                                       detweights, "tot_signal")
        else:
            pipeline_tools.apply_mapmaker(args, comm, data, outpath,
                                          "tot_signal")

        if comm.comm_world is not None:
            comm.comm_world.barrier()
        if comm.world_rank == 0:
            tmr.report_clear("  Map-making {:04d}".format(mc))

        if comm.comm_world is not None:
            comm.comm_world.barrier()
        if comm.world_rank == 0:
            mctmr.report_clear("  Monte Carlo loop {:04d}".format(mc))

    gt.stop_all()
    if comm.comm_world is not None:
        comm.comm_world.barrier()
    tmr.stop()
    tmr.clear()
    tmr.start()
    alltimers = gather_timers(comm=comm.comm_world)
    if comm.world_rank == 0:
        out = os.path.join(args.outdir, "timing")
        dump_timing(alltimers, out)
        tmr.stop()
        tmr.report("Gather and dump timing info")
        timer0.report_clear("toast_satellite_sim.py")
    return
Пример #4
0
def main():
    log = Logger.get()
    gt = GlobalTimers.get()
    gt.start("toast_planck_reduce (total)")

    mpiworld, procs, rank, comm = get_comm()

    # This is the 2-level toast communicator.  By default,
    # there is just one group which spans MPI_COMM_WORLD.
    comm = toast.Comm()

    if comm.comm_world.rank == 0:
        print('Running with {} processes at {}'
              ''.format(procs, str(datetime.datetime.now())))

    parser = argparse.ArgumentParser(description='Planck Ringset making',
                                     fromfile_prefix_chars='@')
    parser.add_argument('--rimo', required=True, help='RIMO file')
    parser.add_argument('--freq', required=True, type=np.int, help='Frequency')
    parser.add_argument('--dets',
                        required=False,
                        default=None,
                        help='Detector list (comma separated)')
    parser.add_argument('--nosingle',
                        dest='nosingle',
                        required=False,
                        default=False,
                        action='store_true',
                        help='Do not compute single detector PSDs')
    parser.add_argument('--effdir',
                        required=True,
                        help='Input Exchange Format File directory')
    parser.add_argument('--effdir_pntg',
                        required=False,
                        help='Input Exchange Format File directory '
                        'for pointing')
    parser.add_argument('--obtmask',
                        required=False,
                        default=1,
                        type=np.int,
                        help='OBT flag mask')
    parser.add_argument('--flagmask',
                        required=False,
                        default=1,
                        type=np.int,
                        help='Quality flag mask')
    parser.add_argument('--skymask', required=False, help='Pixel mask file')
    parser.add_argument('--skymap', required=False, help='Sky estimate file')
    parser.add_argument('--skypol',
                        dest='skypol',
                        required=False,
                        default=False,
                        action='store_true',
                        help='Sky estimate is polarized')
    parser.add_argument('--no_spin_harmonics',
                        dest='no_spin_harmonics',
                        required=False,
                        default=False,
                        action='store_true',
                        help='Do not include PSD bins with spin harmonics')
    parser.add_argument('--calibrate',
                        required=False,
                        help='Path to calibration file to calibrate with.')
    parser.add_argument('--calibrate_signal_estimate',
                        dest='calibrate_signal_estimate',
                        required=False,
                        default=False,
                        action='store_true',
                        help='Calibrate '
                        'the signal estimate using linear regression.')
    parser.add_argument('--ringdb', required=True, help='Ring DB file')
    parser.add_argument('--odfirst',
                        required=False,
                        default=None,
                        type=np.int,
                        help='First OD to use')
    parser.add_argument('--odlast',
                        required=False,
                        default=None,
                        type=np.int,
                        help='Last OD to use')
    parser.add_argument('--ringfirst',
                        required=False,
                        default=None,
                        type=np.int,
                        help='First ring to use')
    parser.add_argument('--ringlast',
                        required=False,
                        default=None,
                        type=np.int,
                        help='Last ring to use')
    parser.add_argument('--obtfirst',
                        required=False,
                        default=None,
                        type=np.float,
                        help='First OBT to use')
    parser.add_argument('--obtlast',
                        required=False,
                        default=None,
                        type=np.float,
                        help='Last OBT to use')
    parser.add_argument('--out',
                        required=False,
                        default='.',
                        help='Output directory')
    parser.add_argument('--nbin_psd',
                        required=False,
                        default=1000,
                        type=np.int,
                        help='Number of logarithmically '
                        'distributed spectral bins to write.')
    parser.add_argument('--lagmax',
                        required=False,
                        default=100000,
                        type=np.int,
                        help='Maximum lag to evaluate for the '
                        'autocovariance function [samples].')
    parser.add_argument('--stationary_period',
                        required=False,
                        default=86400.,
                        type=np.float,
                        help='Length of a stationary interval [seconds].')
    # Dipole parameters
    dipogroup = parser.add_mutually_exclusive_group()
    dipogroup.add_argument('--dipole',
                           dest='dipole',
                           required=False,
                           default=False,
                           action='store_true',
                           help='Simulate dipole')
    dipogroup.add_argument('--solsys_dipole',
                           dest='solsys_dipole',
                           required=False,
                           default=False,
                           action='store_true',
                           help='Simulate solar system dipole')
    dipogroup.add_argument('--orbital_dipole',
                           dest='orbital_dipole',
                           required=False,
                           default=False,
                           action='store_true',
                           help='Simulate orbital dipole')
    # Extra filter
    parser.add_argument('--filterfile',
                        required=False,
                        help='Extra filter file.')

    try:
        args = parser.parse_args()
    except SystemExit:
        sys.exit(0)

    if comm.comm_world.rank == 0:
        print('All parameters:')
        print(args, flush=True)

    timer = Timer()
    timer.start()

    odrange = None
    if args.odfirst is not None and args.odlast is not None:
        odrange = (args.odfirst, args.odlast)

    ringrange = None
    if args.ringfirst is not None and args.ringlast is not None:
        ringrange = (args.ringfirst, args.ringlast)

    obtrange = None
    if args.obtfirst is not None and args.obtlast is not None:
        obtrange = (args.obtfirst, args.obtlast)

    detectors = None
    if args.dets is not None:
        detectors = re.split(',', args.dets)

    if args.nosingle and len(detectors) != 2:
        raise RuntimeError('You cannot skip the single detectors PSDs '
                           'without multiple detectors.')

    # This is the distributed data, consisting of one or
    # more observations, each distributed over a communicator.
    data = toast.Data(comm)

    # Make output directory

    if not os.path.isdir(args.out) and comm.comm_world.rank == 0:
        os.mkdir(args.out)

    # create the TOD for this observation

    tod = tp.Exchange(
        comm=comm.comm_group,
        detectors=detectors,
        ringdb=args.ringdb,
        effdir_in=args.effdir,
        effdir_pntg=args.effdir_pntg,
        obt_range=obtrange,
        ring_range=ringrange,
        od_range=odrange,
        freq=args.freq,
        RIMO=args.rimo,
        obtmask=args.obtmask,
        flagmask=args.flagmask,
        do_eff_cache=False,
    )

    rimo = tod.rimo

    ob = {}
    ob['name'] = 'mission'
    ob['id'] = 0
    ob['tod'] = tod
    ob['intervals'] = tod.valid_intervals
    ob['baselines'] = None
    ob['noise'] = tod.noise

    data.obs.append(ob)

    comm.comm_world.barrier()
    if comm.comm_world.rank == 0:
        timer.report_clear("Metadata queries")

    # Read the signal

    tod_name = 'signal'
    flags_name = 'flags'

    reader = tp.OpInputPlanck(signal_name=tod_name, flags_name=flags_name)
    if comm.comm_world.rank == 0:
        print('Reading input signal from {}'.format(args.effdir), flush=True)
    reader.exec(data)
    comm.comm_world.barrier()
    if comm.comm_world.rank == 0:
        timer.report_clear("Reading")

    if args.calibrate is not None:
        fn = args.calibrate
        if comm.comm_world.rank == 0:
            print('Calibrating with {}'.format(fn), flush=True)
        calibrator = tp.OpCalibPlanck(signal_in=tod_name,
                                      signal_out=tod_name,
                                      file_gain=fn)
        calibrator.exec(data)
        comm.comm_world.barrier()
        if comm.comm_world.rank == 0:
            timer.report_clear("Calibrate")

    # Optionally subtract the dipole

    do_dipole = (args.dipole or args.solsys_dipole or args.orbital_dipole)

    if do_dipole:
        if args.dipole:
            dipomode = 'total'
        elif args.solsys_dipole:
            dipomode = 'solsys'
        else:
            dipomode = 'orbital'

        dipo = tp.OpDipolePlanck(args.freq,
                                 mode=dipomode,
                                 output='dipole',
                                 keep_quats=True)
        dipo.exec(data)

        comm.comm_world.barrier()
        if comm.comm_world.rank == 0:
            timer.report_clear("Dipole")

        subtractor = tp.OpCacheMath(in1=tod_name,
                                    in2='dipole',
                                    subtract=True,
                                    out=tod_name)
        if comm.comm_world.rank == 0:
            print('Subtracting dipole', flush=True)
        subtractor.exec(data)

        comm.comm_world.barrier()
        if comm.comm_world.rank == 0:
            timer.report_clear("Dipole subtraction")

    # Optionally filter the signal

    apply_filter(args, data)
    timer.clear()

    # Estimate noise

    noise_estimator = tp.OpNoiseEstim(
        signal=tod_name,
        flags=flags_name,
        detmask=args.flagmask,
        commonmask=args.obtmask,
        maskfile=args.skymask,
        mapfile=args.skymap,
        out=args.out,
        rimo=rimo,
        pol=args.skypol,
        nbin_psd=args.nbin_psd,
        lagmax=args.lagmax,
        stationary_period=args.stationary_period,
        nosingle=args.nosingle,
        no_spin_harmonics=args.no_spin_harmonics,
        calibrate_signal_estimate=args.calibrate_signal_estimate)

    noise_estimator.exec(data)

    comm.comm_world.barrier()
    if comm.comm_world.rank == 0:
        timer.report_clear("Noise estimation")

    gt.stop_all()
    if mpiworld is not None:
        mpiworld.barrier()
    timer = Timer()
    timer.start()
    alltimers = gather_timers(comm=mpiworld)
    if comm.world_rank == 0:
        out = os.path.join(args.out, "timing")
        dump_timing(alltimers, out)
        timer.stop()
        timer.report("Gather and dump timing info")
    return
def main():
    log = Logger.get()
    gt = GlobalTimers.get()
    gt.start("toast_planck_reduce (total)")

    mpiworld, procs, rank, comm = get_comm()

    memreport("at beginning of main", mpiworld)

    # This is the 2-level toast communicator.  By default,
    # there is just one group which spans MPI_COMM_WORLD.
    comm = toast.Comm()

    if comm.world_rank == 0:
        print("Running with {} processes at {}".format(
            procs, str(datetime.datetime.now())))

    parser = argparse.ArgumentParser(description='Simple MADAM Mapmaking',
                                     fromfile_prefix_chars='@')
    parser.add_argument('--rimo', required=True, help='RIMO file')
    parser.add_argument('--freq', required=True, type=np.int, help='Frequency')
    parser.add_argument('--nside',
                        required=False,
                        type=np.int,
                        default=512,
                        help='Map resolution')
    parser.add_argument('--nside_cross',
                        required=False,
                        type=np.int,
                        default=512,
                        help='Destriping resolution')
    parser.add_argument('--debug',
                        dest='debug',
                        default=False,
                        action='store_true',
                        help='Write data distribution info to file')
    parser.add_argument('--dets',
                        required=False,
                        default=None,
                        help='Detector list (comma separated)')
    parser.add_argument('--effdir',
                        required=True,
                        help='Input Exchange Format File directory')
    parser.add_argument('--effdir_in_diode0',
                        required=False,
                        default=None,
                        help='Input Exchange Format File directory, '
                        'LFI diode 0')
    parser.add_argument('--effdir_in_diode1',
                        required=False,
                        default=None,
                        help='Input Exchange Format File directory, '
                        'LFI diode 1')
    parser.add_argument('--effdir_pntg',
                        required=False,
                        help='Input Exchange Format File directory '
                        'for pointing')
    parser.add_argument('--effdir_out',
                        required=False,
                        help='Output directory for destriped TOD')
    parser.add_argument('--effdir_out_diode0',
                        required=False,
                        help='Output directory for destriped TOD, LFI diode 0')
    parser.add_argument('--effdir_out_diode1',
                        required=False,
                        help='Output directory for destriped TOD, LFI diode 1')
    parser.add_argument('--obtmask',
                        required=False,
                        default=1,
                        type=np.int,
                        help='OBT flag mask')
    parser.add_argument('--flagmask',
                        required=False,
                        default=1,
                        type=np.int,
                        help='Quality flag mask')
    parser.add_argument('--pntflagmask',
                        required=False,
                        default=0,
                        type=np.int,
                        help='Pointing flag mask')
    parser.add_argument('--bad_intervals',
                        required=False,
                        help='Path to bad interval file.')
    parser.add_argument('--ringdb', required=True, help='Ring DB file')
    parser.add_argument('--odfirst',
                        required=False,
                        default=None,
                        type=np.int,
                        help='First OD to use')
    parser.add_argument('--odlast',
                        required=False,
                        default=None,
                        type=np.int,
                        help='Last OD to use')
    parser.add_argument('--ringfirst',
                        required=False,
                        default=None,
                        help='First ring to use (can be a list)')
    parser.add_argument('--ringlast',
                        required=False,
                        default=None,
                        help='Last ring to use (can be a list)')
    parser.add_argument('--obtfirst',
                        required=False,
                        default=None,
                        type=np.float,
                        help='First OBT to use')
    parser.add_argument('--obtlast',
                        required=False,
                        default=None,
                        type=np.float,
                        help='Last OBT to use')
    parser.add_argument('--madampar',
                        required=False,
                        default=None,
                        help='Madam parameter file')
    parser.add_argument('--out',
                        required=False,
                        default='.',
                        help='Output directory')
    parser.add_argument('--madam_prefix', required=False, help='map prefix')
    parser.add_argument('--split_mask',
                        required=False,
                        default=None,
                        help='Intensity mask, non-zero pixels are not split.')
    parser.add_argument('--save_leakage_matrices',
                        dest='save_leakage_matrices',
                        default=False,
                        action='store_true',
                        help='Compile and write out the leakage projection '
                        'matrices.')
    # noise parameters
    parser.add_argument('--noisefile',
                        required=False,
                        default='RIMO',
                        help='Path to noise PSD files for noise filter. '
                        'Tag DETECTOR will be replaced with detector name.')
    parser.add_argument('--static_noise',
                        dest='static_noise',
                        required=False,
                        default=False,
                        action='store_true',
                        help='Assume constant noise PSD')
    parser.add_argument('--filterfile',
                        required=False,
                        help='Extra filter file.')

    try:
        args = parser.parse_args()
    except SystemExit:
        sys.exit(0)

    if comm.comm_world.rank == 0:
        print('All parameters:')
        print(args, flush=True)

    timer = Timer()
    timer.start()

    nrange = 1

    odranges = None
    if args.odfirst is not None and args.odlast is not None:
        odranges = []
        firsts = [int(i) for i in str(args.odfirst).split(',')]
        lasts = [int(i) for i in str(args.odlast).split(',')]
        for odfirst, odlast in zip(firsts, lasts):
            odranges.append((odfirst, odlast))
        nrange = len(odranges)

    ringranges = None
    if args.ringfirst is not None and args.ringlast is not None:
        ringranges = []
        firsts = [int(i) for i in str(args.ringfirst).split(',')]
        lasts = [int(i) for i in str(args.ringlast).split(',')]
        for ringfirst, ringlast in zip(firsts, lasts):
            ringranges.append((ringfirst, ringlast))
        nrange = len(ringranges)

    obtranges = None
    if args.obtfirst is not None and args.obtlast is not None:
        obtranges = []
        firsts = [float(i) for i in str(args.obtfirst).split(',')]
        lasts = [float(i) for i in str(args.obtlast).split(',')]
        for obtfirst, obtlast in zip(firsts, lasts):
            obtranges.append((obtfirst, obtlast))
        nrange = len(obtranges)

    if odranges is None:
        odranges = [None] * nrange

    if ringranges is None:
        ringranges = [None] * nrange

    if obtranges is None:
        obtranges = [None] * nrange

    detectors = None
    if args.dets is not None:
        detectors = re.split(',', args.dets)

    # create the TOD for this observation

    if args.noisefile != 'RIMO' and not args.static_noise:
        do_eff_cache = True
    else:
        do_eff_cache = False

    tods = []

    if args.static_noise:
        noisefile = args.noisefile
    else:
        noisefile = 'RIMO'

    for obtrange, ringrange, odrange in zip(obtranges, ringranges, odranges):
        tods.append(
            tp.Exchange(comm=comm.comm_group,
                        detectors=detectors,
                        ringdb=args.ringdb,
                        effdir_in=args.effdir,
                        effdir_in_diode0=args.effdir_in_diode0,
                        effdir_in_diode1=args.effdir_in_diode1,
                        effdir_pntg=args.effdir_pntg,
                        obt_range=obtrange,
                        ring_range=ringrange,
                        od_range=odrange,
                        freq=args.freq,
                        RIMO=args.rimo,
                        obtmask=args.obtmask,
                        flagmask=args.flagmask,
                        pntflagmask=args.pntflagmask,
                        do_eff_cache=do_eff_cache,
                        noisefile=noisefile))

    rimo = tods[0].rimo

    # Make output directory

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

    # Read in madam parameter file
    # Allow more than one entry, gather into a list
    repeated_keys = ['detset', 'detset_nopol', 'survey']
    pars = {}

    if comm.comm_world.rank == 0:
        pars['kfirst'] = False
        pars['temperature_only'] = True
        pars['base_first'] = 60.0
        pars['nside_map'] = args.nside
        pars['nside_cross'] = min(args.nside, args.nside_cross)
        pars['nside_submap'] = 16
        pars['write_map'] = False
        pars['write_binmap'] = True
        pars['write_matrix'] = False
        pars['write_wcov'] = False
        pars['write_hits'] = True
        pars['kfilter'] = False
        pars['info'] = 3
        pars['pixlim_map'] = 1e-3
        pars['pixlim_cross'] = 1e-3
        if args.madampar:
            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 not comment.match(line):
                        result = pat.match(line)
                        if result:
                            key, value = result.group(1), result.group(2)
                            if key in repeated_keys:
                                if key not in pars:
                                    pars[key] = []
                                pars[key].append(value)
                            else:
                                pars[key] = value
        # Command line parameters override the ones in the madam parameter file
        if 'file_root' not in pars:
            pars['file_root'] = 'madam'
        if args.madam_prefix is not None:
            pars['file_root'] = args.madam_prefix
        sfreq = '{:03}'.format(args.freq)
        if sfreq not in pars['file_root']:
            pars['file_root'] += '_' + sfreq
        try:
            fsample = {30: 32.51, 44: 46.55, 70: 78.77}[args.freq]
        except Exception:
            fsample = 180.3737
        pars['fsample'] = fsample
        pars['path_output'] = args.out

        if args.save_leakage_matrices:
            pars['write_leakmatrix'] = True

    pars = comm.comm_world.bcast(pars, root=0)

    if args.noisefile != 'RIMO':
        # We split MPI_COMM_WORLD into single process groups, each of
        # which is assigned one or more observations (rings)
        comm = toast.Comm(groupsize=1)

    # This is the distributed data, consisting of one or
    # more observations, each distributed over a communicator.
    data = toast.Data(comm)

    for iobs, tod in enumerate(tods):
        if args.noisefile != 'RIMO' and not args.static_noise:
            # Use a toast helper method to optimally distribute rings between
            # processes.
            dist = toast.distribute_discrete(tod.ringsizes, comm.world_size)
            my_first_ring, my_n_ring = dist[comm.comm_world.rank]

            for my_ring in range(my_first_ring, my_first_ring + my_n_ring):
                ringtod = tp.Exchange.from_tod(tod,
                                               my_ring,
                                               comm.comm_group,
                                               noisefile=args.noisefile)
                ob = {}
                ob['name'] = 'ring{:05}'.format(ringtod.globalfirst_ring)
                ob['id'] = ringtod.globalfirst_ring
                ob['tod'] = ringtod
                ob['intervals'] = ringtod.valid_intervals
                ob['baselines'] = None
                ob['noise'] = ringtod.noise
                data.obs.append(ob)
        else:
            ob = {}
            ob['name'] = 'observation{:04}'.format(iobs)
            ob['id'] = 0
            ob['tod'] = tod
            ob['intervals'] = tod.valid_intervals
            ob['baselines'] = None
            ob['noise'] = tod.noise

            data.obs.append(ob)

    comm.comm_world.barrier()
    timer.stop()
    if comm.comm_world.rank == 0:
        timer.report("Metadata queries")

    if args.effdir_out is not None or (args.effdir_out_diode0 is not None
                                       and args.effdir_out_diode1 is not None):
        do_output = True
    else:
        do_output = False

    # Read in the signal

    timer.clear()
    timer.start()
    reader = tp.OpInputPlanck(signal_name='signal', flags_name='flags')
    if comm.comm_world.rank == 0:
        print('Reading input signal from {}'.format(args.effdir), flush=True)
    reader.exec(data)
    comm.comm_world.barrier()
    timer.stop()
    if comm.comm_world.rank == 0:
        timer.report("Read")
    tod_name = 'signal'
    flags_name = 'flags'

    # Optionally filter the signal

    apply_filter(args, data)

    # Optionally flag bad intervals

    if args.bad_intervals is not None:
        timer = Timer()
        timer.start()
        flagger = tp.OpBadIntervals(path=args.bad_intervals)
        flagger.exec(data)
        timer.stop()
        if comm.comm_world.rank == 0:
            timer.report("Apply {}".format(args.bad_intervals))

    # make a planck Healpix pointing matrix
    timer.clear()
    timer.start()
    mode = 'IQU'
    if pars['temperature_only'] == 'T':
        mode = 'I'
    nside = int(pars['nside_map'])
    pointing = tp.OpPointingPlanck(nside=nside,
                                   mode=mode,
                                   RIMO=rimo,
                                   margin=0,
                                   apply_flags=(not do_output),
                                   keep_vel=False,
                                   keep_pos=False,
                                   keep_phase=False,
                                   keep_quats=False)

    pointing.exec(data)

    comm.comm_world.barrier()
    timer.stop()
    if comm.comm_world.rank == 0:
        timer.report("Pointing Matrix, mode = {}".format(mode))

    for obs in data.obs:
        obs['tod'].purge_eff_cache()

    # for now, we pass in the noise weights from the RIMO.
    detweights = {}
    for d in tod.detectors:
        if d[-1] in '01' and d[-2] != '-':
            det = to_radiometer(d)
        else:
            det = d
        net = tod.rimo[det].net
        fsample = tod.rimo[det].fsample
        detweights[d] = 1.0 / (fsample * net * net)

    if do_output:
        name_out = 'madam_tod'
    else:
        name_out = None

    timer.clear()
    timer.start()
    try:
        madam = toast.todmap.OpMadam(name=tod_name,
                                     flag_name=flags_name,
                                     apply_flags=do_output,
                                     params=pars,
                                     detweights=detweights,
                                     purge=True,
                                     name_out=name_out,
                                     translate_timestamps=False)
    except Exception as e:
        raise Exception('{:4} : ERROR: failed to initialize Madam: {}'.format(
            comm.comm_world.rank, e))
    madam.exec(data)

    comm.comm_world.barrier()
    timer.stop()
    if comm.comm_world.rank == 0:
        timer.report("Madam")

    if do_output:
        timer = Timer()
        timer.start()
        writer = tp.OpOutputPlanck(signal_name='madam_tod',
                                   flags_name=None,
                                   commonflags_name=None,
                                   effdir_out=args.effdir_out,
                                   effdir_out_diode0=args.effdir_out_diode0,
                                   effdir_out_diode1=args.effdir_out_diode1)

        writer.exec(data)

        comm.comm_world.barrier()
        timer.stop()
        if comm.comm_world.rank == 0:
            timer.report("Madam output")

    memreport("at end of main", mpiworld)

    gt.stop_all()
    if mpiworld is not None:
        mpiworld.barrier()
    timer = Timer()
    timer.start()
    alltimers = gather_timers(comm=mpiworld)
    if comm.world_rank == 0:
        out = os.path.join(args.out, "timing")
        dump_timing(alltimers, out)
        timer.stop()
        timer.report("Gather and dump timing info")
    return