171 def run(self, exposure, sources, expId=0, matches=None):
172 """Measure the PSF.
173
174 Parameters
175 ----------
176 exposure : `lsst.afw.image.Exposure`
177 Exposure to process; measured PSF will be added.
178 sources : `Unknown`
179 Measured sources on exposure; flag fields will be set marking
180 stars chosen by the star selector and the PSF determiner if a schema
181 was passed to the task constructor.
182 expId : `int`, optional
183 Exposure id used for generating random seed.
184 matches : `list`, optional
185 A list of ``lsst.afw.table.ReferenceMatch`` objects
186 (i.e. of ``lsst.afw.table.Match`` with @c first being
187 of type ``lsst.afw.table.SimpleRecord`` and @c second
188 type lsst.afw.table.SourceRecord --- the reference object and detected
189 object respectively) as returned by @em e.g. the AstrometryTask.
190 Used by star selectors that choose to refer to an external catalog.
191
192 Returns
193 -------
194 measurement : `lsst.pipe.base.Struct`
195 PSF measurement as a struct with attributes:
196
197 ``psf``
198 The measured PSF (also set in the input exposure).
199 ``cellSet``
200 An `lsst.afw.math.SpatialCellSet` containing the PSF candidates
201 as returned by the psf determiner.
202 """
203 self.log.info("Measuring PSF")
204
205 import lsstDebug
209 displayPsfCandidates =
lsstDebug.Info(__name__).displayPsfCandidates
213
214
215
216
217 stars = self.starSelector.run(sourceCat=sources, matches=matches, exposure=exposure)
218 selectionResult = self.makePsfCandidates.run(stars.sourceCat, exposure=exposure)
219 self.log.info("PSF star selector found %d candidates", len(selectionResult.psfCandidates))
220 reserveResult = self.reserve.run(selectionResult.goodStarCat, expId=expId)
221
222 psfDeterminerList = [cand for cand, use
223 in zip(selectionResult.psfCandidates, reserveResult.use) if use]
224
225 if selectionResult.psfCandidates and self.candidateKey is not None:
226 for cand in selectionResult.psfCandidates:
227 source = cand.getSource()
228 source.set(self.candidateKey, True)
229
230 self.log.info("Sending %d candidates to PSF determiner", len(psfDeterminerList))
231
232 if display:
233 frame = 1
234 if displayExposure:
235 disp = afwDisplay.Display(frame=frame)
236 disp.mtv(exposure, title="psf determination")
237 frame += 1
238
239
240
241 psf, cellSet = self.psfDeterminer.determinePsf(exposure, psfDeterminerList, self.metadata,
242 flagKey=self.usedKey)
243 self.log.info("PSF determination using %d/%d stars.",
244 self.metadata.getScalar("numGoodStars"), self.metadata.getScalar("numAvailStars"))
245
246 exposure.setPsf(psf)
247
248 if display:
249 frame = display
250 if displayExposure:
251 disp = afwDisplay.Display(frame=frame)
252 showPsfSpatialCells(exposure, cellSet, showBadCandidates, frame=frame)
253 frame += 1
254
255 if displayPsfCandidates:
256 plotPsfCandidates(cellSet, showBadCandidates=showBadCandidates, frame=frame)
257 frame += 1
258
259 if displayResiduals:
260 frame = plotResiduals(exposure, cellSet,
261 showBadCandidates=showBadCandidates,
262 normalizeResiduals=normalizeResiduals,
263 frame=frame)
264 if displayPsfMosaic:
265 disp = afwDisplay.Display(frame=frame)
266 maUtils.showPsfMosaic(exposure, psf, display=disp, showFwhm=True)
267 disp.scale("linear", 0, 1)
268 frame += 1
269
270 return pipeBase.Struct(
271 psf=psf,
272 cellSet=cellSet,
273 )
274