LSSTApplications  18.1.0
LSSTDataManagementBasePackage
runEotestTask.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 #
4 # Copyright 2008-2017 AURA/LSST.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
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 LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <https://www.lsstcorp.org/LegalNotices/>.
22 #
23 
24 """Calibration products production task code."""
25 from __future__ import absolute_import, division, print_function
26 
27 import os
28 import glob
29 import sys
30 
31 import lsst.pex.config as pexConfig
32 import lsst.pipe.base as pipeBase
33 import lsst.log as lsstLog
34 import lsst.eotest.sensor as sensorTest
35 
36 
37 class RunEotestConfig(pexConfig.Config):
38  """Config class for the calibration products production (CP) task."""
39 
40  ccdKey = pexConfig.Field(
41  dtype=str,
42  doc="The key by which to pull a detector from a dataId, e.g. 'ccd' or 'detector'",
43  default='ccd',
44  )
45  fe55 = pexConfig.ConfigurableField(
46  target=sensorTest.Fe55Task,
47  doc="The Fe55 analysis task.",
48  )
49  doFe55 = pexConfig.Field(
50  dtype=bool,
51  doc="Measure gains using Fe55?",
52  default=True,
53  )
54  readNoise = pexConfig.ConfigurableField(
55  target=sensorTest.ReadNoiseTask,
56  doc="The read noise task.",
57  )
58  doReadNoise = pexConfig.Field(
59  dtype=bool,
60  doc="Measure the read-noise?",
61  default=True,
62  )
63  brightPixels = pexConfig.ConfigurableField(
64  target=sensorTest.BrightPixelsTask,
65  doc="The bright pixel/column finding task.",
66  )
67  doBrightPixels = pexConfig.Field(
68  dtype=bool,
69  doc="Find bright pixels?",
70  default=True,
71  )
72  darkPixels = pexConfig.ConfigurableField(
73  target=sensorTest.DarkPixelsTask,
74  doc="The dark pixel/column finding task.",
75  )
76  doDarkPixels = pexConfig.Field(
77  dtype=bool,
78  doc="Find dark pixels?",
79  default=True,
80  )
81  traps = pexConfig.ConfigurableField(
82  target=sensorTest.TrapTask,
83  doc="The trap-finding task.",
84  )
85  doTraps = pexConfig.Field(
86  dtype=bool,
87  doc="Find traps using pocket-pumping exposures?",
88  default=True,
89  )
90  cte = pexConfig.ConfigurableField(
91  target=sensorTest.CteTask,
92  doc="The CTE analysis task.",
93  )
94  doCTE = pexConfig.Field(
95  dtype=bool,
96  doc="Measure the charge transfer efficiency?",
97  default=True,
98  )
99  ptc = pexConfig.ConfigurableField(
100  target=sensorTest.PtcTask,
101  doc="The PTC analysis task.",
102  )
103  doPTC = pexConfig.Field(
104  dtype=bool,
105  doc="Measure the photon transfer curve?",
106  default=True,
107  )
108  flatPair = pexConfig.ConfigurableField(
109  target=sensorTest.FlatPairTask,
110  doc="The flat-pair analysis task.",
111  )
112  doFlatPair = pexConfig.Field(
113  dtype=bool,
114  doc="Measure the detector response vs incident flux using flat pairs?",
115  default=True,
116  )
117  eotestOutputPath = pexConfig.Field(
118  dtype=str,
119  doc="Path to which to write the eotest output results. Madatory runtime arg for running eotest.",
120  default='',
121  )
122  requireAllEOTests = pexConfig.Field(
123  dtype=bool,
124  doc="If True, all tests are required to be runnable, and will Raise if data is missing. If False, "
125  "processing will continue if a previous part failed due to the input dataset being incomplete.",
126  default=True,
127  )
128  flatPairMaxPdFracDev = pexConfig.Field(
129  dtype=float,
130  doc="Maximum allowed fractional deviation between photodiode currents for the eotest flatPair task. "
131  "This value is passed to the task's run() method at runtime rather than being stored in the task's"
132  "own pexConfig field.",
133  default=0.05,
134  )
135 
136  def setDefaults(self):
137  """Set default config options for the subTasks."""
138  # TODO: Set to proper values - DM-12939
139  self.fe55.temp_set_point = -100
140  self.fe55.temp_set_point_tol = 20
141 
142  # TODO: Set to proper values - DM-12939
143  self.readNoise.temp_set_point = -100
144  self.readNoise.temp_set_point_tol = 20
145 
146  # TODO: make this work - DM-12939
147  self.brightPixels.temp_set_point = -100
148  self.brightPixels.temp_set_point_tol = 20
149 
150  # TODO: find the proper settings for flatPairTask to work. This will mean either using the expTime,
151  # or working out what's wrong with the MONDIODE values (debug that anyway as a large difference might
152  # indicate something else going on). Note that this task doesn't really use much in its config class,
153  # but passes in things at runtime param to its run() method, hence putting in a slot for them here.
154  # DM-12939
155  self.flatPairMaxPdFracDev = 0.99
156 
157  def validate(self):
158  """Override of the valiate() method.
159 
160  The pexConfigs of the subTasks here cannot be validated in the normal way, as they are the configs
161  for eotest, which does illegal things, and this would require an upstream PR to fix. Therefore, we
162  override the validate() method here, and use it to set the output directory for each of the tasks
163  based on the legal pexConfig parameter for the main task.
164  """
165  log = lsstLog.Log.getLogger("cp.pipe.runEotestConfig")
166  if not self.eotestOutputPath:
167  raise RuntimeError("Must supply an output path for eotest data. "
168  "Please set config.eotestOutputPath.")
169 
170  taskList = ['fe55', 'brightPixels', 'darkPixels', 'readNoise', 'traps', 'cte', 'flatPair', 'ptc']
171  for task in taskList:
172  if getattr(self, task).output_dir != '.':
173  # Being thorough here: '.' is the eotest default. If this is not the value then the user has
174  # specified something, and we're going to clobber it, so raise a warning. Unlike to happen.
175  log.warn("OVERWRITING: Found a user defined output path of %s for %sTask. "
176  "This has been overwritten with %s, as individually specified output paths for "
177  "subTasks are not supported at present" % (getattr(self, task).output_dir,
178  task, self.eotestOutputPath))
179  getattr(self, task).output_dir = self.eotestOutputPath
180 
181 
182 class RunEotestTask(pipeBase.CmdLineTask):
183  """
184  Task to run test stand data through eotest using a butler.
185 
186  This task is used to produce an eotest report (the project's sensor
187  acceptance testing package)
188  Examples of some of its operations are as follows:
189  * Given a set of flat-field images, find the dark pixels and columns.
190  * Given a set of darks, find the bright pixels and columns.
191  * Given a set of Fe55 exposures, calulate the gain of the readout chain,
192  in e-/ADU
193  * Given a set of Fe55 exposures, calulate the instrinsic PSF of the silicon,
194  and the degradation of
195  * the PSF due to CTE.
196  * Given a set of flat-pairs, measure the photon transfer curve (PTC).
197  * Given a set of bias frames, calculate the read noise of the system in e-.
198  * Given a set of pocket-pumping exposures, find charge-traps in the silicon.
199 
200  The RunEotestTask.runEotestDirect() is only applicable to LSST sensors, and
201  only for a specific type of dataset. This method takes a
202  dafPersistance.Butler corresponding to a repository in which a full eotest
203  run has been taken and ingested, and runs each of the tasks in eotest
204  directly, allowing for bitwise comparison with results given by the camera
205  team.
206 
207  See http://ls.st/ldm-151 Chapter 4, Calibration Products Production for
208  further details regarding the inputs and outputs.
209  """
210 
211  ConfigClass = RunEotestConfig
212  _DefaultName = "runEotest"
213 
214  def __init__(self, *args, **kwargs):
215  """Constructor for the RunEotestTask."""
216  if 'lsst.eotest.sensor' not in sys.modules: # check we have eotest before going further
217  raise RuntimeError('eotest failed to import')
218 
219  pipeBase.CmdLineTask.__init__(self, *args, **kwargs)
220 
221  # Note - we can't currently call validate on the subTask configs, as they are NOT valid
222  # due to state of eotest. However, we override validate() and call it here
223  # and use it to set the output dir config parameter in the subTasks.
224  self.config.validate()
225  self.config.freeze()
226 
227  self.makeSubtask("fe55")
228  self.makeSubtask("readNoise")
229  self.makeSubtask("brightPixels")
230  self.makeSubtask("darkPixels")
231  self.makeSubtask("traps")
232  self.makeSubtask("cte")
233  self.makeSubtask("flatPair")
234  self.makeSubtask("ptc")
235 
236  def _getMaskFiles(self, path, ccd):
237  """Get all available eotest mask files for a given ccd.
238 
239  Each stage of the processing generates more mask files, so this allows each to be picked up
240  as more and more tests run, and saves having to have clever logic for if some tasks fail.
241 
242  Parameters
243  ----------
244  path : `str`
245  Path on which to find the mask files
246  ccd : `string` or `int`
247  Name/identifier of the CCD
248 
249  Returns
250  -------
251  maskFiles : iterable of `str`
252  List of mask files, or an empty tuple if none are found
253  """
254  pattern = '*' + str(ccd) + '*mask*' # the cast to str supports obs_auxTel
255  maskFiles = glob.glob(os.path.join(path, pattern))
256  return maskFiles if len(maskFiles) > 0 else () # eotest wants an empty tuple here
257 
258  def _cleanupEotest(self, path):
259  """Delete all the medianed files left behind after eotest has run.
260 
261  Running eotest generates a lot of interim medianed files, so this just cleans them up.
262 
263  Parameters
264  ----------
265  path : `str`
266  Path on which to delete all the eotest medianed files.
267  """
268  for filename in glob.glob(os.path.join(path, '*_median_*.fits')):
269  os.remove(filename)
270 
271  def makeEotestReport(self, butler):
272  """After running eotest, generate pdf(s) of the results.
273 
274  Generate a sensor test report from the output data in config.eotestOutputPath, one for each CCD.
275  The pdf file(s), along with the .tex file(s) and the individual plots are written
276  to the eotestOutputPath.
277  .pdf generation requires a TeX distro including pdflatex to be installed.
278  """
279  ccds = butler.queryMetadata('raw', self.config.ccdKey)
280  for ccd in ccds:
281  self.log.info("Starting test report generation for %s"%ccd)
282  try:
283  plotPath = os.path.join(self.config.eotestOutputPath, 'plots')
284  if not os.path.exists(plotPath):
285  os.makedirs(plotPath)
286  plots = sensorTest.EOTestPlots(ccd, self.config.eotestOutputPath, plotPath)
287  eoTestReport = sensorTest.EOTestReport(plots, wl_dir='')
288  eoTestReport.make_figures()
289  eoTestReport.make_pdf()
290  except Exception as e:
291  self.log.warn("Failed to make eotest report for %s: %s"%(ccd, e))
292  self.log.info("Finished test report generation.")
293 
294  @pipeBase.timeMethod
295  def runEotestDirect(self, butler, run=None):
296  """
297  Generate calibration products using eotest algorithms.
298 
299  Generate all calibration products possible using the vanilla eotest implementation,
300  given a butler for a TS8 (raft-test) repo. It can contain multiple runs, but must correspond to
301  only a single raft/RTM.
302 
303  - Run all eotest tasks possible, using the butler to gather the data
304  - Write outputs in eotest format
305 
306  In order to replicate the canonical eotest analysis, the tasks should be run in a specific order.
307  This is given/defined in the "Steps" section here:
308  http://lsst-camera.slac.stanford.edu/eTraveler/exp/LSST-CAMERA/displayProcess.jsp?processPath=1179
309 
310  But is replicated here for conveniece:
311  * 55Fe Analysis
312  * CCD Read Noise Analysis
313  * Bright Defects Analysis
314  * Dark Defects Analysis
315  * Traps Finding
316  * Dark Current X - will not be implemented here
317  * Charge Transfer Efficiencies
318  * Photo-response analysis X - will not be implemented here
319  * Flat Pairs Analysis
320  * Photon Transfer Curve
321  * Quantum Efficiency X - will not be implemented here
322 
323  List of tasks that exist in the eotest package but aren't mentioned on the above link:
324  * linearityTask()
325  * fe55CteTask()
326  * eperTask()
327  * crosstalkTask()
328  * persistenceTask()
329 
330  # TODO: For each eotest task, find out what the standard raft testing does for the optional params.
331  i.e. many have optional params for gains, bias-frames etc - if we want bitwise identicallity then we
332  need to know what is typically provided to these tasks when the camera team runs this code.
333  This can probably be worked out from https://github.com/lsst-camera-dh/lcatr-harness
334  but it sounds like Jim Chiang doesn't recommend trying to do that.
335  DM-12939
336 
337  Parameters
338  ----------
339  butler : `lsst.daf.persistence.butler`
340  Butler for the repo containg the eotest data to be used
341  run : `str` or `int`
342  Optional run number, to be used for repos containing multiple runs
343  """
344  self.log.info("Running eotest routines direct")
345 
346  # Input testing to check that run is in the repo
347  runs = butler.queryMetadata('raw', ['run'])
348  if run is None:
349  if len(runs) == 1:
350  run = runs[0]
351  else:
352  raise RuntimeError("Butler query found %s for runs. eotest datasets must have a run number,"
353  "and you must specify which run to use if a respoitory contains several."
354  % runs)
355  else:
356  run = str(run)
357  if run not in runs:
358  raise RuntimeError("Butler query found %s for runs, but the run specified (%s) "
359  "was not among them." % (runs, run))
360  del runs # we have run defined now, so remove this to avoid potential confusion later
361 
362  if not os.path.exists(self.config.eotestOutputPath):
363  os.makedirs(self.config.eotestOutputPath)
364 
365  ccds = butler.queryMetadata('raw', self.config.ccdKey)
366  imTypes = butler.queryMetadata('raw', ['imageType'])
367  testTypes = butler.queryMetadata('raw', ['testType'])
368 
369 
372  if self.config.doFe55:
373  fe55TaskDataId = {'run': run, 'testType': 'FE55', 'imageType': 'FE55'}
374  self.log.info("Starting Fe55 pixel task")
375  for ccd in ccds:
376  if 'FE55' not in testTypes:
377  msg = "No Fe55 tests found. Available data: %s" % testTypes
378  if self.config.requireAllEOTests:
379  raise RuntimeError(msg)
380  else:
381  self.log.warn(msg + "\nSkipping Fe55 task")
382  break
383  fe55Filenames = [butler.get('raw_filename', dataId={'visit': visit,
384  self.config.ccdKey: ccd})[0][:-3]
385  for visit in butler.queryMetadata('raw', ['visit'], dataId=fe55TaskDataId)]
386  self.log.trace("Fe55Task: Processing %s with %s files" % (ccd, len(fe55Filenames)))
387  maskFiles = self._getMaskFiles(self.config.eotestOutputPath, ccd)
388  gains = self.fe55.run(sensor_id=ccd, infiles=fe55Filenames, mask_files=maskFiles)
389  # gainsPropSet = dafBase.PropertySet()
390  # for amp, gain in gains.items(): # there is no propSet.fromDict() method so make like this
391  # gainsPropSet.addDouble(str(amp), gain)
392  butler.put(gains, 'eotest_gain', dataId={self.config.ccdKey: ccd, 'run': run})
393  del fe55TaskDataId
394 
395  # TODO: validate the results above, and/or change code to (be able to) always run
396  # over all files instead of stopping at the "required accuracy"
397  # This will require making changes to the eotest code.
398  # DM-12939
399 
400 
403  if self.config.doReadNoise:
404  # note that LCA-10103 defines the Fe55 bias frames as the ones to use here
405  self.log.info("Starting readNoise task")
406  noiseTaskDataId = {'run': run, 'testType': 'FE55', 'imageType': 'BIAS'}
407  for ccd in ccds:
408  if ('FE55' not in testTypes) or ('BIAS' not in imTypes):
409  msg = "Required data for readNoise unavailable. Available data:\
410  \ntestTypes: %s\nimageTypes: %s" % (testTypes, imTypes)
411  if self.config.requireAllEOTests:
412  raise RuntimeError(msg)
413  else:
414  self.log.warn(msg + "\nSkipping noise task")
415  noiseFilenames = [butler.get('raw_filename', dataId={'visit': visit,
416  self.config.ccdKey: ccd})[0][:-3]
417  for visit in butler.queryMetadata('raw', ['visit'],
418  dataId=noiseTaskDataId)]
419  self.log.trace("Fe55Task: Processing %s with %s files" % (ccd, len(noiseFilenames)))
420  maskFiles = self._getMaskFiles(self.config.eotestOutputPath, ccd)
421  gains = butler.get('eotest_gain', dataId={self.config.ccdKey: ccd, 'run': run})
422  self.readNoise.run(sensor_id=ccd, bias_files=noiseFilenames,
423  gains=gains, mask_files=maskFiles)
424  del noiseTaskDataId
425 
426 
429  if self.config.doBrightPixels:
430  self.log.info("Starting bright pixel task")
431  brightTaskDataId = {'run': run, 'testType': 'DARK', 'imageType': 'DARK'}
432  for ccd in ccds:
433  if 'DARK' not in testTypes:
434  msg = "No dark tests found. Available data: %s" % testTypes
435  if self.config.requireAllEOTests:
436  raise RuntimeError(msg)
437  else:
438  self.log.warn(msg + "\nSkipping bright pixel task")
439  break
440  darkFilenames = [butler.get('raw_filename', dataId={'visit': visit,
441  self.config.ccdKey: ccd})[0][:-3]
442  for visit in butler.queryMetadata('raw', ['visit'],
443  dataId=brightTaskDataId)]
444  self.log.trace("BrightTask: Processing %s with %s files" % (ccd, len(darkFilenames)))
445  maskFiles = self._getMaskFiles(self.config.eotestOutputPath, ccd)
446  gains = butler.get('eotest_gain', dataId={self.config.ccdKey: ccd, 'run': run})
447  self.brightPixels.run(sensor_id=ccd, dark_files=darkFilenames,
448  mask_files=maskFiles, gains=gains)
449  del brightTaskDataId
450 
451 
454  if self.config.doDarkPixels:
455  self.log.info("Starting dark pixel task")
456  darkTaskDataId = {'run': run, 'testType': 'SFLAT_500', 'imageType': 'FLAT'}
457  for ccd in ccds:
458  if 'SFLAT_500' not in testTypes:
459  msg = "No superflats found. Available data: %s" % testTypes
460  if self.config.requireAllEOTests:
461  raise RuntimeError(msg)
462  else:
463  self.log.warn(msg + "\nSkipping dark pixel task")
464  break
465  sflatFilenames = [butler.get('raw_filename', dataId={'visit': visit,
466  self.config.ccdKey: ccd})[0][:-3]
467  for visit in butler.queryMetadata('raw', ['visit'],
468  dataId=darkTaskDataId)]
469  self.log.trace("DarkTask: Processing %s with %s files" % (ccd, len(sflatFilenames)))
470  maskFiles = self._getMaskFiles(self.config.eotestOutputPath, ccd)
471  self.darkPixels.run(sensor_id=ccd, sflat_files=sflatFilenames, mask_files=maskFiles)
472  del darkTaskDataId
473 
474 
477  if self.config.doTraps:
478  self.log.info("Starting trap task")
479  trapTaskDataId = {'run': run, 'testType': 'TRAP', 'imageType': 'PPUMP'}
480  for ccd in ccds:
481  if ('TRAP' not in testTypes) and ('PPUMP' not in imTypes):
482  msg = "No pocket pumping exposures found. Available data: %s" % testTypes
483  if self.config.requireAllEOTests:
484  raise RuntimeError(msg)
485  else:
486  self.log.warn(msg + "\nSkipping trap task")
487  break
488  trapFilenames = [butler.get('raw_filename', dataId={'visit': visit,
489  self.config.ccdKey: ccd})[0][:-3]
490  for visit in butler.queryMetadata('raw', ['visit'], dataId=trapTaskDataId)]
491  if len(trapFilenames) != 1: # eotest can't handle more than one
492  msg = "Trap Task: Found more than one ppump trap file: %s" % trapFilenames
493  msg += " Running using only the first one found."
494  self.log.warn(msg)
495  self.log.trace("Trap Task: Processing %s with %s files" % (ccd, len(trapFilenames)))
496  maskFiles = self._getMaskFiles(self.config.eotestOutputPath, ccd)
497  gains = butler.get('eotest_gain', dataId={self.config.ccdKey: ccd, 'run': run})
498  self.traps.run(sensor_id=ccd, pocket_pumped_file=trapFilenames[0],
499  mask_files=maskFiles, gains=gains)
500  del trapTaskDataId
501 
502 
505  if self.config.doCTE:
506  self.log.info("Starting CTE task")
507  cteTaskDataId = {'run': run, 'testType': 'SFLAT_500', 'imageType': 'FLAT'}
508  for ccd in ccds:
509  if 'SFLAT_500' not in testTypes:
510  msg = "No superflats found. Available data: %s" % testTypes
511  if self.config.requireAllEOTests:
512  raise RuntimeError(msg)
513  else:
514  self.log.warn(msg + "\nSkipping CTE task")
515  break
516  sflatFilenames = [butler.get('raw_filename', dataId={'visit': visit,
517  self.config.ccdKey: ccd})[0][:-3]
518  for visit in butler.queryMetadata('raw', ['visit'], dataId=cteTaskDataId)]
519  self.log.trace("CTETask: Processing %s with %s files" % (ccd, len(sflatFilenames)))
520  maskFiles = self._getMaskFiles(self.config.eotestOutputPath, ccd)
521  self.cte.run(sensor_id=ccd, superflat_files=sflatFilenames, mask_files=maskFiles)
522  del cteTaskDataId
523 
524 
527  if self.config.doFlatPair:
528  self.log.info("Starting flatPair task")
529  flatPairDataId = {'run': run, 'testType': 'FLAT', 'imageType': 'FLAT'}
530  for ccd in ccds:
531  if 'FLAT' not in testTypes:
532  msg = "No dataset for flat_pairs found. Available data: %s" % testTypes
533  if self.config.requireAllEOTests:
534  raise RuntimeError(msg)
535  else:
536  self.log.warn(msg + "\nSkipping flatPair task")
537  break
538  flatPairFilenames = [butler.get('raw_filename', dataId={'visit': visit,
539  self.config.ccdKey: ccd})[0][:-3]
540  for visit in butler.queryMetadata('raw', ['visit'],
541  dataId=flatPairDataId)]
542  # Note that eotest needs the original filename as written by the test-stand data acquisition
543  # system, as that is the only place the flat pair-number is recorded, so we have to resolve
544  # sym-links and pass in the *original* paths/filenames here :(
545  # Also, there is no "flat-pair" test type, so all FLAT/FLAT imType/testType will appear here
546  # so we need to filter these for only the pair acquisitions (as the eotest code looks like it
547  # isn't totally thorough on rejecting the wrong types of data here)
548  # TODO: adding a translator to obs_comCam and ingesting this would allow this to be done
549  # by the butler instead of here. DM-12939
550  flatPairFilenames = [os.path.realpath(f) for f in flatPairFilenames if
551  os.path.realpath(f).find('flat1') != -1 or
552  os.path.realpath(f).find('flat2') != -1]
553  if not flatPairFilenames:
554  raise RuntimeError("No flatPair files found.")
555  self.log.trace("FlatPairTask: Processing %s with %s files" % (ccd, len(flatPairFilenames)))
556  maskFiles = self._getMaskFiles(self.config.eotestOutputPath, ccd)
557  gains = butler.get('eotest_gain', dataId={self.config.ccdKey: ccd, 'run': run})
558  self.flatPair.run(sensor_id=ccd, infiles=flatPairFilenames, mask_files=maskFiles,
559  gains=gains, max_pd_frac_dev=self.config.flatPairMaxPdFracDev)
560  del flatPairDataId
561 
562 
565  if self.config.doPTC:
566  self.log.info("Starting PTC task")
567  ptcDataId = {'run': run, 'testType': 'FLAT', 'imageType': 'FLAT'}
568  for ccd in ccds:
569  if 'FLAT' not in testTypes:
570  msg = "No dataset for flat_pairs found. Available data: %s" % testTypes
571  if self.config.requireAllEOTests:
572  raise RuntimeError(msg)
573  else:
574  self.log.warn(msg + "\nSkipping PTC task")
575  break
576  ptcFilenames = [butler.get('raw_filename', dataId={'visit': visit,
577  self.config.ccdKey: ccd})[0][:-3]
578  for visit in butler.queryMetadata('raw', ['visit'], dataId=ptcDataId)]
579  # Note that eotest needs the original filename as written by the test-stand data acquisition
580  # system, as that is the only place the flat pair-number is recorded, so we have to resolve
581  # sym-links and pass in the *original* paths/filenames here :(
582  # Also, there is no "flat-pair" test type, so all FLAT/FLAT imType/testType will appear here
583  # so we need to filter these for only the pair acquisitions (as the eotest code looks like it
584  # isn't totally thorough on rejecting the wrong types of data here)
585  # TODO: adding a translator to obs_comCam and ingesting this would allow this to be done
586  # by the butler instead of here. DM-12939
587  ptcFilenames = [os.path.realpath(f) for f in ptcFilenames if
588  os.path.realpath(f).find('flat1') != -1 or
589  os.path.realpath(f).find('flat2') != -1]
590  if not ptcFilenames:
591  raise RuntimeError("No flatPair files found")
592  self.log.trace("PTCTask: Processing %s with %s files" % (ccd, len(ptcFilenames)))
593  maskFiles = self._getMaskFiles(self.config.eotestOutputPath, ccd)
594  gains = butler.get('eotest_gain', dataId={self.config.ccdKey: ccd, 'run': run})
595  self.ptc.run(sensor_id=ccd, infiles=ptcFilenames, mask_files=maskFiles, gains=gains)
596  del ptcDataId
597 
598  self._cleanupEotest(self.config.eotestOutputPath)
599  self.log.info("Finished running EOTest")
Definition: Log.h:691
def runEotestDirect(self, butler, run=None)