Coverage for python/lsst/analysis/tools/atools/shapes.py: 29%
102 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-16 04:37 -0700
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-16 04:37 -0700
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/>.
22from __future__ import annotations
24__all__ = (
25 "ShapeSizeFractionalScalars",
26 "BasePsfResidual",
27 "ShapeSizeFractionalDiff",
28 "E1Diff",
29 "E2Diff",
30 "RhoStatistics",
31 "RelativeSizeResidualPlot",
32)
34from lsst.pex.config import Field
35from lsst.pex.config.configurableActions import ConfigurableActionField
37from ..actions.keyedData import KeyedScalars
38from ..actions.plot import FocalPlanePlot
39from ..actions.plot.rhoStatisticsPlot import RhoStatisticsPlot
40from ..actions.plot.scatterplotWithTwoHists import ScatterPlotStatsAction, ScatterPlotWithTwoHists
41from ..actions.scalar import CountAction, MedianAction, SigmaMadAction
42from ..actions.vector import (
43 CalcE,
44 CalcEDiff,
45 CalcMomentSize,
46 CalcRhoStatistics,
47 CoaddPlotFlagSelector,
48 ConvertFluxToMag,
49 DownselectVector,
50 FractionalDifference,
51 LoadVector,
52 SnSelector,
53 StarSelector,
54 VectorSelector,
55 VisitPlotFlagSelector,
56)
57from ..interfaces import AnalysisTool, KeyedData, VectorAction
60class ShapeSizeFractionalScalars(KeyedScalars):
61 vectorKey = Field[str](doc="Column key to compute scalars")
63 snFluxType = Field[str](doc="column key for the flux type used in SN selection")
65 selector = ConfigurableActionField[VectorAction](doc="Selector to use before computing Scalars")
67 def setDefaults(self):
68 super().setDefaults()
69 self.scalarActions.median = MedianAction(vectorKey=self.vectorKey) # type: ignore
70 self.scalarActions.sigmaMad = SigmaMadAction(vectorKey=self.vectorKey) # type: ignore
71 self.scalarActions.count = CountAction(vectorKey=self.vectorKey) # type: ignore
72 self.scalarActions.approxMag = MedianAction(vectorKey=self.snFluxType) # type: ignore
74 def __call__(self, data: KeyedData, **kwargs) -> KeyedData:
75 mask = kwargs.get("mask")
76 selection = self.selector(data, **kwargs)
77 if mask is not None:
78 mask &= selection
79 else:
80 mask = selection
81 return super().__call__(data, **kwargs | dict(mask=mask))
84class BasePsfResidual(AnalysisTool):
85 """Shared configuration for `prep` and `process` stages of PSF residuals.
87 This is a mixin class used by `BasePsfResidualScatterPlot` and
88 `BasePsfResidualMetric` to share common default configuration.
89 """
91 def setDefaults(self):
92 super().setDefaults()
93 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
94 self.prep.selectors.snSelector = SnSelector(fluxType="{band}_psfFlux", threshold=100)
96 self.process.buildActions.patchWhole = LoadVector()
97 self.process.buildActions.patchWhole.vectorKey = "patch"
99 self.process.buildActions.mags = ConvertFluxToMag(vectorKey="{band}_psfFlux")
101 self.process.buildActions.fracDiff = FractionalDifference(
102 actionA=CalcMomentSize(colXx="{band}_ixx", colYy="{band}_iyy", colXy="{band}_ixy"),
103 actionB=CalcMomentSize(colXx="{band}_ixxPSF", colYy="{band}_iyyPSF", colXy="{band}_ixyPSF"),
104 )
105 # Define an eDiff action and let e1Diff and e2Diff differ only in
106 # component.
107 self.process.buildActions.eDiff = CalcEDiff(
108 colA=CalcE(colXx="{band}_ixx", colYy="{band}_iyy", colXy="{band}_ixy"),
109 colB=CalcE(colXx="{band}_ixxPSF", colYy="{band}_iyyPSF", colXy="{band}_ixyPSF"),
110 )
111 self.process.buildActions.e1Diff = self.process.buildActions.eDiff
112 self.process.buildActions.e1Diff.component = "1"
113 self.process.buildActions.e2Diff = self.process.buildActions.eDiff
114 self.process.buildActions.e2Diff.component = "2"
115 # pre-compute a stellar selector mask so it can be used in the filter
116 # actions while only being computed once, alternatively the stellar
117 # selector could be calculated and applied twice in the filter stage
118 self.process.buildActions.starSelector = StarSelector()
120 self.process.filterActions.xStars = DownselectVector(
121 vectorKey="mags", selector=VectorSelector(vectorKey="starSelector")
122 )
123 # downselect the psfFlux as well
124 self.process.filterActions.psfFlux = DownselectVector(
125 vectorKey="{band}_psfFlux", selector=VectorSelector(vectorKey="starSelector")
126 )
127 self.process.filterActions.psfFluxErr = DownselectVector(
128 vectorKey="{band}_psfFluxErr", selector=VectorSelector(vectorKey="starSelector")
129 )
131 self.process.filterActions.patch = DownselectVector(
132 vectorKey="patchWhole", selector=VectorSelector(vectorKey="starSelector")
133 )
135 self.process.calculateActions.stars = ScatterPlotStatsAction(
136 vectorKey="yStars",
137 )
138 # use the downselected psfFlux
139 self.process.calculateActions.stars.highSNSelector.fluxType = "psfFlux"
140 self.process.calculateActions.stars.lowSNSelector.fluxType = "psfFlux"
141 self.process.calculateActions.stars.fluxType = "psfFlux"
143 self.produce.metric.units = {
144 "{band}_highSNStars_median": "pixel",
145 "{band}_highSNStars_sigmaMad": "pixel",
146 "{band}_highSNStars_count": "count",
147 "{band}_lowSNStars_median": "pixel",
148 "{band}_lowSNStars_sigmaMad": "pixel",
149 "{band}_lowSNStars_count": "count",
150 }
152 self.produce.plot = ScatterPlotWithTwoHists()
154 self.produce.plot.plotTypes = ["stars"]
155 self.produce.plot.xAxisLabel = "PSF Magnitude (mag)"
156 self.produce.plot.magLabel = "PSF Magnitude (mag)"
159class ShapeSizeFractionalDiff(BasePsfResidual):
160 def setDefaults(self):
161 super().setDefaults()
162 self.process.filterActions.yStars = DownselectVector(
163 vectorKey="fracDiff", selector=VectorSelector(vectorKey="starSelector")
164 )
165 self.produce.plot.yAxisLabel = "Fractional size residuals (S/S_PSF - 1)"
168class E1Diff(BasePsfResidual):
169 def setDefaults(self):
170 super().setDefaults()
171 self.process.filterActions.yStars = DownselectVector(
172 vectorKey="e1Diff", selector=VectorSelector(vectorKey="starSelector")
173 )
174 self.produce.plot.yAxisLabel = "Ellipticty residuals (e1 - e1_PSF)"
177class E2Diff(BasePsfResidual):
178 def setDefaults(self):
179 super().setDefaults()
180 self.process.filterActions.yStars = DownselectVector(
181 vectorKey="e2Diff", selector=VectorSelector(vectorKey="starSelector")
182 )
183 self.produce.plot.yAxisLabel = "Ellipticty residuals (e2 - e2_PSF)"
186class RhoStatistics(AnalysisTool):
187 parameterizedBand: bool = True
189 def setDefaults(self):
190 super().setDefaults()
191 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
192 self.prep.selectors.snSelector = SnSelector(fluxType="{band}_psfFlux", threshold=100)
193 self.prep.selectors.starSelector = StarSelector()
195 self.process.calculateActions.rho = CalcRhoStatistics()
196 self.process.calculateActions.rho.treecorr.nbins = 21
197 self.process.calculateActions.rho.treecorr.min_sep = 0.01
198 self.process.calculateActions.rho.treecorr.max_sep = 100.0
199 self.process.calculateActions.rho.treecorr.sep_units = "arcmin"
200 self.process.calculateActions.rho.treecorr.metric = "Arc"
202 self.produce.plot = RhoStatisticsPlot()
205class RelativeSizeResidualPlot(AnalysisTool):
206 def setDefaults(self):
207 super().setDefaults()
208 self.prep.selectors.flagSelector = VisitPlotFlagSelector()
209 self.prep.selectors.starSelector = StarSelector()
210 self.prep.selectors.starSelector.vectorKey = "extendedness"
211 self.process.buildActions.x = LoadVector()
212 self.process.buildActions.x.vectorKey = "x"
213 self.process.buildActions.y = LoadVector()
214 self.process.buildActions.y.vectorKey = "y"
215 self.process.buildActions.z = FractionalDifference(
216 actionA=CalcMomentSize(colXx="ixx", colYy="iyy", colXy="ixy", sizeType="trace"),
217 actionB=CalcMomentSize(colXx="ixxPSF", colYy="iyyPSF", colXy="ixyPSF", sizeType="trace"),
218 )
219 self.process.buildActions.detector = LoadVector(vectorKey="detector")
220 self.produce.plot = FocalPlanePlot()
221 self.produce.plot.zAxisLabel = "Residuals"
222 self.process.buildActions.statMask = SnSelector()
223 self.process.buildActions.statMask.threshold = 20
224 self.process.buildActions.statMask.fluxType = "psfFlux"