LSST Applications  21.0.0-131-g8cabc107+528f53ee53,22.0.0+00495a2688,22.0.0+0ef2527977,22.0.0+11a2aa21cd,22.0.0+269b7e55e3,22.0.0+2c6b6677a3,22.0.0+64c1bc5aa5,22.0.0+7b3a3f865e,22.0.0+e1b6d2281c,22.0.0+ff3c34362c,22.0.1-1-g1b65d06+c95cbdf3df,22.0.1-1-g7058be7+1cf78af69b,22.0.1-1-g7dab645+2a65e40b06,22.0.1-1-g8760c09+64c1bc5aa5,22.0.1-1-g949febb+64c1bc5aa5,22.0.1-1-ga324b9c+269b7e55e3,22.0.1-1-gf9d8b05+ff3c34362c,22.0.1-10-g781e53d+9b51d1cd24,22.0.1-10-gba590ab+b9624b875d,22.0.1-13-g76f9b8d+2c6b6677a3,22.0.1-14-g22236948+57af756299,22.0.1-18-g3db9cf4b+9b7092c56c,22.0.1-18-gb17765a+2264247a6b,22.0.1-2-g8ef0a89+2c6b6677a3,22.0.1-2-gcb770ba+c99495d3c6,22.0.1-24-g2e899d296+4206820b0d,22.0.1-3-g7aa11f2+2c6b6677a3,22.0.1-3-g8c1d971+f253ffa91f,22.0.1-3-g997b569+ff3b2f8649,22.0.1-4-g1930a60+6871d0c7f6,22.0.1-4-g5b7b756+6b209d634c,22.0.1-6-ga02864e+6871d0c7f6,22.0.1-7-g3402376+a1a2182ac4,22.0.1-7-g65f59fa+54b92689ce,master-gcc5351303a+e1b6d2281c,w.2021.32
LSST Data Management Base Package
measurePtcGen2Task.py
Go to the documentation of this file.
1 # This file is part of cp_pipe.
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 #
22 import numpy as np
23 
24 import lsst.pex.config as pexConfig
25 import lsst.pipe.base as pipeBase
26 from lsst.cp.pipe.utils import arrangeFlatsByExpTime
27 
28 from .photodiode import getBOTphotodiodeData
29 
30 from lsst.pipe.tasks.getRepositoryData import DataRefListRunner
31 from lsst.cp.pipe.ptc.cpExtractPtcTask import PhotonTransferCurveExtractTask
32 from lsst.cp.pipe.ptc.cpSolvePtcTask import PhotonTransferCurveSolveTask
33 
34 
35 __all__ = ['MeasurePhotonTransferCurveTask', 'MeasurePhotonTransferCurveTaskConfig']
36 
37 
38 class MeasurePhotonTransferCurveTaskConfig(pexConfig.Config):
39  extract = pexConfig.ConfigurableField(
40  target=PhotonTransferCurveExtractTask,
41  doc="Task to measure covariances from flats.",
42  )
43  solve = pexConfig.ConfigurableField(
44  target=PhotonTransferCurveSolveTask,
45  doc="Task to fit models to the measured covariances.",
46  )
47  ccdKey = pexConfig.Field(
48  dtype=str,
49  doc="The key by which to pull a detector from a dataId, e.g. 'ccd' or 'detector'.",
50  default='ccd',
51  )
52  doPhotodiode = pexConfig.Field(
53  dtype=bool,
54  doc="Apply a correction based on the photodiode readings if available?",
55  default=False,
56  )
57  photodiodeDataPath = pexConfig.Field(
58  dtype=str,
59  doc="Gen2 only: path to locate the data photodiode data files.",
60  default=""
61  )
62 
63 
64 class MeasurePhotonTransferCurveTask(pipeBase.CmdLineTask):
65  """A class to calculate, fit, and plot a PTC from a set of flat pairs.
66  The Photon Transfer Curve (var(signal) vs mean(signal)) is a standard
67  tool used in astronomical detectors characterization (e.g., Janesick 2001,
68  Janesick 2007). If ptcFitType is "EXPAPPROXIMATION" or "POLYNOMIAL",
69  this task calculates the PTC from a series of pairs of flat-field images;
70  each pair taken at identical exposure times. The difference image of each
71  pair is formed to eliminate fixed pattern noise, and then the variance
72  of the difference image and the mean of the average image
73  are used to produce the PTC. An n-degree polynomial or the approximation
74  in Equation 16 of Astier+19 ("The Shape of the Photon Transfer Curve
75  of CCD sensors", arXiv:1905.08677) can be fitted to the PTC curve. These
76  models include parameters such as the gain (e/DN) and readout noise.
77  Linearizers to correct for signal-chain non-linearity are also calculated.
78  The `Linearizer` class, in general, can support per-amp linearizers, but
79  in this task this is not supported.
80  If ptcFitType is "FULLCOVARIANCE", the covariances of the difference
81  images are calculated via the DFT methods described in Astier+19 and the
82  variances for the PTC are given by the cov[0,0] elements at each signal
83  level. The full model in Equation 20 of Astier+19 is fit to the PTC
84  to get the gain and the noise.
85 
86  Parameters
87  ----------
88  *args: `list`
89  Positional arguments passed to the Task constructor. None used at this
90  time.
91  **kwargs: `dict`
92  Keyword arguments passed on to the Task constructor. None used at this
93  time.
94  """
95 
96  RunnerClass = DataRefListRunner
97  ConfigClass = MeasurePhotonTransferCurveTaskConfig
98  _DefaultName = "measurePhotonTransferCurve"
99 
100  def __init__(self, *args, **kwargs):
101  super().__init__(**kwargs)
102  self.makeSubtask("extract")
103  self.makeSubtask("solve")
104 
105  @pipeBase.timeMethod
106  def runDataRef(self, dataRefList):
107  """Run the Photon Transfer Curve (PTC) measurement task.
108  For a dataRef (which is each detector here),
109  and given a list of exposure pairs (postISR) at different exposure times,
110  measure the PTC.
111 
112  Parameters
113  ----------
114  dataRefList : `list` [`lsst.daf.peristence.ButlerDataRef`]
115  Data references for exposures.
116  """
117  if len(dataRefList) < 2:
118  raise RuntimeError("Insufficient inputs to combine.")
119 
120  # setup necessary objects
121  dataRef = dataRefList[0]
122  camera = dataRef.get('camera')
123 
124  if len(set([dataRef.dataId[self.config.ccdKey] for dataRef in dataRefList])) > 1:
125  raise RuntimeError("Too many detectors supplied")
126  # Get exposure list.
127  expList = []
128  for dataRef in dataRefList:
129  try:
130  tempFlat = dataRef.get("postISRCCD")
131  except RuntimeError:
132  self.log.warn("postISR exposure could not be retrieved. Ignoring flat.")
133  continue
134  expList.append(tempFlat)
135  expIds = [exp.getInfo().getVisitInfo().getExposureId() for exp in expList]
136 
137  # Create dictionary of exposures, keyed by exposure time
138  expDict = arrangeFlatsByExpTime(expList)
139  # Call the "extract" (measure flat covariances) and "solve" (fit covariances) subtasks
140  resultsExtract = self.extract.run(inputExp=expDict, inputDims=expIds)
141  resultsSolve = self.solve.run(resultsExtract.outputCovariances, camera=camera)
142 
143  # Fill up the photodiode data, if found, that will be used by linearity task.
144  # Get expIdPairs from one of the amps
145  expIdsPairsList = []
146  ampNames = resultsSolve.outputPtcDataset.ampNames
147  for ampName in ampNames:
148  tempAmpName = ampName
149  if ampName not in resultsSolve.outputPtcDataset.badAmps:
150  break
151  for pair in resultsSolve.outputPtcDataset.inputExpIdPairs[tempAmpName]:
152  first, second = pair[0]
153  expIdsPairsList.append((first, second))
154 
155  resultsSolve.outputPtcDataset = self._setBOTPhotocharge_setBOTPhotocharge(dataRef, resultsSolve.outputPtcDataset,
156  expIdsPairsList)
157  self.log.info("Writing PTC data.")
158  dataRef.put(resultsSolve.outputPtcDataset, datasetType="photonTransferCurveDataset")
159 
160  return
161 
162  def _setBOTPhotocharge(self, dataRef, datasetPtc, expIdList):
163  """Set photoCharge attribute in PTC dataset
164 
165  Parameters
166  ----------
167  dataRef : `lsst.daf.peristence.ButlerDataRef`
168  Data reference for exposurre for detector to process.
169 
170  datasetPtc : `lsst.ip.isr.ptcDataset.PhotonTransferCurveDataset`
171  The dataset containing information such as the means, variances
172  and exposure times.
173 
174  expIdList : `list`
175  List with exposure pairs Ids (one pair per list entry).
176 
177  Returns
178  -------
179  datasetPtc: `lsst.ip.isr.ptcDataset.PhotonTransferCurveDataset`
180  This is the same dataset as the input parameter, however,
181  it has been modified to update the datasetPtc.photoCharge
182  attribute.
183  """
184  if self.config.doPhotodiode:
185  for (expId1, expId2) in expIdList:
186  charges = [-1, -1] # necessary to have a not-found value to keep lists in step
187  for i, expId in enumerate([expId1, expId2]):
188  # //1000 is a Gen2 only hack, working around the fact an
189  # exposure's ID is not the same as the expId in the
190  # registry. Currently expId is concatenated with the
191  # zero-padded detector ID. This will all go away in Gen3.
192  dataRef.dataId['expId'] = expId//1000
193  if self.config.photodiodeDataPath:
194  photodiodeData = getBOTphotodiodeData(dataRef, self.config.photodiodeDataPath)
195  else:
196  photodiodeData = getBOTphotodiodeData(dataRef)
197  if photodiodeData: # default path stored in function def to keep task clean
198  charges[i] = photodiodeData.getCharge()
199  else:
200  # full expId (not //1000) here, as that encodes the
201  # the detector number as so is fully qualifying
202  self.log.warn(f"No photodiode data found for {expId}")
203 
204  for ampName in datasetPtc.ampNames:
205  datasetPtc.photoCharge[ampName].append((charges[0], charges[1]))
206  else:
207  # Can't be an empty list, as initialized, because astropy.Table won't allow it
208  # when saving as fits
209  for ampName in datasetPtc.ampNames:
210  datasetPtc.photoCharge[ampName] = np.repeat(np.nan, len(expIdList))
211 
212  return datasetPtc
daf::base::PropertySet * set
Definition: fits.cc:912
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33
def getBOTphotodiodeData(dataRef, dataPath='/project/shared/BOT/_parent/raw/photodiode_data/', logger=None)
Definition: photodiode.py:31
def arrangeFlatsByExpTime(exposureList, exposureIdList)
Definition: utils.py:594
def run(self, skyInfo, tempExpRefList, imageScalerList, weightList, altMaskList=None, mask=None, supplementaryData=None)