LSST Applications g2079a07aa2+86d27d4dc4,g2305ad1205+a659bff248,g2bbee38e9b+3c60f8fe34,g337abbeb29+3c60f8fe34,g33d1c0ed96+3c60f8fe34,g3502564af9+d77d6d1350,g3a166c0a6a+3c60f8fe34,g487adcacf7+25d9892218,g4be5004598+d77d6d1350,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+4d81263f9a,g5cd07815a0+980d2b1c3b,g607f77f49a+d77d6d1350,g858d7b2824+d77d6d1350,g88963caddf+83e433e629,g99cad8db69+a4d3c48eeb,g9ddcbc5298+9a081db1e4,ga1e77700b3+bcf1af89ad,ga57fefb910+9a39d7b2d7,gae0086650b+585e252eca,gb065fddaf9+4f9fd82a2c,gb0e22166c9+60f28cb32d,gb363559e06+d84b1d3d07,gb3b7280ab2+4563d032e1,gb4b16eec92+babe958938,gba4ed39666+c2a2e4ac27,gbb8dafda3b+ed6854b564,gc120e1dc64+b72d212f87,gc28159a63d+3c60f8fe34,gc3e9b769f7+921dbcd359,gcf0d15dbbd+9a39d7b2d7,gdaeeff99f8+f9a426f77a,gddc38dedce+585e252eca,ge79ae78c31+3c60f8fe34,w.2024.21
LSST Data Management Base Package
Loading...
Searching...
No Matches
Classes | Functions
lsst.meas.extensions.piff.piffPsfDeterminer Namespace Reference

Classes

class  PiffPsfDeterminerConfig
 
class  PiffPsfDeterminerTask
 

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 283 of file piffPsfDeterminer.py.

283def _computeWeightAlternative(maskedImage, maxSNR):
284 """Alternative algorithm for creating weight map.
285
286 This version is equivalent to that used by Piff internally. The weight map
287 it produces tends to leave a residual when removing the Poisson component
288 due to the signal. We leave it here as a reference, but without intending
289 that it be used (or be maintained).
290 """
291 imArr = maskedImage.image.array
292 varArr = maskedImage.variance.array
293 good = (varArr != 0) & np.isfinite(varArr) & np.isfinite(imArr)
294
295 fit = np.polyfit(imArr[good], varArr[good], deg=1)
296 # fit is [1/gain, sky_var]
297 gain = 1./fit[0]
298 varArr[good] -= imArr[good] / gain
299 weightArr = np.zeros_like(imArr, dtype=float)
300 weightArr[good] = 1./varArr[good]
301
302 applyMaxSNR(imArr, weightArr, good, maxSNR)
303 return weightArr
304
305

◆ _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 39 of file piffPsfDeterminer.py.

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

◆ 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 235 of file piffPsfDeterminer.py.

235def applyMaxSNR(imArr, weightArr, good, maxSNR):
236 """Rescale weight of bright stars to cap the computed SNR.
237
238 Parameters
239 ----------
240 imArr : `ndarray`
241 Signal (image) array of stamp.
242 weightArr : `ndarray`
243 Weight map array. May be rescaled in place.
244 good : `ndarray`
245 Index array of pixels to use when computing SNR.
246 maxSNR : `float`
247 Threshold for adjusting variance plane implementing maximum SNR.
248 """
249 # We define the SNR value following Piff. Here's the comment from that
250 # code base explaining the calculation.
251 #
252 # The S/N value that we use will be the weighted total flux where the
253 # weight function is the star's profile itself. This is the maximum S/N
254 # value that any flux measurement can possibly produce, which will be
255 # closer to an in-practice S/N than using all the pixels equally.
256 #
257 # F = Sum_i w_i I_i^2
258 # var(F) = Sum_i w_i^2 I_i^2 var(I_i)
259 # = Sum_i w_i I_i^2 <--- Assumes var(I_i) = 1/w_i
260 #
261 # S/N = F / sqrt(var(F))
262 #
263 # Note that if the image is pure noise, this will produce a "signal" of
264 #
265 # F_noise = Sum_i w_i 1/w_i = Npix
266 #
267 # So for a more accurate estimate of the S/N of the actual star itself, one
268 # should subtract off Npix from the measured F.
269 #
270 # The final formula then is:
271 #
272 # F = Sum_i w_i I_i^2
273 # S/N = (F-Npix) / sqrt(F)
274 F = np.sum(weightArr[good]*imArr[good]**2, dtype=float)
275 Npix = np.sum(good)
276 SNR = 0.0 if F < Npix else (F-Npix)/np.sqrt(F)
277 # rescale weight of bright stars. Essentially makes an error floor.
278 if SNR > maxSNR:
279 factor = (maxSNR / SNR)**2
280 weightArr[good] *= factor
281
282

◆ 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 197 of file piffPsfDeterminer.py.

197def computeWeight(maskedImage, maxSNR, good):
198 """Derive a weight map without Poisson variance component due to signal.
199
200 Parameters
201 ----------
202 maskedImage : `afw.image.MaskedImage`
203 PSF candidate postage stamp
204 maxSNR : `float`
205 Maximum SNR applying variance floor.
206 good : `ndarray`
207 Index array indicating good pixels.
208
209 Returns
210 -------
211 weightArr : `ndarry`
212 Array to use for weight.
213
214 See Also
215 --------
216 `lsst.meas.algorithms.variance_plance.remove_signal_from_variance` :
217 Remove the Poisson contribution from sources in the variance plane of
218 an Exposure.
219 """
220 imArr = maskedImage.image.array
221 varArr = maskedImage.variance.array
222
223 # Fit a straight line to variance vs (sky-subtracted) signal.
224 # The evaluate that line at zero signal to get an estimate of the
225 # signal-free variance.
226 fit = np.polyfit(imArr[good], varArr[good], deg=1)
227 # fit is [1/gain, sky_var]
228 weightArr = np.zeros_like(imArr, dtype=float)
229 weightArr[good] = 1./fit[1]
230
231 applyMaxSNR(imArr, weightArr, good, maxSNR)
232 return weightArr
233
234

◆ 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 170 of file piffPsfDeterminer.py.

170def getGoodPixels(maskedImage, zeroWeightMaskBits):
171 """Compute an index array indicating good pixels to use.
172
173 Parameters
174 ----------
175 maskedImage : `afw.image.MaskedImage`
176 PSF candidate postage stamp
177 zeroWeightMaskBits : `List[str]`
178 List of mask bits for which to set pixel weights to zero.
179
180 Returns
181 -------
182 good : `ndarray`
183 Index array indicating good pixels.
184 """
185 imArr = maskedImage.image.array
186 varArr = maskedImage.variance.array
187 bitmask = maskedImage.mask.getPlaneBitMask(zeroWeightMaskBits)
188 good = (
189 (varArr != 0)
190 & (np.isfinite(varArr))
191 & (np.isfinite(imArr))
192 & ((maskedImage.mask.array & bitmask) == 0)
193 )
194 return good
195
196