Skip to content

Commit

Permalink
Merge 3e609c9 into 05db498
Browse files Browse the repository at this point in the history
  • Loading branch information
sbailey committed May 6, 2021
2 parents 05db498 + 3e609c9 commit b46f76e
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 1 deletion.
4 changes: 3 additions & 1 deletion doc/changes.rst
Expand Up @@ -5,6 +5,8 @@ desitarget Change Log
0.57.3 (unreleased)
-------------------

* Add utility functions ``decode/encode_negative_targetid(ra,dec,group)``
unique to at least 2 milliarcsec [`PR #724`_].
* Update the baseline LRG selection [`PR #723`_]. Changes from SV3 include:
* Change the zfiber faint limit from 21.7 to 21.6.
* Change the low-z limit from z>0.3 to z>0.4.
Expand Down Expand Up @@ -32,7 +34,6 @@ desitarget Change Log
* New function and bin script to make QSO redshift catalogs [`PR #714`_].
* Incorporates functionality from QuasarNET and SQUEzE.


.. _`PR #714`: https://github.com/desihub/desitarget/pull/714
.. _`PR #715`: https://github.com/desihub/desitarget/pull/715
.. _`PR #716`: https://github.com/desihub/desitarget/pull/716
Expand All @@ -41,6 +42,7 @@ desitarget Change Log
.. _`PR #719`: https://github.com/desihub/desitarget/pull/719
.. _`PR #722`: https://github.com/desihub/desitarget/pull/722
.. _`PR #723`: https://github.com/desihub/desitarget/pull/723
.. _`PR #724`: https://github.com/desihub/desitarget/pull/724

0.57.2 (2021-04-18)
-------------------
Expand Down
77 changes: 77 additions & 0 deletions py/desitarget/targets.py
Expand Up @@ -190,6 +190,83 @@ def decode_targetid(targetid):

return outputs

def encode_negative_targetid(ra, dec, group=1):
"""
Create negative 64-bit TARGETID from (ra,dec) unique to ~1.2 milliarcsec
Parameters
----------
ra : :class:`float` or :class:`~numpy.ndarray`
Right Ascension in degrees 0 <= ra <= 360
dec : :class:`float` or :class:`~numpy.ndarray`
Declination in degrees -90 <= dec <= 90
group : int, optional (default 1)
group number 1-15 to encode
Returns
-------
:class:`~numpy.int64` or :class:`~numpy.ndarray`
negative TARGETID derived from (ra,dec)
"""
#- Hardcode number of bits
nbits_ra = 30
nbits_dec = 29
nbits_group = 4

#- Check input dimensionality
scalar_input = np.isscalar(ra)
if np.isscalar(ra) != np.isscalar(dec):
raise TypeError('ra and dec must both be scalars or both be arrays')

if not (1 <= group <= 15):
raise ValueError(f'group {group} must be within 1-15')

group = np.int8(group)

#- Convert to arrays to enable things like .astype(int)
ra = np.atleast_1d(ra)
dec = np.atleast_1d(dec)

assert np.all( (0.0 <= ra) & (ra <= 360.0) )
assert np.all( (-90.0 <= dec) & (dec <= 90.0) )

#- encode ra in bits 30-59 and dec in bits 0-29
ra_bits = ((2**nbits_ra - 1) * (ra/360.0)).astype(int)
dec_bits = ((2**nbits_dec - 1) * ((dec+90.0)/180.0)).astype(int)
group_bitshift = nbits_dec + nbits_ra
ra_bitshift = nbits_dec
targetid = -((group<<group_bitshift) + (ra_bits<<ra_bitshift) + dec_bits)

#- return value has dimensionality of inputs
if scalar_input:
return targetid[0]
else:
return targetid

def decode_negative_targetid(targetid):
"""
TODO: document
"""
#- Hardcode number of bits
nbits_ra = 30
nbits_dec = 29
nbits_group = 4

dec_mask = 2**nbits_dec - 1
ra_mask = 2**nbits_ra - 1
group_mask = 2**nbits_group - 1
group_bitshift = nbits_dec + nbits_ra
ra_bitshift = nbits_dec

dec_bits = (-targetid) & dec_mask
ra_bits = ((-targetid) >> ra_bitshift) & ra_mask
group = ((-targetid) >> group_bitshift) & group_mask

