示例#1
0
parser = optparse.OptionParser(usage = "%prog [options]")
parser.add_option("-b", "--brick", type=str,  help="input brickname")
parser.add_option("-n", "--nspec", type=int,  help="number of spectra to fit [default: all]")
parser.add_option("-o", "--outfile", type=str,  help="output file name")
parser.add_option("--zspec",   help="also include spectra in output file", action="store_true")

opts, args = parser.parse_args()

log = get_logger()

#- Read brick files for each channel
log.info("Reading bricks")
brick = dict()
for channel in ('b', 'r', 'z'):
    filename = io.findfile('brick', band=channel, brickid=opts.brick)
    brick[channel] = io.Brick(filename)

#- Assume all channels have the same number of targets
#- TODO: generalize this to allow missing channels
if opts.nspec is None:
    opts.nspec = brick['b'].get_num_targets()
    log.info("Fitting {} targets".format(opts.nspec))
else:
    log.info("Fitting {} of {} targets".format(opts.nspec, brick['b'].get_num_targets()))

nspec = opts.nspec

#- Coadd individual exposures and combine channels
#- Full coadd code is a bit slow, so try something quick and dirty for
#- now to get something going for redshifting
log.info("Combining individual channels and exposures")
示例#2
0
def main(args, comm=None):

    log = get_logger()

    if args.npoly < 0:
        log.warning("Need npoly>=0, changing this %d -> 1" % args.npoly)
        args.npoly = 0
    if args.nproc < 1:
        log.warning("Need nproc>=1, changing this %d -> 1" % args.nproc)
        args.nproc = 1

    if comm is not None:
        if args.nproc != 1:
            if comm.rank == 0:
                log.warning("Using MPI, forcing multiprocessing nproc -> 1")
            args.nproc = 1

    if args.objtype is not None:
        args.objtype = args.objtype.split(',')

    #- Read brick files for each channel
    if (comm is None) or (comm.rank == 0):
        log.info("Reading bricks")
    brick = dict()
    if args.brick is not None:
        if len(args.brickfiles) != 0:
            raise RuntimeError(
                'Give -b/--brick or input brickfiles but not both')
        for channel in ('b', 'r', 'z'):
            filename = None
            if (comm is None) or (comm.rank == 0):
                filename = io.findfile('brick',
                                       band=channel,
                                       brickname=args.brick,
                                       specprod_dir=args.specprod_dir)
            if comm is not None:
                filename = comm.bcast(filename, root=0)
            brick[channel] = io.Brick(filename)
    else:
        for filename in args.brickfiles:
            bx = io.Brick(filename)
            if bx.channel not in brick:
                brick[bx.channel] = bx
            else:
                if (comm is None) or (comm.rank == 0):
                    log.error('Channel {} in multiple input files'.format(
                        bx.channel))
                sys.exit(2)

    filters = brick.keys()
    for fil in filters:
        if (comm is None) or (comm.rank == 0):
            log.info("Filter found: " + fil)

    #- Assume all channels have the same number of targets
    #- TODO: generalize this to allow missing channels
    #if args.nspec is None:
    #    args.nspec = brick['b'].get_num_targets()
    #    log.info("Fitting {} targets".format(args.nspec))
    #else:
    #    log.info("Fitting {} of {} targets".format(args.nspec, brick['b'].get_num_targets()))

    #- Coadd individual exposures and combine channels
    #- Full coadd code is a bit slow, so try something quick and dirty for
    #- now to get something going for redshifting
    if (comm is None) or (comm.rank == 0):
        log.info("Combining individual channels and exposures")
    wave = []
    for fil in filters:
        wave = np.concatenate([wave, brick[fil].get_wavelength_grid()])
    np.ndarray.sort(wave)
    nwave = len(wave)

    #- flux and ivar arrays to fill for all targets
    #flux = np.zeros((nspec, nwave))
    #ivar = np.zeros((nspec, nwave))
    flux = []
    ivar = []
    good_targetids = []
    targetids = brick['b'].get_target_ids()

    fpinfo = None
    if args.print_info is not None:
        if (comm is None) or (comm.rank == 0):
            fpinfo = open(args.print_info, "w")

    for i, targetid in enumerate(targetids):
        #- wave, flux, and ivar for this target; concatenate
        xwave = list()
        xflux = list()
        xivar = list()

        good = True
        for channel in filters:
            exp_flux, exp_ivar, resolution, info = brick[channel].get_target(
                targetid)
            weights = np.sum(exp_ivar, axis=0)
            ii, = np.where(weights > 0)
            if len(ii) == 0:
                good = False
                break
            xwave.extend(brick[channel].get_wavelength_grid()[ii])
            #- Average multiple exposures on the same wavelength grid for each channel
            xflux.extend(
                np.average(exp_flux[:, ii], weights=exp_ivar[:, ii], axis=0))
            xivar.extend(weights[ii])

        if not good:
            continue

        xwave = np.array(xwave)
        xivar = np.array(xivar)
        xflux = np.array(xflux)

        ii = np.argsort(xwave)
        #flux[i], ivar[i] = resample_flux(wave, xwave[ii], xflux[ii], xivar[ii])
        fl, iv = resample_flux(wave, xwave[ii], xflux[ii], xivar[ii])
        flux.append(fl)
        ivar.append(iv)
        good_targetids.append(targetid)
        if not args.print_info is None:
            s2n = np.median(fl[:-1] * np.sqrt(iv[:-1]) /
                            np.sqrt(wave[1:] - wave[:-1]))
            if (comm is None) or (comm.rank == 0):
                print targetid, s2n
                fpinfo.write(str(targetid) + " " + str(s2n) + "\n")

    if not args.print_info is None:
        if (comm is None) or (comm.rank == 0):
            fpinfo.close()
        sys.exit()

    good_targetids = good_targetids[args.first_spec:]
    flux = np.array(flux[args.first_spec:])
    ivar = np.array(ivar[args.first_spec:])
    nspec = len(good_targetids)
    if (comm is None) or (comm.rank == 0):
        log.info("number of good targets = %d" % nspec)
    if (args.nspec is not None) and (args.nspec < nspec):
        if (comm is None) or (comm.rank == 0):
            log.info("Fitting {} of {} targets".format(args.nspec, nspec))
        nspec = args.nspec
        good_targetids = good_targetids[:nspec]
        flux = flux[:nspec]
        ivar = ivar[:nspec]
    else:
        if (comm is None) or (comm.rank == 0):
            log.info("Fitting {} targets".format(nspec))

    if (comm is None) or (comm.rank == 0):
        log.debug("flux.shape={}".format(flux.shape))

    zf = None
    if comm is None:
        # Use multiprocessing built in to RedMonster.

        zf = RedMonsterZfind(wave=wave,
                             flux=flux,
                             ivar=ivar,
                             objtype=args.objtype,
                             zrange_galaxy=args.zrange_galaxy,
                             zrange_qso=args.zrange_qso,
                             zrange_star=args.zrange_star,
                             nproc=args.nproc,
                             npoly=args.npoly)

    else:
        # Use MPI

        # distribute the spectra among processes
        my_firstspec, my_nspec = dist_uniform(nspec, comm.size, comm.rank)
        my_specs = slice(my_firstspec, my_firstspec + my_nspec)
        for p in range(comm.size):
            if p == comm.rank:
                if my_nspec > 0:
                    log.info("process {} fitting spectra {} - {}".format(
                        p, my_firstspec, my_firstspec + my_nspec - 1))
                else:
                    log.info("process {} idle".format(p))
                sys.stdout.flush()
            comm.barrier()

        # do redshift fitting on each process
        myzf = None
        if my_nspec > 0:
            savelevel = os.environ["DESI_LOGLEVEL"]
            os.environ["DESI_LOGLEVEL"] = "WARNING"
            myzf = RedMonsterZfind(wave=wave,
                                   flux=flux[my_specs, :],
                                   ivar=ivar[my_specs, :],
                                   objtype=args.objtype,
                                   zrange_galaxy=args.zrange_galaxy,
                                   zrange_qso=args.zrange_qso,
                                   zrange_star=args.zrange_star,
                                   nproc=args.nproc,
                                   npoly=args.npoly)
            os.environ["DESI_LOGLEVEL"] = savelevel

        # Combine results into a single ZFindBase object on the root process.
        # We could do this with a gather, but we are using a small number of
        # processes, and point-to-point communication is easier for people to
        # understand.

        if comm.rank == 0:
            zf = ZfindBase(myzf.wave,
                           np.zeros((nspec, myzf.nwave)),
                           np.zeros((nspec, myzf.nwave)),
                           R=None,
                           results=None)

        for p in range(comm.size):
            if comm.rank == 0:
                if p == 0:
                    # root process copies its own data into output
                    zf.flux[my_specs] = myzf.flux
                    zf.ivar[my_specs] = myzf.ivar
                    zf.model[my_specs] = myzf.model
                    zf.z[my_specs] = myzf.z
                    zf.zerr[my_specs] = myzf.zerr
                    zf.zwarn[my_specs] = myzf.zwarn
                    zf.spectype[my_specs] = myzf.spectype
                    zf.subtype[my_specs] = myzf.subtype
                else:
                    # root process receives from process p and copies
                    # it into the output.
                    p_nspec = comm.recv(source=p, tag=0)
                    # only proceed if the sending process actually
                    # has some spectra assigned to it.
                    if p_nspec > 0:
                        p_firstspec = comm.recv(source=p, tag=1)
                        p_slice = slice(p_firstspec, p_firstspec + p_nspec)

                        p_flux = comm.recv(source=p, tag=2)
                        zf.flux[p_slice] = p_flux

                        p_ivar = comm.recv(source=p, tag=3)
                        zf.ivar[p_slice] = p_ivar

                        p_model = comm.recv(source=p, tag=4)
                        zf.model[p_slice] = p_model

                        p_z = comm.recv(source=p, tag=5)
                        zf.z[p_slice] = p_z

                        p_zerr = comm.recv(source=p, tag=6)
                        zf.zerr[p_slice] = p_zerr

                        p_zwarn = comm.recv(source=p, tag=7)
                        zf.zwarn[p_slice] = p_zwarn

                        p_type = comm.recv(source=p, tag=8)
                        zf.spectype[p_slice] = p_type

                        p_subtype = comm.recv(source=p, tag=9)
                        zf.subtype[p_slice] = p_subtype
            else:
                if p == comm.rank:
                    # process p sends to root
                    comm.send(my_nspec, dest=0, tag=0)
                    if my_nspec > 0:
                        comm.send(my_firstspec, dest=0, tag=1)
                        comm.send(myzf.flux, dest=0, tag=2)
                        comm.send(myzf.ivar, dest=0, tag=3)
                        comm.send(myzf.model, dest=0, tag=4)
                        comm.send(myzf.z, dest=0, tag=5)
                        comm.send(myzf.zerr, dest=0, tag=6)
                        comm.send(myzf.zwarn, dest=0, tag=7)
                        comm.send(myzf.spectype, dest=0, tag=8)
                        comm.send(myzf.subtype, dest=0, tag=9)
            comm.barrier()

    if (comm is None) or (comm.rank == 0):
        # The full results exist only on the rank zero process.

        # reformat results
        dtype = list()

        dtype = [
            ('Z', zf.z.dtype),
            ('ZERR', zf.zerr.dtype),
            ('ZWARN', zf.zwarn.dtype),
            ('SPECTYPE', zf.spectype.dtype),
            ('SUBTYPE', zf.subtype.dtype),
        ]

        formatted_data = np.empty(nspec, dtype=dtype)
        formatted_data['Z'] = zf.z
        formatted_data['ZERR'] = zf.zerr
        formatted_data['ZWARN'] = zf.zwarn
        formatted_data['SPECTYPE'] = zf.spectype
        formatted_data['SUBTYPE'] = zf.subtype

        # Create a ZfindBase object with formatted results
        zfi = ZfindBase(None, None, None, results=formatted_data)
        zfi.nspec = nspec

        # QA
        if (args.qafile is not None) or (args.qafig is not None):
            log.info("performing skysub QA")
            # Load
            qabrick = load_qa_brick(args.qafile)
            # Run
            qabrick.run_qa('ZBEST', (zfi, brick))
            # Write
            if args.qafile is not None:
                write_qa_brick(args.qafile, qabrick)
                log.info("successfully wrote {:s}".format(args.qafile))
            # Figure(s)
            if args.qafig is not None:
                raise IOError("Not yet implemented")
                qa_plots.brick_zbest(args.qafig, zfi, qabrick)

        #- Write some output
        if args.outfile is None:
            args.outfile = io.findfile('zbest', brickname=args.brick)

        log.info("Writing " + args.outfile)
        #io.write_zbest(args.outfile, args.brick, targetids, zfi, zspec=args.zspec)
        io.write_zbest(args.outfile,
                       args.brick,
                       good_targetids,
                       zfi,
                       zspec=args.zspec)

    return
