Coverage for python / lsst / analysis / tools / atools / coaddInputCount.py: 22%
85 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-21 10:55 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-21 10:55 +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__ = ("CoaddInputCount", "CoaddQualityCheck", "CoaddQualityPlot")
25from lsst.pex.config import ListField
27from ..actions.plot.calculateRange import MinMax
28from ..actions.plot.coaddDepthPlot import CoaddDepthPlot
29from ..actions.plot.skyPlot import SkyPlot
30from ..actions.scalar.scalarActions import MeanAction, MedianAction, SigmaMadAction, StdevAction
31from ..actions.vector import BandSelector, CoaddPlotFlagSelector, DownselectVector, LoadVector, SnSelector
32from ..interfaces import AnalysisTool
35class CoaddInputCount(AnalysisTool):
36 """Tract-wide metrics pertaining to how many exposures have gone into
37 a deep coadd, per band.
39 This AnalysisTool is designed to run on an object table, which is only
40 created for deep coadds, not template coadds.
41 """
43 def setDefaults(self):
44 super().setDefaults()
45 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
46 # Set this to an empty list to look at the band
47 # the plot is being made in.
48 self.prep.selectors.flagSelector.bands = []
50 self.prep.selectors.snSelector = SnSelector()
51 self.prep.selectors.snSelector.fluxType = "{band}_psfFlux"
52 self.prep.selectors.snSelector.threshold = 5
54 self.process.buildActions.x = LoadVector()
55 self.process.buildActions.x.vectorKey = "coord_ra"
56 self.process.buildActions.y = LoadVector()
57 self.process.buildActions.y.vectorKey = "coord_dec"
58 self.process.buildActions.statMask = SnSelector()
59 self.process.buildActions.statMask.fluxType = "{band}_psfFlux"
60 self.process.buildActions.statMask.threshold = 5
62 self.process.buildActions.z = LoadVector()
63 self.process.buildActions.z.vectorKey = "{band}_inputCount"
65 self.process.buildActions.patch = LoadVector()
66 self.process.buildActions.patch.vectorKey = "patch"
68 self.process.calculateActions.median = MedianAction()
69 self.process.calculateActions.median.vectorKey = "z"
71 self.process.calculateActions.mean = MeanAction()
72 self.process.calculateActions.mean.vectorKey = "z"
74 self.process.calculateActions.sigmaMad = SigmaMadAction()
75 self.process.calculateActions.sigmaMad.vectorKey = "z"
77 # SkyPlot of number of contributing exposures in coad, per tract/band:
78 self.produce.plot = SkyPlot()
79 self.produce.plot.plotTypes = ["any"]
80 self.produce.plot.plotName = "{band}_inputCount"
81 self.produce.plot.xAxisLabel = "R.A. (deg)"
82 self.produce.plot.yAxisLabel = "Dec. (deg)"
83 self.produce.plot.zAxisLabel = "Input Count"
84 self.produce.plot.plotOutlines = True
85 self.produce.plot.showExtremeOutliers = False
86 self.produce.plot.colorbarRange = MinMax()
88 # Summary metrics for the whole coadd, per tract/band.
89 self.produce.metric.units = {"median": "ct", "sigmaMad": "ct", "mean": "ct"}
90 self.produce.metric.newNames = {
91 "median": "{band}_inputCount_median",
92 "mean": "{band}_inputCount_mean",
93 "sigmaMad": "{band}_inputCount_sigmaMad",
94 }
97class CoaddQualityCheck(AnalysisTool):
98 """Compute the percentage of each coadd that has a number of input
99 exposures exceeding a threshold.
101 This AnalysisTool is designed to run on any coadd, provided a
102 coadd_depth_table is created first (via CoaddDepthSummaryAnalysisTask).
104 For example, if exactly half of a coadd patch contains 15 overlapping
105 constituent visits and half contains fewer, the value computed for
106 `depth_above_threshold_12` would be 50.
108 These values come from the n_image data product, which is an image
109 identical to the coadd but with pixel values of the number of input
110 images instead of flux or counts. n_images are persisted during
111 coadd assembly.
112 """
114 band_list = ListField(
115 default=["u", "g", "r", "i", "z", "y"],
116 dtype=str,
117 doc="Bands for colors.",
118 )
120 threshold_list = ListField(
121 default=[1, 3, 5, 12],
122 dtype=int,
123 doc="The n_image pixel value thresholds.",
124 )
126 quantile_list = ListField(
127 default=[5, 10, 25, 50, 75, 90, 95],
128 dtype=int,
129 doc="The percentiles at which to compute n_image values, in ascending order.",
130 )
132 def setDefaults(self):
133 super().setDefaults()
135 self.process.buildActions.patch = LoadVector(vectorKey="patch")
136 self.process.buildActions.band = LoadVector(vectorKey="band")
138 def finalize(self):
139 for threshold in self.threshold_list:
140 # This gives a RuntimeWarning whenever the tract doesn't have
141 # a particular band. Need to use a band list derived from the
142 # bands found in the "band" column of a given tract.
143 for band in self.band_list:
144 name = f"depth_above_threshold_{threshold}"
145 setattr(self.process.buildActions, name, LoadVector(vectorKey=name))
146 setattr(
147 self.process.filterActions,
148 f"{name}_{band}",
149 DownselectVector(vectorKey=name, selector=BandSelector(bands=[band])),
150 )
151 setattr(
152 self.process.calculateActions,
153 f"{name}_{band}_median",
154 MedianAction(vectorKey=f"{name}_{band}"),
155 )
156 setattr(
157 self.process.calculateActions,
158 f"{name}_{band}_mean",
159 MeanAction(vectorKey=f"{name}_{band}"),
160 )
161 setattr(
162 self.process.calculateActions,
163 f"{name}_{band}_stdev",
164 StdevAction(vectorKey=f"{name}_{band}"),
165 )
167 # The units for the quantity are dimensionless (percentage)
168 self.produce.metric.units[f"{name}_{band}_median"] = ""
169 self.produce.metric.units[f"{name}_{band}_mean"] = ""
170 self.produce.metric.units[f"{name}_{band}_stdev"] = ""
172 for quantile in self.quantile_list:
173 for band in self.band_list:
174 name = f"depth_{quantile}_percentile"
175 setattr(self.process.buildActions, name, LoadVector(vectorKey=name))
176 setattr(
177 self.process.filterActions,
178 f"{name}_{band}",
179 DownselectVector(vectorKey=name, selector=BandSelector(bands=[band])),
180 )
181 setattr(
182 self.process.calculateActions,
183 f"{name}_{band}_median",
184 MedianAction(vectorKey=f"{name}_{band}"),
185 )
186 setattr(
187 self.process.calculateActions,
188 f"{name}_{band}_mean",
189 MeanAction(vectorKey=f"{name}_{band}"),
190 )
191 setattr(
192 self.process.calculateActions,
193 f"{name}_{band}_stdev",
194 StdevAction(vectorKey=f"{name}_{band}"),
195 )
197 # The units for the quantity are dimensionless (percentage)
198 self.produce.metric.units[f"{name}_{band}_median"] = ""
199 self.produce.metric.units[f"{name}_{band}_mean"] = ""
200 self.produce.metric.units[f"{name}_{band}_stdev"] = ""
203class CoaddQualityPlot(AnalysisTool):
204 """Make a plot of coadd depth."""
206 parameterizedBand: bool = False
208 def setDefaults(self):
209 super().setDefaults()
210 self.process.buildActions.patch = LoadVector(vectorKey="patch")
211 self.process.buildActions.band = LoadVector(vectorKey="band")
212 self.process.buildActions.depth = LoadVector(vectorKey="depth")
213 self.process.buildActions.pixels = LoadVector(vectorKey="pixels")
215 self.produce.plot = CoaddDepthPlot()