示例#1
0
    def setUp(self):
        self.targets = Table()
        self.types = np.array(['ELG', 'LRG', 'QSO', 'QSO', 'ELG'])
        self.priorities = [Mx[t].priorities['UNOBS'] for t in self.types]
        self.post_prio = [Mx[t].priorities['MORE_ZGOOD'] for t in self.types]
        self.post_prio[0] = 2  # ELG
        self.post_prio[1] = 2  # LRG...all one-pass
        self.post_prio[2] = 2  # lowz QSO
        nt = len(self.types)
        # ADM add some "extra" columns that are needed for observations.
        for col in ["RA", "DEC", "PARALLAX", "PMRA", "PMDEC", "REF_EPOCH"]:
            self.targets[col] = np.zeros(nt, dtype=mtldatamodel[col].dtype)
        self.targets['DESI_TARGET'] = [Mx[t].mask for t in self.types]
        for col in ['BGS_TARGET', 'MWS_TARGET', 'SUBPRIORITY']:
            self.targets[col] = np.zeros(nt, dtype=mtldatamodel[col].dtype)
        n = len(self.targets)
        self.targets['ZFLUX'] = 10**((22.5 - np.linspace(20, 22, n)) / 2.5)
        self.targets['TARGETID'] = list(range(n))
        # ADM determine the initial PRIORITY and NUMOBS.
        pinit, ninit = initial_priority_numobs(self.targets,
                                               obscon="DARK|GRAY")
        self.targets["PRIORITY_INIT"] = pinit
        self.targets["NUMOBS_INIT"] = ninit

        # - reverse the order for zcat to make sure joins work
        self.zcat = Table()
        self.zcat['TARGETID'] = self.targets['TARGETID'][-2::-1]
        self.zcat['Z'] = [2.5, 1.0, 0.5, 1.0]
        self.zcat['ZWARN'] = [0, 0, 0, 0]
        self.zcat['NUMOBS'] = [1, 1, 1, 1]
        self.zcat['SPECTYPE'] = ['QSO', 'QSO', 'GALAXY', 'GALAXY']
示例#2
0
    def setUp(self):
        self.targets = Table()
        self.types = np.array(['ELG', 'LRG', 'QSO', 'QSO', 'ELG'])
        self.priorities = [Mx[t].priorities['UNOBS'] for t in self.types]
        self.post_prio = [Mx[t].priorities['MORE_ZGOOD'] for t in self.types]
        self.post_prio[0] = 2  # ELG
        self.post_prio[1] = 2  # LRG...all one-pass
        self.post_prio[2] = 2  # lowz QSO
        self.targets['DESI_TARGET'] = [Mx[t].mask for t in self.types]
        self.targets['BGS_TARGET'] = np.zeros(len(self.types), dtype=np.int64)
        self.targets['MWS_TARGET'] = np.zeros(len(self.types), dtype=np.int64)
        n = len(self.targets)
        self.targets['ZFLUX'] = 10**((22.5 - np.linspace(20, 22, n)) / 2.5)
        self.targets['TARGETID'] = list(range(n))
        # ADM determine the initial PRIORITY and NUMOBS.
        pinit, ninit = initial_priority_numobs(self.targets)
        self.targets["PRIORITY_INIT"] = pinit
        self.targets["NUMOBS_INIT"] = ninit

        # - reverse the order for zcat to make sure joins work
        self.zcat = Table()
        self.zcat['TARGETID'] = self.targets['TARGETID'][-2::-1]
        self.zcat['Z'] = [2.5, 1.0, 0.5, 1.0]
        self.zcat['ZWARN'] = [0, 0, 0, 0]
        self.zcat['NUMOBS'] = [1, 1, 1, 1]
        self.zcat['SPECTYPE'] = ['QSO', 'QSO', 'GALAXY', 'GALAXY']
