LSST Applications g00d0e8bbd7+edbf708997,g199a45376c+5137f08352,g1fd858c14a+48cd4dd530,g228ff663f5+2051e4e242,g262e1987ae+9c6f24d2e3,g29ae962dfc+03663621e0,g2cef7863aa+73c82f25e4,g35bb328faa+edbf708997,g3fd5ace14f+8c4d25a1ce,g47891489e3+27ba970c8a,g53246c7159+edbf708997,g5b326b94bb+db962c32ee,g64539dfbff+d237af7fd9,g67b6fd64d1+27ba970c8a,g74acd417e5+8234f56c0c,g786e29fd12+af89c03590,g87389fa792+a4172ec7da,g88cb488625+6878ed1c5e,g89139ef638+27ba970c8a,g8d7436a09f+f76ea57dde,g8ea07a8fe4+79658f16ab,g90f42f885a+6577634e1f,g97be763408+494f77a6c4,g98df359435+1750ea0126,g9b50b81019+d8f85438e7,ga2180abaac+edbf708997,ga9e74d7ce9+128cc68277,gad4c79568f+321c5e11c3,gbf99507273+edbf708997,gc2a301910b+d237af7fd9,gca7fc764a6+27ba970c8a,gcedae5159b+afaec0eb3d,gd7ef33dd92+27ba970c8a,gdab6d2f7ff+8234f56c0c,gdbb4c4dda9+d237af7fd9,ge410e46f29+27ba970c8a,ge41e95a9f2+d237af7fd9,geaed405ab2+062dfc8cdc,w.2025.45
LSST Data Management Base Package
Loading...
Searching...
No Matches
lsst.meas.extensions.piff.piffPsfDeterminer Namespace Reference

Classes

class  PiffPsfDeterminerConfig
 
class  PiffPsfDeterminerTask
 
class  PiffTooFewGoodStarsError
 

Functions

bool _validateGalsimInterpolant (str name)
 
 getGoodPixels (maskedImage, zeroWeightMaskBits)
 
 computeWeight (maskedImage, maxSNR, good)
 
 applyMaxSNR (imArr, weightArr, good, maxSNR)
 
 _computeWeightAlternative (maskedImage, maxSNR)
 

Function Documentation

◆ _computeWeightAlternative()

lsst.meas.extensions.piff.piffPsfDeterminer._computeWeightAlternative ( maskedImage,
maxSNR )
protected
Alternative algorithm for creating weight map.

This version is equivalent to that used by Piff internally.  The weight map
it produces tends to leave a residual when removing the Poisson component
due to the signal.  We leave it here as a reference, but without intending
that it be used (or be maintained).

Definition at line 389 of file piffPsfDeterminer.py.

389def _computeWeightAlternative(maskedImage, maxSNR):
390 """Alternative algorithm for creating weight map.
391
392 This version is equivalent to that used by Piff internally. The weight map
393 it produces tends to leave a residual when removing the Poisson component
394 due to the signal. We leave it here as a reference, but without intending
395 that it be used (or be maintained).
396 """
397 imArr = maskedImage.image.array
398 varArr = maskedImage.variance.array
399 good = (varArr != 0) & np.isfinite(varArr) & np.isfinite(imArr)
400
401 fit = np.polyfit(imArr[good], varArr[good], deg=1)
402 # fit is [1/gain, sky_var]
403 gain = 1./fit[0]
404 varArr[good] -= imArr[good] / gain
405 weightArr = np.zeros_like(imArr, dtype=float)
406 weightArr[good] = 1./varArr[good]
407
408 applyMaxSNR(imArr, weightArr, good, maxSNR)
409 return weightArr
410
411

◆ _validateGalsimInterpolant()

bool lsst.meas.extensions.piff.piffPsfDeterminer._validateGalsimInterpolant ( str name)
protected
A helper function to validate the GalSim interpolant at config time.

