Exemple #1
0
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
Exemple #2
0
def write_sky(outfile, skymodel, header=None):
    """Write sky model.

    Args:
        outfile : filename or (night, expid, camera) tuple
        skymodel : SkyModel object, with the following attributes
            wave : 1D wavelength in vacuum Angstroms
            flux : 2D[nspec, nwave] sky flux
            ivar : 2D inverse variance of sky flux
            mask : 2D mask for sky flux
        header : optional fits header data (fits.Header, dict, or list)
    """
    outfile = makepath(outfile, 'sky')

    #- Convert header to fits.Header if needed
    if header is not None:
        hdr = fitsheader(header)
    else:
        hdr = fitsheader(skymodel.header)

    add_dependencies(hdr)

    hx = fits.HDUList()

    hdr['EXTNAME'] = ('SKY', 'no dimension')
    hx.append( fits.PrimaryHDU(skymodel.flux.astype('f4'), header=hdr) )
    hx.append( fits.ImageHDU(skymodel.ivar.astype('f4'), name='IVAR') )
    hx.append( fits.CompImageHDU(skymodel.mask, name='MASK') )
    hx.append( fits.ImageHDU(skymodel.wave.astype('f4'), name='WAVELENGTH') )

    hx.writeto(outfile+'.tmp', clobber=True, checksum=True)
    os.rename(outfile+'.tmp', outfile)

    return outfile
Exemple #3
0
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
Exemple #4
0
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
Exemple #5
0
def write_flux_calibration(outfile, fluxcalib, header=None):
    """Writes  flux calibration.

    Args:
        outfile : output file name
        fluxcalib : FluxCalib object

    Options:
        header : dict-like object of key/value pairs to include in header
    """
    hx = fits.HDUList()

    hdr = fitsheader(header)
    add_dependencies(hdr)

    hdr['EXTNAME'] = 'FLUXCALIB'
    hdr['BUNIT'] = ('10**+17 cm2 count s / erg',
                    'i.e. (elec/A) / (1e-17 erg/s/cm2/A)')
    hx.append(fits.PrimaryHDU(fluxcalib.calib.astype('f4'), header=hdr))
    hx.append(fits.ImageHDU(fluxcalib.ivar.astype('f4'), name='IVAR'))
    # hx.append( fits.CompImageHDU(fluxcalib.mask, name='MASK') )
    hx.append(fits.ImageHDU(fluxcalib.mask, name='MASK'))
    hx.append(fits.ImageHDU(fluxcalib.wave.astype('f4'), name='WAVELENGTH'))
    hx[-1].header['BUNIT'] = 'Angstrom'

    hx.writeto(outfile + '.tmp', overwrite=True, checksum=True)
    os.rename(outfile + '.tmp', outfile)

    return outfile
Exemple #6
0
def write_flux_calibration(outfile, fluxcalib, header=None):
    """Writes  flux calibration.
    
    Args:
        outfile : output file name
        fluxcalib : FluxCalib object
        
    Options:
        header : dict-like object of key/value pairs to include in header
    """
    hx = fits.HDUList()
    
    hdr = fitsheader(header)
    add_dependencies(hdr)
    
    hdr['EXTNAME'] = 'FLUXCALIB'
    hdr['BUNIT'] = ('(electrons/A) / (erg/s/cm2/A)', 'electrons per flux unit')
    hx.append( fits.PrimaryHDU(fluxcalib.calib.astype('f4'), header=hdr) )
    hx.append( fits.ImageHDU(fluxcalib.ivar.astype('f4'), name='IVAR') )
    hx.append( fits.CompImageHDU(fluxcalib.mask, name='MASK') )
    hx.append( fits.ImageHDU(fluxcalib.wave, name='WAVELENGTH') )
    
    hx.writeto(outfile+'.tmp', clobber=True, checksum=True)
    os.rename(outfile+'.tmp', outfile)

    return outfile
Exemple #7
0
def write_fiberflat(outfile,fiberflat,header=None):
    """Write fiberflat object to outfile

    Args:
        outfile: filepath string or (night, expid, camera) tuple
        fiberflat: FiberFlat object
        header: (optional) dict or fits.Header object to use as HDU 0 header

    Returns:
        filepath of file that was written
    """
    outfile = makepath(outfile, 'fiberflat')

    if header is None:
        hdr = fitsheader(fiberflat.header)
    else:
        hdr = fitsheader(header)
    if fiberflat.chi2pdf is not None:
        hdr['chi2pdf'] = float(fiberflat.chi2pdf)

    add_dependencies(hdr)

    ff = fiberflat   #- shorthand
    
    hdus = fits.HDUList()
    hdus.append(fits.PrimaryHDU(ff.fiberflat.astype('f4'), header=hdr))
    hdus.append(fits.ImageHDU(ff.ivar.astype('f4'),     name='IVAR'))
    hdus.append(fits.CompImageHDU(ff.mask,              name='MASK'))
    hdus.append(fits.ImageHDU(ff.meanspec.astype('f4'), name='MEANSPEC'))
    hdus.append(fits.ImageHDU(ff.wave.astype('f4'),     name='WAVELENGTH'))
    
    hdus.writeto(outfile+'.tmp', clobber=True, checksum=True)
    os.rename(outfile+'.tmp', outfile)
    return outfile
Exemple #8
0
def write_fiberflat(outfile, fiberflat, header=None):
    """Write fiberflat object to outfile

    Args:
        outfile: filepath string or (night, expid, camera) tuple
        fiberflat: FiberFlat object
        header: (optional) dict or fits.Header object to use as HDU 0 header

    Returns:
        filepath of file that was written
    """
    outfile = makepath(outfile, 'fiberflat')

    if header is None:
        hdr = fitsheader(fiberflat.header)
    else:
        hdr = fitsheader(header)
    if fiberflat.chi2pdf is not None:
        hdr['chi2pdf'] = float(fiberflat.chi2pdf)

    add_dependencies(hdr)

    ff = fiberflat  #- shorthand

    hdus = fits.HDUList()
    hdus.append(fits.PrimaryHDU(ff.fiberflat.astype('f4'), header=hdr))
    hdus.append(fits.ImageHDU(ff.ivar.astype('f4'), name='IVAR'))
    hdus.append(fits.CompImageHDU(ff.mask, name='MASK'))
    hdus.append(fits.ImageHDU(ff.meanspec.astype('f4'), name='MEANSPEC'))
    hdus.append(fits.ImageHDU(ff.wave.astype('f4'), name='WAVELENGTH'))

    hdus.writeto(outfile + '.tmp', clobber=True, checksum=True)
    os.rename(outfile + '.tmp', outfile)
    return outfile
Exemple #9
0
def write_stdstar_models(norm_modelfile, normalizedFlux, wave, fibers, data,
        fibermap, input_frames, header=None):
    """Writes the normalized flux for the best models.

    Args:
        norm_modelfile : output file path
        normalizedFlux : 2D array of flux[nstdstars, nwave]
        wave : 1D array of wavelengths[nwave] in Angstroms
        fibers : 1D array of fiberids for these spectra
        data : meta data table about which templates best fit
        fibermap : fibermaps rows for the input standard stars
        input_frames : Table with NIGHT, EXPID, CAMERA of input frames used
    """
    log = get_logger()
    hdr = fitsheader(header)
    add_dependencies(hdr)

    #- support input Table, np.array, and dict
    data = Table(data)

    hdr['EXTNAME'] = ('FLUX', '[10**-17 erg/(s cm2 Angstrom)]')
    hdr['BUNIT'] = ('10**-17 erg/(s cm2 Angstrom)', 'Flux units')
    hdu1=fits.PrimaryHDU(normalizedFlux.astype('f4'), header=hdr)

    hdu2 = fits.ImageHDU(wave.astype('f4'))
    hdu2.header['EXTNAME'] = ('WAVELENGTH', '[Angstrom]')
    hdu2.header['BUNIT'] = ('Angstrom', 'Wavelength units')

    hdu3 = fits.ImageHDU(fibers, name='FIBERS')

    # metadata
    from astropy.io.fits import Column
    cols=[]
    for k in data.colnames:
        if len(data[k].shape)==1 :
            cols.append(Column(name=k,format='D',array=data[k]))
    tbhdu=fits.BinTableHDU.from_columns(fits.ColDefs(cols), name='METADATA')

    hdulist=fits.HDUList([hdu1,hdu2,hdu3,tbhdu])

    # add coefficients
    if "COEFF" in data.colnames:
        hdulist.append(fits.ImageHDU(data["COEFF"],name="COEFF"))

    fmhdu = table_to_hdu(Table(fibermap))
    fmhdu.name = 'FIBERMAP'
    hdulist.append(fmhdu)

    inhdu = table_to_hdu(Table(input_frames))
    inhdu.name = 'INPUT_FRAMES'
    hdulist.append(inhdu)

    t0 = time.time()
    tmpfile = norm_modelfile+".tmp"
    hdulist.writeto(tmpfile, overwrite=True, checksum=True)
    os.rename(tmpfile, norm_modelfile)
    duration = time.time() - t0
    log.info(iotime.format('write', norm_modelfile, duration))
