Beispiel #1
0
    def addGuideOffsets(self, cmd, plate, wavelength, pointingID, gprobes):
        """
        Read in the new (needed for APOGEE/MARVELS) plateGuideOffsets interpolation arrays.
        """

        # Get .par file name in the platelist product.
        # plates/0046XX/004671/plateGuideOffsets-004671-p1-l16600.par
        path = os.path.join(os.environ['PLATELIST_DIR'], 'plates',
                            '%04dXX' % (int(plate / 100)), '%06d' % (plate),
                            'plateGuideOffsets-%06d-p%d-l%05d.par' %
                            (plate, pointingID, wavelength))
        if not os.path.exists(path):
            failMsg = ('text="no refraction corrections for '
                       'plate {0} at {1:d}A"'.format(plate, wavelength))
            cmd.error(failMsg)
            return False

        try:
            ygo = yanny.yanny(path, np=True)
            guideOffsets = ygo['HAOFFSETS']
            cmd.inform('text="loaded guider coeffs for %dA from %s"' %
                       (wavelength, path))
        except Exception, e:
            cmd.error('text="failed to read plateGuideOffsets file %s: %s"' %
                      (path, e))
            return False
Beispiel #2
0
def get_platePlans(reload=False):
    """Returns a (cached) copy of platePlans."""

    global PLATE_PLANS

    if PLATE_PLANS is None or reload:
        PLATE_PLANS = yanny.yanny(get_path('platePlans'), np=True)['PLATEPLANS']

    return PLATE_PLANS
 def __init__(self, plPlugMapFile, scanDir, cartID, fscanID, fscanMJD):
     self.scanDir = scanDir
     self.cartID = cartID
     self.fscanID = fscanID
     self.fscanMJD = fscanMJD
     self.plPlugMap = yanny(filename=plPlugMapFile, np=True)
     self.objectInds = numpy.argwhere(self.plPlugMap["PLUGMAPOBJ"]["holeType"]=="OBJECT")
     self.xPos = self.plPlugMap["PLUGMAPOBJ"]["xFocal"][self.objectInds].flatten()
     self.yPos = self.plPlugMap["PLUGMAPOBJ"]["yFocal"][self.objectInds].flatten()
     self.radPos = numpy.sqrt(self.xPos**2+self.yPos**2)
     self.plateID = int(self.plPlugMap["plateId"])
 def updateHeader(self):
     """This is necessary because it seems calling plPlugMap.append(updateDict) erases
     the previously entered fiber map!  WTF?
     """
     updateDict = {
         "fscanMJD": int(self.fscanMJD),
         "fscanId": int(self.fscanID),
         "cartridgeId": int(self.cartID),
         "instruments": "APOGEE_SOUTH"
         }
     plPlugMap = yanny(filename=self.filePath, np=True)
     plPlugMap.append(updateDict)
     os.remove(self.filePath)
     plPlugMap.write(self.filePath)
Beispiel #5
0
def definition_from_id(def_id):
    """Returns a custom dictionary merging the defaults file with plateDefinition."""

    if def_id == -1:
        raise ValueError('SDSS I/II plates don\'t have definition files.')

    custom_defs_file = get_path('plateDefinition', designid=def_id)

    if not custom_defs_file or not os.path.exists(custom_defs_file):
        raise ValueError('cannot find plateDefinition file for design_id={0}.'.format(def_id))

    custom_defs = yanny.yanny(custom_defs_file)
    # Lowercases and removes symbols
    custom_defs_lower = dict((kk.lower(), vv) for kk, vv in custom_defs.items() if kk != 'symbols')

    # Gets defaults file
    assert 'platetype' in custom_defs_lower, \
        '\'platetype\' not defined in {0!r}'.format(custom_defs_file)
    assert 'platedesignversion' in custom_defs_lower, \
        '\'platedesignversion\' not defined in {0!r}'.format(custom_defs_file)

    defaults_file = get_path('plateDefault',
                             type=custom_defs_lower['platetype'],
                             version=custom_defs_lower['platedesignversion'])

    if not defaults_file or not os.path.exists(defaults_file):
        raise ValueError('cannot find plateDefaults file matching design_id={0}.'.format(def_id))

    definition = yanny.yanny(defaults_file)
    definition_lower = dict((kk.lower(), vv)
                            for kk, vv in definition.items() if kk != 'symbols')

    # Overrrides custom values in defaults
    definition_lower.update(custom_defs_lower)

    return definition_lower
