Coverage for python/lsst/faro/measurement/TractMeasurementTasks.py: 41%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of faro.
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 lsst.afw.table import SourceCatalog
23from lsst.pex.config import Config, Field
24from lsst.pipe.base import Struct, Task
25from lsst.verify import Measurement, Datum
27from lsst.faro.utils.stellar_locus import stellarLocusResid, calcQuartileClippedStats
28from lsst.faro.utils.matcher import makeMatchedPhotom
29from lsst.faro.utils.extinction_corr import extinction_corr
30from lsst.faro.utils.tex import calculateTEx
31from lsst.faro.utils.calibrated_catalog import CalibratedCatalog
33import astropy.units as u
34import numpy as np
35from typing import Dict, List
37__all__ = ("WPerpConfig", "WPerpTask", "TExConfig", "TExTask")
40class WPerpConfig(Config):
41 # These are cuts to apply to the r-band only:
42 bright_rmag_cut = Field(
43 doc="Bright limit of catalog entries to include", dtype=float, default=17.0
44 )
45 faint_rmag_cut = Field(
46 doc="Faint limit of catalog entries to include", dtype=float, default=23.0
47 )
50class WPerpTask(Task):
51 ConfigClass = WPerpConfig
52 _DefaultName = "WPerpTask"
54 def run(
55 self, metricName: str, data: Dict[str, List[CalibratedCatalog]],
56 ):
57 self.log.info("Measuring %s", metricName)
58 bands = set("gri")
60 if bands.issubset(set(data.keys())):
61 data = {b: data[b] for b in bands}
62 rgicatAll = makeMatchedPhotom(data)
63 magcut = (rgicatAll["base_PsfFlux_mag_r"] < self.config.faint_rmag_cut) & (
64 rgicatAll["base_PsfFlux_mag_r"] > self.config.bright_rmag_cut
65 )
66 self.log.info("Merged multiband catalog is %d rows (%d match mag cut)",
67 len(rgicatAll), np.sum(magcut))
68 rgicat = rgicatAll[magcut]
69 extVals = extinction_corr(rgicat, bands)
71 wPerp = self.calcWPerp(metricName, rgicat, extVals)
72 return wPerp
73 else:
74 return Struct(measurement=Measurement(metricName, np.nan * u.mmag))
76 def calcWPerp(self, metricName: str, phot: SourceCatalog, extinctionVals):
77 p1, p2, p1coeffs, p2coeffs = stellarLocusResid(
78 *[phot[f"base_PsfFlux_mag_{b}"] - extinctionVals[f"A_{b}"] for b in 'gri'],
79 )
81 if np.size(p2) > 2:
82 p2_rms = calcQuartileClippedStats(p2).rms * u.mag
83 extras = {
84 "p1_coeffs": Datum(
85 p1coeffs * u.Unit(""),
86 label="p1_coefficients",
87 description="p1 coeffs from wPerp fit",
88 ),
89 "p2_coeffs": Datum(
90 p2coeffs * u.Unit(""),
91 label="p2_coefficients",
92 description="p2_coeffs from wPerp fit",
93 ),
94 }
96 return Struct(
97 measurement=Measurement(metricName, p2_rms.to(u.mmag), extras=extras)
98 )
99 else:
100 return Struct(measurement=Measurement(metricName, np.nan * u.mmag))
103class TExConfig(Config):
104 minSep = Field(
105 doc="Inner radius of the annulus in arcmin", dtype=float, default=0.25
106 )
107 maxSep = Field(
108 doc="Outer radius of the annulus in arcmin", dtype=float, default=1.0
109 )
110 nbins = Field(doc="Number of log-spaced angular bins", dtype=int, default=10)
111 rhoStat = Field(doc="Rho statistic to be computed", dtype=int, default=1)
112 shearConvention = Field(
113 doc="Use shear ellipticity convention rather than distortion",
114 dtype=bool,
115 default=False,
116 )
117 columnPsf = Field(
118 doc="Column to use for PSF model shape moments",
119 dtype=str,
120 default="base_SdssShape_psf",
121 )
122 column = Field(
123 doc="Column to use for shape moments", dtype=str, default="base_SdssShape"
124 )
125 # Eventually want to add option to use only PSF reserve stars
128class TExTask(Task):
129 ConfigClass = TExConfig
130 _DefaultName = "TExTask"
132 def run(
133 self, metricName, data: Dict[str, List[CalibratedCatalog]]
134 ):
135 bands = data.keys()
136 if len(bands) != 1:
137 raise RuntimeError(f'TEx task got bands: {bands} but expecting exactly one')
138 else:
139 data = data[list(bands)[0]]
141 self.log.info("Measuring %s", metricName)
143 result = calculateTEx(data, self.config)
144 if "corr" not in result.keys():
145 return Struct(measurement=Measurement(metricName, np.nan * u.Unit("")))
147 writeExtras = True
148 if writeExtras:
149 extras = {}
150 extras["radius"] = Datum(
151 result["radius"], label="radius", description="Separation (arcmin)."
152 )
153 extras["corr"] = Datum(
154 result["corr"], label="Correlation", description="Correlation."
155 )
156 extras["corrErr"] = Datum(
157 result["corrErr"],
158 label="Correlation Uncertianty",
159 description="Correlation Uncertainty.",
160 )
161 else:
162 extras = None
163 return Struct(
164 measurement=Measurement(
165 metricName, np.mean(np.abs(result["corr"])), extras=extras
166 )
167 )