From 14bbf0d7705590377f3e29b9a085e0d34634946e Mon Sep 17 00:00:00 2001 From: Alex Broughton Date: Wed, 30 Oct 2024 13:15:59 -0700 Subject: [PATCH 1/6] Specify serial cti statistics in overscan stats keys --- python/lsst/cp/pipe/cpCtiSolve.py | 40 +++--- tests/test_deferredCharge.py | 194 +++++++++++++++--------------- 2 files changed, 117 insertions(+), 117 deletions(-) diff --git a/python/lsst/cp/pipe/cpCtiSolve.py b/python/lsst/cp/pipe/cpCtiSolve.py index cc5be64a..b4d60635 100644 --- a/python/lsst/cp/pipe/cpCtiSolve.py +++ b/python/lsst/cp/pipe/cpCtiSolve.py @@ -146,15 +146,15 @@ def run(self, inputMeasurements, camera, inputDims): with measurements organized by amplifier name, containing keys: - ``"FIRST_MEAN"`` + ``"FIRST_COLUMN_MEAN"`` Mean value of first image column (`float`). - ``"LAST_MEAN"`` + ``"LAST_COLUMN_MEAN"`` Mean value of last image column (`float`). ``"IMAGE_MEAN"`` Mean value of the entire image region (`float`). - ``"OVERSCAN_COLUMNS"`` + ``"SERIAL_OVERSCAN_COLUMNS"`` List of overscan column indicies (`list` [`int`]). - ``"OVERSCAN_VALUES"`` + ``"SERIAL_OVERSCAN_VALUES"`` List of overscan column means (`list` [`float`]). camera : `lsst.afw.cameraGeom.Camera` Camera geometry to use to find detectors. @@ -209,15 +209,15 @@ def solveLocalOffsets(self, inputMeasurements, calib, detector): with measurements organized by amplifier name, containing keys: - ``"FIRST_MEAN"`` + ``"FIRST_COLUMN_MEAN"`` Mean value of first image column (`float`). - ``"LAST_MEAN"`` + ``"LAST_COLUMN_MEAN"`` Mean value of last image column (`float`). ``"IMAGE_MEAN"`` Mean value of the entire image region (`float`). - ``"OVERSCAN_COLUMNS"`` + ``"SERIAL_OVERSCAN_COLUMNS"`` List of overscan column indicies (`list` [`int`]). - ``"OVERSCAN_VALUES"`` + ``"SERIAL_OVERSCAN_VALUES"`` List of overscan column means (`list` [`float`]). calib : `lsst.ip.isr.DeferredChargeCalib` Calibration to populate with values. @@ -271,7 +271,7 @@ def solveLocalOffsets(self, inputMeasurements, calib, detector): exposureDict = exposureEntry['CTI'] if exposureDict[ampName]['IMAGE_MEAN'] < self.config.maxImageMean: signal.append(exposureDict[ampName]['IMAGE_MEAN']) - data.append(exposureDict[ampName]['OVERSCAN_VALUES'][start:stop+1]) + data.append(exposureDict[ampName]['SERIAL_OVERSCAN_VALUES'][start:stop+1]) else: Nskipped += 1 self.log.info(f"Skipped {Nskipped} exposures brighter than {self.config.maxImageMean}.") @@ -324,15 +324,15 @@ def solveGlobalCti(self, inputMeasurements, calib, detector): with measurements organized by amplifier name, containing keys: - ``"FIRST_MEAN"`` + ``"FIRST_COLUMN_MEAN"`` Mean value of first image column (`float`). - ``"LAST_MEAN"`` + ``"LAST_COLUMN_MEAN"`` Mean value of last image column (`float`). ``"IMAGE_MEAN"`` Mean value of the entire image region (`float`). - ``"OVERSCAN_COLUMNS"`` + ``"SERIAL_OVERSCAN_COLUMNS"`` List of overscan column indicies (`list` [`int`]). - ``"OVERSCAN_VALUES"`` + ``"SERIAL_OVERSCAN_VALUES"`` List of overscan column means (`list` [`float`]). calib : `lsst.ip.isr.DeferredChargeCalib` Calibration to populate with values. @@ -386,7 +386,7 @@ def solveGlobalCti(self, inputMeasurements, calib, detector): exposureDict = exposureEntry['CTI'] if exposureDict[ampName]['IMAGE_MEAN'] < self.config.maxSignalForCti: signal.append(exposureDict[ampName]['IMAGE_MEAN']) - data.append(exposureDict[ampName]['OVERSCAN_VALUES'][start:stop+1]) + data.append(exposureDict[ampName]['SERIAL_OVERSCAN_VALUES'][start:stop+1]) else: Nskipped += 1 self.log.info(f"Skipped {Nskipped} exposures brighter than {self.config.maxSignalForCti}.") @@ -492,15 +492,15 @@ def findTraps(self, inputMeasurements, calib, detector): with measurements organized by amplifier name, containing keys: - ``"FIRST_MEAN"`` + ``"FIRST_COLUMN_MEAN"`` Mean value of first image column (`float`). - ``"LAST_MEAN"`` + ``"LAST_COLUMN_MEAN"`` Mean value of last image column (`float`). ``"IMAGE_MEAN"`` Mean value of the entire image region (`float`). - ``"OVERSCAN_COLUMNS"`` + ``"SERIAL_OVERSCAN_COLUMNS"`` List of overscan column indicies (`list` [`int`]). - ``"OVERSCAN_VALUES"`` + ``"SERIAL_OVERSCAN_VALUES"`` List of overscan column means (`list` [`float`]). calib : `lsst.ip.isr.DeferredChargeCalib` Calibration to populate with values. @@ -556,8 +556,8 @@ def findTraps(self, inputMeasurements, calib, detector): exposureDict = exposureEntry['CTI'] if exposureDict[ampName]['IMAGE_MEAN'] < self.config.maxImageMean: signal.append(exposureDict[ampName]['IMAGE_MEAN']) - data.append(exposureDict[ampName]['OVERSCAN_VALUES'][start:stop+1]) - new_signal.append(exposureDict[ampName]['LAST_MEAN']) + data.append(exposureDict[ampName]['SERIAL_OVERSCAN_VALUES'][start:stop+1]) + new_signal.append(exposureDict[ampName]['LAST_COLUMN_MEAN']) else: Nskipped += 1 self.log.info(f"Skipped {Nskipped} exposures brighter than {self.config.maxImageMean}.") diff --git a/tests/test_deferredCharge.py b/tests/test_deferredCharge.py index 3e8f5551..55f3c79d 100644 --- a/tests/test_deferredCharge.py +++ b/tests/test_deferredCharge.py @@ -87,103 +87,103 @@ def setUp(self): -0.150720824, 0.038237873, 0.17603852613429813] self.inputMeasurements = [ - {'CTI': {'C:0,0': {'FIRST_MEAN': 117.810165, 'LAST_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'OVERSCAN_VALUES': overscanMeansA, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,1': {'FIRST_MEAN': 117.810165, 'LAST_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'OVERSCAN_VALUES': overscanMeansA, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,2': {'FIRST_MEAN': 117.810165, 'LAST_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'OVERSCAN_VALUES': overscanMeansA, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,3': {'FIRST_MEAN': 117.810165, 'LAST_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'OVERSCAN_VALUES': overscanMeansA, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,0': {'FIRST_MEAN': 117.810165, 'LAST_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'OVERSCAN_VALUES': overscanMeansA, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,1': {'FIRST_MEAN': 117.810165, 'LAST_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'OVERSCAN_VALUES': overscanMeansA, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,2': {'FIRST_MEAN': 117.810165, 'LAST_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'OVERSCAN_VALUES': overscanMeansA, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,3': {'FIRST_MEAN': 117.810165, 'LAST_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'OVERSCAN_VALUES': overscanMeansA, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}, - {'CTI': {'C:0,0': {'FIRST_MEAN': 36562.082, 'LAST_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'OVERSCAN_VALUES': overscanMeansB, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,1': {'FIRST_MEAN': 36562.082, 'LAST_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'OVERSCAN_VALUES': overscanMeansB, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,2': {'FIRST_MEAN': 36562.082, 'LAST_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'OVERSCAN_VALUES': overscanMeansB, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,3': {'FIRST_MEAN': 36562.082, 'LAST_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'OVERSCAN_VALUES': overscanMeansB, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,0': {'FIRST_MEAN': 36562.082, 'LAST_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'OVERSCAN_VALUES': overscanMeansB, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,1': {'FIRST_MEAN': 36562.082, 'LAST_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'OVERSCAN_VALUES': overscanMeansB, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,2': {'FIRST_MEAN': 36562.082, 'LAST_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'OVERSCAN_VALUES': overscanMeansB, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,3': {'FIRST_MEAN': 36562.082, 'LAST_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'OVERSCAN_VALUES': overscanMeansB, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}, - - {'CTI': {'C:0,0': {'FIRST_MEAN': 994.811, 'LAST_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'OVERSCAN_VALUES': overscanMeansC, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,1': {'FIRST_MEAN': 994.811, 'LAST_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'OVERSCAN_VALUES': overscanMeansC, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,2': {'FIRST_MEAN': 994.811, 'LAST_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'OVERSCAN_VALUES': overscanMeansC, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,3': {'FIRST_MEAN': 994.811, 'LAST_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'OVERSCAN_VALUES': overscanMeansC, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,0': {'FIRST_MEAN': 994.811, 'LAST_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'OVERSCAN_VALUES': overscanMeansC, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,1': {'FIRST_MEAN': 994.811, 'LAST_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'OVERSCAN_VALUES': overscanMeansC, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,2': {'FIRST_MEAN': 994.811, 'LAST_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'OVERSCAN_VALUES': overscanMeansC, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,3': {'FIRST_MEAN': 994.811, 'LAST_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'OVERSCAN_VALUES': overscanMeansC, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}, - {'CTI': {'C:0,0': {'FIRST_MEAN': 12215.778, 'LAST_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'OVERSCAN_VALUES': overscanMeansD, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,1': {'FIRST_MEAN': 12215.778, 'LAST_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'OVERSCAN_VALUES': overscanMeansD, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,2': {'FIRST_MEAN': 12215.778, 'LAST_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'OVERSCAN_VALUES': overscanMeansD, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:0,3': {'FIRST_MEAN': 12215.778, 'LAST_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'OVERSCAN_VALUES': overscanMeansD, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,0': {'FIRST_MEAN': 12215.778, 'LAST_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'OVERSCAN_VALUES': overscanMeansD, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,1': {'FIRST_MEAN': 12215.778, 'LAST_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'OVERSCAN_VALUES': overscanMeansD, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,2': {'FIRST_MEAN': 12215.778, 'LAST_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'OVERSCAN_VALUES': overscanMeansD, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, - 'C:1,3': {'FIRST_MEAN': 12215.778, 'LAST_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'OVERSCAN_VALUES': overscanMeansD, - 'OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}] + {'CTI': {'C:0,0': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,1': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,2': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,3': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,0': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,1': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,2': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,3': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}, + {'CTI': {'C:0,0': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,1': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,2': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,3': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,0': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,1': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,2': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,3': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}, + + {'CTI': {'C:0,0': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,1': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,2': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,3': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,0': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,1': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,2': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,3': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}, + {'CTI': {'C:0,0': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,1': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,2': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:0,3': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,0': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,1': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,2': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'C:1,3': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}] self.inputDims = [{'detector': 20, 'instrument': 'IsrMock', 'exposure': 2019101200433}, {'detector': 20, 'instrument': 'IsrMock', 'exposure': 2019101300154}, {'detector': 20, 'instrument': 'IsrMock', 'exposure': 2019101300004}, From 762babdc98dda8026707f390e01f2af0e1643c7e Mon Sep 17 00:00:00 2001 From: Alex Broughton Date: Sun, 3 Nov 2024 17:16:20 -0800 Subject: [PATCH 2/6] Give serial EPER its own function --- python/lsst/cp/pipe/cpCtiSolve.py | 118 +++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/python/lsst/cp/pipe/cpCtiSolve.py b/python/lsst/cp/pipe/cpCtiSolve.py index b4d60635..844bc2f9 100644 --- a/python/lsst/cp/pipe/cpCtiSolve.py +++ b/python/lsst/cp/pipe/cpCtiSolve.py @@ -361,12 +361,6 @@ def solveGlobalCti(self, inputMeasurements, calib, detector): CTISIM. This offset removes that last imaging data column from the count. """ - # Range to fit. These are in "camera" coordinates, and so - # need to have the count for last image column removed. - start, stop = self.config.globalCtiColumnRange - start -= 1 - stop -= 1 - # Loop over amps/inputs, fitting those columns from # "non-saturated" inputs. for amp in detector.getAmplifiers(): @@ -375,36 +369,7 @@ def solveGlobalCti(self, inputMeasurements, calib, detector): # Number of serial shifts. nCols = amp.getRawDataBBox().getWidth() + amp.getRawSerialPrescanBBox().getWidth() - # The signal is the mean intensity of each input, and the - # data are the overscan columns to fit. For detectors - # with non-zero CTI, the charge from the imaging region - # leaks into the overscan region. - signal = [] - data = [] - Nskipped = 0 - for exposureEntry in inputMeasurements: - exposureDict = exposureEntry['CTI'] - if exposureDict[ampName]['IMAGE_MEAN'] < self.config.maxSignalForCti: - signal.append(exposureDict[ampName]['IMAGE_MEAN']) - data.append(exposureDict[ampName]['SERIAL_OVERSCAN_VALUES'][start:stop+1]) - else: - Nskipped += 1 - self.log.info(f"Skipped {Nskipped} exposures brighter than {self.config.maxSignalForCti}.") - if len(signal) == 0 or len(data) == 0: - raise RuntimeError("All exposures brighter than config.maxSignalForCti and excluded.") - - signal = np.array(signal) - data = np.array(data) - - ind = signal.argsort() - signal = signal[ind] - data = data[ind] - - # CTI test. This looks at the charge that has leaked into - # the first few columns of the overscan. - overscan1 = data[:, 0] - overscan2 = data[:, 1] - test = (np.array(overscan1) + np.array(overscan2))/(nCols*np.array(signal)) + signal, data, start, stop, test = self.serialEper(inputMeasurements, amp) testResult = np.median(test) > 5.E-6 self.log.info("Estimate of CTI test is %f for amp %s, %s.", np.median(test), ampName, "full fitting will be performed" if testResult else @@ -611,3 +576,84 @@ def findTraps(self, inputMeasurements, calib, detector): calib.serialTraps[ampName] = trap return calib + + def serialEper(self, inputMeasurements, amp): + """Solve for serial global CTI using the extended pixel edge + response (EPER) method. + + Parameters + ---------- + inputMeasurements : `list` [`dict`] + List of overscan measurements from each input exposure. + Each dictionary is nested within a top level 'CTI' key, + with measurements organized by amplifier name, containing + keys: + + ``"FIRST_COLUMN_MEAN"`` + Mean value of first image column (`float`). + ``"LAST_COLUMN_MEAN"`` + Mean value of last image column (`float`). + ``"IMAGE_MEAN"`` + Mean value of the entire image region (`float`). + ``"SERIAL_OVERSCAN_COLUMNS"`` + List of overscan column indicies (`list` [`int`]). + ``"SERIAL_OVERSCAN_VALUES"`` + List of overscan column means (`list` [`float`]). + calib : `lsst.ip.isr.DeferredChargeCalib` + Calibration to populate with values. + amp : `lsst.afw.cameraGeom.Amplifier` + Amplifier object containing the geometry information for + the amplifier. + + Returns + ------- + calib : `lsst.ip.isr.DeferredChargeCalib` + Populated calibration. + + Raises + ------ + RuntimeError : Raised if no data remains after flux filtering. + """ + ampName = amp.getName() + + # Range to fit. These are in "camera" coordinates, and so + # need to have the count for last image column removed. + start, stop = self.config.globalCtiColumnRange + start -= 1 + stop -= 1 + + # Number of serial shifts. + nCols = amp.getRawDataBBox().getWidth() + amp.getRawSerialPrescanBBox().getWidth() + + # The signal is the mean intensity of each input, and the + # data are the overscan columns to fit. For detectors + # with non-zero CTI, the charge from the imaging region + # leaks into the overscan region. + signal = [] + data = [] + Nskipped = 0 + for exposureEntry in inputMeasurements: + exposureDict = exposureEntry['CTI'] + if exposureDict[ampName]['IMAGE_MEAN'] < self.config.maxSignalForCti: + signal.append(exposureDict[ampName]['IMAGE_MEAN']) + data.append(exposureDict[ampName]['SERIAL_OVERSCAN_VALUES'][start:stop+1]) + else: + Nskipped += 1 + self.log.info(f"Skipped {Nskipped} exposures brighter than {self.config.maxSignalForCti}.") + if len(signal) == 0 or len(data) == 0: + raise RuntimeError("All exposures brighter than config.maxSignalForCti and excluded.") + + signal = np.array(signal) + data = np.array(data) + + ind = signal.argsort() + signal = signal[ind] + data = data[ind] + + # CTI test. This looks at the charge that has leaked into + # the first few columns of the overscan. + overscan1 = data[:, 0] + overscan2 = data[:, 1] + serialCtiEstimate = (np.array(overscan1) + np.array(overscan2))/(nCols*np.array(signal)) + + return signal, data, start, stop, serialCtiEstimate \ No newline at end of file From 243214cb06b94bd29a0a33509ce5bd4b50fd6663 Mon Sep 17 00:00:00 2001 From: Alex Broughton Date: Tue, 5 Nov 2024 13:25:12 -0800 Subject: [PATCH 3/6] Add parallel overscan EPER to CTI statistics --- python/lsst/cp/pipe/cpCtiSolve.py | 109 ++++++++--- tests/test_deferredCharge.py | 302 +++++++++++++++++++----------- 2 files changed, 272 insertions(+), 139 deletions(-) diff --git a/python/lsst/cp/pipe/cpCtiSolve.py b/python/lsst/cp/pipe/cpCtiSolve.py index 844bc2f9..78e46937 100644 --- a/python/lsst/cp/pipe/cpCtiSolve.py +++ b/python/lsst/cp/pipe/cpCtiSolve.py @@ -92,7 +92,14 @@ class CpCtiSolveConfig(pipeBase.PipelineTaskConfig, globalCtiColumnRange = pexConfig.ListField( dtype=int, default=[1, 2], - doc="First and last overscan column to use for global CTI fit.", + doc="First and last serialoverscan column to use for " + "global CTI fit.", + ) + globalCtiRowRange = pexConfig.ListField( + dtype=int, + default=[1, 2], + doc="First and last parallel overscan row to use for " + "global CTI fit.", ) trapColumnRange = pexConfig.ListField( @@ -156,6 +163,10 @@ def run(self, inputMeasurements, camera, inputDims): List of overscan column indicies (`list` [`int`]). ``"SERIAL_OVERSCAN_VALUES"`` List of overscan column means (`list` [`float`]). + ``"PARALLEL_OVERSCAN_ROWS"`` + List of overscan row indicies (`list` [`int`]). + ``"PARALLEL_OVERSCAN_VALUES"`` + List of overscan row means (`list` [`float`). camera : `lsst.afw.cameraGeom.Camera` Camera geometry to use to find detectors. inputDims : `list` [`dict`] @@ -219,6 +230,10 @@ def solveLocalOffsets(self, inputMeasurements, calib, detector): List of overscan column indicies (`list` [`int`]). ``"SERIAL_OVERSCAN_VALUES"`` List of overscan column means (`list` [`float`]). + ``"PARALLEL_OVERSCAN_ROWS"`` + List of overscan row indicies (`list` [`int`]). + ``"PARALLEL_OVERSCAN_VALUES"`` + List of overscan row means (`list` [`float`). calib : `lsst.ip.isr.DeferredChargeCalib` Calibration to populate with values. detector : `lsst.afw.cameraGeom.Detector` @@ -334,6 +349,10 @@ def solveGlobalCti(self, inputMeasurements, calib, detector): List of overscan column indicies (`list` [`int`]). ``"SERIAL_OVERSCAN_VALUES"`` List of overscan column means (`list` [`float`]). + ``"PARALLEL_OVERSCAN_ROWS"`` + List of overscan row indicies (`list` [`int`]). + ``"PARALLEL_OVERSCAN_VALUES"`` + List of overscan row means (`list` [`float`). calib : `lsst.ip.isr.DeferredChargeCalib` Calibration to populate with values. detector : `lsst.afw.cameraGeom.Detector` @@ -369,28 +388,31 @@ def solveGlobalCti(self, inputMeasurements, calib, detector): # Number of serial shifts. nCols = amp.getRawDataBBox().getWidth() + amp.getRawSerialPrescanBBox().getWidth() - signal, data, start, stop, test = self.serialEper(inputMeasurements, amp) - testResult = np.median(test) > 5.E-6 - self.log.info("Estimate of CTI test is %f for amp %s, %s.", np.median(test), ampName, - "full fitting will be performed" if testResult else - "only global CTI fitting will be performed") + # Do serial CTI calculation + signals, data, start, stop, serialEperEstimate = self.calcEper("SERIAL", inputMeasurements, amp) + serialEperResult = np.median(serialEperEstimate) > 5.E-6 + self.log.info( + "Estimate of CTI test is %f for amp %s, %s.", np.median(serialEperEstimate), ampName, + "full fitting will be performed" if serialEperResult else + "only global CTI fitting will be performed" + ) - self.debugView(ampName, signal, test) + self.debugView(ampName, signals, serialEperEstimate) params = Parameters() params.add('ctiexp', value=-6, min=-7, max=-5, vary=True) - params.add('trapsize', value=5.0 if testResult else 0.0, min=0.0, max=30., - vary=True if testResult else False) + params.add('trapsize', value=5.0 if serialEperResult else 0.0, min=0.0, max=30., + vary=True if serialEperResult else False) params.add('scaling', value=0.08, min=0.0, max=1.0, - vary=True if testResult else False) + vary=True if serialEperResult else False) params.add('emissiontime', value=0.35, min=0.1, max=1.0, - vary=True if testResult else False) + vary=True if serialEperResult else False) params.add('driftscale', value=calib.driftScale[ampName], min=0., max=0.001, vary=False) params.add('decaytime', value=calib.decayTime[ampName], min=0.1, max=4.0, vary=False) model = SimulatedModel() minner = Minimizer(model.difference, params, - fcn_args=(signal, data, self.config.fitError, nCols, amp), + fcn_args=(signals, data, self.config.fitError, nCols, amp), fcn_kws={'start': start, 'stop': stop, 'trap_type': 'linear'}) result = minner.minimize() @@ -400,6 +422,17 @@ def solveGlobalCti(self, inputMeasurements, calib, detector): ampName, calib.globalCti[ampName], calib.decayTime[ampName], calib.driftScale[ampName]) + # Do parallel EPER calculation + signals, data, start, stop, parallelEperEstimate = self.calcEper( + "PARALLEL", + inputMeasurements, + amp, + ) + + calib.signals[ampName] = signals + calib.serialEper[ampName] = serialEperEstimate + calib.parallelEper[ampName] = parallelEperEstimate + return calib def debugView(self, ampName, signal, test): @@ -577,12 +610,15 @@ def findTraps(self, inputMeasurements, calib, detector): return calib - def serialEper(self, inputMeasurements, amp): + def calcEper(self, mode, inputMeasurements, amp): """Solve for serial global CTI using the extended pixel edge response (EPER) method. Parameters ---------- + mode : `str` + The orientation of the calculation to perform. Can be + either `SERIAL` or `PARALLEL`. inputMeasurements : `list` [`dict`] List of overscan measurements from each input exposure. Each dictionary is nested within a top level 'CTI' key, @@ -596,9 +632,17 @@ def serialEper(self, inputMeasurements, amp): ``"IMAGE_MEAN"`` Mean value of the entire image region (`float`). ``"SERIAL_OVERSCAN_COLUMNS"`` - List of overscan column indicies (`list` [`int`]). + List of serial overscan column + indicies (`list` [`int`]). ``"SERIAL_OVERSCAN_VALUES"`` - List of overscan column means (`list` [`float`]). + List of serial overscan column + means (`list` [`float`]). + ``"PARALLEL_OVERSCAN_ROWS"`` + List of parallel overscan row + indicies (`list` [`int`]). + ``"PARALLEL_OVERSCAN_VALUES"`` + List of parallel overscan row + means (`list` [`float`]). calib : `lsst.ip.isr.DeferredChargeCalib` Calibration to populate with values. amp : `lsst.afw.cameraGeom.Amplifier` @@ -612,18 +656,29 @@ def serialEper(self, inputMeasurements, amp): Raises ------ - RuntimeError : Raised if no data remains after flux filtering. + RuntimeError : Raised if no data remains after flux filtering or if + the mode string is not one of "SERIAL" or "PARALLEL". """ ampName = amp.getName() # Range to fit. These are in "camera" coordinates, and so # need to have the count for last image column removed. - start, stop = self.config.globalCtiColumnRange - start -= 1 - stop -= 1 - - # Number of serial shifts. - nCols = amp.getRawDataBBox().getWidth() + amp.getRawSerialPrescanBBox().getWidth() + if mode == "SERIAL": + start, stop = self.config.globalCtiColumnRange + start -= 1 + stop -= 1 + + # Number of serial shifts = nCols + nShifts = amp.getRawDataBBox().getWidth() + amp.getRawSerialPrescanBBox().getWidth() + elif mode == "PARALLEL": + start, stop = self.config.globalCtiRowRange + start -= 1 + stop -= 1 + + # Number of parallel shifts = nRows + nShifts = amp.getRawDataBBox().getHeight() + else: + raise RuntimeError(f"{mode} is not a known orientation for the EPER calculation.") # The signal is the mean intensity of each input, and the # data are the overscan columns to fit. For detectors @@ -632,11 +687,11 @@ def serialEper(self, inputMeasurements, amp): signal = [] data = [] Nskipped = 0 - for exposureEntry in inputMeasurements: + for idx, exposureEntry in enumerate(inputMeasurements): exposureDict = exposureEntry['CTI'] if exposureDict[ampName]['IMAGE_MEAN'] < self.config.maxSignalForCti: signal.append(exposureDict[ampName]['IMAGE_MEAN']) - data.append(exposureDict[ampName]['SERIAL_OVERSCAN_VALUES'][start:stop+1]) + data.append(exposureDict[ampName][f'{mode}_OVERSCAN_VALUES'][start:stop+1]) else: Nskipped += 1 self.log.info(f"Skipped {Nskipped} exposures brighter than {self.config.maxSignalForCti}.") @@ -650,10 +705,10 @@ def serialEper(self, inputMeasurements, amp): signal = signal[ind] data = data[ind] - # CTI test. This looks at the charge that has leaked into + # This looks at the charge that has leaked into # the first few columns of the overscan. overscan1 = data[:, 0] overscan2 = data[:, 1] - serialCtiEstimate = (np.array(overscan1) + np.array(overscan2))/(nCols*np.array(signal)) + ctiEstimate = (np.array(overscan1) + np.array(overscan2))/(nShifts*np.array(signal)) - return signal, data, start, stop, serialCtiEstimate \ No newline at end of file + return signal, data, start, stop, ctiEstimate diff --git a/tests/test_deferredCharge.py b/tests/test_deferredCharge.py index 55f3c79d..ac6350e0 100644 --- a/tests/test_deferredCharge.py +++ b/tests/test_deferredCharge.py @@ -37,153 +37,231 @@ class CpCtiSolveTaskTestCase(lsst.utils.tests.TestCase): def setUp(self): self.camera = IsrMock().getCamera() - overscanMeansA = [7.18039751e-01, 4.56550479e-01, 4.14261669e-01, 2.88099229e-01, -2.34310962e-02, - -4.59854975e-02, -1.14491098e-02, 5.19846082e-02, 2.05635265e-01, 1.25147207e-02, - 9.00449380e-02, -2.39106059e-01, -1.52413145e-01, 4.63459678e-02, 1.85195580e-01, - -1.58051759e-01, -8.76842241e-04, -5.09192124e-02, 2.58496821e-01, 2.54267782e-01, - -1.37611866e-01, 3.35322201e-01, 1.04846083e-01, -2.16551736e-01, -8.82746354e-02, - -1.00256450e-01, 2.73297966e-01, -4.52805981e-02, 3.40960979e-01, 7.80628920e-02, - -2.90697180e-02, -6.99492991e-02, -1.06599867e-01, 6.89002723e-02, 1.46290688e-02, - 1.19647197e-01, -1.54527843e-01, 9.35689881e-02, -1.06599934e-01, -2.13166289e-02, - 9.35688764e-02, -1.19286761e-01, 1.18098985e-02, -3.69616691e-03, -6.14914447e-02, - -5.81059000e-03, 9.42736641e-02, 3.92978266e-02, -1.55937240e-01, 3.76202404e-01, - -1.13648064e-01, 1.71803936e-01, 6.17138995e-03, 8.22918862e-02, -2.84214199e-01, - -2.99097435e-03, -1.31973490e-01, -2.84214795e-01, -2.99140741e-03, -3.76546055e-01, - 5.97376414e-02, -1.91883057e-01, -1.34087920e-01, -3.23684871e-01] - overscanMeansB = [1.50365152e+01, 4.43178511e+00, 2.66550946e+00, 1.67382801e+00, 1.10997069e+00, - 8.89361799e-01, 4.66469795e-01, 6.10956728e-01, 6.67343795e-01, 5.22854805e-01, - -1.15006611e-01, 2.67710119e-01, 2.05686077e-01, 1.84541523e-01, 8.65717679e-02, - 5.51738311e-03, 2.35288814e-01, 3.45944524e-01, 7.81139359e-02, 1.52119964e-01, - 2.02162191e-01, 3.44150960e-02, -2.86277920e-01, 1.43662184e-01, 3.21276844e-01, - -6.21452965e-02, 8.58670697e-02, -1.63320359e-02, -1.07958235e-01, -1.60820082e-01, - -2.19705645e-02, -1.55181482e-01, -2.39055425e-01, -2.75705636e-01, 6.33126274e-02, - -5.50971478e-02, -2.42579415e-01, -9.87957790e-02, 1.08421087e-01, -1.12892322e-01, - 1.89090632e-02, -1.53086300e-03, -2.18615308e-01, -2.19320312e-01, 9.22102109e-02, - -4.87535410e-02, -1.81964979e-01, -4.17055413e-02, -4.24422681e-01, -1.96061105e-01, - -1.35127297e-02, -1.77031055e-01, -2.30597332e-01, -4.01868790e-01, -4.18784261e-01, - -3.75085384e-01, -3.49007100e-01, -1.77735761e-01, -7.41272718e-02, -1.92537069e-01, - 2.46565759e-01, -3.44777972e-01, -2.85573214e-01, -2.34121397e-01] - overscanMeansC = [0.212578110, 0.107817403, -0.122200218, -0.0089812368, -0.067990060, 0.040077099, - -0.021402006, 0.090923088, -0.099587158, 0.274797124, -0.016930788, 0.045007070, - -0.00379911056, -0.16088248, 0.055911896, 0.0601755001, -0.046872945, 0.210018355, - 0.081641635, -0.046147249, -0.0059020276, 0.108368757, -0.033966731, -0.0058644798, - -0.075746922, -0.203826510, 0.12620401, -0.0156685544, -0.09631182, 0.089754454, - 0.03789926, 0.0304515115, -0.082173715, -0.061332140, -0.24894494, -0.155137551, - -0.073825312, 0.24538413, -0.069597074, 0.192338801, -0.0539746876, -0.184556000, - -0.173069382, -0.209975778, 0.086679191, 0.016299034, -0.0094125706, -0.100099911, - 0.061981365, 0.086250364, 0.209128404, -0.0067993622, 0.171072270, -0.29266333, - 0.075172274, -0.29375612, -0.13377650, 0.0125964781, -0.124991264, 0.226516831, - 0.128244484, -0.05019844, -0.149249925, -0.1557398] - overscanMeansD = [4.0867248, 1.43194193, 0.95319573, 0.43219185, 0.53112239, 0.28648, 0.323903486, - 0.27622156, 0.26031138, 0.144442975, 0.0149878587, 0.062969929, 0.018541051, - -0.237687056, 0.22804558, 0.0600504708, 0.140250022, -0.137477808, 0.119911710, - 0.03770870, -0.20021377, 0.187175400, 0.0168790129, -0.110724371, 0.099311580, - 0.0079969534, -0.157593577, -0.178876067, -0.214948580, -0.11354382, 0.148154530, - -0.056012520, 0.11851939, 0.067902033, 0.18970736, -0.181487703, -0.0101017127, - 0.100998570, -0.0309096733, -0.034450136, -0.066357072, -0.058662959, 0.146185921, - -0.218474021, -0.173691633, 0.055349625, -0.178158524, -0.012917378, -0.166576555, - -0.063862754, 0.113169933, -0.33518338, -0.074239500, 0.22262230, -0.066653975, - -0.200271016, -0.013275277, 0.100596499, -0.092528954, 0.0339541714, 0.113119135, - -0.150720824, 0.038237873, 0.17603852613429813] + serialOscanMeansA = [ + 7.18039751e-01, 4.56550479e-01, 4.14261669e-01, 2.88099229e-01, -2.34310962e-02, + -4.59854975e-02, -1.14491098e-02, 5.19846082e-02, 2.05635265e-01, 1.25147207e-02, + 9.00449380e-02, -2.39106059e-01, -1.52413145e-01, 4.63459678e-02, 1.85195580e-01, + -1.58051759e-01, -8.76842241e-04, -5.09192124e-02, 2.58496821e-01, 2.54267782e-01, + -1.37611866e-01, 3.35322201e-01, 1.04846083e-01, -2.16551736e-01, -8.82746354e-02, + -1.00256450e-01, 2.73297966e-01, -4.52805981e-02, 3.40960979e-01, 7.80628920e-02, + -2.90697180e-02, -6.99492991e-02, -1.06599867e-01, 6.89002723e-02, 1.46290688e-02, + 1.19647197e-01, -1.54527843e-01, 9.35689881e-02, -1.06599934e-01, -2.13166289e-02, + 9.35688764e-02, -1.19286761e-01, 1.18098985e-02, -3.69616691e-03, -6.14914447e-02, + -5.81059000e-03, 9.42736641e-02, 3.92978266e-02, -1.55937240e-01, 3.76202404e-01, + -1.13648064e-01, 1.71803936e-01, 6.17138995e-03, 8.22918862e-02, -2.84214199e-01, + -2.99097435e-03, -1.31973490e-01, -2.84214795e-01, -2.99140741e-03, -3.76546055e-01, + 5.97376414e-02, -1.91883057e-01, -1.34087920e-01, -3.23684871e-01, + ] + serialOscanMeansB = [ + 1.50365152e+01, 4.43178511e+00, 2.66550946e+00, 1.67382801e+00, 1.10997069e+00, + 8.89361799e-01, 4.66469795e-01, 6.10956728e-01, 6.67343795e-01, 5.22854805e-01, + -1.15006611e-01, 2.67710119e-01, 2.05686077e-01, 1.84541523e-01, 8.65717679e-02, + 5.51738311e-03, 2.35288814e-01, 3.45944524e-01, 7.81139359e-02, 1.52119964e-01, + 2.02162191e-01, 3.44150960e-02, -2.86277920e-01, 1.43662184e-01, 3.21276844e-01, + -6.21452965e-02, 8.58670697e-02, -1.63320359e-02, -1.07958235e-01, -1.60820082e-01, + -2.19705645e-02, -1.55181482e-01, -2.39055425e-01, -2.75705636e-01, 6.33126274e-02, + -5.50971478e-02, -2.42579415e-01, -9.87957790e-02, 1.08421087e-01, -1.12892322e-01, + 1.89090632e-02, -1.53086300e-03, -2.18615308e-01, -2.19320312e-01, 9.22102109e-02, + -4.87535410e-02, -1.81964979e-01, -4.17055413e-02, -4.24422681e-01, -1.96061105e-01, + -1.35127297e-02, -1.77031055e-01, -2.30597332e-01, -4.01868790e-01, -4.18784261e-01, + -3.75085384e-01, -3.49007100e-01, -1.77735761e-01, -7.41272718e-02, -1.92537069e-01, + 2.46565759e-01, -3.44777972e-01, -2.85573214e-01, -2.34121397e-01, + ] + serialOscanMeansC = [ + 0.212578110, 0.107817403, -0.122200218, -0.0089812368, -0.067990060, 0.040077099, + -0.021402006, 0.090923088, -0.099587158, 0.274797124, -0.016930788, 0.045007070, + -0.00379911056, -0.16088248, 0.055911896, 0.0601755001, -0.046872945, 0.210018355, + 0.081641635, -0.046147249, -0.0059020276, 0.108368757, -0.033966731, -0.0058644798, + -0.075746922, -0.203826510, 0.12620401, -0.0156685544, -0.09631182, 0.089754454, + 0.03789926, 0.0304515115, -0.082173715, -0.061332140, -0.24894494, -0.155137551, + -0.073825312, 0.24538413, -0.069597074, 0.192338801, -0.0539746876, -0.184556000, + -0.173069382, -0.209975778, 0.086679191, 0.016299034, -0.0094125706, -0.100099911, + 0.061981365, 0.086250364, 0.209128404, -0.0067993622, 0.171072270, -0.29266333, + 0.075172274, -0.29375612, -0.13377650, 0.0125964781, -0.124991264, 0.226516831, + 0.128244484, -0.05019844, -0.149249925, -0.1557398, + ] + serialOscanMeansD = [ + 4.0867248, 1.43194193, 0.95319573, 0.43219185, 0.53112239, 0.28648, 0.323903486, + 0.27622156, 0.26031138, 0.144442975, 0.0149878587, 0.062969929, 0.018541051, + -0.237687056, 0.22804558, 0.0600504708, 0.140250022, -0.137477808, 0.119911710, + 0.03770870, -0.20021377, 0.187175400, 0.0168790129, -0.110724371, 0.099311580, + 0.0079969534, -0.157593577, -0.178876067, -0.214948580, -0.11354382, 0.148154530, + -0.056012520, 0.11851939, 0.067902033, 0.18970736, -0.181487703, -0.0101017127, + 0.100998570, -0.0309096733, -0.034450136, -0.066357072, -0.058662959, 0.146185921, + -0.218474021, -0.173691633, 0.055349625, -0.178158524, -0.012917378, -0.166576555, + -0.063862754, 0.113169933, -0.33518338, -0.074239500, 0.22262230, -0.066653975, + -0.200271016, -0.013275277, 0.100596499, -0.092528954, 0.0339541714, 0.113119135, + -0.150720824, 0.038237873, 0.17603852613429813, + ] + parallelOscanMeans = [ + 7.18039751e-01, 4.56550479e-01, 4.14261669e-01, 2.88099229e-01, -2.34310962e-02, + -4.59854975e-02, -1.14491098e-02, 5.19846082e-02, 2.05635265e-01, 1.25147207e-02, + ] self.inputMeasurements = [ {'CTI': {'C:0,0': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,1': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,2': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,3': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,0': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,1': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,2': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,3': {'FIRST_COLUMN_MEAN': 117.810165, 'LAST_COLUMN_MEAN': 1.09791130e+02, - 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': overscanMeansA, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}, + 'IMAGE_MEAN': 117.810165, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansA, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}}}, + {'CTI': {'C:0,0': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,1': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,2': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,3': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,0': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,1': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,2': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,3': {'FIRST_COLUMN_MEAN': 36562.082, 'LAST_COLUMN_MEAN': 3.45901172e+04, - 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': overscanMeansB, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}, + 'IMAGE_MEAN': 36562.082, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansB, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}}}, {'CTI': {'C:0,0': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,1': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,2': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,3': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,0': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,1': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,2': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,3': {'FIRST_COLUMN_MEAN': 994.811, 'LAST_COLUMN_MEAN': 936.415, - 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': overscanMeansC, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}, + 'IMAGE_MEAN': 994.811, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansC, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}}}, + {'CTI': {'C:0,0': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,1': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,2': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:0,3': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,0': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,1': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,2': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}, + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}, 'C:1,3': {'FIRST_COLUMN_MEAN': 12215.778, 'LAST_COLUMN_MEAN': 11536.875, - 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': overscanMeansD, - 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)]}}}] + 'IMAGE_MEAN': 12215.778, 'SERIAL_OVERSCAN_VALUES': serialOscanMeansD, + 'SERIAL_OVERSCAN_COLUMNS': [x for x in range(0, 64)], + 'PARALLEL_OVERSCAN_VALUES': parallelOscanMeans, + 'PARALLEL_OVERSCAN_ROWS': [x for x in range(0, 10)]}}}] self.inputDims = [{'detector': 20, 'instrument': 'IsrMock', 'exposure': 2019101200433}, {'detector': 20, 'instrument': 'IsrMock', 'exposure': 2019101300154}, {'detector': 20, 'instrument': 'IsrMock', 'exposure': 2019101300004}, From f59120fa1abf46eb59fadb9d53a25ee8d9bb5134 Mon Sep 17 00:00:00 2001 From: Alex Broughton Date: Tue, 5 Nov 2024 18:14:31 -0800 Subject: [PATCH 4/6] Add serial and parallel CTI turnoffs to DeferredChargeCalib --- python/lsst/cp/pipe/cpCtiSolve.py | 94 ++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/python/lsst/cp/pipe/cpCtiSolve.py b/python/lsst/cp/pipe/cpCtiSolve.py index 78e46937..2fdd895f 100644 --- a/python/lsst/cp/pipe/cpCtiSolve.py +++ b/python/lsst/cp/pipe/cpCtiSolve.py @@ -35,6 +35,7 @@ SimulatedModel, SerialTrap) from lmfit import Minimizer, Parameters +from astropy.stats import sigma_clip class CpCtiSolveConnections(pipeBase.PipelineTaskConnections, @@ -89,6 +90,16 @@ class CpCtiSolveConfig(pipeBase.PipelineTaskConfig, default=10000.0, doc="Upper flux limit to use for CTI fit (electron).", ) + serialCtiRange = pexConfig.ListField( + dtype=float, + default=[-1.0e-6, 3.0e-6], + doc="Serial CTI range within containing serial turnoff.", + ) + parallelCtiRange = pexConfig.ListField( + dtype=float, + default=[-2.0e-6, 2.0e-6], + doc="Parallel CTI range within containing serial turnoff.", + ) globalCtiColumnRange = pexConfig.ListField( dtype=int, default=[1, 2], @@ -429,9 +440,24 @@ def solveGlobalCti(self, inputMeasurements, calib, detector): amp, ) + # Calculate the serial and parallel turnoffs + serialCtiTurnoff = self.calcTurnoff( + signals, + serialEperEstimate, + self.config.serialCtiRange, + ) + parallelCtiTurnoff = self.calcTurnoff( + signals, + parallelEperEstimate, + self.config.parallelCtiRange, + ) + + # Save everything to the DeferredChargeCalib calib.signals[ampName] = signals calib.serialEper[ampName] = serialEperEstimate calib.parallelEper[ampName] = parallelEperEstimate + calib.serialCtiTurnoff[ampName] = serialCtiTurnoff + calib.parallelCtiTurnoff[ampName] = parallelCtiTurnoff return calib @@ -687,7 +713,7 @@ def calcEper(self, mode, inputMeasurements, amp): signal = [] data = [] Nskipped = 0 - for idx, exposureEntry in enumerate(inputMeasurements): + for exposureEntry in inputMeasurements: exposureDict = exposureEntry['CTI'] if exposureDict[ampName]['IMAGE_MEAN'] < self.config.maxSignalForCti: signal.append(exposureDict[ampName]['IMAGE_MEAN']) @@ -712,3 +738,69 @@ def calcEper(self, mode, inputMeasurements, amp): ctiEstimate = (np.array(overscan1) + np.array(overscan2))/(nShifts*np.array(signal)) return signal, data, start, stop, ctiEstimate + + def calcTurnoff(self, signalVec, dataVec, ctiRange): + """Solve for turnoff value in a sequenced dataset. + + + Parameters + ---------- + signalVec : `np.ndarray` + Signal values for the dataset. Must be sorted + in ascending order. + dataVec : `np.ndarray` + Data values for the dataset. Must be sorted + in ascending order. + ctiRange : `list` [`float`] + Range of CTI within which to search for the + turnoff point. + + Returns + ------- + turnoff : `float` + the turnoff point in the same units as the + input signals + + Notes + ------ + If the data is sparse and does not cover the turnoff region, + it will likely just return the highest signal in the dataset. + + However, it will issue a warning if the turnoff point is at + the edge of the search range. + """ + # First, trim the data + idxs = (dataVec >= ctiRange[0]) * (dataVec <= ctiRange[1]) + dataVec = dataVec[idxs] + signalVec = signalVec[idxs] + + # Detrend the data + # We will use np.gradient since this method of + # detrending turns out to be more practical + # than using np.diff, which tends to be noisier. + # Besides, this tends to filter out the low + # gradient features of the data, particularly + # in the parallel turnoff. + detrendedDataVec = np.gradient(dataVec) + + # Sigma clip the data to remove the + # turnoff points + cleanDataVecMaArray = sigma_clip( + detrendedDataVec, + sigma=self.config.turnoffFinderSigmaClip, + maxiters=self.config.turnoffFinderSigmaClipMaxIters, + cenfunc=np.nanmedian, + stdfunc=np.nanstd, + masked=True, + ) + + # Retrieve the result + good = ~np.ma.getmask(cleanDataVecMaArray) + cleanDataVec = np.ma.getdata(cleanDataVecMaArray) + turnoff = np.max(signalVec[good]) + + if cleanDataVec[-1] in ctiRange: + self.log.warning("Turnoff point is at the edge of the data range. Setting " + "turnoff to the maximum signal value.") + + return turnoff From f2cd26b784801a8b0de2e21cc1bf069b447d96d9 Mon Sep 17 00:00:00 2001 From: Alex Broughton Date: Wed, 6 Nov 2024 10:20:09 -0800 Subject: [PATCH 5/6] Add turnoff sampling error measurements to DeferredChargeCalib --- python/lsst/cp/pipe/cpCtiSolve.py | 56 +++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/python/lsst/cp/pipe/cpCtiSolve.py b/python/lsst/cp/pipe/cpCtiSolve.py index 2fdd895f..76346176 100644 --- a/python/lsst/cp/pipe/cpCtiSolve.py +++ b/python/lsst/cp/pipe/cpCtiSolve.py @@ -441,23 +441,33 @@ def solveGlobalCti(self, inputMeasurements, calib, detector): ) # Calculate the serial and parallel turnoffs - serialCtiTurnoff = self.calcTurnoff( + serialCtiTurnoff, serialCtiTurnoffSamplingErr = self.calcTurnoff( signals, serialEperEstimate, self.config.serialCtiRange, + amp, ) - parallelCtiTurnoff = self.calcTurnoff( + parallelCtiTurnoff, parallelCtiTurnoffSamplingErr = self.calcTurnoff( signals, parallelEperEstimate, self.config.parallelCtiRange, + amp, ) + # Output the results + self.log.info("Amplifier %s: serial CTI turnoff is %f +/- %f", + amp.getName(), serialCtiTurnoff, serialCtiTurnoffSamplingErr) + self.log.info("Amplifier %s: parallel CTI turnoff is %f +/- %f", + amp.getName(), parallelCtiTurnoff, parallelCtiTurnoffSamplingErr) + # Save everything to the DeferredChargeCalib calib.signals[ampName] = signals calib.serialEper[ampName] = serialEperEstimate calib.parallelEper[ampName] = parallelEperEstimate calib.serialCtiTurnoff[ampName] = serialCtiTurnoff calib.parallelCtiTurnoff[ampName] = parallelCtiTurnoff + calib.serialCtiTurnoffSamplingErr[ampName] = serialCtiTurnoffSamplingErr + calib.parallelCtiTurnoffSamplingErr[ampName] = parallelCtiTurnoffSamplingErr return calib @@ -739,7 +749,7 @@ def calcEper(self, mode, inputMeasurements, amp): return signal, data, start, stop, ctiEstimate - def calcTurnoff(self, signalVec, dataVec, ctiRange): + def calcTurnoff(self, signalVec, dataVec, ctiRange, amp): """Solve for turnoff value in a sequenced dataset. @@ -774,6 +784,18 @@ def calcTurnoff(self, signalVec, dataVec, ctiRange): dataVec = dataVec[idxs] signalVec = signalVec[idxs] + if dataVec.size < 2: + if dataVec.size == 0: + self.log.warning("No data points after cti range cut to compute turnoff " + f" for amplifier {amp.getName()}. Setting turnoff point " + "to 0 el.") + return 0.0, 0.0 + else: + self.log.warning("Insufficient data points after cti range cut to compute turnoff " + f" for amplifier {amp.getName()}. Setting turnoff point " + "to the maximum signal value.") + return signalVec[-1], signalVec[-1] + # Detrend the data # We will use np.gradient since this method of # detrending turns out to be more practical @@ -797,10 +819,30 @@ def calcTurnoff(self, signalVec, dataVec, ctiRange): # Retrieve the result good = ~np.ma.getmask(cleanDataVecMaArray) cleanDataVec = np.ma.getdata(cleanDataVecMaArray) + + if cleanDataVec.size == 0: + self.log.warning("No data points after sigma clipping to compute turnoff " + f" for amplifier {amp.getName()}. Setting turnoff point " + "to 0 el.") + return 0.0, 0.0 + + turnoffIdx = np.argwhere(good)[-1] turnoff = np.max(signalVec[good]) - if cleanDataVec[-1] in ctiRange: - self.log.warning("Turnoff point is at the edge of the data range. Setting " - "turnoff to the maximum signal value.") + if cleanDataVec[-1] in ctiRange or turnoffIdx in [0, len(signalVec)-1]: + self.log.warning("Turnoff point is at the edge of the allowed range for " + f"amplifier {amp.getName()}.") + + # Compute the sampliing error as one half the + # difference between the previous and next point. + # Or, if it is the last index, just compute the + # interval. + + if turnoffIdx == len(signalVec) - 1: + samplingError = signalVec[turnoffIdx-1] - signalVec[turnoffIdx] + elif turnoffIdx == 0: + samplingError = signalVec[turnoffIdx] + else: + samplingError = (signalVec[turnoffIdx+1] - signalVec[turnoffIdx-1]) / 2.0 - return turnoff + return turnoff, samplingError From fb737ecc35b74502fd88b7bda0a32b7fb4e4d301 Mon Sep 17 00:00:00 2001 From: Alex Broughton Date: Wed, 6 Nov 2024 10:27:24 -0800 Subject: [PATCH 6/6] Check for remaining points after cuts in CTI turnoff calculation --- python/lsst/cp/pipe/cpCtiSolve.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/lsst/cp/pipe/cpCtiSolve.py b/python/lsst/cp/pipe/cpCtiSolve.py index 76346176..4747ee88 100644 --- a/python/lsst/cp/pipe/cpCtiSolve.py +++ b/python/lsst/cp/pipe/cpCtiSolve.py @@ -455,9 +455,9 @@ def solveGlobalCti(self, inputMeasurements, calib, detector): ) # Output the results - self.log.info("Amplifier %s: serial CTI turnoff is %f +/- %f", + self.log.info("Amp %s: Setting serial CTI turnoff is %f +/- %f", amp.getName(), serialCtiTurnoff, serialCtiTurnoffSamplingErr) - self.log.info("Amplifier %s: parallel CTI turnoff is %f +/- %f", + self.log.info("Amp %s: Setting parallel CTI turnoff is %f +/- %f", amp.getName(), parallelCtiTurnoff, parallelCtiTurnoffSamplingErr) # Save everything to the DeferredChargeCalib