Hide keyboard shortcuts

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/>. 

21 

22"""Spatial association for Solar System Objects.""" 

23 

24__all__ = ["SolarSystemAssociationConfig", "SolarSystemAssociationTask"] 

25 

26import numpy as np 

27from scipy.spatial import cKDTree 

28 

29import lsst.pex.config as pexConfig 

30import lsst.pipe.base as pipeBase 

31 

32 

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 ) 

42 

43 

44class SolarSystemAssociationTask(pipeBase.Task): 

45 """Associate DIASources into existing SolarSystem Objects. 

46 

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" 

52 

53 @pipeBase.timeMethod 

54 def run(self, diaSourceCatalog, solarSystemObjects): 

55 """Create a searchable tree of unassociated DiaSources and match 

56 to the nearest ssoObject. 

57 

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. 

66 

67 Returns 

68 ------- 

69 resultsStruct : `lsst.pipe.base.Struct` 

70 

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) 

77 

78 # Transform DIA RADEC coordinates to unit sphere xyz for tree building. 

79 vectors = self._radec_to_xyz(diaSourceCatalog["ra"], 

80 diaSourceCatalog["decl"]) 

81 

82 # Create KDTree of DIA sources 

83 tree = cKDTree(vectors) 

84 

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(): 

89 

90 ssoVect = self._radec_to_xyz(ssObject["ra"], ssObject["decl"]) 

91 

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"] 

96 

97 assocMask = diaSourceCatalog["ssObjectId"] != 0 

98 return pipeBase.Struct( 

99 ssoAssocDiaSources=diaSourceCatalog[assocMask].reset_index(drop=True), 

100 unAssocDiaSources=diaSourceCatalog[~assocMask].reset_index(drop=True)) 

101 

102 def _radec_to_xyz(self, ras, decs): 

103 """Convert input ra/dec coordinates to spherical unit-vectors. 

104 

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. 

111 

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)) 

123 

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) 

128 

129 return vectors