Coverage for python / lsst / obs / base / tests.py: 27%
41 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 09:02 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 09:02 +0000
1# This file is part of obs_base.
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"""
23Test utilities for obs_base and concrete obs* packages.
24"""
26from __future__ import annotations
28__all__ = (
29 "ObsTests",
30 "make_ramp_array",
31 "make_ramp_exposure_trimmed",
32 "make_ramp_exposure_untrimmed",
33)
35import logging
36from typing import TYPE_CHECKING, Any
38import numpy as np
40from lsst.afw.cameraGeom.utils import calcRawCcdBBox
41from lsst.afw.image import Exposure
43from . import butler_tests, camera_tests
45if TYPE_CHECKING:
46 import lsst.afw.cameraGeom
47 import lsst.afw.geom
48 import lsst.afw.image
49 import lsst.daf.butler
50 import lsst.geom
53class ObsTests(butler_tests.ButlerGetTests, camera_tests.CameraTests):
54 """Aggregator class for all of the obs_* test classes.
56 Inherit from this class, then lsst.utils.tests.TestCase, in that order.
58 Examples
59 --------
60 Example subclass:
62 .. code-block:: python
64 class TestObs(
65 lsst.obs.base.tests.ObsTests, lsst.utils.tests.TestCase
66 ):
67 def setUp(self):
68 self.setUp_tests(...)
69 self.setUp_butler_get(...)
70 self.setUp_camera(...)
72 Notes
73 -----
74 The intention is for each obs package to have a single test class that
75 inherits from this collector class, thus "automatically" getting all new
76 tests. If those tests require setup that isn't defined in a given obs
77 package, that obs package will be broken until updated. This is
78 intentional, as a way to prevent obs packages from falling behind out of
79 neglect.
80 """
82 def setUp_tests(self, butler: lsst.daf.butler.Butler, dataIds: dict[str, Any]) -> Any:
83 """Set up the necessary shared variables used by multiple tests.
85 Parameters
86 ----------
87 butler : `lsst.daf.butler.Butler`
88 A butler object, instantiated on the testdata repository for the
89 obs package being tested.
90 dataIds : `dict`
91 dictionary of (exposure name): (dataId of that exposure in the
92 testdata repository), with unittest.SkipTest as the value for any
93 exposures you do not have/do not want to test. It must contain a
94 valid 'raw' dataId, in addition to 'bias','flat','dark', which may
95 be set to SkipTest. For example::
97 self.dataIds = {
98 "raw": {"visit": 1, "filter": "g"},
99 "bias": {"visit": 1},
100 "flat": {"visit": 1},
101 "dark": unittest.SkipTest,
102 }
103 """
104 self.butler = butler
105 self.dataIds = dataIds
106 self.log = logging.getLogger(__name__)
108 def tearDown(self) -> None:
109 del self.butler
110 super().tearDown() # type: ignore[misc]
113def make_ramp_array(bbox: lsst.geom.Box2I, pedestal: int) -> tuple[np.ndarray, int]:
114 """Make a 2-d ramp array.
116 Parameters
117 ----------
118 bbox : `lsst.geom.Box2I`
119 Bounding box for the array.
120 pedestal : `int`
121 Minimum value for the ramp.
123 Returns
124 -------
125 ramp : `numpy.ndarray`
126 A 2-d array with shape ``(bbox.getHeight(), bbox.getWidth())``.
127 end : `int`
128 One past the maximum value in the ramp (for use as the
129 pedestal for another box).
130 """
131 end = pedestal + bbox.getArea()
132 return np.arange(pedestal, end).reshape(bbox.getHeight(), bbox.getWidth()), end
135def make_ramp_exposure_untrimmed(
136 detector: lsst.afw.cameraGeom.Detector, dtype: np.dtype | None = None
137) -> Exposure:
138 """Create an untrimmed, assembled exposure with different ramps for
139 each sub-amplifier region.
141 Parameters
142 ----------
143 detector : `lsst.afw.cameraGeom.Detector`
144 Detector object that the new exposure should match. Must have all amp
145 flips and offsets set to False/zero (i.e. represent an already-
146 assembled image).
147 dtype : `numpy.dtype`, optional
148 Type of the new exposure. Defaults to ``int32``.
150 Returns
151 -------
152 exposure : `lsst.afw.image.Exposure`
153 New exposure with the given detector attached.
154 """
155 if dtype is None:
156 dtype = np.dtype(np.int32)
157 ramp_exposure = Exposure(calcRawCcdBBox(detector), dtype=np.dtype(dtype))
158 ramp_exposure.setDetector(detector)
159 pedestal = 0
160 for amp in detector:
161 for name in ("HorizontalOverscan", "VerticalOverscan", "Prescan", "Data"):
162 bbox = getattr(amp, f"getRaw{name}BBox")()
163 ramp, pedestal = make_ramp_array(bbox, pedestal)
164 ramp_exposure.image[bbox].array[:, :] = ramp
165 return ramp_exposure
168def make_ramp_exposure_trimmed(
169 detector: lsst.afw.cameraGeom.Detector, dtype: np.dtype | None = None
170) -> Exposure:
171 """Create a trimmed, assembled exposure with different ramps for
172 each amplifier region.
174 Parameters
175 ----------
176 detector : `lsst.afw.cameraGeom.Detector`
177 Detector object that the new exposure should match.
178 dtype : `numpy.dtype`, optional
179 Type of the new exposure. Defaults to ``int32``.
181 Returns
182 -------
183 exposure : `lsst.afw.image.Exposure`
184 New exposure with the given detector attached.
185 """
186 if dtype is None:
187 dtype = np.dtype(np.int32)
188 ramp_exposure = Exposure(detector.getBBox(), dtype=np.dtype(dtype))
189 ramp_exposure.setDetector(detector)
190 pedestal = 0
191 for amp in detector:
192 ramp, pedestal = make_ramp_array(amp.getBBox(), pedestal)
193 ramp_exposure.image[amp.getBBox()].array[:, :] = ramp
194 return ramp_exposure