示例#3
0
    def test_cmx_priorities(self):
        """Test that priority calculation can handle commissioning files.
        """
        t = self.targets.copy()
        z = self.zcat

        # ADM restructure the table to look like a commissioning table.
        t.rename_column('DESI_TARGET', 'CMX_TARGET')
        t.remove_column('BGS_TARGET')
        t.remove_column('MWS_TARGET')

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

        # ADM retrieve the cmx_mask.
        colnames, masks, _ = main_cmx_or_sv(t)
        cmx_mask = masks[0]

        # ADM test handling of unobserved SV0_BGS and SV0_MWS
        for name, obscon in [("SV0_BGS", "BRIGHT"), ("SV0_MWS", "POOR")]:
            t['CMX_TARGET'] = cmx_mask[name]
            self.assertTrue(
                np.all(
                    calc_priority(t, z, obscon) ==
                    cmx_mask[name].priorities['UNOBS']))

        # ADM done is Done, regardless of ZWARN.
        for name, obscon in [("SV0_BGS", "BRIGHT"), ("SV0_MWS", "POOR")]:
            t['CMX_TARGET'] = cmx_mask[name]
            t["PRIORITY_INIT"], t["NUMOBS_INIT"] = initial_priority_numobs(t)

            # APC: Use NUMOBS_INIT here to avoid hardcoding NOBS corresponding to "done".
            numobs_done = t['NUMOBS_INIT'][0]
            z['NUMOBS'] = [0, numobs_done, numobs_done]
            z['ZWARN'] = [1, 1, 0]
            p = make_mtl(t, obscon, zcat=z)["PRIORITY"]

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

        # BGS ZGOOD targets always have lower priority than MWS targets that
        # are not DONE.
        lowest_bgs_priority_zgood = cmx_mask['SV0_BGS'].priorities[
            'MORE_ZGOOD']

        lowest_mws_priority_unobs = cmx_mask['SV0_MWS'].priorities['UNOBS']
        lowest_mws_priority_zwarn = cmx_mask['SV0_MWS'].priorities[
            'MORE_ZWARN']
        lowest_mws_priority_zgood = cmx_mask['SV0_MWS'].priorities[
            'MORE_ZGOOD']

        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)
示例#4
0
    def setUp(self):
        self.targets = Table()

        # This is a dual identity case. In all cases the target is both QSO and ELG.
        # The first case is a true QSO with lowz.
        # The second case is a true QSO with highz.
        # The third case is an ELG.

        self.type_A = np.array(['QSO', 'QSO', 'ELG'])
        self.type_B = np.array(['ELG', 'ELG', 'QSO'])
        self.priorities_A = np.array(
            [Mx[t].priorities['UNOBS'] for t in self.type_A])
        self.priorities_B = np.array(
            [Mx[t].priorities['UNOBS'] for t in self.type_B])
        self.priorities = np.maximum(
            self.priorities_A,
            self.priorities_B)  # get the maximum between the two.

        nt = len(self.type_A)
        # ADM add some "extra" columns that are needed for observations.
        for col in ["RA", "DEC", "PARALLAX", "PMRA", "PMDEC", "REF_EPOCH"]:
            self.targets[col] = np.zeros(nt, dtype=mtldatamodel[col].dtype)
        Amx = np.array([Mx[t].mask for t in self.type_A])
        Bmx = np.array([Mx[t].mask for t in self.type_B])
        self.targets['DESI_TARGET'] = Amx | Bmx
        for col in ['BGS_TARGET', 'MWS_TARGET', 'SUBPRIORITY']:
            self.targets[col] = np.zeros(nt, dtype=mtldatamodel[col].dtype)

        n = len(self.targets)
        self.targets['ZFLUX'] = 10**((22.5 - np.linspace(20, 22, n)) / 2.5)
        self.targets['TARGETID'] = list(range(n))
        pinit, ninit = initial_priority_numobs(self.targets)
        self.targets["PRIORITY_INIT"] = pinit
        self.targets["NUMOBS_INIT"] = ninit

        # - reverse the order for zcat to make sure joins work.
        self.zcat = Table()
        self.zcat['TARGETID'] = self.targets['TARGETID'][::-1]
        self.zcat['Z'] = [1.0, 1.5, 2.5]
        self.zcat['ZWARN'] = [0, 0, 0]
        self.zcat['NUMOBS'] = [1, 1, 1]
        self.zcat['SPECTYPE'] = ['QSO', 'QSO', 'GALAXY']

        # priorities and numobs more after measuring redshifts.
        self.post_prio = [0 for t in self.type_A]
        self.post_numobs_more = [0 for t in self.type_A]
        self.post_prio[0] = Mx['QSO'].priorities['MORE_ZGOOD']  # highz QSO.
        self.post_prio[1] = Mx['QSO'].priorities['DONE']  # Lowz QSO,  DONE.
        self.post_prio[2] = Mx['ELG'].priorities['DONE']  # ELG, DONE.
        self.post_numobs_more[0] = 3
        self.post_numobs_more[1] = 0
        self.post_numobs_more[2] = 0
