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

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

# 

# LSST Data Management System 

# 

# Copyright 2008-2016 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 os 

import unittest 

import numpy 

from io import StringIO 

 

import lsst.utils.tests 

import lsst.afw.detection 

import lsst.afw.image 

import lsst.afw.geom 

import lsst.afw.geom.ellipses 

import lsst.log 

import lsst.log.utils 

import lsst.meas.modelfit 

import lsst.meas.algorithms 

 

# Set trace to 0-5 to view debug messages. Level 5 enables all traces. 

lsst.log.utils.traceSetAt("meas.modelfit.optimizer.Optimizer", -1) 

lsst.log.utils.traceSetAt("meas.modelfit.optimizer.solveTrustRegion", -1) 

 

 

class DoubleShapeletPsfApproxTestMixin: 

 

Algorithm = lsst.meas.modelfit.DoubleShapeletPsfApproxAlgorithm 

 

def initialize(self, psf, ctrl=None, atol=1E-4, **kwds): 

if not isinstance(psf, lsst.afw.detection.Psf): 

kernel = lsst.afw.math.FixedKernel(psf) 

psf = lsst.meas.algorithms.KernelPsf(kernel) 

self.psf = psf 

self.atol = atol 

53 ↛ 55line 53 didn't jump to line 55, because the condition on line 53 was never false if ctrl is None: 

ctrl = lsst.meas.modelfit.DoubleShapeletPsfApproxControl() 

self.ctrl = ctrl 

for name, value in kwds.items(): 

setattr(self.ctrl, name, value) 

self.exposure = lsst.afw.image.ExposureF(1, 1) 

scale = 5.0e-5 * lsst.afw.geom.degrees 

wcs = lsst.afw.geom.makeSkyWcs(crpix=lsst.afw.geom.Point2D(0.0, 0.0), 

crval=lsst.afw.geom.SpherePoint(45, 45, lsst.afw.geom.degrees), 

cdMatrix=lsst.afw.geom.makeCdMatrix(scale=scale)) 

self.exposure.setWcs(wcs) 

self.exposure.setPsf(self.psf) 

 

def tearDown(self): 

del self.exposure 

del self.psf 

del self.ctrl 

del self.atol 

 

def setupTaskConfig(self, config): 

config.slots.shape = None 

config.slots.psfFlux = None 

config.slots.apFlux = None 

config.slots.gaussianFlux = None 

config.slots.modelFlux = None 

config.slots.calibFlux = None 

config.doReplaceWithNoise = False 

config.plugins.names = ["modelfit_DoubleShapeletPsfApprox"] 

config.plugins["modelfit_DoubleShapeletPsfApprox"].readControl(self.ctrl) 

 

def checkBounds(self, msf): 

"""Check that the bounds specified in the control object are met by a MultiShapeletFunction. 

 

These requirements must be true after a call to any fit method or measure(). 

""" 

self.assertEqual(len(msf.getComponents()), 2) 

self.assertEqual( 

lsst.shapelet.computeSize(self.ctrl.innerOrder), 

len(msf.getComponents()[0].getCoefficients()) 

) 

self.assertEqual( 

lsst.shapelet.computeSize(self.ctrl.outerOrder), 

len(msf.getComponents()[1].getCoefficients()) 

) 

self.assertGreater( 

self.ctrl.maxRadiusBoxFraction * (self.psf.computeKernelImage().getBBox().getArea())**0.5, 

lsst.afw.geom.ellipses.Axes(msf.getComponents()[0].getEllipse().getCore()).getA() 

) 

self.assertGreater( 

self.ctrl.maxRadiusBoxFraction * (self.psf.computeKernelImage().getBBox().getArea())**0.5, 

lsst.afw.geom.ellipses.Axes(msf.getComponents()[1].getEllipse().getCore()).getA() 

) 

self.assertLess( 

self.ctrl.minRadius, 

lsst.afw.geom.ellipses.Axes(msf.getComponents()[0].getEllipse().getCore()).getB() 

) 

