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
Public Member Functions | Static Public Member Functions | Static Public Attributes | List of all members
lsst.cp.pipe.measureCrosstalk.CrosstalkSolveTask Class Reference
Inheritance diagram for lsst.cp.pipe.measureCrosstalk.CrosstalkSolveTask:

Public Member Functions

def runQuantum (self, butlerQC, inputRefs, outputRefs)
 
def run (self, inputRatios, inputFluxes=None, camera=None, inputDims=None, outputDims=None)
 
def measureCrosstalkCoefficients (self, ratios, rejIter, rejSigma)
 
def debugRatios (self, stepname, ratios, i, j, coeff=0.0, valid=False)
 

Static Public Member Functions

def filterCrosstalkCalib (inCalib)
 

Static Public Attributes

 ConfigClass = CrosstalkSolveConfig
 

Detailed Description

Task to solve crosstalk from pixel ratios.

Definition at line 387 of file measureCrosstalk.py.

Member Function Documentation

◆ debugRatios()

def lsst.cp.pipe.measureCrosstalk.CrosstalkSolveTask.debugRatios (   self,
  stepname,
  ratios,
  i,
  j,
  coeff = 0.0,
  valid = False 
)
Utility function to examine the final CT ratio set.

Parameters
----------
stepname : `str`
    State of processing to view.
ratios : `dict` of `dict` of `np.ndarray`
    Array of measured CT ratios, indexed by source/victim
    amplifier.
i : `str`
    Index of the source amplifier.
j : `str`
    Index of the target amplifier.
coeff : `float`, optional
    Coefficient calculated to plot along with the simple mean.
valid : `bool`, optional
    Validity to be added to the plot title.

Definition at line 657 of file measureCrosstalk.py.

657  def debugRatios(self, stepname, ratios, i, j, coeff=0.0, valid=False):
658  """Utility function to examine the final CT ratio set.
659 
660  Parameters
661  ----------
662  stepname : `str`
663  State of processing to view.
664  ratios : `dict` of `dict` of `np.ndarray`
665  Array of measured CT ratios, indexed by source/victim
666  amplifier.
667  i : `str`
668  Index of the source amplifier.
669  j : `str`
670  Index of the target amplifier.
671  coeff : `float`, optional
672  Coefficient calculated to plot along with the simple mean.
673  valid : `bool`, optional
674  Validity to be added to the plot title.
675  """
676  frame = getDebugFrame(self._display, stepname)
677  if frame:
678  if i == j or ratios is None or len(ratios) < 1:
679  pass
680 
681  ratioList = ratios[i][j]
682  if ratioList is None or len(ratioList) < 1:
683  pass
684 
685  mean = np.mean(ratioList)
686  std = np.std(ratioList)
687  import matplotlib.pyplot as plt
688  figure = plt.figure(1)
689  figure.clear()
690  plt.hist(x=ratioList, bins=len(ratioList),
691  cumulative=True, color='b', density=True, histtype='step')
692  plt.xlabel("Measured pixel ratio")
693  plt.ylabel(f"CDF: n={len(ratioList)}")
694  plt.xlim(np.percentile(ratioList, [1.0, 99]))
695  plt.axvline(x=mean, color="k")
696  plt.axvline(x=coeff, color='g')
697  plt.axvline(x=(std / np.sqrt(len(ratioList))), color='r')
698  plt.axvline(x=-(std / np.sqrt(len(ratioList))), color='r')
699  plt.title(f"(Source {i} -> Target {j}) mean: {mean:.2g} coeff: {coeff:.2g} valid: {valid}")
700  figure.show()
701 
702  prompt = "Press Enter to continue: "
703  while True:
704  ans = input(prompt).lower()
705  if ans in ("", "c",):
706  break
707  elif ans in ("pdb", "p",):
708  import pdb
709  pdb.set_trace()
710  plt.close()
711 
712 
def getDebugFrame(debugDisplay, name)
Definition: lsstDebug.py:95

