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

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

#!/usr/bin/env python 

# 

# LSST Data Management System 

# Copyright 2008, 2009, 2010, 2014 LSST Corporation. 

# 

# This product includes software developed by the 

# LSST Project (http://www.lsst.org/). 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 3 of the License, or 

# (at your option) any later version. 

# 

# This program is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

# You should have received a copy of the LSST License Statement and 

# the GNU General Public License along with this program. If not, 

# see <http://www.lsstcorp.org/LegalNotices/>. 

# 

 

"""Base command-line driver task for forced measurement. Must be inherited to specialize for 

a specific dataset to be used (see ForcedPhotCcdTask, ForcedPhotCoaddTask). 

""" 

 

import lsst.afw.table 

import lsst.pex.config 

import lsst.daf.base 

import lsst.pipe.base 

import lsst.pex.config 

 

from .references import MultiBandReferencesTask 

from .forcedMeasurement import ForcedMeasurementTask 

from .applyApCorr import ApplyApCorrTask 

from .catalogCalculation import CatalogCalculationTask 

 

__all__ = ("ForcedPhotImageConfig", "ForcedPhotImageTask") 

 

 

class ForcedPhotImageConfig(lsst.pex.config.Config): 

"""!Config class for forced measurement driver task.""" 

 

references = lsst.pex.config.ConfigurableField( 

target=MultiBandReferencesTask, 

doc="subtask to retrieve reference source catalog" 

) 

measurement = lsst.pex.config.ConfigurableField( 

target=ForcedMeasurementTask, 

doc="subtask to do forced measurement" 

) 

coaddName = lsst.pex.config.Field( 

doc="coadd name: typically one of deep or goodSeeing", 

dtype=str, 

default="deep", 

) 

doApCorr = lsst.pex.config.Field( 

dtype=bool, 

default=True, 

doc="Run subtask to apply aperture corrections" 

) 

applyApCorr = lsst.pex.config.ConfigurableField( 

target=ApplyApCorrTask, 

doc="Subtask to apply aperture corrections" 

) 

catalogCalculation = lsst.pex.config.ConfigurableField( 

target=CatalogCalculationTask, 

doc="Subtask to run catalogCalculation plugins on catalog" 

) 

 

def setDefaults(self): 

# Make catalogCalculation a no-op by default as no modelFlux is setup by default in 

# ForcedMeasurementTask 

self.catalogCalculation.plugins.names = [] 

 

## @addtogroup LSST_task_documentation 

## @{ 

## @page ForcedPhotImageTask 

## ForcedPhotImageTask 

## @copybrief ForcedPhotImageTask 

## @} 

 

 

class ForcedPhotImageTask(lsst.pipe.base.CmdLineTask): 

"""!A base class for command-line forced measurement drivers. 

 

This is a an abstract class, which is the common ancestor for ForcedPhotCcdTask 

and ForcedPhotCoaddTask. It provides the runDataRef() method that does most of 

the work, while delegating a few customization tasks to other methods that are 

overridden by subclasses. 

 

This task is not directly usable as a CmdLineTask; subclasses must: 

- Set the _DefaultName class attribute 

- Implement makeIdFactory 

- Implement fetchReferences 

- (optional) Implement attachFootprints 

""" 

ConfigClass = ForcedPhotImageConfig 

_DefaultName = "processImageForcedTask" 

 

def __init__(self, butler=None, refSchema=None, **kwds): 

"""Initialize the task. 

 

ForcedPhotImageTask takes two keyword arguments beyond the usual CmdLineTask arguments: 

- refSchema: the Schema of the reference catalog, passed to the constructor of the references 

subtask 

- butler: a butler that will be passed to the references subtask to allow it to load its Schema 

from disk 

At least one of these arguments must be present; if both are, schema takes precedence. 

""" 

super(lsst.pipe.base.CmdLineTask, self).__init__(**kwds) 

self.makeSubtask("references", butler=butler, schema=refSchema) 

if refSchema is None: 

refSchema = self.references.schema 

self.makeSubtask("measurement", refSchema=refSchema) 

# It is necessary to get the schema internal to the forced measurement task until such a time 

# that the schema is not owned by the measurement task, but is passed in by an external caller 

if self.config.doApCorr: 

self.makeSubtask("applyApCorr", schema=self.measurement.schema) 

self.makeSubtask('catalogCalculation', schema=self.measurement.schema) 

 

def runDataRef(self, dataRef, psfCache=None): 

"""!Measure a single exposure for forced detection for a reference catalog using a dataRef. 

 

@param[in] dataRef An lsst.daf.persistence.ButlerDataRef. It is passed to the 

references subtask to obtain the reference WCS, the getExposure() 

method (implemented by derived classes) to read the measurement 

image, and the fetchReferences() method (implemented by derived 

classes) to get the exposure and load the reference catalog (see 

the CoaddSrcReferencesTask for more information). The sources are then 

passed to the writeOutputs() method (implemented by derived classes) 

which writes the outputs. See derived class documentation for which 

datasets and data ID keys are used. 

@param[in] psfCache Size of PSF cache, or None. The size of the PSF cache can have 

a significant effect upon the runtime for complicated PSF models. 

""" 