self.assertLess( 

self.ctrl.minRadius, 

lsst.afw.geom.ellipses.Axes(msf.getComponents()[1].getEllipse().getCore()).getB() 

) 

self.assertLess( 

self.ctrl.minRadiusDiff, 

(msf.getComponents()[1].getEllipse().getCore().getDeterminantRadius() - 

msf.getComponents()[0].getEllipse().getCore().getDeterminantRadius()) 

) 

 

def checkRatios(self, msf): 

"""Check that the ratios specified in the control object are met by a MultiShapeletFunction. 

 

These requirements must be true after initializeResult and fitMoments, but will are relaxed 

in later stages of the fit. 

""" 

inner = msf.getComponents()[0] 

outer = msf.getComponents()[1] 

position = msf.getComponents()[0].getEllipse().getCenter() 

self.assertFloatsAlmostEqual(position.getX(), msf.getComponents()[1].getEllipse().getCenter().getX()) 

self.assertFloatsAlmostEqual(position.getY(), msf.getComponents()[1].getEllipse().getCenter().getY()) 

self.assertFloatsAlmostEqual(outer.evaluate()(position), 

inner.evaluate()(position)*self.ctrl.peakRatio) 

self.assertFloatsAlmostEqual( 

outer.getEllipse().getCore().getDeterminantRadius(), 

inner.getEllipse().getCore().getDeterminantRadius() * self.ctrl.radiusRatio 

) 

 

def makeImages(self, msf): 

"""Return an Image of the data and an Image of the model for comparison. 

""" 

dataImage = self.exposure.getPsf().computeKernelImage() 

modelImage = dataImage.Factory(dataImage.getBBox()) 

msf.evaluate().addToImage(modelImage) 

return dataImage, modelImage 

 

def checkFitQuality(self, msf): 

"""Check the quality of the fit by comparing to the PSF image. 

""" 

dataImage, modelImage = self.makeImages(msf) 

self.assertFloatsAlmostEqual(dataImage.getArray(), modelImage.getArray(), atol=self.atol, 

plotOnFailure=True) 

 

def testSingleFramePlugin(self): 

"""Run the algorithm as a single-frame plugin and check the quality of the fit. 

""" 

config = lsst.meas.base.SingleFrameMeasurementTask.ConfigClass() 

self.setupTaskConfig(config) 

config.slots.centroid = "centroid" 

schema = lsst.afw.table.SourceTable.makeMinimalSchema() 

centroidKey = lsst.afw.table.Point2DKey.addFields(schema, "centroid", "centroid", "pixel") 

task = lsst.meas.base.SingleFrameMeasurementTask(config=config, schema=schema) 

measCat = lsst.afw.table.SourceCatalog(schema) 

measRecord = measCat.addNew() 

measRecord.set(centroidKey, lsst.afw.geom.Point2D(0.0, 0.0)) 

task.run(measCat, self.exposure) 

self.assertFalse(measRecord.get("modelfit_DoubleShapeletPsfApprox_flag")) 

key = lsst.shapelet.MultiShapeletFunctionKey(schema["modelfit"]["DoubleShapeletPsfApprox"]) 

msf = measRecord.get(key) 

self.checkBounds(msf) 

self.checkFitQuality(msf) 

 

def testForcedPlugin(self): 

"""Run the algorithm as a forced plugin and check the quality of the fit. 

""" 

config = lsst.meas.base.ForcedMeasurementTask.ConfigClass() 

config.copyColumns = {"id": "objectId", "parent": "parentObjectId"} 

self.setupTaskConfig(config) 

config.slots.centroid = "base_TransformedCentroid" 

config.plugins.names |= ["base_TransformedCentroid"] 

refSchema = lsst.afw.table.SourceTable.makeMinimalSchema() 

refCentroidKey = lsst.afw.table.Point2DKey.addFields(refSchema, "centroid", "centroid", "pixel") 

refSchema.getAliasMap().set("slot_Centroid", "centroid") 

refCat = lsst.afw.table.SourceCatalog(refSchema) 

refRecord = refCat.addNew() 

refRecord.set(refCentroidKey, lsst.afw.geom.Point2D(0.0, 0.0)) 

refWcs = self.exposure.getWcs() # same as measurement Wcs 

task = lsst.meas.base.ForcedMeasurementTask(config=config, refSchema=refSchema) 

measCat = task.generateMeasCat(self.exposure, refCat, refWcs) 

task.run(measCat, self.exposure, refCat, refWcs) 

measRecord = measCat[0] 

self.assertFalse(measRecord.get("modelfit_DoubleShapeletPsfApprox_flag")) 

measSchema = measCat.schema 

key = lsst.shapelet.MultiShapeletFunctionKey(measSchema["modelfit"]["DoubleShapeletPsfApprox"]) 

msf = measRecord.get(key) 

self.checkBounds(msf) 

self.checkFitQuality(msf) 

 

def testInitializeResult(self): 

"""Test that initializeResult() returns a unit-flux, unit-circle MultiShapeletFunction 

with the right peakRatio and radiusRatio. 

""" 

msf = self.Algorithm.initializeResult(self.ctrl) 

self.assertFloatsAlmostEqual(msf.evaluate().integrate(), 1.0) 

moments = msf.evaluate().computeMoments() 

axes = lsst.afw.geom.ellipses.Axes(moments.getCore()) 

self.assertFloatsAlmostEqual(moments.getCenter().getX(), 0.0) 

self.assertFloatsAlmostEqual(moments.getCenter().getY(), 0.0) 

self.assertFloatsAlmostEqual(axes.getA(), 1.0) 

self.assertFloatsAlmostEqual(axes.getB(), 1.0) 

self.assertEqual(len(msf.getComponents()), 2) 

self.checkRatios(msf) 

 

def testFitMoments(self): 

"""Test that fitMoments() preserves peakRatio and radiusRatio while setting moments 

correctly. 

""" 

MOMENTS_RTOL = 1E-13 

image = self.psf.computeKernelImage() 

array = image.getArray() 

bbox = image.getBBox() 

x, y = numpy.meshgrid( 

numpy.arange(bbox.getBeginX(), bbox.getEndX()), 

numpy.arange(bbox.getBeginY(), bbox.getEndY()) 

) 

msf = self.Algorithm.initializeResult(self.ctrl) 

self.Algorithm.fitMoments(msf, self.ctrl, image) 

self.assertFloatsAlmostEqual(msf.evaluate().integrate(), array.sum(), rtol=MOMENTS_RTOL) 

moments = msf.evaluate().computeMoments() 

q = lsst.afw.geom.ellipses.Quadrupole(moments.getCore()) 

cx = (x*array).sum()/array.sum() 

cy = (y*array).sum()/array.sum() 

self.assertFloatsAlmostEqual(moments.getCenter().getX(), cx, rtol=MOMENTS_RTOL) 

self.assertFloatsAlmostEqual(moments.getCenter().getY(), cy, rtol=MOMENTS_RTOL) 

self.assertFloatsAlmostEqual(q.getIxx(), ((x - cx)**2 * array).sum()/array.sum(), rtol=MOMENTS_RTOL) 

self.assertFloatsAlmostEqual(q.getIyy(), ((y - cy)**2 * array).sum()/array.sum(), rtol=MOMENTS_RTOL) 

self.assertFloatsAlmostEqual(q.getIxy(), ((x - cx)*(y - cy)*array).sum()/array.sum(), 

rtol=MOMENTS_RTOL) 

self.assertEqual(len(msf.getComponents()), 2) 

self.checkRatios(msf) 

self.checkBounds(msf) 

 

def testObjective(self): 

"""Test that model evaluation agrees with derivative evaluation in the objective object. 

""" 

image = self.psf.computeKernelImage() 

msf = self.Algorithm.initializeResult(self.ctrl) 

self.Algorithm.fitMoments(msf, self.ctrl, image) 

moments = msf.evaluate().computeMoments() 

r0 = moments.getCore().getDeterminantRadius() 

objective = self.Algorithm.makeObjective(moments, self.ctrl, image) 

image, model = self.makeImages(msf) 

parameters = numpy.zeros(4, dtype=float) 

parameters[0] = msf.getComponents()[0].getCoefficients()[0] 

parameters[1] = msf.getComponents()[1].getCoefficients()[0] 

parameters[2] = msf.getComponents()[0].getEllipse().getCore().getDeterminantRadius() / r0 

parameters[3] = msf.getComponents()[1].getEllipse().getCore().getDeterminantRadius() / r0 

residuals = numpy.zeros(image.getArray().size, dtype=float) 

objective.computeResiduals(parameters, residuals) 

self.assertFloatsAlmostEqual( 

residuals.reshape(image.getHeight(), image.getWidth()), 

image.getArray() - model.getArray() 

) 

step = 1E-6 

derivatives = numpy.zeros((parameters.size, residuals.size), dtype=float).transpose() 

objective.differentiateResiduals(parameters, derivatives) 

for i in range(parameters.size): 

original = parameters[i] 

r1 = numpy.zeros(residuals.size, dtype=float) 

r2 = numpy.zeros(residuals.size, dtype=float) 

parameters[i] = original + step 

objective.computeResiduals(parameters, r1) 

parameters[i] = original - step 

objective.computeResiduals(parameters, r2) 

parameters[i] = original 

d = (r1 - r2)/(2.0*step) 

self.assertFloatsAlmostEqual( 

d.reshape(image.getHeight(), image.getWidth()), 

derivatives[:, i].reshape(image.getHeight(), image.getWidth()), 

atol=1E-11 

) 

 

def testFitProfile(self): 

"""Test that fitProfile() does not modify the ellipticity, that it improves the fit, and 

that small perturbations to the zeroth-order amplitudes and radii do not improve the fit. 

""" 

image = self.psf.computeKernelImage() 

msf = self.Algorithm.initializeResult(self.ctrl) 

self.Algorithm.fitMoments(msf, self.ctrl, image) 

prev = lsst.shapelet.MultiShapeletFunction(msf) 

self.Algorithm.fitProfile(msf, self.ctrl, image) 

 

def getEllipticity(m, c): 

s = lsst.afw.geom.ellipses.SeparableDistortionDeterminantRadius( 

m.getComponents()[c].getEllipse().getCore() 

) 

return numpy.array([s.getE1(), s.getE2()]) 

self.assertFloatsAlmostEqual(getEllipticity(prev, 0), getEllipticity(msf, 0), rtol=1E-13) 

self.assertFloatsAlmostEqual(getEllipticity(prev, 1), getEllipticity(msf, 1), rtol=1E-13) 

 

def computeChiSq(m): 

data, model = self.makeImages(m) 

return numpy.sum((data.getArray() - model.getArray())**2) 

bestChiSq = computeChiSq(msf) 

self.assertLessEqual(bestChiSq, computeChiSq(prev)) 

step = 1E-4 

for component in msf.getComponents(): 

# 0th-order amplitude perturbation 

original = component.getCoefficients()[0] 

component.getCoefficients()[0] = original + step 

self.assertLessEqual(bestChiSq, computeChiSq(msf)) 

component.getCoefficients()[0] = original - step 

self.assertLessEqual(bestChiSq, computeChiSq(msf)) 

component.getCoefficients()[0] = original 

# Radius perturbation 

original = component.getEllipse() 

component.getEllipse().getCore().scale(1.0 + step) 

self.assertLessEqual(bestChiSq, computeChiSq(msf)) 

component.setEllipse(original) 

component.getEllipse().getCore().scale(1.0 - step) 

self.assertLessEqual(bestChiSq, computeChiSq(msf)) 

component.setEllipse(original) 

 

def testFitShapelets(self): 

"""Test that fitShapelets() does not modify the zeroth order coefficients or ellipse, 

that it improves the fit, and that small perturbations to the higher-order coefficients 

do not improve the fit. 

""" 

image = self.psf.computeKernelImage() 

msf = self.Algorithm.initializeResult(self.ctrl) 

self.Algorithm.fitMoments(msf, self.ctrl, image) 

self.Algorithm.fitProfile(msf, self.ctrl, image) 

prev = lsst.shapelet.MultiShapeletFunction(msf) 

self.Algorithm.fitShapelets(msf, self.ctrl, image) 

self.assertFloatsAlmostEqual( 

prev.getComponents()[0].getEllipse().getParameterVector(), 

msf.getComponents()[0].getEllipse().getParameterVector() 

) 

self.assertFloatsAlmostEqual( 

prev.getComponents()[1].getEllipse().getParameterVector(), 

msf.getComponents()[1].getEllipse().getParameterVector() 

) 

 

def computeChiSq(m): 

data, model = self.makeImages(m) 

return numpy.sum((data.getArray() - model.getArray())**2) 

bestChiSq = computeChiSq(msf) 

self.assertLessEqual(bestChiSq, computeChiSq(prev)) 

step = 1E-4 

for component in msf.getComponents(): 

for i in range(1, len(component.getCoefficients())): 

original = component.getCoefficients()[i] 

component.getCoefficients()[i] = original + step 

self.assertLessEqual(bestChiSq, computeChiSq(msf)) 

component.getCoefficients()[i] = original - step 

self.assertLessEqual(bestChiSq, computeChiSq(msf)) 

component.getCoefficients()[i] = original 

 

def testSingleFrameConfigIO(self): 

config1 = lsst.meas.base.SingleFrameMeasurementTask.ConfigClass() 

config2 = lsst.meas.base.SingleFrameMeasurementTask.ConfigClass() 

self.setupTaskConfig(config1) 

stream = StringIO() 

config1.saveToStream(stream) 

config2.loadFromStream(stream.getvalue()) 

self.assertEqual(config1, config2) 

 

 

class SingleGaussianTestCase(DoubleShapeletPsfApproxTestMixin, lsst.utils.tests.TestCase): 

 

def setUp(self): 

numpy.random.seed(500) 

DoubleShapeletPsfApproxTestMixin.initialize( 

self, psf=lsst.afw.detection.GaussianPsf(25, 25, 2.0), 

innerOrder=0, outerOrder=0, peakRatio=0.0 

) 

 

 

class HigherOrderTestCase0(DoubleShapeletPsfApproxTestMixin, lsst.utils.tests.TestCase): 

 

def setUp(self): 

numpy.random.seed(500) 

image = lsst.afw.image.ImageD(os.path.join(os.path.dirname(os.path.realpath(__file__)), 

"data", "psfs/great3-0.fits")) 

DoubleShapeletPsfApproxTestMixin.initialize( 

self, psf=image, 

innerOrder=3, outerOrder=2, 

atol=0.0005 

) 

 

 

class HigherOrderTestCase1(DoubleShapeletPsfApproxTestMixin, lsst.utils.tests.TestCase): 

 

def setUp(self): 

numpy.random.seed(500) 

image = lsst.afw.image.ImageD(os.path.join(os.path.dirname(os.path.realpath(__file__)), 

"data", "psfs/great3-1.fits")) 

DoubleShapeletPsfApproxTestMixin.initialize( 

self, psf=image, 

innerOrder=2, outerOrder=1, 

atol=0.002 

) 

 

 

class TestMemory(lsst.utils.tests.MemoryTestCase): 

pass 

 

 

def setup_module(module): 

lsst.utils.tests.init() 

 

 

411 ↛ 412line 411 didn't jump to line 412, because the condition on line 411 was never trueif __name__ == "__main__": 

lsst.utils.tests.init() 

unittest.main()