LSST Applications g063fba187b+fee0456c91,g0f08755f38+ea96e5a5a3,g1653933729+a8ce1bb630,g168dd56ebc+a8ce1bb630,g1a2382251a+90257ff92a,g20f6ffc8e0+ea96e5a5a3,g217e2c1bcf+937a289c59,g28da252d5a+daa7da44eb,g2bbee38e9b+253935c60e,g2bc492864f+253935c60e,g3156d2b45e+6e55a43351,g32e5bea42b+31359a2a7a,g347aa1857d+253935c60e,g35bb328faa+a8ce1bb630,g3a166c0a6a+253935c60e,g3b1af351f3+a8ce1bb630,g3e281a1b8c+c5dd892a6c,g414038480c+416496e02f,g41af890bb2+afe91b1188,g599934f4f4+0db33f7991,g7af13505b9+e36de7bce6,g80478fca09+da231ba887,g82479be7b0+a4516e59e3,g858d7b2824+ea96e5a5a3,g89c8672015+f4add4ffd5,g9125e01d80+a8ce1bb630,ga5288a1d22+bc6ab8dfbd,gb58c049af0+d64f4d3760,gc28159a63d+253935c60e,gcab2d0539d+3f2b72788c,gcf0d15dbbd+4ea9c45075,gda6a2b7d83+4ea9c45075,gdaeeff99f8+1711a396fd,ge79ae78c31+253935c60e,gef2f8181fd+3031e3cf99,gf0baf85859+c1f95f4921,gfa517265be+ea96e5a5a3,gfa999e8aa5+17cd334064,w.2024.50
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 296 of file piffPsfDeterminer.py.

296def _computeWeightAlternative(maskedImage, maxSNR):
297 """Alternative algorithm for creating weight map.
298
299 This version is equivalent to that used by Piff internally. The weight map
300 it produces tends to leave a residual when removing the Poisson component
301 due to the signal. We leave it here as a reference, but without intending
302 that it be used (or be maintained).
303 """
304 imArr = maskedImage.image.array
305 varArr = maskedImage.variance.array
306 good = (varArr != 0) & np.isfinite(varArr) & np.isfinite(imArr)
307
308 fit = np.polyfit(imArr[good], varArr[good], deg=1)
309 # fit is [1/gain, sky_var]
310 gain = 1./fit[0]
311 varArr[good] -= imArr[good] / gain
312 weightArr = np.zeros_like(imArr, dtype=float)
313 weightArr[good] = 1./varArr[good]
314
315 applyMaxSNR(imArr, weightArr, good, maxSNR)
316 return weightArr
317
318

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

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

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

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

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

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

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

183def getGoodPixels(maskedImage, zeroWeightMaskBits):
184 """Compute an index array indicating good pixels to use.
185
186 Parameters
187 ----------
188 maskedImage : `afw.image.MaskedImage`
189 PSF candidate postage stamp
190 zeroWeightMaskBits : `List[str]`
191 List of mask bits for which to set pixel weights to zero.
192
193 Returns
194 -------
195 good : `ndarray`
196 Index array indicating good pixels.
197 """
198 imArr = maskedImage.image.array
199 varArr = maskedImage.variance.array
200 bitmask = maskedImage.mask.getPlaneBitMask(zeroWeightMaskBits)
201 good = (
202 (varArr != 0)
203 & (np.isfinite(varArr))
204 & (np.isfinite(imArr))
205 & ((maskedImage.mask.array & bitmask) == 0)
206 )
207 return good
208
209