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# This file is part of pipe_tasks. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

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

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

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

12# (at your option) any later version. 

13# 

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

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

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21 

22import astropy.units as u 

23import copy 

24import functools 

25import numpy as np 

26import os 

27import pandas as pd 

28import unittest 

29import tempfile 

30import shutil 

31 

32import lsst.daf.base as dafBase 

33import lsst.afw.geom as afwGeom 

34import lsst.geom as geom 

35from lsst.sphgeom import HtmPixelization 

36import lsst.meas.base as measBase 

37import lsst.utils.tests 

38from lsst.pipe.tasks.parquetTable import MultilevelParquetTable 

39from lsst.daf.butler import Butler, DatasetType 

40from lsst.pipe.tasks.functors import (CompositeFunctor, CustomFunctor, Column, RAColumn, 

41 DecColumn, Mag, MagDiff, Color, StarGalaxyLabeller, 

42 DeconvolvedMoments, SdssTraceSize, PsfSdssTraceSizeDiff, 

43 HsmTraceSize, PsfHsmTraceSizeDiff, HsmFwhm, 

44 LocalPhotometry, LocalNanojansky, LocalNanojanskyErr, 

45 LocalMagnitude, LocalMagnitudeErr, 

46 LocalWcs, ComputePixelScale, ConvertPixelToArcseconds) 

47 

48ROOT = os.path.abspath(os.path.dirname(__file__)) 

49 

50 

51class FunctorTestCase(unittest.TestCase): 

52 

53 def simulateMultiParquet(self, dataDict): 

54 """Create a simple test MultilevelParquetTable 

55 """ 

56 simpleDF = pd.DataFrame(dataDict) 

57 dfFilterDSCombos = [] 

58 for ds in self.datasets: 

59 for filterName in self.filters: 

60 df = copy.copy(simpleDF) 

61 df.reindex(sorted(df.columns), axis=1) 

62 df['dataset'] = ds 

63 df['filter'] = filterName 

64 df.columns = pd.MultiIndex.from_tuples( 

65 [(ds, filterName, c) for c in df.columns], 

66 names=('dataset', 'filter', 'column')) 

67 dfFilterDSCombos.append(df) 

68 

69 df = functools.reduce(lambda d1, d2: d1.join(d2), dfFilterDSCombos) 

70 

71 return MultilevelParquetTable(dataFrame=df) 

72 

73 def getDatasetHandle(self, parq): 

74 df = parq._df 

75 lo, hi = HtmPixelization(7).universe().ranges()[0] 

76 value = np.random.randint(lo, hi) 

77 ref = self.butler.put(df, self.datasetType, dataId={'htm7': value}) 

78 return self.butler.getDeferred(ref) 

79 

80 def setUp(self): 

81 np.random.seed(1234) 

82 self.datasets = ['forced_src', 'meas', 'ref'] 

83 self.filters = ['HSC-G', 'HSC-R'] 

84 self.columns = ['coord_ra', 'coord_dec'] 

85 self.nRecords = 5 

86 self.dataDict = { 

87 "coord_ra": [3.77654137, 3.77643059, 3.77621148, 3.77611944, 3.77610396], 

88 "coord_dec": [0.01127624, 0.01127787, 0.01127543, 0.01127543, 0.01127543]} 

89 

90 # Set up butler 

91 self.root = tempfile.mkdtemp(dir=ROOT) 

92 Butler.makeRepo(self.root) 

93 self.butler = Butler(self.root, run="test_run") 

94 self.datasetType = DatasetType("data", dimensions=('htm7',), storageClass="DataFrame", 

95 universe=self.butler.registry.dimensions) 

96 self.butler.registry.registerDatasetType(self.datasetType) 

97 

98 def tearDown(self): 

99 if os.path.exists(self.root): 

100 shutil.rmtree(self.root, ignore_errors=True) 

101 

102 def _funcVal(self, functor, parq): 

103 self.assertIsInstance(functor.name, str) 

104 self.assertIsInstance(functor.shortname, str) 

105 

106 handle = self.getDatasetHandle(parq) 