示例#5
0
    def setUp(self):
        self.targets = Table()

        # This is a dual identity case. In all cases the target is both QSO and ELG.
        # The first case is a true QSO with lowz.
        # The second case is a true QSO with highz.
        # The third case is an ELG.

        self.type_A = np.array(['QSO', 'QSO', 'ELG'])
        self.type_B = np.array(['ELG', 'ELG', 'QSO'])
        self.priorities_A = np.array(
            [Mx[t].priorities['UNOBS'] for t in self.type_A])
        self.priorities_B = np.array(
            [Mx[t].priorities['UNOBS'] for t in self.type_B])
        self.priorities = np.maximum(
            self.priorities_A,
            self.priorities_B)  # get the maximum between the two.
        self.targets['DESI_TARGET'] = np.array([
            Mx[t].mask for t in self.type_A
        ]) | np.array([Mx[t].mask for t in self.type_B])
        self.targets['BGS_TARGET'] = np.zeros(len(self.type_A), dtype=np.int64)
        self.targets['MWS_TARGET'] = np.zeros(len(self.type_A), dtype=np.int64)

        n = len(self.targets)
        self.targets['ZFLUX'] = 10**((22.5 - np.linspace(20, 22, n)) / 2.5)
        self.targets['TARGETID'] = list(range(n))
        pinit, ninit = initial_priority_numobs(self.targets)
        self.targets["PRIORITY_INIT"] = pinit
        self.targets["NUMOBS_INIT"] = ninit

        # - reverse the order for zcat to make sure joins work.
        self.zcat = Table()
        self.zcat['TARGETID'] = self.targets['TARGETID'][::-1]
        self.zcat['Z'] = [1.0, 1.5, 2.5]
        self.zcat['ZWARN'] = [0, 0, 0]
        self.zcat['NUMOBS'] = [1, 1, 1]
        self.zcat['SPECTYPE'] = ['QSO', 'QSO', 'GALAXY']

        # priorities and numobs more after measuring redshifts.
        self.post_prio = [0 for t in self.type_A]
        self.post_numobs_more = [0 for t in self.type_A]
        self.post_prio[0] = Mx['QSO'].priorities['MORE_ZGOOD']  # highz QSO.
        self.post_prio[1] = Mx['QSO'].priorities['DONE']  # Lowz QSO,  DONE.
        self.post_prio[2] = Mx['ELG'].priorities['DONE']  # ELG, DONE.
        self.post_numobs_more[0] = 3
        self.post_numobs_more[1] = 0
        self.post_numobs_more[2] = 0
示例#6
0
    def test_endless_bgs(self):
        """Test BGS targets always get another observation in bright time.
        """
        # ADM create a set of BGS FAINT/BGS_BRIGHT targets
        # ADM (perhaps) also another target class.
        bgstargets = self.targets.copy()
        bgstargets["DESI_TARGET"] = Mx["BGS_ANY"]
        bgstargets[
            "BGS_TARGET"] = bgs_mask["BGS_FAINT"] | bgs_mask["BGS_BRIGHT"]

        # ADM set their initial conditions for the bright-time survey.
        pinit, ninit = initial_priority_numobs(bgstargets, obscon="BRIGHT")
        bgstargets["PRIORITY_INIT"] = pinit
        bgstargets["NUMOBS_INIT"] = ninit

        # ADM create a copy of the zcat.
        bgszcat = self.zcat.copy()

        # ADM run through MTL.
        mtl = make_mtl(bgstargets, obscon="BRIGHT", zcat=bgszcat)

        # ADM all BGS targets should always have NUMOBS_MORE=1.
        self.assertTrue(np.all(bgszcat["NUMOBS_MORE"] == 1))
