Coverage for tests/test_intrinsicZernikes.py: 24%
94 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-30 02:05 -0700
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-30 02:05 -0700
1# This file is part of ip_isr.
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/>.
22import unittest
23import tempfile
25import numpy as np
26from astropy.table import Table
27import astropy.units as u
29import lsst.utils.tests
31from lsst.ip.isr import IntrinsicZernikes
34class IntrinsicZernikesTestCase(lsst.utils.tests.TestCase):
35 """Test the IntrinsicZernikes calibration class."""
37 def setUp(self):
38 """Create test data for intrinsic Zernikes."""
39 # Create test Zernike coefficients for Noll indices 4, 5, 6
40 # (defocus, astigmatism)
41 self.noll_indices = np.array([4, 5, 6])
43 # Build a regular 3x3 grid of sample points
44 x_unique = np.array([-1.0, 0.0, 1.0])
45 y_unique = np.array([-0.5, 0.0, 0.5])
46 x_grid, y_grid = np.meshgrid(x_unique, y_unique)
47 self.field_x = x_grid.ravel()
48 self.field_y = y_grid.ravel()
50 # Create values: shape (n_points, n_zernikes)
51 rng = np.random.default_rng(seed=57721)
52 self.values = rng.normal(
53 scale=0.1,
54 size=(len(self.field_x), len(self.noll_indices))
55 ) # microns
57 # Create an astropy table in the format expected by __init__
58 self.inputTable = Table()
59 self.inputTable["x"] = self.field_x * u.deg
60 self.inputTable["y"] = self.field_y * u.deg
62 # Add Zernike columns
63 for i, noll in enumerate(self.noll_indices):
64 self.inputTable[f"Z{noll}"] = self.values[:, i] * u.um
66 # Create the calibration object
67 self.calib = IntrinsicZernikes(table=self.inputTable)
69 def test_initialization_with_table(self):
70 """Test that IntrinsicZernikes initializes correctly from a table."""
71 np.testing.assert_array_equal(self.calib.field_x, self.field_x)
72 np.testing.assert_array_equal(self.calib.field_y, self.field_y)
73 np.testing.assert_array_equal(self.calib.noll_indices, self.noll_indices)
74 np.testing.assert_array_equal(self.calib.values, self.values)
75 self.assertIsNotNone(self.calib.interpolator)
77 def test_metadata(self):
78 """Test that metadata is properly set."""
79 metadata = self.calib.getMetadata()
80 self.assertEqual(metadata["OBSTYPE"], "INTRINSIC_ZERNIKES")
81 self.assertEqual(metadata["INTRINSIC_ZERNIKES_SCHEMA"], "Intrinsic Zernikes")
82 self.assertEqual(metadata["INTRINSIC_ZERNIKES_VERSION"], 1.0)
84 def test_dict_roundtrip(self):
85 """Test round-tripping through dictionary."""
86 newCalib = IntrinsicZernikes.fromDict(self.calib.toDict())
87 self.assertEqual(newCalib, self.calib)
89 def test_table_roundtrip(self):
90 """Test round-tripping through table."""
91 newCalib = IntrinsicZernikes.fromTable(self.calib.toTable())
92 self.assertEqual(newCalib, self.calib)
94 def test_yaml_roundtrip(self):
95 """Test round-tripping through YAML file."""
96 with tempfile.TemporaryDirectory() as tempdir:
97 import os
98 filename = os.path.join(tempdir, "intrinsic_zernikes.yaml")
100 self.calib.writeText(filename)
101 newCalib = IntrinsicZernikes.readText(filename)
102 self.assertEqual(newCalib, self.calib)
104 def test_ecsv_roundtrip(self):
105 """Test round-tripping through ECSV file."""
106 with tempfile.TemporaryDirectory() as tempdir:
107 import os
108 filename = os.path.join(tempdir, "intrinsic_zernikes.ecsv")
110 self.calib.writeText(filename)
111 newCalib = IntrinsicZernikes.readText(filename)
112 self.assertEqual(newCalib, self.calib)
114 def test_fits_roundtrip(self):
115 """Test round-tripping through FITS file."""
116 with tempfile.TemporaryDirectory() as tempdir:
117 import os
118 filename = os.path.join(tempdir, "intrinsic_zernikes.fits")
120 self.calib.writeFits(filename)
121 newCalib = IntrinsicZernikes.readFits(filename)
122 self.assertEqual(newCalib, self.calib)
124 def test_fromDict_wrong_obstype(self):
125 """Test that fromDict raises error for wrong OBSTYPE."""
126 outDict = self.calib.toDict()
127 outDict["metadata"]["OBSTYPE"] = "WRONG_TYPE"
129 with self.assertRaises(RuntimeError) as context:
130 IntrinsicZernikes.fromDict(outDict)
132 self.assertIn("Incorrect intrinsic zernikes supplied", str(context.exception))
133 self.assertIn("INTRINSIC_ZERNIKES", str(context.exception))
134 self.assertIn("WRONG_TYPE", str(context.exception))
136 def test_getIntrinsicZernikes(self):
137 """Test interpolation of Zernike coefficients."""
138 # Test at a grid point
139 field_x_test = 0.0
140 field_y_test = 0.0
142 zernikes = self.calib.getIntrinsicZernikes(field_x_test, field_y_test)
144 # In this case, we're on a grid point
145 center_idx = np.flatnonzero((self.field_x == 0.0) & (self.field_y == 0.0))[0]
146 self.assertFloatsEqual(zernikes, self.values[center_idx, :])
148 # Test with specific Noll indices
149 zernikes_subset = self.calib.getIntrinsicZernikes(
150 field_x_test, field_y_test,
151 noll_indices=[4]
152 )
153 self.assertFloatsEqual(zernikes_subset, zernikes[:, 0])
155 def test_getIntrinsicZernikes_array(self):
156 """Test interpolation with array inputs."""
157 field_x_test = np.array([0.0, 0.5])
158 field_y_test = np.array([0.0, 0.25])
160 zernikes = self.calib.getIntrinsicZernikes(field_x_test, field_y_test)
161 z0 = self.calib.getIntrinsicZernikes(field_x_test[0], field_y_test[0])
162 z1 = self.calib.getIntrinsicZernikes(field_x_test[1], field_y_test[1])
163 np.testing.assert_array_almost_equal(zernikes[[0]], z0)
164 np.testing.assert_array_almost_equal(zernikes[[1]], z1)
166 # Should return shape (n_points, n_zernikes)
167 self.assertEqual(zernikes.shape, (2, len(self.noll_indices)))
170class MemoryTester(lsst.utils.tests.MemoryTestCase):
171 pass
174def setup_module(module):
175 lsst.utils.tests.init()
178if __name__ == "__main__": 178 ↛ 179line 178 didn't jump to line 179 because the condition on line 178 was never true
179 import sys
180 setup_module(sys.modules[__name__])
181 unittest.main()