Coverage for tests/test_visitInfo.py: 9%

441 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-27 02:52 -0700

1# 

2# LSST Data Management System 

3# Copyright 2016 LSST Corporation. 

4# 

5# This product includes software developed by the 

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

7# 

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

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

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

11# (at your option) any later version. 

12# 

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

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

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

16# GNU General Public License for more details. 

17# 

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

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

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

21# 

22import math 

23import os 

24import unittest 

25import collections 

26import numpy as np 

27 

28import lsst.utils.tests 

29import lsst.pex.exceptions 

30from lsst.daf.base import DateTime, PropertySet, PropertyList 

31from lsst.geom import Angle, degrees, SpherePoint 

32from lsst.afw.coord import Observatory, Weather 

33import lsst.afw.image as afwImage 

34 

35RotTypeEnumNameDict = { 

36 afwImage.RotType.UNKNOWN: "UNKNOWN", 

37 afwImage.RotType.SKY: "SKY", 

38 afwImage.RotType.HORIZON: "HORIZON", 

39 afwImage.RotType.MOUNT: "MOUNT", 

40} 

41 

42 

43def propertySetFromDict(keyValDict): 

44 """Make an lsst.daf.base.PropertySet from a dict of key: value""" 

45 metadata = PropertySet() 

46 for key, val in keyValDict.items(): 

47 metadata.set(key, val) 

48 return metadata 

49 

50 

51def makeVisitInfo(data): 

52 """Return a VisitInfo constructed from a VisitInfoData namedtuple.""" 

53 return afwImage.VisitInfo(data.exposureId, 

54 data.exposureTime, 

55 data.darkTime, 

56 data.date, 

57 data.ut1, 

58 data.era, 

59 data.boresightRaDec, 

60 data.boresightAzAlt, 

61 data.boresightAirmass, 

62 data.boresightRotAngle, 

63 data.rotType, 

64 data.observatory, 

65 data.weather, 

66 data.instrumentLabel, 

67 data.id, 

68 data.focusZ, 

69 data.observationType, 

70 data.scienceProgram, 

71 data.observationReason, 

72 data.object, 

73 data.hasSimulatedContent, 

74 ) 

75 

76 

77class VisitInfoTestCase(lsst.utils.tests.TestCase): 

78 """Test lsst.afw.image.VisitInfo, a simple struct-like class""" 

79 

80 def setUp(self): 

81 self.testDir = os.path.dirname(__file__) 

82 

83 def computeLstHA(data): 

84 """Return LST, Hour Angle, computed from VisitInfoData.""" 

85 localEra = data.era + data.observatory.getLongitude() 

86 hourAngle = localEra - data.boresightRaDec[0] 

87 return localEra, hourAngle 

88 

89 fields = ['exposureId', 

90 'exposureTime', 

91 'darkTime', 

92 'date', 

93 'ut1', 

94 'era', 

95 'boresightRaDec', 

96 'boresightAzAlt', 

97 'boresightAirmass', 

98 'boresightRotAngle', 

99 'rotType', 

100 'observatory', 

101 'weather', 

102 'instrumentLabel', 

103 'id', 

104 'focusZ', 

105 'observationType', 

106 "scienceProgram", 

107 "observationReason", 

108 "object", 

109 "hasSimulatedContent", 

110 ] 

111 VisitInfoData = collections.namedtuple("VisitInfoData", fields) 

112 data1 = VisitInfoData(exposureId=10313423, 

113 exposureTime=10.01, 

114 darkTime=11.02, 

115 date=DateTime( 

116 65321.1, DateTime.MJD, DateTime.TAI), 

117 ut1=12345.1, 

118 era=45.1*degrees, 

119 boresightRaDec=SpherePoint( 

120 23.1*degrees, 73.2*degrees), 

121 boresightAzAlt=SpherePoint( 

122 134.5*degrees, 33.3*degrees), 

123 boresightAirmass=1.73, 

124 boresightRotAngle=73.2*degrees, 

125 rotType=afwImage.RotType.SKY, 

126 observatory=Observatory( 

127 11.1*degrees, 22.2*degrees, 0.333), 

128 weather=Weather(1.1, 2.2, 34.5), 

129 instrumentLabel="TestCameraOne", 

130 id=987654, 

131 focusZ=1.5, 

132 observationType="flat", 

133 scienceProgram="test program", 

134 observationReason="test reason", 

135 object="test object", 

136 hasSimulatedContent=False, 

137 ) 

138 self.data1 = data1 

139 self.localEra1, self.hourAngle1 = computeLstHA(data1) 

140 data2 = VisitInfoData(exposureId=1, 

141 exposureTime=15.5, 

142 darkTime=17.8, 

143 date=DateTime( 

144 55321.2, DateTime.MJD, DateTime.TAI), 

145 ut1=312345.1, 

146 era=25.1*degrees, 

147 boresightRaDec=SpherePoint( 

148 2.1*degrees, 33.2*degrees), 

149 boresightAzAlt=SpherePoint(13.5*degrees, 83.3*degrees), 

150 boresightAirmass=2.05, 

151 boresightRotAngle=-53.2*degrees, 

152 rotType=afwImage.RotType.HORIZON, 

153 observatory=Observatory( 

154 22.2*degrees, 33.3*degrees, 0.444), 

155 weather=Weather(2.2, 3.3, 44.4), 

156 instrumentLabel="TestCameraTwo", 

157 id=123456, 

158 focusZ=-0.7, 

159 observationType="science", 

160 scienceProgram="test program 2", 

161 observationReason="test reason 2", 

162 object="test object 2", 

163 hasSimulatedContent=True, 

164 ) 

