Coverage for tests/test_utils.py: 22%
134 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-15 11:19 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-15 11:19 +0000
1# This file is part of summit_utils.
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"""Test cases for utils."""
24import copy
25import itertools
26import unittest
28import astropy.time
29import astropy.units as u
30import lsst.afw.image as afwImage
31import lsst.geom as geom
32import lsst.utils.tests
33import numpy as np
34from astro_metadata_translator import makeObservationInfo
35from lsst.obs.base import createInitialSkyWcsFromBoresight
36from lsst.obs.base.makeRawVisitInfoViaObsInfo import MakeRawVisitInfoViaObsInfo
37from lsst.obs.lsst import Latiss
38from lsst.summit.utils.utils import (getExpPositionOffset,
39 getFieldNameAndTileNumber,
40 getAirmassSeeingCorrection,
41 getFilterSeeingCorrection,
42 quickSmooth,
43 getQuantiles,
44 )
45from lsst.obs.lsst.translators.latiss import AUXTEL_LOCATION
48class ExpSkyPositionOffsetTestCase(lsst.utils.tests.TestCase):
49 """A test case for testing sky position offsets for exposures."""
51 def setUp(self):
52 camera = Latiss.getCamera()
53 self.assertTrue(len(camera) == 1)
54 self.detector = camera[0]
56 self.viMaker = MakeRawVisitInfoViaObsInfo()
57 self.mi = afwImage.MaskedImageF(0, 0)
58 self.baseHeader = dict(boresight_airmass=1.5,
59 temperature=15*u.deg_C,
60 observation_type="science",
61 exposure_time=5*u.ks,
62 detector_num=32,
63 location=AUXTEL_LOCATION,
64 )
66 def test_getExpPositionOffset(self):
67 epsilon = 0.0001
68 ra1s = [0, 45, 90]
69 ra2s = copy.copy(ra1s)
70 ra2s.extend([r + epsilon for r in ra1s])
71 ra1s = np.deg2rad(ra1s)
72 ra2s = np.deg2rad(ra2s)
74 epsilon = 0.0001
75 dec1s = [0, 45, 90]
76 dec2s = copy.copy(dec1s)
77 dec2s.extend([d + epsilon for d in dec1s[:-1]]) # skip last point as >90 not allowed for dec
79 rotAngle1 = geom.Angle(43.2, geom.degrees) # arbitrary non-zero
80 rotAngle2 = geom.Angle(56.7, geom.degrees)
82 t1 = astropy.time.Time("2021-09-15T12:00:00", format="isot", scale="utc")
83 t2 = astropy.time.Time("2021-09-15T12:01:00", format="isot", scale="utc")
84 expTime = astropy.time.TimeDelta(20, format='sec')
86 header1 = copy.copy(self.baseHeader)
87 header2 = copy.copy(self.baseHeader)
88 header1['datetime_begin'] = astropy.time.Time(t1, format="isot", scale="utc")
89 header2['datetime_begin'] = astropy.time.Time(t2, format="isot", scale="utc")
91 header1['datetime_end'] = astropy.time.Time(t1+expTime, format="isot", scale="utc")
92 header2['datetime_end'] = astropy.time.Time(t2+expTime, format="isot", scale="utc")
94 obsInfo1 = makeObservationInfo(**header1)
95 obsInfo2 = makeObservationInfo(**header2)
97 vi1 = self.viMaker.observationInfo2visitInfo(obsInfo1)
98 vi2 = self.viMaker.observationInfo2visitInfo(obsInfo2)
99 expInfo1 = afwImage.ExposureInfo()
100 expInfo1.setVisitInfo(vi1)
101 expInfo2 = afwImage.ExposureInfo()
102 expInfo2.setVisitInfo(vi2)
104 for ra1, dec1, ra2, dec2 in itertools.product(ra1s, dec1s, ra2s, dec2s):
105 pos1 = geom.SpherePoint(ra1, dec1, geom.degrees)
106 pos2 = geom.SpherePoint(ra2, dec2, geom.degrees)
108 wcs1 = createInitialSkyWcsFromBoresight(pos1, rotAngle1, self.detector, flipX=True)
109 wcs2 = createInitialSkyWcsFromBoresight(pos2, rotAngle2, self.detector, flipX=True)
111 exp1 = afwImage.ExposureF(self.mi, expInfo1)
112 exp2 = afwImage.ExposureF(self.mi, expInfo2)
114 exp1.setWcs(wcs1)
115 exp2.setWcs(wcs2)
117 result = getExpPositionOffset(exp1, exp2)
119 deltaRa = ra1 - ra2
120 deltaDec = dec1 - dec2
122 self.assertAlmostEqual(result.deltaRa.asDegrees(), deltaRa, 6)
123 self.assertAlmostEqual(result.deltaDec.asDegrees(), deltaDec, 6)
126class MiscUtilsTestCase(lsst.utils.tests.TestCase):
128 def setUp(self) -> None:
129 return super().setUp()
131 def test_getFieldNameAndTileNumber(self):
132 field, num = getFieldNameAndTileNumber('simple')
133 self.assertEqual(field, 'simple')
134 self.assertIsNone(num)
136 field, num = getFieldNameAndTileNumber('_simple')
137 self.assertEqual(field, '_simple')
138 self.assertIsNone(num)
140 field, num = getFieldNameAndTileNumber('simple_321')
141 self.assertEqual(field, 'simple')
142 self.assertEqual(num, 321)
144 field, num = getFieldNameAndTileNumber('_simple_321')
145 self.assertEqual(field, '_simple')
146 self.assertEqual(num, 321)
148 field, num = getFieldNameAndTileNumber('test_321a_123')
149 self.assertEqual(field, 'test_321a')
150 self.assertEqual(num, 123)
152 field, num = getFieldNameAndTileNumber('test_321a_123_')
153 self.assertEqual(field, 'test_321a_123_')
154 self.assertIsNone(num)
156 field, num = getFieldNameAndTileNumber('test_321a_123a')
157 self.assertEqual(field, 'test_321a_123a')
158 self.assertIsNone(num)
160 field, num = getFieldNameAndTileNumber('test_321a:asd_asd-dsa_321')
161 self.assertEqual(field, 'test_321a:asd_asd-dsa')
162 self.assertEqual(num, 321)
164 def test_getAirmassSeeingCorrection(self):
165 for airmass in (1.1, 2.0, 20.0):
166 correction = getAirmassSeeingCorrection(airmass)
167 self.assertGreater(correction, 0.01)
168 self.assertLess(correction, 1.0)
170 correction = getAirmassSeeingCorrection(1)
171 self.assertEqual(correction, 1.0)
173 with self.assertRaises(ValueError):
174 getAirmassSeeingCorrection(0.5)
176 def test_getFilterSeeingCorrection(self):
177 for filterName in ('SDSSg_65mm', 'SDSSr_65mm', 'SDSSi_65mm'):
178 correction = getFilterSeeingCorrection(filterName)
179 self.assertGreater(correction, 0.5)
180 self.assertLess(correction, 1.5)
182 def test_quickSmooth(self):
183 # just test that it runs and returns the right shape. It's a wrapper on
184 # scipy.ndimage.gaussian_filter we can trust that it does what it
185 # should, and we just test the interface hasn't bitrotted on either end
186 data = np.zeros((100, 100), dtype=np.float32)
187 data = quickSmooth(data, 5.0)
188 self.assertEqual(data.shape, (100, 100))
191class QuantileTestCase(lsst.utils.tests.TestCase):
192 def setUp(self) -> None:
193 return super().setUp()
195 def test_quantiles(self):
196 # We understand that our algorithm gives very large rounding error
197 # compared to the generic numpy method. But still test it.
198 np.random.seed(1234)
199 # too big of a width violates the tolerance in the test to cap at 10k
200 dataRanges = [(50, 1, -1), (100_000, 5_000, -1), (5_000_000, 10_000, -2)]
201 colorRanges = [2, 256, 999] # [very few, nominal, lots and an odd number]
202 for nColors, (mean, width, decimal) in itertools.product(colorRanges, dataRanges):
203 data = np.random.normal(mean, width, (100, 100))
204 data[10, 10] = np.nan # check we're still nan-safe
205 edges1 = getQuantiles(data, nColors)
206 edges2 = np.nanquantile(data, np.linspace(0, 1, nColors + 1)) # must check with nanquantile
207 np.testing.assert_almost_equal(edges1, edges2, decimal=decimal)
210class TestMemory(lsst.utils.tests.MemoryTestCase):
211 pass
214def setup_module(module):
215 lsst.utils.tests.init()
218if __name__ == "__main__": 218 ↛ 219line 218 didn't jump to line 219, because the condition on line 218 was never true
219 lsst.utils.tests.init()
220 unittest.main()