lsst.pipe.tasks  21.0.0-95-g1e789892+c91f73bc7b
drpAssociationPipe.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 """Pipeline for running DiaSource association in a DRP context.
24 """
25 
26 import numpy as np
27 import pandas as pd
28 
29 import lsst.geom as geom
30 import lsst.pipe.base as pipeBase
31 from lsst.skymap import BaseSkyMap
32 
33 from .coaddBase import makeSkyInfo
34 
35 __all__ = ["DrpAssociationPipeTask",
36  "DrpAssociationPipeConfig",
37  "DrpAssociationPipeConnections"]
38 
39 
40 class 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 
76 class DrpAssociationPipeConfig(
77  pipeBase.PipelineTaskConfig,
78  pipelineConnections=DrpAssociationPipeConnections):
79  pass
80 
81 
82 class 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
def run(self, skyInfo, tempExpRefList, imageScalerList, weightList, altMaskList=None, mask=None, supplementaryData=None)
def makeSkyInfo(skyMap, tractId, patchId)
Definition: coaddBase.py:289