Coverage for python/lsst/summit/utils/quickLook.py: 19%
102 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-16 05:26 -0700
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-16 05:26 -0700
1# This file is part of summit_utils.
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/>.
22import dataclasses
23import os
24from typing import Any
26import numpy as np
28import lsst.afw.cameraGeom as camGeom
29import lsst.afw.image as afwImage
30import lsst.ip.isr as ipIsr
31import lsst.pex.config as pexConfig
32import lsst.pipe.base as pipeBase
33import lsst.pipe.base.connectionTypes as cT
34from lsst.ip.isr import IsrTask
35from lsst.ip.isr.isrTask import IsrTaskConnections
36from lsst.meas.algorithms.installGaussianPsf import InstallGaussianPsfTask
37from lsst.pipe.tasks.characterizeImage import CharacterizeImageTask
38from lsst.utils import getPackageDir
40__all__ = ["QuickLookIsrTask", "QuickLookIsrTaskConfig"]
43class QuickLookIsrTaskConnections(IsrTaskConnections):
44 """Copy isrTask's connections, changing prereq min values to zero.
46 Copy all the connections directly for IsrTask, keeping ccdExposure as
47 required as non-zero, but changing all the other PrerequisiteInputs'
48 minimum values to zero.
49 """
51 def __init__(self, *, config: Any = None):
52 # programatically clone all of the connections from isrTask
53 # setting minimum values to zero for everything except the ccdExposure
54 super().__init__(config=IsrTask.ConfigClass()) # need a dummy config, isn't used other than for ctor
55 for name, connection in self.allConnections.items():
56 if hasattr(connection, "minimum"):
57 setattr(
58 self,
59 name,
60 dataclasses.replace(connection, minimum=(0 if name != "ccdExposure" else 1)),
61 )
63 exposure = cT.Output( # called just "exposure" to mimic isrTask's return struct
64 name="quickLookExp",
65 doc="The quickLook output exposure.",
66 storageClass="ExposureF",
67 dimensions=("instrument", "exposure", "detector"),
68 )
69 # set like this to make it explicit that the outputExposure
70 # and the exposure are identical. The only reason there are two is for
71 # API compatibility.
72 self.outputExposure = exposure
75class QuickLookIsrTaskConfig(pipeBase.PipelineTaskConfig, pipelineConnections=QuickLookIsrTaskConnections):
76 """Configuration parameters for QuickLookIsrTask."""
78 doRepairCosmics = pexConfig.Field(dtype=bool, doc="Interpolate over cosmic rays?", default=True)
81class QuickLookIsrTask(pipeBase.PipelineTask):
82 """Task automatically performing as much isr as possible. Should never fail
84 Automatically performs as much isr as is possible, depending on the
85 calibration products available. All calibration products that can be found
86 are applied, and if none are found, the image is assembled, the overscan is
87 subtracted and the assembled image is returned. Optionally, cosmic rays are
88 interpolated over.
89 """
91 ConfigClass = QuickLookIsrTaskConfig
92 _DefaultName = "quickLook"
94 def __init__(self, isrTask: IsrTask = IsrTask, **kwargs: Any):
95 super().__init__(**kwargs)
96 # Pass in IsrTask so that we can modify it slightly for unit tests.
97 # Note that this is not an instance of the IsrTask class, but the class
98 # itself, which is then instantiated later on, in the run() method,
99 # with the dynamically generated config.
100 self.isrTask = IsrTask
102 def run(
103 self,
104 ccdExposure: afwImage.Exposure,
105 *,
106 camera: camGeom.Camera | None = None,
107 bias: afwImage.Exposure | None = None,
108 dark: afwImage.Exposure | None = None,
109 flat: afwImage.Exposure | None = None,
110 fringes: afwImage.Exposure | None = None,
111 defects: ipIsr.Defects | None = None,
112 linearizer: ipIsr.linearize.LinearizeBase | None = None,
113 crosstalk: ipIsr.crosstalk.CrosstalkCalib | None = None,
114 bfKernel: ipIsr.BrighterFatterKernel | None = None,
115 newBFKernel: np.ndarray | None = None,
116 ptc: ipIsr.PhotonTransferCurveDataset | None = None,
117 crosstalkSources: list | None = None,
118 isrBaseConfig: ipIsr.IsrTaskConfig | None = None,
119 filterTransmission: afwImage.TransmissionCurve | None = None,
120 opticsTransmission: afwImage.TransmissionCurve | None = None,
121 strayLightData: Any | None = None,
122 sensorTransmission: afwImage.TransmissionCurve | None = None,
123 atmosphereTransmission: afwImage.TransmissionCurve | None = None,
124 deferredChargeCalib: Any | None = None,
125 illumMaskedImage: afwImage.MaskedImage | None = None,
126 ) -> pipeBase.Struct:
127 """Run isr and cosmic ray repair using, doing as much isr as possible.
129 Retrieves as many calibration products as are available, and runs isr
130 with those settings enabled, but always returns an assembled image at
131 a minimum. Then performs cosmic ray repair if configured to.
133 Parameters
134 ----------
135 ccdExposure : `lsst.afw.image.Exposure`
136 The raw exposure that is to be run through ISR. The
137 exposure is modified by this method.
138 camera : `lsst.afw.cameraGeom.Camera`, optional
139 The camera geometry for this exposure. Required if
140 one or more of ``ccdExposure``, ``bias``, ``dark``, or
141 ``flat`` does not have an associated detector.
142 bias : `lsst.afw.image.Exposure`, optional
143 Bias calibration frame.
144 dark : `lsst.afw.image.Exposure`, optional
145 Dark calibration frame.
146 flat : `lsst.afw.image.Exposure`, optional
147 Flat calibration frame.
148 fringes : `lsst.afw.image.Exposure`, optional
149 The fringe correction data.
150 This input is slightly different than the `fringes` keyword to
151 `lsst.ip.isr.IsrTask`, since the processing done in that task's
152 `runQuantum` method is instead done here.
153 defects : `lsst.ip.isr.Defects`, optional
154 List of defects.
155 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
156 Functor for linearization.
157 crosstalk : `lsst.ip.isr.crosstalk.CrosstalkCalib`, optional
158 Calibration for crosstalk.
159 bfKernel : `ipIsr.BrighterFatterKernel`, optional
160 Brighter-fatter kernel.
161 newBFKernel : `numpy.ndarray`, optional
162 New Brighter-fatter kernel.
163 ptc : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
164 Photon transfer curve dataset, with, e.g., gains
165 and read noise.
166 crosstalkSources : `list`, optional
167 List of possible crosstalk sources.
168 isrBaseConfig : `lsst.ip.isr.IsrTaskConfig`, optional
169 An isrTask config to act as the base configuration. Options which
170 involve applying a calibration product are ignored, but this allows
171 for the configuration of e.g. the number of overscan columns.
172 filterTransmission : `lsst.afw.image.TransmissionCurve`
173 A ``TransmissionCurve`` that represents the throughput of the
174 filter itself, to be evaluated in focal-plane coordinates.
175 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional
176 A ``TransmissionCurve`` that represents the throughput of the,
177 optics, to be evaluated in focal-plane coordinates.
178 strayLightData : `object`, optional
179 Opaque object containing calibration information for stray-light
180 correction. If `None`, no correction will be performed.
181 sensorTransmission : `lsst.afw.image.TransmissionCurve`
182 A ``TransmissionCurve`` that represents the throughput of the
183 sensor itself, to be evaluated in post-assembly trimmed detector
184 coordinates.
185 atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
186 A ``TransmissionCurve`` that represents the throughput of the
187 atmosphere, assumed to be spatially constant.
188 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional
189 Illumination correction image.
190 bfGains : `dict` of `float`, optional
191 Gains used to override the detector's nominal gains for the
192 brighter-fatter correction. A dict keyed by amplifier name for
193 the detector in question.
195 Returns
196 -------
197 result : `lsst.pipe.base.Struct`
198 Result struct with component:
199 - ``exposure`` : `afw.image.Exposure`
200 The ISRed and cosmic-ray-repaired exposure.
201 """
202 if not isrBaseConfig:
203 isrConfig = IsrTask.ConfigClass()
204 packageDir = getPackageDir("summit_utils")
205 isrConfig.load(os.path.join(packageDir, "config", "quickLookIsr.py"))
206 else:
207 isrConfig = isrBaseConfig
209 isrConfig.doBias = False
210 isrConfig.doDark = False
211 isrConfig.doFlat = False
212 isrConfig.doFringe = False
213 isrConfig.doDefect = False
214 isrConfig.doLinearize = False
215 isrConfig.doCrosstalk = False
216 isrConfig.doBrighterFatter = False
217 isrConfig.usePtcGains = False
219 if bias:
220 isrConfig.doBias = True
221 self.log.info("Running with bias correction")
223 if dark:
224 isrConfig.doDark = True
225 self.log.info("Running with dark correction")
227 if flat:
228 isrConfig.doFlat = True
229 self.log.info("Running with flat correction")
231 if fringes:
232 isrConfig.doFringe = True
233 self.log.info("Running with fringe correction")
235 if defects:
236 isrConfig.doDefect = True
237 self.log.info("Running with defect correction")
239 if linearizer:
240 isrConfig.doLinearize = True
241 self.log.info("Running with linearity correction")
243 if crosstalk:
244 isrConfig.doCrosstalk = True
245 self.log.info("Running with crosstalk correction")
247 if newBFKernel is not None:
248 bfGains = newBFKernel.gain
249 isrConfig.doBrighterFatter = True
250 self.log.info("Running with new brighter-fatter correction")
251 else:
252 bfGains = None
254 if bfKernel is not None and bfGains is None:
255 isrConfig.doBrighterFatter = True
256 self.log.info("Running with brighter-fatter correction")
258 if ptc:
259 isrConfig.usePtcGains = True
260 self.log.info("Running with ptc correction")
262 isrConfig.doWrite = False
263 isrTask = self.isrTask(config=isrConfig)
265 if fringes:
266 # Must be run after isrTask is instantiated.
267 isrTask.fringe.loadFringes(
268 fringes,
269 expId=ccdExposure.info.id,
270 assembler=isrTask.assembleCcd if isrConfig.doAssembleIsrExposures else None,
271 )
273 result = isrTask.run(
274 ccdExposure,
275 camera=camera,
276 bias=bias,
277 dark=dark,
278 flat=flat,
279 fringes=fringes,
280 defects=defects,
281 linearizer=linearizer,
282 crosstalk=crosstalk,
283 bfKernel=bfKernel,
284 bfGains=bfGains,
285 ptc=ptc,
286 crosstalkSources=crosstalkSources,
287 filterTransmission=filterTransmission,
288 opticsTransmission=opticsTransmission,
289 sensorTransmission=sensorTransmission,
290 atmosphereTransmission=atmosphereTransmission,
291 strayLightData=strayLightData,
292 deferredChargeCalib=deferredChargeCalib,
293 illumMaskedImage=illumMaskedImage,
294 )
296 postIsr = result.exposure
298 if self.config.doRepairCosmics:
299 try: # can fail due to too many CRs detected, and we always want an exposure back
300 self.log.info("Repairing cosmics...")
301 if postIsr.getPsf() is None:
302 installPsfTask = InstallGaussianPsfTask()
303 installPsfTask.run(postIsr)
305 # TODO: try adding a reasonably wide Gaussian as a temp PSF
306 # and then just running repairTask on its own without any
307 # imChar. It should work, and be faster.
308 repairConfig = CharacterizeImageTask.ConfigClass()
309 repairConfig.doMeasurePsf = False
310 repairConfig.doApCorr = False
311 repairConfig.doDeblend = False
312 repairConfig.doWrite = False
313 repairConfig.repair.cosmicray.nCrPixelMax = 200000
314 repairTask = CharacterizeImageTask(config=repairConfig)
316 repairTask.repair.run(postIsr)
317 except Exception as e:
318 self.log.warning(f"During CR repair caught: {e}")
320 # exposure is returned for convenience to mimic isrTask's API.
321 return pipeBase.Struct(exposure=postIsr, outputExposure=postIsr)