Coverage for python/lsst/pipe/tasks/associationUtils.py: 16%
61 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-10-11 02:06 -0700
« prev ^ index » next coverage.py v7.10.6, created at 2025-10-11 02:06 -0700
1# This file is part of pipe_tasks.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22"""Utilities for interfacing with hpgeom. Originally implemented in
23http://github.com/LSSTDESC/dia_pipe and then translated to hpgeom.
24"""
26__all__ = ["toIndex", "toRaDec", "eq2xyz", "eq2xyzVec", "convert_spherical",
27 "convert_spherical_array", "query_disc", "obj_id_to_ss_object_id", "ss_object_id_to_obj_id"]
29import hpgeom as hpg
30import numpy as np
33def toIndex(nside, ra, dec):
34 """Return healpix index given ra, dec in degrees
36 Parameters
37 ----------
38 nside : `int`
39 Power of 2 nside healpix resolution.
40 ra : `float`
41 RA in degrees.
42 dec : `float`
43 Declination in degrees
45 Returns
46 -------
47 index : `int`
48 Unique healpix pixel ID containing point RA, DEC at resolution nside.
49 """
50 return hpg.angle_to_pixel(nside, ra, dec, nest=False)
53def toRaDec(nside, index):
54 """Convert from healpix index to ra,dec in degrees
56 Parameters
57 ----------
58 nside : `int`
59 Resolution of healpixel "grid".
60 index : `int`
61 Index of the healpix pixel we want to find the location of.
63 Returns
64 -------
65 pos : `numpy.ndarray`, (2,)
66 RA and DEC of healpix pixel location in degrees.
67 """
68 ra, dec = hpg.pixel_to_angle(nside, index, nest=False)
69 return np.dstack((ra, dec))[0]
72def eq2xyz(ra, dec):
73 """Convert from equatorial ra,dec in degrees to x,y,z on unit sphere.
75 Parameters
76 ----------
77 ra : `float`
78 RA in degrees.
79 dec : `float`
80 Declination in degrees
82 Returns
83 -------
84 xyz : `numpy.ndarray`, (3,)
85 Float xyz positions on the unit sphere.
86 """
87 phi = np.deg2rad(ra)
88 theta = np.pi/2 - np.deg2rad(dec)
89 sintheta = np.sin(theta)
90 x = sintheta*np.cos(phi)
91 y = sintheta*np.sin(phi)
92 z = np.cos(theta)
93 return np.array([x, y, z])
96def eq2xyzVec(ra, dec):
97 """Convert equatorial ra,dec in degrees to x,y,z on the unit sphere
98 parameters
100 Vectorized version of ``eq2xyz``
102 Parameters
103 ----------
104 ra : array_like, (N,)
105 Array of RA in degrees.
106 dec : array_like, (N,)
107 Declination in degrees
109 Returns
110 -------
111 vec : `numpy.ndarray`, (N,3)
112 Array of unitsphere 3-vectors.
113 """
114 ra = np.array(ra, dtype='f8', ndmin=1, copy=False)
115 dec = np.array(dec, dtype='f8', ndmin=1, copy=False)
116 if ra.size != dec.size:
117 raise ValueError("ra,dec not same size: %s,%s" % (ra.size, dec.size))
119 vec = eq2xyz(ra, dec)
121 return vec
124def convert_spherical(ra, dec):
125 """Convert from ra,dec to spherical coordinates.
127 Used in query_disc.
129 Parameters
130 ----------
131 ra : `float`
132 RA in radians.
133 dec : `float`
134 Declination in radians
135 """
136 return np.dstack([np.cos(dec*np.pi/180)*np.cos(ra*np.pi/180),
137 np.cos(dec*np.pi/180)*np.sin(ra*np.pi/180),
138 np.sin(dec*np.pi/180)])[0]
141def convert_spherical_array(array):
142 """Convert from and a array ra,dec to spherical coordinates.
144 Used in query_disc
146 Parameters
147 ----------
148 array : `numpy.ndarray`, (N, 2)
149 (N, 2) Array of RA, DEC values.
151 Returns
152 -------
153 vecs : `numpy.ndarray`, (N, 3)
154 Vectors on the unit sphere
155 """
156 ra = array[:, 0]
157 dec = array[:, 1]
158 return convert_spherical(ra, dec)
161def query_disc(nside, ra, dec, max_rad, min_rad=0):
162 """Get the list of healpix indices within max_rad, min_rad given in radians
163 around ra,dec given in degrees
165 Parameters
166 ----------
167 nside : `int`
168 Resolution of the healpixels to search/return.
169 ra : `float`
170 RA in degrees.
171 dec : `float`
172 Declination in degrees
173 max_rad : `float`
174 Max distance in radians to search nearby healpixels.
175 min_rad : `float`, optional
176 Minimum distance in radians to search healpixels. Default = 0.
177 """
178 ra = np.atleast_1d(ra)
179 dec = np.atleast_1d(dec)
181 max_rad_deg = np.rad2deg(max_rad)
183 pixels = np.unique(
184 [hpg.query_circle(nside, a, b, max_rad_deg, nest=False)
185 for (a, b) in zip(ra, dec)])
187 if min_rad > 0 and len(pixels) > 0:
188 vec0 = convert_spherical(ra, dec)
189 min_rad2 = min_rad**2
190 vecs = convert_spherical_array(toRaDec(nside, pixels))
191 dsq = np.sum((vecs - vec0)**2, axis=1)
192 match = dsq > min_rad2
193 pixels = pixels[match]
195 return pixels
198def obj_id_to_ss_object_id(objID, flags=0):
199 """Convert from Minor Planet Center packed provisional object ID to
200 Rubin ssObjectID.
202 Parameters
203 ----------
204 objID : `str`
205 Minor Planet Center packed provisional designation for a small solar
206 system object. Must be fewer than eight characters.
207 flags : `int`, optional
208 Eight free bits to enable future decoupling between Minor Planet Center
209 and Rubin. Zero by default, should not be changed unless we need to
210 move away from a 1:1 mapping with the MPC. Must be within [0, 255].
212 Returns
213 -------
214 ssObjectID : `int`
215 Rubin ssObjectID
217 Raises
218 ------
219 ValueError
220 Raised if either objID is longer than 7 characters or flags is greater
221 than 255 or less than 0.
222 """
223 if len(objID) > 7:
224 raise ValueError(f'objID longer than 7 characters: "{objID}"')
225 if len(objID) < 7:
226 raise ValueError(f'objID shorter than 7 characters: "{objID}"')
227 if flags < 0 or flags > 255:
228 raise ValueError(f'Flags ({flags}) outside [0, 255].')
229 if any([ord(c) > 255 for c in objID]):
230 raise ValueError(f'{[c for c in objID if ord(c) > 255]} not legal objID characters (ascii [1, 255])')
232 ssObjectID = flags
233 for character in objID:
234 ssObjectID <<= 8
235 ssObjectID += ord(character)
236 return ssObjectID
239def ss_object_id_to_obj_id(ssObjectID):
240 """Convert from Rubin ssObjectID to Minor Planet Center packed provisional
241 object ID.
243 Parameters
244 ----------
245 ssObjectID : `int`
246 Rubin ssObjectID
248 Returns
249 -------
250 objID : `str`
251 Minor Planet Center packed provisional designation.
253 flags : `int`
254 Rubin flags (not yet defined, but usable in case we decouple from MPC).
256 Raises
257 ------
258 """
259 if ssObjectID < 0 or ssObjectID >= (1 << 64):
260 raise ValueError(f'ssObjectID ({ssObjectID}) outside [0, 2^64 - 1].')
262 objID = ''.join([chr((ssObjectID >> (8 * i)) % 256) for i in reversed(range(0, 7))])
263 return objID, ssObjectID >> (8 * 7) % 256