Ejemplo n.º 1
0
 def __init__(self):
     import pydl.pydlutils.yanny as yanny
     import os
     blockfile = os.path.join(os.getenv('PLATEDESIGN_DIR'), 'data',
                              'apogee', 'fiberBlocksAPOGEE_SOUTH.par')
     self.blocks = yanny.yanny(blockfile)
     self.fibers = self.blocks['TIFIBERBLOCK_APOGEE_SOUTH']
Ejemplo n.º 2
0
 def __init__(self,
              schedule='normal',
              observatory='apo',
              observatoryfile=None):
     """Create Master object for schedule"""
     super().__init__(observatory=observatory,
                      observatoryfile=observatoryfile)
     masterfile = 'master_schedule_{o}_{s}.par'.format(o=observatory,
                                                       s=schedule)
     env_dir = os.getenv('ROBOSCHEDULER_DIR')
     if env_dir is None:
         env_dir = os.path.abspath(__file__).split(
             "/python/roboscheduler/")[0]
     schedulefile = os.path.join(env_dir, 'data', masterfile)
     print(schedulefile)
     self._schedulefile = schedulefile
     self.schedule = yanny.yanny(self._schedulefile)
     self._validate()
     self.event_dates = np.array(
         [date.decode() for date in self.schedule['SCHEDULE']['date']])
     self.event_times = np.array(
         [time.decode() for time in self.schedule['SCHEDULE']['time']])
     self.event_mjds = self._dateandtime2mjd()
     self.events = np.array(
         [event.decode() for event in self.schedule['SCHEDULE']['event']])
     self.start = self._start()
     self.end = self._end()
     self.mjds = self._mjds()
     self.dark_twilight = np.float32(self.schedule['dark_twilight'])
     self.bright_twilight = np.float32(self.schedule['bright_twilight'])
     return
Ejemplo n.º 3
0
 def __init__(self,
              schedulefile=None,
              observatory='apo',
              observatoryfile=None):
     """Create Master object for schedule"""
     super().__init__(observatory=observatory,
                      observatoryfile=observatoryfile)
     if (schedulefile is None):
         masterfile = 'master_schedule_{observatory}.par'.format(
             observatory=observatory)
         schedulefile = os.path.join(os.getenv('OBSERVESIM_DIR'), 'data',
                                     masterfile)
     self._schedulefile = schedulefile
     self.schedule = yanny.yanny(self._schedulefile)
     self._validate()
     self.event_dates = np.array(
         [date.decode() for date in self.schedule['SCHEDULE']['date']])
     self.event_times = np.array(
         [time.decode() for time in self.schedule['SCHEDULE']['time']])
     self.event_mjds = self._dateandtime2mjd()
     self.events = np.array(
         [event.decode() for event in self.schedule['SCHEDULE']['event']])
     self.start = self._start()
     self.end = self._end()
     self.mjds = self._mjds()
     return
Ejemplo n.º 4
0
 def __init__(self,
              observatory='apo',
              observatoryfile=None,
              dark_twilight=-15.,
              bright_twilight=-8.):
     """Create Observer object"""
     super().__init__()
     self.observatory = observatory
     if (observatoryfile is None):
         env_dir = os.getenv('ROBOSCHEDULER_DIR')
         if env_dir is None:
             env_dir = os.path.abspath(__file__).split(
                 "/python/roboscheduler/")[0]
         observatoryfile = os.path.join(env_dir, 'data',
                                        'observatories.par')
     self._file = observatoryfile
     self._data = yanny.yanny(self._file)
     observatories = np.array(
         [obs.decode() for obs in self._data['OBSERVATORY']['observatory']])
     indx = np.where(observatories == self.observatory)[0]
     self.latitude = self._data['OBSERVATORY']['latitude'][indx]
     self.longitude = self._data['OBSERVATORY']['longitude'][indx]
     self.dark_twilight = np.float32(dark_twilight)
     self.bright_twilight = np.float32(bright_twilight)
     return
Ejemplo n.º 5
0
    def from_par_file(cls, f):
        """
        Instantiate the plan set from a par file
        """

        # TODO: The approach here (read using yanny, set to par
        # individually, then covert back to record array using
        # ParDatabase) is stupid...

        if not os.path.isfile(f):
            raise FileNotFoundError('No file {0}.'.format(f))
    
        par = yanny(filename=f, raw=True)
        if len(par['DAPPLAN']['drpqa_key']) == 0:
            raise ValueError('Could not find DAPPLAN entries in {0}!'.format(f))

        # Setup the array of emission line database parameters
        nplan = len(par['DAPPLAN']['drpqa_key'])
        planlist = []
        for i in range(nplan):
            planlist += [ AnalysisPlan(par['DAPPLAN']['drpqa_key'][i],
                                       bool(par['DAPPLAN']['drpqa_clobber'][i]),
                                       par['DAPPLAN']['bin_key'][i],
                                       bool(par['DAPPLAN']['bin_clobber'][i]),
                                       par['DAPPLAN']['continuum_key'][i],
                                       bool(par['DAPPLAN']['continuum_clobber'][i]),
                                       par['DAPPLAN']['elmom_key'][i],
                                       bool(par['DAPPLAN']['elmom_clobber'][i]),
                                       par['DAPPLAN']['elfit_key'][i],
                                       bool(par['DAPPLAN']['elfit_clobber'][i]),
                                       par['DAPPLAN']['spindex_key'][i],
                                       bool(par['DAPPLAN']['spindex_clobber'][i])) ]
        return cls(planlist)
Ejemplo n.º 6
0
def read_tiles(parfile):
    a = yanny(parfile)
    tile = np.array(a['STRUCT1']['tile']).astype('str')
    ra = np.array(a['STRUCT1']['racen'])
    tmp = (ra > 270.)
    ra[tmp] = ra[tmp] - 360.
    dec = np.array(a['STRUCT1']['deccen'])
    return [tile, ra, dec]
Ejemplo n.º 7
0
 def __init__(self, observatory='apo', observatoryfile=None):
     """Create Observer object"""
     super().__init__()
     self.observatory = observatory
     if (observatoryfile is None):
         observatoryfile = os.path.join(os.getenv('OBSERVESIM_DIR'), 'data',
                                        'observatories.par')
     self._file = observatoryfile
     self._data = yanny.yanny(self._file)
     observatories = np.array([
         observatory.decode()
         for observatory in self._data['OBSERVATORY']['observatory']
     ])
     indx = np.where(observatories == self.observatory)[0][0]
     self.latitude = self._data['OBSERVATORY']['latitude'][indx]
     self.longitude = self._data['OBSERVATORY']['longitude'][indx]