示例#7
0
    def test_merged_qso(self):
        """Test QSO tracers that are also other target types get 1 observation.
        """
        # ADM there are other tests of this kind in test_multiple_mtl.py.

        # ADM create a set of targets that are QSOs and
        # ADM (perhaps) also another target class.
        qtargets = self.targets.copy()
        qtargets["DESI_TARGET"] |= Mx["QSO"]

        # ADM give them all a "tracer" redshift (below a LyA QSO).
        qzcat = self.zcat.copy()
        qzcat["Z"] = 0.5

        # ADM set their initial conditions to be that of a QSO.
        pinit, ninit = initial_priority_numobs(qtargets, obscon="DARK|GRAY")
        qtargets["PRIORITY_INIT"] = pinit
        qtargets["NUMOBS_INIT"] = ninit

        # ADM run through MTL.
        mtl = make_mtl(qtargets, obscon="DARK|GRAY", zcat=qzcat)

        # ADM all confirmed tracer quasars should have NUMOBS_MORE=0.
        self.assertTrue(np.all(qzcat["NUMOBS_MORE"] == 0))
示例#8
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)
示例#9
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)
示例#10
0
def finalize_secondary(scxtargs,
                       scnd_mask,
                       survey='main',
                       sep=1.,
                       darkbright=False):
    """Assign secondary targets a realistic TARGETID, finalize columns.

    Parameters
    ----------
    scxtargs : :class:`~numpy.ndarray`
        An array of secondary targets, must contain the columns `RA`,
        `DEC` and `TARGETID`. `TARGETID` should be -1 for objects
        that lack a `TARGETID`.
    scnd_mask : :class:`desiutil.bitmask.BitMask`
        A mask corresponding to a set of secondary targets, e.g, could
        be ``from desitarget.targetmask import scnd_mask`` for the
        main survey mask.
    survey : :class:`str`, optional, defaults to "main"
        string indicating whether we are working in the context of the
        Main Survey (`main`) or SV (e.g. `sv1`, `sv2` etc.). Used to
        set the `RELEASE` number in the `TARGETID` (see Notes).
    sep : :class:`float`, defaults to 1 arcsecond
        The separation at which to match secondary targets to
        themselves in ARCSECONDS.
    darkbright : :class:`bool`, optional, defaults to ``False``
        If sent, then split `NUMOBS_INIT` and `PRIORITY_INIT` into
        `NUMOBS_INIT_DARK`, `NUMOBS_INIT_BRIGHT`, `PRIORITY_INIT_DARK`
        and `PRIORITY_INIT_BRIGHT` and calculate values appropriate
        to "BRIGHT" and "DARK|GRAY" observing conditions.

    Returns
    -------
    :class:`~numpy.ndarray`
        The array of secondary targets, with the `TARGETID` bit updated
        to be unique and reasonable and the `SCND_TARGET` column renamed
        based on the flavor of `scnd_mask`.

    Notes
    -----
        - Secondaries without `OVERRIDE` are also matched to themselves
        Such matches are given the same `TARGETID` (that of the primary
        if they match a primary) and the bitwise or of `SCND_TARGET` and
        `OBSCONDITIONS` bits across matches. The highest `PRIORITY_INIT`
        is retained, and others are set to -1. Only secondaries with
        priorities that are not -1 are written to the main file. If
        multiple matching secondary targets have the same (highest)
        priority, the first one encountered retains its `PRIORITY_INIT`
        - The secondary `TARGETID` is designed to be reproducible. It
        combines `BRICKID` based on location, `OBJID` based on the
        order of the targets in the secondary file (`SCND_ORDER`) and
        `RELEASE` from the secondary bit number (`SCND_TARGET`) and the
        input `survey`. `RELEASE` is set to ((X-1)*100)+np.log2(scnd_bit)
        with X from the `survey` string survey=svX and scnd_bit from
        `SCND_TARGET`. For the main survey (survey="main") X-1 is 5.
    """
    # ADM assign new TARGETIDs to targets without a primary match.
    nomatch = scxtargs["TARGETID"] == -1

    # ADM get the BRICKIDs for each source.
    brxid = bricks.brickid(scxtargs["RA"], scxtargs["DEC"])

    # ADM ensure unique secondary bits for different iterations of SV
    # ADM and the Main Survey.
    if survey == 'main':
        Xm1 = 5
    elif survey[0:2] == 'sv':
        # ADM the re.search just extracts the numbers in the string.
        Xm1 = int(re.search(r'\d+', survey).group()) - 1
        # ADM we've allowed a max of up to sv5 (!). Fail if surpassed.
        if Xm1 >= 5:
            msg = "Only coded for up to 'sv5', not {}!!!".format(survey)
            log.critical(msg)
            raise ValueError(msg)
    else:
        msg = "allowed surveys: 'main', 'svX', not {}!!!".format(survey)
        log.critical(msg)
        raise ValueError(msg)

    # ADM the RELEASE for each source is the `SCND_TARGET` bit NUMBER.
    release = (Xm1 * 100) + np.log2(scxtargs["SCND_TARGET_INIT"]).astype('int')

    # ADM build the OBJIDs based on the values of SCND_ORDER.
    t0 = time()
    log.info("Begin assigning OBJIDs to bricks...")
    # ADM So as not to overwhelm the bit-limits for OBJID
    # ADM rank by SCND_ORDER for each brick and bit combination.
    # ADM First, create a unique ID based on brxid and release.
    scnd_order = scxtargs["SCND_ORDER"]
    sorter = (1000 * brxid) + release
    # ADM sort the unique IDs and split based on where they change.
    argsort = np.argsort(sorter)
    w = np.where(np.diff(sorter[argsort]))[0]
    soperbrxbit = np.split(scnd_order[argsort], w + 1)
    # ADM loop through each (brxid, release) and sort on scnd_order.
    # ADM double argsort returns the ascending ranked order of the entry
    # ADM (whereas a single argsort returns the indexes for ordering).
    sortperbrxbit = [np.argsort(np.argsort(so)) for so in soperbrxbit]
    # ADM finally unroll the (brxid, release) combinations...
    sortedobjid = np.array(list(itertools.chain.from_iterable(sortperbrxbit)))
    # ADM ...and reorder based on the initial argsort.
    objid = np.zeros_like(sortedobjid) - 1
    objid[argsort] = sortedobjid
    log.info("Assigned OBJIDs to bricks in {:.1f}s".format(time() - t0))

    # ADM check that the objid array was entirely populated.
    assert np.all(objid != -1)

    # ADM assemble the TARGETID, SCND objects have RELEASE==0.
    targetid = encode_targetid(objid=objid, brickid=brxid, release=release)

    # ADM a check that the generated TARGETIDs are unique.
    if len(set(targetid)) != len(targetid):
        msg = "duplicate TARGETIDs generated for secondary targets!!!"
        log.critical(msg)
        raise ValueError(msg)

    # ADM assign the unique TARGETIDs to the secondary objects.
    scxtargs["TARGETID"][nomatch] = targetid[nomatch]
    log.debug("Assigned {} targetids to unmatched secondaries".format(
        len(targetid[nomatch])))

    # ADM match secondaries to themselves, to ensure duplicates
    # ADM share a TARGETID. Don't match special (OVERRIDE) targets
    # ADM or sources that have already been matched to a primary.
    w = np.where(~scxtargs["OVERRIDE"] & nomatch)[0]
    if len(w) > 0:
        log.info("Matching secondary targets to themselves...t={:.1f}s".format(
            time() - t0))
        # ADM use astropy for the matching. At NERSC, astropy matches
        # ADM ~20M objects to themselves in about 10 minutes.
        c = SkyCoord(scxtargs["RA"][w] * u.deg, scxtargs["DEC"][w] * u.deg)
        m1, m2, _, _ = c.search_around_sky(c, sep * u.arcsec)
        log.info("Done with matching...t={:.1f}s".format(time() - t0))
        # ADM restrict only to unique matches (and exclude self-matches).
        uniq = m1 > m2
        m1, m2 = m1[uniq], m2[uniq]
        # ADM set same TARGETID for any matches. m2 must come first, here.
        scxtargs["TARGETID"][w[m2]] = scxtargs["TARGETID"][w[m1]]

    # ADM Ensure secondary targets with matching TARGETIDs have all the
    # ADM relevant SCND_TARGET bits set. By definition, targets with
    # ADM OVERRIDE set never have matching TARGETIDs.
    wnoov = np.where(~scxtargs["OVERRIDE"])[0]
    if len(wnoov) > 0:
        for _, inds in duplicates(scxtargs["TARGETID"][wnoov]):
            scnd_targ = 0
            for ind in inds:
                scnd_targ |= scxtargs["SCND_TARGET"][wnoov[ind]]
            scxtargs["SCND_TARGET"][wnoov[inds]] = scnd_targ
    log.info("Done checking SCND_TARGET...t={:.1f}s".format(time() - t0))

    # ADM change the data model depending on whether the mask
    # ADM is an SVX (X = 1, 2, etc.) mask or not. Nothing will
    # ADM change if the mask has no preamble.
    prepend = scnd_mask._name[:-9].upper()
    scxtargs = rfn.rename_fields(scxtargs,
                                 {'SCND_TARGET': prepend + 'SCND_TARGET'})

    # APC same thing for DESI_TARGET
    scxtargs = rfn.rename_fields(scxtargs,
                                 {'DESI_TARGET': prepend + 'DESI_TARGET'})

    # APC Remove duplicate targetids from secondary-only targets
    alldups = []
    for _, dups in duplicates(scxtargs['TARGETID']):
        # Retain the duplicate with highest priority, breaking ties
        # on lowest index in list of duplicates
        dups = np.delete(dups, np.argmax(scxtargs['PRIORITY_INIT'][dups]))
        alldups.append(dups)
    alldups = np.hstack(alldups)
    log.debug(
        "Flagging {} duplicate secondary targetids with PRIORITY_INIT=-1".
        format(len(alldups)))

    # ADM and remove the INIT fields in prep for a dark/bright split.
    scxtargs = rfn.drop_fields(scxtargs, ["PRIORITY_INIT", "NUMOBS_INIT"])

    # ADM set initial priorities, numobs and obsconditions for both
    # ADM BRIGHT and DARK|GRAY conditions, if requested.
    nscx = len(scxtargs)
    nodata = np.zeros(nscx, dtype='int') - 1
    if darkbright:
        ender, obscon = ["_DARK", "_BRIGHT"], ["DARK|GRAY", "BRIGHT"]
    else:
        ender, obscon = [""], ["DARK|GRAY|BRIGHT|POOR|TWILIGHT12|TWILIGHT18"]
    cols, vals, forms = [], [], []
    for edr, oc in zip(ender, obscon):
        cols += ["{}_INIT{}".format(pn, edr) for pn in ["PRIORITY", "NUMOBS"]]
        vals += [nodata, nodata]
        forms += ['>i8', '>i8']

    # ADM write the output array.
    newdt = [dt for dt in zip(cols, forms)]
    done = np.array(np.zeros(nscx), dtype=scxtargs.dtype.descr + newdt)
    for col in scxtargs.dtype.names:
        done[col] = scxtargs[col]
    for col, val in zip(cols, vals):
        done[col] = val

    # ADM add the actual PRIORITY/NUMOBS values.
    for edr, oc in zip(ender, obscon):
        pc, nc = "PRIORITY_INIT" + edr, "NUMOBS_INIT" + edr
        done[pc], done[nc] = initial_priority_numobs(done,
                                                     obscon=oc,
                                                     scnd=True)

        # APC Flagged duplicates are removed in io.write_secondary
        done[pc][alldups] = -1

    # APC add secondary flag in DESI_TARGET
    cols, mx, surv = main_cmx_or_sv(done, scnd=True)
    done[cols[0]] = mx[0]['SCND_ANY']

    # ADM set the OBSCONDITIONS.
    done["OBSCONDITIONS"] = set_obsconditions(done, scnd=True)

    return done