107 

108 val = functor(parq) 

109 val2 = functor(handle) 

110 self.assertTrue((val == val2).all()) 

111 self.assertIsInstance(val, pd.Series) 

112 

113 val = functor(parq, dropna=True) 

114 val2 = functor(handle, dropna=True) 

115 self.assertTrue((val == val2).all()) 

116 self.assertEqual(val.isnull().sum(), 0) 

117 

118 return val 

119 

120 def _differenceVal(self, functor, parq1, parq2): 

121 self.assertIsInstance(functor.name, str) 

122 self.assertIsInstance(functor.shortname, str) 

123 

124 handle1 = self.getDatasetHandle(parq1) 

125 handle2 = self.getDatasetHandle(parq2) 

126 

127 val = functor.difference(parq1, parq2) 

128 val2 = functor.difference(handle1, handle2) 

129 self.assertTrue(val.equals(val2)) 

130 self.assertIsInstance(val, pd.Series) 

131 

132 val = functor.difference(parq1, parq2, dropna=True) 

133 val2 = functor.difference(handle1, handle2, dropna=True) 

134 self.assertTrue(val.equals(val2)) 

135 self.assertEqual(val.isnull().sum(), 0) 

136 

137 val1 = self._funcVal(functor, parq1) 

138 val2 = self._funcVal(functor, parq2) 

139 

140 self.assertTrue(np.allclose(val, val1 - val2)) 

141 

142 return val 

143 

144 def testColumn(self): 

145 self.columns.append("base_FootprintArea_value") 

146 self.dataDict["base_FootprintArea_value"] = \ 

147 np.full(self.nRecords, 1) 

148 parq = self.simulateMultiParquet(self.dataDict) 

149 func = Column('base_FootprintArea_value', filt='HSC-G') 

150 self._funcVal(func, parq) 

151 

152 def testCustom(self): 

153 self.columns.append("base_FootprintArea_value") 

154 self.dataDict["base_FootprintArea_value"] = \ 

155 np.random.rand(self.nRecords) 

156 parq = self.simulateMultiParquet(self.dataDict) 

157 func = CustomFunctor('2*base_FootprintArea_value', filt='HSC-G') 

158 val = self._funcVal(func, parq) 

159 

160 func2 = Column('base_FootprintArea_value', filt='HSC-G') 

161 

162 np.allclose(val.values, 2*func2(parq).values, atol=1e-13, rtol=0) 

163 

164 def testCoords(self): 

165 parq = self.simulateMultiParquet(self.dataDict) 

166 ra = self._funcVal(RAColumn(), parq) 

167 dec = self._funcVal(DecColumn(), parq) 

168 

169 columnDict = {'dataset': 'ref', 'filter': 'HSC-G', 

170 'column': ['coord_ra', 'coord_dec']} 

171 coords = parq.toDataFrame(columns=columnDict, droplevels=True) / np.pi * 180. 

172 

173 self.assertTrue(np.allclose(ra, coords[('ref', 'HSC-G', 'coord_ra')], atol=1e-13, rtol=0)) 

174 self.assertTrue(np.allclose(dec, coords[('ref', 'HSC-G', 'coord_dec')], atol=1e-13, rtol=0)) 

175 

176 def testMag(self): 

177 self.columns.extend(["base_PsfFlux_instFlux", "base_PsfFlux_instFluxErr"]) 

178 self.dataDict["base_PsfFlux_instFlux"] = np.full(self.nRecords, 1000) 

179 self.dataDict["base_PsfFlux_instFluxErr"] = np.full(self.nRecords, 10) 

180 parq = self.simulateMultiParquet(self.dataDict) 

181 # Change one dataset filter combinations value. 

182 parq._df[("meas", "HSC-G", "base_PsfFlux_instFlux")] -= 1 

183 

184 fluxName = 'base_PsfFlux' 

185 

186 # Check that things work when you provide dataset explicitly 

187 for dataset in ['forced_src', 'meas']: 

