Coverage for python/lsst/analysis/tools/tasks/astrometricCatalogMatch.py: 35%
78 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-05 14:05 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-05 14:05 +0000
1# This file is part of analysis_tools.
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__all__ = (
23 "AstrometricCatalogMatchConfig",
24 "AstrometricCatalogMatchTask",
25 "AstrometricCatalogMatchVisitConfig",
26 "AstrometricCatalogMatchVisitTask",
27)
29import lsst.geom
30import lsst.pex.config as pexConfig
31import lsst.pipe.base as pipeBase
32import numpy as np
33from astropy.table import Table
34from astropy.time import Time
35from lsst.pipe.tasks.configurableActions import ConfigurableActionStructField
36from lsst.pipe.tasks.loadReferenceCatalog import LoadReferenceCatalogTask
38from ..actions.vector import VisitPlotFlagSelector
39from ..tasks.catalogMatch import CatalogMatchConfig, CatalogMatchConnections, CatalogMatchTask
42class AstrometricCatalogMatchConfig(CatalogMatchConfig, pipelineConnections=CatalogMatchConnections):
43 bands = pexConfig.ListField[str](
44 doc="The bands to persist downstream",
45 default=["u", "g", "r", "i", "z", "y"],
46 )
48 def setDefaults(self):
49 super().setDefaults()
50 self.referenceCatalogLoader.doApplyColorTerms = False
51 self.referenceCatalogLoader.refObjLoader.requireProperMotion = True
52 self.referenceCatalogLoader.refObjLoader.anyFilterMapsToThis = "phot_g_mean"
55class AstrometricCatalogMatchTask(CatalogMatchTask):
56 """Match a tract-level catalog to a reference catalog"""
58 ConfigClass = AstrometricCatalogMatchConfig
59 _DefaultName = "analysisToolsAstrometricCatalogMatch"
61 def runQuantum(self, butlerQC, inputRefs, outputRefs):
62 # Docs inherited from base class
64 inputs = butlerQC.get(inputRefs)
65 columns = self.prepColumns(self.config.bands)
66 table = inputs["catalog"].get(parameters={"columns": columns})
68 tract = butlerQC.quantum.dataId["tract"]
70 loaderTask = LoadReferenceCatalogTask(
71 config=self.config.referenceCatalogLoader,
72 dataIds=[ref.dataId for ref in inputRefs.refCat],
73 name=inputs["refCat"][0].ref.datasetType.name,
74 refCats=inputs["refCat"],
75 )
77 skymap = inputs.pop("skymap")
78 loadedRefCat = self._loadRefCat(loaderTask, skymap[tract])
79 outputs = self.run(catalog=table, loadedRefCat=loadedRefCat, bands=self.config.bands)
81 butlerQC.put(outputs, outputRefs)
84class AstrometricCatalogMatchVisitConnections(
85 pipeBase.PipelineTaskConnections,
86 dimensions=("visit",),
87 defaultTemplates={"targetCatalog": "sourceTable_visit", "refCatalog": "gaia_dr3_20230707"},
88):
89 catalog = pipeBase.connectionTypes.Input(
90 doc="The visit-wide catalog to make plots from.",
91 storageClass="ArrowAstropy",
92 name="{targetCatalog}",
93 dimensions=("visit",),
94 deferLoad=True,
95 )
97 refCat = pipeBase.connectionTypes.PrerequisiteInput(
98 doc="The astrometry reference catalog to match to loaded input catalog sources.",
99 name="{refCatalog}",
100 storageClass="SimpleCatalog",
101 dimensions=("skypix",),
102 deferLoad=True,
103 multiple=True,
104 )
106 visitSummaryTable = pipeBase.connectionTypes.Input(
107 doc="A summary table of the ccds in the visit",
108 storageClass="ExposureCatalog",
109 name="visitSummary",
110 dimensions=("visit",),
111 )
113 matchedCatalog = pipeBase.connectionTypes.Output(
114 doc="Catalog with matched target and reference objects with separations",
115 name="{targetCatalog}_{refCatalog}_match",
116 storageClass="ArrowAstropy",
117 dimensions=("visit",),
118 )
121class AstrometricCatalogMatchVisitConfig(
122 AstrometricCatalogMatchConfig, pipelineConnections=AstrometricCatalogMatchVisitConnections
123):
124 selectorActions = ConfigurableActionStructField(
125 doc="Which selectors to use to narrow down the data for QA plotting.",
126 default={"flagSelector": VisitPlotFlagSelector},
127 )
129 extraColumns = pexConfig.ListField[str](
130 doc="Other catalog columns to persist to downstream tasks",
131 default=["psfFlux", "psfFluxErr"],
132 )
134 bands = pexConfig.ListField[str](
135 doc="The bands to persist downstream",
136 default=[],
137 )
139 def setDefaults(self):
140 # sourceSelectorActions.sourceSelector is StarSelector
141 self.sourceSelectorActions.sourceSelector.vectorKey = "extendedness"
142 # extraColumnSelectors.selector1 is SnSelector
143 self.extraColumnSelectors.selector1.fluxType = "psfFlux"
144 # extraColumnSelectors.selector2 is GalaxySelector
145 self.extraColumnSelectors.selector2.vectorKey = "extendedness"
146 self.referenceCatalogLoader.doApplyColorTerms = False
147 self.referenceCatalogLoader.refObjLoader.requireProperMotion = False
148 self.referenceCatalogLoader.refObjLoader.anyFilterMapsToThis = "phot_g_mean"
151class AstrometricCatalogMatchVisitTask(AstrometricCatalogMatchTask):
152 """Match a visit-level catalog to a reference catalog"""
154 ConfigClass = AstrometricCatalogMatchVisitConfig
155 _DefaultName = "analysisToolsAstrometricCatalogMatchVisit"
157 def runQuantum(self, butlerQC, inputRefs, outputRefs):
158 # Docs inherited from base class
160 inputs = butlerQC.get(inputRefs)
162 columns = ["coord_ra", "coord_dec", "detector"] + self.config.extraColumns.list()
163 for selectorAction in [
164 self.config.selectorActions,
165 self.config.sourceSelectorActions,
166 self.config.extraColumnSelectors,
167 ]:
168 for selector in selectorAction:
169 selectorSchema = selector.getFormattedInputSchema()
170 columns += [s[0] for s in selectorSchema]
172 table = inputs["catalog"].get(parameters={"columns": columns})
174 loaderTask = LoadReferenceCatalogTask(
175 config=self.config.referenceCatalogLoader,
176 dataIds=[ref.dataId for ref in inputRefs.refCat],
177 name=inputs["refCat"][0].ref.datasetType.name,
178 refCats=inputs["refCat"],
179 )
181 visitSummaryTable = inputs.pop("visitSummaryTable")
182 loadedRefCat = self._loadRefCat(loaderTask, visitSummaryTable)
183 outputs = self.run(catalog=table, loadedRefCat=loadedRefCat, bands=self.config.bands)
185 butlerQC.put(outputs, outputRefs)
187 def _loadRefCat(self, loaderTask, visitSummaryTable):
188 """Make a reference catalog with coordinates in degrees
190 Parameters
191 ----------
192 visitSummaryTable : `lsst.afw.table.ExposureCatalog`
193 The table of visit information
194 """
195 # Get convex hull around the detectors, then get its center and radius
196 corners = []
197 for visSum in visitSummaryTable:
198 for ra, dec in zip(visSum["raCorners"], visSum["decCorners"]):
199 # If the coordinates are nan then don't keep going
200 # because it crashes later
201 if not np.isfinite(ra) or not np.isfinite(dec):
202 raise pipeBase.NoWorkFound("Visit summary corners not finite")
203 corners.append(lsst.geom.SpherePoint(ra, dec, units=lsst.geom.degrees).getVector())
204 visitBoundingCircle = lsst.sphgeom.ConvexPolygon.convexHull(corners).getBoundingCircle()
205 center = lsst.geom.SpherePoint(visitBoundingCircle.getCenter())
206 radius = visitBoundingCircle.getOpeningAngle()
208 # Get the observation date of the visit
209 obsDate = visSum.getVisitInfo().getDate()
210 epoch = Time(obsDate.toPython())
212 # Load the reference catalog in the skyCircle of the detectors, then
213 # convert the coordinates to degrees and convert the catalog to a
214 # dataframe
216 filterName = self.config.referenceCatalogLoader.refObjLoader.anyFilterMapsToThis
217 loadedRefCat = loaderTask.getSkyCircleCatalog(center, radius, filterName, epoch=epoch)
219 return Table(loadedRefCat)