183 def solve(self, exposure, sourceCat):
184 """Load reference objects overlapping an exposure, match to sources and
189 result : `lsst.pipe.base.Struct`
190 Result struct with components:
192 - ``refCat`` : reference object catalog of objects that overlap the
193 exposure (with some margin) (`lsst::afw::table::SimpleCatalog`).
194 - ``matches`` : astrometric matches
195 (`list` of `lsst.afw.table.ReferenceMatch`).
196 - ``scatterOnSky`` : median on-sky separation between reference
197 objects and sources in "matches" (`lsst.geom.Angle`)
198 - ``matchMeta`` : metadata needed to unpersist matches
199 (`lsst.daf.base.PropertyList`)
204 If the measured mean on-sky distance between the matched source and
205 reference objects is greater than
206 ``self.config.maxMeanDistanceArcsec``.
210 ignores config.forceKnownWcs
212 if self.refObjLoader
is None:
213 raise RuntimeError(
"Running matcher task with no refObjLoader set in __init__ or setRefObjLoader")
217 expMd = self._getExposureMetadata(exposure)
219 sourceSelection = self.sourceSelector.
run(sourceCat)
221 self.log.
info(
"Purged %d sources, leaving %d good sources",
222 len(sourceCat) - len(sourceSelection.sourceCat),
223 len(sourceSelection.sourceCat))
225 loadRes = self.refObjLoader.loadPixelBox(
228 filterName=expMd.filterName,
229 photoCalib=expMd.photoCalib,
233 refSelection = self.referenceSelector.
run(loadRes.refCat)
235 matchMeta = self.refObjLoader.getMetadataBox(
238 filterName=expMd.filterName,
239 photoCalib=expMd.photoCalib,
244 frame = int(debug.frame)
246 refCat=refSelection.sourceCat,
247 sourceCat=sourceSelection.sourceCat,
251 title=
"Reference catalog",
256 match_tolerance =
None
257 for i
in range(self.config.maxIter):
260 tryRes = self._matchAndFitWcs(
261 refCat=refSelection.sourceCat,
263 goodSourceCat=sourceSelection.sourceCat,
264 refFluxField=loadRes.fluxField,
268 match_tolerance=match_tolerance,
270 except Exception
as e:
273 self.log.
info(
"Fit WCS iter %d failed; using previous iteration: %s", iterNum, e)
279 match_tolerance = tryRes.match_tolerance
280 tryMatchDist = self._computeMatchStatsOnSky(tryRes.matches)
282 "Match and fit WCS iteration %d: found %d matches with on-sky distance mean "
283 "= %0.3f +- %0.3f arcsec; max match distance = %0.3f arcsec",
284 iterNum, len(tryRes.matches), tryMatchDist.distMean.asArcseconds(),
285 tryMatchDist.distStdDev.asArcseconds(), tryMatchDist.maxMatchDist.asArcseconds())
287 maxMatchDist = tryMatchDist.maxMatchDist
290 if maxMatchDist.asArcseconds() < self.config.minMatchDistanceArcSec:
292 "Max match distance = %0.3f arcsec < %0.3f = config.minMatchDistanceArcSec; "
293 "that's good enough",
294 maxMatchDist.asArcseconds(), self.config.minMatchDistanceArcSec)
296 match_tolerance.maxMatchDist = maxMatchDist
299 "Matched and fit WCS in %d iterations; "
300 "found %d matches with on-sky distance mean and scatter = %0.3f +- %0.3f arcsec",
301 iterNum, len(tryRes.matches), tryMatchDist.distMean.asArcseconds(),
302 tryMatchDist.distStdDev.asArcseconds())
303 if tryMatchDist.distMean.asArcseconds() > self.config.maxMeanDistanceArcsec:
304 raise pipeBase.TaskError(
305 "Fatal astrometry failure detected: mean on-sky distance = %0.3f arcsec > %0.3f "
306 "(maxMeanDistanceArcsec)" %
307 (tryMatchDist.distMean.asArcseconds(), self.config.maxMeanDistanceArcsec))
308 for m
in res.matches:
310 m.second.set(self.usedKey,
True)
311 exposure.setWcs(res.wcs)
314 md = exposure.getMetadata()
315 md[
'SFM_ASTROM_OFFSET_MEAN'] = tryMatchDist.distMean.asArcseconds()
316 md[
'SFM_ASTROM_OFFSET_STD'] = tryMatchDist.distStdDev.asArcseconds()
318 return pipeBase.Struct(
319 refCat=refSelection.sourceCat,
321 scatterOnSky=res.scatterOnSky,