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