ra = ra_bits / (2**nbits_ra - 1) * 360.0
dec = dec_bits / (2**nbits_dec - 1) * 180.0 - 90.0

return ra, dec, group


def switch_main_cmx_or_sv(revamp, archetype):
"""change the data model of a set of targets to match another.
Expand Down
113 changes: 113 additions & 0 deletions py/desitarget/test/test_targetids.py
@@ -0,0 +1,113 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst
# -*- coding: utf-8 -*-
"""Test desitarget.targets targetid encode/decode
"""
import unittest
import numpy as np

from desitarget.targets import (
encode_negative_targetid, decode_negative_targetid )

class TestTargetID(unittest.TestCase):

@classmethod
def setUp(self):
pass

def test_encode_negative_targetid(self):
"""
Test encoding ra,dec -> negative TARGETID
"""
#- Edge cases with scalars; should alsways be negative
for ra in (0,90,180,360):
for dec in (-90, 0, +90):
for group in (1,7,15):
t = encode_negative_targetid(ra,dec,group)
msg = f'targetid({ra},{dec},{group})'
self.assertLess(t, 0, msg)
self.assertTrue(np.isscalar(t), msg)

#- Also works with lists and arrays
ra = (0, 10, 20)
dec = (-10, 0, 89.9)
t = encode_negative_targetid(ra, dec)
self.assertEqual(len(t), len(ra))
self.assertTrue(np.all(t<0))

t = encode_negative_targetid(np.asarray(ra), np.asarray(dec))
self.assertEqual(len(t), len(ra))
self.assertTrue(np.all(t<0))

#- Test invalid group numbers
with self.assertRaises(ValueError):
encode_negative_targetid(0,0,0)

with self.assertRaises(ValueError):
encode_negative_targetid(0,0,16)

with self.assertRaises(ValueError):
encode_negative_targetid(0,0,-1)

#- 2 milliarcsec differences -> different TARGETID
ra, dec = 10.0, 0.0
delta = 2.0/(3600*1000)

t1 = encode_negative_targetid(ra,dec)
t2 = encode_negative_targetid(ra,dec+delta)
t3 = encode_negative_targetid(ra,dec-delta)
t4 = encode_negative_targetid(ra+delta,dec)
t5 = encode_negative_targetid(ra-delta,dec)
self.assertNotEqual(t1, t2)
self.assertNotEqual(t1, t3)
self.assertNotEqual(t1, t4)
self.assertNotEqual(t1, t5)
self.assertNotEqual(t2, t3)
self.assertNotEqual(t2, t4)
self.assertNotEqual(t2, t5)
self.assertNotEqual(t3, t4)
self.assertNotEqual(t3, t5)
self.assertNotEqual(t4, t5)


def test_decode_negative_targetid(self):
"""test negative targetid encoding -> decoding round trip"""

#---- roundtrip accurate to at least 2 milliarcsec (without cos(dec))
n = 1000
ra = np.random.uniform(0, 360, n)
dec = np.random.uniform(-90, 90, n)
group = 5

#- include corner cases
ra = np.concatenate( (ra, [0,0,0, 360,360,360]) )
dec = np.concatenate( (dec, [-90,0,90, -90,0,90]) )

targetids = encode_negative_targetid(ra, dec, group)
ra1, dec1, group1 = decode_negative_targetid(targetids)

delta = 2.0/(3600*1000)
self.assertTrue(np.all(np.abs(ra-ra1) < delta))
self.assertTrue(np.all(np.abs(dec-dec1) < delta))
self.assertTrue(np.all(group1 == group))

#- check group roundtrip, and scalar inputs
ra, dec = 20.1, -16.3333
for group in range(1,16):
targetid = encode_negative_targetid(ra, dec, group)
ra1, dec1, group1 = decode_negative_targetid(targetid)
self.assertEqual(group1, group)
self.assertLess(np.abs(ra1-ra), delta)
self.assertLess(np.abs(dec1-dec), delta)



if __name__ == '__main__':
unittest.main()


def test_suite():
"""Allows testing of only this module with the command:
python setup.py test -m desitarget.test.test_geomask
"""
return unittest.defaultTestLoader.loadTestsFromName(__name__)

0 comments on commit b46f76e

Please sign in to comment.