示例#11
0
def match_secondary(primtargs, scxdir, scndout, sep=1., pix=None, nside=None):
    """Match secondary targets to primary targets and update bits.

    Parameters
    ----------
    primtargs : :class:`~numpy.ndarray`
        An array of primary targets.
    scndout : :class`~numpy.ndarray`
        Name of a sub-directory to which to write the information in
        `desitarget.secondary.outdatamodel` with `TARGETID` and (the
        highest) `PRIORITY_INIT` updated with matching primary info.
    scxdir : :class:`str`, optional, defaults to `None`
        Name of the directory that hosts secondary targets.
    sep : :class:`float`, defaults to 1 arcsecond
        The separation at which to match in ARCSECONDS.
    pix : :class:`list`, optional, defaults to `None`
        Limit secondary targets to (NESTED) HEALpixels that touch
        pix at the supplied `nside`, as a speed-up.
    nside : :class:`int`, optional, defaults to `None`
        The (NESTED) HEALPixel nside to be used with `pixlist`.

    Returns
    -------
    :class:`~numpy.ndarray`
        The array of primary targets, with the `SCND_TARGET` bit
        populated for matches to secondary targets
    """
    # ADM add a SCND_TARGET column to the primary targets.
    dt = primtargs.dtype.descr
    dt.append(('SCND_TARGET', '>i8'))
    targs = np.zeros(len(primtargs), dtype=dt)
    for col in primtargs.dtype.names:
        targs[col] = primtargs[col]

    # ADM check if this is an SV or main survey file.
    cols, mx, surv = main_cmx_or_sv(targs, scnd=True)
    log.info('running on the {} survey...'.format(surv))
    if surv != 'main':
        scxdir = os.path.join(scxdir, surv)

    # ADM read in non-OVERRIDE secondary targets.
    scxtargs = read_files(scxdir, mx[3])
    scxtargs = scxtargs[~scxtargs["OVERRIDE"]]

    # ADM match primary targets to non-OVERRIDE secondary targets.
    inhp = np.ones(len(scxtargs), dtype="?")
    # ADM as a speed-up, save memory by limiting the secondary targets
    # ADM to just HEALPixels that could touch the primary targets.
    if nside is not None and pix is not None:
        # ADM remember to grab adjacent pixels in case of edge effects.
        allpix = add_hp_neighbors(nside, pix)
        inhp = is_in_hp(scxtargs, nside, allpix)
        # ADM it's unlikely that the matching separation is comparable
        # ADM to the HEALPixel resolution, but guard against that anyway.
        halfpix = np.degrees(hp.max_pixrad(nside)) * 3600.
        if sep > halfpix:
            msg = 'sep ({}") exceeds (half) HEALPixel size ({}")'.format(
                sep, halfpix)
            log.critical(msg)
            raise ValueError(msg)

    # ADM warn the user if the secondary and primary samples are "large".
    big = 500000
    if np.sum(inhp) > big and len(primtargs) > big:
        log.warning('Large secondary (N={}) and primary (N={}) samples'.format(
            np.sum(inhp), len(primtargs)))
        log.warning('The code may run slowly')

    # ADM for each secondary target, determine if there is a match
    # ADM with a primary target. Note that sense is important, here
    # ADM (the primary targets must be passed first).
    log.info(
        'Matching primary and secondary targets for {} at {}"...t={:.1f}s'.
        format(scndout, sep,
               time() - start))
    mtargs, mscx = radec_match_to(targs, scxtargs[inhp], sep=sep)
    # ADM recast the indices to the full set of secondary targets,
    # ADM instead of just those that were in the relevant HEALPixels.
    mscx = np.where(inhp)[0][mscx]

    # ADM loop through the matches and update the SCND_TARGET
    # ADM column in the primary target list. The np.unique is a
    # ADM speed-up to assign singular matches first.
    umtargs, inv, cnt = np.unique(mtargs,
                                  return_inverse=True,
                                  return_counts=True)
    # ADM number of times each primary target was matched, ordered
    # ADM the same as mtargs, i.e. n(mtargs) for each entry in mtargs.
    nmtargs = cnt[inv]
    # ADM assign anything with nmtargs = 1 directly.
    singular = nmtargs == 1
    targs["SCND_TARGET"][mtargs[singular]] = scxtargs["SCND_TARGET"][
        mscx[singular]]
    # ADM loop through things with nmtargs > 1 and combine the bits.
    for i in range(len((mtargs[~singular]))):
        targs["SCND_TARGET"][mtargs[~singular][i]] |= scxtargs["SCND_TARGET"][
            mscx[~singular][i]]
    # ADM also assign the SCND_ANY bit to the primary targets.
    desicols, desimasks, _ = main_cmx_or_sv(targs, scnd=True)
    desi_mask = desimasks[0]

    targs[desicols[0]][umtargs] |= desi_mask.SCND_ANY

    # ADM rename the SCND_TARGET column, in case this is an SV file.
    targs = rfn.rename_fields(targs, {'SCND_TARGET': desicols[3]})

    # APC Secondary target bits only affect PRIORITY, NUMOBS and
    # APC obsconditions for specific DESI_TARGET bits
    # APC See https://github.com/desihub/desitarget/pull/530

    # APC Only consider primary targets with secondary bits set
    scnd_update = (targs[desicols[0]] & desi_mask['SCND_ANY']) != 0
    if np.any(scnd_update):
        # APC Allow changes to primaries if the DESI_TARGET bitmask has
        # APC only the following bits set, in any combination.
        log.info(
            'Testing if secondary targets can update {} matched primaries'.
            format(scnd_update.sum()))
        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 &= ((targs[desicols[0]] & ~update_from_scnd_bits) == 0)
        log.info(
            'Setting new priority, numobs and obsconditions from secondary for {} matched primaries'
            .format(scnd_update.sum()))

        # APC Primary and secondary obsconditions are or'd
        scnd_obscon = set_obsconditions(targs[scnd_update], scnd=True)
        targs['OBSCONDITIONS'][scnd_update] &= scnd_obscon

        # APC bit of a hack here
        # APC Check for _BRIGHT, _DARK split in column names
        darkbright = 'NUMOBS_INIT_DARK' in targs.dtype.names
        if darkbright:
            ender, obscon = ["_DARK", "_BRIGHT"], ["DARK|GRAY", "BRIGHT"]
        else:
            ender, obscon = [""], [
                "DARK|GRAY|BRIGHT|POOR|TWILIGHT12|TWILIGHT18"
            ]

        # APC secondaries can increase priority and numobs
        for edr, oc in zip(ender, obscon):
            pc, nc = "PRIORITY_INIT" + edr, "NUMOBS_INIT" + edr
            scnd_priority, scnd_numobs = initial_priority_numobs(
                targs[scnd_update], obscon=oc, scnd=True)
            targs[nc][scnd_update] = np.maximum(targs[nc][scnd_update],
                                                scnd_numobs)
            targs[pc][scnd_update] = np.maximum(targs[pc][scnd_update],
                                                scnd_priority)

    # ADM update the secondary targets with the primary information.
    scxtargs["TARGETID"][mscx] = targs["TARGETID"][mtargs]
    # ADM the maximum priority will be used to break ties in the
    # ADM unlikely event that a secondary matches two primaries.
    hipri = np.maximum(targs["PRIORITY_INIT_DARK"],
                       targs["PRIORITY_INIT_BRIGHT"])
    scxtargs["PRIORITY_INIT"][mscx] = hipri[mtargs]

    # ADM write the secondary targets that have updated TARGETIDs.
    ii = scxtargs["TARGETID"] != -1
    nmatches = np.sum(ii)
    log.info('Writing {} secondary target matches to {}...t={:.1f}s'.format(
        nmatches, scndout,
        time() - start))
    if nmatches > 0:
        hdr = fitsio.FITSHDR()
        hdr["SURVEY"] = surv
        fitsio.write(scndout,
                     scxtargs[ii],
                     extname='SCND_TARG',
                     header=hdr,
                     clobber=True)

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

    return targs