Coverage for python/lsst/ap/pipe/matchApFakes.py : 44%

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_pipe.
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"""Methods to match an input catalog to a set of fakes in AP.
23"""
25import numpy as np
26from scipy.spatial import cKDTree
28from lsst.geom import Box2D, radians, SpherePoint
29import lsst.pex.config as pexConfig
30from lsst.pipe.base import PipelineTask, PipelineTaskConnections, Struct
31import lsst.pipe.base.connectionTypes as connTypes
32from lsst.pipe.tasks.insertFakes import InsertFakesConfig
33from lsst.sphgeom import ConvexPolygon
35__all__ = ["MatchApFakesTask",
36 "MatchApFakesConfig",
37 "MatchApFakesConnections"]
40class MatchApFakesConnections(PipelineTaskConnections,
41 defaultTemplates={"CoaddName": "deep",
42 "fakesType": ""},
43 dimensions=("tract",
44 "skymap",
45 "instrument",
46 "visit",
47 "detector")):
48 fakeCat = connTypes.Input(
49 doc="Catalog of fake sources to draw inputs from.",
50 name="{CoaddName}Coadd_fakeSourceCat",
51 storageClass="DataFrame",
52 dimensions=("tract", "skymap")
53 )
54 diffIm = connTypes.Input(
55 doc="Difference image on which the DiaSources were detected.",
56 name="{fakesType}{CoaddName}Diff_differenceExp",
57 storageClass="ExposureF",
58 dimensions=("instrument", "visit", "detector"),
59 )
60 associatedDiaSources = connTypes.Input(
61 doc="Optional output storing the DiaSource catalog after matching and "
62 "SDMification.",
63 name="{fakesType}{CoaddName}Diff_assocDiaSrc",
64 storageClass="DataFrame",
65 dimensions=("instrument", "visit", "detector"),
66 )
67 matchedDiaSources = connTypes.Output(
68 doc="",
69 name="{fakesType}{CoaddName}Diff_matchDiaSrc",
70 storageClass="DataFrame",
71 dimensions=("instrument", "visit", "detector"),
72 )
75class MatchApFakesConfig(
76 InsertFakesConfig,
77 pipelineConnections=MatchApFakesConnections):
78 """Config for MatchApFakesTask.
79 """
80 matchDistanceArcseconds = pexConfig.RangeField(
81 doc="Distance in arcseconds to ",
82 dtype=float,
83 default=1,
84 min=0,
85 max=10,
86 )
89class MatchApFakesTask(PipelineTask):
90 """Create and store a set of spatially uniform star fakes over the sphere
91 for use in AP processing. Additionally assign random magnitudes to said
92 fakes and assign them to be inserted into either a visit exposure or
93 template exposure.
94 """
96 _DefaultName = "matchApFakes"
97 ConfigClass = MatchApFakesConfig
99 def runQuantum(self, butlerQC, inputRefs, outputRefs):
100 inputs = butlerQC.get(inputRefs)
102 outputs = self.run(**inputs)
103 butlerQC.put(outputs, outputRefs)
105 def run(self, fakeCat, diffIm, associatedDiaSources):
106 """Match fakes to detected diaSources within a difference image bound.
108 Parameters
109 ----------
110 fakeCat : `pandas.DataFrame`
111 Catalog of fakes to match to detected diaSources.
112 diffIm : `lsst.afw.image.Exposure`
113 Difference image where ``associatedDiaSources`` were detected in.
114 associatedDiaSources : `pandas.DataFrame`
115 Catalog of difference image sources detected in ``diffIm``.
117 Returns
118 -------
119 result : `lsst.pipe.base.Struct`
120 Results struct with components.
122 - ``matchedDiaSources`` : Fakes matched to input diaSources. Has
123 length of ``fakeCat``. (`pandas.DataFrame`)
124 """
125 trimmedFakes = self._trimFakeCat(fakeCat, diffIm)
126 nPossibleFakes = len(trimmedFakes)
128 fakeVects = self._getVectors(trimmedFakes[self.config.raColName],
129 trimmedFakes[self.config.decColName])
130 diaSrcVects = self._getVectors(
131 np.radians(associatedDiaSources.loc[:, "ra"]),
132 np.radians(associatedDiaSources.loc[:, "decl"]))
134 diaSrcTree = cKDTree(diaSrcVects)
135 dist, idxs = diaSrcTree.query(
136 fakeVects,
137 distance_upper_bound=np.radians(self.config.matchDistanceArcseconds / 3600))
138 nFakesFound = np.isfinite(dist).sum()
140 self.log.info(f"Found {nFakesFound} out of {nPossibleFakes} possible.")
141 diaSrcIds = associatedDiaSources.iloc[np.where(np.isfinite(dist), idxs, 0)]["diaSourceId"].to_numpy()
142 matchedFakes = trimmedFakes.assign(diaSourceId=np.where(np.isfinite(dist), diaSrcIds, 0))
144 return Struct(
145 matchedDiaSources=matchedFakes.merge(
146 associatedDiaSources.reset_index(drop=True), on="diaSourceId", how="left")
147 )
149 def _trimFakeCat(self, fakeCat, image):
150 """Trim the fake cat to about the size of the input image.
152 Parameters
153 ----------
154 fakeCat : `pandas.core.frame.DataFrame`
155 The catalog of fake sources to be input
156 image : `lsst.afw.image.exposure.exposure.ExposureF`
157 The image into which the fake sources should be added
159 Returns
160 -------
161 fakeCat : `pandas.core.frame.DataFrame`
162 The original fakeCat trimmed to the area of the image
163 """
164 wcs = image.getWcs()
166 bbox = Box2D(image.getBBox())
167 corners = bbox.getCorners()
169 skyCorners = wcs.pixelToSky(corners)
170 region = ConvexPolygon([s.getVector() for s in skyCorners])
172 def trim(row):
173 coord = SpherePoint(row[self.config.raColName], row[self.config.decColName], radians)
174 return region.contains(coord.getVector())
176 return fakeCat[fakeCat.apply(trim, axis=1)]
178 def _getVectors(self, ras, decs):
179 """Convert ra dec to unit vectors on the sphere.
181 Parameters
182 ----------
183 ras : `numpy.ndarray`, (N,)
184 RA coordinates in radians.
185 decs : `numpy.ndarray`, (N,)
186 Dec coordinates in radians.
188 Returns
189 -------
190 vectors : `numpy.ndarray`, (N, 3)
191 Vectors on the unit sphere for the given RA/DEC values.
192 """
193 vectors = np.empty((len(ras), 3))
195 vectors[:, 2] = np.sin(decs)
196 vectors[:, 0] = np.cos(decs) * np.cos(ras)
197 vectors[:, 1] = np.cos(decs) * np.sin(ras)
199 return vectors