def getMangaTileIDs():
    """Returns a dictionary of {plateid: manga_tileid} from the
    plateTargets files stored in mangacore."""

    mangacorePath = readPath(config['fields']['mangacore'])
    plateTargets = glob.glob(
        os.path.join(mangacorePath,
                     'platedesign/platetargets/plateTargets-*.par'))

    if len(plateTargets) == 0:
        raise TotoroError('no plateTargets files found.')

    mangaTileIDs = {}
    neverobserve = {}
    for plateTargetsFile in plateTargets:
        pT = yanny.yanny(plateTargetsFile, np=True)['PLTTRGT']
        for target in pT:
            if (target['plateid'] not in mangaTileIDs and
                    target['manga_tileid'] > 0):
                mangaTileIDs[target['plateid']] = target['manga_tileid']
            if target['plateid'] not in neverobserve:
                neverobserve[target['plateid']] = target['neverobserve']

    return mangaTileIDs, neverobserve
Beispiel #7
0
def plate_holes_db(inputs,
                   plate_mode=False,
                   verbose=False,
                   overwrite=False,
                   log=None):
    """Loads plateruns or plates from plateHoles into the DB.

    Parameters:
        inputs (list, tuple):
            A list of plateruns or plates to be ingested into the DB.
        plate_mode (bool):
            If ``True``, treats ``inputs`` as a list of plates.
            Otherwise assumes they are plateruns.
        verbose (bool):
            If ``True`` outputs more information in the shell log.
        overwrite (bool):
            If ``True``, values in the DB will be overwritten if needed.
        log (``platedesign.core.logger.Logger`` object):
            A ``Logger`` object to use. Otherwise it will create a new log.

    """

    log = log or pd_log

    log.info('running plate_holes_db in mode={0!r}.'.format(
        'platerun' if not plate_mode else 'plate'))

    # Checks the connection
    conn_status = platedb.database.connected
    if conn_status:
        log.info('database connection is open.')
    else:
        raise RuntimeError(
            'cannot connect to the database. Review you connection settings.')

    # Creates a list of plates for each platerun and plate.
    plates = []
    if not plate_mode:
        for platerun in inputs:
            lines = utils.get_lines_for_platerun(platerun)
            if len(lines) == 0:
                raise ValueError(
                    'no platePlans lines found for platerun {0!r}'.format(
                        platerun))
            plates += lines['plateid'].tolist()
    else:
        plates = inputs

    if len(plates) == 0:
        raise ValueError('no plates found. Your input parameters '
                         'do not seem to match any plate.')

    log.info('loading plateHoles for {0} plates.'.format(len(plates)))

    with platedb.database.atomic():

        for plate_id in plates:

            plate_holes_path = utils.get_path('plateHoles', plateid=plate_id)
            plate_holes_filename = os.path.basename(plate_holes_path)

            assert os.path.exists(plate_holes_path), \
                'cannot find file {0}'.format(plate_holes_path)

            plate_holes = yanny.yanny(plate_holes_path, np=True)['STRUCT1']

            log.debug('loaded file {0}.'.format(plate_holes_filename))

            try:
                plate_dbo = platedb.Plate.get(plate_id=plate_id)
            except peewee.DoesNotExist:
                raise ValueError('Could not find plate {0} in the database. '
                                 'Has the plate been added?'.format(plate_id))

            # Populate the plate_holes_file table.
            phf_dbo, created = platedb.PlateHolesFile.get_or_create(
                filename=plate_holes_filename, plate_pk=plate_dbo.pk)

            if not created:
                log.debug(
                    'plateHoles file for plate {0} already in the database.'.
                    format(plate_id))
            else:
                log.debug(
                    'created PlateHolesFile for plate {0}'.format(plate_id))

            if created or overwrite:
                phf_dbo.save()

            if len(phf_dbo.plate_holes) > 0:
                if not overwrite:
                    log.debug(
                        'found plate holes already in the DB for plate {0}. '
                        'Skipping this plate.'.format(plate_id))
                    continue
                else:
                    warnings.warn(
                        'found plate holes in the DB for plate {0}. '
                        'Overwriting.'.format(plate_id), UserWarning)

            log.debug('removing any previous plate holes for plate {0}'.format(
                plate_id))
            ph_delete = platedb.PlateHole.delete().where(
                platedb.PlateHole.plate_holes_file_pk == phf_dbo.pk)
            ph_delete.execute()

            nn = 0
            for plate_hole in plate_holes:

                ph_dbo = platedb.PlateHole()
                ph_dbo.plate_holes_file = phf_dbo
                ph_dbo.xfocal = plate_hole['xfocal']
                ph_dbo.yfocal = plate_hole['yfocal']
                ph_dbo.tmass_h = plate_hole['tmass_h']
                ph_dbo.tmass_j = plate_hole['tmass_j']
                ph_dbo.tmass_k = plate_hole['tmass_k']
                ph_dbo.pointing_number = plate_hole['pointing']
                ph_dbo.apogee_target1 = plate_hole['apogee2_target1'] \
                    if 'apogee2_target1' in plate_hole.dtype.names else None
                ph_dbo.apogee_target2 = plate_hole['apogee2_target2'] \
                    if 'apogee2_target2' in plate_hole.dtype.names else None

                # Populate the rest of the plate_hole table from queries.
                try:
                    pht_dbo = platedb.PlateHoleType.get(
                        label=plate_hole['holetype'])
                    ph_dbo.plate_hole_type = pht_dbo
                except peewee.DoesNotExist:
                    raise RuntimeError(
                        'PlateHoleType {0!r} does not appear to exist in the DB. '
                        'Add it to the PlateHoleType table manually and load '
                        'this plate again.'.format(plate_hole['holetype']))

                try:
                    ot_dbo = platedb.ObjectType.get(
                        label=plate_hole['targettype'])
                    ph_dbo.objectType = ot_dbo
                except peewee.DoesNotExist:
                    raise RuntimeError(
                        'ObjectType {0!r} does not appear to exist in the DB. '
                        'Add it to the ObjectType table manually and load '
                        'this plate again.'.format(plate_hole['targettype']))
                nn += 1

                ph_dbo.save()

            log.info('loaded {0} plateHoles for plate {1}'.format(
                nn, plate_id))
def populate_obs_range(plates, verbose=False, log=None, ignore_missing=False):
    """Loads observing ranges into the DB.

    Parameters:
        plates (list, tuple):
            A list of plates for which to load or update the observing ranges.
        verbose (bool):
            If ``True`` outputs more information in the shell log.
        log (``platedesign.core.logger.Logger`` object):
            A ``Logger`` object to use. Otherwise it will create a new log.
        ignore_missing (bool):
            If ``True``, does not fail if a PlatePointing object cannot be
            found for the plate.

    """

    log = log or pd_log

    # Checks the connection
    conn_status = platedb.database.connected
    if conn_status:
        log.debug('database connection is open.')
    else:
        raise RuntimeError('cannot connect to the database. Review you connection settings.')

    # values we want to read
    keys_to_read = ['ha_observable_min',
                    'ha_observable_max',
                    'npointings',
                    'pointing_name']

    with platedb.database.atomic():

        for plate_id in sorted(plates):

            log.info('loading observing ranges for plate {0}.'.format(plate_id))

            pl_plugmap_p = utils.get_path('plPlugMapP', plateid=plate_id)

            assert os.path.exists(pl_plugmap_p), 'cannot find file {0}'.format(pl_plugmap_p)

            plugmap = yanny.yanny(pl_plugmap_p)

            for key in keys_to_read:
                assert key in plugmap, 'cannot find key {0} in {1}'.format(key, pl_plugmap_p)

            # determine the pointing number
            if int(plugmap['npointings']) == 1:
                pno = 1
            else:
                warnings.warn('skipping plate {0} that '
                              'has multiple pointings.'.format(plate_id), UserWarning)
                continue

            # select the correct value from the list
            ha_observable_min = float(plugmap['ha_observable_min'].split()[pno - 1])
            ha_observable_max = float(plugmap['ha_observable_max'].split()[pno - 1])

            # Fetch the plate_pointing object
            plate_pointing_dbo = platedb.PlatePointing.select().join(
                platedb.Plate).switch(platedb.PlatePointing).join(platedb.Pointing).where(
                    platedb.Plate.plate_id == plate_id, platedb.Pointing.pointing_no == pno)

            if plate_pointing_dbo.count() == 0:
                if not ignore_missing:
                    raise RuntimeError('Could not find PlatePointing for plate {0}. '
                                       'Has this been loaded into the db?'.format(plate_id))
                else:
                    continue
            elif plate_pointing_dbo.count() > 1:
                raise RuntimeError('Multiple plate pointings '
                                   'found for plate_id {0}'.format(plate_id))

            plate_pointing_dbo = plate_pointing_dbo.first()

            if np.any(exceptions['plateid'] == plate_id):
                warnings.warn('plate {0}: override value used.'.format(plate_id), UserWarning)
                row = exceptions[exceptions['plateid'] == plate_id][0]
                plate_pointing_dbo.ha_observable_min = row['ha_observable_min']
                plate_pointing_dbo.ha_observable_max = row['ha_observable_max']
            else:
                plate_pointing_dbo.ha_observable_min = ha_observable_min
                plate_pointing_dbo.ha_observable_max = ha_observable_max

            plate_pointing_dbo.save()
Beispiel #9
0
def doPointing(plateID, pointing, fscanIDs, nGuides=16):
    """Creates the plPlugMapM files for a specific pointing."""

    pointingName = '' if pointing == 'A' else pointing
    filename = os.path.join(
        os.environ['PLATELIST_DIR'], 'plates',
        '{0:06d}'.format(plateID)[:-2] + 'XX', '{0:06d}'.format(plateID),
        'plPlugMapP-{0:d}{1}.par'.format(plateID, pointingName))

    assert os.path.exists(filename)

    yannyFile = yanny.yanny(filename, np=True)
    rawFile = open(filename, 'r').read().splitlines()

    plPlugMapObj = yannyFile['PLUGMAPOBJ']

    # Manually retrieves the header from the raw lines.
    header = []
    for line in rawFile:
        if line.strip().startswith('typedef enum {'):
            break
        header.append(line)

    # Loops over fscanIDs
    for fscanID in fscanIDs:

        # Calculates the range of fiberIds that correspond to this pointing
        # and fscanID.
        pointingNum = string.uppercase.index(pointingName)
        preIndex = nGuides * 3 * pointingNum
        fiberID_range = preIndex + np.arange(1 + (fscanID - 1) * nGuides,
                                             1 + fscanID * nGuides)

        outFileName = 'plPlugMapM-{0}{3}-{1}-{2:02d}.par'.format(
            plateID, mjd, fscanID, pointingName)

        # Gets a list of the holes that we should keep for this fscanID
        validHoles = plPlugMapObj[
            (plPlugMapObj['holeType'] == 'LIGHT_TRAP') |
            (np.in1d(plPlugMapObj['fiberId'], fiberID_range))]

        # Gets the minimum fiberId greater than 0 for the valid holes.
        minFiberId = np.unique(np.sort(validHoles['fiberId']))[1] - 1

        # Renames the fiberIds to be in the range [1, 16]
        validHoles['fiberId'][validHoles['fiberId'] > 0] -= minFiberId

        enums = {
            'holeType': ['HOLETYPE', yannyFile._enum_cache['HOLETYPE']],
            'objType': ['OBJTYPE', yannyFile._enum_cache['OBJTYPE']]
        }
        yanny.write_ndarray_to_yanny(outFileName,
                                     validHoles,
                                     enums=enums,
                                     structname='PLUGMAPOBJ')

        # Replaces the guidenums with the slice used for this file
        for ii, line in enumerate(header):
            if line.startswith('guidenums' + str(pointingNum + 1)):
                guides = str('guidenums' + str(pointingNum + 1)) + ' ' + \
                    ' '.join(map(str, np.arange(1, nGuides + 1)))
                header[ii] = guides
                break

        addHeader(header, outFileName, plateID, fscanID)
Beispiel #10
0
    def makeApogeePlugMap(self, plugmap, newfilename, plate):
        """Return the plPlugMapM given a plateId and pointingName"""

        from sqlalchemy import and_

        # replace the \r with \n to get yanny to parse the text properly
        import re
        data = re.sub("\r", "\n", plugmap.file)

        # append to the standard plPlugMap to add 2mass_style and J, H, Ks mags
        par = yanny.yanny()
        par._contents = data
        par._parse()
        p0 = par

        # update the definition of PLUGMAPOBJ
        pos = 0
        for t in p0.tables():
            if t == 'PLUGMAPOBJ':
                p0['symbols']['struct'][pos] = (
                    p0['symbols']['struct'][pos]).replace(
                        'secTarget;', 'secTarget;\n char tmass_style[30];')
                break
            else:
                pos += 1

        p0['symbols']['PLUGMAPOBJ'].append('tmass_style')
        p0['PLUGMAPOBJ']['tmass_style'] = []
        for i in range(p0.size('PLUGMAPOBJ')):
            p0['PLUGMAPOBJ']['tmass_style'].append('-')

        # get the needed information from the plate_hole
        ph = self.mysession.query(Fiber).join(PlateHole).\
            filter(Fiber.pl_plugmap_m_pk==plugmap.pk).order_by(Fiber.fiber_id).\
            values('fiber_id','tmass_j','tmass_h','tmass_k','apogee_target1','apogee_target2')

        # SDSS-V plates
        if (plate >= 15000):

            # loop through the list and update the PLUGMAPOBJ
            tmass_style = 'Unknown'
            for fid, j_mag, h_mag, k_mag, t1, t2 in ph:
                count = p0['PLUGMAPOBJ']['fiberId'].count(fid)
                if count >= 1:
                    # we have more than one entry for this fiberId -> get the APOGEE
                    ind = -1
                    for i in range(count):
                        pos = p0['PLUGMAPOBJ']['fiberId'][ind + 1:].index(fid)
                        ind = pos + ind + 1
                        if p0['PLUGMAPOBJ']['spectrographId'][ind] == 2:
                            break

                    # print "fid=%d    t1=%d   t2=%d" % (fid,t1,t2)
                    # only modify the fibers for APOGEE (2) that are not sky fibers
                    if p0['PLUGMAPOBJ']['spectrographId'][ind] == 2 and p0[
                            'PLUGMAPOBJ']['objType'][ind] != 'SKY':
                        if not (j_mag and h_mag and k_mag):
                            #cmd.warn('text="some IR mags are bad: j=%s h=%s k=%s"' % (j_mag, h_mag, k_mag))
                            logging.warn(
                                'text="some IR mags are bad: j=%s h=%s k=%s"' %
                                (j_mag, h_mag, k_mag))
                        p0['PLUGMAPOBJ']['mag'][ind][
                            0] = j_mag if j_mag else 0.0
                        p0['PLUGMAPOBJ']['mag'][ind][
                            1] = h_mag if h_mag else 0.0
                        p0['PLUGMAPOBJ']['mag'][ind][
                            2] = k_mag if k_mag else 0.0
                        p0['PLUGMAPOBJ']['tmass_style'][ind] = tmass_style
                        if (p0['PLUGMAPOBJ']['objType'][ind] == 'STAR_BHB'):
                            p0['PLUGMAPOBJ']['objType'][ind] = 'STAR'
                        elif (p0['PLUGMAPOBJ']['objType'][ind] ==
                              'SPECTROPHOTO_STD'):
                            p0['PLUGMAPOBJ']['objType'][ind] = 'HOT_STD'

        # APOGEE-1/2 plates
        else:

            # we'll use the target1 and target2 to define the type of target
            # these are 32 bits each with each bit indicating a type
            skymask = 16
            hotmask = 512
            extmask = 1024
            starmask = skymask | hotmask

            # loop through the list and update the PLUGMAPOBJ
            tmass_style = 'Unknown'
            for fid, j_mag, h_mag, k_mag, t1, t2 in ph:
                count = p0['PLUGMAPOBJ']['fiberId'].count(fid)
                if count >= 1:
                    # we have more than one entry for this fiberId -> get the APOGEE
                    ind = -1
                    for i in range(count):
                        pos = p0['PLUGMAPOBJ']['fiberId'][ind + 1:].index(fid)
                        ind = pos + ind + 1
                        if p0['PLUGMAPOBJ']['spectrographId'][ind] == 2:
                            break

                    # print "fid=%d    t1=%d   t2=%d" % (fid,t1,t2)
                    # only modify the fibers for APOGEE (2) that are not sky fibers
                    if p0['PLUGMAPOBJ']['spectrographId'][ind] == 2 and p0[
                            'PLUGMAPOBJ']['objType'][ind] != 'SKY':
                        if not (j_mag and h_mag and k_mag):
                            #cmd.warn('text="some IR mags are bad: j=%s h=%s k=%s"' % (j_mag, h_mag, k_mag))
                            logging.warn(
                                'text="some IR mags are bad: j=%s h=%s k=%s"' %
                                (j_mag, h_mag, k_mag))
                        p0['PLUGMAPOBJ']['mag'][ind][
                            0] = j_mag if j_mag else 0.0
                        p0['PLUGMAPOBJ']['mag'][ind][
                            1] = h_mag if h_mag else 0.0
                        p0['PLUGMAPOBJ']['mag'][ind][
                            2] = k_mag if k_mag else 0.0
                        p0['PLUGMAPOBJ']['tmass_style'][ind] = tmass_style
                        if (t2 & skymask) > 0:
                            p0['PLUGMAPOBJ']['objType'][ind] = 'SKY'
                        elif (t2 & hotmask) > 0:
                            p0['PLUGMAPOBJ']['objType'][ind] = 'HOT_STD'
                        elif (t1 & extmask) > 0:
                            p0['PLUGMAPOBJ']['objType'][ind] = 'EXTOBJ'
                        elif (t2 & starmask) == 0 and (t1 & extmask) == 0:
                            p0['PLUGMAPOBJ']['objType'][ind] = 'STAR'

        # delete file if it already exists
        if os.path.isfile(newfilename):
            os.remove(newfilename)

        p0.write(newfilename)

        # write a copy to the archive directory
        # define the current mjd archive directory to store the plPlugMapA file
        mjd = astroMJD.mjdFromPyTuple(time.gmtime())
        fmjd = str(int(mjd + 0.3))
        arch_dir = os.path.join(self.archive_dir, fmjd)
        if not os.path.isdir(arch_dir):
            os.mkdir(arch_dir, 0o0775)

        res = os.path.split(newfilename)
        archivefile = os.path.join(arch_dir, res[1])
        p0.write(archivefile)

        return