Parameters
----------
name : str
    The name of the interpolant to use from GalSim.  Valid options are:
        galsim.Lanczos(N) or Lancsos(N), where N is a positive integer
        galsim.Linear
        galsim.Cubic
        galsim.Quintic
        galsim.Delta
        galsim.Nearest
        galsim.SincInterpolant

Returns
-------
is_valid : bool
    Whether the provided interpolant name is valid.

Definition at line 41 of file piffPsfDeterminer.py.

41def _validateGalsimInterpolant(name: str) -> bool:
42 """A helper function to validate the GalSim interpolant at config time.
43
44 Parameters
45 ----------
46 name : str
47 The name of the interpolant to use from GalSim. Valid options are:
48 galsim.Lanczos(N) or Lancsos(N), where N is a positive integer
49 galsim.Linear
50 galsim.Cubic
51 galsim.Quintic
52 galsim.Delta
53 galsim.Nearest
54 galsim.SincInterpolant
55
56 Returns
57 -------
58 is_valid : bool
59 Whether the provided interpolant name is valid.
60 """
61 # First, check if ``name`` is a valid Lanczos interpolant.
62 for pattern in (re.compile(r"Lanczos\‍(\d+\‍)"), re.compile(r"galsim.Lanczos\‍(\d+\‍)"),):
63 match = re.match(pattern, name) # Search from the start of the string.
64 if match is not None:
65 # Check that the pattern is also the end of the string.
66 return match.end() == len(name)
67
68 # If not, check if ``name`` is any other valid GalSim interpolant.
69 names = {f"galsim.{interp}" for interp in
70 ("Cubic", "Delta", "Linear", "Nearest", "Quintic", "SincInterpolant")
71 }
72 return name in names
73
74

◆ applyMaxSNR()

lsst.meas.extensions.piff.piffPsfDeterminer.applyMaxSNR ( imArr,
weightArr,
good,
maxSNR )
Rescale weight of bright stars to cap the computed SNR.

Parameters
----------
imArr : `ndarray`
    Signal (image) array of stamp.
weightArr : `ndarray`
    Weight map array.  May be rescaled in place.
good : `ndarray`
    Index array of pixels to use when computing SNR.
maxSNR : `float`
    Threshold for adjusting variance plane implementing maximum SNR.

Definition at line 341 of file piffPsfDeterminer.py.

341def applyMaxSNR(imArr, weightArr, good, maxSNR):
342 """Rescale weight of bright stars to cap the computed SNR.
343
344 Parameters
345 ----------
346 imArr : `ndarray`
347 Signal (image) array of stamp.
348 weightArr : `ndarray`
349 Weight map array. May be rescaled in place.
350 good : `ndarray`
351 Index array of pixels to use when computing SNR.
352 maxSNR : `float`
353 Threshold for adjusting variance plane implementing maximum SNR.
354 """
355 # We define the SNR value following Piff. Here's the comment from that
356 # code base explaining the calculation.
357 #
358 # The S/N value that we use will be the weighted total flux where the
359 # weight function is the star's profile itself. This is the maximum S/N
360 # value that any flux measurement can possibly produce, which will be
361 # closer to an in-practice S/N than using all the pixels equally.
362 #
363 # F = Sum_i w_i I_i^2
364 # var(F) = Sum_i w_i^2 I_i^2 var(I_i)
365 # = Sum_i w_i I_i^2 <--- Assumes var(I_i) = 1/w_i
366 #
367 # S/N = F / sqrt(var(F))
368 #
369 # Note that if the image is pure noise, this will produce a "signal" of
370 #
371 # F_noise = Sum_i w_i 1/w_i = Npix
372 #
373 # So for a more accurate estimate of the S/N of the actual star itself, one
374 # should subtract off Npix from the measured F.
375 #
376 # The final formula then is:
377 #
378 # F = Sum_i w_i I_i^2
379 # S/N = (F-Npix) / sqrt(F)
380 F = np.sum(weightArr[good]*imArr[good]**2, dtype=float)
381 Npix = np.sum(good)
382 SNR = 0.0 if F < Npix else (F-Npix)/np.sqrt(F)
383 # rescale weight of bright stars. Essentially makes an error floor.
384 if SNR > maxSNR:
385 factor = (maxSNR / SNR)**2
386 weightArr[good] *= factor
387
388

