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

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

# 

# LSST Data Management System 

# Copyright 2008-2015 AURA/LSST. 

# 

# 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 <https://www.lsstcorp.org/LegalNotices/>. 

# 

import lsst.afw.image 

import lsst.afw.geom 

import lsst.pex.config 

import lsst.afw.table 

import lsst.pipe.base 

from lsst.pipe.tasks.makeSkyMap import MakeSkyMapTask 

from lsst.pipe.tasks.makeCoaddTempExp import MakeCoaddTempExpTask 

from lsst.pipe.tasks.assembleCoadd import (AssembleCoaddTask, SafeClipAssembleCoaddTask, 

CompareWarpAssembleCoaddTask) 

from .mockObject import MockObjectTask 

from .mockObservation import MockObservationTask 

from .mockSelect import MockSelectImagesTask 

 

 

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

makeSkyMap = lsst.pex.config.ConfigurableField( 

doc="SkyMap builder subtask", 

target=MakeSkyMapTask 

) 

mockObject = lsst.pex.config.ConfigurableField( 

doc="Subtask that generates and draws the objects/sources in the mock images", 

target=MockObjectTask 

) 

mockObservation = lsst.pex.config.ConfigurableField( 

doc="Subtask that generates the Wcs, Psf, PhotoCalib, etc. of mock images", 

target=MockObservationTask 

) 

coaddName = lsst.pex.config.Field( 

doc="Coadd name used as a prefix for other datasets", 

dtype=str, 

optional=False, 

default="deep" 

) 

nObservations = lsst.pex.config.Field( 

doc="Number of mock observations to generate.", 

dtype=int, 

optional=False, 

default=12 

) 

edgeBuffer = lsst.pex.config.Field( 

doc=("Number of pixels by which to grow object bounding boxes when determining whether they land " 

" completely on a generated image"), 

dtype=int, 

optional=False, 

default=5 

) 

 

def setupSkyMapPatches(self, nPatches=2, patchSize=400, pixelScale=0.2*lsst.afw.geom.arcseconds): 

""" 

Set the nested [discrete] skymap config parameters such that the full tract 

has nPatches x nPatches patches of the given size and pixel scale. 

""" 

self.makeSkyMap.skyMap['discrete'].patchInnerDimensions = [patchSize, patchSize] 

self.makeSkyMap.skyMap['discrete'].pixelScale = pixelScale.asArcseconds() 

# multiply by 0.5 because we want a half-width; subtract 0.49 to ensure that we get the right 

# number after skyMap.TractInfo rounds up. 

radius = (0.5 * nPatches - 0.49) * patchSize * pixelScale.asDegrees() 

self.makeSkyMap.skyMap['discrete'].radiusList = [radius] 

 

def setDefaults(self): 

self.makeSkyMap.skyMap.name = 'discrete' 

self.makeSkyMap.skyMap['discrete'].raList = [90.0] 

self.makeSkyMap.skyMap['discrete'].decList = [0.0] 

self.makeSkyMap.skyMap['discrete'].patchBorder = 10 

self.makeSkyMap.skyMap['discrete'].projection = "TAN" 

self.makeSkyMap.skyMap['discrete'].tractOverlap = 0.0 

self.setupSkyMapPatches() 

 

 

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

"""MockCoaddTask is a driver task for creating mock coadds. As opposed to more realistic 

simulations, MockCoadd generates and uses extremely simple "toy" data that can be used to more 

rigorously test the behavior of high-level task code because the expected results are 

more easily predicted. In particular, calexps are generated directly from the truth catalog, 

and contain only zero-noise stars that are created using the same Psf, PhotoCalib, and Wcs that will 

be attached to the mock calexp. 

 

In addition to creating the mock calexps and truth catalogs, MockCoadd also contains driver 

code to run the MakeSkyMap, MakeCoaddTempExp, and AssembleCoadd tasks on the mock calexps, 

and code to directly create a mock coadd image using CoaddPsf, which can be compared to the 

output of the regular coadd tasks to check that the coadd code and CoaddPsf are consistent. 

 

Note that aside from MakeSkyMapTask, the coadd tasks are *not* subtasks of MockCoaddTasks, 

and their configs are not part of MockCoaddConfig; these are created locally within 

MockCoaddTask methods when needed, as not all coadd task config options are appropriate 

for the mock data generated by MockCoadd. 

""" 

 

ConfigClass = MockCoaddConfig 

 

_DefaultName = "MockCoadd" 

 

def __init__(self, **kwds): 

"""Construct a MockCoaddTask and the subtasks used for generating skymaps, objects, 

and observations (i.e. calexp parameters). 

""" 

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

self.makeSubtask("makeSkyMap") 

self.makeSubtask("mockObject") 

self.makeSubtask("mockObservation") 

self.schema = lsst.afw.table.SimpleTable.makeMinimalSchema() 

self.objectIdKey = self.schema.addField("objectId", type="L", doc="foreign key to truth catalog") 

self.exposureIdKey = self.schema.addField("exposureId", type="L", 

doc="foreign key to observation catalog") 

self.centroidInBBoxKey = self.schema.addField( 

"centroidInBBox", type="Flag", 

doc="set if this source's center position is inside the generated image's bbox" 

) 

self.partialOverlapKey = self.schema.addField( 

"partialOverlap", type="Flag", 

doc="set if this source was not completely inside the generated image" 

) 

 

def buildSkyMap(self, butler): 

"""Build the skymap for the mock dataset.""" 

return self.makeSkyMap.runDataRef(butler.dataRef(self.config.coaddName + "Coadd_skyMap")).skyMap 

 

def buildTruthCatalog(self, butler=None, skyMap=None, tract=0): 

"""Create and save (if butler is not None) a truth catalog containing all the mock objects. 

 

Must be run after buildSkyMap. 

 

Most of the work is delegated to the mockObject subtask. 

""" 

146 ↛ 147line 146 didn't jump to line 147, because the condition on line 146 was never true if skyMap is None: 

skyMap = butler.get(self.config.coaddName + "Coadd_skyMap") 

catalog = self.mockObject.run(tractInfo=skyMap[tract]) 

149 ↛ 151line 149 didn't jump to line 151, because the condition on line 149 was never false if butler is not None: 

butler.put(catalog, "truth", tract=tract) 

return catalog 

 

def buildObservationCatalog(self, butler=None, skyMap=None, tract=0, camera=None): 

"""Create and save (if butler is not None) an ExposureCatalog of simulated observations, 

containing the Psfs, Wcss, PhotoCalibs, etc. of the calexps to be simulated. 

 

Must be run after buildSkyMap. 

 

Most of the work is delegated to the mockObservation subtask. 

""" 

161 ↛ 162line 161 didn't jump to line 162, because the condition on line 161 was never true if skyMap is None: 

skyMap = butler.get(self.config.coaddName + "Coadd_skyMap") 

163 ↛ 165line 163 didn't jump to line 165, because the condition on line 163 was never false if camera is None: 

camera = butler.get("camera") 

catalog = self.mockObservation.run(butler=butler, 

n=self.config.nObservations, camera=camera, 

tractInfo=skyMap[tract]) 

catalog.sort() 

169 ↛ 171line 169 didn't jump to line 171, because the condition on line 169 was never false if butler is not None: 

butler.put(catalog, "observations", tract=tract) 

return catalog 

 

def buildInputImages(self, butler, obsCatalog=None, truthCatalog=None, tract=0): 

"""Use the truth catalog and observation catalog to create and save (if butler is not None) 

mock calexps and an ExposureCatalog ('simsrc') that contains information about which objects 

appear partially or fully in each exposure. 

 

Must be run after buildTruthCatalog and buildObservationCatalog. 

""" 

180 ↛ 181line 180 didn't jump to line 181, because the condition on line 180 was never true if obsCatalog is None: 

obsCatalog = butler.get("observations", tract=tract) 

182 ↛ 183line 182 didn't jump to line 183, because the condition on line 182 was never true if truthCatalog is None: 

truthCatalog = butler.get("truth", tract=tract) 

ccdKey = obsCatalog.getSchema().find("ccd").key 

visitKey = obsCatalog.getSchema().find("visit").key 

simSrcCatalog = lsst.afw.table.SimpleCatalog(self.schema) 

for obsRecord in obsCatalog: 

ccd = obsRecord.getI(ccdKey) 

visit = obsRecord.getI(visitKey) 

self.log.info("Generating image for visit={visit}, ccd={ccd}".format(ccd=ccd, visit=visit)) 

exposure = lsst.afw.image.ExposureF(obsRecord.getBBox()) 

# Apply a tiny offset to the images, so that they have non-zero background. 

# If the image background is identically zero, the calculated variance will be NaN. 

exposure.maskedImage.image.array += 1e-8 

exposure.setPhotoCalib(obsRecord.getPhotoCalib()) 

exposure.setWcs(obsRecord.getWcs()) 

exposure.setPsf(obsRecord.getPsf()) 

exposure.getInfo().setApCorrMap(obsRecord.getApCorrMap()) 

exposure.getInfo().setTransmissionCurve(obsRecord.getTransmissionCurve()) 

for truthRecord in truthCatalog: 

status = self.mockObject.drawSource(truthRecord, exposure, buffer=self.config.edgeBuffer) 

if status: 

simSrcRecord = simSrcCatalog.addNew() 

simSrcRecord.setCoord(truthRecord.getCoord()) 

simSrcRecord.setL(self.objectIdKey, truthRecord.getId()) 

simSrcRecord.setL(self.exposureIdKey, obsRecord.getId()) 

simSrcRecord.setFlag(self.centroidInBBoxKey, obsRecord.contains(truthRecord.getCoord())) 

simSrcRecord.setFlag(self.partialOverlapKey, status == 1) 

self.log.info(" added object {id}".format(id=truthRecord.getId())) 

exposure.getMaskedImage().getVariance().set(1.0) 

211 ↛ 187line 211 didn't jump to line 187, because the condition on line 211 was never false if butler is not None: 

butler.put(exposure, "calexp", ccd=ccd, visit=visit) 

213 ↛ 215line 213 didn't jump to line 215, because the condition on line 213 was never false if butler is not None: 

butler.put(simSrcCatalog, "simsrc", tract=tract) 

return simSrcCatalog 

 

def buildAllInputs(self, butler): 

"""Convenience function that calls buildSkyMap, buildObservationCatalog, buildTruthCatalog, 

and buildInputImages. 

""" 

skyMap = self.buildSkyMap(butler) 

observations = self.buildObservationCatalog(butler, skyMap=skyMap) 

truth = self.buildTruthCatalog(butler, skyMap=skyMap) 

self.buildInputImages(butler, obsCatalog=observations, truthCatalog=truth) 

 

def makeCoaddTask(self, cls, assemblePsfMatched=False): 

"""Helper function to create a Coadd task with configuration appropriate for the simulations. 

 

MockCoaddTask does not include MakeCoaddTempExpTask or AssembleCoaddTask as subtasks, because 

we want explicit control over their configs, rather than leaving this up to the user. 

However, we have to install our own SelectImages task for both of these, so it made sense 

to have a single method that would create one of these two tasks, set the config values we 

want, and install the custom SelectImagesTask. 

""" 

config = cls.ConfigClass() 

config.coaddName = self.config.coaddName 

config.select.retarget(MockSelectImagesTask) 

if cls == MakeCoaddTempExpTask: 

config.bgSubtracted = True 

config.makeDirect = True 

config.makePsfMatched = True 

config.modelPsf.defaultFwhm = 9 

config.modelPsf.addWing = False 

config.warpAndPsfMatch.psfMatch.kernel['AL'].scaleByFwhm = False 

config.warpAndPsfMatch.psfMatch.kernel['AL'].kernelSize = 25 

config.warpAndPsfMatch.psfMatch.kernel['AL'].sizeCellX = 64 

config.warpAndPsfMatch.psfMatch.kernel['AL'].sizeCellY = 64 

 

249 ↛ 257line 249 didn't jump to line 257, because the condition on line 249 was never false elif cls in [AssembleCoaddTask, SafeClipAssembleCoaddTask, CompareWarpAssembleCoaddTask]: 

if assemblePsfMatched: 

config.warpType = 'psfMatched' 

if cls != AssembleCoaddTask: 

config.doWrite = False 

if cls == CompareWarpAssembleCoaddTask: 

config.assembleStaticSkyModel.select.retarget(MockSelectImagesTask) 

config.doAttachTransmissionCurve = True 

return cls(config=config) 

 

def iterPatchRefs(self, butler, tractInfo): 

"""Generator that iterates over the patches in a tract, yielding dataRefs. 

""" 

nPatchX, nPatchY = tractInfo.getNumPatches() 

for iPatchX in range(nPatchX): 

for iPatchY in range(nPatchY): 

patchRef = butler.dataRef(self.config.coaddName + "Coadd", 

tract=tractInfo.getId(), patch="%d,%d" % (iPatchX, iPatchY), 

filter='r') 

yield patchRef 

 

def buildCoadd(self, butler, skyMap=None, tract=0): 

"""Run the coadd tasks (MakeCoaddTempExp and AssembleCoadd) on the mock data. 

 

Must be run after buildInputImages. 

Makes both direct and PSF-matched coadds 

""" 

276 ↛ 278line 276 didn't jump to line 278, because the condition on line 276 was never false if skyMap is None: 

skyMap = butler.get(self.config.coaddName + "Coadd_skyMap") 

tractInfo = skyMap[tract] 

makeCoaddTempExpTask = self.makeCoaddTask(MakeCoaddTempExpTask) 

directCoaddTaskList = [] 

for coaddTask in [SafeClipAssembleCoaddTask, CompareWarpAssembleCoaddTask, AssembleCoaddTask]: 

directCoaddTaskList.append(self.makeCoaddTask(coaddTask)) 

assemblePsfMatchedCoaddTask = self.makeCoaddTask(AssembleCoaddTask, assemblePsfMatched=True) 

for patchRef in self.iterPatchRefs(butler, tractInfo): 

makeCoaddTempExpTask.runDataRef(patchRef) 

for patchRef in self.iterPatchRefs(butler, tractInfo): 

for directCoaddTask in directCoaddTaskList: 

directCoaddTask.runDataRef(patchRef) 

assemblePsfMatchedCoaddTask.runDataRef(patchRef) 

 

def buildMockCoadd(self, butler, truthCatalog=None, skyMap=None, tract=0): 

"""Directly create a simulation of the coadd, using the CoaddPsf (and ModelPsf) 

of the direct (and psfMatched) coadd exposure and the truth catalog. 

 

Must be run after buildCoadd. 

""" 

297 ↛ 299line 297 didn't jump to line 299, because the condition on line 297 was never false if truthCatalog is None: 

truthCatalog = butler.get("truth", tract=tract) 

299 ↛ 301line 299 didn't jump to line 301, because the condition on line 299 was never false if skyMap is None: 

skyMap = butler.get(self.config.coaddName + "Coadd_skyMap") 

tractInfo = skyMap[tract] 

for patchRef in self.iterPatchRefs(butler, tractInfo): 

for dataProduct in ["Coadd", "CoaddPsfMatched"]: 

exposure = patchRef.get(self.config.coaddName + dataProduct) 

exposure.getMaskedImage().getImage().set(0.0) 

coaddPsf = lsst.meas.algorithms.CoaddPsf( 

exposure.getInfo().getCoaddInputs().ccds, exposure.getWcs() 

) 

exposure.setPsf(coaddPsf) 

for truthRecord in truthCatalog: 

self.mockObject.drawSource(truthRecord, exposure, buffer=0) 

patchRef.put(exposure, self.config.coaddName + dataProduct + "_mock") 

 

 

def run(root): 

"""Convenience function to create and run MockCoaddTask with default settings. 

""" 

from .simpleMapper import makeDataRepo 

butler = makeDataRepo(root=root) 

task = MockCoaddTask() 

task.buildAllInputs(butler) 

task.buildCoadd(butler) 

task.buildMockCoadd(butler)