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