Skip to content

Commit

Permalink
Enforce gain usage in CTI construction and application.
Browse files Browse the repository at this point in the history
  • Loading branch information
czwa committed Nov 16, 2023
1 parent 5692000 commit cd5ac5b
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 5 deletions.
18 changes: 15 additions & 3 deletions python/lsst/ip/isr/deferredCharge.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,17 @@ class DeferredChargeCalib(IsrCalib):
_SCHEMA = 'Deferred Charge'
_VERSION = 1.0

def __init__(self, **kwargs):
def __init__(self, useGains=True, **kwargs):
self.driftScale = {}
self.decayTime = {}
self.globalCti = {}
self.serialTraps = {}

super().__init__(**kwargs)

units = 'electrons' if useGains else 'ADU'
self.updateMetadata(USEGAINS=useGains, UNITS=units)

self.requiredAttributes.update(['driftScale', 'decayTime', 'globalCti', 'serialTraps'])

def fromDetector(self, detector):
Expand Down Expand Up @@ -560,11 +564,19 @@ def run(self, exposure, ctiCalib, gains=None):
# true, but no external gains were supplied, use the nominal
# gains listed in the detector. Finally, if useGains is
# false, fake a dictionary of unit gains for ``gainContext``.
if self.config.useGains:
useGains = True
if "USEGAINS" in ctiCalib.getMetadata().keys():
useGains = ctiCalib.getMetadata()["USEGAINS"]
self.log.info(f"useGains = {useGains} due to calibration")
else:
useGains = self.config.useGains
self.log.info(f"useGains = {useGains} due to config")

if useGains:
if gains is None:
gains = {amp.getName(): amp.getGain() for amp in detector.getAmplifiers()}

with gainContext(exposure, image, self.config.useGains, gains):
with gainContext(exposure, image, useGains, gains):
for amp in detector.getAmplifiers():
ampName = amp.getName()

Expand Down
10 changes: 9 additions & 1 deletion python/lsst/ip/isr/isrStatistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class IsrStatisticsTaskConfig(pexConfig.Config):
doc="Measure CTI statistics from image and overscans?",
default=False,
)
doApplyGainsForCtiStatistics = pexConfig.Field(
dtype=bool,
doc="Apply gain to the overscan region when measuring CTI statistics?",
default=True,
)

doBandingStatistics = pexConfig.Field(
dtype=bool,
Expand Down Expand Up @@ -283,7 +288,10 @@ def measureCti(self, inputExp, overscans, gains):
osMean = afwMath.makeStatistics(overscanImage.image.array[:, column],
self.statType, self.statControl).getValue()
columns.append(column)
values.append(gain * osMean)
if self.config.doApplyGainsForCtiStatistics:
values.append(gain * osMean)
else:
values.append(osMean)

# We want these relative to the readout corner. If that's
# on the right side, we need to swap them.
Expand Down
3 changes: 3 additions & 0 deletions python/lsst/ip/isr/isrTask.py
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,9 @@ def validate(self):
self.maskListToInterpolate.remove(self.saturatedMaskName)
if self.doNanInterpolation and "UNMASKEDNAN" not in self.maskListToInterpolate:
self.maskListToInterpolate.append("UNMASKEDNAN")
if self.doCalculateStatistics and self.isrStats.doCtiStatistics:
if self.doApplyGains != self.isrStats.doApplyGainsForCtiStatistics:
raise ValueError("doApplyGains must match isrStats.applyGainForCtiStatistics.")


class IsrTask(pipeBase.PipelineTask):
Expand Down
29 changes: 28 additions & 1 deletion tests/test_deferredCharge.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
import tempfile
import lsst.utils.tests

from lsst.ip.isr import DeferredChargeCalib, DeferredChargeTask, SerialTrap
from lsst.ip.isr import (DeferredChargeCalib, DeferredChargeTask, SerialTrap,
IsrTaskConfig)


class SerialTrapTestCase(lsst.utils.tests.TestCase):
Expand Down Expand Up @@ -135,6 +136,32 @@ def testTaskMethods(self):
# As above + ~320 deposited in prescan
self.assertAlmostEqual(np.sum(corrected), 821733.2317048, 5)

def testConfigLogic(self):
config = IsrTaskConfig()
config.doFlat = False
config.doCalculateStatistics = True
config.isrStats.doCtiStatistics = True

# Confirm that image and overscan are treated the same.
config.doApplyGains = True
config.isrStats.doApplyGainsForCtiStatistics = False
with self.assertRaises(ValueError):
config.validate()

config.doApplyGains = False
config.isrStats.doApplyGainsForCtiStatistics = True
with self.assertRaises(ValueError):
config.validate()

# these should not raise
config.doApplyGains = True
config.isrStats.doApplyGainsForCtiStatistics = True
config.validate()

config.doApplyGains = False
config.isrStats.doApplyGainsForCtiStatistics = False
config.validate()


class MemoryTester(lsst.utils.tests.MemoryTestCase):
pass
Expand Down

0 comments on commit cd5ac5b

Please sign in to comment.