Coverage for tests/test_coaddTransmissionCurve.py: 17%
110 statements
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-11 03:32 -0700
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-11 03:32 -0700
1#
2# LSST Data Management System
3# Copyright 2018 LSST Corporation.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <http://www.lsstcorp.org/LegalNotices/>.
21#
23import numpy as np
24import unittest
26import lsst.utils.tests
27from lsst.pex.exceptions import InvalidParameterError
28from lsst.geom import Point2D, Extent2D, Point2I, Box2D, Box2I, SpherePoint, degrees
29from lsst.afw.geom import makeSkyWcs, makeCdMatrix, Polygon
30from lsst.afw.image import TransmissionCurve
31from lsst.afw.table import ExposureTable, ExposureCatalog
32from lsst.meas.algorithms import makeCoaddTransmissionCurve
33from lsst.meas.algorithms.testUtils import makeRandomTransmissionCurve
36class CoaddBoundedFieldTestCase(lsst.utils.tests.TestCase):
38 def setUp(self):
39 # Test geometry:
40 #
41 # -100,99 99,99
42 # +--------------------+
43 # |AAAAAAAAAACCCCCDDDDD| A == only in epoch A
44 # |AAAAAAAAAACCCCCDDDDD| B == only in epoch B
45 # |AAAAAAAAAACCCCCDDDDD| C == in both epoch A and epoch B
46 # |AAAAAAAAAACCCCCDDDDD| D == in epoch A; in B's bbox but outside its ValidPolygon
47 # |AAAAAAAAAACCCCCDDDDD|
48 # | BBBBBBBBBB| All WCSs have the same CRVAL and CD.
49 # | BBBBBBBBBB|
50 # | BBBBBBBBBB| Coadd has CRPIX=(0, 0)
51 # | BBBBBBBBBB| Epoch A has CRPIX=(0, -50)
52 # | BBBBBBBBBB| Epoch B has CRPIX=(-50, 0)
53 # +--------------------+
54 # -100,-100 99,-100
55 #
56 self.rng = np.random.RandomState(50)
57 crval = SpherePoint(45.0, 45.0, degrees)
58 cdMatrix = makeCdMatrix(scale=5E-5*degrees, flipX=True)
59 self.wcsCoadd = makeSkyWcs(crpix=Point2D(0.0, 0.0), crval=crval, cdMatrix=cdMatrix)
60 self.wcsA = makeSkyWcs(crpix=Point2D(0.0, -50.0), crval=crval, cdMatrix=cdMatrix)
61 self.wcsB = makeSkyWcs(crpix=Point2D(-50.0, 0.0), crval=crval, cdMatrix=cdMatrix)
62 self.bboxCoadd = Box2I(Point2I(-100, -100), Point2I(99, 99))
63 self.bboxA = Box2I(Point2I(-100, -50), Point2I(99, 49))
64 self.bboxB = Box2I(Point2I(-50, -100), Point2I(49, 99))
65 self.polygonA = None
66 polygonD = Polygon(Box2D(Box2I(Point2I(0, 0), Point2I(49, 99))))
67 self.polygonB, = polygonD.symDifference(Polygon(Box2D(self.bboxB)))
68 self.curveA = makeRandomTransmissionCurve(self.rng)
69 self.curveB = makeRandomTransmissionCurve(self.rng)
70 self.weightA = 0.6
71 self.weightB = 0.2
72 schema = ExposureTable.makeMinimalSchema()
73 weightKey = schema.addField("weight", type=float, doc="relative weight of image in Coadd")
74 catalog = ExposureCatalog(schema)
75 recordA = catalog.addNew()
76 recordA[weightKey] = self.weightA
77 recordA.setWcs(self.wcsA)
78 recordA.setValidPolygon(self.polygonA)
79 recordA.setBBox(self.bboxA)
80 recordA.setTransmissionCurve(self.curveA)
81 recordB = catalog.addNew()
82 recordB[weightKey] = self.weightB
83 recordB.setWcs(self.wcsB)
84 recordB.setValidPolygon(self.polygonB)
85 recordB.setBBox(self.bboxB)
86 recordB.setTransmissionCurve(self.curveB)
87 self.curveCoadd = makeCoaddTransmissionCurve(self.wcsCoadd, catalog)
89 def tearDown(self):
90 del self.wcsCoadd
91 del self.wcsA
92 del self.wcsB
93 del self.curveCoadd
95 def makeRandomPoint(self, *args, **kwds):
96 """Draw a random Point2D within a Box2I.
98 All arguments are forwarded directly to the Box2I constructor, allowing
99 the caller to pass a fully-constructed Box2I, a (Point2I, Point2I) pair,
100 or a (Point2I, Extent2I) pair.
101 """
102 bboxD = Box2D(Box2I(*args, **kwds))
103 return bboxD.getMin() + Extent2D(bboxD.getWidth()*self.rng.rand(),
104 bboxD.getHeight()*self.rng.rand())
106 def testSampleAt(self):
107 """Test the behavior of TransmissionCurve.sampleAt on the subclass
108 returned by makeCoaddTransmissionCurve.
109 """
110 wavelengths = np.linspace(4000, 7000, 200)
112 # Points in coadd coordinates in each of the distinct regions
113 point0 = self.makeRandomPoint(Point2I(-100, -100), Point2I(-1, -1))
114 pointA = self.makeRandomPoint(Point2I(-100, 0), Point2I(-1, 99))
115 pointB = self.makeRandomPoint(Point2I(0, -100), Point2I(99, -1))
116 pointC = self.makeRandomPoint(Point2I(0, 0), Point2I(49, 99))
117 pointD = self.makeRandomPoint(Point2I(50, 0), Point2I(99, 99))
118 points = [point0, pointA, pointB, pointC, pointD]
120 # The same points, in sky coordinates
121 coords = [self.wcsCoadd.pixelToSky(point) for point in points]
123 # The same points, in Epoch A's coordinates
124 point0A, pointAA, pointBA, pointCA, pointDA = [self.wcsA.skyToPixel(coord) for coord in coords]
125 self.assertFalse(Box2D(self.bboxA).contains(point0A))
126 self.assertTrue(Box2D(self.bboxA).contains(pointAA))
127 self.assertFalse(Box2D(self.bboxA).contains(pointBA))
128 self.assertTrue(Box2D(self.bboxA).contains(pointCA))
129 self.assertTrue(Box2D(self.bboxA).contains(pointDA))
131 # The same points, in Epoch B's coordinates
132 point0B, pointAB, pointBB, pointCB, pointDB = [self.wcsB.skyToPixel(coord) for coord in coords]
133 self.assertFalse(Box2D(self.bboxB).contains(point0B))
134 self.assertFalse(Box2D(self.bboxB).contains(pointAB))
135 self.assertTrue(Box2D(self.bboxB).contains(pointBB))
136 self.assertTrue(Box2D(self.bboxB).contains(pointCB))
137 self.assertTrue(Box2D(self.bboxB).contains(pointDB))
138 self.assertTrue(self.polygonB.contains(pointBB))
139 self.assertTrue(self.polygonB.contains(pointCB))
140 self.assertFalse(self.polygonB.contains(pointDB))
142 # Test that we can't compute throughputs in region 0 (where there are no inputs)
143 self.assertRaises(InvalidParameterError, self.curveCoadd.sampleAt, point0, wavelengths)
145 # Test throughputs in region A (only Epoch A contributes)
146 throughputA1 = self.curveCoadd.sampleAt(pointA, wavelengths)
147 throughputA2 = self.curveA.sampleAt(pointAA, wavelengths)
148 self.assertFloatsAlmostEqual(throughputA1, throughputA2)
150 # Test throughputs in region B (only Epoch B contributes)
151 throughputB1 = self.curveCoadd.sampleAt(pointB, wavelengths)
152 throughputB2 = self.curveB.sampleAt(pointBB, wavelengths)
153 self.assertFloatsAlmostEqual(throughputB1, throughputB2)
155 # Test throughputs in region C (both epochs contribute)
156 throughputC1 = self.curveCoadd.sampleAt(pointC, wavelengths)
157 throughputC2 = self.curveA.sampleAt(pointCA, wavelengths)
158 throughputC3 = self.curveB.sampleAt(pointCB, wavelengths)
159 self.assertFloatsAlmostEqual(throughputC1, throughputC2*0.75 + throughputC3*0.25)
161 # Test throughputs in region D (only Epoch A contributes)
162 throughputD1 = self.curveCoadd.sampleAt(pointD, wavelengths)
163 throughputD2 = self.curveA.sampleAt(pointDA, wavelengths)
164 self.assertFloatsAlmostEqual(throughputD1, throughputD2)
166 def testPersistence(self):
167 wavelengths = np.linspace(4000, 7000, 200)
168 with lsst.utils.tests.getTempFilePath(".fits") as filename:
169 self.curveCoadd.writeFits(filename)
170 roundtripped = TransmissionCurve.readFits(filename)
171 for i in range(10):
172 point = self.makeRandomPoint(self.bboxCoadd)
173 try:
174 throughput1 = self.curveCoadd.sampleAt(point, wavelengths)
175 except InvalidParameterError:
176 self.assertRaises(InvalidParameterError, roundtripped.sampleAt, point, wavelengths)
177 else:
178 throughput2 = roundtripped.sampleAt(point, wavelengths)
179 self.assertFloatsAlmostEqual(throughput1, throughput2, atol=1e-10)
182class TestMemory(lsst.utils.tests.MemoryTestCase):
183 pass
186def setup_module(module):
187 lsst.utils.tests.init()
190if __name__ == "__main__": 190 ↛ 191line 190 didn't jump to line 191, because the condition on line 190 was never true
191 lsst.utils.tests.init()
192 unittest.main()