169 def run(self, inputPtc, dummy, camera, inputDims):
170 """Combine covariance information from PTC into brighter-fatter kernels.
174 inputPtc : `lsst.ip.isr.PhotonTransferCurveDataset`
175 PTC data containing per-amplifier covariance measurements.
176 dummy : `lsst.afw.image.Exposure`
177 The exposure used to select the appropriate PTC dataset.
178 In almost all circumstances, one of the input exposures
179 used to generate the PTC dataset is the best option.
180 camera : `lsst.afw.cameraGeom.Camera`
181 Camera to use for camera geometry information.
182 inputDims : `lsst.daf.butler.DataCoordinate` or `dict`
183 DataIds to use to populate the output calibration.
187 results : `lsst.pipe.base.Struct`
188 The resulst struct containing:
190 ``outputBfk`` : `lsst.ip.isr.BrighterFatterKernel`
191 Resulting Brighter-Fatter Kernel.
194 self.log.
warn(
"No dummy exposure found.")
196 detector = camera[inputDims[
'detector']]
197 detName = detector.getName()
199 if self.config.level ==
'DETECTOR':
200 detectorCorrList =
list()
202 bfk = BrighterFatterKernel(camera=camera, detectorId=detector.getId(), level=self.config.level)
203 bfk.means = inputPtc.finalMeans
204 bfk.variances = inputPtc.finalVars
208 bfk.rawXcorrs = inputPtc.covariances
209 bfk.badAmps = inputPtc.badAmps
210 bfk.shape = (inputPtc.covMatrixSide*2 + 1, inputPtc.covMatrixSide*2 + 1)
211 bfk.gain = inputPtc.gain
212 bfk.noise = inputPtc.noise
213 bfk.meanXcorrs = dict()
217 ampName = amp.getName()
218 mask = np.array(inputPtc.expIdMask[ampName], dtype=bool)
220 gain = bfk.gain[ampName]
221 fluxes = np.array(bfk.means[ampName])[mask]
222 variances = np.array(bfk.variances[ampName])[mask]
223 xCorrList = [np.array(xcorr)
for xcorr
in bfk.rawXcorrs[ampName]]
224 xCorrList = np.array(xCorrList)[mask]
228 self.log.
warn(
"Impossible gain recieved from PTC for %s: %f. Skipping amplifier.",
230 bfk.meanXcorrs[ampName] = np.zeros(bfk.shape)
231 bfk.ampKernels[ampName] = np.zeros(bfk.shape)
232 bfk.valid[ampName] =
False
235 fluxes = np.array([flux*gain
for flux
in fluxes])
236 variances = np.array([variance*gain*gain
for variance
in variances])
239 scaledCorrList =
list()
240 for xcorrNum, (xcorr, flux, var)
in enumerate(zip(xCorrList, fluxes, variances), 1):
241 q = np.array(xcorr) * gain * gain
243 self.log.
info(
"Amp: %s %d/%d Flux: %f Var: %f Q(0,0): %g Q(1,0): %g Q(0,1): %g",
244 ampName, xcorrNum, len(xCorrList), flux, var, q[0][0], q[1][0], q[0][1])
249 q[0][0] -= 2.0*(flux)
252 self.log.
warn(
"Amp: %s %d skipped due to value of (variance-mean)=%f",
253 ampName, xcorrNum, q[0][0])
258 scaled = self._tileArray(q)
260 xcorrCheck = np.abs(np.sum(scaled))/np.sum(np.abs(scaled))
261 if (xcorrCheck > self.config.xcorrCheckRejectLevel)
or not (np.isfinite(xcorrCheck)):
262 self.log.
warn(
"Amp: %s %d skipped due to value of triangle-inequality sum %f",
263 ampName, xcorrNum, xcorrCheck)
266 scaledCorrList.append(scaled)
267 self.log.
info(
"Amp: %s %d/%d Final: %g XcorrCheck: %f",
268 ampName, xcorrNum, len(xCorrList), q[0][0], xcorrCheck)
270 if len(scaledCorrList) == 0:
271 self.log.
warn(
"Amp: %s All inputs rejected for amp!", ampName)
272 bfk.meanXcorrs[ampName] = np.zeros(bfk.shape)
273 bfk.ampKernels[ampName] = np.zeros(bfk.shape)
274 bfk.valid[ampName] =
False
277 if self.config.useAmatrix:
279 preKernel = np.pad(self._tileArray(np.array(inputPtc.aMatrix[ampName])), ((1, 1)))
280 elif self.config.correlationQuadraticFit:
282 preKernel = self.quadraticCorrelations(scaledCorrList, fluxes, f
"Amp: {ampName}")
285 preKernel = self.averageCorrelations(scaledCorrList, f
"Amp: {ampName}")
287 center = int((bfk.shape[0] - 1) / 2)
289 if self.config.forceZeroSum:
290 totalSum = np.sum(preKernel)
292 if self.config.correlationModelRadius < (preKernel.shape[0] - 1) / 2:
294 preFactor = np.sqrt(preKernel[center, center + 1] * preKernel[center + 1, center])
295 slopeFactor = 2.0 * np.abs(self.config.correlationModelSlope)
296 totalSum += 2.0*np.pi*(preFactor / (slopeFactor*(center + 0.5))**slopeFactor)
298 preKernel[center, center] -= totalSum
299 self.log.
info(
"%s Zero-Sum Scale: %g", ampName, totalSum)
301 finalSum = np.sum(preKernel)
302 bfk.meanXcorrs[ampName] = preKernel
304 postKernel = self.successiveOverRelax(preKernel)
305 bfk.ampKernels[ampName] = postKernel
306 if self.config.level ==
'DETECTOR':
307 detectorCorrList.extend(scaledCorrList)
308 bfk.valid[ampName] =
True
309 self.log.
info(
"Amp: %s Sum: %g Center Info Pre: %g Post: %g",
310 ampName, finalSum, preKernel[center, center], postKernel[center, center])
313 if self.config.level ==
'DETECTOR':
314 preKernel = self.averageCorrelations(detectorCorrList, f
"Det: {detName}")
315 finalSum = np.sum(preKernel)
316 center = int((bfk.shape[0] - 1) / 2)
318 postKernel = self.successiveOverRelax(preKernel)
319 bfk.detKernels[detName] = postKernel
320 self.log.
info(
"Det: %s Sum: %g Center Info Pre: %g Post: %g",
321 detName, finalSum, preKernel[center, center], postKernel[center, center])
323 return pipeBase.Struct(
daf::base::PropertyList * list
def run(self, skyInfo, tempExpRefList, imageScalerList, weightList, altMaskList=None, mask=None, supplementaryData=None)