23 """Simple association algorithm for DRP.
24 Adapted from http://github.com/LSSTDESC/dia_pipe
36 from .associationUtils
import query_disc, eq2xyz, toIndex
40 """Configuration parameters for the SimpleAssociationTask
42 tolerance = pexConfig.Field(
44 doc=
'maximum distance to match sources together in arcsec',
47 nside = pexConfig.Field(
49 doc=
'Healpix nside value used for indexing',
55 """Construct DiaObjects from a DataFrame of DIASources by spatially
56 associating the sources.
58 Represents a simple, brute force algorithm, 2-way matching of DiaSources
59 into. DiaObjects. Algorithm picks the nearest, first match within the
60 matching radius of a DiaObject to associate a source to for simplicity.
62 ConfigClass = SimpleAssociationConfig
63 _DefaultName =
"simpleAssociation"
65 def run(self, diaSources, tractPatchId, skymapBits):
66 """Associate DiaSources into a collection of DiaObjects using a
67 brute force matching algorithm.
69 Reproducible is for the same input data is assured by ordering the
70 DiaSource data by ccdVisit ordering.
74 diaSources : `pandas.DataFrame`
75 DiaSources grouped by CcdVisitId to spatially associate into
78 Unique identifier for the tract patch.
80 Maximum number of bits used the ``tractPatchId`` integer
85 results : `lsst.pipe.base.Struct`
86 Results struct with attributes:
89 Table of DiaSources with updated values for the DiaObjects
90 they are spatially associated to (`pandas.DataFrame`).
92 Table of DiaObjects from matching DiaSources
99 doDropIndex = diaSources.index.names[0]
is None
100 diaSources.reset_index(inplace=
True, drop=doDropIndex)
104 diaSources.set_index([
"ccdVisitId",
"diaSourceId"], inplace=
True)
112 exposureIdInfo = ExposureIdInfo(tractPatchId, skymapBits)
113 idFactory = exposureIdInfo.makeSourceIdFactory()
115 afwTable.SourceTable.make(afwTable.SourceTable.makeMinimalSchema(),
118 for ccdVisit
in diaSources.index.levels[0]:
121 ccdVisitSources = diaSources.loc[ccdVisit]
122 if len(diaObjectCat) == 0:
123 for diaSourceId, diaSrc
in ccdVisitSources.iterrows():
134 usedMatchIndicies = []
136 for diaSourceId, diaSrc
in ccdVisitSources.iterrows():
138 matchResult = self.
findMatchesfindMatches(diaSrc[
"ra"],
140 2*self.config.tolerance,
143 dists = matchResult.dists
144 matches = matchResult.matches
157 if np.min(dists) < np.deg2rad(self.config.tolerance/3600):
158 matchDistArg = np.argmin(dists)
159 matchIndex = matches[matchDistArg]
161 if np.isin([matchIndex], usedMatchIndicies).sum() < 1:
170 usedMatchIndicies.append(matchIndex)
195 diaSources.reset_index(inplace=
True)
196 diaSources.set_index(
"diaSourceId", inplace=
True, verify_integrity=
True)
198 objs = diaObjectCat
if diaObjectCat
else np.array([], dtype=[(
'diaObjectId',
'int64'),
201 (
'nDiaSources',
'int64')])
202 diaObjects = pd.DataFrame(data=objs)
204 if "diaObjectId" in diaObjects.columns:
205 diaObjects.set_index(
"diaObjectId", inplace=
True, verify_integrity=
True)
207 return pipeBase.Struct(
208 assocDiaSources=diaSources,
209 diaObjects=diaObjects)
220 """Create a new DiaObject and append its data.
224 diaSrc : `pandas.Series`
225 Full unassociated DiaSource to create a DiaObject from.
226 diaSources : `pandas.DataFrame`
227 DiaSource catalog to update information in. The catalog is
230 Unique identifier of the ccdVisit where ``diaSrc`` was observed.
232 Unique identifier of the DiaSource.
233 diaObjectCat : `list` of `dict`s
234 Catalog of diaObjects to append the new object o.
235 idCat : `lsst.afw.table.SourceCatalog`
236 Catalog with the IdFactory used to generate unique DiaObject
238 diaObjectCoords : `list` of `list`s of `lsst.geom.SpherePoint`s
239 Set of coordinates of DiaSource locations that make up the
240 DiaObject average coordinate.
241 healPixIndices : `list` of `int`s
242 HealPix indices representing the locations of each currently
245 hpIndex =
toIndex(self.config.nside,
248 healPixIndices.append(hpIndex)
253 diaObjCoords.append([sphPoint])
255 diaObjId = idCat.addNew().get(
"id")
259 diaSources.loc[(ccdVisit, diaSourceId),
"diaObjectId"] = diaObjId
270 """Update DiaObject and DiaSource values after an association.
275 Array index location of the DiaObject that ``diaSrc`` was
277 diaSrc : `pandas.Series`
278 Full unassociated DiaSource to create a DiaObject from.
279 diaSources : `pandas.DataFrame`
280 DiaSource catalog to update information in. The catalog is
283 Unique identifier of the ccdVisit where ``diaSrc`` was observed.
285 Unique identifier of the DiaSource.
286 diaObjectCat : `list` of `dict`s
287 Catalog of diaObjects to append the new object o.
288 diaObjectCoords : `list` of `list`s of `lsst.geom.SpherePoint`s
289 Set of coordinates of DiaSource locations that make up the
290 DiaObject average coordinate.
291 healPixIndices : `list` of `int`s
292 HealPix indices representing the locations of each currently
299 diaObjCoords[matchIndex].
append(sphPoint)
301 diaObjCat[matchIndex][
"ra"] = aveCoord.getRa().asDegrees()
302 diaObjCat[matchIndex][
"decl"] = aveCoord.getDec().asDegrees()
303 nSources = diaObjCat[matchIndex][
"nDiaSources"]
304 diaObjCat[matchIndex][
"nDiaSources"] = nSources + 1
305 healPixIndices[matchIndex] =
toIndex(self.config.nside,
306 diaObjCat[matchIndex][
"ra"],
307 diaObjCat[matchIndex][
"decl"])
309 diaSources.loc[(ccdVisit, diaSourceId),
"diaObjectId"] = \
310 diaObjCat[matchIndex][
"diaObjectId"]
313 """Search healPixels around DiaSource locations for DiaObjects.
318 DiaSource RA location.
320 DiaSource Dec location.
322 Size of annulus to convert to covering healPixels and search for
324 hpIndices : `list` of `int`s
325 List of heal pix indices containing the DiaObjects in ``diaObjs``.
326 diaObjs : `list` of `dict`s
327 Catalog diaObjects to with full location information for comparing
332 results : `lsst.pipe.base.Struct`
333 Results struct containing
336 Array of distances between the current DiaSource diaObjects.
337 (`numpy.ndarray` or `None`)
339 Array of array indices of diaObjects this DiaSource matches to.
340 (`numpy.ndarray` or `None`)
345 np.deg2rad(tol/3600.))
346 matchIndices = np.argwhere(np.isin(hpIndices, match_indices)).flatten()
348 if len(matchIndices) < 1:
349 return pipeBase.Struct(dists=
None, matches=
None)
352 [np.sqrt(np.sum((
eq2xyz(src_ra, src_dec)
353 -
eq2xyz(diaObjs[match][
"ra"],
354 diaObjs[match][
"decl"]))**2))
355 for match
in matchIndices])
356 return pipeBase.Struct(
358 matches=matchIndices)
361 """Create a simple empty DiaObject with location and id information.
366 Unique ID for this new DiaObject.
368 RA location of this DiaObject.
370 Dec location of this DiaObject
375 Dictionary of values representing a DiaObject.
377 new_dia_object = {
"diaObjectId": objId,
381 return new_dia_object
Point in an unspecified spherical coordinate system.
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)
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
SpherePoint averageSpherePoint(std::vector< SpherePoint > const &coords)
Return the average of a list of coordinates.
def query_disc(nside, ra, dec, max_rad, min_rad=0)
def toIndex(nside, ra, dec)