refWcs = self.references.getWcs(dataRef) 

exposure = self.getExposure(dataRef) 

if psfCache is not None: 

exposure.getPsf().setCacheSize(psfCache) 

refCat = self.fetchReferences(dataRef, exposure) 

 

measCat = self.measurement.generateMeasCat(exposure, refCat, refWcs, 

idFactory=self.makeIdFactory(dataRef)) 

self.log.info("Performing forced measurement on %s" % (dataRef.dataId,)) 

self.attachFootprints(measCat, refCat, exposure, refWcs, dataRef) 

 

exposureId = self.getExposureId(dataRef) 

 

forcedPhotResult = self.run(measCat, exposure, refCat, refWcs, exposureId=exposureId) 

 

self.writeOutput(dataRef, forcedPhotResult.measCat) 

 

def run(self, measCat, exposure, refCat, refWcs, exposureId=None): 

"""!Measure a single exposure with forced detection for a reference catalog. 

 

@param[in] measCat The measurement catalog generated by measurement.generateMeasCat(), 

based on the sources listed in the reference catalog. 

@param[in] exposure The measurement image upon which to perform forced detection. 

@param[in] refCat The reference catalog of sources to measure. 

@param[in] refWcs The WCS for the references. 

@param[in] exposureId Optional unique exposureId used for random seed in measurement task. 

 

@return result An lsst.pipe.base.Struct containing fields: 

measCat Source catalog of forced measurement results from measurement.run(). 

""" 

self.measurement.run(measCat, exposure, refCat, refWcs, exposureId=exposureId) 

if self.config.doApCorr: 

self.applyApCorr.run( 

catalog=measCat, 

apCorrMap=exposure.getInfo().getApCorrMap() 

) 

self.catalogCalculation.run(measCat) 

 

return lsst.pipe.base.Struct(measCat=measCat) 

 

def makeIdFactory(self, dataRef): 

"""!Hook for derived classes to define how to make an IdFactory for forced sources. 

 

Note that this is for forced source IDs, not object IDs, which are usually handled by 

the measurement.copyColumns config option. 

""" 

raise NotImplementedError() 

 

def getExposureId(self, dataRef): 

raise NotImplementedError() 

 

def fetchReferences(self, dataRef, exposure): 

"""!Hook for derived classes to define how to get references objects. 

 

Derived classes should call one of the fetch* methods on the references subtask, 

but which one they call depends on whether the region to get references for is a 

easy to describe in patches (as it would be when doing forced measurements on a 

coadd), or is just an arbitrary box (as it would be for CCD forced measurements). 

""" 

raise NotImplementedError() 

 

def attachFootprints(self, sources, refCat, exposure, refWcs, dataRef): 

"""!Hook for derived classes to define how to attach Footprints to blank sources prior to measurement 

 

Footprints for forced photometry must be in the pixel coordinate system of the image being 

measured, while the actual detections may start out in a different coordinate system. 

 

Subclasses for ForcedPhotImageTask must implement this method to define how those Footprints 

should be generated. 

 

The default implementation (defined in forcedMeasurement.py) transforms the Footprints from 

the reference catalog from the refWcs to the exposure's Wcs, which downgrades HeavyFootprints 

into regular Footprints, destroying deblend information. 

""" 

return self.measurement.attachTransformedFootprints(sources, refCat, exposure, refWcs) 

 

def getExposure(self, dataRef): 

"""!Read input exposure on which to perform the measurements 

 

@param dataRef Data reference from butler. 

""" 

return dataRef.get(self.dataPrefix + "calexp", immediate=True) 

 

def writeOutput(self, dataRef, sources): 

"""!Write forced source table 

 

@param dataRef Data reference from butler; the forced_src dataset (with self.dataPrefix included) 

is all that will be modified. 

@param sources SourceCatalog to save 

""" 

dataRef.put(sources, self.dataPrefix + "forced_src", flags=lsst.afw.table.SOURCE_IO_NO_FOOTPRINTS) 

 

def getSchemaCatalogs(self): 

"""!Get a dict of Schema catalogs that will be used by this Task. 

 

In the case of forced taks, there is only one schema for each type of forced measurement. 

The dataset type for this measurement is defined in the mapper. 

""" 

catalog = lsst.afw.table.SourceCatalog(self.measurement.schema) 

catalog.getTable().setMetadata(self.measurement.algMetadata) 

datasetType = self.dataPrefix + "forced_src" 

return {datasetType: catalog} 

 

def _getConfigName(self): 

"""!Return the name of the config dataset. Forces config comparison from run-to-run 

""" 

return self.dataPrefix + "forced_config" 

 

def _getMetadataName(self): 

"""!Return the name of the metadata dataset. Forced metadata to be saved 

""" 

return self.dataPrefix + "forced_metadata"