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