Ejemplo n.º 1
0
    def _update_info_div(self):
        '''Update the text div with information about the current target'''
        fibermap = self.spectra.fibermap[self.ispec]
        zb = self.zbest[self.izbest]

        info = list()
        info.append('<table>')
        info.append('<tr><th>TargetID</th><td>{}</td></tr>'.format(
            zb['TARGETID']))
        info.append('<tr><th>DESI_TARGET</th><td>{0}</td></tr>'.format(
            ' '.join(desi_mask.names(fibermap['DESI_TARGET']))))
        info.append('<tr><th>BGS_TARGET</th><td>{0}</td></tr>'.format(' '.join(
            bgs_mask.names(fibermap['BGS_TARGET']))))
        info.append('<tr><th>MWS_TARGET</th><td>{0}</td></tr>'.format(' '.join(
            mws_mask.names(fibermap['MWS_TARGET']))))
        info.append('</table>')

        self.info_div.text = '\n'.join(info)
Ejemplo n.º 2
0
def calc_priority(targets, zcat, obscon):
    """
    Calculate target priorities from masks, observation/redshift status.

    Parameters
    ----------
    targets : :class:`~numpy.ndarray`
        numpy structured array or astropy Table of targets. Must include
        the columns `DESI_TARGET`, `BGS_TARGET`, `MWS_TARGET`
        (or their SV/cmx equivalents).
    zcat : :class:`~numpy.ndarray`
        numpy structured array or Table of redshift information. Must
        include 'Z', `ZWARN`, `NUMOBS` and be the same length as
        `targets`. May also contain `NUMOBS_MORE` if this isn't the
        first time through MTL and `NUMOBS > 0`.
    obscon : :class:`str`
        A combination of strings that are in the desitarget bitmask yaml
        file (specifically in `desitarget.targetmask.obsconditions`), e.g.
        "DARK|GRAY". Governs the behavior of how priorities are set based
        on "obsconditions" in the desitarget bitmask yaml file.

    Returns
    -------
    :class:`~numpy.array`
        integer array of priorities.

    Notes
    -----
        - If a target passes multiple selections, highest priority wins.
        - Will automatically detect if the passed targets are main
          survey, commissioning or SV and behave accordingly.
    """
    # ADM check the input arrays are the same length.
    assert len(targets) == len(zcat)

    # ADM determine whether the input targets are main survey, cmx or SV.
    colnames, masks, survey = main_cmx_or_sv(targets, scnd=True)
    # ADM the target bits/names should be shared between main survey and SV.
    if survey != 'cmx':
        desi_target, bgs_target, mws_target, scnd_target = colnames
        desi_mask, bgs_mask, mws_mask, scnd_mask = masks
    else:
        cmx_mask = masks[0]

    # Default is 0 priority, i.e. do not observe.
    priority = np.zeros(len(targets), dtype='i8')

    # Determine which targets have been observed.
    # TODO: this doesn't distinguish between really unobserved vs not yet
    # processed.
    unobs = (zcat["NUMOBS"] == 0)
    log.debug('calc_priority has %d unobserved targets' % (np.sum(unobs)))
    if np.all(unobs):
        done = np.zeros(len(targets), dtype=bool)
        zgood = np.zeros(len(targets), dtype=bool)
        zwarn = np.zeros(len(targets), dtype=bool)
    else:
        nmore = zcat["NUMOBS_MORE"]
        assert np.all(nmore >= 0)
        done = ~unobs & (nmore == 0)
        zgood = ~unobs & (nmore > 0) & (zcat['ZWARN'] == 0)
        zwarn = ~unobs & (nmore > 0) & (zcat['ZWARN'] != 0)

    # zgood, zwarn, done, and unobs should be mutually exclusive and cover all
    # targets.
    assert not np.any(unobs & zgood)
    assert not np.any(unobs & zwarn)
    assert not np.any(unobs & done)
    assert not np.any(zgood & zwarn)
    assert not np.any(zgood & done)
    assert not np.any(zwarn & done)
    assert np.all(unobs | done | zgood | zwarn)

    # DESI dark time targets.
    if survey != 'cmx':
        if desi_target in targets.dtype.names:
            # ADM 'LRG' is the guiding column in SV and the main survey
            # ADM (once, it was 'LRG_1PASS' and 'LRG_2PASS' in the MS).
            # names = ('ELG', 'LRG_1PASS', 'LRG_2PASS')
            # if survey[0:2] == 'sv':
            names = ('ELG', 'LRG')
            for name in names:
                # ADM only update priorities for passed observing conditions.
                pricon = obsconditions.mask(desi_mask[name].obsconditions)
                if (obsconditions.mask(obscon) & pricon) != 0:
                    ii = (targets[desi_target] & desi_mask[name]) != 0
                    priority[ii & unobs] = np.maximum(
                        priority[ii & unobs],
                        desi_mask[name].priorities['UNOBS'])
                    priority[ii & done] = np.maximum(
                        priority[ii & done],
                        desi_mask[name].priorities['DONE'])
                    priority[ii & zgood] = np.maximum(
                        priority[ii & zgood],
                        desi_mask[name].priorities['MORE_ZGOOD'])
                    priority[ii & zwarn] = np.maximum(
                        priority[ii & zwarn],
                        desi_mask[name].priorities['MORE_ZWARN'])

            # QSO could be Lyman-alpha or Tracer.
            name = 'QSO'
            # ADM only update priorities for passed observing conditions.
            pricon = obsconditions.mask(desi_mask[name].obsconditions)
            if (obsconditions.mask(obscon) & pricon) != 0:
                ii = (targets[desi_target] & desi_mask[name]) != 0
                # ADM all redshifts require more observations in SV.
                good_hiz = zgood & (zcat['Z'] >= 2.15) & (zcat['ZWARN'] == 0)
                if survey[0:2] == 'sv':
                    good_hiz = zgood & (zcat['ZWARN'] == 0)
                priority[ii & unobs] = np.maximum(
                    priority[ii & unobs], desi_mask[name].priorities['UNOBS'])
                priority[ii & done] = np.maximum(
                    priority[ii & done], desi_mask[name].priorities['DONE'])
                priority[ii & good_hiz] = np.maximum(
                    priority[ii & good_hiz],
                    desi_mask[name].priorities['MORE_ZGOOD'])
                priority[ii & ~good_hiz] = np.maximum(
                    priority[ii & ~good_hiz],
                    desi_mask[name].priorities['DONE'])
                priority[ii & zwarn] = np.maximum(
                    priority[ii & zwarn],
                    desi_mask[name].priorities['MORE_ZWARN'])

        # BGS targets.
        if bgs_target in targets.dtype.names:
            for name in bgs_mask.names():
                # ADM only update priorities for passed observing conditions.
                pricon = obsconditions.mask(bgs_mask[name].obsconditions)
                if (obsconditions.mask(obscon) & pricon) != 0:
                    ii = (targets[bgs_target] & bgs_mask[name]) != 0
                    priority[ii & unobs] = np.maximum(
                        priority[ii & unobs],
                        bgs_mask[name].priorities['UNOBS'])
                    priority[ii & done] = np.maximum(
                        priority[ii & done], bgs_mask[name].priorities['DONE'])
                    priority[ii & zgood] = np.maximum(
                        priority[ii & zgood],
                        bgs_mask[name].priorities['MORE_ZGOOD'])
                    priority[ii & zwarn] = np.maximum(
                        priority[ii & zwarn],
                        bgs_mask[name].priorities['MORE_ZWARN'])

        # MWS targets.
        if mws_target in targets.dtype.names:
            for name in mws_mask.names():
                # ADM only update priorities for passed observing conditions.
                pricon = obsconditions.mask(mws_mask[name].obsconditions)
                if (obsconditions.mask(obscon) & pricon) != 0:
                    ii = (targets[mws_target] & mws_mask[name]) != 0
                    priority[ii & unobs] = np.maximum(
                        priority[ii & unobs],
                        mws_mask[name].priorities['UNOBS'])
                    priority[ii & done] = np.maximum(
                        priority[ii & done], mws_mask[name].priorities['DONE'])
                    priority[ii & zgood] = np.maximum(
                        priority[ii & zgood],
                        mws_mask[name].priorities['MORE_ZGOOD'])
                    priority[ii & zwarn] = np.maximum(
                        priority[ii & zwarn],
                        mws_mask[name].priorities['MORE_ZWARN'])

        # ADM Secondary targets.
        if scnd_target in targets.dtype.names:
            # APC Secondary target bits only drive updates to targets with specific DESI_TARGET bits
            # APC See https://github.com/desihub/desitarget/pull/530
            scnd_update = (targets[desi_target] & desi_mask['SCND_ANY']) != 0
            if np.any(scnd_update):
                # APC Allow changes to primaries if the DESI_TARGET bitmask has any of the
                # APC following bits set, but not any other bits.
                update_from_scnd_bits = (desi_mask['SCND_ANY']
                                         | desi_mask['MWS_ANY']
                                         | desi_mask['STD_BRIGHT']
                                         | desi_mask['STD_FAINT']
                                         | desi_mask['STD_WD'])
                scnd_update &= ((targets[desi_target]
                                 & ~update_from_scnd_bits) == 0)
                print('{} scnd targets to be updated'.format(
                    scnd_update.sum()))

            for name in scnd_mask.names():
                # ADM only update priorities for passed observing conditions.
                pricon = obsconditions.mask(scnd_mask[name].obsconditions)
                if (obsconditions.mask(obscon) & pricon) != 0:
                    ii = (targets[scnd_target] & scnd_mask[name]) != 0
                    ii &= scnd_update
                    priority[ii & unobs] = np.maximum(
                        priority[ii & unobs],
                        scnd_mask[name].priorities['UNOBS'])
                    priority[ii & done] = np.maximum(
                        priority[ii & done],
                        scnd_mask[name].priorities['DONE'])
                    priority[ii & zgood] = np.maximum(
                        priority[ii & zgood],
                        scnd_mask[name].priorities['MORE_ZGOOD'])
                    priority[ii & zwarn] = np.maximum(
                        priority[ii & zwarn],
                        scnd_mask[name].priorities['MORE_ZWARN'])

        # Special case: IN_BRIGHT_OBJECT means priority=-1 no matter what
        ii = (targets[desi_target] & desi_mask.IN_BRIGHT_OBJECT) != 0
        priority[ii] = -1

    # ADM Special case: SV-like commissioning targets.
    if 'CMX_TARGET' in targets.dtype.names:
        for name in ['SV0_' + label for label in ('BGS', 'MWS')]:
            ii = (targets['CMX_TARGET'] & cmx_mask[name]) != 0
            priority[ii & unobs] = np.maximum(
                priority[ii & unobs], cmx_mask[name].priorities['UNOBS'])
            priority[ii & done] = np.maximum(priority[ii & done],
                                             cmx_mask[name].priorities['DONE'])
            priority[ii & zgood] = np.maximum(
                priority[ii & zgood], cmx_mask[name].priorities['MORE_ZGOOD'])
            priority[ii & zwarn] = np.maximum(
                priority[ii & zwarn], cmx_mask[name].priorities['MORE_ZWARN'])

    return priority