Ejemplo n.º 8
0
    def _parse_yanny(self):
        """
        Parse the yanny file (provided by :attr:`file`) for the
        bandhead database.

        Returns:
            :obj:`list`: The list of
            :class:`mangadap.par.parset.ParSet` instances for each
            line of the database.
        """
        # Read the yanny file
        par = yanny(filename=self.file, raw=True)
        if len(par['DAPABI']['index']) == 0:
            raise ValueError('Could not find DAPABI entries in {0}!'.format(
                self.file))

        # Check if any of the bands are dummy bands and warn the user
        self.dummy = numpy.any(numpy.array(par['DAPABI']['blueside']) < 0,
                               axis=1)
        self.dummy |= numpy.any(numpy.array(par['DAPABI']['redside']) < 0,
                                axis=1)
        self.dummy |= numpy.any(numpy.array(par['DAPABI']['primary']) < 0,
                                axis=1)
        if numpy.sum(self.dummy) > 0:
            warnings.warn(
                'Bands with negative wavelengths are used to insert dummy values.'
                '  Ignoring input bands with indices: {0}'.format(
                    numpy.array(par['DAPABI']['index'])[self.dummy]))

        # Setup the array of absorption-line index database parameters
        self.size = len(par['DAPABI']['index'])
        parlist = []
        for i in range(self.size):
            invac = par['DAPABI']['waveref'][i] == 'vac'
            comp = par['DAPABI']['component'][i] != 0
            parlist += [ BandPassFilterPar(index=par['DAPABI']['index'][i],
                                           name=par['DAPABI']['name'][i],
                                blueside=par['DAPABI']['blueside'][i] if invac \
                                        else airtovac(numpy.array(par['DAPABI']['blueside'][i])),
                                redside=par['DAPABI']['redside'][i] if invac \
                                        else airtovac(numpy.array(par['DAPABI']['redside'][i])),
                                primary=par['DAPABI']['primary'][i] if invac \
                                        else airtovac(numpy.array(par['DAPABI']['primary'][i])),
                                           units=par['DAPABI']['units'][i],
                                           integrand='flambda',
                                           component=comp) ]
        return parlist
Ejemplo n.º 9
0
    def _read_fix_data(self, fix_file):
        """
        Read "fix" data from the provided file.

        Fix data are generally corrections to the metadata provides by the NSA
        for specific MaNGA observations.

        Args:
            fix_file (:obj:`str`):
                SDSS parameter file with the fix data.

        Returns:
            yanny: A yanny structure with the data fixes.  Returns
            None and raises a warning if the file does not exist.
        """
        if not fix_file.exists():
            warnings.warn(f'DAP "fix" file does not exist: {fix_file}')
            return None
        return yanny(filename=str(fix_file))
Ejemplo n.º 10
0
Archivo: plate.py Proyecto: jcbird/ppv
def load_yanny(platenum):
    platepath = paths.plateholes(platenum)

    if platepath.exists():
        pass
    else:  # need to get plugHoles files
        platebatch_path = paths.plate_batch(platenum)
        platebatch = platebatch_path.name
        print(
            f'{os.fspath(platepath)} does not exist. Getting files via rsync now'
        )
        print(
            f'--- Please enter your password for {config.utah_username} below --'
        )
        download.plugHoles_batch(platebatch)

    filepath = os.fspath(paths.plateholes(platenum))
    pholes_obj = yanny(filepath, raw=True)
    return pholes_obj
Ejemplo n.º 11
0
def select_targets(outfile, location):
    numpy.random.seed(location)
    datafilename = '../target_info/SecQuanObj_%03i+00.fits' % location
    data = fitsio.read(datafilename)
    # De-redden
    ak = data['AK']
    aj = ak * 2.5
    j0 = data['J_M'] - aj
    k0 = data['K_M'] - ak
    # Cut on jk0,h
    indx = ((j0 - k0) > 0.8) * (data['H_M'] > 12.) * (data['H_M'] < 13.)
    data = data[indx]
    # setup dust map
    dmap = mwdust.Marshall06(filter='2MASS H')
    # Calculate A_H(7 kpc) according to Marshall et al. (2006)
    ahdmap = numpy.zeros(len(data))
    lb = bovy_coords.radec_to_lb(data['RA'], data['DEC'], degree=True)
    for ii in range(len(data)):
        ahdmap[ii] = dmap(lb[ii, 0], lb[ii, 1], 7.)
    # Cut on AH <= 1.4
    indx = ahdmap <= 1.4
    data = data[indx]
    # Match against already drilled targets
    data = esutil.numpy_util.add_fields(data, [('FLAG_DONT_OBSERVE', int)])
    data['FLAG_DONT_OBSERVE'] = 0
    yannyfiles = glob.glob('../target_info/plateHolesSorted-*.par')
    for yannyfile in yannyfiles:
        drillFile = yanny.yanny(filename=yannyfile, np=True)
        drilled = drillFile['STRUCT1']
        # spherematch
        h = esutil.htm.HTM()
        m1, m2, d12 = h.match(data['RA'],
                              data['DEC'],
                              drilled['target_ra'],
                              drilled['target_dec'],
                              2. / 3600.,
                              maxmatch=1)
        data['FLAG_DONT_OBSERVE'][m1] = 1
    # Write to file
    fitsio.write(outfile,
                 data[numpy.random.permutation(len(data))],
                 clobber=True)
    return None
Ejemplo n.º 12
0
    def from_par_file(cls, f, name):
        r"""
        Define the object using an `SDSS-style parameter file`_.  This
        has been tailored to work with the sdssMaskbits.par file in
        IDLUTILS; however, it can work with similar files.

        See :class:`mangadap.util.drpfits.DRPFitsBitMask` for an
        example that uses this function.

        Args:
            f (:obj:`str`):
                File name to use for defining the :class:`BitMask`.
            name (:obj:`str`):
                The designation of the bits to assign. For example,
                in :class:`mangadap.util.drpfits.DRPFitsBitMask` this
                is `'MANGA_DRP3PIXMASK'`.
        
        Returns:
            :class:`BitMask`: Object with bitmasks defined by the
            parameter file.

        Raises:
            FileNotFoundError: Raised if the input file does not exist.
        """
        # Check the file exists
        if not os.path.isfile(f):
            raise FileNotFoundError('Could not find ini file: {0}'.format(f))

        # Read the full yanny file and only select the maskbits typedef
        bits = yanny(filename=f, raw=True)['MASKBITS']

        # Find the bits with the correct designation
        indx = numpy.array(bits['flag']) == name
        keys = numpy.array(bits['label'])[indx]
        vals = numpy.array(bits['bit'])[indx]
        descr = numpy.array(bits['description'])[indx]

        # Slot in NULLs where necessary and return the object instance
        keys, vals, descr = cls._fill_sequence(keys, vals, descr)
        srt = numpy.argsort(vals)
        return cls(keys[srt], descr=None if descr is None else descr[srt])
Ejemplo n.º 13
0
    def from_par_file(cls, f):
        """
        Read the observation parameters from the provided yanny file.

        Args:
            f (str) : Name of the file to read

        Returns:
            :class:`ObsInputPar`: Derived instance of
            :class:`mangadap.par.ParSet` with the input observational
            parameters of the DRP data product to analyze with the DAP.

        Raises:
            FileNotFoundError: Raised if the provided file does not
                exist.
            ValueError: Raised if the input yanny file has more than
                one entry of DAPPAR.
            KeyError: Raised if selected keys are not in provided file.

        """
        if not os.path.isfile(f):
            raise FileNotFoundError('Could not open {0}!'.format(f))

        # Read the file
        par = yanny(filename=f, raw=True)

        # Check the number of entries
        if len(par['DAPPAR']['plate']) > 1:
            raise ValueError('File must contain only instance of DAPPAR!')

        # Return the ObsInputPar instance
        return cls(par['DAPPAR']['plate'][0],
                   par['DAPPAR']['ifudesign'][0],
                   mode=par['DAPPAR']['mode'][0],
                   vel=par['DAPPAR']['vel'][0],
                   vdisp=par['DAPPAR']['vdisp'][0],
                   ell=par['DAPPAR']['ell'][0],
                   pa=par['DAPPAR']['pa'][0],
                   reff=par['DAPPAR']['reff'][0])
