23 """Simple association algorithm for DRP.
24 Adapted from http://github.com/LSSTDESC/dia_pipe
35 from .associationUtils
import query_disc, eq2xyz, toIndex
39 """Configuration parameters for the SimpleAssociationTask
41 tolerance = pexConfig.Field(
43 doc=
'maximum distance to match sources together in arcsec',
46 nside = pexConfig.Field(
48 doc=
'Healpix nside value used for indexing',
54 """Construct DiaObjects from a DataFrame of DIASources by spatially
55 associating the sources.
57 Represents a simple, brute force algorithm, 2-way matching of DiaSources
58 into. DiaObjects. Algorithm picks the nearest, first match within the
59 matching radius of a DiaObject to associate a source to for simplicity.
61 ConfigClass = SimpleAssociationConfig
62 _DefaultName =
"simpleAssociation"
64 def run(self, diaSources, tractPatchId, skymapBits):
65 """Associate DiaSources into a collection of DiaObjects using a
66 brute force matching algorithm.
68 Reproducible is for the same input data is assured by ordering the
69 DiaSource data by ccdVisit ordering.
73 diaSources : `pandas.DataFrame`
74 DiaSources grouped by CcdVisitId to spatially associate into
77 Unique identifier for the tract patch.
79 Maximum number of bits used the ``tractPatchId`` integer
84 results : `lsst.pipe.base.Struct`
85 Results struct with attributes:
88 Table of DiaSources with updated values for the DiaObjects
89 they are spatially associated to (`pandas.DataFrame`).
91 Table of DiaObjects from matching DiaSources
97 diaSources.set_index([
"ccdVisitId",
"diaSourceId"], inplace=
True)
105 idFactory = afwTable.IdFactory.makeSource(tractPatchId,
107 idCat = afwTable.SourceCatalog(
108 afwTable.SourceTable.make(afwTable.SourceTable.makeMinimalSchema(),
111 for ccdVisit
in diaSources.index.levels[0]:
114 ccdVisitSources = diaSources.loc[ccdVisit]
115 if len(diaObjectCat) == 0:
116 for diaSourceId, diaSrc
in ccdVisitSources.iterrows():
127 usedMatchIndicies = []
129 for diaSourceId, diaSrc
in ccdVisitSources.iterrows():
131 matchResult = self.
findMatchesfindMatches(diaSrc[
"ra"],
133 2*self.config.tolerance,
136 dists = matchResult.dists
137 matches = matchResult.matches
150 if np.min(dists) < np.deg2rad(self.config.tolerance/3600):
151 matchDistArg = np.argmin(dists)
152 matchIndex = matches[matchDistArg]
154 if np.isin([matchIndex], usedMatchIndicies).sum() < 1:
163 usedMatchIndicies.append(matchIndex)
187 diaSources.reset_index(inplace=
True)
189 return pipeBase.Struct(
190 assocDiaSources=diaSources,
191 diaObjects=pd.DataFrame(data=diaObjectCat))
202 """Create a new DiaObject and append its data.
206 diaSrc : `pandas.Series`
207 Full unassociated DiaSource to create a DiaObject from.
208 diaSources : `pandas.DataFrame`
209 DiaSource catalog to update information in. The catalog is
212 Unique identifier of the ccdVisit where ``diaSrc`` was observed.
214 Unique identifier of the DiaSource.
215 diaObjectCat : `list` of `dict`s
216 Catalog of diaObjects to append the new object o.
217 idCat : `lsst.afw.table.SourceCatalog`
218 Catalog with the IdFactory used to generate unique DiaObject
220 diaObjectCoords : `list` of `list`s of `lsst.geom.SpherePoint`s
221 Set of coordinates of DiaSource locations that make up the
222 DiaObject average coordinate.
223 healPixIndices : `list` of `int`s
224 HealPix indices representing the locations of each currently
227 hpIndex =
toIndex(self.config.nside,
230 healPixIndices.append(hpIndex)
235 diaObjCoords.append([sphPoint])
237 diaObjId = idCat.addNew().get(
"id")
241 diaSources.loc[(ccdVisit, diaSourceId),
"diaObjectId"] = diaObjId
252 """Update DiaObject and DiaSource values after an association.
257 Array index location of the DiaObject that ``diaSrc`` was
259 diaSrc : `pandas.Series`
260 Full unassociated DiaSource to create a DiaObject from.
261 diaSources : `pandas.DataFrame`
262 DiaSource catalog to update information in. The catalog is
265 Unique identifier of the ccdVisit where ``diaSrc`` was observed.
267 Unique identifier of the DiaSource.
268 diaObjectCat : `list` of `dict`s
269 Catalog of diaObjects to append the new object o.
270 diaObjectCoords : `list` of `list`s of `lsst.geom.SpherePoint`s
271 Set of coordinates of DiaSource locations that make up the
272 DiaObject average coordinate.
273 healPixIndices : `list` of `int`s
274 HealPix indices representing the locations of each currently
281 diaObjCoords[matchIndex].append(sphPoint)
282 aveCoord = geom.averageSpherePoint(diaObjCoords[matchIndex])
283 diaObjCat[matchIndex][
"ra"] = aveCoord.getRa().asDegrees()
284 diaObjCat[matchIndex][
"decl"] = aveCoord.getDec().asDegrees()
285 nSources = diaObjCat[matchIndex][
"nDiaSources"]
286 diaObjCat[matchIndex][
"nDiaSources"] = nSources + 1
287 healPixIndices[matchIndex] =
toIndex(self.config.nside,
288 diaObjCat[matchIndex][
"ra"],
289 diaObjCat[matchIndex][
"decl"])
291 diaSources.loc[(ccdVisit, diaSourceId),
"diaObjectId"] = \
292 diaObjCat[matchIndex][
"diaObjectId"]
295 """Search healPixels around DiaSource locations for DiaObjects.
300 DiaSource RA location.
302 DiaSource Dec location.
304 Size of annulus to convert to covering healPixels and search for
306 hpIndices : `list` of `int`s
307 List of heal pix indices containing the DiaObjects in ``diaObjs``.
308 diaObjs : `list` of `dict`s
309 Catalog diaObjects to with full location information for comparing
314 results : `lsst.pipe.base.Struct`
315 Results struct containing
318 Array of distances between the current DiaSource diaObjects.
319 (`numpy.ndarray` or `None`)
321 Array of array indices of diaObjects this DiaSource matches to.
322 (`numpy.ndarray` or `None`)
327 np.deg2rad(tol/3600.))
328 matchIndices = np.argwhere(np.isin(hpIndices, match_indices)).flatten()
330 if len(matchIndices) < 1:
331 return pipeBase.Struct(dists=
None, matches=
None)
334 [np.sqrt(np.sum((
eq2xyz(src_ra, src_dec)
335 -
eq2xyz(diaObjs[match][
"ra"],
336 diaObjs[match][
"decl"]))**2))
337 for match
in matchIndices])
338 return pipeBase.Struct(
340 matches=matchIndices)
343 """Create a simple empty DiaObject with location and id information.
348 Unique ID for this new DiaObject.
350 RA location of this DiaObject.
352 Dec location of this DiaObject
357 Dictionary of values representing a DiaObject.
359 new_dia_object = {
"diaObjectId": objId,
363 return new_dia_object
def updateCatalogs(self, matchIndex, diaSrc, diaSources, ccdVisit, diaSourceId, diaObjCat, diaObjCoords, healPixIndices)
def addNewDiaObject(self, diaSrc, diaSources, ccdVisit, diaSourceId, diaObjCat, idCat, diaObjCoords, healPixIndices)
def createDiaObject(self, objId, ra, decl)
def findMatches(self, src_ra, src_dec, tol, hpIndices, diaObjs)
def run(self, diaSources, tractPatchId, skymapBits)
def query_disc(nside, ra, dec, max_rad, min_rad=0)
def toIndex(nside, ra, dec)