◆ filterCrosstalkCalib()

def lsst.cp.pipe.measureCrosstalk.CrosstalkSolveTask.filterCrosstalkCalib (   inCalib)
static
Apply valid constraints to the measured values.

Any measured coefficient that is determined to be invalid is
set to zero, and has the error set to nan.  The validation is
determined by checking that the measured coefficient is larger
than the calculated standard error of the mean.

Parameters
----------
inCalib : `lsst.ip.isr.CrosstalkCalib`
    Input calibration to filter.

Returns
-------
outCalib : `lsst.ip.isr.CrosstalkCalib`
     Filtered calibration.

Definition at line 625 of file measureCrosstalk.py.

625  def filterCrosstalkCalib(inCalib):
626  """Apply valid constraints to the measured values.
627 
628  Any measured coefficient that is determined to be invalid is
629  set to zero, and has the error set to nan. The validation is
630  determined by checking that the measured coefficient is larger
631  than the calculated standard error of the mean.
632 
633  Parameters
634  ----------
635  inCalib : `lsst.ip.isr.CrosstalkCalib`
636  Input calibration to filter.
637 
638  Returns
639  -------
640  outCalib : `lsst.ip.isr.CrosstalkCalib`
641  Filtered calibration.
642  """
643  outCalib = CrosstalkCalib()
644  outCalib.numAmps = inCalib.numAmps
645 
646  outCalib.coeffs = inCalib.coeffs
647  outCalib.coeffs[~inCalib.coeffValid] = 0.0
648 
649  outCalib.coeffErr = inCalib.coeffErr
650  outCalib.coeffErr[~inCalib.coeffValid] = np.nan
651 
652  outCalib.coeffNum = inCalib.coeffNum
653  outCalib.coeffValid = inCalib.coeffValid
654 
655  return outCalib
656 

◆ measureCrosstalkCoefficients()

def lsst.cp.pipe.measureCrosstalk.CrosstalkSolveTask.measureCrosstalkCoefficients (   self,
  ratios,
  rejIter,
  rejSigma 
)
Measure crosstalk coefficients from the ratios.

Given a list of ratios for each target/source amp combination,
we measure a sigma clipped mean and error.

The coefficient errors returned are the standard deviation of
the final set of clipped input ratios.

Parameters
----------
ratios : `dict` of `dict` of `numpy.ndarray`
   Catalog of arrays of ratios.
rejIter : `int`
   Number of rejection iterations.
rejSigma : `float`
   Rejection threshold (sigma).

Returns
-------
calib : `lsst.ip.isr.CrosstalkCalib`
    The output crosstalk calibration.

Notes
-----
The lsstDebug.Info() method can be rewritten for __name__ =
`lsst.ip.isr.measureCrosstalk`, and supports the parameters:

debug.display['measure'] : `bool`
    Display the CDF of the combined ratio measurements for
    a pair of source/target amplifiers from the final set of
    clipped input ratios.

Definition at line 548 of file measureCrosstalk.py.