Ejemplo n.º 14
0
def select_targets(outfile,location):
    numpy.random.seed(location)
    datafilename= '../target_info/SecQuanObj_%03i+00.fits' % location
    data= fitsio.read(datafilename)
    # De-redden
    ak= data['AK']
    aj= ak*2.5
    j0= data['J_M']-aj
    k0= data['K_M']-ak
    # Cut on jk0,h
    indx= ((j0-k0) > 0.8)*(data['H_M'] > 12.)*(data['H_M'] < 13.)
    data= data[indx]
    # setup dust map
    dmap= mwdust.Marshall06(filter='2MASS H')
    # Calculate A_H(7 kpc) according to Marshall et al. (2006)
    ahdmap= numpy.zeros(len(data))
    lb= bovy_coords.radec_to_lb(data['RA'],data['DEC'],degree=True)
    for ii in range(len(data)):
        ahdmap[ii]= dmap(lb[ii,0],lb[ii,1],7.)
    # Cut on AH <= 1.4
    indx= ahdmap <= 1.4
    data= data[indx]
    # Match against already drilled targets
    data= esutil.numpy_util.add_fields(data,[('FLAG_DONT_OBSERVE', int)])
    data['FLAG_DONT_OBSERVE']= 0
    yannyfiles= glob.glob('../target_info/plateHolesSorted-*.par')
    for yannyfile in yannyfiles:
        drillFile= yanny.yanny(filename=yannyfile,np=True)
        drilled= drillFile['STRUCT1']
        # spherematch
        h=esutil.htm.HTM()
        m1,m2,d12 = h.match(data['RA'],data['DEC'],
                            drilled['target_ra'],drilled['target_dec'],
                             2./3600.,maxmatch=1)
        data['FLAG_DONT_OBSERVE'][m1]= 1
    # Write to file
    fitsio.write(outfile,data[numpy.random.permutation(len(data))],
                 clobber=True)
    return None
Ejemplo n.º 15
0
    def _parse_yanny(self):
        """
        Parse the yanny file (provided by :attr:`file`) for the emission-line
        database.

        Returns:
            :obj:`list`: The list of :class:`~mangadap.par.parset.ParSet`
            instances for each line of the database.
        """
        # Read the yanny file
        par = yanny(filename=self.file, raw=True)
        if len(par['DAPEML']['index']) == 0:
            raise ValueError('Could not find DAPEML entries in {0}!'.format(
                self.file))

        # Setup the array of emission line database parameters
        self.size = len(par['DAPEML']['index'])
        parlist = []
        for i in range(self.size):
            invac = par['DAPEML']['waveref'][i] == 'vac'

            tie_index = -1 if par['DAPEML']['tie'][i][0] == 'None' \
                            else int(par['DAPEML']['tie'][i][0])
            tie_par = [
                None if t == 'None' else t for t in par['DAPEML']['tie'][i][1:]
            ]

            parlist += [EmissionLinePar(index=par['DAPEML']['index'][i],
                                       name=par['DAPEML']['name'][i],
                                       restwave=par['DAPEML']['restwave'][i] if invac
                                                else airtovac(par['DAPEML']['restwave'][i]),
                                       action=par['DAPEML']['action'][i], tie_index=tie_index,
                                       tie_par=tie_par,
                                       blueside=par['DAPEML']['blueside'][i] if invac else \
                                            airtovac(numpy.array(par['DAPEML']['blueside'][i])),
                                       redside=par['DAPEML']['redside'][i] if invac else \
                                            airtovac(numpy.array(par['DAPEML']['redside'][i])))]
        return parlist
Ejemplo n.º 16
0
    def _parse_yanny(self):
        """
        Parse the yanny file (provided by :attr:`file`) for the artifact
        database.

        Returns:
            :obj:`list`: The list of
            :class:`mangadap.par.parset.ParSet` instances for each
            line of the database.
        """
        # Read the yanny file
        par = yanny(filename=self.file, raw=True)
        if len(par['DAPART']['index']) == 0:
            raise ValueError('Could not find DAPART entries in {0}!'.format(self.file))

        # Setup the array of emission line database parameters
        self.size = len(par['DAPART']['index'])
        parlist = []
        for i in range(self.size):
            invac = par['DAPART']['waveref'][i] == 'vac'
            parlist += [ ArtifactPar(index=par['DAPART']['index'][i], name=par['DAPART']['name'][i],
                                     waverange=numpy.asarray(par['DAPART']['waverange'][i]) \
                                      if invac else airtovac(par['DAPEML']['waverange'][i]) )]
        return parlist
Ejemplo n.º 17
0
def load_filters(**kwargs):
    """
    Load filter information from a list of files.

    Filter files should contain one structure with columns 'lambda' &
    'pass'.  The name of the table can be arbitrary though.
    """
    from os import getenv
    from os.path import join
    from numpy import array
    from pydl.pydlutils.yanny import yanny
    if 'filterlist' not in kwargs:
        raise TypeError('Invalid keyword arguments passed to load_filters')
    if 'filterpath' not in kwargs:
        kwargs['filterpath'] = join(getenv('KCORRECT_DIR'),'data','filters')
    filterdata = {'lambda':[], 'pass':[]}
    for fil in kwargs['filterlist']:
        f = yanny(join(kwargs['filterpath'],fil))
        if str(f) == '':
            raise IOError("Could not read {0}".format(join(kwargs['filterpath'],fil)))
        t = f.tables()
        filterdata['lambda'].append(array(f[t[0]]['lambda']))
        filterdata['pass'].append(array(f[t[0]]['pass']))
    return filterdata
Ejemplo n.º 18
0
    def __init__(self, mode='boss', paramfile=None, parametersdir=None):
        self.mode = mode
        self.parametersdir = parametersdir
        self.param = yanny.yanny(paramfile)
        self.alignment = self._set_alignment(mode=mode)
        self.lighttrap = self._set_lighttrap(mode=mode)
        self.objects = self._set_objects(mode=mode)
        self.completion_text = self._set_completion_text(mode=mode)
        self.manga = self._set_manga(mode=mode)
        self.manga_alignment = self._set_manga_alignment(mode=mode)

        # Use different codes for movement for APOGEE south plates,
        # appropriate to drilling on a flat mandrel.
        if(self.mode == 'apogee_south'):
            self.coordcode = 'G56'
        else:
            self.coordcode = 'G54'

        # Comments in CNC code are labeled differently for MaNGA plates
        if(self.mode == 'manga'):
            self.name = 'SDSS/MaNGA'
        else:
            self.name = 'SDSS'

        self.acquisition_header = """(CAMERA MOUNTING HOLES)
G90 G56
"""
        self.acquisition_offaxis_template = """G68 X0.0 Y0.0 R-90.0
G00 X{axy[0]:.6f} Y{axy[1]:.6f}
M98 P9775
G69
"""
        self.acquisition_center = """M98 P9776
M01
"""

        self.header_text = """%
O{plateId7K:d}({name} PLUG-PLATE {plateId:d})
(Drilling temperature {tempShopF:5.1f} degrees F)
(INPUT FILE NAME: {plug_name})
(CNC PROGRAM NAME: {fanuc_name})
"""

        self.first_text = str(self.coordcode) + """ G60 X{cx:.6f} Y{cy:.6f}
G43 H{drillSeq:02d} Z0.1
M08
"""
        self.hole_text = dict()
        self.hole_text['objects'] = """G83 G98 Z{cz:.6f} R{czr:.3f} L0 Q0.5 F9.0
G60 X{cx:.6f} Y{cy:.6f} ( {objId[0]} {objId[1]} {objId[2]} {objId[3]} {objId[4]} )
"""
        self.hole_text['lighttrap'] = self.hole_text['objects']
        self.hole_text['manga'] = self.hole_text['objects']
        self.hole_text['alignment'] = """G83 G98 Z{cz:.6f} R{czr:.3f} L0 Q0.02 F2.0
G60 X{cx:.6f} Y{cy:.6f} ( {objId[0]} {objId[1]} {objId[2]} {objId[3]} {objId[4]} )
"""
        self.hole_text['manga_alignment'] = """G83 G98 Z{cz:.6f} R{czr:.3f} L0 Q0.02 F1.5
G60 X{cx:.6f} Y{cy:.6f} ( {objId[0]} {objId[1]} {objId[2]} {objId[3]} {objId[4]} )
"""

        self.holediam_values = dict()
        self.holediam_values['OBJECT'] = np.float32(2.16662)
        self.holediam_values['COHERENT_SKY'] = np.float32(2.16662)
        self.holediam_values['GUIDE'] = np.float32(2.16662)
        self.holediam_values['LIGHT_TRAP'] = np.float32(3.175)
        self.holediam_values['ALIGNMENT'] = np.float32(1.1811)
        self.holediam_values['MANGA'] = np.float32(2.8448)
        self.holediam_values['MANGA_ALIGNMENT'] = np.float32(0.7874)
        self.holediam_values['MANGA_SINGLE'] = np.float32(3.2766)
        self.holediam_values['ACQUISITION_CENTER'] = np.float32(60.)
        self.holediam_values['ACQUISITION_OFFAXIS'] = np.float32(68.)

        self.drillSeq = dict()
        self.drillSeq['objects'] = 1
        if(self.mode == 'apogee_south'):
            self.drillSeq['objects'] = 11
        self.drillSeq['lighttrap'] = 2
        self.drillSeq['alignment'] = 3
        self.drillSeq['manga'] = 11
        self.drillSeq['manga_alignment'] = 12
