コード例 #1
0
ファイル: targets.py プロジェクト: rongpu/desitarget
def _cmx_calc_priority(targets, priority, obscon,
                       unobs, done, zgood, zwarn, cmx_mask, obsconditions):
    """Special-case logic for target priorities in CMX.

    Parameters
    ----------
    targets : :class:`~numpy.ndarray`
        numpy structured array or astropy Table of targets. Must include
        the column `CMX_TARGET`.
    priority : :class:`~numpy.ndarray`
        Initial priority values set, in calc_priorities().
    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.
    unobs : :class:`~numpy.ndarray`
        Boolean flag on targets indicating state UNOBS.
    done : :class:`~numpy.ndarray`
        Boolean flag on targets indicating state DONE.
    zgood : :class:`~numpy.ndarray`
        Boolean flag on targets indicating state ZGOOD.
    zwarn : :class:`~numpy.ndarray`
        Boolean flag on targets indicating state ZWARN.
    cmx_mask : :class:`~desiutil.bitmask.BitMask`
        The CMX target bitmask.
    obscondtions : :class:`~desiutil.bitmask.BitMask`
        The CMX obsconditions bitmask.

    Returns
    -------
    :class:`~numpy.ndarray`
        The updated priority values.

    Notes
    -----
        - Intended to be called only from within calc_priority(), where any
          pre-processing of the target state flags (uobs, done, zgood, zwarn) is
          handled.

    """
    # Build a whitelist of targets to update
    names_to_update = ['SV0_' + label for label in ('STD_FAINT', 'STD_BRIGHT',
                                                    'BGS', 'MWS', 'WD', 'MWS_FAINT',
                                                    'MWS_CLUSTER', 'MWS_CLUSTER_VERYBRIGHT')]
    names_to_update.extend(['BACKUP_BRIGHT', 'BACKUP_FAINT'])

    for name in names_to_update:
        pricon = obsconditions.mask(cmx_mask[name].obsconditions)
        if (obsconditions.mask(obscon) & pricon) != 0:
            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
コード例 #2
0
def set_obsconditions(targets):
    """set the OBSCONDITIONS mask for each target bit.

    Parameters
    ----------
    targets : :class:`~numpy.ndarray`
        An array of targets generated by, e.g., :mod:`~desitarget.cuts`.
        Must include at least (all of) the columns `DESI_TARGET`,
        `BGS_TARGET`, `MWS_TARGET` or corresponding cmx or SV columns.

    Returns
    -------
    :class:`~numpy.ndarray`
        The OBSCONDITIONS bitmask for the passed targets.

    Notes
    -----
        - the OBSCONDITIONS for each target bit is in the file, e.g.
          data/targetmask.yaml. It can be retrieved using, for example,
          `obsconditions.mask(desi_mask["ELG"].obsconditions)`.
    """
    colnames, masks, _ = main_cmx_or_sv(targets)

    n = len(targets)
    obscon = np.zeros(n, dtype='i4')
    for mask, xxx_target in zip(masks, colnames):
        for name in mask.names():
            # ADM which targets have this bit for this mask set?
            ii = (targets[xxx_target] & mask[name]) != 0
            # ADM under what conditions can that bit be observed?
            if np.any(ii):
                obscon[ii] |= obsconditions.mask(mask[name].obsconditions)

    return obscon
コード例 #3
0
def read_files(scxdir, scnd_mask):
    """Read in all secondary files and concatenate them into one array.

    Parameters
    ----------
    scxdir : :class:`str`
        Directory produced by :func:`~secondary._check_files()`.
    scnd_mask : :class:`desiutil.bitmask.BitMask`, optional
        A mask corresponding to a set of secondary targets, e.g, could
        be ``from desitarget.targetmask import scnd_mask`` for the
        main survey mask.

    Returns
    -------
    :class:`~numpy.ndarray`
        All secondary targets concatenated as one array with columns
        that correspond to `desitarget.secondary.outdatamodel`.
    """
    # ADM the full directory name for the input data files.
    fulldir = os.path.join(scxdir, 'indata')

    scxall = []
    # ADM loop through all of the scx bits.
    for name in scnd_mask.names():
        log.debug('SCND target: {}'.format(name))
        # ADM the full file path without the extension.
        fn = os.path.join(fulldir, scnd_mask[name].filename)
        log.debug('     path:   {}'.format(fn))
        # ADM if the relevant file is a .txt file, read it in.
        if os.path.exists(fn + '.txt'):
            scxin = np.loadtxt(fn + '.txt',
                               usecols=[0, 1, 2, 3, 4, 5],
                               dtype=indatamodel.dtype)
        # ADM otherwise it's a fits file, read it in.
        else:
            scxin = fitsio.read(fn + '.fits', columns=indatamodel.dtype.names)

        # ADM ensure this is a properly constructed numpy array.
        scxin = np.atleast_1d(scxin)

        # ADM the default is 2015.5 for the REF_EPOCH.
        ii = scxin["REF_EPOCH"] == 0
        scxin["REF_EPOCH"][ii] = 2015.5

        # ADM add the other output columns.
        dt = outdatamodel.dtype.descr + suppdatamodel.dtype.descr
        scxout = np.zeros(len(scxin), dtype=dt)
        for col in indatamodel.dtype.names:
            scxout[col] = scxin[col]
        scxout["SCND_TARGET"] = scnd_mask[name]
        scxout["SCND_TARGET_INIT"] = scnd_mask[name]
        scxout["SCND_ORDER"] = np.arange(len(scxin))
        scxout["PRIORITY_INIT"] = scnd_mask[name].priorities['UNOBS']
        scxout["NUMOBS_INIT"] = scnd_mask[name].numobs
        scxout["TARGETID"] = -1
        scxout["OBSCONDITIONS"] =     \
            obsconditions.mask(scnd_mask[name].obsconditions)
        scxall.append(scxout)

    return np.concatenate(scxall)
