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