Ejemplo n.º 19
0
def epsilon_plugprob(xtarget=None,
                     ytarget=None,
                     gang=None,
                     minavail=None,
                     mininblock=0,
                     maxinblock=24,
                     fiberused=None,
                     nmax=None,
                     limitDegree=0.8148,
                     toblock=None,
                     blockfile=None,
                     noycost=False,
                     ylimits=None,
                     reachfunc=None,
                     stretch=0,
                     observatory=None,
                     blockcenx=None,
                     blockceny=None,
                     gangcheck=True):
    """Test AS4 epsilon plugging problem

    Parameters
    ----------
    xtarget, ytarget : np.float32
    ndarray with target locations in degrees

    Returns
    -------
    fiberid
    ndarray with fiber IDs for each target
    toblock
           ndarray with block IDs for each target

    Notes
    -----
    Calls write_plugprob.c in libdimage.so
    """

    assert len(xtarget) == len(ytarget)

    if (toblock is None):
        toblock = np.zeros(len(xtarget), dtype=np.int32)

    if (nmax is None):
        nmax = len(xtarget)

    if (minavail is None):
        if (maxinblock < 8):
            minavail = maxinblock
        else:
            minavail = 8

    if (blockfile is None):
        blockfile = os.path.join(os.getenv('PLATEDESIGN_DIR'), 'data',
                                 'apogee', 'fiberBlocksAPOGEEepsilon.par')

    if (observatory is None):
        observatory = Observatory(name='APO')

    fiberblocks = yanny.yanny(blockfile)
    fiberlist = fiberblocks['TIFIBERBLOCK']
    nblocks = fiberlist['blockid'].max()

    # Set block centers
    blockconstrain = True
    if (blockcenx is None or blockceny is None):
        blockcenx = np.zeros(nblocks, dtype=np.float64)
        blockceny = np.zeros(nblocks, dtype=np.float64)
        for block in (np.arange(nblocks) + 1):
            ib = np.nonzero(fiberlist['blockid'] == block)[0]
            blockcenx[block - 1] = np.mean(fiberlist['fibercenx'][ib])
            blockceny[block - 1] = np.mean(fiberlist['fiberceny'][ib])
        blockconstrain = False

    if (ylimits is None):
        ylimits = np.zeros((nblocks, 2), dtype=np.float64)
        ylimits[:, 0] = -10.
        ylimits[:, 1] = 10.

    xfiber = np.array(fiberlist['fibercenx'], dtype=np.float64)
    yfiber = np.array(fiberlist['fiberceny'], dtype=np.float64)
    fiberblockid = np.array(fiberlist['blockid'], dtype=np.int32)
    fgang = np.array(fiberlist['gang'], dtype=np.str_)

    xtarget_deg = xtarget / observatory.platescale
    ytarget_deg = ytarget / observatory.platescale

    used = np.zeros(len(xfiber), dtype=np.int32)
    if (fiberused is not None):
        used[fiberused - 1] = 1

    fiberTargetsPossible = None
    fiberTargetsPossible = np.zeros((len(xtarget), len(xfiber)),
                                    dtype=np.int32)
    for j in np.arange(len(xfiber)):
        if (gangcheck):
            igang = np.nonzero(gang == fgang[j])[0]
        else:
            igang = np.arange(len(xtarget))
        reachable = boss_reachcheck(xfiber[j] * observatory.platescale,
                                    yfiber[j] * observatory.platescale,
                                    xtarget[igang],
                                    ytarget[igang],
                                    stretch=stretch)
        fiberTargetsPossible[igang, j] = reachable

    probfile = tempfile.mkstemp()[1]

    write_plugprob(xtarget=xtarget_deg,
                   ytarget=ytarget_deg,
                   xfiber=xfiber,
                   yfiber=yfiber,
                   fiberblockid=fiberblockid,
                   used=used,
                   toblock=toblock,
                   nMax=None,
                   limitDegree=limitDegree,
                   fiberTargetsPossible=fiberTargetsPossible,
                   minAvailInBlock=0,
                   minFibersInBlock=mininblock,
                   maxFibersInBlock=maxinblock,
                   blockcenx=blockcenx,
                   blockceny=blockceny,
                   blockconstrain=blockconstrain,
                   probfile=probfile,
                   noycost=noycost,
                   blockylimits=ylimits)

    ansfile = tempfile.mkstemp()[1]

    pfp = open(probfile, mode="r")
    afp = open(ansfile, mode="w")

    cs2_path = os.path.join(os.getenv('PLATEDESIGN_DIR'), 'src', 'cs2', 'cs2')
    subprocess.call(cs2_path, stdin=pfp, stdout=afp)

    afp.close()
    pfp.close()

    (targetFiber, targetBlock) = read_plugprob(xtarget=xtarget_deg,
                                               ytarget=ytarget_deg,
                                               fiberblockid=fiberblockid,
                                               ansfile=ansfile)

    # os.remove(ansfile)
    # os.remove(probile)

    return (targetFiber, targetBlock, fiberTargetsPossible)
