Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

 

__all__ = ["DirectMatchConfig", "DirectMatchTask", "DirectMatchConfigWithoutLoader"] 

 

from lsst.pex.config import Config, Field, ConfigurableField 

from lsst.pipe.base import Task, Struct 

from lsst.meas.algorithms import (LoadIndexedReferenceObjectsTask, ScienceSourceSelectorTask, 

ReferenceSourceSelectorTask) 

import lsst.afw.table as afwTable 

from lsst.geom import arcseconds, averageSpherePoint 

 

 

class DirectMatchConfigWithoutLoader(Config): 

"""Configuration for `DirectMatchTask` when an already-initialized 

``refObjLoader`` will be passed to this task. 

""" 

matchRadius = Field(dtype=float, default=0.25, doc="Matching radius, arcsec") 

sourceSelection = ConfigurableField(target=ScienceSourceSelectorTask, 

doc="Selection of science sources") 

referenceSelection = ConfigurableField(target=ReferenceSourceSelectorTask, 

doc="Selection of reference sources") 

 

 

class DirectMatchConfig(DirectMatchConfigWithoutLoader): 

"""Configuration for `DirectMatchTask`. 

""" 

refObjLoader = ConfigurableField(target=LoadIndexedReferenceObjectsTask, doc="Load reference objects") 

 

 

class DirectMatchTask(Task): 

"""Simple, brute force matching of a source catalog to a reference catalog. 

 

Parameters 

---------- 

butler : `lsst.daf.persistence.Butler` 

Data butler containing the relevant reference catalog data. 

refObjLoader : `lsst.meas.algorithms.LoadReferenceObjectsTask` or `None` 

For loading reference objects. 

**kwargs 

Other keyword arguments required for instantiating a Task (such as 

``config``). 

""" 

ConfigClass = DirectMatchConfig 

_DefaultName = "directMatch" 

 

def __init__(self, butler=None, refObjLoader=None, **kwargs): 

Task.__init__(self, **kwargs) 

if not refObjLoader: 

if butler: 

if not isinstance(self.config, DirectMatchConfig): 

raise RuntimeError("DirectMatchTask must be initialized with DirectMatchConfig " 

"if a refObjLoader is not supplied at initialization") 

self.makeSubtask("refObjLoader", butler=butler) 

else: 

self.refObjLoader = None 

 

else: 

self.refObjLoader = refObjLoader 

self.makeSubtask("sourceSelection") 

self.makeSubtask("referenceSelection") 

 

def setRefObjLoader(self, refObjLoader): 

"""Set the reference object loader for the task. 

 

Parameters 

---------- 

refObjLoader 

An instance of a reference object loader, either a 

`lsst.meas.algorithms.LoadReferenceObjectsTask` task or a 

`lsst.meas.algorithms.ReferenceObjectLoader` instance. A task can 

be used as a subtask and is generally used in gen2 middleware. The 

class is designed to be used with gen3 middleware and is 

initialized outside the normal task framework. 

""" 

self.refObjLoader = refObjLoader 

 

def run(self, catalog, filterName=None, epoch=None): 

"""Load reference objects and match to them. 

 

Parameters 

---------- 

catalog : `lsst.afw.table.SourceCatalog` 

Catalog to match. 

filterName : `str` 

Name of filter loading fluxes. 

epoch : `astropy.time.Time` or `None` 

Epoch to which to correct proper motion and parallax, or `None` to 

not apply such corrections. 

 

Returns 

------- 

result : `lsst.pipe.base.Struct` 

Result struct with components: 

 

``matches`` 

Matched sources with associated reference 

(`lsst.afw.table.SourceMatchVector`). 

``matchMeta`` 

Match metadata (`lsst.meas.astrom.MatchMetadata`). 

""" 

if self.refObjLoader is None: 

raise RuntimeError("Running matcher task with no refObjLoader set in __ini__ or setRefObjLoader") 

circle = self.calculateCircle(catalog) 

matchMeta = self.refObjLoader.getMetadataCircle(circle.center, circle.radius, filterName, epoch=epoch) 

emptyResult = Struct(matches=[], matchMeta=matchMeta) 

sourceSelection = self.sourceSelection.run(catalog) 

if len(sourceSelection.sourceCat) == 0: 

self.log.warn("No objects selected from %d objects in source catalog", len(catalog)) 

return emptyResult 

refData = self.refObjLoader.loadSkyCircle(circle.center, circle.radius, filterName, epoch=epoch) 

refCat = refData.refCat 

refSelection = self.referenceSelection.run(refCat) 

if len(refSelection.sourceCat) == 0: 

self.log.warn("No objects selected from %d objects in reference catalog", len(refCat)) 

return emptyResult 

matches = afwTable.matchRaDec(refSelection.sourceCat, sourceSelection.sourceCat, 

self.config.matchRadius*arcseconds) 

self.log.info("Matched %d from %d/%d input and %d/%d reference sources" % 

(len(matches), len(sourceSelection.sourceCat), len(catalog), 

len(refSelection.sourceCat), len(refCat))) 

return Struct(matches=matches, matchMeta=matchMeta, refCat=refCat, sourceSelection=sourceSelection, 

refSelection=refSelection) 

 

def calculateCircle(self, catalog): 

"""Calculate a circle enclosing the catalog. 

 

Parameters 

---------- 

catalog : `lsst.afw.table.SourceCatalog` 

Catalog to encircle. 

 

Returns 

------- 

result : `lsst.pipe.base.Struct` 

Result struct with components: 

 

``center`` 

ICRS center coordinate (`lsst.afw.geom.SpherePoint`). 

``radius`` 

Radius of the circle (`lsst.geom.Angle`). 

""" 

coordList = [src.getCoord() for src in catalog] 

center = averageSpherePoint(coordList) 

radius = max(center.separation(coord) for coord in coordList) 

return Struct(center=center, radius=radius + self.config.matchRadius*arcseconds)