◆ computeWeight()

lsst.meas.extensions.piff.piffPsfDeterminer.computeWeight ( maskedImage,
maxSNR,
good )
Derive a weight map without Poisson variance component due to signal.

Parameters
----------
maskedImage : `afw.image.MaskedImage`
    PSF candidate postage stamp
maxSNR : `float`
    Maximum SNR applying variance floor.
good : `ndarray`
    Index array indicating good pixels.

Returns
-------
weightArr : `ndarry`
    Array to use for weight.

See Also
--------
`lsst.meas.algorithms.variance_plance.remove_signal_from_variance` :
    Remove the Poisson contribution from sources in the variance plane of
    an Exposure.

Definition at line 303 of file piffPsfDeterminer.py.

303def computeWeight(maskedImage, maxSNR, good):
304 """Derive a weight map without Poisson variance component due to signal.
305
306 Parameters
307 ----------
308 maskedImage : `afw.image.MaskedImage`
309 PSF candidate postage stamp
310 maxSNR : `float`
311 Maximum SNR applying variance floor.
312 good : `ndarray`
313 Index array indicating good pixels.
314
315 Returns
316 -------
317 weightArr : `ndarry`
318 Array to use for weight.
319
320 See Also
321 --------
322 `lsst.meas.algorithms.variance_plance.remove_signal_from_variance` :
323 Remove the Poisson contribution from sources in the variance plane of
324 an Exposure.
325 """
326 imArr = maskedImage.image.array
327 varArr = maskedImage.variance.array
328
329 # Fit a straight line to variance vs (sky-subtracted) signal.
330 # The evaluate that line at zero signal to get an estimate of the
331 # signal-free variance.
332 fit = np.polyfit(imArr[good], varArr[good], deg=1)
333 # fit is [1/gain, sky_var]
334 weightArr = np.zeros_like(imArr, dtype=float)
335 weightArr[good] = 1./fit[1]
336
337 applyMaxSNR(imArr, weightArr, good, maxSNR)
338 return weightArr
339
340

◆ getGoodPixels()

lsst.meas.extensions.piff.piffPsfDeterminer.getGoodPixels ( maskedImage,
zeroWeightMaskBits )
Compute an index array indicating good pixels to use.

Parameters
----------
maskedImage : `afw.image.MaskedImage`
    PSF candidate postage stamp
zeroWeightMaskBits : `List[str]`
    List of mask bits for which to set pixel weights to zero.

Returns
-------
good : `ndarray`
    Index array indicating good pixels.

Definition at line 276 of file piffPsfDeterminer.py.

276def getGoodPixels(maskedImage, zeroWeightMaskBits):
277 """Compute an index array indicating good pixels to use.
278
279 Parameters
280 ----------
281 maskedImage : `afw.image.MaskedImage`
282 PSF candidate postage stamp
283 zeroWeightMaskBits : `List[str]`
284 List of mask bits for which to set pixel weights to zero.
285
286 Returns
287 -------
288 good : `ndarray`
289 Index array indicating good pixels.
290 """
291 imArr = maskedImage.image.array
292 varArr = maskedImage.variance.array
293 bitmask = maskedImage.mask.getPlaneBitMask(zeroWeightMaskBits)
294 good = (
295 (varArr != 0)
296 & (np.isfinite(varArr))
297 & (np.isfinite(imArr))
298 & ((maskedImage.mask.array & bitmask) == 0)
299 )
300 return good
301
302