@@ -166,12 +166,27 @@ class BrighterFatterKernelSolveConfig(pipeBase.PipelineTaskConfig,
166
166
doc = "Slope of the correlation model for radii larger than correlationModelRadius" ,
167
167
default = - 1.35 ,
168
168
)
169
+
170
+ nSigmaTolForValidEdge = pexConfig .Field (
171
+ dtype = float ,
172
+ doc = "A valid kernel will have all pixels in a 3px picture frame (edges) "
173
+ "be within +/- nSigmaTolForValidEdge of zero." ,
174
+ default = 5.0 ,
175
+ )
176
+
169
177
useBfkPtc = pexConfig .Field (
170
178
dtype = bool ,
171
179
doc = "Use a BFK ptc in a single pipeline?" ,
172
180
default = False ,
173
181
)
174
182
183
+ doCheckValidity = pexConfig .Field (
184
+ dtype = bool ,
185
+ doc = "Check the AMP kernels for basic validity criteria? "
186
+ "Will set bfk.valid for each amp." ,
187
+ default = True ,
188
+ )
189
+
175
190
176
191
class BrighterFatterKernelSolveTask (pipeBase .PipelineTask ):
177
192
"""Measure appropriate Brighter-Fatter Kernel from the PTC dataset.
@@ -415,7 +430,52 @@ def run(self, inputPtc, dummy, camera, inputDims):
415
430
if self .config .level == 'DETECTOR' and ampName not in self .config .ignoreAmpsForAveraging :
416
431
detectorCorrList .extend (scaledCorrList )
417
432
detectorFluxes .extend (fluxes )
418
- bfk .valid [ampName ] = True
433
+
434
+ # Check for validity
435
+ if self .config .doCheckValidity :
436
+ kernelSize = bfk .ampKernels [ampName ].shape [0 ]
437
+ kernelCenterValue = bfk .ampKernels [ampName ][kernelSize // 2 , kernelSize // 2 ]
438
+ xv , yv = np .meshgrid (range (kernelSize ), range (kernelSize ))
439
+
440
+ # Get a mask for a 3px picture frame
441
+ valid = True
442
+ if kernelSize >= 7 :
443
+ edges = (xv < 3 )
444
+ edges += (xv > kernelSize - 3 - 1 )
445
+ edges += (yv < 3 )
446
+ edges += (yv > kernelSize - 3 - 1 )
447
+
448
+ kernelEdges = bfk .ampKernels [ampName ][edges ].ravel ()
449
+ kernelEdgeStd = np .std (kernelEdges )
450
+
451
+ threshold = self .config .nSigmaTolForValidEdge * kernelEdgeStd
452
+
453
+ # Edges should converge to zero
454
+ valid = np .all (np .isclose (
455
+ kernelEdges , 0 ,
456
+ rtol = threshold ,
457
+ ))
458
+ if not valid :
459
+ self .log .warning ("%s: the kernel edges did not converge to 0 within "
460
+ "+/- %s sigma." , ampName , threshold )
461
+
462
+ # Kernel should be negative
463
+ valid *= np .all (bfk .ampKernels [ampName ][~ edges ] <= threshold )
464
+ if not np .all (bfk .ampKernels [ampName ][~ edges ] <= threshold ):
465
+ self .log .warning ("%s: the kernel is not negative" , ampName )
466
+
467
+ # The center should be the minimum
468
+ valid *= np .all (bfk .ampKernels [ampName ] >= kernelCenterValue )
469
+ if not np .all (bfk .ampKernels [ampName ] >= kernelCenterValue ):
470
+ self .log .warning ("%s: the kernel center is not the absolute minimum." , ampName )
471
+ else :
472
+ raise RuntimeError ("%s: The kernel is too small for validity check." , ampName )
473
+
474
+ bfk .valid [ampName ] = valid
475
+ else :
476
+ # The kernel at this point will be valid
477
+ bfk .valid [ampName ] = True
478
+
419
479
self .log .info ("Amp: %s Sum: %g Center Info Pre: %g Post: %g" ,
420
480
ampName , finalSum , preKernel [center , center ], postKernel [center , center ])
421
481
0 commit comments