548  def measureCrosstalkCoefficients(self, ratios, rejIter, rejSigma):
549  """Measure crosstalk coefficients from the ratios.
550 
551  Given a list of ratios for each target/source amp combination,
552  we measure a sigma clipped mean and error.
553 
554  The coefficient errors returned are the standard deviation of
555  the final set of clipped input ratios.
556 
557  Parameters
558  ----------
559  ratios : `dict` of `dict` of `numpy.ndarray`
560  Catalog of arrays of ratios.
561  rejIter : `int`
562  Number of rejection iterations.
563  rejSigma : `float`
564  Rejection threshold (sigma).
565 
566  Returns
567  -------
568  calib : `lsst.ip.isr.CrosstalkCalib`
569  The output crosstalk calibration.
570 
571  Notes
572  -----
573  The lsstDebug.Info() method can be rewritten for __name__ =
574  `lsst.ip.isr.measureCrosstalk`, and supports the parameters:
575 
576  debug.display['measure'] : `bool`
577  Display the CDF of the combined ratio measurements for
578  a pair of source/target amplifiers from the final set of
579  clipped input ratios.
580  """
581  calib = CrosstalkCalib(nAmp=len(ratios))
582 
583  # Calibration stores coefficients as a numpy ndarray.
584  ordering = list(ratios.keys())
585  for ii, jj in itertools.product(range(calib.nAmp), range(calib.nAmp)):
586  if ii == jj:
587  values = [0.0]
588  else:
589  values = np.array(ratios[ordering[ii]][ordering[jj]])
590  values = values[np.abs(values) < 1.0] # Discard unreasonable values
591 
592  calib.coeffNum[ii][jj] = len(values)
593 
594  if len(values) == 0:
595  self.log.warn("No values for matrix element %d,%d" % (ii, jj))
596  calib.coeffs[ii][jj] = np.nan
597  calib.coeffErr[ii][jj] = np.nan
598  calib.coeffValid[ii][jj] = False
599  else:
600  if ii != jj:
601  for rej in range(rejIter):
602  lo, med, hi = np.percentile(values, [25.0, 50.0, 75.0])
603  sigma = 0.741*(hi - lo)
604  good = np.abs(values - med) < rejSigma*sigma
605  if good.sum() == len(good):
606  break
607  values = values[good]
608 
609  calib.coeffs[ii][jj] = np.mean(values)
610  if calib.coeffNum[ii][jj] == 1:
611  calib.coeffErr[ii][jj] = np.nan
612  else:
613  correctionFactor = sigmaClipCorrection(rejSigma)
614  calib.coeffErr[ii][jj] = np.std(values) * correctionFactor
615  calib.coeffValid[ii][jj] = (np.abs(calib.coeffs[ii][jj])
616  > calib.coeffErr[ii][jj] / np.sqrt(calib.coeffNum[ii][jj]))
617 
618  if calib.coeffNum[ii][jj] > 1:
619  self.debugRatios('measure', ratios, ordering[ii], ordering[jj],
620  calib.coeffs[ii][jj], calib.coeffValid[ii][jj])
621 
622  return calib
623 
daf::base::PropertyList * list
Definition: fits.cc:913
def sigmaClipCorrection(nSigClip)
Definition: utils.py:42

◆ run()

def lsst.cp.pipe.measureCrosstalk.CrosstalkSolveTask.run (   self,
  inputRatios,
  inputFluxes = None,
  camera = None,
  inputDims = None,
  outputDims = None 
)
Combine ratios to produce crosstalk coefficients.

Parameters
----------
inputRatios : `list` [`dict` [`dict` [`dict` [`dict` [`list`]]]]]
    A list of nested dictionaries of ratios indexed by target
    and source chip, then by target and source amplifier.
inputFluxes : `list` [`dict` [`dict` [`list`]]]
    A list of nested dictionaries of source pixel fluxes, indexed
    by source chip and amplifier.
camera : `lsst.afw.cameraGeom.Camera`
    Input camera.
inputDims : `list` [`lsst.daf.butler.DataCoordinate`]
    DataIds to use to construct provenance.
outputDims : `list` [`lsst.daf.butler.DataCoordinate`]
    DataIds to use to populate the output calibration.

Returns
-------
results : `lsst.pipe.base.Struct`
    The results struct containing:

    ``outputCrosstalk`` : `lsst.ip.isr.CrosstalkCalib`
        Final crosstalk calibration.
    ``outputProvenance`` : `lsst.ip.isr.IsrProvenance`
        Provenance data for the new calibration.

Raises
------
RuntimeError
    Raised if the input data contains multiple target detectors.

Notes
-----
The lsstDebug.Info() method can be rewritten for __name__ =
`lsst.ip.isr.measureCrosstalk`, and supports the parameters:

