Coverage for python/lsst/analysis/tools/atools/shapes.py: 31%
84 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-26 04:08 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-26 04:08 -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)
33from lsst.pex.config import Field
34from lsst.pex.config.configurableActions import ConfigurableActionField
36from ..actions.keyedData import KeyedScalars
37from ..actions.plot.rhoStatisticsPlot import RhoStatisticsPlot
38from ..actions.plot.scatterplotWithTwoHists import ScatterPlotStatsAction, ScatterPlotWithTwoHists
39from ..actions.scalar import CountAction, MedianAction, SigmaMadAction
40from ..actions.vector import (
41 CalcE,
42 CalcEDiff,
43 CalcMomentSize,
44 CalcRhoStatistics,
45 CoaddPlotFlagSelector,
46 ConvertFluxToMag,
47 DownselectVector,
48 FractionalDifference,
49 LoadVector,
50 SnSelector,
51 StarSelector,
52 VectorSelector,
53)
54from ..interfaces import AnalysisTool, KeyedData, VectorAction
57class ShapeSizeFractionalScalars(KeyedScalars):
58 vectorKey = Field[str](doc="Column key to compute scalars")
60 snFluxType = Field[str](doc="column key for the flux type used in SN selection")
62 selector = ConfigurableActionField[VectorAction](doc="Selector to use before computing Scalars")
64 def setDefaults(self):
65 super().setDefaults()
66 self.scalarActions.median = MedianAction(vectorKey=self.vectorKey) # type: ignore
67 self.scalarActions.sigmaMad = SigmaMadAction(vectorKey=self.vectorKey) # type: ignore
68 self.scalarActions.count = CountAction(vectorKey=self.vectorKey) # type: ignore
69 self.scalarActions.approxMag = MedianAction(vectorKey=self.snFluxType) # type: ignore
71 def __call__(self, data: KeyedData, **kwargs) -> KeyedData:
72 mask = kwargs.get("mask")
73 selection = self.selector(data, **kwargs)
74 if mask is not None:
75 mask &= selection
76 else:
77 mask = selection
78 return super().__call__(data, **kwargs | dict(mask=mask))
81class BasePsfResidual(AnalysisTool):
82 """Shared configuration for `prep` and `process` stages of PSF residuals.
84 This is a mixin class used by `BasePsfResidualScatterPlot` and
85 `BasePsfResidualMetric` to share common default configuration.
86 """
88 def setDefaults(self):
89 super().setDefaults()
90 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
91 self.prep.selectors.snSelector = SnSelector(fluxType="{band}_psfFlux", threshold=100)
93 self.process.buildActions.patchWhole = LoadVector()
94 self.process.buildActions.patchWhole.vectorKey = "patch"
96 self.process.buildActions.mags = ConvertFluxToMag(vectorKey="{band}_psfFlux")
98 self.process.buildActions.fracDiff = FractionalDifference(
99 actionA=CalcMomentSize(colXx="{band}_ixx", colYy="{band}_iyy", colXy="{band}_ixy"),
100 actionB=CalcMomentSize(colXx="{band}_ixxPSF", colYy="{band}_iyyPSF", colXy="{band}_ixyPSF"),
101 )
102 # Define an eDiff action and let e1Diff and e2Diff differ only in
103 # component.
104 self.process.buildActions.eDiff = CalcEDiff(
105 colA=CalcE(colXx="{band}_ixx", colYy="{band}_iyy", colXy="{band}_ixy"),
106 colB=CalcE(colXx="{band}_ixxPSF", colYy="{band}_iyyPSF", colXy="{band}_ixyPSF"),
107 )
108 self.process.buildActions.e1Diff = self.process.buildActions.eDiff
109 self.process.buildActions.e1Diff.component = "1"
110 self.process.buildActions.e2Diff = self.process.buildActions.eDiff
111 self.process.buildActions.e2Diff.component = "2"
112 # pre-compute a stellar selector mask so it can be used in the filter
113 # actions while only being computed once, alternatively the stellar
114 # selector could be calculated and applied twice in the filter stage
115 self.process.buildActions.starSelector = StarSelector()
117 self.process.filterActions.xStars = DownselectVector(
118 vectorKey="mags", selector=VectorSelector(vectorKey="starSelector")
119 )
120 # downselect the psfFlux as well
121 self.process.filterActions.psfFlux = DownselectVector(
122 vectorKey="{band}_psfFlux", selector=VectorSelector(vectorKey="starSelector")
123 )
124 self.process.filterActions.psfFluxErr = DownselectVector(
125 vectorKey="{band}_psfFluxErr", selector=VectorSelector(vectorKey="starSelector")
126 )
128 self.process.filterActions.patch = DownselectVector(
129 vectorKey="patchWhole", selector=VectorSelector(vectorKey="starSelector")
130 )
132 self.process.calculateActions.stars = ScatterPlotStatsAction(
133 vectorKey="yStars",
134 )
135 # use the downselected psfFlux
136 self.process.calculateActions.stars.highSNSelector.fluxType = "psfFlux"
137 self.process.calculateActions.stars.lowSNSelector.fluxType = "psfFlux"
138 self.process.calculateActions.stars.fluxType = "psfFlux"
140 self.produce.metric.units = {
141 "{band}_highSNStars_median": "pixel",
142 "{band}_highSNStars_sigmaMad": "pixel",
143 "{band}_highSNStars_count": "count",
144 "{band}_lowSNStars_median": "pixel",
145 "{band}_lowSNStars_sigmaMad": "pixel",
146 "{band}_lowSNStars_count": "count",
147 }
149 self.produce.plot = ScatterPlotWithTwoHists()
151 self.produce.plot.plotTypes = ["stars"]
152 self.produce.plot.xAxisLabel = "PSF Magnitude (mag)"
153 self.produce.plot.magLabel = "PSF Magnitude (mag)"
156class ShapeSizeFractionalDiff(BasePsfResidual):
157 def setDefaults(self):
158 super().setDefaults()
159 self.process.filterActions.yStars = DownselectVector(
160 vectorKey="fracDiff", selector=VectorSelector(vectorKey="starSelector")
161 )
162 self.produce.plot.yAxisLabel = "Fractional size residuals (S/S_PSF - 1)"
165class E1Diff(BasePsfResidual):
166 def setDefaults(self):
167 super().setDefaults()
168 self.process.filterActions.yStars = DownselectVector(
169 vectorKey="e1Diff", selector=VectorSelector(vectorKey="starSelector")
170 )
171 self.produce.plot.yAxisLabel = "Ellipticty residuals (e1 - e1_PSF)"
174class E2Diff(BasePsfResidual):
175 def setDefaults(self):
176 super().setDefaults()
177 self.process.filterActions.yStars = DownselectVector(
178 vectorKey="e2Diff", selector=VectorSelector(vectorKey="starSelector")
179 )
180 self.produce.plot.yAxisLabel = "Ellipticty residuals (e2 - e2_PSF)"
183class RhoStatistics(AnalysisTool):
184 parameterizedBand: bool = True
186 def setDefaults(self):
187 super().setDefaults()
188 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
189 self.prep.selectors.snSelector = SnSelector(fluxType="{band}_psfFlux", threshold=100)
190 self.prep.selectors.starSelector = StarSelector()
192 self.process.calculateActions.rho = CalcRhoStatistics()
193 self.process.calculateActions.rho.treecorr.nbins = 21
194 self.process.calculateActions.rho.treecorr.min_sep = 0.01
195 self.process.calculateActions.rho.treecorr.max_sep = 100.0
196 self.process.calculateActions.rho.treecorr.sep_units = "arcmin"
197 self.process.calculateActions.rho.treecorr.metric = "Arc"
199 self.produce.plot = RhoStatisticsPlot()