LSST Applications g034a557a3c+dd8dd8f11d,g0afe43252f+b86e4b8053,g11f7dcd041+017865fdd3,g1cd03abf6b+8446defddb,g1ce3e0751c+f991eae79d,g28da252d5a+ca8a1a9fb3,g2bbee38e9b+b6588ad223,g2bc492864f+b6588ad223,g2cdde0e794+8523d0dbb4,g347aa1857d+b6588ad223,g35bb328faa+b86e4b8053,g3a166c0a6a+b6588ad223,g461a3dce89+b86e4b8053,g52b1c1532d+b86e4b8053,g7f3b0d46df+ad13c1b82d,g80478fca09+f29c5d6c70,g858d7b2824+293f439f82,g8cd86fa7b1+af721d2595,g965a9036f2+293f439f82,g979bb04a14+51ed57f74c,g9ddcbc5298+f24b38b85a,gae0086650b+b86e4b8053,gbb886bcc26+b97e247655,gc28159a63d+b6588ad223,gc30aee3386+a2f0f6cab9,gcaf7e4fdec+293f439f82,gcd45df26be+293f439f82,gcdd4ae20e8+70b5def7e6,gce08ada175+da9c58a417,gcf0d15dbbd+70b5def7e6,gdaeeff99f8+006e14e809,gdbce86181e+6a170ce272,ge3d4d395c2+224150c836,ge5f7162a3a+bb2241c923,ge6cb8fbbf7+d119aed356,ge79ae78c31+b6588ad223,gf048a9a2f4+40ffced2b8,gf0baf85859+b4cca3d10f,w.2024.30
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