LSST Applications  21.0.0+04719a4bac,21.0.0-1-ga51b5d4+f5e6047307,21.0.0-11-g2b59f77+a9c1acf22d,21.0.0-11-ga42c5b2+86977b0b17,21.0.0-12-gf4ce030+76814010d2,21.0.0-13-g1721dae+760e7a6536,21.0.0-13-g3a573fe+768d78a30a,21.0.0-15-g5a7caf0+f21cbc5713,21.0.0-16-g0fb55c1+b60e2d390c,21.0.0-19-g4cded4ca+71a93a33c0,21.0.0-2-g103fe59+bb20972958,21.0.0-2-g45278ab+04719a4bac,21.0.0-2-g5242d73+3ad5d60fb1,21.0.0-2-g7f82c8f+8babb168e8,21.0.0-2-g8f08a60+06509c8b61,21.0.0-2-g8faa9b5+616205b9df,21.0.0-2-ga326454+8babb168e8,21.0.0-2-gde069b7+5e4aea9c2f,21.0.0-2-gecfae73+1d3a86e577,21.0.0-2-gfc62afb+3ad5d60fb1,21.0.0-25-g1d57be3cd+e73869a214,21.0.0-3-g357aad2+ed88757d29,21.0.0-3-g4a4ce7f+3ad5d60fb1,21.0.0-3-g4be5c26+3ad5d60fb1,21.0.0-3-g65f322c+e0b24896a3,21.0.0-3-g7d9da8d+616205b9df,21.0.0-3-ge02ed75+a9c1acf22d,21.0.0-4-g591bb35+a9c1acf22d,21.0.0-4-g65b4814+b60e2d390c,21.0.0-4-gccdca77+0de219a2bc,21.0.0-4-ge8a399c+6c55c39e83,21.0.0-5-gd00fb1e+05fce91b99,21.0.0-6-gc675373+3ad5d60fb1,21.0.0-64-g1122c245+4fb2b8f86e,21.0.0-7-g04766d7+cd19d05db2,21.0.0-7-gdf92d54+04719a4bac,21.0.0-8-g5674e7b+d1bd76f71f,master-gac4afde19b+a9c1acf22d,w.2021.13
LSST Data Management Base Package
computeExposureSummaryStats.py
Go to the documentation of this file.
1 # This file is part of pipe_tasks.
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/>.
21 import warnings
22 import numpy as np
23 import astropy.units as units
24 from astropy.time import Time
25 from astropy.coordinates import AltAz, SkyCoord, EarthLocation
26 from lsst.daf.base import DateTime
27 
28 import lsst.pipe.base as pipeBase
29 import lsst.pex.config as pexConfig
30 import lsst.afw.math as afwMath
31 import lsst.afw.image as afwImage
32 import lsst.geom
33 
34 
35 __all__ = ("ComputeExposureSummaryStatsTask", "ComputeExposureSummaryStatsConfig")
36 
37 
38 class ComputeExposureSummaryStatsConfig(pexConfig.Config):
39  """Config for ComputeExposureSummaryTask"""
40  sigmaClip = pexConfig.Field(
41  dtype=float,
42  doc="Sigma for outlier rejection for sky noise.",
43  default=3.0,
44  )
45  clipIter = pexConfig.Field(
46  dtype=int,
47  doc="Number of iterations of outlier rejection for sky noise.",
48  default=2,
49  )
50  badMaskPlanes = pexConfig.ListField(
51  dtype=str,
52  doc="Mask planes that, if set, the associated pixel should not be included sky noise calculation.",
53  default=("NO_DATA", "SUSPECT"),
54  )
55 
56 
57 class ComputeExposureSummaryStatsTask(pipeBase.Task):
58  """Task to compute exposure summary statistics.
59 
60  This task computes various quantities suitable for DPDD and other
61  downstream processing at the detector centers, including:
62  - psfSigma
63  - psfArea
64  - psfIxx
65  - psfIyy
66  - psfIxy
67  - ra
68  - decl
69  - zenithDistance
70  - zeroPoint
71  - skyBg
72  - skyNoise
73  - meanVar
74  - raCorners
75  - decCorners
76  """
77  ConfigClass = ComputeExposureSummaryStatsConfig
78  _DefaultName = "computeExposureSummaryStats"
79 
80  @pipeBase.timeMethod
81  def run(self, exposure, sources, background):
82  """Measure exposure statistics from the exposure, sources, and background.
83 
84  Parameters
85  ----------
86  exposure : `lsst.afw.image.ExposureF`
87  sources : `lsst.afw.table.SourceCatalog`
88  background : `lsst.afw.math.BackgroundList`
89 
90  Returns
91  -------
92  summary : `lsst.afw.image.ExposureSummary`
93  """
94  self.log.info("Measuring exposure statistics")
95 
96  bbox = exposure.getBBox()
97 
98  psf = exposure.getPsf()
99  if psf is not None:
100  shape = psf.computeShape(bbox.getCenter())
101  psfSigma = shape.getDeterminantRadius()
102  psfIxx = shape.getIxx()
103  psfIyy = shape.getIyy()
104  psfIxy = shape.getIxy()
105  im = psf.computeKernelImage(bbox.getCenter())
106  # The calculation of effective psf area is taken from
107  # meas_base/src/PsfFlux.cc#L112. See
108  # https://github.com/lsst/meas_base/blob/
109  # 750bffe6620e565bda731add1509507f5c40c8bb/src/PsfFlux.cc#L112
110  psfArea = np.sum(im.array)/np.sum(im.array**2.)
111  else:
112  psfSigma = np.nan
113  psfIxx = np.nan
114  psfIyy = np.nan
115  psfIxy = np.nan
116  psfArea = np.nan
117 
118  wcs = exposure.getWcs()
119  if wcs is not None:
120  sph_pts = wcs.pixelToSky(lsst.geom.Box2D(bbox).getCorners())
121  raCorners = [float(sph.getRa().asDegrees()) for sph in sph_pts]
122  decCorners = [float(sph.getDec().asDegrees()) for sph in sph_pts]
123 
124  sph_pt = wcs.pixelToSky(bbox.getCenter())
125  ra = sph_pt.getRa().asDegrees()
126  decl = sph_pt.getDec().asDegrees()
127  else:
128  raCorners = [float(np.nan)]*4
129  decCorners = [float(np.nan)]*4
130  ra = np.nan
131  decl = np.nan
132 
133  photoCalib = exposure.getPhotoCalib()
134  if photoCalib is not None:
135  zeroPoint = 2.5*np.log10(photoCalib.getInstFluxAtZeroMagnitude())
136  else:
137  zeroPoint = np.nan
138 
139  visitInfo = exposure.getInfo().getVisitInfo()
140  date = visitInfo.getDate()
141 
142  if date.isValid():
143  # We compute the zenithDistance at the center of the detector rather
144  # than use the boresight value available via the visitInfo, because
145  # the zenithDistance may vary significantly over a large field of view.
146  observatory = visitInfo.getObservatory()
147  loc = EarthLocation(lat=observatory.getLatitude().asDegrees()*units.deg,
148  lon=observatory.getLongitude().asDegrees()*units.deg,
149  height=observatory.getElevation()*units.m)
150  obstime = Time(visitInfo.getDate().get(system=DateTime.MJD),
151  location=loc, format='mjd')
152  coord = SkyCoord(ra*units.degree, decl*units.degree, obstime=obstime, location=loc)
153  with warnings.catch_warnings():
154  warnings.simplefilter('ignore')
155  altaz = coord.transform_to(AltAz)
156 
157  zenithDistance = altaz.alt.degree
158  else:
159  zenithDistance = np.nan
160 
161  if background is not None:
162  bgStats = (bg[0].getStatsImage().getImage().array
163  for bg in background)
164  skyBg = sum(np.median(bg[np.isfinite(bg)]) for bg in bgStats)
165  else:
166  skyBg = np.nan
167 
168  statsCtrl = afwMath.StatisticsControl()
169  statsCtrl.setNumSigmaClip(self.config.sigmaClip)
170  statsCtrl.setNumIter(self.config.clipIter)
171  statsCtrl.setAndMask(afwImage.Mask.getPlaneBitMask(self.config.badMaskPlanes))
172  statsCtrl.setNanSafe(True)
173 
174  statObj = afwMath.makeStatistics(exposure.getMaskedImage(), afwMath.STDEVCLIP,
175  statsCtrl)
176  skyNoise, _ = statObj.getResult(afwMath.STDEVCLIP)
177 
178  statObj = afwMath.makeStatistics(exposure.getMaskedImage().getVariance(),
179  exposure.getMaskedImage().getMask(),
180  afwMath.MEANCLIP, statsCtrl)
181  meanVar, _ = statObj.getResult(afwMath.MEANCLIP)
182 
183  summary = afwImage.ExposureSummaryStats(psfSigma=float(psfSigma),
184  psfArea=float(psfArea),
185  psfIxx=float(psfIxx),
186  psfIyy=float(psfIyy),
187  psfIxy=float(psfIxy),
188  ra=float(ra),
189  decl=float(decl),
190  zenithDistance=float(zenithDistance),
191  zeroPoint=float(zeroPoint),
192  skyBg=float(skyBg),
193  skyNoise=float(skyNoise),
194  meanVar=float(meanVar),
195  raCorners=raCorners,
196  decCorners=decCorners)
197 
198  return summary
Pass parameters to a Statistics object.
Definition: Statistics.h:93
A floating-point coordinate rectangle geometry.
Definition: Box.h:413
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Definition: Statistics.h:354