Ejemplo n.º 3
0
    def test_priorities(self):
        """Test that priorities are set correctly for both the main survey and SV.
        """
        # ADM loop through once for SV and once for the main survey.
        for prefix in ["", "SV1_"]:
            t = self.targets.copy()
            z = self.zcat.copy()

            main_names = ['DESI_TARGET', 'BGS_TARGET', 'MWS_TARGET']
            for name in main_names:
                t.rename_column(name, prefix + name)

            # ADM retrieve the mask and column names for this survey flavor.
            colnames, masks, _ = main_cmx_or_sv(t)
            desi_target, bgs_target, mws_target = colnames
            desi_mask, bgs_mask, mws_mask = masks

            # - No targeting bits set is priority=0
            self.assertTrue(np.all(calc_priority(t, z) == 0))

            # - test QSO > (LRG_1PASS | LRG_2PASS) > ELG
            t[desi_target] = desi_mask.ELG
            self.assertTrue(
                np.all(
                    calc_priority(t, z) == desi_mask.ELG.priorities['UNOBS']))
            t[desi_target] |= desi_mask.LRG_1PASS
            self.assertTrue(
                np.all(
                    calc_priority(t, z) == desi_mask.LRG.priorities['UNOBS']))
            t[desi_target] |= desi_mask.LRG_2PASS
            self.assertTrue(
                np.all(
                    calc_priority(t, z) == desi_mask.LRG.priorities['UNOBS']))
            t[desi_target] |= desi_mask.QSO
            self.assertTrue(
                np.all(
                    calc_priority(t, z) == desi_mask.QSO.priorities['UNOBS']))

            # - different states -> different priorities

            # - Done is Done, regardless of ZWARN.
            t[desi_target] = desi_mask.ELG
            t["PRIORITY_INIT"], t["NUMOBS_INIT"] = initial_priority_numobs(t)
            z['NUMOBS'] = [0, 1, 1]
            z['ZWARN'] = [1, 1, 0]
            p = make_mtl(t, z)["PRIORITY"]

            self.assertEqual(p[0], desi_mask.ELG.priorities['UNOBS'])
            self.assertEqual(p[1], desi_mask.ELG.priorities['DONE'])
            self.assertEqual(p[2], desi_mask.ELG.priorities['DONE'])

            # - BGS FAINT targets are never DONE, only MORE_ZGOOD.
            t[desi_target] = desi_mask.BGS_ANY
            t[bgs_target] = bgs_mask.BGS_FAINT
            t["PRIORITY_INIT"], t["NUMOBS_INIT"] = initial_priority_numobs(t)
            z['NUMOBS'] = [0, 1, 1]
            z['ZWARN'] = [1, 1, 0]
            p = make_mtl(t, z)["PRIORITY"]

            self.assertEqual(p[0], bgs_mask.BGS_FAINT.priorities['UNOBS'])
            self.assertEqual(p[1], bgs_mask.BGS_FAINT.priorities['MORE_ZWARN'])
            self.assertEqual(p[2], bgs_mask.BGS_FAINT.priorities['MORE_ZGOOD'])
            # BGS_FAINT: {UNOBS: 2000, MORE_ZWARN: 2000, MORE_ZGOOD: 1000, DONE: 2, OBS: 1, DONOTOBSERVE: 0}

            # - BGS BRIGHT targets are never DONE, only MORE_ZGOOD.
            t[desi_target] = desi_mask.BGS_ANY
            t[bgs_target] = bgs_mask.BGS_BRIGHT
            t["PRIORITY_INIT"], t["NUMOBS_INIT"] = initial_priority_numobs(t)
            z['NUMOBS'] = [0, 1, 1]
            z['ZWARN'] = [1, 1, 0]
            p = make_mtl(t, z)["PRIORITY"]

            self.assertEqual(p[0], bgs_mask.BGS_BRIGHT.priorities['UNOBS'])
            self.assertEqual(p[1],
                             bgs_mask.BGS_BRIGHT.priorities['MORE_ZWARN'])
            self.assertEqual(p[2],
                             bgs_mask.BGS_BRIGHT.priorities['MORE_ZGOOD'])
            # BGS_BRIGHT: {UNOBS: 2100, MORE_ZWARN: 2100, MORE_ZGOOD: 1000, DONE: 2, OBS: 1, DONOTOBSERVE: 0}

            # BGS targets are NEVER done even after 100 observations
            t[desi_target] = desi_mask.BGS_ANY
            t[bgs_target] = bgs_mask.BGS_BRIGHT
            t["PRIORITY_INIT"], t["NUMOBS_INIT"] = initial_priority_numobs(t)
            z['NUMOBS'] = [0, 100, 100]
            z['ZWARN'] = [1, 1, 0]
            p = calc_priority(t, z)

            self.assertEqual(p[0], bgs_mask.BGS_BRIGHT.priorities['UNOBS'])
            self.assertEqual(p[1],
                             bgs_mask.BGS_BRIGHT.priorities['MORE_ZWARN'])
            self.assertEqual(p[2],
                             bgs_mask.BGS_BRIGHT.priorities['MORE_ZGOOD'])

            # BGS ZGOOD targets always have lower priority than MWS targets that
            # are not DONE.
            # ADM first discard N/S informational bits from bitmask as these
            # ADM should never trump the other bits.
            bgs_names = [
                name for name in bgs_mask.names()
                if 'NORTH' not in name and 'SOUTH' not in name
            ]
            mws_names = [
                name for name in mws_mask.names()
                if 'NORTH' not in name and 'SOUTH' not in name
            ]

            lowest_bgs_priority_zgood = min(
                [bgs_mask[n].priorities['MORE_ZGOOD'] for n in bgs_names])

            lowest_mws_priority_unobs = min(
                [mws_mask[n].priorities['UNOBS'] for n in mws_names])
            lowest_mws_priority_zwarn = min(
                [mws_mask[n].priorities['MORE_ZWARN'] for n in mws_names])
            lowest_mws_priority_zgood = min(
                [mws_mask[n].priorities['MORE_ZGOOD'] for n in mws_names])

            lowest_mws_priority = min(lowest_mws_priority_unobs,
                                      lowest_mws_priority_zwarn,
                                      lowest_mws_priority_zgood)

            self.assertLess(lowest_bgs_priority_zgood, lowest_mws_priority)
