LSST Applications 27.0.0,g0265f82a02+469cd937ee,g02d81e74bb+21ad69e7e1,g1470d8bcf6+cbe83ee85a,g2079a07aa2+e67c6346a6,g212a7c68fe+04a9158687,g2305ad1205+94392ce272,g295015adf3+81dd352a9d,g2bbee38e9b+469cd937ee,g337abbeb29+469cd937ee,g3939d97d7f+72a9f7b576,g487adcacf7+71499e7cba,g50ff169b8f+5929b3527e,g52b1c1532d+a6fc98d2e7,g591dd9f2cf+df404f777f,g5a732f18d5+be83d3ecdb,g64a986408d+21ad69e7e1,g858d7b2824+21ad69e7e1,g8a8a8dda67+a6fc98d2e7,g99cad8db69+f62e5b0af5,g9ddcbc5298+d4bad12328,ga1e77700b3+9c366c4306,ga8c6da7877+71e4819109,gb0e22166c9+25ba2f69a1,gb6a65358fc+469cd937ee,gbb8dafda3b+69d3c0e320,gc07e1c2157+a98bf949bb,gc120e1dc64+615ec43309,gc28159a63d+469cd937ee,gcf0d15dbbd+72a9f7b576,gdaeeff99f8+a38ce5ea23,ge6526c86ff+3a7c1ac5f1,ge79ae78c31+469cd937ee,gee10cc3b42+a6fc98d2e7,gf1cff7945b+21ad69e7e1,gfbcc870c63+9a11dc8c8f
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 246 of file piffPsfDeterminer.py.

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

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

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

◆ 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 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 See Also
178 --------
179 `lsst.meas.algorithms.variance_plance.remove_signal_from_variance` :
180 Remove the Poisson contribution from sources in the variance plane of
181 an Exposure.
182 """
183 imArr = maskedImage.image.array
184 varArr = maskedImage.variance.array
185
186 # Fit a straight line to variance vs (sky-subtracted) signal.
187 # The evaluate that line at zero signal to get an estimate of the
188 # signal-free variance.
189 fit = np.polyfit(imArr[good], varArr[good], deg=1)
190 # fit is [1/gain, sky_var]
191 weightArr = np.zeros_like(imArr, dtype=float)
192 weightArr[good] = 1./fit[1]
193
194 applyMaxSNR(imArr, weightArr, good, maxSNR)
195 return weightArr
196
197

◆ 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