class IsrCalibCases(lsst.utils.tests.TestCase): """Test unified calibration type. """ def setUp(self): self.calib = IsrProvenance(detectorName='test_calibType Det00', detectorSerial='Det00', calibType="Test Calib") self.calib.updateMetadata() self.calib.fromDataIds([{'exposure': 1234, 'detector': 0, 'filter': 'G'}, {'exposure': 1235, 'detector': 0, 'filter': 'G'}, {'exposure': 1234, 'detector': 1, 'filter': 'G'}, {'exposure': 1235, 'detector': 1, 'filter': 'G'}]) def runText(self, textType): filename = tempfile.mktemp() usedFilename = self.calib.writeText(filename + textType) fromText = IsrProvenance.readText(usedFilename) self.assertEqual(self.calib, fromText) def test_Text(self): self.runText('.yaml') self.runText('.ecsv') def test_Fits(self): filename = tempfile.mktemp() usedFilename = self.calib.writeFits(filename + '.fits') fromFits = IsrProvenance.readFits(usedFilename) self.assertEqual(self.calib, fromFits) fromFits.updateMetadata(setDate=True) self.assertNotEqual(self.calib, fromFits)
def 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. """ if outputDims: calibChip = outputDims['detector'] instrument = outputDims['instrument'] else: # calibChip needs to be set manually in Gen2. calibChip = None instrument = None self.log.info("Combining measurements from %d ratios and %d fluxes", len(inputRatios), len(inputFluxes) if inputFluxes else 0) if inputFluxes is None: inputFluxes = [None for exp in inputRatios] combinedRatios = defaultdict(lambda: defaultdict(list)) combinedFluxes = defaultdict(lambda: defaultdict(list)) for ratioDict, fluxDict in zip(inputRatios, inputFluxes): for targetChip in ratioDict: if calibChip and targetChip != calibChip: raise RuntimeError("Received multiple target chips!") sourceChip = targetChip if sourceChip in ratioDict[targetChip]: ratios = ratioDict[targetChip][sourceChip] for targetAmp in ratios: for sourceAmp in ratios[targetAmp]: combinedRatios[targetAmp][sourceAmp].extend(ratios[targetAmp][sourceAmp]) if fluxDict: combinedFluxes[targetAmp][sourceAmp].extend(fluxDict[sourceChip][sourceAmp]) # TODO: DM-21904 # Iterating over all other entries in ratioDict[targetChip] will yield # inter-chip terms. for targetAmp in combinedRatios: for sourceAmp in combinedRatios[targetAmp]: self.log.info("Read %d pixels for %s -> %s", len(combinedRatios[targetAmp][sourceAmp]), targetAmp, sourceAmp) if len(combinedRatios[targetAmp][sourceAmp]) > 1: self.debugRatios('reduce', combinedRatios, targetAmp, sourceAmp) if self.config.fluxOrder == 0: self.log.info("Fitting crosstalk coefficients.") calib = self.measureCrosstalkCoefficients(combinedRatios, self.config.rejIter, self.config.rejSigma) else: raise NotImplementedError("Non-linear crosstalk terms are not yet supported.") self.log.info("Number of valid coefficients: %d", np.sum(calib.coeffValid)) if self.config.doFiltering: # This step will apply the calculated validity values to # censor poorly measured coefficients. self.log.info("Filtering measured crosstalk to remove invalid solutions.") calib = self.filterCrosstalkCalib(calib) # Populate the remainder of the calibration information. calib.hasCrosstalk = True calib.interChip = {} # calibChip is the detector dimension, which is the detector Id calib._detectorId = calibChip if camera: calib._detectorName = camera[calibChip].getName() calib._detectorSerial = camera[calibChip].getSerial() calib._instrument = instrument calib.updateMetadata(setCalibId=True, setDate=True) # Make an IsrProvenance(). provenance = IsrProvenance(calibType="CROSSTALK") provenance._detectorName = calibChip if inputDims: provenance.fromDataIds(inputDims) provenance._instrument = instrument provenance.updateMetadata() return pipeBase.Struct( outputCrosstalk=calib, outputProvenance=provenance, )