Ejemplo n.º 4
0
def calc_priority(targets, zcat, obscon, state=False):
    """
    Calculate target priorities from masks, observation/redshift status.

    Parameters
    ----------
    targets : :class:`~numpy.ndarray`
        numpy structured array or astropy Table of targets. Must include
        the columns `DESI_TARGET`, `BGS_TARGET`, `MWS_TARGET`
        (or their SV/cmx equivalents) and `TARGETID`.
    zcat : :class:`~numpy.ndarray`
        numpy structured array or Table of redshift info. Must include
        `Z`, `ZWARN`, `NUMOBS` and `TARGETID` and BE SORTED ON TARGETID
        to match `targets` row-by-row. May also contain `NUMOBS_MORE` if
        this isn't the first time through MTL and `NUMOBS > 0`.
    obscon : :class:`str`
        A combination of strings that are in the desitarget bitmask yaml
        file (specifically in `desitarget.targetmask.obsconditions`), e.g.
        "DARK|GRAY". Governs the behavior of how priorities are set based
        on "obsconditions" in the desitarget bitmask yaml file.
    state : :class:`bool`
        If ``True`` then also return a string denoting the state that
        was set. The state is a string combining the observational
        state (e.g. "DONE", "MORE_ZGOOD") from the targeting yaml file
        and the target type (e.g. "ELG", "LRG").

    Returns
    -------
    :class:`~numpy.array`
        integer array of priorities.
    :class:`~numpy.array`
        string array of states. Only returned if `state`=``True``

    Notes
    -----
        - If a target passes multiple selections, highest priority wins.
        - Will automatically detect if the passed targets are main
          survey, commissioning or SV and behave accordingly.
    """
    # ADM check input arrays are sorted to match row-by-row on TARGETID.
    assert np.all(targets["TARGETID"] == zcat["TARGETID"])

    # ADM determine whether the input targets are main survey, cmx or SV.
    colnames, masks, survey = main_cmx_or_sv(targets, scnd=True)
    # ADM the target bits/names should be shared between main survey and SV.
    if survey != 'cmx':
        desi_target, bgs_target, mws_target, scnd_target = colnames
        desi_mask, bgs_mask, mws_mask, scnd_mask = masks
    else:
        cmx_mask = masks[0]

    # Default is 0 priority, i.e. do not observe.
    priority = np.zeros(len(targets), dtype='i8')
    # ADM set up a string to record the state of each target.
    from desitarget.mtl import mtldatamodel
    target_state = np.zeros(len(targets),
                            dtype=mtldatamodel["TARGET_STATE"].dtype)

    # Determine which targets have been observed.
    # TODO: this doesn't distinguish between really unobserved vs not yet
    # processed.
    unobs = (zcat["NUMOBS"] == 0)
    log.debug('calc_priority has %d unobserved targets' % (np.sum(unobs)))
    if np.all(unobs):
        done = np.zeros(len(targets), dtype=bool)
        zgood = np.zeros(len(targets), dtype=bool)
        zwarn = np.zeros(len(targets), dtype=bool)
    else:
        nmore = zcat["NUMOBS_MORE"]
        assert np.all(nmore >= 0)
        done = ~unobs & (nmore == 0)
        zgood = ~unobs & (nmore > 0) & (zcat['ZWARN'] == 0)
        zwarn = ~unobs & (nmore > 0) & (zcat['ZWARN'] != 0)

    # zgood, zwarn, done, and unobs should be mutually exclusive and cover all
    # targets.
    assert not np.any(unobs & zgood)
    assert not np.any(unobs & zwarn)
    assert not np.any(unobs & done)
    assert not np.any(zgood & zwarn)
    assert not np.any(zgood & done)
    assert not np.any(zwarn & done)
    assert np.all(unobs | done | zgood | zwarn)

    # DESI dark time targets.
    if survey != 'cmx':
        if desi_target in targets.dtype.names:
            # ADM set initialstate of CALIB for potential calibration targets.
            names = ('SKY', 'BAD_SKY', 'SUPP_SKY',
                     'STD_FAINT', 'STD_WD', 'STD_BRIGHT')
            for name in names:
                # ADM only update states for passed observing conditions.
                pricon = obsconditions.mask(desi_mask[name].obsconditions)
                if (obsconditions.mask(obscon) & pricon) != 0:
                    ii = (targets[desi_target] & desi_mask[name]) != 0
                    target_state[ii] = "CALIB"

            # ADM 'LRG' is the guiding column in SV and the main survey
            # ADM (once, it was 'LRG_1PASS' and 'LRG_2PASS' in the MS).
            # names = ('ELG', 'LRG_1PASS', 'LRG_2PASS')
            # if survey[0:2] == 'sv':
            names = ('ELG', 'LRG')
            for name in names:
                # ADM only update priorities for passed observing conditions.
                pricon = obsconditions.mask(desi_mask[name].obsconditions)
                if (obsconditions.mask(obscon) & pricon) != 0:
                    ii = (targets[desi_target] & desi_mask[name]) != 0
                    for sbool, sname in zip(
                        [unobs, done, zgood, zwarn],
                        ["UNOBS", "DONE", "MORE_ZGOOD", "MORE_ZWARN"]
                    ):
                        # ADM update priorities and target states.
                        Mxp = desi_mask[name].priorities[sname]
                        # ADM update states BEFORE changing priorities.
                        ts = "{}|{}".format(name, sname)
                        target_state[ii & sbool] = np.where(
                            priority[ii & sbool] < Mxp, ts, target_state[ii & sbool])
                        priority[ii & sbool] = np.where(
                            priority[ii & sbool] < Mxp, Mxp, priority[ii & sbool])

            # QSO could be Lyman-alpha or Tracer.
            name = 'QSO'
            # ADM only update priorities for passed observing conditions.
            pricon = obsconditions.mask(desi_mask[name].obsconditions)
            if (obsconditions.mask(obscon) & pricon) != 0:
                ii = (targets[desi_target] & desi_mask[name]) != 0
                # ADM all redshifts require more observations in SV.
                # ADM (zcut is defined at the top of this module).
                good_hiz = zgood & (zcat['Z'] >= zcut) & (zcat['ZWARN'] == 0)
                if survey[:2] == 'sv':
                    good_hiz = zgood & (zcat['ZWARN'] == 0)
                for sbool, sname in zip(
                        [unobs, done, good_hiz, ~good_hiz, zwarn],
                        ["UNOBS", "DONE", "MORE_ZGOOD", "DONE", "MORE_ZWARN"]
                ):
                    # ADM update priorities and target states.
                    Mxp = desi_mask[name].priorities[sname]
                    # ADM update states BEFORE changing priorities.
                    ts = "{}|{}".format(name, sname)
                    target_state[ii & sbool] = np.where(
                        priority[ii & sbool] < Mxp, ts, target_state[ii & sbool])
                    priority[ii & sbool] = np.where(
                        priority[ii & sbool] < Mxp, Mxp, priority[ii & sbool])

        # BGS targets.
        if bgs_target in targets.dtype.names:
            for name in bgs_mask.names():
                # ADM only update priorities for passed observing conditions.
                pricon = obsconditions.mask(bgs_mask[name].obsconditions)
                if (obsconditions.mask(obscon) & pricon) != 0:
                    ii = (targets[bgs_target] & bgs_mask[name]) != 0
                    for sbool, sname in zip(
                            [unobs, done, zgood, zwarn],
                            ["UNOBS", "DONE", "MORE_ZGOOD", "MORE_ZWARN"]
                    ):
                        # ADM update priorities and target states.
                        Mxp = bgs_mask[name].priorities[sname]
                        # ADM update states BEFORE changing priorities.
                        ts = "{}|{}".format("BGS", sname)
                        target_state[ii & sbool] = np.where(
                            priority[ii & sbool] < Mxp, ts, target_state[ii & sbool])
                        priority[ii & sbool] = np.where(
                            priority[ii & sbool] < Mxp, Mxp, priority[ii & sbool])

        # MWS targets.
        if mws_target in targets.dtype.names:
            for name in mws_mask.names():
                # ADM only update priorities for passed observing conditions.
                pricon = obsconditions.mask(mws_mask[name].obsconditions)
                if (obsconditions.mask(obscon) & pricon) != 0:
                    ii = (targets[mws_target] & mws_mask[name]) != 0
                    for sbool, sname in zip(
                            [unobs, done, zgood, zwarn],
                            ["UNOBS", "DONE", "MORE_ZGOOD", "MORE_ZWARN"]
                    ):
                        # ADM update priorities and target states.
                        Mxp = mws_mask[name].priorities[sname]
                        # ADM update states BEFORE changing priorities.
                        ts = "{}|{}".format("MWS", sname)
                        target_state[ii & sbool] = np.where(
                            priority[ii & sbool] < Mxp, ts, target_state[ii & sbool])
                        priority[ii & sbool] = np.where(
                            priority[ii & sbool] < Mxp, Mxp, priority[ii & sbool])

        # ADM Secondary targets.
        if scnd_target in targets.dtype.names:
            # APC Secondary target bits only drive updates to targets with specific DESI_TARGET bits
            # APC See https://github.com/desihub/desitarget/pull/530
            scnd_update = (targets[desi_target] & desi_mask['SCND_ANY']) != 0
            if np.any(scnd_update):
                # APC Allow changes to primaries if the DESI_TARGET bitmask has any of the
                # APC following bits set, but not any other bits.
                update_from_scnd_bits = (
                    desi_mask['SCND_ANY'] | desi_mask['MWS_ANY'] |
                    desi_mask['STD_BRIGHT'] | desi_mask['STD_FAINT'] |
                    desi_mask['STD_WD'])
                scnd_update &= ((targets[desi_target] & ~update_from_scnd_bits) == 0)
                log.info('{} scnd targets to be updated'.format(scnd_update.sum()))

            for name in scnd_mask.names():
                # ADM only update priorities for passed observing conditions.
                pricon = obsconditions.mask(scnd_mask[name].obsconditions)
                if (obsconditions.mask(obscon) & pricon) != 0:
                    ii = (targets[scnd_target] & scnd_mask[name]) != 0
                    ii &= scnd_update
                    for sbool, sname in zip(
                            [unobs, done, zgood, zwarn],
                            ["UNOBS", "DONE", "MORE_ZGOOD", "MORE_ZWARN"]
                    ):
                        # ADM update priorities and target states.
                        Mxp = scnd_mask[name].priorities[sname]
                        # ADM update states BEFORE changing priorities.
                        ts = "{}|{}".format("SCND", sname)
                        target_state[ii & sbool] = np.where(
                            priority[ii & sbool] < Mxp, ts, target_state[ii & sbool])
                        priority[ii & sbool] = np.where(
                            priority[ii & sbool] < Mxp, Mxp, priority[ii & sbool])

        # Special case: IN_BRIGHT_OBJECT means priority=-1 no matter what.
        ii = (targets[desi_target] & desi_mask.IN_BRIGHT_OBJECT) != 0
        priority[ii] = -1
        target_state[ii] = "IN_BRIGHT_OBJECT"

    # ADM Special case: SV-like commissioning targets.
    if 'CMX_TARGET' in targets.dtype.names:
        priority = _cmx_calc_priority(targets, priority, obscon,
                                      unobs, done, zgood, zwarn, cmx_mask, obsconditions)

    if state:
        return priority, target_state
    return priority
Ejemplo n.º 5
0
def calc_priority(targets, zcat):
    """
    Calculate target priorities from masks, observation/redshift status.

    Parameters
    ----------
    targets : :class:`~numpy.ndarray`
        numpy structured array or astropy Table of targets. Must include
        the columns `DESI_TARGET`, `BGS_TARGET`, `MWS_TARGET`
        (or their SV/cmx equivalents).
    zcat : :class:`~numpy.ndarray`
        numpy structured array or Table of redshift information. Must
        include 'Z', `ZWARN`, `NUMOBS` and be the same length as
        `targets`. May also contain `NUMOBS_MORE` if this isn't the
        first time through MTL and `NUMOBS > 0`.

    Returns
    -------
    :class:`~numpy.array`
        integer array of priorities.

    Notes
    -----
        - If a target passes multiple selections, highest priority wins.
        - Will automatically detect if the passed targets are main
          survey, commissioning or SV and behave accordingly.
    """
    # ADM check the input arrays are the same length.
    assert len(targets) == len(zcat)

    # ADM determine whether the input targets are main survey, cmx or SV.
    colnames, masks, survey = main_cmx_or_sv(targets)
    # ADM the target bits/names should be shared between main survey and SV.
    if survey != 'cmx':
        desi_target, bgs_target, mws_target = colnames
        desi_mask, bgs_mask, mws_mask = masks
    else:
        cmx_mask = masks[0]

    # Default is 0 priority, i.e. do not observe.
    priority = np.zeros(len(targets), dtype='i8')

    # Determine which targets have been observed.
    # TODO: this doesn't distinguish between really unobserved vs not yet
    # processed.
    unobs = (zcat["NUMOBS"] == 0)
    log.debug('calc_priority has %d unobserved targets' % (np.sum(unobs)))
    if np.all(unobs):
        done = np.zeros(len(targets), dtype=bool)
        zgood = np.zeros(len(targets), dtype=bool)
        zwarn = np.zeros(len(targets), dtype=bool)
    else:
        nmore = zcat["NUMOBS_MORE"]
        assert np.all(nmore >= 0)
        done = ~unobs & (nmore == 0)
        zgood = ~unobs & (nmore > 0) & (zcat['ZWARN'] == 0)
        zwarn = ~unobs & (nmore > 0) & (zcat['ZWARN'] != 0)

    # zgood, zwarn, done, and unobs should be mutually exclusive and cover all
    # targets.
    assert not np.any(unobs & zgood)
    assert not np.any(unobs & zwarn)
    assert not np.any(unobs & done)
    assert not np.any(zgood & zwarn)
    assert not np.any(zgood & done)
    assert not np.any(zwarn & done)
    assert np.all(unobs | done | zgood | zwarn)

    # DESI dark time targets.
    if survey != 'cmx':
        if desi_target in targets.dtype.names:
            # ADM 'LRG' is the guiding column in SV
            # ADM whereas 'LRG_1PASS' and 'LRG_2PASS' are in the main survey.
            names = ('ELG', 'LRG_1PASS', 'LRG_2PASS')
            if survey[0:2] == 'sv':
                names = ('ELG', 'LRG')
            for name in names:
                ii = (targets[desi_target] & desi_mask[name]) != 0
                priority[ii & unobs] = np.maximum(
                    priority[ii & unobs], desi_mask[name].priorities['UNOBS'])
                priority[ii & done] = np.maximum(
                    priority[ii & done], desi_mask[name].priorities['DONE'])
                priority[ii & zgood] = np.maximum(
                    priority[ii & zgood],
                    desi_mask[name].priorities['MORE_ZGOOD'])
                priority[ii & zwarn] = np.maximum(
                    priority[ii & zwarn],
                    desi_mask[name].priorities['MORE_ZWARN'])

            # QSO could be Lyman-alpha or Tracer.
            name = 'QSO'
            ii = (targets[desi_target] & desi_mask[name]) != 0
            good_hiz = zgood & (zcat['Z'] >= 2.15) & (zcat['ZWARN'] == 0)
            priority[ii & unobs] = np.maximum(
                priority[ii & unobs], desi_mask[name].priorities['UNOBS'])
            priority[ii & done] = np.maximum(
                priority[ii & done], desi_mask[name].priorities['DONE'])
            priority[ii & good_hiz] = np.maximum(
                priority[ii & good_hiz],
                desi_mask[name].priorities['MORE_ZGOOD'])
            priority[ii & ~good_hiz] = np.maximum(
                priority[ii & ~good_hiz], desi_mask[name].priorities['DONE'])
            priority[ii & zwarn] = np.maximum(
                priority[ii & zwarn], desi_mask[name].priorities['MORE_ZWARN'])

        # BGS targets.
        if bgs_target in targets.dtype.names:
            for name in bgs_mask.names():
                ii = (targets[bgs_target] & bgs_mask[name]) != 0
                priority[ii & unobs] = np.maximum(
                    priority[ii & unobs], bgs_mask[name].priorities['UNOBS'])
                priority[ii & done] = np.maximum(
                    priority[ii & done], bgs_mask[name].priorities['DONE'])
                priority[ii & zgood] = np.maximum(
                    priority[ii & zgood],
                    bgs_mask[name].priorities['MORE_ZGOOD'])
                priority[ii & zwarn] = np.maximum(
                    priority[ii & zwarn],
                    bgs_mask[name].priorities['MORE_ZWARN'])

        # MWS targets.
        if mws_target in targets.dtype.names:
            for name in mws_mask.names():
                ii = (targets[mws_target] & mws_mask[name]) != 0
                priority[ii & unobs] = np.maximum(
                    priority[ii & unobs], mws_mask[name].priorities['UNOBS'])
                priority[ii & done] = np.maximum(
                    priority[ii & done], mws_mask[name].priorities['DONE'])
                priority[ii & zgood] = np.maximum(
                    priority[ii & zgood],
                    mws_mask[name].priorities['MORE_ZGOOD'])
                priority[ii & zwarn] = np.maximum(
                    priority[ii & zwarn],
                    mws_mask[name].priorities['MORE_ZWARN'])

        # Special case: IN_BRIGHT_OBJECT means priority=-1 no matter what
        ii = (targets[desi_target] & desi_mask.IN_BRIGHT_OBJECT) != 0
        priority[ii] = -1

    # ADM Special case: SV-like commissioning targets.
    if 'CMX_TARGET' in targets.dtype.names:
        for name in ['SV0_' + label for label in ('BGS', 'MWS')]:
            ii = (targets['CMX_TARGET'] & cmx_mask[name]) != 0
            priority[ii & unobs] = np.maximum(
                priority[ii & unobs], cmx_mask[name].priorities['UNOBS'])
            priority[ii & done] = np.maximum(priority[ii & done],
                                             cmx_mask[name].priorities['DONE'])
            priority[ii & zgood] = np.maximum(
                priority[ii & zgood], cmx_mask[name].priorities['MORE_ZGOOD'])
            priority[ii & zwarn] = np.maximum(
                priority[ii & zwarn], cmx_mask[name].priorities['MORE_ZWARN'])

    return priority
Ejemplo n.º 6
0
    def test_priorities(self):
        """Test that priorities are set correctly for both the main survey and SV.
        """
        # ADM loop through once for SV and once for the main survey.
        for prefix in ["", "SV1_"]:
            t = self.targets.copy()
            z = self.zcat.copy()

            main_names = ['DESI_TARGET', 'BGS_TARGET', 'MWS_TARGET']
            for name in main_names:
                t.rename_column(name, prefix + name)

            # ADM retrieve the mask and column names for this survey flavor.
            colnames, masks, _ = main_cmx_or_sv(t)
            desi_target, bgs_target, mws_target = colnames
            desi_mask, bgs_mask, mws_mask = masks

            # - No targeting bits set is priority=0
            self.assertTrue(np.all(calc_priority(t, z, "BRIGHT") == 0))

            # ADM test QSO > LRG > ELG for main survey and SV.
            t[desi_target] = desi_mask.ELG
            self.assertTrue(
                np.all(
                    calc_priority(t, z, "GRAY|DARK") ==
                    desi_mask.ELG.priorities['UNOBS']))
            t[desi_target] |= desi_mask.LRG
            self.assertTrue(
                np.all(
                    calc_priority(t, z, "GRAY|DARK") ==
                    desi_mask.LRG.priorities['UNOBS']))
            t[desi_target] |= desi_mask.QSO
            self.assertTrue(
                np.all(
                    calc_priority(t, z, "GRAY|DARK") ==
                    desi_mask.QSO.priorities['UNOBS']))

            # - different states -> different priorities
            # - Done is Done, regardless of ZWARN.
            t[desi_target] = desi_mask.ELG
            t["PRIORITY_INIT"], t["NUMOBS_INIT"] = initial_priority_numobs(t)
            z['NUMOBS'] = [0, 1, 1]
            z['ZWARN'] = [1, 1, 0]
            p = make_mtl(t, "GRAY|DARK", zcat=z)["PRIORITY"]

            self.assertEqual(p[0], desi_mask.ELG.priorities['UNOBS'])
            self.assertEqual(p[1], desi_mask.ELG.priorities['DONE'])
            self.assertEqual(p[2], desi_mask.ELG.priorities['DONE'])

            # ADM In BRIGHT conditions BGS FAINT targets are
            # ADM never DONE, only MORE_ZGOOD.
            t[desi_target] = desi_mask.BGS_ANY
            t[bgs_target] = bgs_mask.BGS_FAINT
            t["PRIORITY_INIT"], t["NUMOBS_INIT"] = initial_priority_numobs(t)
            z['NUMOBS'] = [0, 1, 1]
            z['ZWARN'] = [1, 1, 0]
            p = make_mtl(t, "BRIGHT", zcat=z)["PRIORITY"]

            self.assertEqual(p[0], bgs_mask.BGS_FAINT.priorities['UNOBS'])
            self.assertEqual(p[1], bgs_mask.BGS_FAINT.priorities['MORE_ZWARN'])
            self.assertEqual(p[2], bgs_mask.BGS_FAINT.priorities['MORE_ZGOOD'])
            # BGS_FAINT: {UNOBS: 2000, MORE_ZWARN: 2000, MORE_ZGOOD: 1000, DONE: 2, OBS: 1, DONOTOBSERVE: 0}

            # ADM but in DARK conditions, BGS_FAINT should behave as
            # ADM for other target classes.
            z = self.zcat.copy()
            z['NUMOBS'] = [0, 1, 1]
            z['ZWARN'] = [1, 1, 0]
            p = make_mtl(t, "DARK|GRAY", zcat=z)["PRIORITY"]

            self.assertEqual(p[0], bgs_mask.BGS_FAINT.priorities['UNOBS'])
            self.assertEqual(p[1], bgs_mask.BGS_FAINT.priorities['DONE'])
            self.assertEqual(p[2], bgs_mask.BGS_FAINT.priorities['DONE'])

            # ADM In BRIGHT conditions BGS BRIGHT targets are
            # ADM never DONE, only MORE_ZGOOD.
            t[desi_target] = desi_mask.BGS_ANY
            t[bgs_target] = bgs_mask.BGS_BRIGHT
            t["PRIORITY_INIT"], t["NUMOBS_INIT"] = initial_priority_numobs(t)
            z['NUMOBS'] = [0, 1, 1]
            z['ZWARN'] = [1, 1, 0]
            p = make_mtl(t, "BRIGHT", zcat=z)["PRIORITY"]

            self.assertEqual(p[0], bgs_mask.BGS_BRIGHT.priorities['UNOBS'])
            self.assertEqual(p[1],
                             bgs_mask.BGS_BRIGHT.priorities['MORE_ZWARN'])
            self.assertEqual(p[2],
                             bgs_mask.BGS_BRIGHT.priorities['MORE_ZGOOD'])
            # BGS_BRIGHT: {UNOBS: 2100, MORE_ZWARN: 2100, MORE_ZGOOD: 1000, DONE: 2, OBS: 1, DONOTOBSERVE: 0}

            # ADM In BRIGHT conditions BGS targets are
            # ADM NEVER done even after 100 observations
            t[desi_target] = desi_mask.BGS_ANY
            t[bgs_target] = bgs_mask.BGS_BRIGHT
            t["PRIORITY_INIT"], t["NUMOBS_INIT"] = initial_priority_numobs(t)
            z['NUMOBS'] = [0, 100, 100]
            z['ZWARN'] = [1, 1, 0]
            p = calc_priority(t, z, "BRIGHT")

            self.assertEqual(p[0], bgs_mask.BGS_BRIGHT.priorities['UNOBS'])
            self.assertEqual(p[1],
                             bgs_mask.BGS_BRIGHT.priorities['MORE_ZWARN'])
            self.assertEqual(p[2],
                             bgs_mask.BGS_BRIGHT.priorities['MORE_ZGOOD'])

            # BGS ZGOOD targets always have lower priority than MWS targets that
            # are not DONE. Exempting the MWS "BACKUP" targets.
            # ADM first discard N/S informational bits from bitmask as these
            # ADM should never trump the other bits.
            bgs_names = [
                name for name in bgs_mask.names()
                if 'NORTH' not in name and 'SOUTH' not in name
            ]
            mws_names = [
                name for name in mws_mask.names() if 'NORTH' not in name
                and 'SOUTH' not in name and 'BACKUP' not in name
            ]

            lowest_mws_priority_unobs = [
                mws_mask[n].priorities['UNOBS'] for n in mws_names
            ]

            lowest_bgs_priority_zgood = np.min(
                [bgs_mask[n].priorities['MORE_ZGOOD'] for n in bgs_names])

            # ADM MORE_ZGOOD and MORE_ZWARN are only meaningful if a
            # ADM target class requests more than 1 observation (except
            # ADM for BGS, which has a numobs=infinity exception)
            lowest_mws_priority_zwarn = [
                mws_mask[n].priorities['MORE_ZWARN'] for n in mws_names
                if mws_mask[n].numobs > 1
            ]
            lowest_mws_priority_zgood = [
                mws_mask[n].priorities['MORE_ZGOOD'] for n in mws_names
                if mws_mask[n].numobs > 1
            ]

            lowest_mws_priority = np.min(
                np.concatenate([
                    lowest_mws_priority_unobs, lowest_mws_priority_zwarn,
                    lowest_mws_priority_zgood
                ]))

            self.assertLess(lowest_bgs_priority_zgood, lowest_mws_priority)