Ejemplo n.º 20
0
def fanuc(mode='boss', planfile=None):
    """Create plFanuc file for plates

    Parameters
    ----------
    planfile : str
        name of plan file (e.g. 'plPlan-test.par')
    mode : str
        plate run type ('boss', 'manga', or 'apogee_south'; default 'boss')

    Notes
    -----

    Creates plFanucUnadjusted files with CNC code for drilling SDSS plates.

    """

    # Read in files
    plan = yanny.yanny(planfile)
    parametersdir = plan['parametersDir']
    paramfile = os.path.join(parametersdir, plan['parameters'])
    param = yanny.yanny(paramfile)
    obs = yanny.yanny(plan['plObsFile'])

    # Set up templates for CNC code
    gcodes = Gcodes(mode=mode, paramfile=paramfile,
                    parametersdir=parametersdir)

    # Loop through the plates
    plug_dir = plan['outFileDir']
    plug_template = 'plPlugMapP-{plate}.par'
    fanuc_template = 'plFanucUnadjusted-{plate}.par' + post_str
    png_template = 'plFanucUnadjusted-{plate}.png'
    for plate in obs['PLOBS']:
        print("Making files for plate {plateid}".format(plateid=plate['plateId']))
        plug_name = plug_template.format(plate=plate['plateId'])
        fanuc_name = fanuc_template.format(plate=plate['plateId'])
        png_name = png_template.format(plate=plate['plateId'])

        # Read in plug map file
        plugmap = yanny.yanny(os.path.join(plug_dir, plug_name))

        # Make plDrillPos file
        _fanuc_drillpos(gcodes=gcodes, plugmap=plugmap,
                        plate=plate, param=param)

        # Check overlapping holes
        ok = _fanuc_check(plateid=plate['plateId'])
        if(ok is False):
            print(" ... overlapping holes! not making holes for {plate}".format(plate=plate['plateId']))
            break

        # Separate into types for sorting
        pmaps = _fanuc_separate(plugmap)

        # Some checks
        if((len(pmaps['acquisition_center']) > 0) &
           (mode != 'apogee_south')):
            print("Not an apogee_south mode plate, but central acquisition specified")
            sys.exit(1)
        if((len(pmaps['acquisition_offaxis']) > 0) &
           (mode != 'apogee_south')):
            print("Not an apogee_south mode plate, but offaxis acquisition specified")
            sys.exit(1)
        if(len(pmaps['acquisition_offaxis']) > 1):
            print("More than one offaxis camera specified")
            sys.exit(1)
        if(len(pmaps['acquisition_center']) > 1):
            print("More than one center camera specified")
            sys.exit(1)
        if((len(pmaps['acquisition_center']) == 0) &
           (mode == 'apogee_south')):
            print("No center camera specified")
            sys.exit(1)

        # Open Fanuc file and write header
        ffp = open(fanuc_name, 'w')
        hdr = _fanuc_header(gcodes=gcodes, plate=plate, param=param,
                            fanuc_name=fanuc_name, plug_name=plug_name)
        ffp.write(hdr)

        # Open drilling path PNG
        plt.figure(dpi=150, figsize=(5, 5))
        plt.title("plate {plateId}".format(plateId=plate['plateId']))
        plt.xlim((-405., 485.))
        plt.ylim((-405., 485.))
        plt.xlabel("X drill (mm)")
        plt.ylabel("Y drill (mm)")
        
        # Plot circle at limit
        rlimit = np.float32(param['maxRadius'])
        theta = np.arange(1000) / 999. * np.pi * 2.
        xlimit = rlimit * np.cos(theta)
        ylimit = rlimit * np.sin(theta)
        plt.plot(xlimit, ylimit, color='black')

        # Loop through plug map hole types
        linecolor = dict()
        linecolor['objects'] = 'red'
        linecolor['lighttrap'] = 'blue'
        linecolor['alignment'] = 'green'
        linecolor['manga'] = 'cyan'
        linecolor['manga_alignment'] = 'magenta'
        for holetype in pmaps.keys():
            pmap = pmaps[holetype]
            if((len(pmap) > 0) &
               (holetype != 'acquisition_center') &
               (holetype != 'acquisition_offaxis')):
                ffp.write(getattr(gcodes, holetype))
                iorder = optimize_path(pmap['xFocal'], pmap['yFocal'])
                (x, y, z, zr, zo) = _fanuc_xyz(plugmap=pmap[iorder],
                                               plate=plate, param=param)
                length = _fanuc_length(x, y)
                codes, xpath, ypath = _fanuc_codes(gcodes=gcodes, x=x, y=y,
                                                   z=z, zr=zr, objId=pmap['objId'][iorder],
                                                   holetype=holetype, rlimit=rlimit)
                ffp.write(codes)
                label = "{holetype} holes ({length:.1f} m)"
                label = label.format(holetype=holetype,
                                     length=length / 1000.)
                _plot_path(x, y, xpath, ypath, color=linecolor[holetype], label=label)

        # Write completion
        (ax, ay, az, azr, azo) = _fanuc_xyz(plugmap=pmaps['acquisition_offaxis'],
                                            plate=plate, param=param)
        if(len(ax) > 0):
            ax = ax[0]
            ay = ay[0]
        else:
            ax = None
            ay = None

        completion = gcodes.completion(plateId=plate['plateId'],
                                       axy=(ax, ay))
        ffp.write(completion)
        ffp.write("%\n")

        ffp.close()

        plt.legend(fontsize=6)
        plt.savefig(png_name)
        plt.close()
Ejemplo n.º 21
0
def _fanuc_check(plateid=None):
    """Read in plDrillPos file and make sure holes do not overlap

    Parameters
    ----------
    plateid : np.int32, int
        plate ID

    Returns
    -------
    ok : boolean
        True if OK, False if not

    Notes 
    -----
    Assumes 5 arcmin is biggest thing it needs to check

    For the off-axis acquisition camera at LCO,
     it assumes a 55 x 40 mm footprint.
    """
    offaxis_xsize = 55.
    offaxis_ysize = 40.
    drillpos_template = 'plDrillPos-{plate}.par' + post_str
    drillpos_name = drillpos_template.format(plate=plateid)
    dpos = yanny.yanny(drillpos_name)
    (m1, m2, d12) = spheregroup.spherematch(dpos['DRILLPOS']['ra'],
                                            dpos['DRILLPOS']['dec'],
                                            dpos['DRILLPOS']['ra'],
                                            dpos['DRILLPOS']['dec'],
                                            5. / 60., maxmatch=0)
    for indx1, indx2 in zip(m1, m2):
        if(indx1 != indx2):
            dx = (dpos['DRILLPOS']['xDrill'][indx1] -
                  dpos['DRILLPOS']['xDrill'][indx2])
            dy = (dpos['DRILLPOS']['yDrill'][indx1] -
                  dpos['DRILLPOS']['yDrill'][indx2])
            d12 = np.sqrt(dx**2 + dy**2)
            limit = 0.5 * (dpos['DRILLPOS']['holeDiam'][indx1] +
                           dpos['DRILLPOS']['holeDiam'][indx2])
            if(d12 < limit):
                conflict = True
                holeType1 = dpos['DRILLPOS']['holeType'][indx1].decode()
                holeType2 = dpos['DRILLPOS']['holeType'][indx2].decode()
                # Special case for acquisition camera
                if(holeType1 == "ACQUISITION_OFFAXIS" or
                   holeType2 == "ACQUISITION_OFFAXIS"):
                    conflict = False
                    x1 = dpos['DRILLPOS']['xDrill'][indx1]
                    x2 = dpos['DRILLPOS']['xDrill'][indx2]
                    y1 = dpos['DRILLPOS']['yDrill'][indx1]
                    y2 = dpos['DRILLPOS']['yDrill'][indx2]
                    if(holeType1 == "ACQUISITION_OFFAXIS"):
                        holeDiam = dpos['DRILLPOS']['holeDiam'][indx2]
                    else:
                        holeDiam = dpos['DRILLPOS']['holeDiam'][indx1]
                    if(np.abs(x1 - x2) < 0.5 * (offaxis_xsize + holeDiam) and
                       np.abs(y1 - y2) < 0.5 * (offaxis_ysize + holeDiam)):
                        conflict = True
                if(conflict is True):
                    return(False)
    return(True)
