def write_templates(outfile, flux, wave, meta, objtype=None, comments=None, units=None): """Write out simulated galaxy templates. (Incomplete documentation...) Args: outfile (str): Output file name. """ from astropy.io import fits from desispec.io.util import fitsheader, write_bintable, makepath # Create the path to OUTFILE if necessary. outfile = makepath(outfile) header = dict(OBJTYPE=(objtype, 'Object type'), CUNIT=('Angstrom', 'units of wavelength array'), CRPIX1=(1, 'reference pixel number'), CRVAL1=(wave[0], 'Starting wavelength [Angstrom]'), CDELT1=(wave[1] - wave[0], 'Wavelength step [Angstrom]'), LOGLAM=(0, 'linear wavelength steps, not log10'), AIRORVAC=('vac', 'wavelengths in vacuum (vac) or air'), BUNIT=('1e-17 erg/(s cm2 Angstrom)', 'spectrum flux units')) hdr = fitsheader(header) fits.writeto(outfile, flux.astype(np.float32), header=hdr, clobber=True) write_bintable(outfile, meta, header=hdr, comments=comments, units=units, extname='METADATA')
def main(args): log = get_logger() for filename in args.infile: log.info("reading %s" % filename) frame = io.read_frame(filename) flux_per_angstrom = None if args.flux_per_angstrom: flux_per_angstrom = True elif args.flux_per_pixel: flux_per_angstrom = False else: flux_per_angstrom = None scores, comments = compute_and_append_frame_scores( frame, suffix=args.suffix, flux_per_angstrom=flux_per_angstrom, overwrite=args.overwrite) log.info("Adding or replacing SCORES extention with {} in {}".format( scores.keys(), filename)) write_bintable(filename, data=scores, comments=comments, extname="SCORES", clobber=True)
def write_fibermap(outfile, fibermap, header=None): """Write fibermap binary table to outfile. Args: outfile (str): output filename fibermap: astropy Table of fibermap data header: header data to include in same HDU as fibermap Returns: write_fibermap (str): full path to filename of fibermap file written. """ outfile = makepath(outfile) #- astropy.io.fits incorrectly generates warning about 2D arrays of strings #- Temporarily turn off warnings to avoid this; desispec.test.test_io will #- catch it if the arrays actually are written incorrectly. if header is not None: hdr = fitsheader(header) else: hdr = fitsheader(fibermap.meta) add_dependencies(hdr) with warnings.catch_warnings(): warnings.simplefilter("ignore") write_bintable(outfile, fibermap, hdr, comments=fibermap_comments, extname="FIBERMAP", clobber=True) return outfile
def write_fibermap(outfile, fibermap, header=None, clobber=True, extname='FIBERMAP'): """Write fibermap binary table to outfile. Args: outfile (str): output filename fibermap: astropy Table of fibermap data header: header data to include in same HDU as fibermap clobber (bool, optional): overwrite outfile if it exists extname (str, optional): set the extension name. Returns: write_fibermap (str): full path to filename of fibermap file written. """ outfile = makepath(outfile) #- astropy.io.fits incorrectly generates warning about 2D arrays of strings #- Temporarily turn off warnings to avoid this; desispec.test.test_io will #- catch it if the arrays actually are written incorrectly. if header is not None: hdr = fitsheader(header) else: hdr = fitsheader(fibermap.meta) add_dependencies(hdr) with warnings.catch_warnings(): warnings.simplefilter("ignore") write_bintable(outfile, fibermap, hdr, comments=fibermap_comments, extname=extname, clobber=clobber) return outfile
def write_fibermap(outfile, fibermap, header=None): """Write fibermap binary table to outfile. Args: outfile (str): output filename fibermap: ndarray with named columns of fibermap data header: header data to include in same HDU as fibermap Returns: write_fibermap (str): full path to filename of fibermap file written. """ outfile = makepath(outfile) #- astropy.io.fits incorrectly generates warning about 2D arrays of strings #- Temporarily turn off warnings to avoid this; desispec.test.test_io will #- catch it if the arrays actually are written incorrectly. hdr = fitsheader(header) add_dependencies(hdr) with warnings.catch_warnings(): warnings.simplefilter("ignore") write_bintable(outfile, fibermap, hdr, comments=fibermap_comments, extname="FIBERMAP", clobber=True) return outfile
def write_templates(outfile, flux, wave, meta, objtype=None, comments=None, units=None): """Write out simulated galaxy templates. (Incomplete documentation...) Args: outfile (str): Output file name. Returns: Raises """ from astropy.io import fits from desispec.io.util import fitsheader, write_bintable, makepath # Create the path to OUTFILE if necessary. outfile = makepath(outfile) header = dict( OBJTYPE = (objtype, 'Object type'), CUNIT = ('Angstrom', 'units of wavelength array'), CRPIX1 = (1, 'reference pixel number'), CRVAL1 = (wave[0], 'Starting wavelength [Angstrom]'), CDELT1 = (wave[1]-wave[0], 'Wavelength step [Angstrom]'), LOGLAM = (0, 'linear wavelength steps, not log10'), AIRORVAC = ('vac', 'wavelengths in vacuum (vac) or air'), BUNIT = ('erg/s/cm2/A', 'spectrum flux units') ) hdr = fitsheader(header) fits.writeto(outfile,flux.astype(np.float32),header=hdr,clobber=True) write_bintable(outfile, meta, header=hdr, comments=comments, units=units, extname='METADATA')
def bgs_write_simdata(sim, obs_conds, simdir, obsrand, overwrite=False): """Build and write a metadata table with the simulation inputs. Currently, the only quantities that can be varied are moonfrac, moonsep, and exptime, but more choices can be added as needed. """ from desispec.io.util import makepath simdatafile = os.path.join(simdir, sim['suffix'], 'bgs-{}-simdata.fits'.format(sim['suffix'])) makepath(simdatafile) cols = [('SEED', 'S20'), ('NSPEC', 'i4'), ('EXPTIME', 'f4'), ('AIRMASS', 'f4'), ('SEEING', 'f4'), ('MOONFRAC', 'f4'), ('MOONSEP', 'f4'), ('MOONALT', 'f4')] simdata = Table(np.zeros(sim['nsim'], dtype=cols)) simdata['EXPTIME'].unit = 's' simdata['SEEING'].unit = 'arcsec' simdata['MOONSEP'].unit = 'deg' simdata['MOONALT'].unit = 'deg' simdata['SEED'] = sim['seed'] simdata['NSPEC'] = sim['nspec'] simdata['AIRMASS'] = obs_conds['AIRMASS'] simdata['SEEING'] = obs_conds['SEEING'] simdata['MOONALT'] = obs_conds['MOONALT'] if 'moonfracmin' in obs_conds.keys(): simdata['MOONFRAC'] = obsrand.uniform(obs_conds['moonfracmin'], obs_conds['moonfracmax'], sim['nsim']) else: simdata['MOONFRAC'] = obs_conds['MOONFRAC'] if 'moonsepmin' in obs_conds.keys(): simdata['MOONSEP'] = obsrand.uniform(obs_conds['moonsepmin'], obs_conds['moonsepmax'], sim['nsim']) else: simdata['MOONSEP'] = obs_conds['MOONSEP'] if 'exptimemin' in obs_conds.keys(): simdata['EXPTIME'] = obsrand.uniform(obs_conds['exptimemin'], obs_conds['exptimemax'], sim['nsim']) else: simdata['EXPTIME'] = obs_conds['EXPTIME'] if overwrite or not os.path.isfile(simdatafile): print('Writing {}'.format(simdatafile)) write_bintable(simdatafile, simdata, extname='SIMDATA', clobber=overwrite) return simdata
def bgs_write_simdata(sim, overwrite=False): """Create a metadata table with simulation inputs. Parameters ---------- sim : dict Simulation parameters from command line. overwrite : bool Overwrite simulation data file. Returns ------- simdata : Table Data table written to disk. """ from desispec.io.util import makepath from desispec.io.util import write_bintable simdatafile = os.path.join(sim.simdir, 'bgs_{}_simdata.fits'.format(sim.simid)) makepath(simdatafile) cols = [('SEED', 'S20'), ('NSPEC', 'i4'), ('EXPTIME', 'f4'), ('AIRMASS', 'f4'), ('SEEING', 'f4'), ('MOONFRAC', 'f4'), ('MOONSEP', 'f4'), ('MOONALT', 'f4')] simdata = Table(np.zeros(sim.nsim, dtype=cols)) simdata['EXPTIME'].unit = 's' simdata['SEEING'].unit = 'arcsec' simdata['MOONSEP'].unit = 'deg' simdata['MOONALT'].unit = 'deg' simdata['SEED'] = sim.seed simdata['NSPEC'] = sim.nspec simdata['AIRMASS'] = sim.airmass simdata['SEEING'] = sim.seeing simdata['MOONALT'] = sim.moonalt simdata['MOONSEP'] = sim.moonsep simdata['MOONFRAC'] = sim.moonfrac simdata['EXPTIME'] = sim.exptime if overwrite or not os.path.isfile(simdatafile): print('Writing {}'.format(simdatafile)) write_bintable(simdatafile, simdata, extname='SIMDATA', clobber=True) return simdata
def write_fibermap(outfile, fibermap, header=None, clobber=True, extname='FIBERMAP'): """Write fibermap binary table to outfile. Args: outfile (str): output filename fibermap: astropy Table of fibermap data header: header data to include in same HDU as fibermap clobber (bool, optional): overwrite outfile if it exists extname (str, optional): set the extension name. Returns: write_fibermap (str): full path to filename of fibermap file written. """ log = get_logger() outfile = makepath(outfile) #- astropy.io.fits incorrectly generates warning about 2D arrays of strings #- Temporarily turn off warnings to avoid this; desispec.test.test_io will #- catch it if the arrays actually are written incorrectly. if header is not None: hdr = fitsheader(header) else: hdr = fitsheader(fibermap.meta) add_dependencies(hdr) with warnings.catch_warnings(): warnings.simplefilter("ignore") t0 = time.time() write_bintable(outfile, fibermap, hdr, comments=fibermap_comments, extname=extname, clobber=clobber) duration = time.time() - t0 log.info(iotime.format('write', outfile, duration)) return outfile
def main(args) : log = get_logger() for filename in args.infile : log.info("reading %s"%filename) frame=io.read_frame(filename) flux_per_angstrom=None if args.flux_per_angstrom : flux_per_angstrom=True elif args.flux_per_pixel : flux_per_angstrom=False else : flux_per_angstrom=None scores,comments=compute_and_append_frame_scores(frame,suffix=args.suffix,flux_per_angstrom=flux_per_angstrom,overwrite=args.overwrite) log.info("Adding or replacing SCORES extention with {} in {}".format(scores.keys(),filename)) write_bintable(filename,data=scores,comments=comments,extname="SCORES",clobber=True)
def write_simspec(meta, truth, expid, night, header=None, outfile=None): """ Write $DESI_SPECTRO_SIM/$PIXPROD/{night}/simspec-{expid}.fits Args: meta : metadata table to write to "METADATA" HDU truth : dictionary with keys: FLUX - 2D array [nspec, nwave] in erg/s/cm2/A WAVE - 1D array of vacuum wavelengths [Angstroms] SKYFLUX - array of sky flux [erg/s/cm2/A/arcsec], either 1D [nwave] or 2D [nspec, nwave] PHOT_{B,R,Z} - 2D array [nspec, nwave] of object photons/bin SKYPHOT_{B,R,Z} - 1D or 2D array of sky photons/bin expid : integer exposure ID night : string YEARMMDD header : optional dictionary of header items to add to output outfile : optional filename to write (otherwise auto-derived) Returns: full file path of output file written """ #- Where should this go? if outfile is None: outdir = simdir(night, mkdir=True) outfile = '{}/simspec-{:08d}.fits'.format(outdir, expid) #- Primary HDU is just a header from the input hx = fits.HDUList() hx.append(fits.PrimaryHDU(None, header=desispec.io.util.fitsheader(header))) #- Object flux HDU (might not exist, e.g. for an arc) if 'FLUX' in truth: x = fits.ImageHDU(truth['WAVE'], name='WAVE') x.header['BUNIT'] = ('Angstrom', 'Wavelength units') x.header['AIRORVAC'] = ('vac', 'Vacuum wavelengths') hx.append(x) x = fits.ImageHDU(truth['FLUX'].astype(np.float32), name='FLUX') x.header['BUNIT'] = '1e-17 erg/s/cm2/A' hx.append(x) #- Sky flux HDU if 'SKYFLUX' in truth: x = fits.ImageHDU(truth['SKYFLUX'].astype(np.float32), name='SKYFLUX') x.header['BUNIT'] = '1e-17 erg/s/cm2/A/arcsec2' hx.append(x) #- Write object photon and sky photons for each channel for channel in ['B', 'R', 'Z']: x = fits.ImageHDU(truth['WAVE_' + channel], name='WAVE_' + channel) x.header['BUNIT'] = ('Angstrom', 'Wavelength units') x.header['AIRORVAC'] = ('vac', 'Vacuum wavelengths') hx.append(x) extname = 'PHOT_' + channel x = fits.ImageHDU(truth[extname].astype(np.float32), name=extname) x.header['EXTNAME'] = (extname, channel + ' channel object photons per bin') hx.append(x) extname = 'SKYPHOT_' + channel if extname in truth: x = fits.ImageHDU(truth[extname].astype(np.float32), name=extname) x.header['EXTNAME'] = (extname, channel + ' channel sky photons per bin') hx.append(x) #- Write the file hx.writeto(outfile, clobber=True) #- Add Metadata table HDU; use write_bintable to get units and comments if meta is not None: comments = dict(OBJTYPE='Object type (ELG, LRG, QSO, STD, STAR)', REDSHIFT='true object redshift', TEMPLATEID='input template ID', OIIFLUX='[OII] flux [erg/s/cm2]', D4000='4000-A break') units = dict( # OBJTYPE = 'Object type (ELG, LRG, QSO, STD, STAR)', # REDSHIFT = 'true object redshift', # TEMPLATEID = 'input template ID', OIIFLUX='erg/s/cm2', ) write_bintable(outfile, meta, header=None, extname="METADATA", comments=comments, units=units) return outfile
def bgs_gather_results(sim, simdir, overwrite=False): """Gather all the pieces so we can make plots. """ from desispec.io.spectra import read_spectra import fitsio nspec = sim['nspec'] nall = nspec * sim['nsim'] resultfile = os.path.join(simdir, sim['suffix'], 'bgs-{}-results.fits'.format(sim['suffix'])) if not os.path.isfile(resultfile) or overwrite: pass else: log.info('File {} exists...skipping.'.format(resultfile)) return cols = [('EXPTIME', 'f4'), ('AIRMASS', 'f4'), ('MOONFRAC', 'f4'), ('MOONSEP', 'f4'), ('MOONALT', 'f4'), ('SNR_B', 'f4'), ('SNR_R', 'f4'), ('SNR_Z', 'f4'), ('TARGETID', 'i8'), ('TEMPLATEID', 'i4'), ('RMAG', 'f4'), ('GR', 'f4'), ('D4000', 'f4'), ('EWHBETA', 'f4'), ('ZTRUE', 'f4'), ('Z', 'f4'), ('ZERR', 'f4'), ('ZWARN', 'f4')] result = Table(np.zeros(nall, dtype=cols)) result['EXPTIME'].unit = 's' result['MOONSEP'].unit = 'deg' result['MOONALT'].unit = 'deg' # Read the simulation parameters data table. simdatafile = os.path.join(simdir, sim['suffix'], 'bgs-{}-simdata.fits'.format(sim['suffix'])) simdata = Table.read(simdatafile) for ii, simdata1 in enumerate(simdata): # Copy over some data. result['EXPTIME'][nspec * ii:nspec * (ii + 1)] = simdata1['EXPTIME'] result['AIRMASS'][nspec * ii:nspec * (ii + 1)] = simdata1['AIRMASS'] result['MOONFRAC'][nspec * ii:nspec * (ii + 1)] = simdata1['MOONFRAC'] result['MOONSEP'][nspec * ii:nspec * (ii + 1)] = simdata1['MOONSEP'] result['MOONALT'][nspec * ii:nspec * (ii + 1)] = simdata1['MOONALT'] # Read the metadata table. truefile = os.path.join( simdir, sim['suffix'], 'bgs-{}-{:03}-true.fits'.format(sim['suffix'], ii)) if os.path.isfile(truefile): log.info('Reading {}'.format(truefile)) meta = Table.read(truefile) #result['TARGETID'][nspec*ib:nspec*(ii+1)] = truth['TARGETID'] result['TEMPLATEID'][nspec * ii:nspec * (ii + 1)] = meta['TEMPLATEID'] result['RMAG'][nspec * ii:nspec * (ii + 1)] = 22.5 - 2.5 * np.log10(meta['FLUX_R']) result['GR'][nspec * ii:nspec * (ii + 1)] = -2.5 * np.log10( meta['FLUX_G'] / meta['FLUX_R']) result['D4000'][nspec * ii:nspec * (ii + 1)] = meta['D4000'] result['EWHBETA'][nspec * ii:nspec * (ii + 1)] = meta['EWHBETA'] result['ZTRUE'][nspec * ii:nspec * (ii + 1)] = meta['REDSHIFT'] # Read the zbest file. zbestfile = os.path.join( simdir, sim['suffix'], 'bgs-{}-{:03}-zbest.fits'.format(sim['suffix'], ii)) if os.path.isfile(zbestfile): log.info('Reading {}'.format(zbestfile)) #zbest = fitsio.read(zbestfile, 'ZBEST') #from astropy.table import Table zbest = Table.read(zbestfile, 'ZBEST') # Assume the tables are row-ordered! result['Z'][nspec * ii:nspec * (ii + 1)] = zbest['Z'] result['ZERR'][nspec * ii:nspec * (ii + 1)] = zbest['ZERR'] result['ZWARN'][nspec * ii:nspec * (ii + 1)] = zbest['ZWARN'] # Finally, read the spectra to get the S/N. spectrafile = os.path.join( simdir, sim['suffix'], 'bgs-{}-{:03}.fits'.format(sim['suffix'], ii)) if os.path.isfile(spectrafile): log.info('Reading {}'.format(spectrafile)) spec = read_spectra(spectrafile) for band in ('b', 'r', 'z'): for iobj in range(nspec): these = np.where( (spec.wave[band] > np.mean(spec.wave[band]) - 50) * (spec.wave[band] < np.mean(spec.wave[band]) + 50) * (spec.flux[band][iobj, :] > 0))[0] result['SNR_{}'.format( band.upper())][nspec * ii + iobj] = (np.median( spec.flux[band][iobj, these] * np.sqrt(spec.ivar[band][iobj, these]))) log.info('Writing {}'.format(resultfile)) write_bintable(resultfile, result, extname='RESULTS', clobber=True)
def main(args): # Set up the logger. if args.verbose: log = get_logger(DEBUG) else: log = get_logger() objtype = args.objtype.upper() log.debug('Using OBJTYPE {}'.format(objtype)) log.debug('Simulating {:g} bricks each with {:g} spectra'.format( args.nexp, args.nspec)) # Draw priors uniformly given the input ranges. rand = np.random.RandomState(args.seed) exptime = rand.uniform(args.exptime_range[0], args.exptime_range[1], args.nexp) airmass = rand.uniform(args.airmass_range[0], args.airmass_range[1], args.nexp) moonphase = rand.uniform(args.moon_phase_range[0], args.moon_phase_range[1], args.nexp) moonangle = rand.uniform(args.moon_angle_range[0], args.moon_angle_range[1], args.nexp) moonzenith = rand.uniform(args.moon_zenith_range[0], args.moon_zenith_range[1], args.nexp) maglimits = args.rmagrange - bgs zrange = args.zrange - bgs # Build a metadata table with the simulation inputs. metafile = makepath( os.path.join(args.outdir, '{}-input.fits'.format(args.brickname))) metacols = [('BRICKNAME', 'S20'), ('SEED', 'S20'), ('EXPTIME', 'f4'), ('AIRMASS', 'f4'), ('MOONPHASE', 'f4'), ('MOONANGLE', 'f4'), ('MOONZENITH', 'f4')] meta = Table(np.zeros(args.nexp, dtype=metacols)) meta['EXPTIME'].unit = 's' meta['MOONANGLE'].unit = 'deg' meta['MOONZENITH'].unit = 'deg' meta['BRICKNAME'] = [ '{}-{:03d}'.format(args.nexp, ii) for ii in range(args.nexp) ] meta['EXPTIME'] = exptime meta['AIRMASS'] = airmass meta['MOONPHASE'] = moonphase meta['MOONANGLE'] = moonangle meta['MOONZENITH'] = moonzenith log.debug('Writing {}'.format(metafile)) write_bintable(metafile, meta, extname='METADATA', clobber=True) # Generate each brick in turn. for ii in range(args.nexp): thisbrick = meta['BRICKNAME'][ii] log.debug('Building brick {}'.format(thisbrick)) brickargs = [ '--brickname', thisbrick, '--objtype', args.objtype, '--nspec', '{}'.format(args.nspec), '--outdir', os.path.join(args.brickdir, thisbrick), '--outdir-truth', os.path.join(args.brickdir, thisbrick), '--exptime', '{}'.format(exptime[ii]), '--airmass', '{}'.format(airmass[ii]), '--moon-phase', '{}'.format( moonphase[ii]), '--moon-angle', '{}'.format(moonangle[ii]), '--moon-zenith', '{}'.format(moonzenith[ii]), '--zrange-bgs', '{}'.format(args.zrange_bgs[0]), '{}'.format(args.zrange_bgs[1]), '--rmagrange-bgs', '{}'.format(args.rmagrange_bgs[0]), '{}'.format(args.rmagrange_bgs[1]) ] if args.seed is not None: brickargs.append('--seed') brickargs.append('{}'.format(args.seed)) quickargs = quickbrick.parse(brickargs) if args.verbose: quickargs.verbose = True quickbrick.main(quickargs)
def write_simspec(meta, truth, expid, night, header=None, outfile=None): """ Write $DESI_SPECTRO_SIM/$PIXPROD/{night}/simspec-{expid}.fits Args: meta : metadata table to write to "METADATA" HDU truth : dictionary with keys: FLUX - 2D array [nspec, nwave] in erg/s/cm2/A WAVE - 1D array of vacuum wavelengths [Angstroms] SKYFLUX - array of sky flux [erg/s/cm2/A/arcsec], either 1D [nwave] or 2D [nspec, nwave] PHOT_{B,R,Z} - 2D array [nspec, nwave] of object photons/bin SKYPHOT_{B,R,Z} - 1D or 2D array of sky photons/bin expid : integer exposure ID night : string YEARMMDD header : optional dictionary of header items to add to output outfile : optional filename to write (otherwise auto-derived) Returns: full file path of output file written """ #- Where should this go? if outfile is None: outdir = simdir(night, mkdir=True) outfile = '{}/simspec-{:08d}.fits'.format(outdir, expid) #- Primary HDU is just a header from the input hx = fits.HDUList() hx.append(fits.PrimaryHDU(None, header=desispec.io.util.fitsheader(header))) #- Object flux HDU (might not exist, e.g. for an arc) if 'FLUX' in truth: x = fits.ImageHDU(truth['WAVE'], name='WAVE') x.header['BUNIT'] = ('Angstrom', 'Wavelength units') x.header['AIRORVAC'] = ('vac', 'Vacuum wavelengths') hx.append(x) x = fits.ImageHDU(truth['FLUX'].astype(np.float32), name='FLUX') x.header['BUNIT'] = '1e-17 erg/s/cm2/A' hx.append(x) #- Sky flux HDU if 'SKYFLUX' in truth: x = fits.ImageHDU(truth['SKYFLUX'].astype(np.float32), name='SKYFLUX') x.header['BUNIT'] = '1e-17 erg/s/cm2/A/arcsec2' hx.append(x) #- Write object photon and sky photons for each channel for channel in ['B', 'R', 'Z']: x = fits.ImageHDU(truth['WAVE_'+channel], name='WAVE_'+channel) x.header['BUNIT'] = ('Angstrom', 'Wavelength units') x.header['AIRORVAC'] = ('vac', 'Vacuum wavelengths') hx.append(x) extname = 'PHOT_'+channel x = fits.ImageHDU(truth[extname].astype(np.float32), name=extname) x.header['EXTNAME'] = (extname, channel+' channel object photons per bin') hx.append(x) extname = 'SKYPHOT_'+channel if extname in truth: x = fits.ImageHDU(truth[extname].astype(np.float32), name=extname) x.header['EXTNAME'] = (extname, channel+' channel sky photons per bin') hx.append(x) #- Write the file hx.writeto(outfile, clobber=True) #- Add Metadata table HDU; use write_bintable to get units and comments if meta is not None: comments = dict( OBJTYPE = 'Object type (ELG, LRG, QSO, STD, STAR)', REDSHIFT = 'true object redshift', TEMPLATEID = 'input template ID', OIIFLUX = '[OII] flux [erg/s/cm2]', D4000 = '4000-A break' ) units = dict( # OBJTYPE = 'Object type (ELG, LRG, QSO, STD, STAR)', # REDSHIFT = 'true object redshift', # TEMPLATEID = 'input template ID', OIIFLUX = 'erg/s/cm2', ) write_bintable(outfile, meta, header=None, extname="METADATA", comments=comments, units=units) return outfile
def main(args): # Set up the logger. if args.verbose: log = get_logger(DEBUG) else: log = get_logger() objtype = args.objtype.upper() log.debug('Using OBJTYPE {}'.format(objtype)) log.debug('Simulating {:g} bricks each with {:g} spectra'.format(args.nbrick, args.nspec)) # Draw priors uniformly given the input ranges. rand = np.random.RandomState(args.seed) exptime = rand.uniform(args.exptime_range[0], args.exptime_range[1], args.nbrick) airmass = rand.uniform(args.airmass_range[0], args.airmass_range[1], args.nbrick) moonphase = rand.uniform(args.moon_phase_range[0], args.moon_phase_range[1], args.nbrick) moonangle = rand.uniform(args.moon_angle_range[0], args.moon_angle_range[1], args.nbrick) moonzenith = rand.uniform(args.moon_zenith_range[0], args.moon_zenith_range[1], args.nbrick) # Build a metadata table with the simulation inputs. metafile = makepath(os.path.join(args.outdir, '{}-input.fits'.format(args.brickname))) metacols = [ ('BRICKNAME', 'S20'), ('SEED', 'S20'), ('EXPTIME', 'f4'), ('AIRMASS', 'f4'), ('MOONPHASE', 'f4'), ('MOONANGLE', 'f4'), ('MOONZENITH', 'f4')] meta = Table(np.zeros(args.nbrick, dtype=metacols)) meta['EXPTIME'].unit = 's' meta['MOONANGLE'].unit = 'deg' meta['MOONZENITH'].unit = 'deg' meta['BRICKNAME'] = ['{}-{:03d}'.format(args.brickname, ii) for ii in range(args.nbrick)] meta['EXPTIME'] = exptime meta['AIRMASS'] = airmass meta['MOONPHASE'] = moonphase meta['MOONANGLE'] = moonangle meta['MOONZENITH'] = moonzenith log.debug('Writing {}'.format(metafile)) write_bintable(metafile, meta, extname='METADATA', clobber=True) # Generate each brick in turn. for ii in range(args.nbrick): thisbrick = meta['BRICKNAME'][ii] log.debug('Building brick {}'.format(thisbrick)) brickargs = ['--brickname', thisbrick, '--objtype', args.objtype, '--nspec', '{}'.format(args.nspec), '--outdir', os.path.join(args.brickdir, thisbrick), '--outdir-truth', os.path.join(args.brickdir, thisbrick), '--exptime', '{}'.format(exptime[ii]), '--airmass', '{}'.format(airmass[ii]), '--moon-phase', '{}'.format(moonphase[ii]), '--moon-angle', '{}'.format(moonangle[ii]), '--moon-zenith', '{}'.format(moonzenith[ii]), '--zrange-bgs', '{}'.format(args.zrange_bgs[0]), '{}'.format(args.zrange_bgs[1]), '--rmagrange-bgs', '{}'.format(args.rmagrange_bgs[0]), '{}'.format(args.rmagrange_bgs[1])] if args.seed is not None: brickargs.append('--seed') brickargs.append('{}'.format(args.seed)) quickargs = quickbrick.parse(brickargs) if args.verbose: quickargs.verbose = True quickbrick.main(quickargs)
def write_simspec(meta, truth, expid, night, header=None, outfile=None): """ Write $DESI_SPECTRO_SIM/$PIXPROD/{night}/simspec-{expid}.fits Args: meta : metadata table to write to "METADATA" HDU truth : dictionary with keys: FLUX - 2D array [nspec, nwave] in erg/s/cm2/A WAVE - 1D array of vacuum wavelengths [Angstroms] SKYFLUX - array of sky flux [erg/s/cm2/A/arcsec], either 1D [nwave] or 2D [nspec, nwave] PHOT_{B,R,Z} - 2D array [nspec, nwave] of object photons/bin SKYPHOT_{B,R,Z} - 1D or 2D array of sky photons/bin expid : integer exposure ID night : string YEARMMDD header : optional dictionary of header items to add to output outfile : optional filename to write (otherwise auto-derived) Returns: full file path of output file written """ #- Where should this go? if outfile is None: outdir = simdir(night, mkdir=True) outfile = '{}/simspec-{:08d}.fits'.format(outdir, expid) #- Object flux HDU (which might be just a header, e.g. for an arc) hdr = desispec.io.util.fitsheader(header) wave = truth['WAVE'] hdr['CRVAL1'] = (wave[0], 'Starting wavelength [Angstroms]') hdr['CDELT1'] = (wave[1]-wave[0], 'Wavelength step [Angstroms]') hdr['AIRORVAC'] = ('vac', 'Vacuum wavelengths') hdr['LOGLAM'] = (0, 'linear wavelength steps, not log10') if 'FLUX' in truth: hdr['EXTNAME'] = ('FLUX', 'Object flux [erg/s/cm2/A]') fits.writeto(outfile, truth['FLUX'].astype(np.float32), header=hdr, clobber=True) else: fits.writeto(outfile, np.zeros(0), header=hdr, clobber=True) #- Sky flux HDU if 'SKYFLUX' in truth: hdr['EXTNAME'] = ('SKYFLUX', 'Sky flux [erg/s/cm2/A/arcsec2]') hdu = fits.ImageHDU(truth['SKYFLUX'].astype(np.float32), header=hdr) fits.append(outfile, hdu.data, header=hdu.header) #- Metadata table HDU if meta is not None: comments = dict( OBJTYPE = 'Object type (ELG, LRG, QSO, STD, STAR)', REDSHIFT = 'true object redshift', TEMPLATEID = 'input template ID', O2FLUX = '[OII] flux [erg/s/cm2]', ) units = dict( # OBJTYPE = 'Object type (ELG, LRG, QSO, STD, STAR)', # REDSHIFT = 'true object redshift', # TEMPLATEID = 'input template ID', O2FLUX = 'erg/s/cm2', ) write_bintable(outfile, meta, header=None, extname="METADATA", comments=comments, units=units) #- Write object photon and sky photons for each channel for channel in ['B', 'R', 'Z']: hdr = fits.Header() wave = truth['WAVE_'+channel] hdr['CRVAL1'] = (wave[0], 'Starting wavelength [Angstroms]') hdr['CDELT1'] = (wave[1]-wave[0], 'Wavelength step [Angstroms]') hdr['AIRORVAC'] = ('vac', 'Vacuum wavelengths') hdr['LOGLAM'] = (0, 'linear wavelength steps, not log10') extname = 'PHOT_'+channel hdr['EXTNAME'] = (extname, channel+' channel object photons per bin') hdu = fits.ImageHDU(truth[extname].astype(np.float32), header=hdr) fits.append(outfile, hdu.data, header=hdu.header) extname = 'SKYPHOT_'+channel if extname in truth: hdr['EXTNAME'] = (extname, channel+' channel sky photons per bin') hdu = fits.ImageHDU(truth[extname].astype(np.float32), header=hdr) fits.append(outfile, hdu.data, header=hdu.header) return outfile
return vstack(dat), filemap if __name__ == '__main__': import argparse _nproc = multiprocessing.cpu_count() // 2 parser = argparse.ArgumentParser(usage="%(prog)s [options]") parser.add_argument("-i", "--indir", type=str, help="input data") parser.add_argument("-o", "--output", type=str, help="output file") parser.add_argument("--nproc", type=int, help="output file", default=_nproc) args = parser.parse_args() if args.indir is None: args.indir = _lyapath() if args.output is None: args.output = 'metadata-simpleSpec.fits' log.info('Reading mocks from {}'.format(args.indir)) data, filemap = read_lya(args.indir, nproc=args.nproc) log.info('Writing {}'.format(args.output)) write_bintable(args.output, data, extname='METADATA', clobber=True) header = dict(MOCKDIR=os.path.abspath(args.indir)) write_bintable(args.output, filemap, extname='FILEMAP', header=header)
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()