debug.display['reduce'] : `bool`
    Display a histogram of the combined ratio measurements for
    a pair of source/target amplifiers from all input
    exposures/detectors.

Definition at line 415 of file measureCrosstalk.py.

415  def run(self, inputRatios, inputFluxes=None, camera=None, inputDims=None, outputDims=None):
416  """Combine ratios to produce crosstalk coefficients.
417 
418  Parameters
419  ----------
420  inputRatios : `list` [`dict` [`dict` [`dict` [`dict` [`list`]]]]]
421  A list of nested dictionaries of ratios indexed by target
422  and source chip, then by target and source amplifier.
423  inputFluxes : `list` [`dict` [`dict` [`list`]]]
424  A list of nested dictionaries of source pixel fluxes, indexed
425  by source chip and amplifier.
426  camera : `lsst.afw.cameraGeom.Camera`
427  Input camera.
428  inputDims : `list` [`lsst.daf.butler.DataCoordinate`]
429  DataIds to use to construct provenance.
430  outputDims : `list` [`lsst.daf.butler.DataCoordinate`]
431  DataIds to use to populate the output calibration.
432 
433  Returns
434  -------
435  results : `lsst.pipe.base.Struct`
436  The results struct containing:
437 
438  ``outputCrosstalk`` : `lsst.ip.isr.CrosstalkCalib`
439  Final crosstalk calibration.
440  ``outputProvenance`` : `lsst.ip.isr.IsrProvenance`
441  Provenance data for the new calibration.
442 
443  Raises
444  ------
445  RuntimeError
446  Raised if the input data contains multiple target detectors.
447 
448  Notes
449  -----
450  The lsstDebug.Info() method can be rewritten for __name__ =
451  `lsst.ip.isr.measureCrosstalk`, and supports the parameters:
452 
453  debug.display['reduce'] : `bool`
454  Display a histogram of the combined ratio measurements for
455  a pair of source/target amplifiers from all input
456  exposures/detectors.
457 
458  """
459  if outputDims:
460  calibChip = outputDims['detector']
461  instrument = outputDims['instrument']
462  else:
463  # calibChip needs to be set manually in Gen2.
464  calibChip = None
465  instrument = None
466 
467  if camera and calibChip:
468  calibDetector = camera[calibChip]
469  else:
470  calibDetector = None
471 
472  self.log.info("Combining measurements from %d ratios and %d fluxes",
473  len(inputRatios), len(inputFluxes) if inputFluxes else 0)
474 
475  if inputFluxes is None:
476  inputFluxes = [None for exp in inputRatios]
477 
478  combinedRatios = defaultdict(lambda: defaultdict(list))
479  combinedFluxes = defaultdict(lambda: defaultdict(list))
480  for ratioDict, fluxDict in zip(inputRatios, inputFluxes):
481  for targetChip in ratioDict:
482  if calibChip and targetChip != calibChip and targetChip != calibDetector.getName():
483  raise RuntimeError(f"Target chip: {targetChip} does not match calibration dimension: "
484  f"{calibChip}, {calibDetector.getName()}!")
485 
486  sourceChip = targetChip
487  if sourceChip in ratioDict[targetChip]:
488  ratios = ratioDict[targetChip][sourceChip]
489 
490  for targetAmp in ratios:
491  for sourceAmp in ratios[targetAmp]:
492  combinedRatios[targetAmp][sourceAmp].extend(ratios[targetAmp][sourceAmp])
493  if fluxDict:
494  combinedFluxes[targetAmp][sourceAmp].extend(fluxDict[sourceChip][sourceAmp])
495  # TODO: DM-21904
496  # Iterating over all other entries in ratioDict[targetChip] will yield
497  # inter-chip terms.
498 
499  for targetAmp in combinedRatios:
500  for sourceAmp in combinedRatios[targetAmp]:
501  self.log.info("Read %d pixels for %s -> %s",
502  len(combinedRatios[targetAmp][sourceAmp]),
503  targetAmp, sourceAmp)
504  if len(combinedRatios[targetAmp][sourceAmp]) > 1:
505  self.debugRatios('reduce', combinedRatios, targetAmp, sourceAmp)
506 
507  if self.config.fluxOrder == 0:
508  self.log.info("Fitting crosstalk coefficients.")
509  calib = self.measureCrosstalkCoefficients(combinedRatios,
510  self.config.rejIter, self.config.rejSigma)
511  else:
512  raise NotImplementedError("Non-linear crosstalk terms are not yet supported.")
513 
514  self.log.info("Number of valid coefficients: %d", np.sum(calib.coeffValid))
515 
516  if self.config.doFiltering:
517  # This step will apply the calculated validity values to
518  # censor poorly measured coefficients.
519  self.log.info("Filtering measured crosstalk to remove invalid solutions.")
520  calib = self.filterCrosstalkCalib(calib)
521 
522  # Populate the remainder of the calibration information.
523  calib.hasCrosstalk = True
524  calib.interChip = {}
525 
526  # calibChip is the detector dimension, which is the detector Id
527  calib._detectorId = calibChip
528  if calibDetector:
529  calib._detectorName = calibDetector.getName()
530  calib._detectorSerial = calibDetector.getSerial()
531 
532  calib._instrument = instrument
533  calib.updateMetadata(setCalibId=True, setDate=True)
534 
535  # Make an IsrProvenance().
536  provenance = IsrProvenance(calibType="CROSSTALK")
537  provenance._detectorName = calibChip
538  if inputDims:
539  provenance.fromDataIds(inputDims)
540  provenance._instrument = instrument
541  provenance.updateMetadata()
542 
543  return pipeBase.Struct(
544  outputCrosstalk=calib,
545  outputProvenance=provenance,
546  )
547 
def run(self, skyInfo, tempExpRefList, imageScalerList, weightList, altMaskList=None, mask=None, supplementaryData=None)