Ejemplo n.º 22
0
def pca_star(**kwargs):
    """Wrapper on pca_solve to handle stellar eigenspectra.
    """
    import os
    import os.path
    import pickle
    import pylab
    from astropy.io import fits as pyfits
    import numpy as np
    import pydl.pydlutils.yanny as yanny
    from matplotlib.font_manager import fontManager, FontProperties
    from ... import uniq
    from ...pydlutils.goddard.astro import get_juldate
    from ...pydlutils.image import djs_maskinterp
    from . import pca_solve, readspec, skymask
    if 'inputfile' in kwargs:
        inputfile = kwargs['inputfile']
    else:
        inputfile = os.path.join(os.getenv('IDLSPEC2D_DIR'),
            'templates','eigeninput_star.par')
    wavemin = 0
    wavemax = 0
    snmax = 100.0
    if 'niter' in kwargs:
        niter = kwargs['niter']
    else:
        niter = 10
    cspeed = 2.99792458e5
    #
    # Name the output files.
    #
    jd = get_juldate()
    outfile = "spEigenStar-{0:d}".format(int(jd - 2400000.5))
    #
    # Read the input spectra
    #
    par = yanny.yanny(inputfile,np=True)
    slist = par['EIGENOBJ']
    spplate = readspec(slist['plate'],slist['fiberid'],mjd=slist['mjd'],align=True,**kwargs)
    objdloglam = spplate['loglam'][0,1] - spplate['loglam'][0,0]
    #
    # Insist that all of the requested spectra exist.
    #
    missing = spplate['plugmap']['FIBERID'] == 0
    if missing.any():
        imissing = missing.nonzero()[0]
        for k in imissing:
            print("Missing plate={0:d} mjd={1:d} fiber={2:d}".format(plate[k],mjd[k],fiber[k]))
        raise ValueError("{0:d} missing object(s).".format(missing.sum()))
    #
    # Do not fit where the spectrum may be dominated by sky-sub residuals.
    #
    objinvvar = skymask(spplate['invvar'],spplate['andmask'],spplate['ormask'])
    ifix = spplate['flux']**2 * objinvvar > snmax**2
    if ifix.any():
        objinvvar[ifix.nonzero()] = (snmax/spplate['flux'][ifix.nonzero()])**2
    #
    # Find the list of unique star types
    #
    isort = np.argsort(slist['class'])
    classlist = slist['class'][isort[uniq(slist['class'][isort])]]
    #
    # Loop over each star type
    #
    fullflux = None
    namearr = list()
    colorvec = ['k','r','g','b','m','c']
    smallfont = FontProperties(size='xx-small');
    for c in classlist:
        #
        # Find the subclasses for this stellar type
        #
        print("Finding eigenspectra for Stellar class {0}".format(c))
        indx = (slist['class'] == c).nonzero()[0]
        nindx = indx.size
        thesesubclass = slist['subclass'][indx]
        isort = np.argsort(thesesubclass)
        subclasslist = thesesubclass[isort[uniq(thesesubclass[isort])]]
        nsubclass = subclasslist.size
        #
        # Solve for 2 eigencomponents if we have specified subclasses for
        # this stellar type
        #
        if nsubclass == 1:
            nkeep = 1
        else:
            nkeep = 2
        newloglam = spplate['loglam'][0,:]
        pcaflux = pca_solve(spplate['flux'][indx,:],objinvvar[indx,:],
            spplate['loglam'][indx,:], slist['cz'][indx]/cspeed, wavemin=wavemin,
            wavemax=wavemax, niter=niter, nkeep=nkeep, newloglam=newloglam)
        #
        # Interpolate over bad flux values in the middle of a spectrum,
        # and set fluxes to zero at the blue+red ends of the spectrum
        #
        # minuse = 1 # ?
        minuse = np.floor((nindx+1) / 3.0)
        qbad = pcaflux['usemask'] < minuse
        #
        # Interpolate over all bad pixels
        #
        for j in range(nkeep):
            pcaflux['flux'][j,:] = djs_maskinterp(pcaflux['flux'][j,:],qbad,const=True)
        #
        # Set bad pixels at the very start or end of the spectrum to zero instead
        #
        npix = qbad.size
        igood = (~qbad).nonzero()[0]
        if qbad[0]:
            pcaflux['flux'][:,0:igood[0]-1] = 0
        if qbad[npix-1]:
            pcaflux['flux'][:,igood[::-1][0]+1:npix] = 0
        #
        # Re-normalize the first eigenspectrum to a mean of 1
        #
        # print(pcaflux['flux'].dtype)
        norm = pcaflux['flux'][0,:].mean()
        pcaflux['flux'] /= norm
        pcaflux['acoeff'] *= norm
        # print(pcaflux['flux'].dtype)
        #
        # Now loop through each stellar subclass and reconstruct
        # an eigenspectrum for that subclass
        #
        thesesubclassnum = np.zeros(thesesubclass.size,dtype='i4')
        #
        # Create a figure for this class
        #
        fig = pylab.figure(dpi=100)
        ax = fig.add_subplot(111)
        for isub in range(nsubclass):
            ii = (thesesubclass == subclasslist[isub]).nonzero()[0]
            thesesubclassnum[ii] = isub
            if nkeep == 1:
                thisflux = pcaflux['flux'].reshape(pcaflux['newloglam'].shape)
                # print(thisflux.dtype)
            else:
                aratio = pcaflux['acoeff'][ii,1]/pcaflux['acoeff'][ii,0]
                #
                # np.median(foo) is equivalent to MEDIAN(foo,/EVEN)
                #
                thisratio = np.median(aratio)
                thisflux = pcaflux['flux'][0,:] + thisratio.astype('f') * pcaflux['flux'][1,:]
                # print(thisflux.dtype)
            if fullflux is None:
                fullflux = thisflux
            else:
                fullflux = np.vstack((fullflux,thisflux))
            namearr.append(subclasslist[isub])
            #
            # Plot spectra
            #
            plotflux = thisflux/thisflux.max()
            # print(pcaflux['newloglam'].shape)
            # print(plotflux.shape)
            ax.plot(10.0**pcaflux['newloglam'],plotflux,"{0}-".format(colorvec[isub%len(colorvec)]),linewidth=1)
            if isub == 0:
                ax.set_xlabel(r'Wavelength [$\AA$]')
                ax.set_ylabel('Flux [arbitrary units]')
                ax.set_title('STAR {0}: Eigenspectra Reconstructions'.format(c))
            t = ax.text(10.0**pcaflux['newloglam'][-1],plotflux[-1],subclasslist[isub],
                horizontalalignment='right',verticalalignment='center',
                color=colorvec[isub%len(colorvec)],fontproperties=smallfont)
        fig.savefig(outfile+'.{0}.png'.format(c))
        pylab.close(fig)
        #
        # Plot eigenvalue ratios if nkeep > 1
        #
        if nkeep > 1:
            fig = pylab.figure(dpi=100)
            ax = fig.add_subplot(111)
            allratio = pcaflux['acoeff'][:,1]/pcaflux['acoeff'][:,0]
            isort = thesesubclassnum.argsort()
            p = ax.plot(thesesubclassnum[isort],allratio[isort],marker='None',linestyle='None')
            for k in range(len(indx)):
                t = ax.text(thesesubclassnum[isort[k]],allratio[isort[k]],
                    "%04d-%04d"%(slist['plate'][indx[isort[k]]],slist['fiberid'][indx[isort[k]]]),
                    horizontalalignment='center', verticalalignment='center',
                    color=colorvec[k%len(colorvec)],
                    fontproperties=smallfont)
            ax.set_xlabel('Subclass')
            ax.set_xticks(np.arange(nsubclass))
            ax.set_xticklabels(subclasslist)
            ax.set_ylabel('Eigenvalue Ratio, $a_1/a_0$')
            ax.set_title('STAR {0}: Eigenvalue Ratios'.format(c))
            fig.savefig(outfile+'.{0}.ratio.png'.format(c))
            pylab.close(fig)
    #
    # Save output to FITS file.
    #
    if os.path.exists(outfile+'.fits'):
        os.remove(outfile+'.fits')
    hdu0 = pyfits.PrimaryHDU(fullflux)
    hdu1 = pyfits.new_table(pyfits.ColDefs([
        pyfits.Column(name='plate',format='J',array=slist['plate']),
        pyfits.Column(name='mjd',format='J',array=slist['mjd']),
        pyfits.Column(name='fiber',format='J',array=slist['fiberid']),
        pyfits.Column(name='redshift',format='D',unit='km/s',array=slist['cz'])]))
    hdulist = pyfits.HDUList([hdu0,hdu1])
    hdulist[0].header.update('OBJECT','STAR')
    hdulist[0].header.update('COEFF0',pcaflux['newloglam'][0])
    hdulist[0].header.update('COEFF1',objdloglam)
    hdulist[0].header.update('IDLUTILS','pydlutils','Version of idlutils')
    hdulist[0].header.update('SPEC2D','eigenspectra','Version of idlspec2d')
    hdulist[0].header.update('RUN2D',os.getenv('RUN2D'),'Version of 2d reduction')
    hdulist[0].header.update('RUN1D',os.getenv('RUN1D'),'Version of 1d reduction')
    for i in range(len(namearr)):
        hdulist[0].header.update("NAME%d" % i,namearr[i]+' ')
    hdulist[1].header.update('FILENAME',inputfile)
    hdulist.writeto(outfile+'.fits')
    # plot_eig(outfile+'.fits')
    return