コード例 #4
0
ファイル: targets.py プロジェクト: rongpu/desitarget
def set_obsconditions(targets, scnd=False):
    """set the OBSCONDITIONS mask for each target bit.

    Parameters
    ----------
    targets : :class:`~numpy.ndarray`
        An array of targets generated by, e.g., :mod:`~desitarget.cuts`.
        Must include at least (all of) the columns `DESI_TARGET`,
        `BGS_TARGET`, `MWS_TARGET` or corresponding cmx or SV columns.
    scnd : :class:`bool`, optional, defaults to ``False``
        If ``True`` then make all of the comparisons on the `SCND_TARGET`
        column instead of `DESI_TARGET`, `BGS_TARGET` and `MWS_TARGET`.

    Returns
    -------
    :class:`~numpy.ndarray`
        The OBSCONDITIONS bitmask for the passed targets.

    Notes
    -----
        - the OBSCONDITIONS for each target bit is in the file, e.g.
          data/targetmask.yaml. It can be retrieved using, for example,
          `obsconditions.mask(desi_mask["ELG"].obsconditions)`.
    """
    colnames, masks, _ = main_cmx_or_sv(targets, scnd=scnd)
    # ADM if we requested secondary targets, the needed information
    # ADM was returned as the last part of each array.
    if scnd:
        colnames, masks = colnames[-1:], masks[-1:]

    n = len(targets)
    from desitarget.mtl import mtldatamodel as mtldm
    obscon = np.zeros(n, dtype=mtldm["OBSCONDITIONS"].dtype)
    for mask, xxx_target in zip(masks, colnames):
        for name in mask.names():
            # ADM which targets have this bit for this mask set?
            ii = (targets[xxx_target] & mask[name]) != 0
            # ADM under what conditions can that bit be observed?
            if np.any(ii):
                obscon[ii] |= obsconditions.mask(mask[name].obsconditions)

    return obscon
コード例 #5
0
ファイル: targets.py プロジェクト: qmxp55/desitarget
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
コード例 #6
0
ファイル: targets.py プロジェクト: qmxp55/desitarget
def initial_priority_numobs(
        targets,
        scnd=False,
        obscon="DARK|GRAY|BRIGHT|POOR|TWILIGHT12|TWILIGHT18"):
    """highest initial priority and numobs for an array of target bits.

    Parameters
    ----------
    targets : :class:`~numpy.ndarray`
        An array of targets generated by, e.g., :mod:`~desitarget.cuts`.
        Must include at least (all of) the columns `DESI_TARGET`,
        `BGS_TARGET`, `MWS_TARGET` or corresponding cmx or SV columns.
    scnd : :class:`bool`, optional, defaults to ``False``
        If ``True`` then make all of the comparisons on the `SCND_TARGET`
        column instead of `DESI_TARGET`, `BGS_TARGET` and `MWS_TARGET`.
    obscon : :class:`str`, optional, defaults to almost all OBSCONDITIONS
        A combination of strings that are in the desitarget bitmask yaml
        file (specifically in `desitarget.targetmask.obsconditions`).

    Returns
    -------
    :class:`~numpy.ndarray`
        An array of integers corresponding to the highest initial
        priority for each target consistent with the constraints
        on observational conditions imposed by `obscon`.
    :class:`~numpy.ndarray`
        An array of integers corresponding to the largest number of
        observations for each target consistent with the constraints
        on observational conditions imposed by `obscon`.

    Notes
    -----
        - the initial priority for each target bit is in the file, e.g.,
          data/targetmask.yaml. It can be retrieved using, for example,
          `desi_mask["ELG"].priorities["UNOBS"]`.
        - the input obscon string can be converted to a bitmask using
          `desitarget.targetmask.obsconditions.mask(blat)`.
    """
    colnames, masks, _ = main_cmx_or_sv(targets, scnd=scnd)
    # ADM if we requested secondary targets, the needed information
    # ADM was returned as the last part of each array.
    if scnd:
        colnames, masks = colnames[-1:], masks[-1:]

    # ADM set up the output arrays.
    outpriority = np.zeros(len(targets), dtype='int')
    # ADM remember that calibs have NUMOBS of -1.
    outnumobs = np.zeros(len(targets), dtype='int') - 1

    # ADM convert the passed obscon string to bits.
    obsbits = obsconditions.mask(obscon)

    # ADM loop through the masks to establish all bitnames of interest.
    for colname, mask in zip(colnames, masks):
        # ADM first determine which bits actually have priorities.
        bitnames = []
        for name in mask.names():
            try:
                _ = mask[name].priorities["UNOBS"]
                # ADM also only consider bits with correct OBSCONDITIONS.
                obsforname = obsconditions.mask(mask[name].obsconditions)
                if (obsforname & obsbits) != 0:
                    bitnames.append(name)
            except KeyError:
                pass

        # ADM loop through the relevant bits updating with the highest
        # ADM priority and the largest value of NUMOBS.
        for name in bitnames:
            # ADM indexes in the DESI/MWS/BGS_TARGET column that have this bit set
            istarget = (targets[colname] & mask[name]) != 0
            # ADM for each index, determine where this bit is set and the priority
            # ADM for this bit is > than the currently stored priority.
            w = np.where((mask[name].priorities['UNOBS'] >= outpriority)
                         & istarget)[0]
            # ADM where a larger priority trumps the stored priority, update the priority
            if len(w) > 0:
                outpriority[w] = mask[name].priorities['UNOBS']
            # ADM for each index, determine where this bit is set and whether NUMOBS
            # ADM for this bit is > than the currently stored NUMOBS.
            w = np.where((mask[name].numobs >= outnumobs) & istarget)[0]
            # ADM where a larger NUMOBS trumps the stored NUMOBS, update NUMOBS.
            if len(w) > 0:
                outnumobs[w] = mask[name].numobs

    return outpriority, outnumobs
コード例 #7
0
ファイル: targets.py プロジェクト: rongpu/desitarget
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
コード例 #8
0
ファイル: targets.py プロジェクト: rongpu/desitarget
def calc_numobs_more(targets, zcat, obscon):
    """
    Calculate target NUMOBS_MORE 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) `TARGETID` and `NUMOBS_INIT`.
    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.

    Returns
    -------
    :class:`~numpy.array`
        Integer array of number of additional observations (NUMOBS_MORE).

    Notes
    -----
        - Will automatically detect if the passed targets are main
          survey, commissioning or SV and behave accordingly.
        - Most targets are updated to NUMOBS_MORE = NUMOBS_INIT-NUMOBS.
          Special cases include BGS targets which always get NUMOBS_MORE
          of 1 in bright time and QSO "tracer" targets which always get
          NUMOBS_MORE=0 in dark time.
    """
    # 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]

    # ADM main case, just decrement by NUMOBS.
    numobs_more = np.maximum(0, targets['NUMOBS_INIT'] - zcat['NUMOBS'])

    if survey != 'cmx':
        # ADM BGS targets are observed during the BRIGHT survey, regardless
        # ADM of how often they've previously been observed.
        if (obsconditions.mask(obscon) & obsconditions.mask("BRIGHT")) != 0:
            ii = targets[desi_target] & desi_mask.BGS_ANY > 0
            numobs_more[ii] = 1

    if survey == 'main':
        # ADM If a DARK layer target is confirmed to have a good redshift
        # ADM at z < zcut it always needs just one total observation.
        # ADM (zcut is defined at the top of this module).
        if (obsconditions.mask(obscon) & obsconditions.mask("DARK")) != 0:
            ii = (zcat['ZWARN'] == 0)
            ii &= (zcat['Z'] < zcut)
            ii &= (zcat['NUMOBS'] > 0)
            numobs_more[ii] = 0

        # ADM We will have to be more careful if some DARK layer targets
        # ADM other than QSOs request more than one observation.
        check = {bit: desi_mask[bit].numobs for bit in desi_mask.names() if
                 'DARK' in desi_mask[bit].obsconditions and 'QSO' not in bit
                 and desi_mask[bit].numobs > 1}
        if len(check) > 1:
            msg = "logic not programmed for main survey dark-time targets other"
            msg += " than QSOs having NUMOBS_INIT > 1: {}".format(check)
            log.critical(msg)
            raise ValueError(msg)

    return numobs_more
コード例 #9
0
ファイル: mtl.py プロジェクト: jinyiY/desitarget
def make_mtl(targets, obscon, zcat=None, trim=False, scnd=None):
    """Adds NUMOBS, PRIORITY, and OBSCONDITIONS columns to a targets table.

    Parameters
    ----------
    targets : :class:`~numpy.array` or `~astropy.table.Table`
        A numpy rec array or astropy Table with at least the columns
        ``TARGETID``, ``DESI_TARGET``, ``NUMOBS_INIT``, ``PRIORITY_INIT``.
        or the corresponding columns for SV or commissioning.
    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.
    zcat : :class:`~astropy.table.Table`, optional
        Redshift catalog table with columns ``TARGETID``, ``NUMOBS``, ``Z``,
        ``ZWARN``.
    trim : :class:`bool`, optional
        If ``True`` (default), don't include targets that don't need
        any more observations.  If ``False``, include every input target.
    scnd : :class:`~numpy.array`, `~astropy.table.Table`, optional
        A set of secondary targets associated with the `targets`. As with
        the `target` must include at least ``TARGETID``, ``NUMOBS_INIT``,
        ``PRIORITY_INIT`` or the corresponding SV columns.
        The secondary targets will be padded to have the same columns
        as the targets, and concatenated with them.

    Returns
    -------
    :class:`~astropy.table.Table`
        MTL Table with targets columns plus:

        * NUMOBS_MORE    - number of additional observations requested
        * PRIORITY       - target priority (larger number = higher priority)
        * OBSCONDITIONS  - replaces old GRAYLAYER
    """
    start = time()
    # ADM set up the default logger.
    from desiutil.log import get_logger
    log = get_logger()

    # ADM if secondaries were passed, concatenate them with the targets.
    if scnd is not None:
        nrows = len(scnd)
        log.info(
            'Pad {} primary targets with {} secondaries...t={:.1f}s'.format(
                len(targets), nrows,
                time() - start))
        padit = np.zeros(nrows, dtype=targets.dtype)
        sharedcols = set(targets.dtype.names).intersection(
            set(scnd.dtype.names))
        for col in sharedcols:
            padit[col] = scnd[col]
        targets = np.concatenate([targets, padit])
        # APC Propagate a flag on which targets came from scnd
        is_scnd = np.repeat(False, len(targets))
        is_scnd[-nrows:] = True
        log.info('Done with padding...t={:.1f}s'.format(time() - start))

    # ADM determine whether the input targets are main survey, cmx or SV.
    colnames, masks, survey = main_cmx_or_sv(targets)
    # ADM set the first column to be the "desitarget" column
    desi_target, desi_mask = colnames[0], masks[0]

    # Trim targets from zcat that aren't in original targets table
    if zcat is not None:
        ok = np.in1d(zcat['TARGETID'], targets['TARGETID'])
        num_extra = np.count_nonzero(~ok)
        if num_extra > 0:
            log.warning("Ignoring {} zcat entries that aren't "
                        "in the input target list".format(num_extra))
            zcat = zcat[ok]

    n = len(targets)
    # ADM if the input target columns were incorrectly called NUMOBS or PRIORITY
    # ADM rename them to NUMOBS_INIT or PRIORITY_INIT.
    # ADM Note that the syntax is slightly different for a Table.
    for name in ['NUMOBS', 'PRIORITY']:
        if isinstance(targets, Table):
            try:
                targets.rename_column(name, name + '_INIT')
            except KeyError:
                pass
        else:
            targets.dtype.names = [
                name + '_INIT' if col == name else col
                for col in targets.dtype.names
            ]

    # ADM if a redshift catalog was passed, order it to match the input targets
    # ADM catalog on 'TARGETID'.
    if zcat is not None:
        # ADM there might be a quicker way to do this?
        # ADM set up a dictionary of the indexes of each target id.
        d = dict(tuple(zip(targets["TARGETID"], np.arange(n))))
        # ADM loop through the zcat and look-up the index in the dictionary.
        zmatcher = np.array([d[tid] for tid in zcat["TARGETID"]])
        ztargets = zcat
        if ztargets.masked:
            unobs = ztargets['NUMOBS'].mask
            ztargets['NUMOBS'][unobs] = 0
            unobsz = ztargets['Z'].mask
            ztargets['Z'][unobsz] = -1
            unobszw = ztargets['ZWARN'].mask
            ztargets['ZWARN'][unobszw] = -1
    else:
        ztargets = Table()
        ztargets['TARGETID'] = targets['TARGETID']
        ztargets['NUMOBS'] = np.zeros(n, dtype=np.int32)
        ztargets['Z'] = -1 * np.ones(n, dtype=np.float32)
        ztargets['ZWARN'] = -1 * np.ones(n, dtype=np.int32)
        # ADM if zcat wasn't passed, there is a one-to-one correspondence
        # ADM between the targets and the zcat.
        zmatcher = np.arange(n)

    # ADM extract just the targets that match the input zcat.
    targets_zmatcher = targets[zmatcher]

    # ADM use passed value of NUMOBS_INIT instead of calling the memory-heavy calc_numobs.
    # ztargets['NUMOBS_MORE'] = np.maximum(0, calc_numobs(ztargets) - ztargets['NUMOBS'])
    ztargets['NUMOBS_MORE'] = np.maximum(
        0, targets_zmatcher['NUMOBS_INIT'] - ztargets['NUMOBS'])

    # ADM need a minor hack to ensure BGS targets are observed once
    # ADM (and only once) every time during the BRIGHT survey, regardless
    # ADM of how often they've previously been observed. I've turned this
    # ADM off for commissioning. Not sure if we'll keep it in general.
    if survey != 'cmx':
        # ADM only if we're considering bright survey conditions.
        if (obsconditions.mask(obscon) & obsconditions.mask("BRIGHT")) != 0:
            ii = targets_zmatcher[desi_target] & desi_mask.BGS_ANY > 0
            ztargets['NUMOBS_MORE'][ii] = 1
    if survey == 'main':
        # If the object is confirmed to be a tracer QSO, then don't request more observations
        if (obsconditions.mask(obscon) & obsconditions.mask("DARK")) != 0:
            if zcat is not None:
                ii = ztargets['SPECTYPE'] == 'QSO'
                ii &= (ztargets['ZWARN'] == 0)
                ii &= (ztargets['Z'] < 2.1)
                ii &= (ztargets['NUMOBS'] > 0)
                ztargets['NUMOBS_MORE'][ii] = 0

    # ADM assign priorities, note that only things in the zcat can have changed priorities.
    # ADM anything else will be assigned PRIORITY_INIT, below.
    priority = calc_priority(targets_zmatcher, ztargets, obscon)

    # If priority went to 0==DONOTOBSERVE or 1==OBS or 2==DONE, then NUMOBS_MORE should also be 0.
    # ## mtl['NUMOBS_MORE'] = ztargets['NUMOBS_MORE']
    ii = (priority <= 2)
    log.info(
        '{:d} of {:d} targets have priority zero, setting N_obs=0.'.format(
            np.sum(ii), n))
    ztargets['NUMOBS_MORE'][ii] = 0

    # - Set the OBSCONDITIONS mask for each target bit.
    obsconmask = set_obsconditions(targets)

    # APC obsconmask will now be incorrect for secondary-only targets. Fix this
    # APC using the mask on secondary targets.
    if scnd is not None:
        obsconmask[is_scnd] = set_obsconditions(targets[is_scnd], scnd=True)

    # ADM set up the output mtl table.
    mtl = Table(targets)
    mtl.meta['EXTNAME'] = 'MTL'
    # ADM any target that wasn't matched to the ZCAT should retain its
    # ADM original (INIT) value of PRIORITY and NUMOBS.
    mtl['NUMOBS_MORE'] = mtl['NUMOBS_INIT']
    mtl['PRIORITY'] = mtl['PRIORITY_INIT']
    # ADM now populate the new mtl columns with the updated information.
    mtl['OBSCONDITIONS'] = obsconmask
    mtl['PRIORITY'][zmatcher] = priority
    mtl['NUMOBS_MORE'][zmatcher] = ztargets['NUMOBS_MORE']

    # Filter out any targets marked as done.
    if trim:
        notdone = mtl['NUMOBS_MORE'] > 0
        log.info('{:d} of {:d} targets are done, trimming these'.format(
            len(mtl) - np.sum(notdone), len(mtl)))
        mtl = mtl[notdone]

    # Filtering can reset the fill_value, which is just wrong wrong wrong
    # See https://github.com/astropy/astropy/issues/4707
    # and https://github.com/astropy/astropy/issues/4708
    mtl['NUMOBS_MORE'].fill_value = -1

    log.info('Done...t={:.1f}s'.format(time() - start))

    return mtl
コード例 #10
0
def nextFieldSelector(obsplan, mjd, conditions, tilesObserved, slew,
                      previous_ra, previous_dec):
    """
    Returns the first tile for which the current time falls inside
    its assigned LST window and is far enough from the Moon and
    planets.

    Args:
        obsplan: string, FITS file containing the afternoon plan
        mjd: float, current time
        conditions: dictionnary containing the weather info
        tilesObserved: list containing the tileID of all completed tiles
        slew: bool, True if a slew time needs to be taken into account
        previous_ra: float, ra of the previous observed tile (degrees)
        previous_dec: float, dec of the previous observed tile (degrees)

    Returns:
        target: dictionnary containing the following keys:
                'tileID', 'RA', 'DEC', 'Program', 'Ebmv', 'maxLen',
                'MoonFrac', 'MoonDist', 'MoonAlt', 'DESsn2', 'Status',
                'Exposure', 'obsSN2', 'obsConds'
        overhead: float (seconds)
    """

    hdulist = pyfits.open(obsplan)
    tiledata = hdulist[1].data
    moonfrac = hdulist[0].header['MOONFRAC']
    tileID = tiledata['TILEID']
    tmin = tiledata['LSTMIN']
    tmax = tiledata['LSTMAX']
    explen = tiledata['MAXEXPLEN'] / 240.0
    ra = tiledata['RA']
    dec = tiledata['DEC']
    program = tiledata['PROGRAM']
    obsconds = tiledata['OBSCONDITIONS']

    lst = mjd2lst(mjd)
    dt = Time(mjd, format='mjd')
    found = False
    for i in range(len(tileID)):
        dra = np.abs(ra[i] - previous_ra)
        if dra > 180.0:
            dra = 360.0 - dra
        ddec = np.abs(dec[i] - previous_dec)
        overhead = setup_time(slew, dra, ddec)
        t1 = tmin[i] + overhead / 240.0
        t2 = tmax[i] - explen[i]

        if (((t1 <= t2) and (lst > t1 and lst < t2))
                or ((t2 < t1) and ((lst > t1 and t1 <= 360.0) or
                                   (lst >= 0.0 and lst < t2)))):
            if (avoidObject(dt.datetime, ra[i], dec[i])
                    and airMassCalculator(ra[i], dec[i], lst) < MAX_AIRMASS):
                moondist, moonalt, moonaz = moonLoc(dt.datetime, ra[i], dec[i])
                if ((len(tilesObserved) > 0
                     and tileID[i] not in tilesObserved['TILEID'])
                        or len(tilesObserved) == 0):
                    if (((moonalt < 0.0 and
                          (obsconds[i] & obsbits.mask('DARK')) != 0)) or
                        (moonalt >= 0.0 and
                         (((moonfrac < 0.2 or (moonalt * moonfrac < 12.0))
                           and moondist > MIN_MOON_SEP and
                           (obsconds[i] & obsbits.mask('GRAY')) != 0) or
                          ((obsconds[i] & obsbits.mask('BRIGHT')) != 0
                           and moondist > MIN_MOON_SEP_BGS)))):
                        found = True
                        break

    if found == True:
        tileID = tiledata['TILEID'][i]
        RA = ra[i]
        DEC = dec[i]
        Ebmv = tiledata['EBV_MED'][i]
        maxLen = tiledata['MAXEXPLEN'][i]
        DESsn2 = 100.0  # Some made-up number -> has to be the same as the reference in exposurecalc.py
        status = tiledata['STATUS'][i]
        exposure = -1.0  # Updated after observation
        obsSN2 = -1.0  # Idem
        target = {
            'tileID': tileID,
            'RA': RA,
            'DEC': DEC,
            'Program': program[i],
            'Ebmv': Ebmv,
            'maxLen': maxLen,
            'MoonFrac': moonfrac,
            'MoonDist': moondist,
            'MoonAlt': moonalt,
            'DESsn2': DESsn2,
            'Status': status,
            'Exposure': exposure,
            'obsSN2': obsSN2,
            'obsConds': obsconds[i]
        }
    else:
        target = None
    return target, overhead