165 self.data2 = data2 

166 self.localEra2, self.hourAngle2 = computeLstHA(data2) 

167 

168 def _testValueConstructor(self, data, localEra, hourAngle): 

169 visitInfo = makeVisitInfo(data) 

170 with self.assertWarns(FutureWarning): 

171 self.assertEqual(visitInfo.getExposureId(), data.exposureId) 

172 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime) 

173 self.assertEqual(visitInfo.getDarkTime(), data.darkTime) 

174 self.assertEqual(visitInfo.getDate(), data.date) 

175 self.assertEqual(visitInfo.getUt1(), data.ut1) 

176 self.assertEqual(visitInfo.getEra(), data.era) 

177 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec) 

178 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt) 

179 self.assertEqual(visitInfo.getBoresightAirmass(), 

180 data.boresightAirmass) 

181 self.assertEqual(visitInfo.getBoresightRotAngle(), 

182 data.boresightRotAngle) 

183 self.assertEqual(visitInfo.getRotType(), data.rotType) 

184 self.assertEqual(visitInfo.getObservatory(), data.observatory) 

185 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel) 

186 self.assertEqual(visitInfo.getWeather(), data.weather) 

187 self.assertEqual(visitInfo.getLocalEra(), localEra) 

188 self.assertEqual(visitInfo.getBoresightHourAngle(), hourAngle) 

189 self.assertEqual(visitInfo.getId(), data.id) 

190 self.assertEqual(visitInfo.getFocusZ(), data.focusZ) 

191 self.assertEqual(visitInfo.getObservationType(), data.observationType) 

192 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram) 

193 self.assertEqual(visitInfo.getObservationReason(), data.observationReason) 

194 self.assertEqual(visitInfo.getObject(), data.object) 

195 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent) 

196 

197 def _testProperties(self, data, localEra, hourAngle): 

198 """Test property attribute accessors.""" 

199 visitInfo = makeVisitInfo(data) 

200 self.assertEqual(visitInfo.exposureTime, data.exposureTime) 

201 self.assertEqual(visitInfo.darkTime, data.darkTime) 

202 self.assertEqual(visitInfo.date, data.date) 

203 self.assertEqual(visitInfo.ut1, data.ut1) 

204 self.assertEqual(visitInfo.era, data.era) 

205 self.assertEqual(visitInfo.boresightRaDec, data.boresightRaDec) 

206 self.assertEqual(visitInfo.boresightAzAlt, data.boresightAzAlt) 

207 self.assertEqual(visitInfo.boresightAirmass, data.boresightAirmass) 

208 self.assertEqual(visitInfo.boresightRotAngle, data.boresightRotAngle) 

209 self.assertEqual(visitInfo.rotType, data.rotType) 

210 self.assertEqual(visitInfo.observatory, data.observatory) 

211 self.assertEqual(visitInfo.instrumentLabel, data.instrumentLabel) 

212 self.assertEqual(visitInfo.weather, data.weather) 

213 self.assertEqual(visitInfo.localEra, localEra) 

214 self.assertEqual(visitInfo.boresightHourAngle, hourAngle) 

215 self.assertEqual(visitInfo.id, data.id) 

216 self.assertEqual(visitInfo.focusZ, data.focusZ) 

217 self.assertEqual(visitInfo.observationType, data.observationType) 

218 self.assertEqual(visitInfo.scienceProgram, data.scienceProgram) 

219 self.assertEqual(visitInfo.observationReason, data.observationReason) 

220 self.assertEqual(visitInfo.object, data.object) 

221 self.assertEqual(visitInfo.hasSimulatedContent, data.hasSimulatedContent) 

222 

223 def testValueConstructor_data1(self): 

224 self._testValueConstructor(self.data1, self.localEra1, self.hourAngle1) 

225 self._testProperties(self.data1, self.localEra1, self.hourAngle1) 

226 

227 def testValueConstructor_data2(self): 

228 self._testValueConstructor(self.data2, self.localEra2, self.hourAngle2) 

229 self._testProperties(self.data2, self.localEra2, self.hourAngle2) 

230 

231 def testCopyWith(self): 

232 visitInfo1 = makeVisitInfo(self.data1) 

233 visitInfo2 = makeVisitInfo(self.data2) 

234 

235 updateFields1 = [ 

236 "exposureTime", 

237 "darkTime", 

238 "date", 

239 "ut1", 

240 "era", 

241 "boresightRaDec", 

242 "boresightAzAlt", 

243 "boresightAirmass", 

244 "boresightRotAngle", 

245 ] 

246 

247 updateFields2 = [ 

248 "rotType", 

249 "observatory", 

250 "weather", 

251 "instrumentLabel", 

252 "id", 

253 "focusZ", 

254 "observationType", 

255 "scienceProgram", 

256 "observationReason", 

257 "object", 

258 "hasSimulatedContent", 

259 ] 

260 

261 kwargs1 = {k: getattr(visitInfo2, k) for k in updateFields1} 

262 kwargs2 = {k: getattr(visitInfo2, k) for k in updateFields2} 

263 

264 # These both warn because exposureId is not passed in. 

265 with self.assertWarns(FutureWarning): 

266 newVisit1 = visitInfo1.copyWith(**kwargs1) 

267 newVisit2 = visitInfo1.copyWith(**kwargs2) 

268 

269 for field in updateFields1: 

270 self.assertEqual(getattr(newVisit1, field), getattr(visitInfo2, field)) 

271 self.assertEqual(getattr(newVisit2, field), getattr(visitInfo1, field)) 

272 

273 for field in updateFields2: 

274 self.assertEqual(getattr(newVisit1, field), getattr(visitInfo1, field)) 

275 self.assertEqual(getattr(newVisit2, field), getattr(visitInfo2, field)) 

276 

277 # Test the deprecated exposureId. 

278 # This code can be removed with DM-32138. 

279 deprecatedVisit = visitInfo1.copyWith(exposureId=3) 

280 with self.assertWarns(FutureWarning): 

281 self.assertEqual(deprecatedVisit.getExposureId(), 3) 

282 with self.assertWarns(FutureWarning): 

283 deprecatedCopy = deprecatedVisit.copyWith(**kwargs1) 

284 self.assertEqual(deprecatedCopy.getExposureId(), 3) 

285 

286 def testTablePersistence(self): 

287 """Test that VisitInfo can be round-tripped with current code. 

288 """ 

289 for item in (self.data1, self.data2): 

290 tablePath = os.path.join( 

291 self.testDir, "testVisitInfo_testTablePersistence.fits") 

292 v1 = afwImage.VisitInfo(*item) 

293 v1.writeFits(tablePath) 

294 v2 = afwImage.VisitInfo.readFits(tablePath) 

295 self.assertEqual(v1, v2) 

296 os.unlink(tablePath) 

297 

298 def _testFitsRead(self, data, filePath, version): 

299 """Test that old VersionInfo files are read correctly. 

300 

301 Parameters 

302 ---------- 

303 data : `VisitInfoData` 

304 The values expected to be stored in the file, or a 

305 superset thereof. 

306 filePath : `str` 

307 The file to test. 

308 version : `int` 

309 The VersionInfo persistence format used in ``filePath``. 

310 """ 

311 visitInfo = afwImage.VisitInfo.readFits(filePath) 

312 

313 if version >= 0: 

314 with self.assertWarns(FutureWarning): 

315 self.assertEqual(visitInfo.getExposureId(), data.exposureId) 

316 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime) 

317 self.assertEqual(visitInfo.getDarkTime(), data.darkTime) 

318 self.assertEqual(visitInfo.getDate(), data.date) 

319 self.assertEqual(visitInfo.getUt1(), data.ut1) 

320 self.assertEqual(visitInfo.getEra(), data.era) 

321 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec) 

322 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt) 

323 self.assertEqual(visitInfo.getBoresightAirmass(), 

324 data.boresightAirmass) 

325 self.assertEqual(visitInfo.getBoresightRotAngle(), 

326 data.boresightRotAngle) 

327 self.assertEqual(visitInfo.getRotType(), data.rotType) 

328 self.assertEqual(visitInfo.getObservatory(), data.observatory) 

329 self.assertEqual(visitInfo.getWeather(), data.weather) 

330 if version >= 1: 

331 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel) 

332 else: 

333 self.assertEqual(visitInfo.getInstrumentLabel(), "") 

334 if version >= 2: 

335 self.assertEqual(visitInfo.getId(), data.id) 

336 else: 

337 self.assertEqual(visitInfo.getId(), 0) 

338 if version >= 3: 

339 self.assertEqual(visitInfo.getFocusZ(), data.focusZ) 

340 else: 

341 self.assertTrue(math.isnan(visitInfo.getFocusZ())) 

342 if version >= 4: 

343 self.assertEqual(visitInfo.getObservationType(), data.observationType) 

344 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram) 

345 self.assertEqual(visitInfo.getObservationReason(), data.observationReason) 

346 self.assertEqual(visitInfo.getObject(), data.object) 

347 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent) 

348 else: 

349 self.assertEqual(visitInfo.getObservationType(), "") 

350 self.assertEqual(visitInfo.getScienceProgram(), "") 

351 self.assertEqual(visitInfo.getObservationReason(), "") 

352 self.assertEqual(visitInfo.getObject(), "") 

353 self.assertEqual(visitInfo.getHasSimulatedContent(), False) 

354 

355 def testPersistenceVersions(self): 

356 """Test that older versions are handled appropriately. 

357 """ 

358 dataDir = os.path.join(os.path.dirname(__file__), "data") 

359 

360 # All files created by makeVisitInfo(self.data1).writeFits() 

361 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-noversion.fits"), 0) 

362 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-1.fits"), 1) 

363 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-2.fits"), 2) 

364 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-3.fits"), 3) 

365 self._testFitsRead(self.data1, os.path.join(dataDir, "visitInfo-version-4.fits"), 4) 

366 

367 # Check that reading a newer file raises a useful exception. 

368 with self.assertRaisesRegex(TypeError, 

369 r"Cannot read VisitInfo FITS version > [0-9]+, found version 999999"): 

370 afwImage.VisitInfo.readFits(os.path.join(dataDir, "visitInfo-version-999999.fits")) 

371 

372 def testSetVisitInfoMetadata(self): 

373 for item in (self.data1, self.data2): 

374 visitInfo = makeVisitInfo(item) 

375 metadata = PropertyList() 

376 afwImage.setVisitInfoMetadata(metadata, visitInfo) 

377 self.assertEqual(metadata.nameCount(), 28) 

378 self.assertEqual(metadata.getScalar("EXPID"), item.exposureId) 

379 self.assertEqual(metadata.getScalar("EXPTIME"), item.exposureTime) 

380 self.assertEqual(metadata.getScalar("DARKTIME"), item.darkTime) 

381 self.assertEqual(metadata.getScalar("DATE-AVG"), 

382 item.date.toString(DateTime.TAI)) 

383 self.assertEqual(metadata.getScalar("TIMESYS"), "TAI") 

384 self.assertEqual(metadata.getScalar("MJD-AVG-UT1"), item.ut1) 

385 self.assertEqual(metadata.getScalar("AVG-ERA"), item.era.asDegrees()) 

386 self.assertEqual(metadata.getScalar("BORE-RA"), 

387 item.boresightRaDec[0].asDegrees()) 

388 self.assertEqual(metadata.getScalar("BORE-DEC"), 

389 item.boresightRaDec[1].asDegrees()) 

390 self.assertEqual(metadata.getScalar("BORE-AZ"), 

391 item.boresightAzAlt[0].asDegrees()) 

392 self.assertEqual(metadata.getScalar("BORE-ALT"), 

393 item.boresightAzAlt[1].asDegrees()) 

394 self.assertEqual(metadata.getScalar("BORE-AIRMASS"), 

395 item.boresightAirmass) 

396 self.assertEqual(metadata.getScalar("BORE-ROTANG"), 

397 item.boresightRotAngle.asDegrees()) 

398 self.assertEqual(metadata.getScalar("ROTTYPE"), 

399 RotTypeEnumNameDict[item.rotType]) 

400 self.assertEqual(metadata.getScalar("OBS-LONG"), 

401 item.observatory.getLongitude().asDegrees()) 

402 self.assertEqual(metadata.getScalar("OBS-LAT"), 

403 item.observatory.getLatitude().asDegrees()) 

404 self.assertEqual(metadata.getScalar("OBS-ELEV"), 

405 item.observatory.getElevation()) 

406 self.assertEqual(metadata.getScalar("AIRTEMP"), 

407 item.weather.getAirTemperature()) 

408 self.assertEqual(metadata.getScalar("AIRPRESS"), 

409 item.weather.getAirPressure()) 

410 self.assertEqual(metadata.getScalar("HUMIDITY"), 

411 item.weather.getHumidity()) 

412 self.assertEqual(metadata.getScalar("INSTRUMENT"), 

413 item.instrumentLabel) 

414 self.assertEqual(metadata.getScalar("IDNUM"), 

415 item.id) 

416 self.assertEqual(metadata.getScalar("FOCUSZ"), 

417 item.focusZ) 

418 self.assertEqual(metadata.getScalar("OBSTYPE"), 

419 item.observationType) 

420 self.assertEqual(metadata.getScalar("PROGRAM"), 

421 item.scienceProgram) 

422 self.assertEqual(metadata.getScalar("REASON"), 

423 item.observationReason) 

424 self.assertEqual(metadata.getScalar("OBJECT"), 

425 item.object) 

426 self.assertEqual(metadata.getScalar("HAS-SIMULATED-CONTENT"), 

427 item.hasSimulatedContent) 

428 

429 def testSetVisitInfoMetadataMissingValues(self): 

430 """If a value is unknown then it should not be written to the metadata""" 

431 # Only rot type and hasSimulatedContent are "known" when default- 

432 # constructed, by virtue of having no "null" state. 

433 visitInfo = afwImage.VisitInfo() 

434 metadata = PropertyList() 

435 afwImage.setVisitInfoMetadata(metadata, visitInfo) 

436 self.assertEqual(metadata.getScalar("ROTTYPE"), 

437 RotTypeEnumNameDict[afwImage.RotType.UNKNOWN]) 

438 self.assertEqual(metadata.getScalar("HAS-SIMULATED-CONTENT"), False) 

439 self.assertEqual(metadata.nameCount(), 2) 

440 

441 def testStripVisitInfoKeywords(self): 

442 for argList in (self.data1, self.data2): 

443 visitInfo = afwImage.VisitInfo(*argList) 

444 metadata = PropertyList() 

445 afwImage.setVisitInfoMetadata(metadata, visitInfo) 

446 # add an extra keyword that will not be stripped 

447 metadata.set("EXTRA", 5) 

448 self.assertEqual(metadata.nameCount(), 29) 

449 afwImage.stripVisitInfoKeywords(metadata) 

450 self.assertEqual(metadata.nameCount(), 1) 

451 

452 def _testIsEmpty(self, visitInfo): 

453 """Test that visitInfo is all NaN, 0, or empty string, as appropriate. 

454 """ 

455 with self.assertWarns(FutureWarning): 

456 self.assertEqual(visitInfo.getExposureId(), 0) 

457 self.assertTrue(math.isnan(visitInfo.getExposureTime())) 

458 self.assertTrue(math.isnan(visitInfo.getDarkTime())) 

459 self.assertEqual(visitInfo.getDate(), DateTime()) 

460 self.assertTrue(math.isnan(visitInfo.getUt1())) 

461 self.assertTrue(math.isnan(visitInfo.getEra().asDegrees())) 

462 for i in range(2): 

463 self.assertTrue(math.isnan( 

464 visitInfo.getBoresightRaDec()[i].asDegrees())) 

465 self.assertTrue(math.isnan( 

466 visitInfo.getBoresightAzAlt()[i].asDegrees())) 

467 self.assertTrue(math.isnan(visitInfo.getBoresightAirmass())) 

468 self.assertTrue(math.isnan( 

469 visitInfo.getBoresightRotAngle().asDegrees())) 

470 self.assertEqual(visitInfo.getRotType(), afwImage.RotType.UNKNOWN) 

471 self.assertTrue(math.isnan( 

472 visitInfo.getObservatory().getLongitude().asDegrees())) 

473 self.assertTrue(math.isnan( 

474 visitInfo.getObservatory().getLatitude().asDegrees())) 

475 self.assertTrue(math.isnan(visitInfo.getObservatory().getElevation())) 

476 self.assertTrue(math.isnan(visitInfo.getWeather().getAirTemperature())) 

477 self.assertTrue(math.isnan(visitInfo.getWeather().getAirPressure())) 

478 self.assertTrue(math.isnan(visitInfo.getWeather().getHumidity())) 

479 self.assertTrue(math.isnan(visitInfo.getBoresightHourAngle())) 

480 self.assertEqual(visitInfo.getInstrumentLabel(), "") 

481 self.assertEqual(visitInfo.getId(), 0) 

482 self.assertTrue(math.isnan(visitInfo.getFocusZ())) 

483 self.assertEqual(visitInfo.getObservationType(), "") 

484 self.assertEqual(visitInfo.getScienceProgram(), "") 

485 self.assertEqual(visitInfo.getObservationReason(), "") 

486 self.assertEqual(visitInfo.getObject(), "") 

487 self.assertEqual(visitInfo.getHasSimulatedContent(), False) 

488 

489 def testEquals(self): 

490 """Test that identical VisitInfo objects compare equal, even if some fields are NaN. 

491 """ 

492 # objects with "equal state" should be equal 

493 self.assertEqual(makeVisitInfo(self.data1), makeVisitInfo(self.data1)) 

494 self.assertEqual(makeVisitInfo(self.data2), makeVisitInfo(self.data2)) 

495 self.assertNotEqual(makeVisitInfo(self.data1), makeVisitInfo(self.data2)) 

496 self.assertEqual(afwImage.VisitInfo(), afwImage.VisitInfo()) 

497 

498 # equality must be reflexive 

499 info = makeVisitInfo(self.data1) 

500 self.assertEqual(info, info) 

501 info = makeVisitInfo(self.data2) 

502 self.assertEqual(info, info) 

503 info = afwImage.VisitInfo() 

504 self.assertEqual(info, info) 

505 

506 # commutativity and transitivity difficult to test with this setup 

507 

508 def testMetadataConstructor(self): 

509 """Test the metadata constructor 

510 

511 This constructor allows missing values 

512 """ 

513 data = self.data1 

514 

515 metadata = propertySetFromDict({}) 

516 visitInfo = afwImage.VisitInfo(metadata) 

517 self._testIsEmpty(visitInfo) 

518 

519 metadata = propertySetFromDict({"EXPID": data.exposureId}) 

520 visitInfo = afwImage.VisitInfo(metadata) 

521 with self.assertWarns(FutureWarning): 

522 self.assertEqual(visitInfo.getExposureId(), data.exposureId) 

523 self.assertTrue(math.isnan(visitInfo.getExposureTime())) 

524 

525 metadata = propertySetFromDict({"EXPTIME": data.exposureTime}) 

526 visitInfo = afwImage.VisitInfo(metadata) 

527 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime) 

528 

529 metadata = propertySetFromDict({"DARKTIME": data.darkTime}) 

530 visitInfo = afwImage.VisitInfo(metadata) 

531 self.assertEqual(visitInfo.getDarkTime(), data.darkTime) 

532 

533 metadata = propertySetFromDict( 

534 {"DATE-AVG": data.date.toString(DateTime.TAI), "TIMESYS": "TAI"}) 

535 visitInfo = afwImage.VisitInfo(metadata) 

536 self.assertEqual(visitInfo.getDate(), data.date) 

537 

538 # TIME-MID in UTC is an acceptable alternative to DATE-AVG 

539 metadata = propertySetFromDict( 

540 {"TIME-MID": data.date.toString(DateTime.UTC)}) 

541 visitInfo = afwImage.VisitInfo(metadata) 

542 self.assertEqual(visitInfo.getDate(), data.date) 

543 

544 # TIME-MID must be in UTC and TIMESYS is ignored 

545 metadata = propertySetFromDict({ 

546 "TIME-MID": data.date.toString(DateTime.TAI) + "Z", 

547 "TIMESYS": "TAI", 

548 }) 

549 visitInfo = afwImage.VisitInfo(metadata) 

550 self.assertNotEqual(visitInfo.getDate(), data.date) 

551 

552 # if both DATE-AVG and TIME-MID provided then use DATE-AVG 

553 # use the wrong time system for TIME-MID so if it is used, an error 

554 # will result 

555 metadata = propertySetFromDict({ 

556 "DATE-AVG": data.date.toString(DateTime.TAI), 

557 "TIMESYS": "TAI", 

558 "TIME-MID": data.date.toString(DateTime.TAI) + "Z", 

559 }) 

560 visitInfo = afwImage.VisitInfo(metadata) 

561 self.assertEqual(visitInfo.getDate(), data.date) 

562 

563 metadata = propertySetFromDict({"MJD-AVG-UT1": data.ut1}) 

564 visitInfo = afwImage.VisitInfo(metadata) 

565 self.assertEqual(visitInfo.getUt1(), data.ut1) 

566 

567 metadata = propertySetFromDict({"AVG-ERA": data.era.asDegrees()}) 

568 visitInfo = afwImage.VisitInfo(metadata) 

569 self.assertEqual(visitInfo.getEra(), data.era) 

570 

571 for i, key in enumerate(("BORE-RA", "BORE-DEC")): 

572 metadata = propertySetFromDict( 

573 {key: data.boresightRaDec[i].asDegrees()}) 

574 visitInfo = afwImage.VisitInfo(metadata) 

575 self.assertEqual(visitInfo.getBoresightRaDec() 

576 [i], data.boresightRaDec[i]) 

577 

578 for i, key in enumerate(("BORE-AZ", "BORE-ALT")): 

579 metadata = propertySetFromDict( 

580 {key: data.boresightAzAlt[i].asDegrees()}) 

581 visitInfo = afwImage.VisitInfo(metadata) 

582 self.assertEqual(visitInfo.getBoresightAzAlt() 

583 [i], data.boresightAzAlt[i]) 

584 

585 metadata = propertySetFromDict({"BORE-AIRMASS": data.boresightAirmass}) 

586 visitInfo = afwImage.VisitInfo(metadata) 

587 self.assertEqual(visitInfo.getBoresightAirmass(), 

588 data.boresightAirmass) 

589 

590 metadata = propertySetFromDict( 

591 {"BORE-ROTANG": data.boresightRotAngle.asDegrees()}) 

592 visitInfo = afwImage.VisitInfo(metadata) 

593 self.assertEqual(visitInfo.getBoresightRotAngle(), 

594 data.boresightRotAngle) 

595 

596 metadata = propertySetFromDict( 

597 {"ROTTYPE": RotTypeEnumNameDict[data.rotType]}) 

598 visitInfo = afwImage.VisitInfo(metadata) 

599 self.assertEqual(visitInfo.getRotType(), data.rotType) 

600 

601 metadata = propertySetFromDict( 

602 {"OBS-LONG": data.observatory.getLongitude().asDegrees()}) 

603 visitInfo = afwImage.VisitInfo(metadata) 

604 self.assertEqual(visitInfo.getObservatory().getLongitude(), 

605 data.observatory.getLongitude()) 

606 

607 metadata = propertySetFromDict( 

608 {"OBS-LAT": data.observatory.getLatitude().asDegrees()}) 

609 visitInfo = afwImage.VisitInfo(metadata) 

610 self.assertEqual(visitInfo.getObservatory().getLatitude(), 

611 data.observatory.getLatitude()) 

612 

613 metadata = propertySetFromDict( 

614 {"OBS-ELEV": data.observatory.getElevation()}) 

615 visitInfo = afwImage.VisitInfo(metadata) 

616 self.assertEqual(visitInfo.getObservatory().getElevation(), 

617 data.observatory.getElevation()) 

618 

619 metadata = propertySetFromDict( 

620 {"AIRTEMP": data.weather.getAirTemperature()}) 

621 visitInfo = afwImage.VisitInfo(metadata) 

622 self.assertEqual(visitInfo.getWeather().getAirTemperature(), 

623 data.weather.getAirTemperature()) 

624 

625 metadata = propertySetFromDict( 

626 {"AIRPRESS": data.weather.getAirPressure()}) 

627 visitInfo = afwImage.VisitInfo(metadata) 

628 self.assertEqual(visitInfo.getWeather().getAirPressure(), 

629 data.weather.getAirPressure()) 

630 

631 metadata = propertySetFromDict( 

632 {"HUMIDITY": data.weather.getHumidity()}) 

633 visitInfo = afwImage.VisitInfo(metadata) 

634 self.assertEqual(visitInfo.getWeather().getHumidity(), 

635 data.weather.getHumidity()) 

636 

637 metadata = propertySetFromDict({"INSTRUMENT": data.instrumentLabel}) 

638 visitInfo = afwImage.VisitInfo(metadata) 

639 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel) 

640 

641 metadata = propertySetFromDict({"IDNUM": data.id}) 

642 visitInfo = afwImage.VisitInfo(metadata) 

643 self.assertEqual(visitInfo.getId(), data.id) 

644 

645 metadata = propertySetFromDict({"FOCUSZ": data.focusZ}) 

646 visitInfo = afwImage.VisitInfo(metadata) 

647 self.assertEqual(visitInfo.getFocusZ(), data.focusZ) 

648 

649 metadata = propertySetFromDict({"OBSTYPE": data.observationType}) 

650 visitInfo = afwImage.VisitInfo(metadata) 

651 self.assertEqual(visitInfo.getObservationType(), data.observationType) 

652 

653 metadata = propertySetFromDict({"PROGRAM": data.scienceProgram}) 

654 visitInfo = afwImage.VisitInfo(metadata) 

655 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram) 

656 

657 metadata = propertySetFromDict({"REASON": data.observationReason}) 

658 visitInfo = afwImage.VisitInfo(metadata) 

659 self.assertEqual(visitInfo.getObservationReason(), data.observationReason) 

660 

661 metadata = propertySetFromDict({"OBJECT": data.object}) 

662 visitInfo = afwImage.VisitInfo(metadata) 

663 self.assertEqual(visitInfo.getObject(), data.object) 

664 

665 metadata = propertySetFromDict({"HAS-SIMULATED-CONTENT": data.hasSimulatedContent}) 

666 visitInfo = afwImage.VisitInfo(metadata) 

667 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent) 

668 

669 def testMetadataConstructorUndefined(self): 

670 """Test that we can create VisitInfo using None in metadata.""" 

671 

672 metadata = propertySetFromDict({ 

673 # These are examples of generic reader code. 

674 "PROGRAM": None, # A string should convert to "". 

675 "DARKTIME": None, # A missing float should convert to NaN. 

676 # These headers have special logic in the reader. 

677 "EXPTIME": None, 

678 "IDNUM": None, 

679 "DATE-AVG": None, 

680 "ROTTYPE": None, 

681 "HAS-SIMULATED-CONTENT": None, 

682 }) 

683 visitInfo = afwImage.VisitInfo(metadata) 

684 self.assertEqual(visitInfo.getScienceProgram(), "") 

685 self.assertTrue(math.isnan(visitInfo.getDarkTime())) 

686 self.assertTrue(math.isnan(visitInfo.getExposureTime())) 

687 self.assertEqual(visitInfo.getId(), 0) 

688 self.assertFalse(visitInfo.getDate().isValid()) 

689 

690 def testConstructorKeywordArguments(self): 

691 """Test VisitInfo with named arguments""" 

692 data = self.data1 

693 

694 visitInfo = afwImage.VisitInfo() 

695 self._testIsEmpty(visitInfo) 

696 

697 visitInfo = afwImage.VisitInfo(exposureId=data.exposureId) 

698 with self.assertWarns(FutureWarning): 

699 self.assertEqual(visitInfo.getExposureId(), data.exposureId) 

700 self.assertTrue(math.isnan(visitInfo.getExposureTime())) 

701 

702 visitInfo = afwImage.VisitInfo(exposureTime=data.exposureTime) 

703 self.assertEqual(visitInfo.getExposureTime(), data.exposureTime) 

704 

705 visitInfo = afwImage.VisitInfo(darkTime=data.darkTime) 

706 self.assertEqual(visitInfo.getDarkTime(), data.darkTime) 

707 

708 visitInfo = afwImage.VisitInfo(date=data.date) 

709 self.assertEqual(visitInfo.getDate(), data.date) 

710 

711 visitInfo = afwImage.VisitInfo(ut1=data.ut1) 

712 self.assertEqual(visitInfo.getUt1(), data.ut1) 

713 

714 visitInfo = afwImage.VisitInfo(era=data.era) 

715 self.assertEqual(visitInfo.getEra(), data.era) 

716 

717 visitInfo = afwImage.VisitInfo(boresightRaDec=data.boresightRaDec) 

718 self.assertEqual(visitInfo.getBoresightRaDec(), data.boresightRaDec) 

719 

720 visitInfo = afwImage.VisitInfo(boresightAzAlt=data.boresightAzAlt) 

721 self.assertEqual(visitInfo.getBoresightAzAlt(), data.boresightAzAlt) 

722 

723 visitInfo = afwImage.VisitInfo(boresightAirmass=data.boresightAirmass) 

724 self.assertEqual(visitInfo.getBoresightAirmass(), 

725 data.boresightAirmass) 

726 

727 visitInfo = afwImage.VisitInfo( 

728 boresightRotAngle=data.boresightRotAngle) 

729 self.assertEqual(visitInfo.getBoresightRotAngle(), 

730 data.boresightRotAngle) 

731 

732 visitInfo = afwImage.VisitInfo(rotType=data.rotType) 

733 self.assertEqual(visitInfo.getRotType(), data.rotType) 

734 

735 visitInfo = afwImage.VisitInfo(observatory=data.observatory) 

736 self.assertEqual(visitInfo.getObservatory(), data.observatory) 

737 

738 visitInfo = afwImage.VisitInfo(weather=data.weather) 

739 self.assertEqual(visitInfo.getWeather(), data.weather) 

740 

741 visitInfo = afwImage.VisitInfo(instrumentLabel=data.instrumentLabel) 

742 self.assertEqual(visitInfo.getInstrumentLabel(), data.instrumentLabel) 

743 

744 visitInfo = afwImage.VisitInfo(id=data.id) 

745 self.assertEqual(visitInfo.getId(), data.id) 

746 

747 visitInfo = afwImage.VisitInfo(focusZ=data.focusZ) 

748 self.assertEqual(visitInfo.getFocusZ(), data.focusZ) 

749 

750 visitInfo = afwImage.VisitInfo(observationType=data.observationType) 

751 self.assertEqual(visitInfo.getObservationType(), data.observationType) 

752 

753 visitInfo = afwImage.VisitInfo(scienceProgram=data.scienceProgram) 

754 self.assertEqual(visitInfo.getScienceProgram(), data.scienceProgram) 

755 

756 visitInfo = afwImage.VisitInfo(observationReason=data.observationReason) 

757 self.assertEqual(visitInfo.getObservationReason(), data.observationReason) 

758 

759 visitInfo = afwImage.VisitInfo(object=data.object) 

760 self.assertEqual(visitInfo.getObject(), data.object) 

761 

762 visitInfo = afwImage.VisitInfo(hasSimulatedContent=data.hasSimulatedContent) 

763 self.assertEqual(visitInfo.getHasSimulatedContent(), data.hasSimulatedContent) 

764 

765 def testGoodRotTypes(self): 

766 """Test round trip of all valid rot types""" 

767 for rotType in RotTypeEnumNameDict: 

768 metadata = propertySetFromDict( 

769 {"ROTTYPE": RotTypeEnumNameDict[rotType]}) 

770 visitInfo = afwImage.VisitInfo(metadata) 

771 self.assertEqual(visitInfo.getRotType(), rotType) 

772 

773 def testBadRotTypes(self): 

774 """Test that invalid rot type names cannot be used to construct a VisitInfo""" 

775 for badRotTypeName in ( 

776 "unknown", # must be all uppercase 

777 "sky", # must be all uppercase 

778 "Sky", # must be all uppercase 

779 "SKY1", # extra chars 

780 "HORIZONTAL", # extra chars 

781 ): 

782 metadata = propertySetFromDict({"ROTTYPE": badRotTypeName}) 

783 with self.assertRaises(lsst.pex.exceptions.RuntimeError): 

784 afwImage.VisitInfo(metadata) 

785 

786 def test_str(self): 

787 """Check that we get something reasonable for str()""" 

788 visitInfo = makeVisitInfo(self.data1) 

789 string = str(visitInfo) 

790 self.assertEqual(string, 

791 "VisitInfo(exposureId=10313423, exposureTime=10.01, darkTime=11.02, " 

792 "date=2037-09-20T02:24:00.000000000, UT1=12345.1, ERA=0.787143 rad, " 

793 "boresightRaDec=(23.1000000000, +73.2000000000), " 

794 "boresightAzAlt=(134.5000000000, +33.3000000000), boresightAirmass=1.73, " 

795 "boresightRotAngle=1.27758 rad, rotType=1, observatory=22.2N, 11.1E 0.333, " 

796 "weather=Weather(1.1, 2.2, 34.5), instrumentLabel='TestCameraOne', " 

797 "id=987654, focusZ=1.5, observationType='flat', scienceProgram='test program', " 

798 "observationReason='test reason', object='test object', hasSimulatedContent=false)") 

799 

800 def testParallacticAngle(self): 

801 """Check that we get the same precomputed values for parallactic angle.""" 

802 parallacticAngle = [141.39684140703142*degrees, 76.99982166973487*degrees] 

803 for item, parAngle in zip((self.data1, self.data2), parallacticAngle): 

804 visitInfo = afwImage.VisitInfo(era=item.era, 

805 boresightRaDec=item.boresightRaDec, 

806 observatory=item.observatory, 

807 ) 

808 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), parAngle) 

809 

810 def testParallacticAngleNorthMeridian(self): 

811 """An observation on the Meridian that is North of zenith has a parallactic angle of pi radians.""" 

812 meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude() 

813 northBoresightDec = self.data1.observatory.getLatitude() + 10.*degrees 

814 visitInfo = afwImage.VisitInfo(era=self.data1.era, 

815 boresightRaDec=SpherePoint(meridianBoresightRA, 

816 northBoresightDec), 

817 observatory=self.data1.observatory, 

818 ) 

819 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(np.pi)) 

820 

821 def testParallacticAngleSouthMeridian(self): 

822 """An observation on the Meridian that is South of zenith has a parallactic angle of zero.""" 

823 meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude() 

824 southBoresightDec = self.data1.observatory.getLatitude() - 10.*degrees 

825 visitInfo = afwImage.VisitInfo(era=self.data1.era, 

826 boresightRaDec=SpherePoint(meridianBoresightRA, 

827 southBoresightDec), 

828 observatory=self.data1.observatory, 

829 ) 

830 self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(0.)) 

831 

832 

833def setup_module(module): 

834 lsst.utils.tests.init() 

835 

836 

837class MemoryTester(lsst.utils.tests.MemoryTestCase): 

838 pass 

839 

840 

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

842 lsst.utils.tests.init() 

843 unittest.main()