示例#3
0
def main():

    log = get_logger()

    key = 'DESI_ROOT'
    if key not in os.environ:
        log.fatal('Required ${} environment variable not set'.format(key))
        return 0
    desidir = os.getenv(key)
    simsdir = os.path.join(desidir, 'spectro', 'sim', 'bgs-sims1.0')
    brickdir = os.path.join(simsdir, 'bricks')

    parser = argparse.ArgumentParser()
    parser.add_argument('--sim', type=int, default=None, help='Simulation number (see documentation)')
    parser.add_argument('--nproc', type=int, default=1, help='Number of processors to use.')
    parser.add_argument('--simsdir', default=simsdir, help='Top-level simulation directory')
    parser.add_argument('--bricks', action='store_true', help='Generate the brick files.')
    parser.add_argument('--zfind', action='store_true', help='Fit for the redshifts.')
    parser.add_argument('--results', action='store_true', help='Merge all the relevant results.')
    parser.add_argument('--qaplots', action='store_true', help='Generate QAplots.')

    args = parser.parse_args()
    if args.sim is None:
        parser.print_help()
        sys.exit(1)

    # --------------------------------------------------
    # Initialize the parameters of each simulation here.

    if args.sim == 1:
        seed = 678245
        
        brickname = 'sim01'
        nbrick = 50
        nspec = 20

        phase = 0.25
        rmag = (19.5, 19.5)
        redshift = (0.1, 0.3)
        zenith = 30
        exptime = 300
        angle = (0, 150)

        simoptions = [
            '--exptime-range', '{}'.format(exptime), '{}'.format(exptime), 
            '--rmagrange-bgs', '{}'.format(rmag[0]), '{}'.format(rmag[1]), 
            '--zrange-bgs', '{}'.format(redshift[0]), '{}'.format(redshift[1]), 
            '--moon-zenith-range', '{}'.format(zenith), '{}'.format(zenith), 
            '--moon-phase-range', '{}'.format(phase), '{}'.format(phase), 
            '--moon-angle-range', '{}'.format(angle[0]), '{}'.format(angle[1])
            ]
    elif args.sim == 2:
        seed = 991274
        
        brickname = 'sim02'
        nbrick = 50
        nspec = 20

        phase = (0.0, 1.0)
        rmag = (19.5, 19.5)
        redshift = (0.1, 0.3)
        zenith = 30
        exptime = 300
        angle = 60

        simoptions = [
            '--exptime-range', '{}'.format(exptime), '{}'.format(exptime), 
            '--rmagrange-bgs', '{}'.format(rmag[0]), '{}'.format(rmag[1]), 
            '--zrange-bgs', '{}'.format(redshift[0]), '{}'.format(redshift[1]), 
            '--moon-zenith-range', '{}'.format(zenith), '{}'.format(zenith), 
            '--moon-phase-range', '{}'.format(phase[0]), '{}'.format(phase[1]), 
            '--moon-angle-range', '{}'.format(angle), '{}'.format(angle)
            ]
            
    elif args.sim == 3:
        seed = 471934
        
        brickname = 'sim03'
        nbrick = 50
        nspec = 20

        phase = (0.0, 1.0)
        rmag = (19.5, 19.5)
        redshift = (0.1, 0.3)
        zenith = 30
        exptime = 300
        angle = (0, 150)

        simoptions = [
            '--exptime-range', '{}'.format(exptime), '{}'.format(exptime), 
            '--rmagrange-bgs', '{}'.format(rmag[0]), '{}'.format(rmag[1]), 
            '--zrange-bgs', '{}'.format(redshift[0]), '{}'.format(redshift[1]), 
            '--moon-zenith-range', '{}'.format(zenith), '{}'.format(zenith), 
            '--moon-phase-range', '{}'.format(phase[0]), '{}'.format(phase[1]), 
            '--moon-angle-range', '{}'.format(angle[0]), '{}'.format(angle[1])
            ]

    elif args.sim == 4:
        seed = 971234
        
        brickname = 'sim04'
        nbrick = 100
        nspec = 50

        phase = (0.0, 1.0)
        rmag = (17.5, 20.0)
        redshift = (0.1, 0.3)
        zenith = 30
        exptime = 300
        angle = (0, 150)

        simoptions = [
            '--exptime-range', '{}'.format(exptime), '{}'.format(exptime), 
            '--rmagrange-bgs', '{}'.format(rmag[0]), '{}'.format(rmag[1]), 
            '--zrange-bgs', '{}'.format(redshift[0]), '{}'.format(redshift[1]), 
            '--moon-zenith-range', '{}'.format(zenith), '{}'.format(zenith), 
            '--moon-phase-range', '{}'.format(phase[0]), '{}'.format(phase[1]), 
            '--moon-angle-range', '{}'.format(angle[0]), '{}'.format(angle[1])
            ]
            
    nobj = nbrick*nspec
    rand = np.random.RandomState(seed)

    # --------------------------------------------------
    # Generate the brick and truth files.
    if args.bricks:
        brightoptions = [
            '--brickname', '{}'.format(brickname),
            '--nbrick', '{}'.format(nbrick),
            '--nspec', '{}'.format(nspec),
            '--outdir', '{}'.format(simsdir),
            '--brickdir', '{}'.format(brickdir),
            '--seed', '{}'.format(seed),
            '--objtype', 'BGS']

        brightargs = brightsims.parse(np.hstack((brightoptions, simoptions)))
        brightargs.verbose = True
        brightsims.main(brightargs)

    # --------------------------------------------------
    # Fit the redshifts
    if args.zfind:
        inputfile = os.path.join(simsdir, brickname+'-input.fits')
        log.info('Reading {}'.format(inputfile))
        cat = fits.getdata(inputfile, 1)

        log.info('Testing with just one brick!')
        #for ib in range(10, 11):
        for ib in range(nbrick):
            thisbrick = cat['BRICKNAME'][ib]
            brickfiles = [os.path.join(brickdir, 'brick-{}-{}.fits'.format(ch, thisbrick)) for ch in ['b', 'r', 'z']]
            redoptions = [
                '--brick', thisbrick,
                '--nproc', '{}'.format(args.nproc),
                '--specprod_dir', simsdir, 
                '--zrange-galaxy', '{}'.format(redshift[0]), '{}'.format(redshift[1]), 
                '--outfile', os.path.join(brickdir, thisbrick, 'zbest-{}.fits'.format(thisbrick)),
                '--objtype', 'ELG,LRG']
            redargs = zfind.parse(redoptions)
            zfind.main(redargs)

    # --------------------------------------------------
    # Parse and write out the simulation inputs, brick spectra, and redshifts
    if args.results:
        inputfile = os.path.join(simsdir, brickname+'-input.fits')
        log.info('Reading {}'.format(inputfile))
        cat = fits.getdata(inputfile, 1)

        # Build a results table.
        resultfile = makepath(os.path.join(simsdir, '{}-results.fits'.format(brickname)))
        resultcols = [
            ('EXPTIME', 'f4'),
            ('AIRMASS', 'f4'),
            ('MOONPHASE', 'f4'),
            ('MOONANGLE', 'f4'),
            ('MOONZENITH', 'f4'),
            ('SNR_B', 'f4'),
            ('SNR_R', 'f4'),
            ('SNR_Z', 'f4'),
            ('TARGETID', 'i8'),
            ('RMAG', 'f4'),
            ('D4000', 'f4'),
            ('EWHBETA', 'f4'), 
            ('ZTRUE', 'f4'), 
            ('Z', 'f4'), 
            ('ZERR', 'f4'), 
            ('ZWARNING', 'f4')]
        result = Table(np.zeros(nobj, dtype=resultcols))

        result['EXPTIME'].unit = 's'
        result['MOONANGLE'].unit = 'deg'
        result['MOONZENITH'].unit = 'deg'

        for ib in range(nbrick):
            # Copy over some data.
            thisbrick = cat['BRICKNAME'][ib]
            result['EXPTIME'][nspec*ib:nspec*(ib+1)] = cat['EXPTIME'][ib]
            result['AIRMASS'][nspec*ib:nspec*(ib+1)] = cat['AIRMASS'][ib]
            result['MOONPHASE'][nspec*ib:nspec*(ib+1)] = cat['MOONPHASE'][ib]
            result['MOONANGLE'][nspec*ib:nspec*(ib+1)] = cat['MOONANGLE'][ib]
            result['MOONZENITH'][nspec*ib:nspec*(ib+1)] = cat['MOONZENITH'][ib]

            # Read the truth file of the first channel to get the metadata.
            truthfile = os.path.join(brickdir, thisbrick, 'truth-brick-{}-{}.fits'.format('b', thisbrick))
            log.info('Reading {}'.format(truthfile))
            truth = io.Brick(truthfile).hdu_list[4].data

            result['TARGETID'][nspec*ib:nspec*(ib+1)] = truth['TARGETID']
            result['RMAG'][nspec*ib:nspec*(ib+1)] = 22.5-2.5*np.log10(truth['DECAM_FLUX'][:,2])
            result['D4000'][nspec*ib:nspec*(ib+1)] = truth['D4000']
            result['EWHBETA'][nspec*ib:nspec*(ib+1)] = truth['EWHBETA']
            result['ZTRUE'][nspec*ib:nspec*(ib+1)] = truth['TRUEZ']

            # Finally read the zbest file. 
            zbestfile = os.path.join(brickdir, thisbrick, 'zbest-{}.fits'.format(thisbrick))
            if os.path.isfile(zbestfile):
                log.info('Reading {}'.format(zbestfile))
                zbest = read_zbest(zbestfile)
                # There's gotta be a better way than looping here!
                for ii in range(nspec):
                    this = np.where(zbest.targetid[ii] == result['TARGETID'])[0]
                    result['Z'][this] = zbest.z[ii]
                    result['ZERR'][this] = zbest.zerr[ii]
                    result['ZWARNING'][this] = zbest.zwarn[ii]

            #pdb.set_trace()
                    
            # Finally, read the spectra and truth tables, one per channel.
            for channel in ('b','r','z'):
                brickfile = os.path.join(brickdir, thisbrick, 'brick-{}-{}.fits'.format(channel, thisbrick))

                log.info('Reading {}'.format(brickfile))
                brick = io.Brick(brickfile)
                wave = brick.get_wavelength_grid()

                for iobj in range(nspec):
                    flux = brick.hdu_list[0].data[iobj,:]
                    ivar = brick.hdu_list[1].data[iobj,:]
                    these = np.where((wave>np.mean(wave)-50)*(wave<np.mean(wave)+50)*(flux>0))[0]
                    result['SNR_'+channel.upper()][nspec*ib+iobj] = \
                      np.median(np.sqrt(flux[these]*ivar[these]))

        log.info('Writing {}'.format(resultfile))
        write_bintable(resultfile, result, extname='RESULTS', clobber=True)
        
    # --------------------------------------------------
    # Build QAplots
    if args.qaplots:
        phaserange = (-0.05, 1.05)
        snrrange = (0, 3)
        rmagrange = (17.5, 20)
        anglerange = (-5, 155)
        d4000range = (0.9, 2.2)
        snrinterval = 1.0

        cmap = mpl.colors.ListedColormap(sns.color_palette('muted'))
        
        resultfile = os.path.join(simsdir, '{}-results.fits'.format(brickname))
        log.info('Reading {}'.format(resultfile))
        res = fits.getdata(resultfile, 1)

        qafile = os.path.join(simsdir, 'qa-{}.pdf'.format(brickname))
        log.info('Writing {}'.format(qafile))

        # ------------------------------
        # Simulation 1
        if args.sim == 1:
            fig, ax0 = plt.subplots(1, 1, figsize=(6, 4.5))

            ax0.scatter(res['MOONANGLE']+rand.normal(0, 0.2, nobj), res['SNR_B'], label='b channel', c=col[1])
            ax0.scatter(res['MOONANGLE']+rand.normal(0, 0.2, nobj), res['SNR_R'], label='r channel', c=col[2])
            ax0.scatter(res['MOONANGLE']+rand.normal(0, 0.2, nobj), res['SNR_Z'], label='z channel', c=col[0])
            ax0.set_xlabel('Object-Moon Angle (deg)')
            ax0.set_ylabel(r'Signal-to-Noise Ratio (pixel$^{-1}$)')
            ax0.set_xlim(anglerange)
            ax0.set_ylim(snrrange)

            plt.legend(loc='upper left', labelspacing=0.25)
            plt.text(0.95, 0.7, 't = {:g} s\nr = {:g} mag\nRedshift = {:.2f}-{:.2f}'.format(exptime, rmag,
                                                                                            redshift[0], redshift[1])+\
                     '\nLunar Zenith Angle = {:g} deg\nLunar Phase = {:g}'.format(zenith, phase),
                     horizontalalignment='right', transform=ax0.transAxes,
                     fontsize=11)
    
            plt.subplots_adjust(bottom=0.2, right=0.95, left=0.15)
            plt.savefig(qafile)
            plt.close()

            #pdb.set_trace()

        # ------------------------------
        # Simulation 2
        if args.sim == 2:
            fig, ax0 = plt.subplots(1, 1, figsize=(6, 4.5))

            ax0.scatter(res['MOONPHASE']+rand.normal(0, 0.02, nobj), res['SNR_B'], label='b channel', c=col[1])
            ax0.scatter(res['MOONPHASE']+rand.normal(0, 0.02, nobj), res['SNR_R'], label='r channel', c=col[2])
            ax0.scatter(res['MOONPHASE']+rand.normal(0, 0.02, nobj), res['SNR_Z'], label='z channel', c=col[0])
            ax0.set_xlabel('Lunar Phase (0=Full, 1=New)')
            ax0.set_ylabel(r'Signal-to-Noise Ratio (pixel$^{-1}$)')
            ax0.set_xlim(phaserange)
            ax0.set_ylim(snrrange)

            plt.legend(loc='upper left', labelspacing=0.25)
            plt.text(0.95, 0.7, 't = {:g} s\nr = {:g} mag\nRedshift = {:.2f}-{:.2f}'.format(exptime, rmag,
                                                                                            redshift[0], redshift[1])+\
                     '\nLunar Zenith Angle = {:g} deg\nObject-Moon Angle = {:g} deg'.format(zenith, angle),
                     horizontalalignment='right', transform=ax0.transAxes,
                     fontsize=11)
    
            plt.subplots_adjust(bottom=0.2, right=0.95, left=0.15)
            plt.savefig(qafile)
            plt.close()

        # ------------------------------
        # Simulation 3
        if args.sim == 3:
            fig, ax0 = plt.subplots(1, 1, figsize=(6, 4))

            im = ax0.scatter(res['MOONANGLE']+rand.normal(0, 0.3, nobj), res['SNR_R'], c=res['MOONPHASE'], vmin=phaserange,
                             cmap=cmap)
            ax0.set_xlabel('Object-Moon Angle (deg)')
            ax0.set_ylabel(r'r channel Signal-to-Noise Ratio (pixel$^{-1}$)')
            ax0.set_xlim(anglerange)
            ax0.set_ylim(snrrange)
            ax0.yaxis.set_major_locator(mpl.ticker.MultipleLocator(snrinterval))

            plt.text(0.95, 0.7, 't = {:g} s\nr = {:g} mag\nRedshift = {:.2f}-{:.2f}'.format(exptime, rmag,
                                                                                            redshift[0], redshift[1])+\
                     '\nLunar Zenith Angle = {:g} deg'.format(zenith),
                     horizontalalignment='right', transform=ax0.transAxes,
                     fontsize=11)
    
            cbar = fig.colorbar(im)
            cbar.set_ticks([0, 0.5, 1])
            cbar.ax.set_yticklabels(['Full','Quarter','New'], rotation=90)
            cbar.ax.set_ylabel('Lunar Phase')

            plt.subplots_adjust(bottom=0.2, right=1.0)
            plt.savefig(qafile)
            plt.close()

        # ------------------------------
        # Simulation 4
        if args.sim == 4:
            bins = 30
            zgood = (np.abs(res['Z']-res['ZTRUE'])<5E-5)*(res['ZWARNING']==0)*1

            H, xedges, yedges = np.histogram2d(res['RMAG'], res['MOONPHASE'], bins=bins, weights=zgood)
            H2, _, _ = np.histogram2d(res['RMAG'], res['MOONPHASE'], bins=bins)
            extent = np.array((rmagrange, phaserange)).flatten()

            #pdb.set_trace()            

            fig, ax0 = plt.subplots(1, 1, figsize=(6, 4))
            im = ax0.imshow(H/H2, extent=extent, interpolation='nearest')#, cmap=cmap)
            ax0.set_xlabel('r (AB mag)')
            ax0.set_ylabel('Lunar Phase (0=Full, 1=New)')
            ax0.set_xlim(rmagrange)
            ax0.set_ylim(phaserange)

            #plt.text(0.95, 0.7, 't = {:g} s\nr = {:g} mag\nRedshift = {:.2f}-{:.2f}'.format(exptime, rmag,
            #                                                                                redshift[0], redshift[1])+\
            #         '\nLunar Zenith Angle = {:g} deg'.format(zenith),
            #         horizontalalignment='right', transform=ax0.transAxes,
            #         fontsize=11)
    
            cbar = fig.colorbar(im)
            #cbar.set_ticks([0, 0.5, 1])
            #cbar.ax.set_yticklabels(['Full','Quarter','New'], rotation=90)
            #cbar.ax.set_ylabel('Lunar Phase')

            plt.subplots_adjust(bottom=0.2, right=1.0)
            plt.savefig(qafile)
            plt.close()
        
        # ------------------------------
        # Simulation 10
        if args.sim == 10:
            zgood = (np.abs(res['Z']-res['ZTRUE'])<0.001)*(res['ZWARNING']==0)*1
            #pdb.set_trace()            
            
            fig, (ax0, ax1, ax2) = plt.subplots(3, 1, figsize=(6,6))

            # moon phase vs S/N(r)
            im = ax0.scatter(res['RMAG'], res['SNR_R'], c=res['MOONPHASE'], vmin=phaserange,
                             cmap=cmap)
            ax0.set_xlabel('r (AB mag)')
            ax0.set_ylabel('S/N (r channel)')
            ax0.set_xlim(rmagrange)
            ax0.set_ylim(snrrange)
            ax0.yaxis.set_major_locator(mpl.ticker.MultipleLocator(snrinterval))

            # object-moon angle vs S/N(r)
            im = ax1.scatter(res['MOONANGLE'], res['SNR_R'], c=res['MOONPHASE'], vmin=phaserange,
                             cmap=cmap)
            ax1.set_xlabel('Object-Moon Angle (deg)')
            ax1.set_ylabel('S/N (r channel)')
            ax1.set_xlim(anglerange)
            ax1.set_ylim(snrrange)
            ax1.yaxis.set_major_locator(mpl.ticker.MultipleLocator(snrinterval))

            # D(4000) vs S/N(r)
            im = ax2.scatter(res['D4000'], res['SNR_R'], c=res['MOONPHASE'], vmin=phaserange,
                             cmap=cmap)
            ax2.set_xlabel('$D_{n}(4000)$')
            ax2.set_ylabel('S/N (r channel)')
            ax2.set_xlim(d4000range)
            ax2.set_ylim(snrrange)
            ax2.yaxis.set_major_locator(mpl.ticker.MultipleLocator(snrinterval))

            # Shared colorbar
            cbarax = fig.add_axes([0.83, 0.15, 0.03, 0.8])
            cbar = fig.colorbar(im, cax=cbarax)
            ticks = ['Full','Quarter','New']
            cbar.set_ticks([0, 0.5, 1])
            cbar.ax.set_yticklabels(ticks, rotation=-45)
        
            plt.tight_layout(pad=0.5)#, h_pad=0.2, w_pad=0.3)
            plt.subplots_adjust(right=0.78)
            plt.savefig(qafile)
            plt.close()