Coverage for tests/test_io_hierarchical_blend_data.py: 96%
66 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-09 09:32 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-09 09:32 +0000
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/>.
22"""Tests for the meas-owned ``LsstHierarchicalBlendData`` and its independent
23schema lineage.
24"""
26import unittest
28import numpy as np
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
43DEFAULT_SHAPE = (5, 6)
44DEFAULT_ORIGIN = (10, 20)
47def get_spans(shape=(5, 6)):
48 spans = np.zeros(shape, dtype=bool)
49 spans[1:4, 2:5] = True
50 return spans
53def get_child():
54 return scl.io.ScarletBlendData(origin=DEFAULT_ORIGIN, shape=DEFAULT_SHAPE, sources={})
57class TestLsstHierarchicalBlendData(lsst.utils.tests.TestCase):
58 """Round-trip, conversion, and lineage tests for
59 LsstHierarchicalBlendData.
60 """
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)
74 # Schema/dispatch tags.
75 self.assertEqual(blend2.blend_type, "lsst_hierarchical")
76 self.assertEqual(blend2.version, hbd_module.CURRENT_SCHEMA)
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)
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)
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))
96 # Residual metadata survives.
97 self.assertEqual(blend2.metadata, {"note": "detected parent"})
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)
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)
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())
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))
141def setup_module(module):
142 lsst.utils.tests.init()
145class MemoryTester(lsst.utils.tests.MemoryTestCase):
146 pass
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()