Coverage for python/lsst/ap/association/ssoAssociation.py : 36%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of ap_association.
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"""Spatial association for Solar System Objects."""
24__all__ = ["SolarSystemAssociationConfig", "SolarSystemAssociationTask"]
26import numpy as np
27from scipy.spatial import cKDTree
29import lsst.pex.config as pexConfig
30import lsst.pipe.base as pipeBase
33class SolarSystemAssociationConfig(pexConfig.Config):
34 """Config class for SolarSystemAssociationTask.
35 """
36 maxDistArcSeconds = pexConfig.Field(
37 dtype=float,
38 doc='Maximum distance in arcseconds to test for a DIASource to be a '
39 'match to a SSObject.',
40 default=2.0,
41 )
44class SolarSystemAssociationTask(pipeBase.Task):
45 """Associate DIASources into existing SolarSystem Objects.
47 This task performs the association of detected DIASources in a visit
48 with known solar system objects.
49 """
50 ConfigClass = SolarSystemAssociationConfig
51 _DefaultName = "ssoAssociation"
53 @pipeBase.timeMethod
54 def run(self, diaSourceCatalog, solarSystemObjects):
55 """Create a searchable tree of unassociated DiaSources and match
56 to the nearest ssoObject.
58 Parameters
59 ----------
60 diaSourceCatalog : `pandas.DataFrame`
61 Catalog of DiaSources. Modified in place to add ssObjectId to
62 successfully associated DiaSources.
63 solarSystemObjects : `pandas.DataFrame`
64 Set of solar system objects that should be within the footprint
65 of the current visit.
67 Returns
68 -------
69 resultsStruct : `lsst.pipe.base.Struct`
71 - ``ssoAssocDiaSources`` : DiaSources that were associated with
72 solar system objects in this visit. (`pandas.DataFrame`)
73 - ``unAssocDiaSources`` : Set of DiaSources that were not
74 associated with any solar system object. (`pandas.DataFrame`)
75 """
76 maxRadius = np.deg2rad(self.config.maxDistArcSeconds / 3600)
78 # Transform DIA RADEC coordinates to unit sphere xyz for tree building.
79 vectors = self._radec_to_xyz(diaSourceCatalog["ra"],
80 diaSourceCatalog["decl"])
82 # Create KDTree of DIA sources
83 tree = cKDTree(vectors)
85 # Query the KDtree for DIA nearest neighbors to SSOs. Currently only
86 # picks the DiaSource with the shortest distance. We can do something
87 # fancier later.
88 for index, ssObject in solarSystemObjects.iterrows():
90 ssoVect = self._radec_to_xyz(ssObject["ra"], ssObject["decl"])
92 # Which DIA Sources fall within r?
93 dist, idx = tree.query(ssoVect, maxRadius)
94 if np.isfinite(dist[0]):
95 diaSourceCatalog.loc[idx[0], "ssObjectId"] = ssObject["ssObjectId"]
97 assocMask = diaSourceCatalog["ssObjectId"] != 0
98 return pipeBase.Struct(
99 ssoAssocDiaSources=diaSourceCatalog[assocMask],
100 unAssocDiaSources=diaSourceCatalog[~assocMask])
102 def _radec_to_xyz(self, ras, decs):
103 """Convert input ra/dec coordinates to spherical unit-vectors.
105 Parameters
106 ----------
107 ras : `array-like`
108 RA coordinates of objects in degrees.
109 decs : `array-like`
110 DEC coordinates of objects in degrees.
112 Returns
113 -------
114 vectors : `numpy.ndarray`, (N, 3)
115 Output unit-vectors
116 """
117 ras = np.radians(ras)
118 decs = np.radians(decs)
119 try:
120 vectors = np.empty((len(ras), 3))
121 except TypeError:
122 vectors = np.empty((1, 3))
124 sin_dec = np.sin(np.pi / 2 - decs)
125 vectors[:, 0] = sin_dec * np.cos(ras)
126 vectors[:, 1] = sin_dec * np.sin(ras)
127 vectors[:, 2] = np.cos(np.pi / 2 - decs)
129 return vectors