Coverage for python/lsst/analysis/tools/atools/astrometricRepeatability.py: 25%
105 statements
« prev ^ index » next coverage.py v7.4.3, created at 2024-02-24 11:17 +0000
« prev ^ index » next coverage.py v7.4.3, created at 2024-02-24 11:17 +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/>.
21from __future__ import annotations
23__all__ = (
24 "AstrometricRelativeRepeatability",
25 "StellarAstrometricResidualsRAFocalPlanePlot",
26 "StellarAstrometricResidualsDecFocalPlanePlot",
27 "StellarAstrometricResidualStdDevRAFocalPlanePlot",
28 "StellarAstrometricResidualStdDevDecFocalPlanePlot",
29 "StellarAstrometricResidualsRASkyPlot",
30 "StellarAstrometricResidualsDecSkyPlot",
31)
33from lsst.pex.config import Field
35from ..actions.keyedData import CalcRelativeDistances
36from ..actions.plot import FocalPlanePlot, HistPanel, HistPlot, SkyPlot
37from ..actions.vector import (
38 BandSelector,
39 ConvertFluxToMag,
40 ConvertUnits,
41 DownselectVector,
42 LoadVector,
43 RAcosDec,
44 RangeSelector,
45 ResidualWithPerGroupStatistic,
46 SnSelector,
47 ThresholdSelector,
48)
49from ..interfaces import AnalysisTool
52class StellarAstrometricResidualsBase(AnalysisTool):
53 """Plot mean astrometric residuals.
55 The individual source measurements are grouped together by object index
56 and the per-group centroid is computed. The residuals between the
57 individual sources and these centroids are then used to construct a plot
58 showing the mean residual as a function of the focal-plane or sky position.
59 """
61 def setDefaults(self):
62 super().setDefaults()
64 # Apply per-source selection criteria
65 self.prep.selectors.bandSelector = BandSelector()
67 self.process.buildActions.mags = ConvertFluxToMag(vectorKey="psfFlux")
68 self.process.buildActions.residual = ConvertUnits()
69 self.process.buildActions.residual.inUnit = "degree"
70 self.process.buildActions.residual.outUnit = "marcsec"
72 self.process.buildActions.residual.buildAction = ResidualWithPerGroupStatistic()
74 self.process.buildActions.x = LoadVector(vectorKey="x")
75 self.process.buildActions.y = LoadVector(vectorKey="y")
77 self.process.buildActions.detector = LoadVector(vectorKey="detector")
79 self.process.filterActions.x = DownselectVector(vectorKey="x")
80 self.process.filterActions.x.selector = ThresholdSelector(
81 vectorKey="mags",
82 op="le",
83 threshold=24,
84 )
85 self.process.filterActions.y = DownselectVector(
86 vectorKey="y", selector=self.process.filterActions.x.selector
87 )
88 self.process.filterActions.z = DownselectVector(
89 vectorKey="residual", selector=self.process.filterActions.x.selector
90 )
91 self.process.filterActions.detector = DownselectVector(
92 vectorKey="detector", selector=self.process.filterActions.x.selector
93 )
95 self.process.buildActions.statMask = SnSelector()
96 self.process.buildActions.statMask.threshold = 0
97 self.process.buildActions.statMask.fluxType = "psfFlux"
100class StellarAstrometricResidualsRASkyPlot(StellarAstrometricResidualsBase):
101 """Plot mean astrometric residuals in RA as a function of the position in
102 RA and Dec.
103 """
105 def setDefaults(self):
106 super().setDefaults()
108 # Compute per-group quantities
109 self.process.buildActions.residual.buildAction.buildAction = RAcosDec()
110 self.process.buildActions.x = LoadVector(vectorKey="coord_ra")
111 self.process.buildActions.y = LoadVector(vectorKey="coord_dec")
113 self.produce = SkyPlot()
115 self.produce.plotTypes = ["any"]
116 self.produce.plotName = "ra_residuals"
117 self.produce.xAxisLabel = "R.A. (degrees)"
118 self.produce.yAxisLabel = "Dec. (degrees)"
119 self.produce.zAxisLabel = "RAcos(Dec) - RAcos(Dec)$_{mean}$"
122class StellarAstrometricResidualsDecSkyPlot(StellarAstrometricResidualsBase):
123 """Plot mean astrometric residuals in RA as a function of the position in
124 RA and Dec.
125 """
127 def setDefaults(self):
128 super().setDefaults()
130 # Compute per-group quantities
131 self.process.buildActions.residual.buildAction.buildAction.vectorKey = "coord_dec"
132 self.process.buildActions.x = LoadVector(vectorKey="coord_ra")
133 self.process.buildActions.y = LoadVector(vectorKey="coord_dec")
135 self.produce = SkyPlot()
137 self.produce.plotTypes = ["any"]
138 self.produce.plotName = "ra_residuals"
139 self.produce.xAxisLabel = "R.A. (degrees)"
140 self.produce.yAxisLabel = "Dec. (degrees)"
141 self.produce.zAxisLabel = "RAcos(Dec) - RAcos(Dec)$_{mean}$"
144class StellarAstrometricResidualsRAFocalPlanePlot(StellarAstrometricResidualsBase):
145 """Plot mean astrometric residuals in RA as a function of the focal plane
146 position.
147 """
149 def setDefaults(self):
150 super().setDefaults()
152 # Compute per-group quantities
153 self.process.buildActions.residual.buildAction.buildAction = RAcosDec()
155 self.produce = FocalPlanePlot()
156 self.produce.zAxisLabel = "RAcos(Dec) - RAcos(Dec)$_{mean}$ (mArcsec)"
159class StellarAstrometricResidualStdDevRAFocalPlanePlot(StellarAstrometricResidualsBase):
160 """Plot mean astrometric residuals in RA as a function of the focal plane
161 position.
162 """
164 def setDefaults(self):
165 super().setDefaults()
167 # Compute per-group quantities
168 self.process.buildActions.residual.buildAction.buildAction = RAcosDec()
170 self.produce = FocalPlanePlot()
171 self.produce.statistic = "std"
172 self.produce.zAxisLabel = "Std(RAcos(Dec) - RAcos(Dec)$_{mean}$) (mArcsec)"
175class StellarAstrometricResidualsDecFocalPlanePlot(StellarAstrometricResidualsBase):
176 """Plot mean astrometric residuals in RA as a function of the focal plane
177 position.
178 """
180 def setDefaults(self):
181 super().setDefaults()
183 # Compute per-group quantities
184 self.process.buildActions.residual.buildAction.buildAction.vectorKey = "coord_dec"
186 self.produce = FocalPlanePlot()
187 self.produce.zAxisLabel = "Dec - Dec$_{mean}$ (mArcsec)"
190class StellarAstrometricResidualStdDevDecFocalPlanePlot(StellarAstrometricResidualsBase):
191 """Plot mean astrometric residuals in RA as a function of the focal plane
192 position.
193 """
195 def setDefaults(self):
196 super().setDefaults()
198 # Compute per-group quantities
199 self.process.buildActions.residual.buildAction.buildAction.vectorKey = "coord_dec"
201 self.produce = FocalPlanePlot()
202 self.produce.statistic = "std"
203 self.produce.zAxisLabel = "Std(Dec - Dec$_{mean}$) (mArcsec)"
206class AstrometricRelativeRepeatability(AnalysisTool):
207 """Calculate the AMx, ADx, AFx metrics and make histograms showing the data
208 used to compute the metrics.
209 """
211 fluxType = Field[str](doc="Flux type to calculate repeatability with", default="psfFlux")
212 xValue = Field[int](doc="Metric suffix corresponding to annulus size (1, 2, or 3)", default=1)
214 def setDefaults(self):
215 super().setDefaults()
216 self.prep.selectors.bandSelector = BandSelector()
217 # Following what was done in faro, only sources with S/N between 50
218 # and 50000 are included. The other filtering that was done in faro
219 # is now covered by only including sources from isolated_star_sources.
220 self.prep.selectors.snSelector = SnSelector()
221 self.prep.selectors.snSelector.threshold = 50
222 self.prep.selectors.snSelector.maxSN = 50000
224 # Select only sources with magnitude between 17 and 21.5
225 self.process.filterActions.coord_ra = DownselectVector(vectorKey="coord_ra")
226 self.process.filterActions.coord_ra.selector = RangeSelector(
227 vectorKey="mags", minimum=17, maximum=21.5
228 )
229 self.process.filterActions.coord_dec = DownselectVector(
230 vectorKey="coord_dec", selector=self.process.filterActions.coord_ra.selector
231 )
232 self.process.filterActions.obj_index = DownselectVector(
233 vectorKey="obj_index", selector=self.process.filterActions.coord_ra.selector
234 )
235 self.process.filterActions.visit = DownselectVector(
236 vectorKey="visit", selector=self.process.filterActions.coord_ra.selector
237 )
239 self.process.calculateActions.rms = CalcRelativeDistances()
241 self.produce.metric.units = {
242 "AMx": "mas",
243 "AFx": "percent",
244 "ADx": "mas",
245 }
247 self.produce.plot = HistPlot()
249 self.produce.plot.panels["panel_sep"] = HistPanel()
250 self.produce.plot.panels["panel_sep"].hists = dict(separationResiduals="Source separations")
251 self.produce.plot.panels["panel_sep"].label = "Separation Distances (marcsec)"
253 self.produce.plot.panels["panel_rms"] = HistPanel()
254 self.produce.plot.panels["panel_rms"].hists = dict(rmsDistances="Object RMS")
255 self.produce.plot.panels["panel_rms"].label = "Per-Object RMS (marcsec)"
256 # TODO: DM-39163 add reference lines for ADx, AMx, and AFx.
258 def finalize(self):
259 super().finalize()
260 self.prep.selectors.snSelector.fluxType = self.fluxType
261 self.process.buildActions.mags = ConvertFluxToMag(vectorKey=self.fluxType)
263 self.produce.metric.newNames = {
264 "AMx": f"{{band}}_AM{self.xValue}",
265 "AFx": f"{{band}}_AF{self.xValue}",
266 "ADx": f"{{band}}_AD{self.xValue}",
267 }