コード例 #11
0
def make_mtl(targets, zcat=None, trim=False):
    """Adds NUMOBS, PRIORITY, and OBSCONDITIONS columns to a targets table.

    Parameters
    ----------
    targets : :class:`~numpy.array` or `~astropy.table.Table`
        A numpy rec array or astropy Table with at least the columns
        ``TARGETID``, ``DESI_TARGET``, ``NUMOBS_INIT``, ``PRIORITY_INIT``.
        or the corresponding columns for SV or commissioning.
    zcat : :class:`~astropy.table.Table`, optional
        Redshift catalog table with columns ``TARGETID``, ``NUMOBS``, ``Z``,
        ``ZWARN``.
    trim : :class:`bool`, optional
        If ``True`` (default), don't include targets that don't need
        any more observations.  If ``False``, include every input target.

    Returns
    -------
    :class:`~astropy.table.Table`
        MTL Table with targets columns plus:

        * NUMOBS_MORE    - number of additional observations requested
        * PRIORITY       - target priority (larger number = higher priority)
        * OBSCONDITIONS  - replaces old GRAYLAYER
    """
    # ADM set up the default logger.
    from desiutil.log import get_logger
    log = get_logger()

    # ADM determine whether the input targets are main survey, cmx or SV.
    colnames, masks, survey = main_cmx_or_sv(targets)
    # ADM set the first column to be the "desitarget" column
    desi_target, desi_mask = colnames[0], masks[0]

    # Trim targets from zcat that aren't in original targets table
    if zcat is not None:
        ok = np.in1d(zcat['TARGETID'], targets['TARGETID'])
        num_extra = np.count_nonzero(~ok)
        if num_extra > 0:
            log.warning("Ignoring {} zcat entries that aren't "
                        "in the input target list".format(num_extra))
            zcat = zcat[ok]

    n = len(targets)
    # ADM if the input target columns were incorrectly called NUMOBS or PRIORITY
    # ADM rename them to NUMOBS_INIT or PRIORITY_INIT.
    # ADM Note that the syntax is slightly different for a Table.
    for name in ['NUMOBS', 'PRIORITY']:
        if isinstance(targets, Table):
            try:
                targets.rename_column(name, name + '_INIT')
            except KeyError:
                pass
        else:
            targets.dtype.names = [
                name + '_INIT' if col == name else col
                for col in targets.dtype.names
            ]

    # ADM if a redshift catalog was passed, order it to match the input targets
    # ADM catalog on 'TARGETID'.
    if zcat is not None:
        # ADM there might be a quicker way to do this?
        # ADM set up a dictionary of the indexes of each target id.
        d = dict(tuple(zip(targets["TARGETID"], np.arange(n))))
        # ADM loop through the zcat and look-up the index in the dictionary.
        zmatcher = np.array([d[tid] for tid in zcat["TARGETID"]])
        ztargets = zcat
        if ztargets.masked:
            unobs = ztargets['NUMOBS'].mask
            ztargets['NUMOBS'][unobs] = 0
            unobsz = ztargets['Z'].mask
            ztargets['Z'][unobsz] = -1
            unobszw = ztargets['ZWARN'].mask
            ztargets['ZWARN'][unobszw] = -1
    else:
        ztargets = Table()
        ztargets['TARGETID'] = targets['TARGETID']
        ztargets['NUMOBS'] = np.zeros(n, dtype=np.int32)
        ztargets['Z'] = -1 * np.ones(n, dtype=np.float32)
        ztargets['ZWARN'] = -1 * np.ones(n, dtype=np.int32)
        # ADM if zcat wasn't passed, there is a one-to-one correspondence
        # ADM between the targets and the zcat.
        zmatcher = np.arange(n)

    # ADM extract just the targets that match the input zcat.
    targets_zmatcher = targets[zmatcher]

    # ADM use passed value of NUMOBS_INIT instead of calling the memory-heavy calc_numobs.
    # ztargets['NUMOBS_MORE'] = np.maximum(0, calc_numobs(ztargets) - ztargets['NUMOBS'])
    ztargets['NUMOBS_MORE'] = np.maximum(
        0, targets_zmatcher['NUMOBS_INIT'] - ztargets['NUMOBS'])

    # ADM we need a minor hack to ensure that BGS targets are observed once (and only once)
    # ADM every time, regardless of how many times they've previously been observed.
    # ADM I've turned this off for commissioning. Not sure if we'll keep it in general.
    if survey != 'cmx':
        ii = targets_zmatcher[desi_target] & desi_mask.BGS_ANY > 0
        ztargets['NUMOBS_MORE'][ii] = 1

    # ADM assign priorities, note that only things in the zcat can have changed priorities.
    # ADM anything else will be assigned PRIORITY_INIT, below.
    priority = calc_priority(targets_zmatcher, ztargets)

    # If priority went to 0==DONOTOBSERVE or 1==OBS or 2==DONE, then NUMOBS_MORE should also be 0.
    # ## mtl['NUMOBS_MORE'] = ztargets['NUMOBS_MORE']
    ii = (priority <= 2)
    log.info(
        '{:d} of {:d} targets have priority zero, setting N_obs=0.'.format(
            np.sum(ii), n))
    ztargets['NUMOBS_MORE'][ii] = 0

    # - Set the OBSCONDITIONS mask for each target bit.
    obscon = np.zeros(n, dtype='i4')
    for mask, xxx_target in zip(masks, colnames):
        for name in mask.names():
            # - which targets have this bit for this mask set?
            ii = (targets[xxx_target] & mask[name]) != 0
            # - under what conditions can that bit be observed?
            if np.any(ii):
                obscon[ii] |= obsconditions.mask(mask[name].obsconditions)

    # ADM set up the output mtl table.
    mtl = Table(targets)
    mtl.meta['EXTNAME'] = 'MTL'
    # ADM any target that wasn't matched to the ZCAT should retain its
    # ADM original (INIT) value of PRIORITY and NUMOBS.
    mtl['NUMOBS_MORE'] = mtl['NUMOBS_INIT']
    mtl['PRIORITY'] = mtl['PRIORITY_INIT']
    # ADM now populate the new mtl columns with the updated information.
    mtl['OBSCONDITIONS'] = obscon
    mtl['PRIORITY'][zmatcher] = priority
    mtl['NUMOBS_MORE'][zmatcher] = ztargets['NUMOBS_MORE']

    # Filter out any targets marked as done.
    if trim:
        notdone = mtl['NUMOBS_MORE'] > 0
        log.info('{:d} of {:d} targets are done, trimming these'.format(
            len(mtl) - np.sum(notdone), len(mtl)))
        mtl = mtl[notdone]

    # Filtering can reset the fill_value, which is just wrong wrong wrong
    # See https://github.com/astropy/astropy/issues/4707
    # and https://github.com/astropy/astropy/issues/4708
    mtl['NUMOBS_MORE'].fill_value = -1

    return mtl