188 psfMag_G = self._funcVal(Mag(fluxName, dataset=dataset, 

189 filt='HSC-G'), 

190 parq) 

191 psfMag_R = self._funcVal(Mag(fluxName, dataset=dataset, 

192 filt='HSC-R'), 

193 parq) 

194 

195 psfColor_GR = self._funcVal(Color(fluxName, 'HSC-G', 'HSC-R', 

196 dataset=dataset), 

197 parq) 

198 

199 self.assertTrue(np.allclose((psfMag_G - psfMag_R).dropna(), psfColor_GR, rtol=0, atol=1e-13)) 

200 

201 # Check that behavior as expected when dataset not provided; 

202 # that is, that the color comes from forced and default Mag is meas 

203 psfMag_G = self._funcVal(Mag(fluxName, filt='HSC-G'), parq) 

204 psfMag_R = self._funcVal(Mag(fluxName, filt='HSC-R'), parq) 

205 

206 psfColor_GR = self._funcVal(Color(fluxName, 'HSC-G', 'HSC-R'), parq) 

207 

208 # These should *not* be equal. 

209 self.assertFalse(np.allclose((psfMag_G - psfMag_R).dropna(), psfColor_GR)) 

210 

211 def testMagDiff(self): 

212 self.columns.extend(["base_PsfFlux_instFlux", "base_PsfFlux_instFluxErr", 

213 "modelfit_CModel_instFlux", "modelfit_CModel_instFluxErr"]) 

214 self.dataDict["base_PsfFlux_instFlux"] = np.full(self.nRecords, 1000) 

215 self.dataDict["base_PsfFlux_instFluxErr"] = np.full(self.nRecords, 10) 

216 self.dataDict["modelfit_CModel_instFlux"] = np.full(self.nRecords, 1000) 

217 self.dataDict["modelfit_CModel_instFluxErr"] = np.full(self.nRecords, 10) 

218 parq = self.simulateMultiParquet(self.dataDict) 

219 

220 for filt in self.filters: 

221 filt = 'HSC-G' 

222 val = self._funcVal(MagDiff('base_PsfFlux', 'modelfit_CModel', filt=filt), parq) 

223 

224 mag1 = self._funcVal(Mag('modelfit_CModel', filt=filt), parq) 

225 mag2 = self._funcVal(Mag('base_PsfFlux', filt=filt), parq) 

226 self.assertTrue(np.allclose((mag2 - mag1).dropna(), val, rtol=0, atol=1e-13)) 

227 

228 def testDifference(self): 

229 """Test .difference method using MagDiff as the example. 

230 """ 

231 self.columns.extend(["base_PsfFlux_instFlux", "base_PsfFlux_instFluxErr", 

232 "modelfit_CModel_instFlux", "modelfit_CModel_instFluxErr"]) 

233 

234 self.dataDict["base_PsfFlux_instFlux"] = np.full(self.nRecords, 1000) 

235 self.dataDict["modelfit_CModel_instFlux"] = np.full(self.nRecords, 1000) 

236 parq1 = self.simulateMultiParquet(self.dataDict) 

237 

238 self.dataDict["base_PsfFlux_instFlux"] = np.full(self.nRecords, 999) 

239 self.dataDict["modelfit_CModel_instFlux"] = np.full(self.nRecords, 999) 

240 parq2 = self.simulateMultiParquet(self.dataDict) 

241 

242 magDiff = MagDiff('base_PsfFlux', 'modelfit_CModel', filt='HSC-G') 

243 

244 # Asserts that differences computed properly 

245 self._differenceVal(magDiff, parq1, parq2) 

246 

247 def testLabeller(self): 

248 # Covering the code is better than nothing 

249 self.columns.append("base_ClassificationExtendedness_value") 

250 self.dataDict["base_ClassificationExtendedness_value"] = np.full(self.nRecords, 1) 

251 parq = self.simulateMultiParquet(self.dataDict) 

252 labels = self._funcVal(StarGalaxyLabeller(), parq) # noqa 

253 

254 def testPixelScale(self): 

255 # Test that the pixel scale and pix->arcsec calculations perform as 

256 # expected. 

257 pass 

258 

259 def testOther(self): 

260 self.columns.extend(["ext_shapeHSM_HsmSourceMoments_xx", "ext_shapeHSM_HsmSourceMoments_yy", 

261 "base_SdssShape_xx", "base_SdssShape_yy", 

262 "ext_shapeHSM_HsmPsfMoments_xx", "ext_shapeHSM_HsmPsfMoments_yy", 

263 "base_SdssShape_psf_xx", "base_SdssShape_psf_yy"]) 

264 self.dataDict["ext_shapeHSM_HsmSourceMoments_xx"] = np.full(self.nRecords, 1 / np.sqrt(2)) 

265 self.dataDict["ext_shapeHSM_HsmSourceMoments_yy"] = np.full(self.nRecords, 1 / np.sqrt(2)) 

266 self.dataDict["base_SdssShape_xx"] = np.full(self.nRecords, 1 / np.sqrt(2)) 

267 self.dataDict["base_SdssShape_yy"] = np.full(self.nRecords, 1 / np.sqrt(2)) 

268 self.dataDict["ext_shapeHSM_HsmPsfMoments_xx"] = np.full(self.nRecords, 1 / np.sqrt(2)) 

269 self.dataDict["ext_shapeHSM_HsmPsfMoments_yy"] = np.full(self.nRecords, 1 / np.sqrt(2)) 

270 self.dataDict["base_SdssShape_psf_xx"] = np.full(self.nRecords, 1) 

271 self.dataDict["base_SdssShape_psf_yy"] = np.full(self.nRecords, 1) 

272 parq = self.simulateMultiParquet(self.dataDict) 

273 # Covering the code is better than nothing 

274 for filt in self.filters: 

275 for Func in [DeconvolvedMoments, 

276 SdssTraceSize, 

277 PsfSdssTraceSizeDiff, 

278 HsmTraceSize, PsfHsmTraceSizeDiff, HsmFwhm]: 

279 val = self._funcVal(Func(filt=filt), parq) # noqa 

280 

281 def _compositeFuncVal(self, functor, parq): 

282 self.assertIsInstance(functor, CompositeFunctor) 

283 

284 handle = self.getDatasetHandle(parq) 

285 

286 df = functor(parq) 

287 df2 = functor(handle) 

288 self.assertTrue(df.equals(df2)) 

289 

290 self.assertIsInstance(df, pd.DataFrame) 

291 self.assertTrue(np.all([k in df.columns for k in functor.funcDict.keys()])) 

292 

293 df = functor(parq, dropna=True) 

294 df2 = functor(handle, dropna=True) 

295 self.assertTrue(df.equals(df2)) 

296 

297 # Check that there are no nulls 

298 self.assertFalse(df.isnull().any(axis=None)) 

299 

300 return df 

301 

302 def _compositeDifferenceVal(self, functor, parq1, parq2): 

303 self.assertIsInstance(functor, CompositeFunctor) 

304 

305 handle1 = self.getDatasetHandle(parq1) 

306 handle2 = self.getDatasetHandle(parq2) 

307 

308 df = functor.difference(parq1, parq2) 

309 df2 = functor.difference(handle1, handle2) 

310 self.assertTrue(df.equals(df2)) 

311 

312 self.assertIsInstance(df, pd.DataFrame) 

313 self.assertTrue(np.all([k in df.columns for k in functor.funcDict.keys()])) 

314 

315 df = functor.difference(parq1, parq2, dropna=True) 

316 df2 = functor.difference(handle1, handle2, dropna=True) 

317 self.assertTrue(df.equals(df2)) 

318 

319 # Check that there are no nulls 

320 self.assertFalse(df.isnull().any(axis=None)) 

321 

322 df1 = functor(parq1) 

323 df2 = functor(parq2) 

324 

325 self.assertTrue(np.allclose(df.values, df1.values - df2.values)) 

326 

327 return df 

328 

329 def testComposite(self): 

330 self.columns.extend(["modelfit_CModel_instFlux", "base_PsfFlux_instFlux"]) 

331 self.dataDict["modelfit_CModel_instFlux"] = np.full(self.nRecords, 1) 

332 self.dataDict["base_PsfFlux_instFlux"] = np.full(self.nRecords, 1) 

333 parq = self.simulateMultiParquet(self.dataDict) 

334 # Modify r band value slightly. 

335 parq._df[("meas", "HSC-R", "base_PsfFlux_instFlux")] -= 0.1 

336 

337 filt = 'HSC-G' 

338 funcDict = {'psfMag_ref': Mag('base_PsfFlux', dataset='ref'), 

339 'ra': RAColumn(), 

340 'dec': DecColumn(), 

341 'psfMag': Mag('base_PsfFlux', filt=filt), 

342 'cmodel_magDiff': MagDiff('base_PsfFlux', 

343 'modelfit_CModel', filt=filt)} 

344 func = CompositeFunctor(funcDict) 

345 df = self._compositeFuncVal(func, parq) 

346 

347 # Repeat same, but define filter globally instead of individually 

348 funcDict2 = {'psfMag_ref': Mag('base_PsfFlux', dataset='ref'), 

349 'ra': RAColumn(), 

350 'dec': DecColumn(), 

351 'psfMag': Mag('base_PsfFlux'), 

352 'cmodel_magDiff': MagDiff('base_PsfFlux', 

353 'modelfit_CModel')} 

354 

355 func2 = CompositeFunctor(funcDict2, filt=filt) 

356 df2 = self._compositeFuncVal(func2, parq) 

357 self.assertTrue(df.equals(df2)) 

358 

359 func2.filt = 'HSC-R' 

360 df3 = self._compositeFuncVal(func2, parq) 

361 # Because we modified the R filter this should fail. 

362 self.assertFalse(df2.equals(df3)) 

363 

364 # Make sure things work with passing list instead of dict 

365 funcs = [Mag('base_PsfFlux', dataset='ref'), 

366 RAColumn(), 

367 DecColumn(), 

368 Mag('base_PsfFlux', filt=filt), 

369 MagDiff('base_PsfFlux', 'modelfit_CModel', filt=filt)] 

370 

371 df = self._compositeFuncVal(CompositeFunctor(funcs), parq) 

372 

373 def testCompositeColor(self): 

374 self.dataDict["base_PsfFlux_instFlux"] = np.full(self.nRecords, 1000) 

375 self.dataDict["base_PsfFlux_instFluxErr"] = np.full(self.nRecords, 10) 

376 parq = self.simulateMultiParquet(self.dataDict) 

377 funcDict = {'a': Mag('base_PsfFlux', dataset='meas', filt='HSC-G'), 

378 'b': Mag('base_PsfFlux', dataset='forced_src', filt='HSC-G'), 

379 'c': Color('base_PsfFlux', 'HSC-G', 'HSC-R')} 

380 # Covering the code is better than nothing 

381 df = self._compositeFuncVal(CompositeFunctor(funcDict), parq) # noqa 

382 

383 def testCompositeDifference(self): 

384 self.dataDict["base_PsfFlux_instFlux"] = np.full(self.nRecords, 1000) 

385 self.dataDict["base_PsfFlux_instFluxErr"] = np.full(self.nRecords, 10) 

386 parq1 = self.simulateMultiParquet(self.dataDict) 

387 

388 self.dataDict["base_PsfFlux_instFlux"] = np.full(self.nRecords, 999) 

389 self.dataDict["base_PsfFlux_instFluxErr"] = np.full(self.nRecords, 9) 

390 parq2 = self.simulateMultiParquet(self.dataDict) 

391 

392 funcDict = {'a': Mag('base_PsfFlux', dataset='meas', filt='HSC-G'), 

393 'b': Mag('base_PsfFlux', dataset='forced_src', filt='HSC-G'), 

394 'c': Color('base_PsfFlux', 'HSC-G', 'HSC-R')} 

395 # Covering the code is better than nothing 

396 df = self._compositeDifferenceVal(CompositeFunctor(funcDict), parq1, parq2) # noqa 

397 

398 def testCompositeFail(self): 

399 """Test a composite functor where one of the functors should be junk. 

400 """ 

401 self.dataDict["base_PsfFlux_instFlux"] = np.full(self.nRecords, 1000) 

402 parq = self.simulateMultiParquet(self.dataDict) 

403 

404 funcDict = {'good': Column("base_PsfFlux_instFlux"), 

405 'bad': Column('not_a_column')} 

406 

407 df = self._compositeFuncVal(CompositeFunctor(funcDict), parq) # noqa 

408 

409 def testLocalPhotometry(self): 

410 """Test the local photometry functors. 

411 """ 

412 flux = 1000 

413 fluxErr = 10 

414 calib = 10 

415 calibErr = 1 

416 self.dataDict["base_PsfFlux_instFlux"] = np.full(self.nRecords, flux) 

417 self.dataDict["base_PsfFlux_instFluxErr"] = np.full(self.nRecords, 

418 fluxErr) 

419 self.dataDict["base_LocalPhotoCalib"] = np.full(self.nRecords, calib) 

420 self.dataDict["base_LocalPhotoCalibErr"] = np.full(self.nRecords, 

421 calibErr) 

422 parq = self.simulateMultiParquet(self.dataDict) 

423 func = LocalPhotometry("base_PsfFlux_instFlux", 

424 "base_PsfFlux_instFluxErr", 

425 "base_LocalPhotoCalib", 

426 "base_LocalPhotoCalibErr") 

427 df = parq.toDataFrame(columns={"dataset": "meas", 

428 "filter": "HSC-G", 

429 "columns": ["base_PsfFlux_instFlux", 

430 "base_PsfFlux_instFluxErr", 

431 "base_LocalPhotoCalib", 

432 "base_LocalPhotoCalibErr"]}) 

433 nanoJansky = func.instFluxToNanojansky( 

434 df[("meas", "HSC-G", "base_PsfFlux_instFlux")], 

435 df[("meas", "HSC-G", "base_LocalPhotoCalib")]) 

436 mag = func.instFluxToMagnitude( 

437 df[("meas", "HSC-G", "base_PsfFlux_instFlux")], 

438 df[("meas", "HSC-G", "base_LocalPhotoCalib")]) 

439 nanoJanskyErr = func.instFluxErrToNanojanskyErr( 

440 df[("meas", "HSC-G", "base_PsfFlux_instFlux")], 

441 df[("meas", "HSC-G", "base_PsfFlux_instFluxErr")], 

442 df[("meas", "HSC-G", "base_LocalPhotoCalib")], 

443 df[("meas", "HSC-G", "base_LocalPhotoCalibErr")]) 

444 magErr = func.instFluxErrToMagnitudeErr( 

445 df[("meas", "HSC-G", "base_PsfFlux_instFlux")], 

446 df[("meas", "HSC-G", "base_PsfFlux_instFluxErr")], 

447 df[("meas", "HSC-G", "base_LocalPhotoCalib")], 

448 df[("meas", "HSC-G", "base_LocalPhotoCalibErr")]) 

449 

450 self.assertTrue(np.allclose(nanoJansky.values, 

451 flux * calib, 

452 atol=1e-13, 

453 rtol=0)) 

454 self.assertTrue(np.allclose(mag.values, 

455 (flux * calib * u.nJy).to_value(u.ABmag), 

456 atol=1e-13, 

457 rtol=0)) 

458 self.assertTrue(np.allclose(nanoJanskyErr.values, 

459 np.hypot(fluxErr * calib, flux * calibErr), 

460 atol=1e-13, 

461 rtol=0)) 

462 self.assertTrue(np.allclose( 

463 magErr.values, 

464 2.5 / np.log(10) * nanoJanskyErr.values / nanoJansky.values, 

465 atol=1e-13, 

466 rtol=0)) 

467 

468 # Test functors against the values computed above. 

469 self._testLocalPhotometryFunctors(LocalNanojansky, 

470 parq, 

471 nanoJansky) 

472 self._testLocalPhotometryFunctors(LocalNanojanskyErr, 

473 parq, 

474 nanoJanskyErr) 

475 self._testLocalPhotometryFunctors(LocalMagnitude, 

476 parq, 

477 mag) 

478 self._testLocalPhotometryFunctors(LocalMagnitudeErr, 

479 parq, 

480 magErr) 

481 

482 def _testLocalPhotometryFunctors(self, functor, parq, testValues): 

483 func = functor("base_PsfFlux_instFlux", 

484 "base_PsfFlux_instFluxErr", 

485 "base_LocalPhotoCalib", 

486 "base_LocalPhotoCalibErr") 

487 val = self._funcVal(func, parq) 

488 self.assertTrue(np.allclose(testValues.values, 

489 val.values, 

490 atol=1e-13, 

491 rtol=0)) 

492 

493 def testConvertPixelToArcseconds(self): 

494 """Test calculations of the pixel scale and conversions of pixel to 

495 arcseconds. 

496 """ 

497 dipoleSep = 10 

498 np.random.seed(1234) 

499 testPixelDeltas = np.random.uniform(-100, 100, size=(self.nRecords, 2)) 

500 import lsst.afw.table as afwTable 

501 localWcsPlugin = measBase.EvaluateLocalWcsPlugin( 

502 None, 

503 "base_LocalWcs", 

504 afwTable.SourceTable.makeMinimalSchema(), 

505 None) 

506 for dec in np.linspace(-90, 90, 10): 

507 for x, y in zip(np.random.uniform(2 * 1109.99981456774, size=10), 

508 np.random.uniform(2 * 560.018167811613, size=10)): 

509 

510 center = geom.Point2D(x, y) 

511 wcs = self._makeWcs(dec) 

512 skyOrigin = wcs.pixelToSky(center) 

513 

514 linAffMatrix = localWcsPlugin.makeLocalTransformMatrix(wcs, 

515 center) 

516 self.dataDict["dipoleSep"] = np.full(self.nRecords, dipoleSep) 

517 self.dataDict["slot_Centroid_x"] = np.full(self.nRecords, x) 

518 self.dataDict["slot_Centroid_y"] = np.full(self.nRecords, y) 

519 self.dataDict["someCentroid_x"] = x + testPixelDeltas[:, 0] 

520 self.dataDict["someCentroid_y"] = y + testPixelDeltas[:, 1] 

521 self.dataDict["base_LocalWcs_CDMatrix_1_1"] = np.full(self.nRecords, 

522 linAffMatrix[0, 0]) 

523 self.dataDict["base_LocalWcs_CDMatrix_1_2"] = np.full(self.nRecords, 

524 linAffMatrix[0, 1]) 

525 self.dataDict["base_LocalWcs_CDMatrix_2_1"] = np.full(self.nRecords, 

526 linAffMatrix[1, 0]) 

527 self.dataDict["base_LocalWcs_CDMatrix_2_2"] = np.full(self.nRecords, 

528 linAffMatrix[1, 1]) 

529 parq = self.simulateMultiParquet(self.dataDict) 

530 func = LocalWcs("base_LocalWcs_CDMatrix_1_1", 

531 "base_LocalWcs_CDMatrix_1_2", 

532 "base_LocalWcs_CDMatrix_2_1", 

533 "base_LocalWcs_CDMatrix_2_2") 

534 df = parq.toDataFrame(columns={"dataset": "meas", 

535 "filter": "HSC-G", 

536 "columns": ["dipoleSep", 

537 "slot_Centroid_x", 

538 "slot_Centroid_y", 

539 "someCentroid_x", 

540 "someCentroid_y", 

541 "base_LocalWcs_CDMatrix_1_1", 

542 "base_LocalWcs_CDMatrix_1_2", 

543 "base_LocalWcs_CDMatrix_2_1", 

544 "base_LocalWcs_CDMatrix_2_2"]}) 

545 

546 # Exercise the full set of functions in LocalWcs. 

547 sepRadians = func.getSkySeperationFromPixel( 

548 df[("meas", "HSC-G", "someCentroid_x")] - df[("meas", "HSC-G", "slot_Centroid_x")], 

549 df[("meas", "HSC-G", "someCentroid_y")] - df[("meas", "HSC-G", "slot_Centroid_y")], 

550 0.0, 

551 0.0, 

552 df[("meas", "HSC-G", "base_LocalWcs_CDMatrix_1_1")], 

553 df[("meas", "HSC-G", "base_LocalWcs_CDMatrix_1_2")], 

554 df[("meas", "HSC-G", "base_LocalWcs_CDMatrix_2_1")], 

555 df[("meas", "HSC-G", "base_LocalWcs_CDMatrix_2_2")]) 

556 

557 # Test functor values against afw SkyWcs computations. 

558 for centX, centY, sep in zip(testPixelDeltas[:, 0], 

559 testPixelDeltas[:, 1], 

560 sepRadians.values): 

561 afwSepRadians = skyOrigin.separation( 

562 wcs.pixelToSky(x + centX, y + centY)).asRadians() 

563 self.assertAlmostEqual(1 - sep / afwSepRadians, 0, places=6) 

564 

565 # Test the pixel scale computation. 

566 func = ComputePixelScale("base_LocalWcs_CDMatrix_1_1", 

567 "base_LocalWcs_CDMatrix_1_2", 

568 "base_LocalWcs_CDMatrix_2_1", 

569 "base_LocalWcs_CDMatrix_2_2") 

570 pixelScale = self._funcVal(func, parq) 

571 self.assertTrue(np.allclose( 

572 wcs.getPixelScale(center).asArcseconds(), 

573 pixelScale.values, 

574 rtol=1e-8, 

575 atol=0)) 

576 

577 func = ConvertPixelToArcseconds("dipoleSep", 

578 "base_LocalWcs_CDMatrix_1_1", 

579 "base_LocalWcs_CDMatrix_1_2", 

580 "base_LocalWcs_CDMatrix_2_1", 

581 "base_LocalWcs_CDMatrix_2_2") 

582 val = self._funcVal(func, parq) 

583 self.assertTrue(np.allclose(pixelScale.values * dipoleSep, 

584 val.values, 

585 atol=1e-16, 

586 rtol=1e-16)) 

587 

588 def _makeWcs(self, dec=53.1595451514076): 

589 """Create a wcs from real CFHT values. 

590 

591 Returns 

592 ------- 

593 wcs : `lsst.afw.geom` 

594 Created wcs. 

595 """ 

596 metadata = dafBase.PropertySet() 

597 

598 metadata.set("SIMPLE", "T") 

599 metadata.set("BITPIX", -32) 

600 metadata.set("NAXIS", 2) 

601 metadata.set("NAXIS1", 1024) 

602 metadata.set("NAXIS2", 1153) 

603 metadata.set("RADECSYS", 'FK5') 

604 metadata.set("EQUINOX", 2000.) 

605 

606 metadata.setDouble("CRVAL1", 215.604025685476) 

607 metadata.setDouble("CRVAL2", dec) 

608 metadata.setDouble("CRPIX1", 1109.99981456774) 

609 metadata.setDouble("CRPIX2", 560.018167811613) 

610 metadata.set("CTYPE1", 'RA---SIN') 

611 metadata.set("CTYPE2", 'DEC--SIN') 

612 

613 metadata.setDouble("CD1_1", 5.10808596133527E-05) 

614 metadata.setDouble("CD1_2", 1.85579539217196E-07) 

615 metadata.setDouble("CD2_2", -5.10281493481982E-05) 

616 metadata.setDouble("CD2_1", -8.27440751733828E-07) 

617 

618 return afwGeom.makeSkyWcs(metadata) 

619 

620 

621class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

622 pass 

623 

624 

625def setup_module(module): 

626 lsst.utils.tests.init() 

627 

628 

629if __name__ == "__main__": 629 ↛ 630line 629 didn't jump to line 630, because the condition on line 629 was never true

630 lsst.utils.tests.init() 

631 unittest.main()