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

21# 

22 

23"""Pipeline for running DiaSource association in a DRP context. 

24""" 

25 

26import numpy as np 

27import pandas as pd 

28 

29import lsst.geom as geom 

30import lsst.pipe.base as pipeBase 

31from lsst.skymap import BaseSkyMap 

32 

33from .coaddBase import makeSkyInfo 

34 

35__all__ = ["DrpAssociationPipeTask", 

36 "DrpAssociationPipeConfig", 

37 "DrpAssociationPipeConnections"] 

38 

39 

40class DrpAssociationPipeConnections(pipeBase.PipelineTaskConnections, 

41 dimensions=("tract", "patch", "skymap"), 

42 defaultTemplates={"coaddName": "deep", 

43 "warpTypeSuffix": "", 

44 "fakesType": ""}): 

45 diaSourceTables = pipeBase.connectionTypes.Input( 

46 doc="Set of catalogs of calibrated DiaSources.", 

47 name="{fakesType}{coaddName}Diff_diaSrcTable", 

48 storageClass="DataFrame", 

49 dimensions=("instrument", "visit", "detector"), 

50 deferLoad=True, 

51 multiple=True 

52 ) 

53 skyMap = pipeBase.connectionTypes.Input( 

54 doc="Input definition of geometry/bbox and projection/wcs for coadded " 

55 "exposures", 

56 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME, 

57 storageClass="SkyMap", 

58 dimensions=("skymap", ), 

59 ) 

60 assocDiaSourceTable = pipeBase.connectionTypes.Output( 

61 doc="Catalog of DiaSources covering the patch and associated with a " 

62 "DiaObject.", 

63 name="{fakesType}{coaddName}Diff_assocDiaSrcTable", 

64 storageClass="DataFrame", 

65 dimensions=("tract", "patch"), 

66 ) 

67 diaObjectTable = pipeBase.connectionTypes.Output( 

68 doc="Catalog of DiaObjects created from spatially associating " 

69 "DiaSources.", 

70 name="{fakesType}{coaddName}Diff_diaObjTable", 

71 storageClass="DataFrame", 

72 dimensions=("tract", "patch"), 

73 ) 

74 

75 

76class DrpAssociationPipeConfig( 

77 pipeBase.PipelineTaskConfig, 

78 pipelineConnections=DrpAssociationPipeConnections): 

79 pass 

80 

81 

82class DrpAssociationPipeTask(pipeBase.PipelineTask): 

83 """Driver pipeline for loading DiaSource catalogs in a patch/tract 

84 region and associating them. 

85 """ 

86 ConfigClass = DrpAssociationPipeConfig 

87 _DefaultName = "drpAssociation" 

88 

89 def runQuantum(self, butlerQC, inputRefs, outputRefs): 

90 inputs = butlerQC.get(inputRefs) 

91 

92 inputs["tractId"] = butlerQC.quantum.dataId["tract"] 

93 inputs["patchId"] = butlerQC.quantum.dataId["patch"] 

94 

95 outputs = self.run(**inputs) 

96 butlerQC.put(outputs, outputRefs) 

97 

98 def run(self, diaSourceTables, skyMap, tractId, patchId): 

99 """Trim DiaSources to the current Patch and run association. 

100 

101 Takes in the set of DiaSource catalogs that covers the current patch, 

102 trims them to the dimensions of the patch, and [TODO: eventually] 

103 runs association on the concatenated DiaSource Catalog. 

104 

105 Parameters 

106 ---------- 

107 diaSourceTables : `list` of `lst.daf.butler.DeferredDatasetHandle` 

108 Set of DiaSource catalogs potentially covering this patch/tract. 

109 skyMap : `lsst.skymap.BaseSkyMap` 

110 SkyMap defining the patch/tract 

111 tractId : `int` 

112 Id of current tract being processed. 

113 patchId : `int` 

114 Id of current patch being processed 

115 

116 Returns 

117 ------- 

118 output : `lsst.pipe.base.Struct` 

119 Results struct with attributes: 

120 

121 ``assocDiaSourceTable`` 

122 Table of DiaSources with updated value for diaObjectId. 

123 (`pandas.DataFrame`) 

124 ``diaObjectTable`` 

125 Table of DiaObjects from matching DiaSources 

126 (`pandas.DataFrame`). 

127 """ 

128 self.log.info("Running DPR Association on patch %i, tract %i..." % 

129 (patchId, tractId)) 

130 

131 skyInfo = makeSkyInfo(skyMap, tractId, patchId) 

132 

133 # Get the patch bounding box. 

134 innerPatchBox = geom.Box2D(skyInfo.patchInfo.getInnerBBox()) 

135 

136 diaSourceHistory = [] 

137 for catRef in diaSourceTables: 

138 cat = catRef.get( 

139 datasetType=self.config.connections.diaSourceTables, 

140 immediate=True) 

141 

142 isInTractPatch = self._trimToPatch(cat, 

143 innerPatchBox, 

144 skyInfo.wcs) 

145 

146 nDiaSrc = isInTractPatch.sum() 

147 self.log.info( 

148 "Read DiaSource catalog of length %i from visit %i, " 

149 "detector %i. Found %i sources within the patch/tract " 

150 "footprint." % 

151 (len(cat), catRef.dataId["visit"], 

152 catRef.dataId["detector"], nDiaSrc)) 

153 

154 if nDiaSrc <= 0: 

155 diaSourceHistory.append(pd.DataFrame(columns=cat.columns)) 

156 continue 

157 

158 cutCat = cat[isInTractPatch] 

159 diaSourceHistory.append(cutCat) 

160 

161 # self.associator.addCatalog() 

162 

163 diaSourceHistoryCat = pd.concat(diaSourceHistory) 

164 self.log.info("Found %i DiaSources overlaping patch %i, tract %i" 

165 % (len(diaSourceHistoryCat), patchId, tractId)) 

166 

167 return pipeBase.Struct( 

168 diaObjectTable=pd.DataFrame(columns=["diaObjectId", 

169 "nDiaSources"]), 

170 assocDiaSourceTable=diaSourceHistoryCat) 

171 

172 def _trimToPatch(self, cat, innerPatchBox, wcs): 

173 """Create generator testing if a set of DiaSources are in the 

174 patch/tract. 

175 

176 Parameters 

177 ---------- 

178 cat : `pandas.DataFrame` 

179 Catalog of DiaSources to test within patch/tract. 

180 innerPatchBox : `lsst.geom.Box2D` 

181 Bounding box of the patch. 

182 wcs : `lsst.geom.SkyWcs` 

183 Wcs of the tract. 

184 

185 Returns 

186 ------ 

187 isInPatch : `numpy.ndarray`, (N,) 

188 Booleans representing if the DiaSources are contained within the 

189 current patch and tract. 

190 """ 

191 isInPatch = np.array([ 

192 innerPatchBox.contains( 

193 wcs.skyToPixel( 

194 geom.SpherePoint(row["ra"], row["decl"], geom.degrees))) 

195 for idx, row in cat.iterrows()]) 

196 return isInPatch