Ejemplo n.º 23
0
def overlay_print(plateid,
                  numbers=False,
                  noguides=False,
                  renumber=False,
                  rotate180=False):
    '''
    This function returns a plate overlay as a pyx.document object.
    The calling script can determine what to do with it. To print this
    object as a PDF:

    overlay = overlay_print(plateid=12345)
    overlay.writePDFfile(destination_path)
    '''

    # Get the needed files
    plateHolesSorted_file = sdssPath.full('plateHolesSorted', plateid=plateid)
    if not os.path.isfile(plateHolesSorted_file):
        raise IOError("File not found: {0}".format(plateHolesSorted_file))

    platePlans_file = sdssPath.full('platePlans')
    if not os.path.isfile(platePlans_file):
        raise IOError("File not found: {0}".format(platePlans_file))

    # Read in holes data, extract meta data
    holes_yanny = yanny.yanny(plateHolesSorted_file)
    racen = holes_yanny['raCen']
    deccen = holes_yanny['decCen']
    designid = holes_yanny['designid']
    ha = holes_yanny['ha']
    holes = holes_yanny['STRUCT1']

    if (rotate180):
        holes['xfocal'] = -np.array(holes['xfocal'])
        holes['yfocal'] = -np.array(holes['yfocal'])

    # Read in plans
    plans = yanny.yanny(platePlans_file)
    iplan = np.nonzero(np.array(plans['PLATEPLANS']['plateid']) == plateid)[0]
    survey = plans['PLATEPLANS']['survey'][iplan[0]].decode()
    programname = plans['PLATEPLANS']['programname'][iplan[0]].decode()

    # Texify the string
    programname_str = str(programname)
    programname_str = re.sub("_", "\_", programname_str)

    # Create information string
    information = r"\font\myfont=cmr10 at 40pt {\myfont"
    information += " Plate=" + str(plateid) + "; "
    information += "Design=" + str(designid) + "; "
    information += "Survey=" + str(survey) + "; "
    information += "Program=" + str(programname_str) + "; "
    information += "RA=" + str(racen) + "; "
    information += "Dec=" + str(deccen) + "; "
    information += "HA=" + str(np.float32((ha.split())[0])) + "."
    information += "}"

    # Create message
    message = "Una placa para APOGEE Sur"
    message_tex = r"\font\myfont=cmr10 at 40pt {\myfont " + message + "}"

    apogee = apogee_layer(holes, numbers=numbers, renumber=renumber)
    if (noguides is False):
        guide = guide_layer(holes)
    else:
        guide = None
    whiteout = whiteout_layer(holes)
    plate_circle = plate_circle_layer(plateid, information, message_tex)
    outline = outline_layer()  # pyx.canvas object
    acquisition = acquisition_layer(holes)

    outline.insert(apogee)
    if (guide is not None):
        outline.insert(guide)
    outline.insert(plate_circle)
    if (acquisition is not None):
        outline.insert(acquisition)
    outline.insert(whiteout)

    pformat = document.paperformat(limit_radius * 2., limit_radius * 2.)
    final = document.page(outline, paperformat=pformat)

    return document.document(pages=[final])