Beispiel #11
0
def plate_addenda_db(inputs, design_mode=False, verbose=False, log=None):
    """Loads plateDefinitionAddendas to the DB from plateruns or designs.

    Parameters:
        inputs (list, tuple):
            A list of plateruns or plates to be ingested into the DB.
        design_mode (bool):
            If ``True``, treats ``inputs`` as a list of design ids.
            Otherwise assumes they are plateruns.
        verbose (bool):
            If ``True`` outputs more information in the shell log.
        log (``platedesign.core.logger.Logger`` object):
            A ``Logger`` object to use. Otherwise it will create a new log.

    """

    log = log or pd_log

    log.info('running plate_addenda_db in mode={0!r}.'.format(
        'platerun' if not design_mode else 'plate'))

    # Checks the connection
    conn_status = platedb.database.connected
    if conn_status:
        log.info('database connection is open.')
    else:
        raise RuntimeError(
            'cannot connect to the database. Review you connection settings.')

    # Creates a list of plates for each platerun and plate.
    designs = []
    if not design_mode:
        for platerun in inputs:
            lines = utils.get_lines_for_platerun(platerun)
            if len(lines) == 0:
                raise ValueError(
                    'no platePlans lines found for platerun {0!r}'.format(
                        platerun))
            designs += lines['designid'].tolist()
    else:
        designs = inputs

    if len(designs) == 0:
        raise ValueError('no designs found. Your input parameters '
                         'do not seem to match any plate.')

    log.info('loading plateDefinitionAddenda for {0} designs.'.format(
        len(designs)))

    with platedb.database.atomic():

        for design_id in designs:

            log.info('loading plateDefinitionAddenda for design {0}'.format(
                design_id))

            plate_definition_path = utils.get_path('plateDefinitionAddenda',
                                                   designid=design_id)

            if not os.path.exists(plate_definition_path):
                warnings.warn(
                    'cannot find a plateDefinitionAddenda '
                    'for design {0}. Skipping it.'.format(design_id),
                    UserWarning)
                continue

            definition = yanny.yanny(plate_definition_path)
            definition_lower = dict((kk.lower(), vv)
                                    for kk, vv in definition.items()
                                    if kk != 'symbols')

            # Fetch design
            try:
                design_dbo = platedb.Design.get(pk=design_id)
            except peewee.DoesNotExist:
                raise RuntimeError(
                    'the design {0} cannot be found in the DB. '
                    'Make sure you have loaded it first.'.format(design_id))

            for key in definition_lower:

                field_name = key
                value = definition_lower[key]

                # Gets or creates the field object

                design_field_dbo, created = platedb.DesignField.get_or_create(
                    label=field_name)

                if created:
                    warnings.warn(
                        'design_id={0}: the design field {1} was not found. Adding it.'
                        .format(design_id, field_name), UserWarning)

                # Check if there is an existing value with this key
                design_value_dbo = platedb.DesignValue.select().where(
                    platedb.DesignValue.design_pk == design_dbo.pk,
                    platedb.DesignValue.design_field_pk ==
                    design_field_dbo.pk).first()

                if design_value_dbo is None:
                    # Create a new value
                    design_value_dbo = platedb.DesignValue()
                    design_value_dbo.design_field_pk = design_field_dbo.pk
                    design_value_dbo.design_pk = design_dbo.pk
                    log.debug(
                        'design_id={0}: adding new (field, value)=({1!r}, {2!r}).'
                        .format(design_id, design_value_dbo.field.label,
                                value))

                # Updates the value from the plateDefinitionAddenda value
                design_value_dbo.value = value

                design_field_dbo.save()
                design_value_dbo.save()
