Coverage for tests/test_io_hierarchical_blend_data.py: 96%

66 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-07-09 02:24 -0700

1# This file is part of meas_extensions_scarlet. 

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# 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 <https://www.gnu.org/licenses/>. 

21 

22"""Tests for the meas-owned ``LsstHierarchicalBlendData`` and its independent 

23schema lineage. 

24""" 

25 

26import unittest 

27 

28import numpy as np 

29 

30import lsst.scarlet.lite as scl 

31import lsst.utils.tests 

32from lsst.afw.table import SourceCatalog, SourceTable 

33from lsst.meas.extensions.scarlet.io import hierarchical_blend_data as hbd_module 

34from lsst.meas.extensions.scarlet.io import ( 

35 LsstHierarchicalBlendData, 

36 LsstScarletModelData, 

37 updateCatalogFootprints, 

38) 

39from lsst.scarlet.lite.io.blend_base import ScarletBlendBaseData 

40from lsst.scarlet.lite.io.migration import MigrationRegistry 

41 

42 

43DEFAULT_SHAPE = (5, 6) 

44DEFAULT_ORIGIN = (10, 20) 

45 

46 

47def get_spans(shape=(5, 6)): 

48 spans = np.zeros(shape, dtype=bool) 

49 spans[1:4, 2:5] = True 

50 return spans 

51 

52 

53def get_child(): 

54 return scl.io.ScarletBlendData(origin=DEFAULT_ORIGIN, shape=DEFAULT_SHAPE, sources={}) 

55 

56 

57class TestLsstHierarchicalBlendData(lsst.utils.tests.TestCase): 

58 """Round-trip, conversion, and lineage tests for 

59 LsstHierarchicalBlendData. 

60 """ 

61 

62 def test_roundtrip(self): 

63 spans = get_spans() 

64 child = scl.io.ScarletBlendData(origin=(11, 22), shape=(3, 4), sources={}) 

65 blend = LsstHierarchicalBlendData( 

66 children={7: child}, 

67 span_array=spans, 

68 origin=DEFAULT_ORIGIN, 

69 metadata={"note": "detected parent"}, 

70 ) 

71 blend2 = ScarletBlendBaseData.from_dict(blend.as_dict()) 

72 self.assertIsInstance(blend2, LsstHierarchicalBlendData) 

73 

74 # Schema/dispatch tags. 

75 self.assertEqual(blend2.blend_type, "lsst_hierarchical") 

76 self.assertEqual(blend2.version, hbd_module.CURRENT_SCHEMA) 

77 

78 # Footprint and provenance. 

79 np.testing.assert_array_equal(blend2.span_array, spans) 

80 self.assertEqual(blend2.span_array.dtype, np.dtype(bool)) 

81 self.assertEqual(tuple(blend2.origin), DEFAULT_ORIGIN) 

82 self.assertFalse(blend2.legacy_spans) 

83 

84 # Derived shape/bbox properties. 

85 self.assertEqual(blend2.shape, spans.shape) 

86 self.assertEqual(blend2.bbox.origin, DEFAULT_ORIGIN) 

87 self.assertEqual(blend2.bbox.shape, spans.shape) 

88 

89 # Children round-trip to their concrete type with fields intact. 

90 self.assertEqual(set(blend2.children), {7}) 

91 child2 = blend2.children[7] 

92 self.assertIsInstance(child2, scl.io.ScarletBlendData) 

93 self.assertEqual(tuple(child2.origin), (11, 22)) 

94 self.assertEqual(tuple(child2.shape), (3, 4)) 

95 

96 # Residual metadata survives. 

97 self.assertEqual(blend2.metadata, {"note": "detected parent"}) 

98 

99 def test_registry_routes_new_tag(self): 

100 blend = LsstHierarchicalBlendData( 

101 children={1: get_child()}, span_array=get_spans(), origin=DEFAULT_ORIGIN 

102 ) 

103 data = blend.as_dict() 

104 self.assertEqual(data["blend_type"], "lsst_hierarchical") 

105 self.assertIs(ScarletBlendBaseData.blend_registry["lsst_hierarchical"], 

106 LsstHierarchicalBlendData) 

107 

108 def test_scarlet_lite_hierarchical_not_overridden(self): 

109 self.assertIs( 

110 ScarletBlendBaseData.blend_registry["hierarchical"], 

111 scl.io.HierarchicalBlendData, 

112 ) 

113 # Independent lineages, each with its own current version. 

114 self.assertEqual(MigrationRegistry.current["lsst_hierarchical"], 

115 hbd_module.CURRENT_SCHEMA) 

116 self.assertEqual(MigrationRegistry.current["hierarchical"], 

117 scl.io.hierarchical_blend.CURRENT_SCHEMA) 

118 

119 def test_convert_synthesis_rejects_nested_children(self): 

120 nested = scl.io.HierarchicalBlendData( 

121 children={1: scl.io.HierarchicalBlendData(children={2: get_child()})} 

122 ) 

123 with self.assertRaises(NotImplementedError): 

124 LsstHierarchicalBlendData.convert_from_hierarchical(nested.as_dict()) 

125 

126 def test_updateCatalogFootprints_rejects_non_hierarchical(self): 

127 modelData = LsstScarletModelData( 

128 bands=("g",), 

129 model_psf=np.ones((3, 3), dtype=np.float32), 

130 psf=np.ones((1, 3, 3), dtype=np.float32), 

131 ) 

132 modelData.blends[1] = scl.io.ScarletBlendData( 

133 origin=(0, 0), shape=(3, 3), sources={} 

134 ) 

135 catalog = SourceCatalog(SourceTable.makeMinimalSchema()) 

136 with self.assertRaises(ValueError) as cm: 

137 updateCatalogFootprints(modelData, catalog, "g") 

138 self.assertIn("LsstHierarchicalBlendData", str(cm.exception)) 

139 

140 

141def setup_module(module): 

142 lsst.utils.tests.init() 

143 

144 

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

146 pass 

147 

148 

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

150 lsst.utils.tests.init() 

151 unittest.main()