Ejemplo n.º 24
0
def pca_star(**kwargs):
    """Wrapper on pca_solve to handle stellar eigenspectra.
    """
    import os
    import os.path
    import pickle
    import pylab
    from astropy.io import fits as pyfits
    import numpy as np
    import pydl.pydlutils.yanny as yanny
    from matplotlib.font_manager import fontManager, FontProperties
    from ... import uniq
    from ...pydlutils.goddard.astro import get_juldate
    from ...pydlutils.image import djs_maskinterp
    from . import pca_solve, readspec, skymask
    if 'inputfile' in kwargs:
        inputfile = kwargs['inputfile']
    else:
        inputfile = os.path.join(os.getenv('IDLSPEC2D_DIR'), 'templates',
                                 'eigeninput_star.par')
    wavemin = 0
    wavemax = 0
    snmax = 100.0
    if 'niter' in kwargs:
        niter = kwargs['niter']
    else:
        niter = 10
    cspeed = 2.99792458e5
    #
    # Name the output files.
    #
    jd = get_juldate()
    outfile = "spEigenStar-{0:d}".format(int(jd - 2400000.5))
    #
    # Read the input spectra
    #
    par = yanny.yanny(inputfile, np=True)
    slist = par['EIGENOBJ']
    spplate = readspec(slist['plate'],
                       slist['fiberid'],
                       mjd=slist['mjd'],
                       align=True,
                       **kwargs)
    objdloglam = spplate['loglam'][0, 1] - spplate['loglam'][0, 0]
    #
    # Insist that all of the requested spectra exist.
    #
    missing = spplate['plugmap']['FIBERID'] == 0
    if missing.any():
        imissing = missing.nonzero()[0]
        for k in imissing:
            print("Missing plate={0:d} mjd={1:d} fiber={2:d}".format(
                plate[k], mjd[k], fiber[k]))
        raise ValueError("{0:d} missing object(s).".format(missing.sum()))
    #
    # Do not fit where the spectrum may be dominated by sky-sub residuals.
    #
    objinvvar = skymask(spplate['invvar'], spplate['andmask'],
                        spplate['ormask'])
    ifix = spplate['flux']**2 * objinvvar > snmax**2
    if ifix.any():
        objinvvar[ifix.nonzero()] = (snmax /
                                     spplate['flux'][ifix.nonzero()])**2
    #
    # Find the list of unique star types
    #
    isort = np.argsort(slist['class'])
    classlist = slist['class'][isort[uniq(slist['class'][isort])]]
    #
    # Loop over each star type
    #
    fullflux = None
    namearr = list()
    colorvec = ['k', 'r', 'g', 'b', 'm', 'c']
    smallfont = FontProperties(size='xx-small')
    for c in classlist:
        #
        # Find the subclasses for this stellar type
        #
        print("Finding eigenspectra for Stellar class {0}".format(c))
        indx = (slist['class'] == c).nonzero()[0]
        nindx = indx.size
        thesesubclass = slist['subclass'][indx]
        isort = np.argsort(thesesubclass)
        subclasslist = thesesubclass[isort[uniq(thesesubclass[isort])]]
        nsubclass = subclasslist.size
        #
        # Solve for 2 eigencomponents if we have specified subclasses for
        # this stellar type
        #
        if nsubclass == 1:
            nkeep = 1
        else:
            nkeep = 2
        newloglam = spplate['loglam'][0, :]
        pcaflux = pca_solve(spplate['flux'][indx, :],
                            objinvvar[indx, :],
                            spplate['loglam'][indx, :],
                            slist['cz'][indx] / cspeed,
                            wavemin=wavemin,
                            wavemax=wavemax,
                            niter=niter,
                            nkeep=nkeep,
                            newloglam=newloglam)
        #
        # Interpolate over bad flux values in the middle of a spectrum,
        # and set fluxes to zero at the blue+red ends of the spectrum
        #
        # minuse = 1 # ?
        minuse = np.floor((nindx + 1) / 3.0)
        qbad = pcaflux['usemask'] < minuse
        #
        # Interpolate over all bad pixels
        #
        for j in range(nkeep):
            pcaflux['flux'][j, :] = djs_maskinterp(pcaflux['flux'][j, :],
                                                   qbad,
                                                   const=True)
        #
        # Set bad pixels at the very start or end of the spectrum to zero
        # instead.
        #
        npix = qbad.size
        igood = (~qbad).nonzero()[0]
        if qbad[0]:
            pcaflux['flux'][:, 0:igood[0] - 1] = 0
        if qbad[npix - 1]:
            pcaflux['flux'][:, igood[::-1][0] + 1:npix] = 0
        #
        # Re-normalize the first eigenspectrum to a mean of 1
        #
        # print(pcaflux['flux'].dtype)
        norm = pcaflux['flux'][0, :].mean()
        pcaflux['flux'] /= norm
        pcaflux['acoeff'] *= norm
        # print(pcaflux['flux'].dtype)
        #
        # Now loop through each stellar subclass and reconstruct
        # an eigenspectrum for that subclass
        #
        thesesubclassnum = np.zeros(thesesubclass.size, dtype='i4')
        #
        # Create a figure for this class
        #
        fig = pylab.figure(dpi=100)
        ax = fig.add_subplot(111)
        for isub in range(nsubclass):
            ii = (thesesubclass == subclasslist[isub]).nonzero()[0]
            thesesubclassnum[ii] = isub
            if nkeep == 1:
                thisflux = pcaflux['flux'].reshape(pcaflux['newloglam'].shape)
                # print(thisflux.dtype)
            else:
                aratio = pcaflux['acoeff'][ii, 1] / pcaflux['acoeff'][ii, 0]
                #
                # np.median(foo) is equivalent to MEDIAN(foo,/EVEN)
                #
                thisratio = np.median(aratio)
                thisflux = (pcaflux['flux'][0, :] +
                            thisratio.astype('f') * pcaflux['flux'][1, :])
                # print(thisflux.dtype)
            if fullflux is None:
                fullflux = thisflux
            else:
                fullflux = np.vstack((fullflux, thisflux))
            namearr.append(subclasslist[isub])
            #
            # Plot spectra
            #
            plotflux = thisflux / thisflux.max()
            # print(pcaflux['newloglam'].shape)
            # print(plotflux.shape)
            ax.plot(10.0**pcaflux['newloglam'],
                    plotflux,
                    "{0}-".format(colorvec[isub % len(colorvec)]),
                    linewidth=1)
            if isub == 0:
                ax.set_xlabel(r'Wavelength [$\AA$]')
                ax.set_ylabel('Flux [arbitrary units]')
                ax.set_title(
                    'STAR {0}: Eigenspectra Reconstructions'.format(c))
            t = ax.text(10.0**pcaflux['newloglam'][-1],
                        plotflux[-1],
                        subclasslist[isub],
                        horizontalalignment='right',
                        verticalalignment='center',
                        color=colorvec[isub % len(colorvec)],
                        fontproperties=smallfont)
        fig.savefig(outfile + '.{0}.png'.format(c))
        pylab.close(fig)
        #
        # Plot eigenvalue ratios if nkeep > 1
        #
        if nkeep > 1:
            fig = pylab.figure(dpi=100)
            ax = fig.add_subplot(111)
            allratio = pcaflux['acoeff'][:, 1] / pcaflux['acoeff'][:, 0]
            isort = thesesubclassnum.argsort()
            p = ax.plot(thesesubclassnum[isort],
                        allratio[isort],
                        marker='None',
                        linestyle='None')
            for k in range(len(indx)):
                t = ax.text(thesesubclassnum[isort[k]],
                            allratio[isort[k]],
                            "%04d-%04d" % (slist['plate'][indx[isort[k]]],
                                           slist['fiberid'][indx[isort[k]]]),
                            horizontalalignment='center',
                            verticalalignment='center',
                            color=colorvec[k % len(colorvec)],
                            fontproperties=smallfont)
            ax.set_xlabel('Subclass')
            ax.set_xticks(np.arange(nsubclass))
            ax.set_xticklabels(subclasslist)
            ax.set_ylabel('Eigenvalue Ratio, $a_1/a_0$')
            ax.set_title('STAR {0}: Eigenvalue Ratios'.format(c))
            fig.savefig(outfile + '.{0}.ratio.png'.format(c))
            pylab.close(fig)
    #
    # Save output to FITS file.
    #
    if os.path.exists(outfile + '.fits'):
        os.remove(outfile + '.fits')
    hdu0 = pyfits.PrimaryHDU(fullflux)
    hdu1 = pyfits.new_table(
        pyfits.ColDefs([
            pyfits.Column(name='plate', format='J', array=slist['plate']),
            pyfits.Column(name='mjd', format='J', array=slist['mjd']),
            pyfits.Column(name='fiber', format='J', array=slist['fiberid']),
            pyfits.Column(name='redshift',
                          format='D',
                          unit='km/s',
                          array=slist['cz'])
        ]))
    hdulist = pyfits.HDUList([hdu0, hdu1])
    hdulist[0].header.update('OBJECT', 'STAR')
    hdulist[0].header.update('COEFF0', pcaflux['newloglam'][0])
    hdulist[0].header.update('COEFF1', objdloglam)
    hdulist[0].header.update('IDLUTILS', 'pydlutils', 'Version of idlutils')
    hdulist[0].header.update('SPEC2D', 'eigenspectra', 'Version of idlspec2d')
    hdulist[0].header.update('RUN2D', os.getenv('RUN2D'),
                             'Version of 2d reduction')
    hdulist[0].header.update('RUN1D', os.getenv('RUN1D'),
                             'Version of 1d reduction')
    for i in range(len(namearr)):
        hdulist[0].header.update("NAME%d" % i, namearr[i] + ' ')
    hdulist[1].header.update('FILENAME', inputfile)
    hdulist.writeto(outfile + '.fits')
    # plot_eig(outfile+'.fits')
    return