◆ runQuantum()

def lsst.cp.pipe.measureCrosstalk.CrosstalkSolveTask.runQuantum (   self,
  butlerQC,
  inputRefs,
  outputRefs 
)
Ensure that the input and output dimensions are passed along.

Parameters
----------
butlerQC : `lsst.daf.butler.butlerQuantumContext.ButlerQuantumContext`
    Butler to operate on.
inputRefs : `lsst.pipe.base.connections.InputQuantizedConnection`
    Input data refs to load.
ouptutRefs : `lsst.pipe.base.connections.OutputQuantizedConnection`
    Output data refs to persist.

Definition at line 394 of file measureCrosstalk.py.

394  def runQuantum(self, butlerQC, inputRefs, outputRefs):
395  """Ensure that the input and output dimensions are passed along.
396 
397  Parameters
398  ----------
399  butlerQC : `lsst.daf.butler.butlerQuantumContext.ButlerQuantumContext`
400  Butler to operate on.
401  inputRefs : `lsst.pipe.base.connections.InputQuantizedConnection`
402  Input data refs to load.
403  ouptutRefs : `lsst.pipe.base.connections.OutputQuantizedConnection`
404  Output data refs to persist.
405  """
406  inputs = butlerQC.get(inputRefs)
407 
408  # Use the dimensions to set calib/provenance information.
409  inputs['inputDims'] = [exp.dataId.byName() for exp in inputRefs.inputRatios]
410  inputs['outputDims'] = outputRefs.outputCrosstalk.dataId.byName()
411 
412  outputs = self.run(**inputs)
413  butlerQC.put(outputs, outputRefs)
414 

Member Data Documentation

◆ ConfigClass

lsst.cp.pipe.measureCrosstalk.CrosstalkSolveTask.ConfigClass = CrosstalkSolveConfig
static

Definition at line 391 of file measureCrosstalk.py.


The documentation for this class was generated from the following file: