Coverage for python / lsst / ap / association / ssSingleFrameAssociation.py: 55%
40 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 09:21 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 09:21 +0000
1#
2# LSST Data Management System
3# Copyright 2008-2016 AURA/LSST.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <https://www.lsstcorp.org/LegalNotices/>.
21#
23"""PipelineTask for associating single-frame sources with solar system objects.
25"""
27__all__ = ("SsSingleFrameAssociationConfig",
28 "SsSingleFrameAssociationTask",
29 "SsSingleFrameAssociationConnections")
30from astropy.units import deg
32import lsst.pex.config as pexConfig
33import lsst.pipe.base as pipeBase
34import lsst.pipe.base.connectionTypes as connTypes
35from lsst.pipe.tasks.ssoAssociation import SolarSystemAssociationTask
36from lsst.utils.timer import timeMethod
39class SsSingleFrameAssociationConnections(
40 pipeBase.PipelineTaskConnections,
41 dimensions=("instrument", "visit", "detector"),
42 defaultTemplates={"fakesType": ""}):
43 """Butler connections for SsSingleFrameAssociationTask.
44 """
45 exposure = connTypes.Input(
46 doc="Exposure from which source table was generated",
47 name="initial_pvi",
48 storageClass="ExposureF",
49 dimensions=("instrument", "visit", "detector"),
50 )
51 sourceTable = connTypes.Input(
52 doc="Catalog of calibrated Sources.",
53 name="initial_stars_footprints_detector",
54 storageClass="SourceCatalog",
55 dimensions=("instrument", "visit", "detector"),
56 )
57 solarSystemObjectTable = connTypes.Input(
58 doc="Optional catalog of SolarSolarSystem objects expected to be"
59 "observable in this detectorVisit.",
60 name="preloaded_SsObjects",
61 storageClass="ArrowAstropy",
62 dimensions=("instrument", "group", "detector"),
63 minimum=0,
64 )
65 associatedSsSources = connTypes.Output(
66 doc="ssSource record columns which can be computed as of association",
67 name="ssSingleFrameAssociatedSources",
68 storageClass="ArrowAstropy",
69 dimensions=("instrument", "visit", "detector"),
70 )
71 unassociatedSsObjects = connTypes.Output(
72 doc="Expected locations of an ssObject with no source",
73 name="ssSingleFrameUnassociatedObjects",
74 storageClass="ArrowAstropy",
75 dimensions=("instrument", "visit", "detector"),
76 )
79class SsSingleFrameAssociationConfig(pipeBase.PipelineTaskConfig,
80 pipelineConnections=SsSingleFrameAssociationConnections):
81 """Config for DiaPipelineTask.
82 """
83 solarSystemAssociator = pexConfig.ConfigurableField(
84 target=SolarSystemAssociationTask,
85 doc="Task used to associate DiaSources with SolarSystemObjects.",
86 )
87 imagePixelMargin = pexConfig.RangeField(
88 dtype=int,
89 default=10,
90 min=0,
91 doc="Pad the image by this many pixels before removing off-image "
92 "diaObjects for association.",
93 )
96class SsSingleFrameAssociationTask(pipeBase.PipelineTask):
97 """Task for loading, associating and storing Difference Image Analysis
98 (DIA) Objects and Sources.
99 """
100 ConfigClass = SsSingleFrameAssociationConfig
101 _DefaultName = "ssSingleFrameAssociation"
103 def __init__(self, initInputs=None, **kwargs):
104 super().__init__(**kwargs)
105 self.makeSubtask("solarSystemAssociator")
107 def runQuantum(self, butlerQC, inputRefs, outputRefs):
108 inputs = butlerQC.get(inputRefs)
109 inputs["band"] = butlerQC.quantum.dataId["band"]
110 outputs = self.run(**inputs)
112 butlerQC.put(outputs, outputRefs)
114 @timeMethod
115 def run(self,
116 exposure,
117 sourceTable,
118 band,
119 solarSystemObjectTable=None):
120 """Process DiaSources and DiaObjects.
122 Load previous DiaObjects and their DiaSource history. Calibrate the
123 values in the diaSourceCat. Associate new DiaSources with previous
124 DiaObjects. Run forced photometry at the updated DiaObject locations.
125 Store the results in the Alert Production Database (Apdb).
127 Parameters
128 ----------
129 exposure : `lsst.afw.image.ExposureF`
130 Calibrated exposure with wcs and midpoint time.
131 sourceTable : `lsst.afw.table.SourceCatalog`
132 Newly detected sources.
133 band : `str`
134 The band in which the new DiaSources were detected.
135 solarSystemObjectTable : `astropy.table.Table` or `None`
136 Preloaded Solar System objects expected to be visible in the image.
138 Returns
139 -------
140 results : `lsst.pipe.base.Struct`
141 Results struct with components.
142 - ``ssoAssocDiaSources`` : DiaSources that were associated with
143 solar system objects in this visit. (`Astropy.table.Table`)
144 - ``unAssocDiaSources`` : Set of DiaSources that were not
145 associated with any solar system object. (`astropy.table.Table`)
146 - ``nTotalSsObjects`` : Total number of SolarSystemObjects
147 contained in the CCD footprint. (`int`)
148 - ``nAssociatedSsObjects`` : Number of SolarSystemObjects
149 that were associated with DiaSources.
150 - ``ssSourceData`` : ssSource table data. (`Astropy.table.Table`)
153 Raises
154 ------
155 RuntimeError
156 Raised if duplicate DiaObjects or duplicate DiaSources are found.
157 """
158 if solarSystemObjectTable is None:
159 raise pipeBase.NoWorkFound("No ephemerides to associate. Skipping ssSingleFrameAssociation.")
161 # Associate DiaSources with DiaObjects
162 sourceTable = sourceTable.asAstropy()
163 sourceTable['ra'] = sourceTable['coord_ra'].to(deg).value
164 sourceTable['dec'] = sourceTable['coord_dec'].to(deg).value
165 sourceTable['midpointMjdTai'] = exposure.visitInfo.date.toAstropy().tai.mjd
166 sourceTable['band'] = exposure.getFilter().bandLabel
167 colNames = ['base_ClassificationSizeExtendedness_value',
168 'base_PsfFlux_instFlux',
169 'base_PsfFlux_instFluxErr',
170 ]
171 newNames = ["extendedness",
172 "psfFlux",
173 "psfFluxErr",
174 ]
175 sourceTable.rename_columns(colNames, newNames)
176 return self.solarSystemAssociator.run(sourceTable, solarSystemObjectTable,
177 exposure.visitInfo, exposure.getBBox(), exposure.wcs)