Exemple #10
0
def write_fiberflat(outfile,fiberflat,header=None, fibermap=None):
    """Write fiberflat object to outfile

    Args:
        outfile: filepath string or (night, expid, camera) tuple
        fiberflat: FiberFlat object

    Optional:
        header: dict or fits.Header object to use as HDU 0 header
        fibermap: table to store as FIBERMAP HDU

    Returns:
        filepath of file that was written
    """
    log = get_logger()
    outfile = makepath(outfile, 'fiberflat')

    if header is None:
        hdr = fitsheader(fiberflat.header)
    else:
        hdr = fitsheader(header)
    if fiberflat.chi2pdf is not None:
        hdr['chi2pdf'] = float(fiberflat.chi2pdf)

    hdr['EXTNAME'] = 'FIBERFLAT'
    if 'BUNIT' in hdr:
        del hdr['BUNIT']

    add_dependencies(hdr)

    ff = fiberflat   #- shorthand

    hdus = fits.HDUList()
    hdus.append(fits.PrimaryHDU(ff.fiberflat.astype('f4'), header=hdr))
    hdus.append(fits.ImageHDU(ff.ivar.astype('f4'),     name='IVAR'))
    hdus.append(fits.ImageHDU(ff.mask,              name='MASK'))
    hdus.append(fits.ImageHDU(ff.meanspec.astype('f4'), name='MEANSPEC'))
    hdus.append(fits.ImageHDU(ff.wave.astype('f4'),     name='WAVELENGTH'))
    if fibermap is None :
        fibermap=ff.fibermap
    if fibermap is not None:
        fibermap = encode_table(fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append( fits.convenience.table_to_hdu(fibermap) )
    hdus[0].header['BUNIT'] = ("","adimensional quantity to divide to flatfield a frame")
    hdus["IVAR"].header['BUNIT'] = ("","inverse variance, adimensional")
    hdus["MEANSPEC"].header['BUNIT'] = ("electron/Angstrom")
    hdus["WAVELENGTH"].header['BUNIT'] = 'Angstrom'

    t0 = time.time()
    hdus.writeto(outfile+'.tmp', overwrite=True, checksum=True)
    os.rename(outfile+'.tmp', outfile)
    duration = time.time() - t0
    log.info(iotime.format('write', outfile, duration))

    return outfile
Exemple #11
0
def write_frame(outfile, frame, header=None, fibermap=None):
    """Write a frame fits file and returns path to file written.

    Args:
        outfile: full path to output file, or tuple (night, expid, channel)
        frame:  desispec.frame.Frame object with wave, flux, ivar...

    Optional:
        header: astropy.io.fits.Header or dict to override frame.header
        fibermap: table to store as FIBERMAP HDU

    Returns:
        full filepath of output file that was written

    Note:
        to create a Frame object to pass into write_frame,
        frame = Frame(wave, flux, ivar, resolution_data)
    """
    outfile = makepath(outfile, 'frame')

    if header is not None:
        hdr = fitsheader(header)
    else:
        hdr = fitsheader(frame.meta)

    add_dependencies(hdr)

    hdus = fits.HDUList()
    x = fits.PrimaryHDU(frame.flux.astype('f4'), header=hdr)
    x.header['EXTNAME'] = 'FLUX'
    hdus.append(x)

    hdus.append(fits.ImageHDU(frame.ivar.astype('f4'), name='IVAR'))
    hdus.append(fits.CompImageHDU(frame.mask, name='MASK'))
    hdus.append(fits.ImageHDU(frame.wave.astype('f4'), name='WAVELENGTH'))
    hdus.append(
        fits.ImageHDU(frame.resolution_data.astype('f4'), name='RESOLUTION'))

    if fibermap is not None:
        hdus.append(fits.BinTableHDU(np.asarray(fibermap), name='FIBERMAP'))
    elif frame.fibermap is not None:
        hdus.append(
            fits.BinTableHDU(np.asarray(frame.fibermap), name='FIBERMAP'))
    elif frame.spectrograph is not None:
        x.header[
            'FIBERMIN'] = 500 * frame.spectrograph  # Hard-coded (as in desispec.frame)
    else:
        log.error(
            "You are likely writing a frame without sufficient fiber info")

    hdus.writeto(outfile + '.tmp', clobber=True, checksum=True)
    os.rename(outfile + '.tmp', outfile)

    return outfile
Exemple #12
0
def write_frame(outfile, frame, header=None, fibermap=None):
    """Write a frame fits file and returns path to file written.

    Args:
        outfile: full path to output file, or tuple (night, expid, channel)
        frame:  desispec.frame.Frame object with wave, flux, ivar...

    Optional:
        header: astropy.io.fits.Header or dict to override frame.header
        fibermap: table to store as FIBERMAP HDU

    Returns:
        full filepath of output file that was written

    Note:
        to create a Frame object to pass into write_frame,
        frame = Frame(wave, flux, ivar, resolution_data)
    """
    outfile = makepath(outfile, 'frame')

    if header is not None:
        hdr = fitsheader(header)
    else:
        hdr = fitsheader(frame.meta)
        
    add_dependencies(hdr)

    hdus = fits.HDUList()
    x = fits.PrimaryHDU(frame.flux.astype('f4'), header=hdr)
    x.header['EXTNAME'] = 'FLUX'
    hdus.append(x)

    hdus.append( fits.ImageHDU(frame.ivar.astype('f4'), name='IVAR') )
    hdus.append( fits.CompImageHDU(frame.mask, name='MASK') )
    hdus.append( fits.ImageHDU(frame.wave.astype('f4'), name='WAVELENGTH') )
    hdus.append( fits.ImageHDU(frame.resolution_data.astype('f4'), name='RESOLUTION' ) )
    
    if fibermap is not None:
        hdus.append( fits.BinTableHDU(np.asarray(fibermap), name='FIBERMAP' ) )
    elif frame.fibermap is not None:
        hdus.append( fits.BinTableHDU(np.asarray(frame.fibermap), name='FIBERMAP' ) )
    elif frame.spectrograph is not None:
        x.header['FIBERMIN'] = 500*frame.spectrograph  # Hard-coded (as in desispec.frame)
    else:
        log.error("You are likely writing a frame without sufficient fiber info")

    if frame.chi2pix is not None:
        hdus.append( fits.ImageHDU(frame.chi2pix.astype('f4'), name='CHI2PIX' ) )

    hdus.writeto(outfile+'.tmp', clobber=True, checksum=True)
    os.rename(outfile+'.tmp', outfile)

    return outfile
Exemple #13
0
def write_zbest(filename, brickname, targetids, zfind, zspec=False):
    """Writes zfinder output to ``filename``.

    Args:
        filename : full path to output file
        brickname : brick name, e.g. '1234p5678'
        targetids[nspec] : 1D array of target IDs
        zfind : subclass of desispec.zfind.ZFindBase with member variables
            z, zerr, zwarn, type, subtype
        zspec : if True, also write the following member variables from zfind
            wave[nwave], flux[nspec, nwave], ivar[nspec, nwave],
            model[nspec, nwave]

    The first set of variables are each 1D arrays and are written into a
    binary table in an HDU with EXTNAME=ZBEST.  The zspec=True outputs are
    written to 3 additional image HDUs with names WAVELENGTH, FLUX, IVAR,
    and MODEL.
    """
    dtype = [
        ('BRICKNAME', (str, 8)),
        ('TARGETID', np.int64),
        ('Z', zfind.z.dtype),
        ('ZERR', zfind.zerr.dtype),
        ('ZWARN', zfind.zwarn.dtype),
        ('SPECTYPE', zfind.spectype.dtype),
        ('SUBTYPE', zfind.subtype.dtype),
    ]

    data = np.empty(zfind.nspec, dtype=dtype)
    data['BRICKNAME'] = brickname
    data['TARGETID'] = targetids
    data['Z'] = zfind.z
    data['ZERR'] = zfind.zerr
    data['ZWARN'] = zfind.zwarn
    data['SPECTYPE'] = zfind.spectype
    data['SUBTYPE'] = zfind.subtype

    hdus = fits.HDUList()
    phdr = fits.Header()
    add_dependencies(phdr)
    hdus.append(fits.PrimaryHDU(None, header=phdr))
    data = desiutil.io.encode_table(data)
    data.meta['EXTNAME'] = 'ZBEST'
    hdus.append(fits.convenience.table_to_hdu(data))

    if zspec:
        hdus.append(fits.ImageHDU(zfind.wave.astype('f4'), name='WAVELENGTH'))
        hdus.append(fits.ImageHDU(zfind.flux.astype('f4'), name='FLUX'))
        hdus.append(fits.ImageHDU(zfind.ivar.astype('f4'), name='IVAR'))
        hdus.append(fits.ImageHDU(zfind.model.astype('f4'), name='MODEL'))

    hdus.writeto(filename + '.tmp', clobber=True, checksum=True)
    os.rename(filename + '.tmp', filename)
Exemple #14
0
def write_zbest(filename, brickname, targetids, zfind, zspec=False):
    """Writes zfinder output to ``filename``.

    Args:
        filename : full path to output file
        brickname : brick name, e.g. '1234p5678'
        targetids[nspec] : 1D array of target IDs
        zfind : subclass of desispec.zfind.ZFindBase with member variables
            z, zerr, zwarn, type, subtype
        zspec : if True, also write the following member variables from zfind
            wave[nwave], flux[nspec, nwave], ivar[nspec, nwave],
            model[nspec, nwave]

    The first set of variables are each 1D arrays and are written into a
    binary table in an HDU with EXTNAME=ZBEST.  The zspec=True outputs are
    written to 3 additional image HDUs with names WAVELENGTH, FLUX, IVAR,
    and MODEL.
    """
    dtype = [
        ('BRICKNAME', 'S8'),
        ('TARGETID',  np.int64),
        ('Z',         zfind.z.dtype),
        ('ZERR',      zfind.zerr.dtype),
        ('ZWARN',     zfind.zwarn.dtype),
        ('SPECTYPE',  zfind.spectype.dtype),
        ('SUBTYPE',   zfind.subtype.dtype),
    ]

    data = np.empty(zfind.nspec, dtype=dtype)
    data['BRICKNAME'] = brickname
    data['TARGETID']  = targetids
    data['Z']         = zfind.z
    data['ZERR']      = zfind.zerr
    data['ZWARN']     = zfind.zwarn
    data['SPECTYPE']  = zfind.spectype
    data['SUBTYPE']   = zfind.subtype

    hdus = fits.HDUList()
    phdr = fits.Header()
    add_dependencies(phdr)
    hdus.append(fits.PrimaryHDU(None, header=phdr))
    hdus.append(fits.BinTableHDU(data, name='ZBEST', uint=True))

    if zspec:
        hdus.append(fits.ImageHDU(zfind.wave.astype('f4'), name='WAVELENGTH'))
        hdus.append(fits.ImageHDU(zfind.flux.astype('f4'), name='FLUX'))
        hdus.append(fits.ImageHDU(zfind.ivar.astype('f4'), name='IVAR'))
        hdus.append(fits.ImageHDU(zfind.model.astype('f4'), name='MODEL'))

    hdus.writeto(filename+'.tmp', clobber=True, checksum=True)
    os.rename(filename+'.tmp', filename)
Exemple #15
0
def write_sky(outfile, skymodel, header=None):
    """Write sky model.

    Args:
        outfile : filename or (night, expid, camera) tuple
        skymodel : SkyModel object, with the following attributes
            wave : 1D wavelength in vacuum Angstroms
            flux : 2D[nspec, nwave] sky flux
            ivar : 2D inverse variance of sky flux
            mask : 2D mask for sky flux
            stat_ivar : 2D inverse variance of sky flux (statistical only)
        header : optional fits header data (fits.Header, dict, or list)
    """
    from desiutil.depend import add_dependencies
    from .util import fitsheader, makepath

    log = get_logger()
    outfile = makepath(outfile, 'sky')

    #- Convert header to fits.Header if needed
    if header is not None:
        hdr = fitsheader(header)
    else:
        hdr = fitsheader(skymodel.header)

    add_dependencies(hdr)

    hx = fits.HDUList()

    hdr['EXTNAME'] = ('SKY', 'no dimension')
    hx.append( fits.PrimaryHDU(skymodel.flux.astype('f4'), header=hdr) )
    hx.append( fits.ImageHDU(skymodel.ivar.astype('f4'), name='IVAR') )
    # hx.append( fits.CompImageHDU(skymodel.mask, name='MASK') )
    hx.append( fits.ImageHDU(skymodel.mask, name='MASK') )
    hx.append( fits.ImageHDU(skymodel.wave.astype('f4'), name='WAVELENGTH') )
    if skymodel.stat_ivar is not None :
       hx.append( fits.ImageHDU(skymodel.stat_ivar.astype('f4'), name='STATIVAR') )
    if skymodel.throughput_corrections is not None:
        hx.append( fits.ImageHDU(skymodel.throughput_corrections.astype('f4'), name='THRPUTCORR') )

    hx[-1].header['BUNIT'] = 'Angstrom'

    t0 = time.time()
    hx.writeto(outfile+'.tmp', overwrite=True, checksum=True)
    os.rename(outfile+'.tmp', outfile)
    duration = time.time() - t0
    log.info(iotime.format('write', outfile, duration))

    return outfile
Exemple #16
0
def write_fiberflat(outfile, fiberflat, header=None, fibermap=None):
    """Write fiberflat object to outfile

    Args:
        outfile: filepath string or (night, expid, camera) tuple
        fiberflat: FiberFlat object

    Optional:
        header: dict or fits.Header object to use as HDU 0 header
        fibermap: table to store as FIBERMAP HDU

    Returns:
        filepath of file that was written
    """
    outfile = makepath(outfile, 'fiberflat')

    if header is None:
        hdr = fitsheader(fiberflat.header)
    else:
        hdr = fitsheader(header)
    if fiberflat.chi2pdf is not None:
        hdr['chi2pdf'] = float(fiberflat.chi2pdf)

    hdr['EXTNAME'] = 'FIBERFLAT'
    if 'BUNIT' in hdr:
        del hdr['BUNIT']

    add_dependencies(hdr)

    ff = fiberflat  #- shorthand

    hdus = fits.HDUList()
    hdus.append(fits.PrimaryHDU(ff.fiberflat.astype('f4'), header=hdr))
    hdus.append(fits.ImageHDU(ff.ivar.astype('f4'), name='IVAR'))
    hdus.append(fits.ImageHDU(ff.mask, name='MASK'))
    hdus.append(fits.ImageHDU(ff.meanspec.astype('f4'), name='MEANSPEC'))
    hdus.append(fits.ImageHDU(ff.wave.astype('f4'), name='WAVELENGTH'))
    if fibermap is None:
        fibermap = ff.fibermap
    if fibermap is not None:
        fibermap = encode_table(fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append(fits.convenience.table_to_hdu(fibermap))
    hdus[-1].header['BUNIT'] = 'Angstrom'

    hdus.writeto(outfile + '.tmp', overwrite=True, checksum=True)
    os.rename(outfile + '.tmp', outfile)
    return outfile
Exemple #17
0
def write_stdstar_models(norm_modelfile,
                         normalizedFlux,
                         wave,
                         fibers,
                         data,
                         header=None):
    """Writes the normalized flux for the best models.

    Args:
        norm_modelfile : output file path
        normalizedFlux : 2D array of flux[nstdstars, nwave]
        wave : 1D array of wavelengths[nwave] in Angstroms
        fibers : 1D array of fiberids for these spectra
        data : meta data table about which templates best fit
    """
    hdr = fitsheader(header)
    add_dependencies(hdr)

    #- support input Table, np.array, and dict
    data = Table(data)

    hdr['EXTNAME'] = ('FLUX', '[10**-17 erg/(s cm2 Angstrom)]')
    hdr['BUNIT'] = ('10**-17 erg/(s cm2 Angstrom)', 'Flux units')
    hdu1 = fits.PrimaryHDU(normalizedFlux.astype('f4'), header=hdr)

    hdu2 = fits.ImageHDU(wave.astype('f4'))
    hdu2.header['EXTNAME'] = ('WAVELENGTH', '[Angstrom]')
    hdu2.header['BUNIT'] = ('Angstrom', 'Wavelength units')

    hdu3 = fits.ImageHDU(fibers, name='FIBERS')

    # metadata
    from astropy.io.fits import Column
    cols = []
    for k in data.colnames:
        if len(data[k].shape) == 1:
            cols.append(Column(name=k, format='D', array=data[k]))
    tbhdu = fits.BinTableHDU.from_columns(fits.ColDefs(cols), name='METADATA')

    hdulist = fits.HDUList([hdu1, hdu2, hdu3, tbhdu])

    # add coefficients
    if "COEFF" in data.colnames:
        hdulist.append(fits.ImageHDU(data["COEFF"], name="COEFF"))

    tmpfile = norm_modelfile + ".tmp"
    hdulist.writeto(tmpfile, overwrite=True, checksum=True)
    os.rename(tmpfile, norm_modelfile)
Exemple #18
0
def write_image(outfile, image, meta=None):
    """Writes image object to outfile

    Args:
        outfile : output file string
        image : desispec.image.Image object
            (or any object with 2D array attributes image, ivar, mask)

    Optional:
        meta : dict-like object with metadata key/values (e.g. FITS header)
    """

    if meta is not None:
        hdr = fitsheader(meta)
    else:
        hdr = fitsheader(image.meta)

    add_dependencies(hdr)

    outdir = os.path.dirname(os.path.abspath(outfile))
    if not os.path.isdir(outdir):
        os.makedirs(outdir)

    hx = fits.HDUList()
    hdu = fits.ImageHDU(image.pix.astype(np.float32), name='IMAGE', header=hdr)
    if 'CAMERA' not in hdu.header:
        hdu.header.append(
            ('CAMERA', image.camera.lower(), 'Spectrograph Camera'))

    if 'RDNOISE' not in hdu.header and np.isscalar(image.readnoise):
        hdu.header.append(
            ('RDNOISE', image.readnoise, 'Read noise [RMS electrons/pixel]'))

    hx.append(hdu)
    hx.append(fits.ImageHDU(image.ivar.astype(np.float32), name='IVAR'))
    hx.append(fits.CompImageHDU(image.mask.astype(np.int16), name='MASK'))
    if not np.isscalar(image.readnoise):
        hx.append(
            fits.ImageHDU(image.readnoise.astype(np.float32),
                          name='READNOISE'))

    hx.writeto(outfile + '.tmp', overwrite=True, checksum=True)
    os.rename(outfile + '.tmp', outfile)

    return outfile
Exemple #19
0
def write_stdstar_models(norm_modelfile,
                         normalizedFlux,
                         wave,
                         fibers,
                         data,
                         header=None):
    """Writes the normalized flux for the best models.
    
    Args:
        norm_modelfile : output file path
        normalizedFlux : 2D array of flux[nstdstars, nwave]
        wave : 1D array of wavelengths[nwave] in Angstroms
        fibers : 1D array of fiberids for these spectra
        data : meta data table about which templates best fit; should include
            BESTMODEL, TEMPLATEID, CHI2DOF, REDSHIFT
    """
    hdr = fitsheader(header)
    add_dependencies(hdr)

    hdr['EXTNAME'] = ('FLUX', 'erg/s/cm2/A')
    hdr['BUNIT'] = ('erg/s/cm2/A', 'Flux units')
    hdu1 = fits.PrimaryHDU(normalizedFlux.astype('f4'), header=hdr.copy())

    hdr['EXTNAME'] = ('WAVELENGTH', '[Angstroms]')
    hdr['BUNIT'] = ('Angstrom', 'Wavelength units')
    hdu2 = fits.ImageHDU(wave.astype('f4'), header=hdr.copy())

    hdr['EXTNAME'] = ('FIBERS', 'no dimension')
    hdu3 = fits.ImageHDU(fibers, header=hdr.copy())

    hdr['EXTNAME'] = ('METADATA', 'no dimension')
    from astropy.io.fits import Column
    BESTMODEL = Column(name='BESTMODEL', format='K', array=data['BESTMODEL'])
    TEMPLATEID = Column(name='TEMPLATEID',
                        format='K',
                        array=data['TEMPLATEID'])
    CHI2DOF = Column(name='CHI2DOF', format='D', array=data['CHI2DOF'])
    REDSHIFT = Column(name='REDSHIFT', format='D', array=data['REDSHIFT'])
    cols = fits.ColDefs([BESTMODEL, TEMPLATEID, CHI2DOF, REDSHIFT])
    tbhdu = fits.BinTableHDU.from_columns(cols, header=hdr)

    hdulist = fits.HDUList([hdu1, hdu2, hdu3, tbhdu])
    tmpfile = norm_modelfile + ".tmp"
    hdulist.writeto(tmpfile, clobber=True, checksum=True)
    os.rename(tmpfile, norm_modelfile)
Exemple #20
0
def write_flux_calibration(outfile, fluxcalib, header=None):
    """Writes  flux calibration.

    Args:
        outfile : output file name
        fluxcalib : FluxCalib object

    Options:
        header : dict-like object of key/value pairs to include in header
    """
    log = get_logger()
    hx = fits.HDUList()

    hdr = fitsheader(header)
    add_dependencies(hdr)

    hdr['EXTNAME'] = 'FLUXCALIB'
    hdr['BUNIT'] = ('10**+17 cm2 count s / erg', 'i.e. (elec/A) / (1e-17 erg/s/cm2/A)')
    hx.append( fits.PrimaryHDU(fluxcalib.calib.astype('f4'), header=hdr) )
    hx.append( fits.ImageHDU(fluxcalib.ivar.astype('f4'), name='IVAR') )
    # hx.append( fits.CompImageHDU(fluxcalib.mask, name='MASK') )
    hx.append( fits.ImageHDU(fluxcalib.mask, name='MASK') )
    hx.append( fits.ImageHDU(fluxcalib.wave.astype('f4'), name='WAVELENGTH') )
    hx[-1].header['BUNIT'] = 'Angstrom'

    if fluxcalib.fibercorr is not None :
        tbl = encode_table(fluxcalib.fibercorr)  #- unicode -> bytes
        tbl.meta['EXTNAME'] = 'FIBERCORR'
        hx.append( fits.convenience.table_to_hdu(tbl) )
        if fluxcalib.fibercorr_comments is not None : # add comments in header
            hdu=hx['FIBERCORR']
            for i in range(1,999):
                key = 'TTYPE'+str(i)
                if key in hdu.header:
                    value = hdu.header[key]
                    if value in fluxcalib.fibercorr_comments.keys() :
                        hdu.header[key] = (value, fluxcalib.fibercorr_comments[value])

    t0 = time.time()
    hx.writeto(outfile+'.tmp', overwrite=True, checksum=True)
    os.rename(outfile+'.tmp', outfile)
    duration = time.time() - t0
    log.info(iotime.format('write', outfile, duration))

    return outfile
Exemple #21
0
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
Exemple #22
0
def write_image(outfile, image, meta=None):
    """Writes image object to outfile

    Args:
        outfile : output file string
        image : desispec.image.Image object
            (or any object with 2D array attributes image, ivar, mask)

    Optional:
        meta : dict-like object with metadata key/values (e.g. FITS header)
    """

    if meta is not None:
        hdr = fitsheader(meta)
    else:
        hdr = fitsheader(image.meta)

    add_dependencies(hdr)

    outdir = os.path.dirname(os.path.abspath(outfile))
    if not os.path.isdir(outdir):
        os.makedirs(outdir)

    hx = fits.HDUList()
    hdu = fits.ImageHDU(image.pix.astype(np.float32), name='IMAGE', header=hdr)
    if 'CAMERA' not in hdu.header:
        hdu.header.append( ('CAMERA', image.camera.lower(), 'Spectrograph Camera') )

    if 'RDNOISE' not in hdu.header and np.isscalar(image.readnoise):
        hdu.header.append( ('RDNOISE', image.readnoise, 'Read noise [RMS electrons/pixel]'))

    hx.append(hdu)
    hx.append(fits.ImageHDU(image.ivar.astype(np.float32), name='IVAR'))
    hx.append(fits.CompImageHDU(image.mask.astype(np.int16), name='MASK'))
    if not np.isscalar(image.readnoise):
        hx.append(fits.ImageHDU(image.readnoise.astype(np.float32), name='READNOISE'))

    hx.writeto(outfile+'.tmp', overwrite=True, checksum=True)
    os.rename(outfile+'.tmp', outfile)

    return outfile
Exemple #23
0
def write_stdstar_models(norm_modelfile,normalizedFlux,wave,fibers,data,header=None):
    """Writes the normalized flux for the best models.

    Args:
        norm_modelfile : output file path
        normalizedFlux : 2D array of flux[nstdstars, nwave]
        wave : 1D array of wavelengths[nwave] in Angstroms
        fibers : 1D array of fiberids for these spectra
        data : meta data table about which templates best fit
    """
    hdr = fitsheader(header)
    add_dependencies(hdr)

    hdr['EXTNAME'] = ('FLUX', '[10**-17 erg/(s cm2 Angstrom)]')
    hdr['BUNIT'] = ('10**-17 erg/(s cm2 Angstrom)', 'Flux units')
    hdu1=fits.PrimaryHDU(normalizedFlux.astype('f4'), header=hdr)

    hdu2 = fits.ImageHDU(wave.astype('f4'))
    hdu2.header['EXTNAME'] = ('WAVELENGTH', '[Angstrom]')
    hdu2.header['BUNIT'] = ('Angstrom', 'Wavelength units')

    hdu3 = fits.ImageHDU(fibers, name='FIBERS')

    # metadata
    from astropy.io.fits import Column
    cols=[]
    for k in data.keys() :
        if len(data[k].shape)==1 :
            cols.append(Column(name=k,format='D',array=data[k]))
    tbhdu=fits.BinTableHDU.from_columns(fits.ColDefs(cols), name='METADATA')

    hdulist=fits.HDUList([hdu1,hdu2,hdu3,tbhdu])

    # add coefficients
    if "COEFF" in data :
        hdulist.append(fits.ImageHDU(data["COEFF"],name="COEFF"))

    tmpfile = norm_modelfile+".tmp"
    hdulist.writeto(tmpfile, overwrite=True, checksum=True)
    os.rename(tmpfile, norm_modelfile)
Exemple #24
0
def write_stdstar_models(norm_modelfile,normalizedFlux,wave,fibers,data,header=None):
    """Writes the normalized flux for the best models.
    
    Args:
        norm_modelfile : output file path
        normalizedFlux : 2D array of flux[nstdstars, nwave]
        wave : 1D array of wavelengths[nwave] in Angstroms
        fibers : 1D array of fiberids for these spectra
        data : meta data table about which templates best fit; should include
            BESTMODEL, TEMPLATEID, CHI2DOF, REDSHIFT
    """
    hdr = fitsheader(header)
    add_dependencies(hdr)
    
    hdr['EXTNAME'] = ('FLUX', 'erg/s/cm2/A')
    hdr['BUNIT'] = ('erg/s/cm2/A', 'Flux units')
    hdu1=fits.PrimaryHDU(normalizedFlux.astype('f4'), header=hdr.copy())

    hdr['EXTNAME'] = ('WAVELENGTH', '[Angstroms]')
    hdr['BUNIT'] = ('Angstrom', 'Wavelength units')
    hdu2 = fits.ImageHDU(wave.astype('f4'), header=hdr.copy())

    hdr['EXTNAME'] = ('FIBERS', 'no dimension')
    hdu3 = fits.ImageHDU(fibers, header=hdr.copy())

    hdr['EXTNAME'] = ('METADATA', 'no dimension')
    from astropy.io.fits import Column
    BESTMODEL=Column(name='BESTMODEL',format='K',array=data['BESTMODEL'])
    TEMPLATEID=Column(name='TEMPLATEID',format='K',array=data['TEMPLATEID'])
    CHI2DOF=Column(name='CHI2DOF',format='D',array=data['CHI2DOF'])
    REDSHIFT=Column(name='REDSHIFT',format='D',array=data['REDSHIFT'])
    cols=fits.ColDefs([BESTMODEL,TEMPLATEID,CHI2DOF,REDSHIFT])
    tbhdu=fits.BinTableHDU.from_columns(cols,header=hdr)

    hdulist=fits.HDUList([hdu1,hdu2,hdu3,tbhdu])
    tmpfile = norm_modelfile+".tmp"
    hdulist.writeto(tmpfile, clobber=True, checksum=True)
    os.rename(tmpfile, norm_modelfile)
Exemple #25
0
def write_qframe(outfile, qframe, header=None, fibermap=None, units=None):
    """Write a frame fits file and returns path to file written.

    Args:
        outfile: full path to output file, or tuple (night, expid, channel)
        qframe:  desispec.qproc.QFrame object with wave, flux, ivar...

    Optional:
        header: astropy.io.fits.Header or dict to override frame.header
        fibermap: table to store as FIBERMAP HDU

    Returns:
        full filepath of output file that was written

    Note:
        to create a QFrame object to pass into write_qframe,
        qframe = QFrame(wave, flux, ivar)
    """
    log = get_logger()
    outfile = makepath(outfile, 'qframe')

    if header is not None:
        hdr = fitsheader(header)
    else:
        hdr = fitsheader(qframe.meta)

    add_dependencies(hdr)

    hdus = fits.HDUList()
    x = fits.PrimaryHDU(qframe.flux.astype('f4'), header=hdr)
    x.header['EXTNAME'] = 'FLUX'
    if units is not None:
        units = str(units)
        if 'BUNIT' in hdr and hdr['BUNIT'] != units:
            log.warning('BUNIT {bunit} != units {units}; using {units}'.format(
                        bunit=hdr['BUNIT'], units=units))
        x.header['BUNIT'] = units
    hdus.append(x)

    hdus.append( fits.ImageHDU(qframe.ivar.astype('f4'), name='IVAR') )
    if qframe.mask is None :
        qframe.mask=np.zeros(qframe.flux.shape,dtype=np.uint32)
    # hdus.append( fits.CompImageHDU(qframe.mask, name='MASK') )
    hdus.append( fits.ImageHDU(qframe.mask, name='MASK') )

    if qframe.sigma is None :
        qframe.sigma=np.zeros(qframe.flux.shape,dtype=np.float)
    hdus.append( fits.ImageHDU(qframe.sigma.astype('f4'), name='YSIGMA') )

    hdus.append( fits.ImageHDU(qframe.wave.astype('f8'), name='WAVELENGTH') )
    hdus[-1].header['BUNIT'] = 'Angstrom'
    if fibermap is not None:
        fibermap = encode_table(fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append( fits.convenience.table_to_hdu(fibermap) )
    elif qframe.fibermap is not None:
        fibermap = encode_table(qframe.fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append( fits.convenience.table_to_hdu(fibermap) )
    elif qframe.spectrograph is not None:
        x.header['FIBERMIN'] = 500*qframe.spectrograph  # Hard-coded (as in desispec.qproc.qframe)
    else:
        log.error("You are likely writing a qframe without sufficient fiber info")

    hdus.writeto(outfile+'.tmp', clobber=True, checksum=True)
    os.rename(outfile+'.tmp', outfile)

    return outfile
Exemple #26
0
def write_frame(outfile, frame, header=None, fibermap=None, units=None):
    """Write a frame fits file and returns path to file written.

    Args:
        outfile: full path to output file, or tuple (night, expid, channel)
        frame:  desispec.frame.Frame object with wave, flux, ivar...

    Optional:
        header: astropy.io.fits.Header or dict to override frame.header
        fibermap: table to store as FIBERMAP HDU

    Returns:
        full filepath of output file that was written

    Note:
        to create a Frame object to pass into write_frame,
        frame = Frame(wave, flux, ivar, resolution_data)
    """
    log = get_logger()
    outfile = makepath(outfile, 'frame')

    #- Ignore some known and harmless units warnings
    import warnings
    warnings.filterwarnings(
        'ignore', message="'.*nanomaggies.* did not parse as fits unit.*")
    warnings.filterwarnings(
        'ignore', message=".*'10\*\*6 arcsec.* did not parse as fits unit.*")

    if header is not None:
        hdr = fitsheader(header)
    else:
        hdr = fitsheader(frame.meta)

    add_dependencies(hdr)

    # Vette
    diagnosis = frame.vet()
    if diagnosis != 0:
        raise IOError(
            "Frame did not pass simple vetting test. diagnosis={:d}".format(
                diagnosis))

    hdus = fits.HDUList()
    x = fits.PrimaryHDU(frame.flux.astype('f4'), header=hdr)
    x.header['EXTNAME'] = 'FLUX'
    if units is not None:
        units = str(units)
        if 'BUNIT' in hdr and hdr['BUNIT'] != units:
            log.warning('BUNIT {bunit} != units {units}; using {units}'.format(
                bunit=hdr['BUNIT'], units=units))
        x.header['BUNIT'] = units
    hdus.append(x)

    hdus.append(fits.ImageHDU(frame.ivar.astype('f4'), name='IVAR'))
    # hdus.append( fits.CompImageHDU(frame.mask, name='MASK') )
    hdus.append(fits.ImageHDU(frame.mask, name='MASK'))
    hdus.append(fits.ImageHDU(frame.wave.astype('f8'), name='WAVELENGTH'))
    hdus[-1].header['BUNIT'] = 'Angstrom'
    if frame.resolution_data is not None:
        hdus.append(
            fits.ImageHDU(frame.resolution_data.astype('f4'),
                          name='RESOLUTION'))
    elif frame.wsigma is not None:
        log.debug("Using ysigma from qproc")
        qrimg = fits.ImageHDU(frame.wsigma.astype('f4'), name='YSIGMA')
        qrimg.header["NDIAG"] = frame.ndiag
        hdus.append(qrimg)
    if fibermap is not None:
        fibermap = encode_table(fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append(fits.convenience.table_to_hdu(fibermap))
    elif frame.fibermap is not None:
        fibermap = encode_table(frame.fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append(fits.convenience.table_to_hdu(fibermap))
    elif frame.spectrograph is not None:
        x.header[
            'FIBERMIN'] = 500 * frame.spectrograph  # Hard-coded (as in desispec.frame)
    else:
        log.error(
            "You are likely writing a frame without sufficient fiber info")

    if frame.chi2pix is not None:
        hdus.append(fits.ImageHDU(frame.chi2pix.astype('f4'), name='CHI2PIX'))

    if frame.scores is not None:
        scores_tbl = encode_table(frame.scores)  #- unicode -> bytes
        scores_tbl.meta['EXTNAME'] = 'SCORES'
        hdus.append(fits.convenience.table_to_hdu(scores_tbl))
        if frame.scores_comments is not None:  # add comments in header
            hdu = hdus['SCORES']
            for i in range(1, 999):
                key = 'TTYPE' + str(i)
                if key in hdu.header:
                    value = hdu.header[key]
                    if value in frame.scores_comments.keys():
                        hdu.header[key] = (value, frame.scores_comments[value])

    hdus.writeto(outfile + '.tmp', overwrite=True, checksum=True)

    os.rename(outfile + '.tmp', outfile)

    return outfile
Exemple #27
0
    def __init__(self, path, mode='readonly', header=None):
        from .util import fitsheader
        from .fibermap import fibermap_columns, fibermap_comments
        if mode not in ('readonly', 'update'):
            raise RuntimeError('Invalid mode %r' % mode)
        self.path = path
        self.mode = mode
        # Create a new file if necessary.
        if self.mode == 'update' and not os.path.exists(self.path):
            # BRICKNAM must be in header if creating the file for the first time
            if header is None or 'BRICKNAM' not in header:
                raise ValueError(
                    'header must have BRICKNAM when creating new brick file')

            self.brickname = header['BRICKNAM']
            if 'CHANNEL' in header:
                self.channel = header['CHANNEL']
            else:
                self.channel = 'brz'  #- could be any spectrograph channel

            # Create the parent directory, if necessary.
            head, tail = os.path.split(os.path.abspath(self.path))
            if not os.path.exists(head):
                os.makedirs(head)
            # Create empty HDUs. It would be good to refactor io.frame to avoid any duplication here.
            hdr = fitsheader(header)
            add_dependencies(hdr)
            hdr['EXTNAME'] = ('FLUX', '1e-17 erg/(s cm2 Angstrom)')
            hdr['BUNIT'] = '1e-17 erg/(s cm2 Angstrom)'
            hdu0 = astropy.io.fits.PrimaryHDU(header=hdr)
            hdu1 = astropy.io.fits.ImageHDU(name='IVAR')
            hdu2 = astropy.io.fits.ImageHDU(name='WAVELENGTH')
            hdu2.header['BUNIT'] = 'Angstrom'
            hdu3 = astropy.io.fits.ImageHDU(name='RESOLUTION')
            # Create an HDU4 using the columns from fibermap with a few extras added.
            columns = fibermap_columns[:]
            columns.extend([
                ('NIGHT', 'i4'),
                ('EXPID', 'i4'),
                ('INDEX', 'i4'),
            ])
            data = np.empty(shape=(0, ), dtype=columns)
            data = encode_table(data)  #- unicode -> bytes
            data.meta['EXTNAME'] = 'FIBERMAP'
            for key, value in header.items():
                data.meta[key] = value
            hdu4 = astropy.io.fits.convenience.table_to_hdu(data)

            # Add comments for fibermap columns.
            num_fibermap_columns = len(fibermap_comments)
            for i in range(1, 1 + num_fibermap_columns):
                key = 'TTYPE%d' % i
                name = hdu4.header[key]
                comment = fibermap_comments[name]
                hdu4.header[key] = (name, comment)
            # Add comments for our additional columns.
            hdu4.header['TTYPE%d' % (1 + num_fibermap_columns)] = (
                'NIGHT', 'Night of exposure YYYYMMDD')
            hdu4.header['TTYPE%d' %
                        (2 + num_fibermap_columns)] = ('EXPID', 'Exposure ID')
            hdu4.header['TTYPE%d' % (3 + num_fibermap_columns)] = (
                'INDEX', 'Index of this object in other HDUs')
            self.hdu_list = astropy.io.fits.HDUList(
                [hdu0, hdu1, hdu2, hdu3, hdu4])
        else:
            self.hdu_list = astropy.io.fits.open(path, mode=self.mode)
            try:
                self.brickname = self.hdu_list[0].header['BRICKNAM']
                self.channel = self.hdu_list[0].header['CHANNEL']
            except KeyError:
                self.channel, self.brickname = _parse_brick_filename(path)
Exemple #28
0
def main_mpi(args, comm=None, timing=None):
    freeze_iers()
    nproc = 1
    rank = 0
    if comm is not None:
        nproc = comm.size
        rank = comm.rank

    mark_start = time.time()

    log = get_logger()

    psf_file = args.psf
    input_file = args.input

    # these parameters are interpreted as the *global* spec range,
    # to be divided among processes.
    specmin = args.specmin
    nspec = args.nspec

    #- Load input files and broadcast

    # FIXME: after we have fixed the serialization
    # of the PSF, read and broadcast here, to reduce
    # disk contention.

    img = None
    if rank == 0:
        img = io.read_image(input_file)
    if comm is not None:
        img = comm.bcast(img, root=0)

    psf = load_psf(psf_file)

    mark_read_input = time.time()

    # get spectral range
    if nspec is None:
        nspec = psf.nspec

    if args.fibermap is not None:
        fibermap = io.read_fibermap(args.fibermap)
    else:
        try:
            fibermap = io.read_fibermap(args.input)
        except (AttributeError, IOError, KeyError):
            fibermap = None

    if fibermap is not None:
        fibermap = fibermap[specmin:specmin + nspec]
        if nspec > len(fibermap):
            log.warning(
                "nspec {} > len(fibermap) {}; reducing nspec to {}".format(
                    nspec, len(fibermap), len(fibermap)))
            nspec = len(fibermap)
        fibers = fibermap['FIBER']
    else:
        fibers = np.arange(specmin, specmin + nspec)

    specmax = specmin + nspec

    #- Get wavelength grid from options
    if args.wavelength is not None:
        raw_wstart, raw_wstop, raw_dw = [
            float(tmp) for tmp in args.wavelength.split(',')
        ]
    else:
        raw_wstart = np.ceil(psf.wmin_all)
        raw_wstop = np.floor(psf.wmax_all)
        raw_dw = 0.7

    raw_wave = np.arange(raw_wstart, raw_wstop + raw_dw / 2.0, raw_dw)
    nwave = len(raw_wave)
    bundlesize = args.bundlesize

    if args.barycentric_correction:
        if ('RA' in img.meta) or ('TARGTRA' in img.meta):
            barycentric_correction_factor = \
                    barycentric_correction_multiplicative_factor(img.meta)
        #- Early commissioning has RA/TARGTRA in fibermap but not HDU 0
        elif fibermap is not None and \
                (('RA' in fibermap.meta) or ('TARGTRA' in fibermap.meta)):
            barycentric_correction_factor = \
                    barycentric_correction_multiplicative_factor(fibermap.meta)
        else:
            msg = 'Barycentric corr requires (TARGT)RA in HDU 0 or fibermap'
            log.critical(msg)
            raise KeyError(msg)
    else:
        barycentric_correction_factor = 1.

    # Explictly define the correct wavelength values to avoid confusion of reference frame
    # If correction applied, otherwise divide by 1 and use the same raw values
    wstart = raw_wstart / barycentric_correction_factor
    wstop = raw_wstop / barycentric_correction_factor
    dw = raw_dw / barycentric_correction_factor
    wave = raw_wave / barycentric_correction_factor

    #- Confirm that this PSF covers these wavelengths for these spectra
    psf_wavemin = np.max(psf.wavelength(list(range(specmin, specmax)), y=-0.5))
    psf_wavemax = np.min(
        psf.wavelength(list(range(specmin, specmax)), y=psf.npix_y - 0.5))
    if psf_wavemin - 5 > wstart:
        raise ValueError(
            'Start wavelength {:.2f} < min wavelength {:.2f} for these fibers'.
            format(wstart, psf_wavemin))
    if psf_wavemax + 5 < wstop:
        raise ValueError(
            'Stop wavelength {:.2f} > max wavelength {:.2f} for these fibers'.
            format(wstop, psf_wavemax))

    if rank == 0:
        #- Print parameters
        log.info("extract:  input = {}".format(input_file))
        log.info("extract:  psf = {}".format(psf_file))
        log.info("extract:  specmin = {}".format(specmin))
        log.info("extract:  nspec = {}".format(nspec))
        log.info("extract:  wavelength = {},{},{}".format(wstart, wstop, dw))
        log.info("extract:  nwavestep = {}".format(args.nwavestep))
        log.info("extract:  regularize = {}".format(args.regularize))

    if barycentric_correction_factor != 1.:
        img.meta['HELIOCOR'] = barycentric_correction_factor

    #- Augment input image header for output
    img.meta['NSPEC'] = (nspec, 'Number of spectra')
    img.meta['WAVEMIN'] = (raw_wstart, 'First wavelength [Angstroms]')
    img.meta['WAVEMAX'] = (raw_wstop, 'Last wavelength [Angstroms]')
    img.meta['WAVESTEP'] = (raw_dw, 'Wavelength step size [Angstroms]')
    img.meta['SPECTER'] = (specter.__version__,
                           'https://github.com/desihub/specter')
    img.meta['IN_PSF'] = (io.shorten_filename(psf_file), 'Input spectral PSF')
    img.meta['IN_IMG'] = io.shorten_filename(input_file)
    depend.add_dependencies(img.meta)

    #- Check if input PSF was itself a traceshifted version of another PSF
    orig_psf = None
    if rank == 0:
        try:
            psfhdr = fits.getheader(psf_file, 'PSF')
            orig_psf = psfhdr['IN_PSF']
        except KeyError:
            #- could happen due to PSF format not having "PSF" extension,
            #- or due to PSF header not having 'IN_PSF' keyword.  Either is OK
            pass

    if comm is not None:
        orig_psf = comm.bcast(orig_psf, root=0)

    if orig_psf is not None:
        img.meta['ORIG_PSF'] = orig_psf

    #- If not using MPI, use a single call to each of these and then end this function call
    #  Otherwise, continue on to splitting things up for the different ranks
    if comm is None:
        _extract_and_save(img, psf, specmin, nspec, specmin, wave, raw_wave,
                          fibers, fibermap, args.output, args.model,
                          bundlesize, args, log)

        #- This is it if we aren't running MPI, so return
        return
    #else:
    #    # Continue to the MPI section, which could go under this else statment
    #    # But to save on indentation we'll just pass on to the rest of the function
    #    # since the alternative has already returned
    #    pass

    # Now we divide our spectra into bundles
    checkbundles = set()
    checkbundles.update(
        np.floor_divide(np.arange(specmin, specmax),
                        bundlesize * np.ones(nspec)).astype(int))
    bundles = sorted(checkbundles)
    nbundle = len(bundles)

    bspecmin = {}
    bnspec = {}

    for b in bundles:
        if specmin > b * bundlesize:
            bspecmin[b] = specmin
        else:
            bspecmin[b] = b * bundlesize
        if (b + 1) * bundlesize > specmax:
            bnspec[b] = specmax - bspecmin[b]
        else:
            bnspec[b] = bundlesize

    # Now we assign bundles to processes
    mynbundle = int(nbundle // nproc)
    myfirstbundle = 0
    leftover = nbundle % nproc
    if rank < leftover:
        mynbundle += 1
        myfirstbundle = rank * mynbundle
    else:
        myfirstbundle = ((mynbundle + 1) * leftover) + (mynbundle *
                                                        (rank - leftover))

    # get the root output file
    outpat = re.compile(r'(.*)\.fits')
    outmat = outpat.match(args.output)
    if outmat is None:
        raise RuntimeError(
            "extraction output file should have .fits extension")
    outroot = outmat.group(1)

    outdir = os.path.normpath(os.path.dirname(outroot))

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

    if comm is not None:
        comm.barrier()

    mark_preparation = time.time()
    time_total_extraction = 0.0
    time_total_write_output = 0.0
    failcount = 0

    for b in range(myfirstbundle, myfirstbundle + mynbundle):
        mark_iteration_start = time.time()
        outbundle = "{}_{:02d}.fits".format(outroot, b)
        outmodel = "{}_model_{:02d}.fits".format(outroot, b)

        log.info('extract:  Rank {} extracting {} spectra {}:{} at {}'.format(
            rank,
            os.path.basename(input_file),
            bspecmin[b],
            bspecmin[b] + bnspec[b],
            time.asctime(),
        ))
        sys.stdout.flush()

        #- The actual extraction
        try:
            mark_extraction = _extract_and_save(img, psf, bspecmin[b],
                                                bnspec[b], specmin, wave,
                                                raw_wave, fibers, fibermap,
                                                outbundle, outmodel,
                                                bundlesize, args, log)

            mark_write_output = time.time()

            time_total_extraction += mark_extraction - mark_iteration_start
            time_total_write_output += mark_write_output - mark_extraction
        except:
            # Log the error and increment the number of failures
            log.error(
                "extract:  FAILED bundle {}, spectrum range {}:{}".format(
                    b, bspecmin[b], bspecmin[b] + bnspec[b]))
            exc_type, exc_value, exc_traceback = sys.exc_info()
            lines = traceback.format_exception(exc_type, exc_value,
                                               exc_traceback)
            log.error(''.join(lines))
            failcount += 1
            sys.stdout.flush()

    if comm is not None:
        failcount = comm.allreduce(failcount)

    if failcount > 0:
        # all processes throw
        raise RuntimeError("some extraction bundles failed")

    time_merge = None
    if rank == 0:
        mark_merge_start = time.time()
        mergeopts = ['--output', args.output, '--force', '--delete']
        mergeopts.extend(
            ["{}_{:02d}.fits".format(outroot, b) for b in bundles])
        mergeargs = mergebundles.parse(mergeopts)
        mergebundles.main(mergeargs)

        if args.model is not None:
            model = None
            for b in bundles:
                outmodel = "{}_model_{:02d}.fits".format(outroot, b)
                if model is None:
                    model = fits.getdata(outmodel)
                else:
                    #- TODO: test and warn if models overlap for pixels with
                    #- non-zero values
                    model += fits.getdata(outmodel)

                os.remove(outmodel)

            fits.writeto(args.model, model)
        mark_merge_end = time.time()
        time_merge = mark_merge_end - mark_merge_start

    # Resolve difference timer data

    if type(timing) is dict:
        timing["read_input"] = mark_read_input - mark_start
        timing["preparation"] = mark_preparation - mark_read_input
        timing["total_extraction"] = time_total_extraction
        timing["total_write_output"] = time_total_write_output
        timing["merge"] = time_merge
Exemple #29
0
def write_qframe(outfile, qframe, header=None, fibermap=None, units=None):
    """Write a frame fits file and returns path to file written.

    Args:
        outfile: full path to output file, or tuple (night, expid, channel)
        qframe:  desispec.qproc.QFrame object with wave, flux, ivar...

    Optional:
        header: astropy.io.fits.Header or dict to override frame.header
        fibermap: table to store as FIBERMAP HDU

    Returns:
        full filepath of output file that was written

    Note:
        to create a QFrame object to pass into write_qframe,
        qframe = QFrame(wave, flux, ivar)
    """
    log = get_logger()
    outfile = makepath(outfile, 'qframe')

    if header is not None:
        hdr = fitsheader(header)
    else:
        hdr = fitsheader(qframe.meta)

    add_dependencies(hdr)

    hdus = fits.HDUList()
    x = fits.PrimaryHDU(qframe.flux.astype('f4'), header=hdr)
    x.header['EXTNAME'] = 'FLUX'
    if units is not None:
        units = str(units)
        if 'BUNIT' in hdr and hdr['BUNIT'] != units:
            log.warning('BUNIT {bunit} != units {units}; using {units}'.format(
                bunit=hdr['BUNIT'], units=units))
        x.header['BUNIT'] = units
    hdus.append(x)

    hdus.append(fits.ImageHDU(qframe.ivar.astype('f4'), name='IVAR'))
    if qframe.mask is None:
        qframe.mask = np.zeros(qframe.flux.shape, dtype=np.uint32)
    # hdus.append( fits.CompImageHDU(qframe.mask, name='MASK') )
    hdus.append(fits.ImageHDU(qframe.mask, name='MASK'))

    if qframe.sigma is None:
        qframe.sigma = np.zeros(qframe.flux.shape, dtype=np.float)
    hdus.append(fits.ImageHDU(qframe.sigma.astype('f4'), name='YSIGMA'))

    hdus.append(fits.ImageHDU(qframe.wave.astype('f8'), name='WAVELENGTH'))
    hdus[-1].header['BUNIT'] = 'Angstrom'
    if fibermap is not None:
        fibermap = encode_table(fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append(fits.convenience.table_to_hdu(fibermap))
    elif qframe.fibermap is not None:
        fibermap = encode_table(qframe.fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append(fits.convenience.table_to_hdu(fibermap))
    elif qframe.spectrograph is not None:
        x.header[
            'FIBERMIN'] = 500 * qframe.spectrograph  # Hard-coded (as in desispec.qproc.qframe)
    else:
        log.error(
            "You are likely writing a qframe without sufficient fiber info")
        raise ValueError('no fibermap')

    hdus.writeto(outfile + '.tmp', overwrite=True, checksum=True)
    os.rename(outfile + '.tmp', outfile)

    return outfile
Exemple #30
0
def write_spectra(outfile, spec, units=None):
    """
    Write Spectra object to FITS file.

    This places the metadata into the header of the (empty) primary HDU.
    The first extension contains the fibermap, and then HDUs are created for
    the different data arrays for each band.

    Floating point data is converted to 32 bits before writing.

    Args:
        outfile (str): path to write
        spec (Spectra): the object containing the data
        units (str): optional string to use for the BUNIT key of the flux
            HDUs for each band.

    Returns:
        The absolute path to the file that was written.

    """

    outfile = os.path.abspath(outfile)

    # Create the parent directory, if necessary.
    dir, base = os.path.split(outfile)
    if not os.path.exists(dir):
        os.makedirs(dir)

    # Create HDUs from the data
    all_hdus = fits.HDUList()

    # metadata goes in empty primary HDU
    hdr = fitsheader(spec.meta)
    add_dependencies(hdr)

    all_hdus.append(fits.PrimaryHDU(header=hdr))

    # Next is the fibermap
    fmap = spec.fibermap.copy()
    fmap.meta["EXTNAME"] = "FIBERMAP"
    hdu = fits.convenience.table_to_hdu(fmap)

    # Add comments for fibermap columns.
    for i, colname in enumerate(fmap.dtype.names):
        if colname in fibermap_comments:
            key = "TTYPE{}".format(i+1)
            name = hdu.header[key]
            assert name == colname
            comment = fibermap_comments[name]
            hdu.header[key] = (name, comment)
        else:
            print('Unknown comment for {}'.format(colname))

    all_hdus.append(hdu)

    # Now append the data for all bands

    for band in spec.bands:
        hdu = fits.ImageHDU(name="{}_WAVELENGTH".format(band.upper()))
        hdu.header["BUNIT"] = "Angstrom"
        hdu.data = spec.wave[band].astype("f8")
        all_hdus.append(hdu)

        hdu = fits.ImageHDU(name="{}_FLUX".format(band.upper()))
        if units is None:
            hdu.header["BUNIT"] = "10**-17 erg/(s cm2 Angstrom)"
        else:
            hdu.header["BUNIT"] = units
        hdu.data = spec.flux[band].astype("f4")
        all_hdus.append(hdu)

        hdu = fits.ImageHDU(name="{}_IVAR".format(band.upper()))
        if units is None:
            hdu.header["BUNIT"] = '10**+34 (s2 cm4 Angstrom2) / erg2'
        else:
            hdu.header["BUNIT"] = ((u.Unit(units, format='fits'))**-2).to_string('fits')
        hdu.data = spec.ivar[band].astype("f4")
        all_hdus.append(hdu)

        if spec.mask is not None:
            # hdu = fits.CompImageHDU(name="{}_MASK".format(band.upper()))
            hdu = fits.ImageHDU(name="{}_MASK".format(band.upper()))
            hdu.data = spec.mask[band].astype(np.uint32)
            all_hdus.append(hdu)

        if spec.resolution_data is not None:
            hdu = fits.ImageHDU(name="{}_RESOLUTION".format(band.upper()))
            hdu.data = spec.resolution_data[band].astype("f4")
            all_hdus.append(hdu)

        if spec.extra is not None:
            for ex in spec.extra[band].items():
                hdu = fits.ImageHDU(name="{}_{}".format(band.upper(), ex[0]))
                hdu.data = ex[1].astype("f4")
                all_hdus.append(hdu)

    all_hdus.writeto("{}.tmp".format(outfile), overwrite=True, checksum=True)
    os.rename("{}.tmp".format(outfile), outfile)

    return outfile
Exemple #31
0
def write_frame(outfile, frame, header=None, fibermap=None, units=None):
    """Write a frame fits file and returns path to file written.

    Args:
        outfile: full path to output file, or tuple (night, expid, channel)
        frame:  desispec.frame.Frame object with wave, flux, ivar...

    Optional:
        header: astropy.io.fits.Header or dict to override frame.header
        fibermap: table to store as FIBERMAP HDU

    Returns:
        full filepath of output file that was written

    Note:
        to create a Frame object to pass into write_frame,
        frame = Frame(wave, flux, ivar, resolution_data)
    """
    log = get_logger()
    outfile = makepath(outfile, 'frame')

    if header is not None:
        hdr = fitsheader(header)
    else:
        hdr = fitsheader(frame.meta)

    add_dependencies(hdr)

    # Vette
    diagnosis = frame.vet()
    if diagnosis != 0:
        raise IOError(
            "Frame did not pass simple vetting test. diagnosis={:d}".format(
                diagnosis))

    hdus = fits.HDUList()
    x = fits.PrimaryHDU(frame.flux.astype('f4'), header=hdr)
    x.header['EXTNAME'] = 'FLUX'
    if units is not None:
        units = str(units)
        if 'BUNIT' in hdr and hdr['BUNIT'] != units:
            log.warn('BUNIT {bunit} != units {units}; using {units}'.format(
                bunit=hdr['BUNIT'], units=units))
        x.header['BUNIT'] = units
    hdus.append(x)

    hdus.append(fits.ImageHDU(frame.ivar.astype('f4'), name='IVAR'))
    hdus.append(fits.CompImageHDU(frame.mask, name='MASK'))
    hdus.append(fits.ImageHDU(frame.wave.astype('f4'), name='WAVELENGTH'))
    hdus[-1].header['BUNIT'] = 'Angstrom'
    hdus.append(
        fits.ImageHDU(frame.resolution_data.astype('f4'), name='RESOLUTION'))

    if fibermap is not None:
        fibermap = encode_table(fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append(fits.convenience.table_to_hdu(fibermap))
    elif frame.fibermap is not None:
        fibermap = encode_table(frame.fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append(fits.convenience.table_to_hdu(fibermap))
    elif frame.spectrograph is not None:
        x.header[
            'FIBERMIN'] = 500 * frame.spectrograph  # Hard-coded (as in desispec.frame)
    else:
        log.error(
            "You are likely writing a frame without sufficient fiber info")

    if frame.chi2pix is not None:
        hdus.append(fits.ImageHDU(frame.chi2pix.astype('f4'), name='CHI2PIX'))

    hdus.writeto(outfile + '.tmp', clobber=True, checksum=True)
    os.rename(outfile + '.tmp', outfile)

    return outfile
Exemple #32
0
def write_raw(filename, rawdata, header, camera=None, primary_header=None):
    '''
    Write raw pixel data to a DESI raw data file

    Args:
        filename : file name to write data; if this exists, append a new HDU
        rawdata : 2D ndarray of raw pixel data including overscans
        header : dict-like object or fits.Header with keywords
            CCDSECx, BIASSECx, DATASECx where x=1,2,3, or 4

    Options:
        camera : b0, r1 .. z9 - override value in header
        primary_header : header to write in HDU0 if filename doesn't yet exist

    The primary utility of this function over raw fits calls is to ensure
    that all necessary keywords are present before writing the file.
    CCDSECx, BIASSECx, DATASECx where x=1,2,3, or 4
    DATE-OBS, GAINx and RDNOISEx will generate a non-fatal warning if missing
    '''
    log = get_logger()

    header = desispec.io.util.fitsheader(header)
    primary_header = desispec.io.util.fitsheader(primary_header)

    if rawdata.dtype not in (np.int16, np.int32, np.int64):
        message = 'dtype {} not supported for raw data'.format(rawdata.dtype)
        log.fatal(message)
        raise ValueError(message)

    fail_message = ''
    for required_key in ['DOSVER', 'FEEVER', 'DETECTOR']:
        if required_key not in primary_header:
            if required_key in header:
                primary_header[required_key] = header[required_key]
            else:
                fail_message = fail_message + \
                    'Keyword {} must be in header or primary_header\n'.format(required_key)
    if fail_message != '':
        raise ValueError(fail_message)

    #- Check required keywords before writing anything
    missing_keywords = list()
    if camera is None and 'CAMERA' not in header:
        log.error("Must provide camera keyword or header['CAMERA']")
        missing_keywords.append('CAMERA')

    for amp in ['1', '2', '3', '4']:
        for prefix in ['CCDSEC', 'BIASSEC', 'DATASEC']:
            keyword = prefix + amp
            if keyword not in header:
                log.error('Missing keyword ' + keyword)
                missing_keywords.append(keyword)

    #- Missing DATE-OBS is warning but not error
    if 'DATE-OBS' not in primary_header:
        if 'DATE-OBS' in header:
            primary_header['DATE-OBS'] = header['DATE-OBS']
        else:
            log.warning('missing keyword DATE-OBS')

    #- Missing GAINx is warning but not error
    for amp in ['1', '2', '3', '4']:
        keyword = 'GAIN' + amp
        if keyword not in header:
            log.warning('Gain keyword {} missing; using 1.0'.format(keyword))
            header[keyword] = 1.0

    #- Missing RDNOISEx is warning but not error
    for amp in ['1', '2', '3', '4']:
        keyword = 'RDNOISE' + amp
        if keyword not in header:
            log.warning('Readnoise keyword {} missing'.format(keyword))

    #- Stop if any keywords are missing
    if len(missing_keywords) > 0:
        raise KeyError('missing required keywords {}'.format(missing_keywords))

    #- Set EXTNAME=camera
    if camera is not None:
        header['CAMERA'] = camera.lower()
        extname = camera.upper()
    else:
        if header['CAMERA'] != header['CAMERA'].lower():
            log.warning('Converting CAMERA {} to lowercase'.format(
                header['CAMERA']))
            header['CAMERA'] = header['CAMERA'].lower()
        extname = header['CAMERA'].upper()

    header['INHERIT'] = True

    #- fits.CompImageHDU doesn't know how to fill in default keywords, so
    #- temporarily generate an uncompressed HDU to get those keywords
    header = fits.ImageHDU(rawdata, header=header, name=extname).header

    #- Bizarrely, compression of 64-bit integers isn't supported.
    #- downcast to 32-bit if that won't lose precision.
    #- Real raw data should be 32-bit or 16-bit anyway
    if rawdata.dtype == np.int64:
        if np.max(np.abs(rawdata)) < 2**31:
            rawdata = rawdata.astype(np.int32)

    if rawdata.dtype in (np.int16, np.int32):
        dataHDU = fits.CompImageHDU(rawdata, header=header, name=extname)
    elif rawdata.dtype == np.int64:
        log.warning(
            'Image compression not supported for 64-bit; writing uncompressed')
        dataHDU = fits.ImageHDU(rawdata, header=header, name=extname)
    else:
        log.error("How did we get this far with rawdata dtype {}?".format(
            rawdata.dtype))
        dataHDU = fits.ImageHDU(rawdata, header=header, name=extname)

    #- Actually write or update the file
    if os.path.exists(filename):
        hdus = fits.open(filename, mode='append', memmap=False)
        if extname in hdus:
            hdus.close()
            raise ValueError('Camera {} already in {}'.format(
                camera, filename))
        else:
            hdus.append(dataHDU)
            hdus.flush()
            hdus.close()
    else:
        hdus = fits.HDUList()
        add_dependencies(primary_header)
        hdus.append(fits.PrimaryHDU(None, header=primary_header))
        hdus.append(dataHDU)
        hdus.writeto(filename)
Exemple #33
0
def write_spectra(outfile, spec, units=None):
    """
    Write Spectra object to FITS file.

    This places the metadata into the header of the (empty) primary HDU.
    The first extension contains the fibermap, and then HDUs are created for
    the different data arrays for each band.

    Floating point data is converted to 32 bits before writing.

    Args:
        outfile (str): path to write
        spec (Spectra): the object containing the data
        units (str): optional string to use for the BUNIT key of the flux
            HDUs for each band.

    Returns:
        The absolute path to the file that was written.

    """

    outfile = os.path.abspath(outfile)

    # Create the parent directory, if necessary.
    dir, base = os.path.split(outfile)
    if not os.path.exists(dir):
        os.makedirs(dir)

    # Create HDUs from the data
    all_hdus = fits.HDUList()

    # metadata goes in empty primary HDU
    hdr = fitsheader(spec.meta)
    add_dependencies(hdr)

    all_hdus.append(fits.PrimaryHDU(header=hdr))

    # Next is the fibermap
    fmap = spec.fibermap.copy()
    fmap.meta["EXTNAME"] = "FIBERMAP"
    hdu = fits.convenience.table_to_hdu(fmap)

    # Add comments for fibermap columns.
    comments = spectra_comments()
    num_columns = len(comments)
    for i in range(1, 1 + num_columns):
        key = "TTYPE{}".format(i)
        name = hdu.header[key]
        comment = comments[name]
        hdu.header[key] = (name, comment)

    all_hdus.append(hdu)

    # Now append the data for all bands

    for band in spec.bands:
        hdu = fits.ImageHDU(name="{}_WAVELENGTH".format(band.upper()))
        hdu.header["BUNIT"] = "Angstrom"
        hdu.data = spec.wave[band].astype("f4")
        all_hdus.append(hdu)

        hdu = fits.ImageHDU(name="{}_FLUX".format(band.upper()))
        if units is None:
            hdu.header["BUNIT"] = "1e-17 erg/(s cm2 Angstrom)"
        else:
            hdu.header["BUNIT"] = units
        hdu.data = spec.flux[band].astype("f4")
        all_hdus.append(hdu)

        hdu = fits.ImageHDU(name="{}_IVAR".format(band.upper()))
        hdu.data = spec.ivar[band].astype("f4")
        all_hdus.append(hdu)

        if spec.mask is not None:
            hdu = fits.CompImageHDU(name="{}_MASK".format(band.upper()))
            hdu.data = spec.mask[band].astype(np.uint32)
            all_hdus.append(hdu)

        if spec.resolution_data is not None:
            hdu = fits.ImageHDU(name="{}_RESOLUTION".format(band.upper()))
            hdu.data = spec.resolution_data[band].astype("f4")
            all_hdus.append(hdu)

        if spec.extra is not None:
            for ex in spec.extra[band].items():
                hdu = fits.ImageHDU(name="{}_{}".format(band.upper(), ex[0]))
                hdu.data = ex[1].astype("f4")
                all_hdus.append(hdu)

    try:
        all_hdus.writeto("{}.tmp".format(outfile),
                         overwrite=True,
                         checksum=True)
    except TypeError:
        all_hdus.writeto("{}.tmp".format(outfile), clobber=True, checksum=True)
    os.rename("{}.tmp".format(outfile), outfile)

    return outfile
Exemple #34
0
def write_assignment_fits_tile(asgn, fulltarget, overwrite, params):
    """Write a single tile assignment to a FITS file.

    Args:
        outroot (str):  full path of the output root file name.
        asgn (Assignment):  the assignment class instance.
        fulltarget (bool):  if True, dump the target information for all
            available targets, not just the ones that are assigned.
        overwrite (bool): overwrite output files or not
        params (tuple):  tuple containing the tile ID, RA, DEC, rotation,
            output path, and GFA targets

    Returns:
        None

    """
    tm = Timer()
    tm.start()
    tile_id, tile_ra, tile_dec, tile_obstheta, tile_obstime, tile_obsha, \
        tile_file, gfa_targets = params
    log = Logger.get()

    # Hardware properties
    hw = asgn.hardware()

    # Targets available for all tile / locs
    tgsavail = asgn.targets_avail()

    # Target properties
    tgs = asgn.targets()

    # Data for this tile
    tdata = asgn.tile_location_target(tile_id)
    avail = tgsavail.tile_data(tile_id)

    # The recarray dtypes
    assign_dtype = np.dtype([(x, y)
                             for x, y in results_assign_columns.items()])
    targets_dtype = np.dtype([(x, y)
                              for x, y in results_targets_columns.items()])
    avail_dtype = np.dtype([(x, y) for x, y in results_avail_columns.items()])
    # tm.stop()
    # tm.report("  data pointers for tile {}".format(tile_id))
    # tm.clear()
    # tm.start()

    locs = np.array(hw.locations)
    nloc = len(locs)

    # Compute the total list of targets
    navail = np.sum([len(avail[x]) for x in avail.keys()])
    tgids_avail = np.unique(
        np.concatenate(
            [np.array(avail[x], dtype=np.int64) for x in avail.keys()]))

    # tm.stop()
    # tm.report("  available targets tile {}".format(tile_id))

    if len(tgids_avail) > 0:
        # We have some available targets.
        # tm.clear()
        # tm.start()

        if os.path.isfile(tile_file):
            if overwrite:
                log.warning("Overwriting {}".format(tile_file))
                os.remove(tile_file)
            else:
                raise RuntimeError(
                    "output file {} already exists".format(tile_file))
        # This tile has some available targets
        log.info("Writing tile {}".format(tile_id))
        tmp_file = tile_file + ".tmp"
        fd = fitsio.FITS(tmp_file, "rw")

        # tm.stop()
        # tm.report("  opening file for tile {}".format(tile_id))
        # tm.clear()
        # tm.start()

        # Unpack all our target properties from C++ objects into numpy arrays

        tgids = None
        if fulltarget:
            # We are dumping all targets
            tgids = tgids_avail
        else:
            # We are dumping only assigned targets
            tgids = np.array(sorted([y for x, y in tdata.items()]),
                             dtype=np.int64)

        ntarget = len(tgids)

        tg_ra = np.empty(ntarget, dtype=np.float64)
        tg_dec = np.empty(ntarget, dtype=np.float64)
        tg_bits = np.zeros(ntarget, dtype=np.int64)
        tg_x = np.empty(ntarget, dtype=np.float64)
        tg_y = np.empty(ntarget, dtype=np.float64)
        tg_type = np.empty(ntarget, dtype=np.uint8)
        tg_priority = np.empty(ntarget, dtype=np.int32)
        tg_subpriority = np.empty(ntarget, dtype=np.float64)
        tg_obscond = np.empty(ntarget, dtype=np.int32)
        tg_indx = dict()
        for indx, tg in enumerate(tgids):
            tg_indx[tg] = indx
            props = tgs.get(tg)
            tg_ra[indx] = props.ra
            tg_dec[indx] = props.dec
            tg_bits[indx] = props.bits
            tg_type[indx] = props.type
            tg_priority[indx] = props.priority
            tg_subpriority[indx] = props.subpriority
            tg_obscond[indx] = props.obscond

        # We compute the X / Y focalplane coordinates for ALL available
        # targets, not just the assigned ones.  This allows us to write out
        # the focalplane coordinates of all available targets as computed by
        # the code at the time it was run.
        #
        # NOTE:  The output format is explicitly CS5 coordinates, even though
        # we use curved focal surface internally.
        xy = hw.radec2xy_multi(tile_ra, tile_dec, tile_obstheta, tg_ra, tg_dec,
                               True, 0)
        for indx, fxy in enumerate(xy):
            tg_x[indx] = fxy[0]
            tg_y[indx] = fxy[1]

        # tm.stop()
        # tm.report("  extract target props for {}".format(tile_id))
        # tm.clear()
        # tm.start()

        # Write a header-only HDU 0 with some basic keywords
        header = dict()
        header["TILEID"] = tile_id
        header["TILERA"] = tile_ra
        header["TILEDEC"] = tile_dec
        header["FIELDROT"] = tile_obstheta
        header["FA_PLAN"] = tile_obstime
        header["FA_HA"] = tile_obsha
        header["FA_RUN"] = hw.time()

        header["REQRA"] = tile_ra
        header["REQDEC"] = tile_dec
        header["FIELDNUM"] = 0
        header["FA_VER"] = __version__
        header["FA_SURV"] = tgs.survey()
        add_dependencies(header,
                         module_names=[
                             "numpy", "matplotlib", "astropy", "fitsio",
                             "desiutil", "desimodel", "desitarget"
                         ])
        fd.write(None, header=header, extname="PRIMARY")

        # FIXME:  write "-1" for unassigned targets.  Write all other fiber
        # status and other properties to this table.

        log.debug(
            "Write:  copying assignment data for tile {}".format(tile_id))

        fdata = np.zeros(nloc, dtype=assign_dtype)
        fdata["LOCATION"] = locs

        # For unassigned fibers, we give each location a unique negative
        # number based on the tile and loc.
        unassign_offset = tile_id * nloc
        assigned_tgids = np.array([
            tdata[x] if x in tdata.keys() else -(unassign_offset + x)
            for x in locs
        ],
                                  dtype=np.int64)
        fdata["TARGETID"] = assigned_tgids

        # Rows containing assigned locations
        assigned_valid = np.where(assigned_tgids >= 0)[0]
        assigned_invalid = np.where(assigned_tgids < 0)[0]

        # Buffers for X/Y/RA/DEC
        assigned_tgx = np.full(nloc, 9999.9, dtype=np.float64)
        assigned_tgy = np.full(nloc, 9999.9, dtype=np.float64)
        assigned_tgra = np.full(nloc, 9999.9, dtype=np.float64)
        assigned_tgdec = np.full(nloc, 9999.9, dtype=np.float64)
        assigned_tgbits = np.zeros(nloc, dtype=np.int64)
        assigned_tgtype = np.zeros(nloc, dtype=np.uint8)

        if (len(assigned_invalid) > 0):
            # Fill our unassigned location X/Y coordinates with the central
            # positioner locations.  Then convert these to RA/DEC.
            # NOTE:  Positioner locations are in curved focal surface coordinates.
            empty_fibers = locs[assigned_invalid]
            fpos_xy_mm = dict(hw.loc_pos_curved_mm)
            empty_x = np.array([fpos_xy_mm[f][0] for f in empty_fibers],
                               dtype=np.float64)
            empty_y = np.array([fpos_xy_mm[f][1] for f in empty_fibers],
                               dtype=np.float64)
            radec = hw.xy2radec_multi(tile_ra, tile_dec, tile_obstheta,
                                      empty_x, empty_y, False, 0)
            assigned_tgra[assigned_invalid] = [x for x, y in radec]
            assigned_tgdec[assigned_invalid] = [y for x, y in radec]
            empty_xy = hw.radec2xy_multi(tile_ra, tile_dec, tile_obstheta,
                                         assigned_tgra[assigned_invalid],
                                         assigned_tgdec[assigned_invalid],
                                         True, 0)
            assigned_tgx[assigned_invalid] = [x for x, y in empty_xy]
            assigned_tgy[assigned_invalid] = [y for x, y in empty_xy]

        if (len(assigned_valid) > 0):
            # The target IDs assigned to fibers (note- NOT sorted)
            assigned_real = np.copy(assigned_tgids[assigned_valid])

            # Mapping of our assigned target rows into the target properties.
            target_rows = [tg_indx[x] for x in assigned_real]

            # Copy values out of the full target list.
            assigned_tgra[assigned_valid] = np.array(tg_ra[target_rows])
            assigned_tgdec[assigned_valid] = np.array(tg_dec[target_rows])
            assigned_tgx[assigned_valid] = np.array(tg_x[target_rows])
            assigned_tgy[assigned_valid] = np.array(tg_y[target_rows])
            assigned_tgtype[assigned_valid] = np.array(tg_type[target_rows])
            assigned_tgbits[assigned_valid] = np.array(tg_bits[target_rows])

        fdata["TARGET_RA"] = assigned_tgra
        fdata["TARGET_DEC"] = assigned_tgdec
        fdata["FIBERASSIGN_X"] = assigned_tgx
        fdata["FIBERASSIGN_Y"] = assigned_tgy
        fdata["FA_TARGET"] = assigned_tgbits
        fdata["FA_TYPE"] = assigned_tgtype

        fibers = dict(hw.loc_fiber)
        etcloc = sorted([x for x, y in fibers.items() if y < 0])
        fakefibers = {y: (5000 + x) for x, y in enumerate(etcloc)}
        fullfibers = fibers
        fullfibers.update(fakefibers)
        device = dict(hw.loc_device)
        petal = dict(hw.loc_petal)
        device_type = dict(hw.loc_device_type)

        fdata["FIBER"] = np.array([fullfibers[x]
                                   for x in locs]).astype(np.int32)
        fdata["DEVICE_LOC"] = np.array([device[x]
                                        for x in locs]).astype(np.int32)
        fdata["PETAL_LOC"] = np.array([petal[x]
                                       for x in locs]).astype(np.int16)
        fdata["DEVICE_TYPE"] = np.array([device_type[x]
                                         for x in locs]).astype(np.dtype("a3"))

        # This hard-coded value copied from the original code...
        lambda_ref = np.ones(nloc, dtype=np.float32) * 5400.0
        fdata["LAMBDA_REF"] = lambda_ref

        # Fiber status
        fstate = dict(hw.state)
        fstatus = np.zeros(nloc, dtype=np.int32)
        # Set unused bit
        fstatus |= [
            0 if x in tdata.keys() else FIBER_STATE_UNASSIGNED for x in locs
        ]
        # Set stuck / broken bits
        fstatus |= [2 if (fstate[x] & FIBER_STATE_STUCK) else 0 for x in locs]
        fstatus |= [4 if (fstate[x] & FIBER_STATE_BROKEN) else 0 for x in locs]
        fstatus[assigned_valid] |= \
            [8 if (tg_type[x] & TARGET_TYPE_SAFE) else 0
             for x in target_rows]
        fdata["FIBERSTATUS"] = fstatus

        # tm.stop()
        # tm.report("  copy and compute assign data tile {}".format(tile_id))
        # tm.clear()
        # tm.start()

        log.debug(
            "Write:  writing assignment data for tile {}".format(tile_id))

        fd.write(fdata, header=header, extname="FASSIGN")
        del fdata

        # tm.stop()
        # tm.report("  write assign data tile {}".format(tile_id))
        # tm.clear()
        # tm.start()

        log.debug("Write:  writing target data for tile {}".format(tile_id))

        fdata = np.zeros(ntarget, dtype=targets_dtype)
        fdata["TARGETID"] = tgids
        fdata["TARGET_RA"] = tg_ra
        fdata["TARGET_DEC"] = tg_dec
        fdata["FA_TARGET"] = tg_bits
        fdata["FA_TYPE"] = tg_type
        fdata["PRIORITY"] = tg_priority
        fdata["SUBPRIORITY"] = tg_subpriority
        fdata["OBSCONDITIONS"] = tg_obscond

        # tm.stop()
        # tm.report("  copy targets data tile {}".format(tile_id))
        # tm.clear()
        # tm.start()

        fd.write(fdata, header=header, extname="FTARGETS")
        del fdata

        # tm.stop()
        # tm.report("  write targets data tile {}".format(tile_id))
        # tm.clear()
        # tm.start()

        log.debug("Write:  writing avail data for tile {}".format(tile_id))

        fdata = np.zeros(navail, dtype=avail_dtype)
        off = 0
        for lid in sorted(avail.keys()):
            for tg in avail[lid]:
                fdata[off] = (lid, fibers[lid], tg)
                off += 1

        # tm.stop()
        # tm.report("  copy avail data tile {}".format(tile_id))
        # tm.clear()
        # tm.start()

        fd.write(fdata, header=header, extname="FAVAIL")
        del fdata

        if gfa_targets is not None:
            try:
                # Astropy Table
                fd.write(gfa_targets.as_array(), extname="GFA_TARGETS")
            except AttributeError:
                # numpy structured array
                fd.write(gfa_targets, extname="GFA_TARGETS")

        fd.close()
        os.rename(tmp_file, tile_file)

        # tm.stop()
        # tm.report("  write avail data tile {}".format(tile_id))
    return
Exemple #35
0
def write_frame(outfile, frame, header=None, fibermap=None, units=None):
    """Write a frame fits file and returns path to file written.

    Args:
        outfile: full path to output file, or tuple (night, expid, channel)
        frame:  desispec.frame.Frame object with wave, flux, ivar...

    Optional:
        header: astropy.io.fits.Header or dict to override frame.header
        fibermap: table to store as FIBERMAP HDU

    Returns:
        full filepath of output file that was written

    Note:
        to create a Frame object to pass into write_frame,
        frame = Frame(wave, flux, ivar, resolution_data)
    """
    log = get_logger()
    outfile = makepath(outfile, 'frame')

    #- Ignore some known and harmless units warnings
    import warnings
    warnings.filterwarnings('ignore', message="'.*nanomaggies.* did not parse as fits unit.*")
    warnings.filterwarnings('ignore', message=".*'10\*\*6 arcsec.* did not parse as fits unit.*")

    if header is not None:
        hdr = fitsheader(header)
    else:
        hdr = fitsheader(frame.meta)

    add_dependencies(hdr)

    # Vette
    diagnosis = frame.vet()
    if diagnosis != 0:
        raise IOError("Frame did not pass simple vetting test. diagnosis={:d}".format(diagnosis))

    hdus = fits.HDUList()
    x = fits.PrimaryHDU(frame.flux.astype('f4'), header=hdr)
    x.header['EXTNAME'] = 'FLUX'
    if units is not None:
        units = str(units)
        if 'BUNIT' in hdr and hdr['BUNIT'] != units:
            log.warning('BUNIT {bunit} != units {units}; using {units}'.format(
                        bunit=hdr['BUNIT'], units=units))
        x.header['BUNIT'] = units
    hdus.append(x)

    hdus.append( fits.ImageHDU(frame.ivar.astype('f4'), name='IVAR') )
    # hdus.append( fits.CompImageHDU(frame.mask, name='MASK') )
    hdus.append( fits.ImageHDU(frame.mask, name='MASK') )
    hdus.append( fits.ImageHDU(frame.wave.astype('f8'), name='WAVELENGTH') )
    hdus[-1].header['BUNIT'] = 'Angstrom'
    if frame.resolution_data is not None:
        hdus.append( fits.ImageHDU(frame.resolution_data.astype('f4'), name='RESOLUTION' ) )
    elif frame.wsigma is not None:
        log.debug("Using ysigma from qproc")
        qrimg=fits.ImageHDU(frame.wsigma.astype('f4'), name='YSIGMA' )
        qrimg.header["NDIAG"] =frame.ndiag
        hdus.append(qrimg)
    if fibermap is not None:
        fibermap = encode_table(fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append( fits.convenience.table_to_hdu(fibermap) )
    elif frame.fibermap is not None:
        fibermap = encode_table(frame.fibermap)  #- unicode -> bytes
        fibermap.meta['EXTNAME'] = 'FIBERMAP'
        hdus.append( fits.convenience.table_to_hdu(fibermap) )
    elif frame.spectrograph is not None:
        x.header['FIBERMIN'] = 500*frame.spectrograph  # Hard-coded (as in desispec.frame)
    else:
        log.error("You are likely writing a frame without sufficient fiber info")

    if frame.chi2pix is not None:
        hdus.append( fits.ImageHDU(frame.chi2pix.astype('f4'), name='CHI2PIX' ) )

    if frame.scores is not None :
        scores_tbl = encode_table(frame.scores)  #- unicode -> bytes
        scores_tbl.meta['EXTNAME'] = 'SCORES'
        hdus.append( fits.convenience.table_to_hdu(scores_tbl) )
        if frame.scores_comments is not None : # add comments in header
            hdu=hdus['SCORES']
            for i in range(1,999):
                key = 'TTYPE'+str(i)
                if key in hdu.header:
                    value = hdu.header[key]
                    if value in frame.scores_comments.keys() :
                        hdu.header[key] = (value, frame.scores_comments[value])

    hdus.writeto(outfile+'.tmp', overwrite=True, checksum=True)

    os.rename(outfile+'.tmp', outfile)

    return outfile
Exemple #36
0
    def __init__(self,path,mode = 'readonly',header = None):
        if mode not in ('readonly','update'):
            raise RuntimeError('Invalid mode %r' % mode)
        self.path = path
        self.mode = mode
        # Create a new file if necessary.
        if self.mode == 'update' and not os.path.exists(self.path):
            # BRICKNAM must be in header if creating the file for the first time
            if header is None or 'BRICKNAM' not in header:
                raise ValueError('header must have BRICKNAM when creating new brick file')

            self.brickname = header['BRICKNAM']
            if 'CHANNEL' in header:
                self.channel = header['CHANNEL']
            else:
                self.channel = 'brz'  #- could be any spectrograph channel
                
            # Create the parent directory, if necessary.
            head,tail = os.path.split(self.path)
            if not os.path.exists(head):
                os.makedirs(head)
            # Create empty HDUs. It would be good to refactor io.frame to avoid any duplication here.
            hdr = desispec.io.util.fitsheader(header)
            add_dependencies(hdr)
            hdr['EXTNAME'] = ('FLUX', 'no dimension')
            hdu0 = astropy.io.fits.PrimaryHDU(header = hdr)
            hdr['EXTNAME'] = ('IVAR', 'no dimension')
            hdu1 = astropy.io.fits.ImageHDU(header = hdr)
            hdr['EXTNAME'] = ('WAVELENGTH', '[Angstroms]')
            hdu2 = astropy.io.fits.ImageHDU(header = hdr)
            hdr['EXTNAME'] = ('RESOLUTION', 'no dimension')
            hdu3 = astropy.io.fits.ImageHDU(header = hdr)
            # Create an HDU4 using the columns from fibermap with a few extras added.
            columns = desispec.io.fibermap.fibermap_columns[:]
            columns.extend([
                ('NIGHT','i4'),
                ('EXPID','i4'),
                ('INDEX','i4'),
                ])
            data = np.empty(shape = (0,),dtype = columns)
            hdr = desispec.io.util.fitsheader(header)

            #- ignore incorrect and harmless fits TDIM7 warning for
            #- FILTER column that is a 2D array of strings
            with warnings.catch_warnings():
                warnings.simplefilter('ignore')
                hdu4 = astropy.io.fits.BinTableHDU(data=data, header=hdr, name='FIBERMAP')

            # Add comments for fibermap columns.
            num_fibermap_columns = len(desispec.io.fibermap.fibermap_comments)
            for i in range(1,1+num_fibermap_columns):
                key = 'TTYPE%d' % i
                name = hdu4.header[key]
                comment = desispec.io.fibermap.fibermap_comments[name]
                hdu4.header[key] = (name,comment)
            # Add comments for our additional columns.
            hdu4.header['TTYPE%d' % (1+num_fibermap_columns)] = ('NIGHT','Night of exposure YYYYMMDD')
            hdu4.header['TTYPE%d' % (2+num_fibermap_columns)] = ('EXPID','Exposure ID')
            hdu4.header['TTYPE%d' % (3+num_fibermap_columns)] = ('INDEX','Index of this object in other HDUs')
            self.hdu_list = astropy.io.fits.HDUList([hdu0,hdu1,hdu2,hdu3,hdu4])
        else:
            self.hdu_list = astropy.io.fits.open(path,mode = self.mode)
            try:
                self.brickname = self.hdu_list[0].header['BRICKNAM']
                self.channel = self.hdu_list[0].header['CHANNEL']
            except KeyError:
                self.channel, self.brickname = _parse_brick_filename(path)
Exemple #37
0
def write_image(outfile, image, meta=None):
    """Writes image object to outfile

    Args:
        outfile : output file string
        image : desispec.image.Image object
            (or any object with 2D array attributes image, ivar, mask)

    Optional:
        meta : dict-like object with metadata key/values (e.g. FITS header)
    """

    log = get_logger()
    if meta is not None:
        hdr = fitsheader(meta)
    else:
        hdr = fitsheader(image.meta)

    add_dependencies(hdr)

    #- Work around fitsio>1.0 writing blank keywords, e.g. on 20191212
    for key in hdr.keys():
        if type(hdr[key]) == fits.card.Undefined:
            log.warning('Setting blank keyword {} to None'.format(key))
            hdr[key] = None

    outdir = os.path.dirname(os.path.abspath(outfile))
    if not os.path.isdir(outdir):
        os.makedirs(outdir)

    hx = fits.HDUList()
    hdu = fits.ImageHDU(image.pix.astype(np.float32), name='IMAGE', header=hdr)
    if 'CAMERA' not in hdu.header:
        hdu.header.append(
            ('CAMERA', image.camera.lower(), 'Spectrograph Camera'))

    if 'RDNOISE' not in hdu.header and np.isscalar(image.readnoise):
        hdu.header.append(
            ('RDNOISE', image.readnoise, 'Read noise [RMS electrons/pixel]'))

    hx.append(hdu)
    hx.append(fits.ImageHDU(image.ivar.astype(np.float32), name='IVAR'))
    hx.append(fits.CompImageHDU(image.mask.astype(np.int16), name='MASK'))
    if not np.isscalar(image.readnoise):
        hx.append(
            fits.ImageHDU(image.readnoise.astype(np.float32),
                          name='READNOISE'))

    if hasattr(image, 'fibermap'):
        if isinstance(image.fibermap, Table):
            fmhdu = fits.convenience.table_to_hdu(image.fibermap)
            fmhdu.name = 'FIBERMAP'
        else:
            fmhdu = fits.BinTableHDU(image.fibermap, name='FIBERMAP')

        hx.append(fmhdu)

    hx.writeto(outfile + '.tmp', overwrite=True, checksum=True)
    os.rename(outfile + '.tmp', outfile)

    return outfile
Exemple #38
0
def write_raw(filename, rawdata, header, camera=None, primary_header=None):
    '''
    Write raw pixel data to a DESI raw data file

    Args:
        filename : file name to write data; if this exists, append a new HDU
        rawdata : 2D ndarray of raw pixel data including overscans
        header : dict-like object or fits.Header with keywords
            CCDSECx, BIASSECx, DATASECx where x=1,2,3, or 4

    Options:
        camera : b0, r1 .. z9 - override value in header
        primary_header : header to write in HDU0 if filename doesn't yet exist

    The primary utility of this function over raw fits calls is to ensure
    that all necessary keywords are present before writing the file.
    CCDSECx, BIASSECx, DATASECx where x=1,2,3, or 4
    DATE-OBS, GAINx and RDNOISEx will generate a non-fatal warning if missing
    '''
    log = get_logger()

    header = desispec.io.util.fitsheader(header)
    primary_header = desispec.io.util.fitsheader(primary_header)

    if rawdata.dtype not in (np.int16, np.int32, np.int64):
        message = 'dtype {} not supported for raw data'.format(rawdata.dtype)
        log.fatal(message)
        raise ValueError(message)

    fail_message = ''
    for required_key in ['DOSVER', 'FEEVER', 'DETECTOR']:
        if required_key not in primary_header:
            if required_key in header:
                primary_header[required_key] = header[required_key]
            else:
                fail_message = fail_message + \
                    'Keyword {} must be in header or primary_header\n'.format(required_key)
    if fail_message != '':
        raise ValueError(fail_message)

    #- Check required keywords before writing anything
    missing_keywords = list()
    if camera is None and 'CAMERA' not in header:
        log.error("Must provide camera keyword or header['CAMERA']")
        missing_keywords.append('CAMERA')

    for amp in ['1', '2', '3', '4']:
        for prefix in ['CCDSEC', 'BIASSEC', 'DATASEC']:
            keyword = prefix+amp
            if keyword not in header:
                log.error('Missing keyword '+keyword)
                missing_keywords.append(keyword)

    #- Missing DATE-OBS is warning but not error
    if 'DATE-OBS' not in primary_header:
        if 'DATE-OBS' in header:
            primary_header['DATE-OBS'] = header['DATE-OBS']
        else:
            log.warning('missing keyword DATE-OBS')

    #- Missing GAINx is warning but not error
    for amp in ['1', '2', '3', '4']:
        keyword = 'GAIN'+amp
        if keyword not in header:
            log.warning('Gain keyword {} missing; using 1.0'.format(keyword))
            header[keyword] = 1.0

    #- Missing RDNOISEx is warning but not error
    for amp in ['1', '2', '3', '4']:
        keyword = 'RDNOISE'+amp
        if keyword not in header:
            log.warning('Readnoise keyword {} missing'.format(keyword))

    #- Stop if any keywords are missing
    if len(missing_keywords) > 0:
        raise KeyError('missing required keywords {}'.format(missing_keywords))

    #- Set EXTNAME=camera
    if camera is not None:
        header['CAMERA'] = camera.lower()
        extname = camera.upper()
    else:
        if header['CAMERA'] != header['CAMERA'].lower():
            log.warning('Converting CAMERA {} to lowercase'.format(header['CAMERA']))
            header['CAMERA'] = header['CAMERA'].lower()
        extname = header['CAMERA'].upper()

    header['INHERIT'] = True

    #- fits.CompImageHDU doesn't know how to fill in default keywords, so
    #- temporarily generate an uncompressed HDU to get those keywords
    header = fits.ImageHDU(rawdata, header=header, name=extname).header

    #- Bizarrely, compression of 64-bit integers isn't supported.
    #- downcast to 32-bit if that won't lose precision.
    #- Real raw data should be 32-bit or 16-bit anyway
    if rawdata.dtype == np.int64:
        if np.max(np.abs(rawdata)) < 2**31:
            rawdata = rawdata.astype(np.int32)

    if rawdata.dtype in (np.int16, np.int32):
        dataHDU = fits.CompImageHDU(rawdata, header=header, name=extname)
    elif rawdata.dtype == np.int64:
        log.warning('Image compression not supported for 64-bit; writing uncompressed')
        dataHDU = fits.ImageHDU(rawdata, header=header, name=extname)
    else:
        log.error("How did we get this far with rawdata dtype {}?".format(rawdata.dtype))
        dataHDU = fits.ImageHDU(rawdata, header=header, name=extname)

    #- Actually write or update the file
    if os.path.exists(filename):
        hdus = fits.open(filename, mode='append', memmap=False)
        if extname in hdus:
            hdus.close()
            raise ValueError('Camera {} already in {}'.format(camera, filename))
        else:
            hdus.append(dataHDU)
            hdus.flush()
            hdus.close()
    else:
        hdus = fits.HDUList()
        add_dependencies(primary_header)
        hdus.append(fits.PrimaryHDU(None, header=primary_header))
        hdus.append(dataHDU)
        hdus.writeto(filename)
Exemple #39
0
def write_spectra(outfile, spec, units=None):
    """
    Write Spectra object to FITS file.

    This places the metadata into the header of the (empty) primary HDU.
    The first extension contains the fibermap, and then HDUs are created for
    the different data arrays for each band.

    Floating point data is converted to 32 bits before writing.

    Args:
        outfile (str): path to write
        spec (Spectra): the object containing the data
        units (str): optional string to use for the BUNIT key of the flux
            HDUs for each band.

    Returns:
        The absolute path to the file that was written.

    """

    outfile = os.path.abspath(outfile)

    # Create the parent directory, if necessary.
    dir, base = os.path.split(outfile)
    if not os.path.exists(dir):
        os.makedirs(dir)

    # Create HDUs from the data
    all_hdus = fits.HDUList()

    # metadata goes in empty primary HDU
    hdr = fitsheader(spec.meta)
    add_dependencies(hdr)

    all_hdus.append(fits.PrimaryHDU(header=hdr))

    # Next is the fibermap
    fmap = spec.fibermap.copy()
    fmap.meta["EXTNAME"] = "FIBERMAP"
    with warnings.catch_warnings():
        #- nanomaggies aren't an official IAU unit but don't complain
        warnings.filterwarnings('ignore', '.*nanomaggies.*')
        hdu = fits.convenience.table_to_hdu(fmap)

    # Add comments for fibermap columns.
    for i, colname in enumerate(fmap.dtype.names):
        if colname in fibermap_comments:
            key = "TTYPE{}".format(i + 1)
            name = hdu.header[key]
            assert name == colname
            comment = fibermap_comments[name]
            hdu.header[key] = (name, comment)
        else:
            pass
            #print('Unknown comment for {}'.format(colname))

    all_hdus.append(hdu)

    # Now append the data for all bands

    for band in spec.bands:
        hdu = fits.ImageHDU(name="{}_WAVELENGTH".format(band.upper()))
        hdu.header["BUNIT"] = "Angstrom"
        hdu.data = spec.wave[band].astype("f8")
        all_hdus.append(hdu)

        hdu = fits.ImageHDU(name="{}_FLUX".format(band.upper()))
        if units is None:
            hdu.header["BUNIT"] = "10**-17 erg/(s cm2 Angstrom)"
        else:
            hdu.header["BUNIT"] = units
        hdu.data = spec.flux[band].astype("f4")
        all_hdus.append(hdu)

        hdu = fits.ImageHDU(name="{}_IVAR".format(band.upper()))
        if units is None:
            hdu.header["BUNIT"] = '10**+34 (s2 cm4 Angstrom2) / erg2'
        else:
            hdu.header["BUNIT"] = ((u.Unit(
                units, format='fits'))**-2).to_string('fits')
        hdu.data = spec.ivar[band].astype("f4")
        all_hdus.append(hdu)

        if spec.mask is not None:
            # hdu = fits.CompImageHDU(name="{}_MASK".format(band.upper()))
            hdu = fits.ImageHDU(name="{}_MASK".format(band.upper()))
            hdu.data = spec.mask[band].astype(np.uint32)
            all_hdus.append(hdu)

        if spec.resolution_data is not None:
            hdu = fits.ImageHDU(name="{}_RESOLUTION".format(band.upper()))
            hdu.data = spec.resolution_data[band].astype("f4")
            all_hdus.append(hdu)

        if spec.extra is not None:
            for ex in spec.extra[band].items():
                hdu = fits.ImageHDU(name="{}_{}".format(band.upper(), ex[0]))
                hdu.data = ex[1].astype("f4")
                all_hdus.append(hdu)

    if spec.scores is not None:
        scores_tbl = encode_table(spec.scores)  #- unicode -> bytes
        scores_tbl.meta['EXTNAME'] = 'SCORES'
        all_hdus.append(fits.convenience.table_to_hdu(scores_tbl))
        if spec.scores_comments is not None:  # add comments in header
            hdu = all_hdus['SCORES']
            for i in range(1, 999):
                key = 'TTYPE' + str(i)
                if key in hdu.header:
                    value = hdu.header[key]
                    if value in spec.scores_comments.keys():
                        hdu.header[key] = (value, spec.scores_comments[value])

    all_hdus.writeto("{}.tmp".format(outfile), overwrite=True, checksum=True)
    os.rename("{}.tmp".format(outfile), outfile)

    return outfile
Exemple #40
0
                #- found a tile for this timeslot; don't check other programs
                break

    #- Advance by 10 min; approximates long slews and observing inefficiencies
    now += 10*u.min
 
#- TODO: write outputs (exposures list, subset of tiles observed)

x = exposures = Table(obslist)

if not os.path.exists(args.outdir):
    os.makedirs(args.outdir, exist_ok=True)

exposures.meta['EXTNAME'] = 'EXPOSURES'
exposures.meta['RANDSEED'] = args.randseed
add_dependencies(exposures.meta)
outfile = os.path.join(args.outdir, 'sv_exposures.fits')
exposures.write(outfile, overwrite=True)
print('Wrote {}'.format(outfile))

for tiles in program_tiles:
    filename = os.path.join(args.outdir, tiles.meta['PROGRAM'] + '-tiles.fits')
    ii = (tiles['NUMOBS_DARK'] > 0) | (tiles['NUMOBS_GRAY'] > 0) | (tiles['NUMOBS_BRIGHT'] > 0)
    tiles.meta['EXTNAME'] = 'TILES'
    tiles.meta['RANDSEED'] = args.randseed
    add_dependencies(tiles.meta)
    tiles[ii].write(filename, overwrite=True)
    print('Wrote {}'.format(filename))

#- Create concatentation of tiles
for tiles in program_tiles: