Coverage for tests/test_cameras.py: 43%

71 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-16 15:24 -0700

1# This file is part of lsst-images. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

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

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

7# for details of code ownership. 

8# 

9# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11 

12from __future__ import annotations 

13 

14import os 

15from typing import Any 

16 

17import pytest 

18 

19from lsst.images import YX 

20from lsst.images.cameras import AmplifierRawGeometry, Detector, ReadoutCorner 

21from lsst.images.tests import DP2_VISIT_DETECTOR_DATA_ID, RoundtripFits, compare_detector_to_legacy 

22 

23EXTERNAL_DATA_DIR = os.environ.get("TESTDATA_IMAGES_DIR", None) 

24 

25 

26@pytest.fixture(scope="session") 

27def legacy_camera_data() -> dict[str, Any]: 

28 """Return a dict with legacy_camera and legacy_detector loaded from the 

29 test data directory. 

30 """ 

31 if EXTERNAL_DATA_DIR is None: 31 ↛ 33line 31 didn't jump to line 33 because the condition on line 31 was always true

32 pytest.skip("TESTDATA_IMAGES_DIR is not in the environment.") 

33 try: 

34 from lsst.afw.cameraGeom import Camera as LegacyCamera 

35 from lsst.afw.image import ExposureFitsReader 

36 except ImportError: 

37 pytest.skip("lsst.afw could not be imported.") 

38 legacy_camera = LegacyCamera.readFits(os.path.join(EXTERNAL_DATA_DIR, "dp2", "legacy", "camera.fits")) 

39 legacy_detector = ExposureFitsReader( 

40 os.path.join(EXTERNAL_DATA_DIR, "dp2", "legacy", "visit_image.fits") 

41 ).readDetector() 

42 return {"legacy_camera": legacy_camera, "legacy_detector": legacy_detector} 

43 

44 

45def test_visit_image_detector_legacy_conversions(legacy_camera_data: dict[str, Any]) -> None: 

46 """Test converting a visit-image detector from legacy and back with 

47 serialization in between. 

48 """ 

49 legacy_detector = legacy_camera_data["legacy_detector"] 

50 detector = Detector.from_legacy( 

51 legacy_detector, 

52 instrument=DP2_VISIT_DETECTOR_DATA_ID["instrument"], 

53 visit=DP2_VISIT_DETECTOR_DATA_ID["visit"], 

54 ) 

55 compare_detector_to_legacy(detector, legacy_detector, is_raw_assembled=True) 

56 with RoundtripFits(detector) as roundtrip: 

57 pass 

58 compare_detector_to_legacy(roundtrip.result, legacy_detector, is_raw_assembled=True) 

59 compare_detector_to_legacy(detector, roundtrip.result.to_legacy(), is_raw_assembled=True) 

60 

61 

62def test_camera_detector_legacy_conversions(legacy_camera_data: dict[str, Any]) -> None: 

63 """Test converting one detector of each type from a legacy Camera and 

64 back. 

65 """ 

66 legacy_camera = legacy_camera_data["legacy_camera"] 

67 detector_types_seen = set() 

68 for legacy_detector_1 in legacy_camera: 

69 if legacy_detector_1.getType() in detector_types_seen: 

70 continue 

71 detector_types_seen.add(legacy_detector_1.getType()) 

72 detector = Detector.from_legacy( 

73 legacy_detector_1, 

74 instrument=DP2_VISIT_DETECTOR_DATA_ID["instrument"], 

75 ) 

76 compare_detector_to_legacy(detector, legacy_detector_1, is_raw_assembled=False) 

77 with RoundtripFits(detector) as roundtrip: 

78 pass 

79 compare_detector_to_legacy(roundtrip.result, legacy_detector_1, is_raw_assembled=False) 

80 legacy_detector_2 = roundtrip.result.to_legacy() 

81 compare_detector_to_legacy(detector, legacy_detector_2, is_raw_assembled=False) 

82 

83 

84def test_expanded_detector_roundtrip(legacy_camera_data: dict[str, Any]) -> None: 

85 """Test round-tripping a detector that holds both assembled and 

86 unassembled raw amplifier geometry. 

87 """ 

88 legacy_camera = legacy_camera_data["legacy_camera"] 

89 legacy_detector = legacy_camera_data["legacy_detector"] 

90 legacy_camera_detector = legacy_camera[DP2_VISIT_DETECTOR_DATA_ID["detector"]] 

91 detector = Detector.from_legacy( 

92 legacy_camera_detector, 

93 instrument=DP2_VISIT_DETECTOR_DATA_ID["instrument"], 

94 visit=DP2_VISIT_DETECTOR_DATA_ID["visit"], 

95 is_raw_assembled=False, 

96 ) 

97 for amplifier, legacy_assembled_amplifier in zip( 

98 detector.amplifiers, legacy_detector.getAmplifiers(), strict=True 

99 ): 

100 assert amplifier.unassembled_raw_geometry is not None 

101 assert amplifier.assembled_raw_geometry is None 

102 amplifier.assembled_raw_geometry = AmplifierRawGeometry.from_legacy_amplifier( 

103 legacy_assembled_amplifier 

104 ) 

105 with RoundtripFits(detector) as roundtrip: 

106 pass 

107 compare_detector_to_legacy(roundtrip.result, legacy_camera_detector, is_raw_assembled=False) 

108 compare_detector_to_legacy(roundtrip.result, legacy_detector, is_raw_assembled=True) 

109 

110 

111def test_as_flips() -> None: 

112 """Tes that ReadoutCorner.as_flips returns the correct YX flip flags for 

113 each corner. 

114 """ 

115 assert ReadoutCorner.LL.as_flips() == YX(y=False, x=False) 

116 assert ReadoutCorner.LR.as_flips() == YX(y=False, x=True) 

117 assert ReadoutCorner.UL.as_flips() == YX(y=True, x=False) 

118 assert ReadoutCorner.UR.as_flips() == YX(y=True, x=True) 

119 

120 

121def test_flips_roundtrip() -> None: 

122 """Test that ReadoutCorner.from_flips is the inverse of as_flips for all 

123 corners. 

124 """ 

125 for corner in ReadoutCorner: 

126 flips = corner.as_flips() 

127 assert ReadoutCorner.from_flips(y=flips.y, x=flips.x) is corner 

128 

129 

130def test_apply_flips() -> None: 

131 """Test that ReadoutCorner.apply_flips with identity returns the same 

132 corner, and a few specific flips are correct. 

133 """ 

134 for corner in ReadoutCorner: 

135 assert corner.apply_flips(y=False, x=False) is corner 

136 assert ReadoutCorner.LL.apply_flips(y=True, x=True) is ReadoutCorner.UR 

137 assert ReadoutCorner.LR.apply_flips(y=False, x=True) is ReadoutCorner.LL 

138 assert ReadoutCorner.UL.apply_flips(y=True, x=False) is ReadoutCorner.LL 

139 assert ReadoutCorner.UR.apply_flips(y=True, x=True) is ReadoutCorner.LL