89 def __init__(self, refObjLoader=None, **kwargs):
90 pipeBase.Task.__init__(self, **kwargs)
96 if self.config.sourceSelector.name ==
'matcher':
97 if self.config.sourceSelector[
'matcher'].sourceFluxType != self.config.sourceFluxType:
98 raise RuntimeError(
"The sourceFluxType in the sourceSelector['matcher'] must match "
99 "the configured sourceFluxType")
101 self.makeSubtask(
"matcher")
102 self.makeSubtask(
"sourceSelector")
103 self.makeSubtask(
"referenceSelector")
117 """Load reference objects overlapping an exposure and match to sources
118 detected on that exposure.
122 exposure : `lsst.afw.image.Exposure`
123 exposure that the sources overlap
124 sourceCat : `lsst.afw.table.SourceCatalog.`
125 catalog of sources detected on the exposure
129 result : `lsst.pipe.base.Struct`
130 Result struct with Components:
132 - ``refCat`` : reference object catalog of objects that overlap the
133 exposure (`lsst.afw.table.SimpleCatalog`)
134 - ``matches`` : Matched sources and references
135 (`list` of `lsst.afw.table.ReferenceMatch`)
136 - ``matchMeta`` : metadata needed to unpersist matches
137 (`lsst.daf.base.PropertyList`)
141 ignores config.matchDistanceSigma
144 raise RuntimeError(
"Running matcher task with no refObjLoader set in __ini__ or setRefObjLoader")
150 sourceSelection = self.sourceSelector.run(sourceCat)
152 sourceFluxField =
"slot_%sFlux_instFlux" % (self.config.sourceFluxType)
157 filterName=expMd.filterName,
161 refSelection = self.referenceSelector.run(loadRes.refCat)
166 filterName=expMd.filterName,
170 matchRes = self.matcher.matchObjectsToSources(
171 refCat=refSelection.sourceCat,
172 sourceCat=sourceSelection.sourceCat,
174 sourceFluxField=sourceFluxField,
175 refFluxField=loadRes.fluxField,
176 match_tolerance=
None,
181 "Found %d matches with scatter = %0.3f +- %0.3f arcsec; ",
182 len(matchRes.matches), distStats.distMean.asArcseconds(), distStats.distStdDev.asArcseconds()
186 frame = int(debug.frame)
188 refCat=refSelection.sourceCat,
189 sourceCat=sourceSelection.sourceCat,
190 matches=matchRes.matches,
197 return pipeBase.Struct(
198 refCat=loadRes.refCat,
199 refSelection=refSelection,
200 sourceSelection=sourceSelection,
201 matches=matchRes.matches,
206 """Compute on-sky radial distance statistics for a match list
210 matchList : `list` of `lsst.afw.table.ReferenceMatch`
211 list of matches between reference object and sources;
212 the distance field is the only field read and it must be set to distance in radians
216 result : `lsst.pipe.base.Struct`
217 Result struct with components:
219 - ``distMean`` : clipped mean of on-sky radial separation (`float`)
220 - ``distStdDev`` : clipped standard deviation of on-sky radial
222 - ``maxMatchDist`` : distMean + self.config.matchDistanceSigma *
225 distStatsInRadians = makeMatchStatistics(matchList, afwMath.MEANCLIP | afwMath.STDEVCLIP)
226 distMean = distStatsInRadians.getValue(afwMath.MEANCLIP)*lsst.geom.radians
227 distStdDev = distStatsInRadians.getValue(afwMath.STDEVCLIP)*lsst.geom.radians
228 return pipeBase.Struct(
230 distStdDev=distStdDev,
231 maxMatchDist=distMean + self.config.matchDistanceSigma * distStdDev,
235 """Extract metadata from an exposure.
239 exposure : `lsst.afw.image.Exposure`
243 result : `lsst.pipe.base.Struct`
244 Result struct with components:
246 - ``bbox`` : parent bounding box (`lsst.geom.Box2I`)
247 - ``wcs`` : exposure WCS (`lsst.afw.geom.SkyWcs`)
248 - ``photoCalib`` : photometric calibration (`lsst.afw.image.PhotoCalib`)
249 - ``filterName`` : name of filter band (`str`)
250 - ``epoch`` : date of exposure (`astropy.time.Time`)
252 filterLabel = exposure.info.getFilter()
253 filterName = filterLabel.bandLabel
if filterLabel
is not None else None
255 if exposure.info.hasVisitInfo():
256 epochTaiMjd = exposure.visitInfo.date.get(system=DateTime.MJD, scale=DateTime.TAI)
257 epoch = astropy.time.Time(epochTaiMjd, scale=
"tai", format=
"mjd")
259 return pipeBase.Struct(
260 bbox=exposure.getBBox(),
261 wcs=exposure.info.getWcs(),
262 photoCalib=exposure.info.getPhotoCalib(),
263 filterName=filterName,