def create_plPlugMapM_LCO(plateID, pointing, field, mjd,
                          lookupTable=None, fscanId=1):
    """Converts the plPlugMapP files for a `plateID` into a plPlugMapM.

    This scripts converts the plPlugMapP files for a `plateID` into a
    plPlugMapM given a lookup table that simulates a mapping.

    Each guider commissioning plate contains four pointings, each with 3
    sets of 16 guiding stars (fields).

    """

    lookupArray = getLookupArray(lookupTable)

    pointingName = '' if pointing == 'A' else pointing

    filename = os.path.join(
        os.environ['PLATELIST_DIR'], 'plates',
        '{0:06d}'.format(plateID)[:-2] + 'XX', '{0:06d}'.format(plateID),
        'plPlugMapP-{0:d}{1}.par'.format(plateID, pointingName))

    assert os.path.exists(filename)

    yannyFile = yanny.yanny(filename, np=True)
    rawFile = open(filename, 'r').read().splitlines()

    plPlugMapObj = yannyFile['PLUGMAPOBJ']

    # Manually retrieves the header from the raw lines.
    header = []
    for line in rawFile:
        if line.strip().startswith('typedef enum {'):
            break
        header.append(line)

    # Calculates the range of fiberIds that correspond to this pointing
    # and fscanID.
    pointingNum = string.uppercase.index(pointingName)
    preIndex = nGuides * 3 * pointingNum
    fiberID_range = preIndex + np.arange(1 + (field - 1) * nGuides,
                                         1 + field * nGuides)
    print(fiberID_range)
    outFileName = 'plPlugMapM-{0}{3}-{1}-{2:02d}_{4}.par'.format(
        plateID, mjd, fscanId, pointingName, colourDict[field])

    # Gets a list of the holes that we should keep for this fscanID
    validHoles = plPlugMapObj[(plPlugMapObj['holeType'] == 'LIGHT_TRAP') |
                              (np.in1d(plPlugMapObj['fiberId'],
                                       fiberID_range))]

    lightTraps = validHoles[validHoles['holeType'] == 'LIGHT_TRAP']
    guides = validHoles[validHoles['holeType'] == 'GUIDE']
    alignments = validHoles[validHoles['holeType'] == 'ALIGNMENT']

    guides = guides[lookupArray[:, 1] - 1]
    guides['fiberId'] = lookupArray[:, 0]

    sortedPlPlugMapM = np.concatenate((lightTraps, alignments, guides))

    enums = {'holeType': ['HOLETYPE', yannyFile._enum_cache['HOLETYPE']],
             'objType': ['OBJTYPE', yannyFile._enum_cache['OBJTYPE']]}
    yanny.write_ndarray_to_yanny(outFileName, sortedPlPlugMapM, enums=enums,
                                 structname='PLUGMAPOBJ')

    # Replaces the guidenums with the slice used for this file
    for ii, line in enumerate(header):
        if line.startswith('guidenums' + str(pointingNum + 1)):
            guides = str('guidenums' + str(pointingNum + 1)) + ' ' + \
                ' '.join(map(str, np.arange(1, nGuides + 1)))
            header[ii] = guides
            break

    addHeader(header, outFileName, plateID, fscanId, field,
              mjd, colourDict[field])

    return