diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 73ad442..327cafe 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -17,9 +17,9 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest] - python-version: ['3.8', '3.9', '3.10'] # , '3.11'] Astropy 5.0 may be incompatible with Python 3.11 + python-version: ['3.9', '3.10'] #, '3.11'] There are still issues with Numpy <1.23 and 3.11. numpy-version: ['<1.23'] - astropy-version: ['==5.0', '<5.1', '<6.0'] + astropy-version: ['<5.1', '<6.0', '<7.0'] steps: - name: Checkout code @@ -93,6 +93,33 @@ jobs: - name: Test the documentation run: sphinx-build -W --keep-going -b html doc doc/_build/html + api: + name: API doc completeness test + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + python-version: ['3.10'] + + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + python -m pip install --editable . + - name: Generate api.rst + run: ./bin/desi_api_file --api ./api.rst desiutil + - name: Compare generated api.rst to checked-in version + run: diff --ignore-space-change --ignore-blank-lines ./api.rst ./doc/api.rst + style: name: Style check runs-on: ${{ matrix.os }} diff --git a/doc/api.rst b/doc/api.rst index 294152d..74f5150 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -47,6 +47,9 @@ desiutil API .. automodule:: desiutil.modules :members: +.. automodule:: desiutil.names + :members: + .. automodule:: desiutil.plots :members: diff --git a/doc/changes.rst b/doc/changes.rst index 5645d9f..f1f521e 100644 --- a/doc/changes.rst +++ b/doc/changes.rst @@ -14,9 +14,13 @@ Change Log 3.4.2 (unreleased) ------------------ -* Fully support adding units and comments to FITS table columns (PR `#201`). +* Fully support adding units and comments to FITS table columns (PR `#201`_). +* Created :mod:`desiutil.names` and added a function to it that takes an RA + and DEC and produces a DESINAME that can be used to refer to a DESI + object at that sky location (PR `#203`_). .. _`#201`: https://github.com/desihub/desiutil/pull/201 +.. _`#203`: https://github.com/desihub/desiutil/pull/203 3.4.1 (2023-09-25) ------------------ diff --git a/py/desiutil/names.py b/py/desiutil/names.py new file mode 100644 index 0000000..a02d0bb --- /dev/null +++ b/py/desiutil/names.py @@ -0,0 +1,64 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +# -*- coding: utf-8 -*- +""" +============== +desiutil.names +============== + +This package contains functions for naming 'things' in DESI +or decoding those names. +""" +import numpy as np + + +def radec_to_desiname(target_ra, target_dec): + """Convert the right ascension and declination of a DESI target + into the corresponding "DESINAME" for reference in publications. + Length of target_ra and target_dec must be the same if providing an + array or list. Note that these names are not unique for roughly + one percent of DESI targets, so also including TARGETID in + publications is highly recommended for uniqueness. + + Parameters + ---------- + target_ra: array of :class:`float64` + Right ascension in degrees of target object(s). Can be float, double, + or array/list of floats or doubles + target_dec: array of :class:`float64` + Declination in degrees of target object(s). Can be float, double, + or array/list of floats or doubles + + Returns + ------- + array of :class:`str` + The DESI names referring to the input target RA and DEC's. Array is + the same length as the input arrays. + + """ + # Convert to numpy array in case inputs are scalars or lists + target_ra, target_dec = np.atleast_1d(target_ra), np.atleast_1d(target_dec) + + # Number of decimal places in final naming convention + precision = 4 + + # Truncate decimals to the given precision + ratrunc = np.trunc((10 ** precision) * target_ra).astype(int).astype(str) + dectrunc = np.trunc((10 ** precision) * target_dec).astype(int).astype(str) + + # Loop over input values and create DESINAME as: DESI JXXX.XXXX+/-YY.YYYY + # Here J refers to J2000, which isn't strictly correct but is the closest + # IAU compliant term + desinames = [] + for ra, dec in zip(ratrunc, dectrunc): + desiname = 'DESI J' + ra[:-precision].zfill(3) + '.' + ra[-precision:] + # Positive numbers need an explicit "+" while negative numbers + # already have a "-". + # zfill works properly with '-' but counts it in number of characters + # so need one more + if dec.startswith('-'): + desiname += dec[:-precision].zfill(3) + '.' + dec[-precision:] + else: + desiname += '+' + dec[:-precision].zfill(2) + '.' + dec[-precision:] + desinames.append(desiname) + + return np.array(desinames) diff --git a/py/desiutil/test/test_names.py b/py/desiutil/test/test_names.py new file mode 100644 index 0000000..e4e1155 --- /dev/null +++ b/py/desiutil/test/test_names.py @@ -0,0 +1,46 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +# -*- coding: utf-8 -*- +"""Test desiutil.names. +""" +import unittest +import numpy as np + + +class TestNames(unittest.TestCase): + """Test desiutil.names + """ + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + + def test_radec_to_desiname(self): + """Test MaskedArrayWithLimits + """ + from ..names import radec_to_desiname + ras = [6.2457354547234, 23.914121939862518, 36.23454570972834, + 235.25235223446, 99.9999999999999] + decs = [29.974787585945496, -42.945872347904356, -0.9968423456, + 8.45677345352345, 89.234958294953] + correct_names = np.array(['DESI J006.2457+29.9747', + 'DESI J023.9141-42.9458', + 'DESI J036.2345-00.9968', + 'DESI J235.2523+08.4567', + 'DESI J099.9999+89.2349']) + # Test scalar conversion + for ra, dec, correct_name in zip(ras, decs, correct_names): + outname = radec_to_desiname(ra, dec) + self.assertEqual(outname, correct_name) + + # Test list conversion + outnames = radec_to_desiname(ras, decs) + self.assertTrue(np.alltrue(outnames == correct_names)) + + # Test array conversion + outnames = radec_to_desiname(np.array(ras), + np.array(decs)) + self.assertTrue(np.alltrue(outnames == correct_names))