LSST Applications g0265f82a02+c6dfa2ddaf,g1162b98a3f+b2075782a9,g2079a07aa2+1b2e822518,g2bbee38e9b+c6dfa2ddaf,g337abbeb29+c6dfa2ddaf,g3ddfee87b4+a60788ef87,g50ff169b8f+2eb0e556e8,g52b1c1532d+90ebb246c7,g555ede804d+a60788ef87,g591dd9f2cf+ba8caea58f,g5ec818987f+864ee9cddb,g858d7b2824+9ee1ab4172,g876c692160+a40945ebb7,g8a8a8dda67+90ebb246c7,g8cdfe0ae6a+4fd9e222a8,g99cad8db69+5e309b7bc6,g9ddcbc5298+a1346535a5,ga1e77700b3+df8f93165b,ga8c6da7877+aa12a14d27,gae46bcf261+c6dfa2ddaf,gb0e22166c9+8634eb87fb,gb3f2274832+d0da15e3be,gba4ed39666+1ac82b564f,gbb8dafda3b+5dfd9c994b,gbeb006f7da+97157f9740,gc28159a63d+c6dfa2ddaf,gc86a011abf+9ee1ab4172,gcf0d15dbbd+a60788ef87,gdaeeff99f8+1cafcb7cd4,gdc0c513512+9ee1ab4172,ge79ae78c31+c6dfa2ddaf,geb67518f79+ba1859f325,geb961e4c1e+f9439d1e6f,gee10cc3b42+90ebb246c7,gf1cff7945b+9ee1ab4172,w.2024.12
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 240 of file piffPsfDeterminer.py.

240def _computeWeightAlternative(maskedImage, maxSNR):
241 """Alternative algorithm for creating weight map.
242
243 This version is equivalent to that used by Piff internally. The weight map
244 it produces tends to leave a residual when removing the Poisson component
245 due to the signal. We leave it here as a reference, but without intending
246 that it be used (or be maintained).
247 """
248 imArr = maskedImage.image.array
249 varArr = maskedImage.variance.array
250 good = (varArr != 0) & np.isfinite(varArr) & np.isfinite(imArr)
251
252 fit = np.polyfit(imArr[good], varArr[good], deg=1)
253 # fit is [1/gain, sky_var]
254 gain = 1./fit[0]
255 varArr[good] -= imArr[good] / gain
256 weightArr = np.zeros_like(imArr, dtype=float)
257 weightArr[good] = 1./varArr[good]
258
259 applyMaxSNR(imArr, weightArr, good, maxSNR)
260 return weightArr
261
262

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

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

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

192def applyMaxSNR(imArr, weightArr, good, maxSNR):
193 """Rescale weight of bright stars to cap the computed SNR.
194
195 Parameters
196 ----------
197 imArr : `ndarray`
198 Signal (image) array of stamp.
199 weightArr : `ndarray`
200 Weight map array. May be rescaled in place.
201 good : `ndarray`
202 Index array of pixels to use when computing SNR.
203 maxSNR : `float`
204 Threshold for adjusting variance plane implementing maximum SNR.
205 """
206 # We define the SNR value following Piff. Here's the comment from that
207 # code base explaining the calculation.
208 #
209 # The S/N value that we use will be the weighted total flux where the
210 # weight function is the star's profile itself. This is the maximum S/N
211 # value that any flux measurement can possibly produce, which will be
212 # closer to an in-practice S/N than using all the pixels equally.
213 #
214 # F = Sum_i w_i I_i^2
215 # var(F) = Sum_i w_i^2 I_i^2 var(I_i)
216 # = Sum_i w_i I_i^2 <--- Assumes var(I_i) = 1/w_i
217 #
218 # S/N = F / sqrt(var(F))
219 #
220 # Note that if the image is pure noise, this will produce a "signal" of
221 #
222 # F_noise = Sum_i w_i 1/w_i = Npix
223 #
224 # So for a more accurate estimate of the S/N of the actual star itself, one
225 # should subtract off Npix from the measured F.
226 #
227 # The final formula then is:
228 #
229 # F = Sum_i w_i I_i^2
230 # S/N = (F-Npix) / sqrt(F)
231 F = np.sum(weightArr[good]*imArr[good]**2, dtype=float)
232 Npix = np.sum(good)
233 SNR = 0.0 if F < Npix else (F-Npix)/np.sqrt(F)
234 # rescale weight of bright stars. Essentially makes an error floor.
235 if SNR > maxSNR:
236 factor = (maxSNR / SNR)**2
237 weightArr[good] *= factor
238
239

◆ 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.

Definition at line 160 of file piffPsfDeterminer.py.

160def computeWeight(maskedImage, maxSNR, good):
161 """Derive a weight map without Poisson variance component due to signal.
162
163 Parameters
164 ----------
165 maskedImage : `afw.image.MaskedImage`
166 PSF candidate postage stamp
167 maxSNR : `float`
168 Maximum SNR applying variance floor.
169 good : `ndarray`
170 Index array indicating good pixels.
171
172 Returns
173 -------
174 weightArr : `ndarry`
175 Array to use for weight.
176 """
177 imArr = maskedImage.image.array
178 varArr = maskedImage.variance.array
179
180 # Fit a straight line to variance vs (sky-subtracted) signal.
181 # The evaluate that line at zero signal to get an estimate of the
182 # signal-free variance.
183 fit = np.polyfit(imArr[good], varArr[good], deg=1)
184 # fit is [1/gain, sky_var]
185 weightArr = np.zeros_like(imArr, dtype=float)
186 weightArr[good] = 1./fit[1]
187
188 applyMaxSNR(imArr, weightArr, good, maxSNR)
189 return weightArr
190
191

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

133def getGoodPixels(maskedImage, zeroWeightMaskBits):
134 """Compute an index array indicating good pixels to use.
135
136 Parameters
137 ----------
138 maskedImage : `afw.image.MaskedImage`
139 PSF candidate postage stamp
140 zeroWeightMaskBits : `List[str]`
141 List of mask bits for which to set pixel weights to zero.
142
143 Returns
144 -------
145 good : `ndarray`
146 Index array indicating good pixels.
147 """
148 imArr = maskedImage.image.array
149 varArr = maskedImage.variance.array
150 bitmask = maskedImage.mask.getPlaneBitMask(zeroWeightMaskBits)
151 good = (
152 (varArr != 0)
153 & (np.isfinite(varArr))
154 & (np.isfinite(imArr))
155 & ((maskedImage.mask.array & bitmask) == 0)
156 )
157 return good
158
159