コード例 #12
0
    def afternoonPlan(self, day_stats, tiles_observed):
        """
        All the file names are hard coded, so there is no need to
        have them as arguments to this function.

        Args:
            day_stats: dictionnary containing the following keys:
                       'MJDsunset', 'MJDsunrise', 'MJDetwi', 'MJDmtwi', 'MJDe13twi',
                       'MJDm13twi', 'MJDmoonrise', 'MJDmoonset', 'MoonFrac', 'dirName'
            tiles_observed: table with follwing columns: tileID, status

        Returns:
            string containg the filename for today's plan; it has the format
            obsplanYYYYMMDD.fits
        """

        year = int(
            np.floor(
                (day_stats['MJDsunset'] - tiles_observed.meta['MJDBEGIN'])) /
            365.25) + 1
        # Adjust DARK time program tile priorities
        # From the DESI document 1767 (v3) "Baseline survey strategy":
        # In the northern galactic cap:
        # Year - Layer 1 tiles - Layer 2 tiles - Layer 3 tiles - Layer 4 tiles
        # 1         900              200              0                0
        # 2         485              415            200              200
        # 3           0              770            100              100
        # 4           0                0            450              450
        # 5           0                0            685              685
        # In the southern galactic cap:
        # Year - Layer 1 tiles - Layer 2 tiles - Layer 3 tiles - Layer 4 tiles
        # 1         450                0              0                0
        # 2         165              300              0                0
        # 3           0              315             90               90
        # 4           0                0            265              260
        # 5           0                0            260              265
        # Priorities shall be adjusted accordingly.
        # Priorities can only be set to 0, 1, 2 or 8, 9, 10 by
        # *human intervention*!

        # Update status
        nto = len(tiles_observed)
        for i in range(nto):
            j = np.where(self.tileID == tiles_observed['TILEID'][i])
            self.status[j] = tiles_observed['STATUS'][i]

        planList0 = []

        lst15evening = mjd2lst(day_stats['MJDetwi'])
        lst15morning = mjd2lst(day_stats['MJDmtwi'])
        lst13evening = mjd2lst(day_stats['MJDe13twi'])
        lst13morning = mjd2lst(day_stats['MJDe13twi'])
        # Dark and grey Pass 1, 2, 3, & 4 are numbered 0, 1, 2, 3.
        # BGS, pass 1, 2 & 3 are numbered 4, 5, 6.
        for i in range(len(self.tileID)):
            if (self.status[i] < 2):
                # Add this tile to the plan, first adjust its priority.
                if (((self.obsconds[i] & obsbits.mask('DARK|GRAY')) != 0)
                        and (lst15evening < self.LSTmin[i]
                             and self.LSTmax[i] < lst15morning)):
                    if year == 1:
                        if ((self.cap[i] == 'N' and
                             (self.Pass[i] == 2 or self.Pass[i] == 3))
                                or (self.cap[i] == 'S' and
                                    (self.Pass[i] == 1 or self.Pass[i] == 2
                                     or self.Pass[i] == 3))):
                            self.priority[i] = 7
                        if (self.cap[i] == 'N' and self.Pass[i] == 0
                                and self.priority[i] > 3):
                            self.priority[i] -= 1
                    if year == 2:
                        if (self.cap[i] == 'S'
                                and (self.Pass[i] == 2 or self.Pass[i] == 3)):
                            self.priority[i] = 7
                    if year == 3:
                        if self.Pass[i] == 0:
                            self.priority[i] = 3
                        if self.Pass[i] == 1 and self.priority[i] > 3:
                            self.priority[i] -= 1
                    if year >= 4:
                        if self.Pass[i] <= 1:
                            self.priority[i] = 3
                elif (((self.obsconds[i] & obsbits.mask('BRIGHT')) != 0)
                      and (lst13evening < self.LSTmin[i]
                           and self.LSTmax[i] < lst13morning)):
                    if year == 1:
                        if (self.Pass[i] == 4 or self.Pass[i] == 5):
                            self.priority[i] -= 1
                    if year == 2 or year == 3:
                        if self.Pass[i] == 4:
                            self.priority[i] = 3
                        if self.Pass[i] == 5:
                            self.priority[i] -= 1
                    if year >= 4:
                        if self.Pass[i] <= 5:
                            self.priority[i] = 3

                planList0.append(
                    (self.tileID[i], self.RA[i], self.DEC[i], self.Ebmv[i],
                     self.LSTmin[i], self.LSTmax[i], self.maxExpLen[i],
                     self.priority[i], self.status[i], self.program[i],
                     self.obsconds[i]))

        planList = sorted(planList0, key=itemgetter(7), reverse=False)
        cols = np.rec.array(planList,
                            names=('TILEID', 'RA', 'DEC', 'EBV_MED', 'LSTMIN',
                                   'LSTMAX', 'MAXEXPLEN', 'PRIORITY', 'STATUS',
                                   'PROGRAM', 'OBSCONDITIONS'),
                            formats=[
                                'i4', 'f8', 'f8', 'f8', 'f4', 'f4', 'f4', 'i4',
                                'i4', 'a6', 'i2'
                            ])

        tbhdu = pyfits.BinTableHDU.from_columns(cols)

        prihdr = pyfits.Header()
        prihdr['MOONFRAC'] = day_stats['MoonFrac']
        prihdu = pyfits.PrimaryHDU(header=prihdr)
        filename = 'obsplan' + day_stats['dirName'] + '.fits'
        thdulist = pyfits.HDUList([prihdu, tbhdu])
        thdulist.writeto(filename, clobber=True)

        tilesTODO = len(planList)

        return tilesTODO, filename