Coverage for tests/test_brighterFatterKernel.py: 22%
79 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-27 03:41 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-27 03:41 -0700
1#
2# LSST Data Management System
3#
4# Copyright 2008-2017 AURA/LSST.
5#
6# This product includes software developed by the
7# LSST Project (http://www.lsst.org/).
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 LSST License Statement and
20# the GNU General Public License along with this program. If not,
21# see <https://www.lsstcorp.org/LegalNotices/>.
22#
23"""Test cases for lsst.cp.pipe.BrighterFatterKernelSolveTask.
24"""
26import unittest
27import numpy as np
29import lsst.utils
30import lsst.utils.tests
32import lsst.ip.isr as ipIsr
33import lsst.cp.pipe as cpPipe
34import lsst.afw.cameraGeom as cameraGeom
37class BfkSolveTaskTestCase(lsst.utils.tests.TestCase):
38 """A test case for the brighter fatter kernel solver.
39 """
41 def setUp(self):
42 """Set up a plausible PTC dataset, with 1% of the expected variance
43 shifted into covariance terms.
44 """
45 cameraBuilder = cameraGeom.Camera.Builder('fake camera')
46 detectorWrapper = cameraGeom.testUtils.DetectorWrapper(numAmps=1, cameraBuilder=cameraBuilder)
47 self.detector = detectorWrapper.detector
48 self.camera = cameraBuilder.finish()
50 self.defaultConfig = cpPipe.BrighterFatterKernelSolveConfig()
51 self.ptc = ipIsr.PhotonTransferCurveDataset(ampNames=['amp 1'], ptcFitType='FULLCOVARIANCE',
52 covMatrixSide=3)
53 self.ptc.expIdMask['amp 1'] = np.array([False, True, True, True, True, True, True, True, True, True])
54 self.ptc.rawMeans['amp 1'] = np.array([1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000])
55 self.ptc.finalMeans['amp 1'] = self.ptc.rawMeans['amp 1'][self.ptc.expIdMask['amp 1']]
56 self.ptc.rawVars['amp 1'] = 0.99 * np.array(self.ptc.rawMeans['amp 1'], dtype=float)
57 self.ptc.finalVars['amp 1'] = self.ptc.rawVars['amp 1'][self.ptc.expIdMask['amp 1']]
58 self.ptc.covariances['amp 1'] = []
59 for mean, variance in zip(self.ptc.rawMeans['amp 1'], self.ptc.rawVars['amp 1']):
60 residual = mean - variance
61 covariance = [[variance, 0.5 * residual, 0.1 * residual],
62 [0.2 * residual, 0.1 * residual, 0.05 * residual],
63 [0.025 * residual, 0.015 * residual, 0.01 * residual]]
64 self.ptc.covariances['amp 1'].append(covariance)
66 self.ptc.covariancesModel = self.ptc.covariances
67 self.ptc.gain['amp 1'] = 1.0
68 self.ptc.noise['amp 1'] = 5.0
69 self.ptc.noiseMatrix['amp 1'] = np.zeros((3, 3))
70 self.ptc.noiseMatrix['amp 1'][0][0] = self.ptc.noise['amp 1']
72 # This is empirically determined from the above parameters.
73 self.ptc.aMatrix['amp 1'] = np.array([[2.14329806e-06, -4.28659612e-07, -5.35824515e-08],
74 [-1.07164903e-06, -2.14329806e-07, -3.21494709e-08],
75 [-2.14329806e-07, -1.07164903e-07, -2.14329806e-08]])
77 # This is empirically determined from the above parameters.
78 self.expectation = np.array([[4.88348887e-08, 1.01136877e-07, 1.51784114e-07,
79 1.77570668e-07, 1.51784114e-07, 1.01136877e-07, 4.88348887e-08],
80 [9.42026776e-08, 2.03928507e-07, 3.28428909e-07,
81 4.06714446e-07, 3.28428909e-07, 2.03928507e-07, 9.42026776e-08],
82 [1.24047315e-07, 2.70512582e-07, 4.44123665e-07,
83 5.78099493e-07, 4.44123665e-07, 2.70512582e-07, 1.24047315e-07],
84 [1.31474000e-07, 2.77801372e-07, 3.85123870e-07,
85 -5.42128333e-08, 3.85123870e-07, 2.77801372e-07, 1.31474000e-07],
86 [1.24047315e-07, 2.70512582e-07, 4.44123665e-07,
87 5.78099493e-07, 4.44123665e-07, 2.70512582e-07, 1.24047315e-07],
88 [9.42026776e-08, 2.03928507e-07, 3.28428909e-07,
89 4.06714446e-07, 3.28428909e-07, 2.03928507e-07, 9.42026776e-08],
90 [4.88348887e-08, 1.01136877e-07, 1.51784114e-07,
91 1.77570668e-07, 1.51784114e-07, 1.01136877e-07, 4.88348887e-08]])
93 def test_averaged(self):
94 """Test "averaged" brighter-fatter kernel.
95 """
96 task = cpPipe.BrighterFatterKernelSolveTask()
98 results = task.run(self.ptc, ['this is a dummy exposure'], self.camera, {'detector': 1})
99 self.assertFloatsAlmostEqual(results.outputBFK.ampKernels['amp 1'], self.expectation, atol=1e-5)
101 def test_aMatrix(self):
102 """Test solution from Astier et al. 2019 "A" matrix
103 """
104 config = cpPipe.BrighterFatterKernelSolveConfig()
105 config.useAmatrix = True
106 task = cpPipe.BrighterFatterKernelSolveTask(config=config)
108 results = task.run(self.ptc, ['this is a dummy exposure'], self.camera, {'detector': 1})
109 self.assertFloatsAlmostEqual(results.outputBFK.ampKernels['amp 1'], self.expectation, atol=1e-5)
111 def test_covSample(self):
112 """Test solution from Broughton et al. 2024 eq. 4 preKernel
113 """
114 config = cpPipe.BrighterFatterKernelSolveConfig()
115 config.useCovModelSample = True
116 config.covModelFluxSample = {'ALL_AMPS': 30000.}
117 task = cpPipe.BrighterFatterKernelSolveTask(config=config)
119 results = task.run(self.ptc, ['this is a dummy exposure'], self.camera, {'detector': 1})
121 expectation = np.array([[2.19577206e-08, 4.22977941e-08, 5.54871324e-08,
122 5.85845588e-08, 5.54871324e-08, 4.22977941e-08,
123 2.19577206e-08],
124 [4.55330882e-08, 9.17463235e-08, 1.21066176e-07,
125 1.23363971e-07, 1.21066176e-07, 9.17463235e-08,
126 4.55330882e-08],
127 [6.84283088e-08, 1.48088235e-07, 1.98667279e-07,
128 1.67738971e-07, 1.98667279e-07, 1.48088235e-07,
129 6.84283088e-08],
130 [8.00919118e-08, 1.83511029e-07, 2.57775735e-07,
131 -4.97426471e-08, 2.57775735e-07, 1.83511029e-07,
132 8.00919118e-08],
133 [6.84283088e-08, 1.48088235e-07, 1.98667279e-07,
134 1.67738971e-07, 1.98667279e-07, 1.48088235e-07,
135 6.84283088e-08],
136 [4.55330882e-08, 9.17463235e-08, 1.21066176e-07,
137 1.23363971e-07, 1.21066176e-07, 9.17463235e-08,
138 4.55330882e-08],
139 [2.19577206e-08, 4.22977941e-08, 5.54871324e-08,
140 5.85845588e-08, 5.54871324e-08, 4.22977941e-08,
141 2.19577206e-08]])
143 self.assertFloatsAlmostEqual(results.outputBFK.ampKernels['amp 1'], expectation, atol=1e-5)
145 def test_quadratic(self):
146 """Test quadratic correlation solver.
148 This requires a different model for the variance, so cannot
149 use the one generated by setUp. This model is not entirely
150 physical, but will ensure that accidental code changes are
151 detected.
152 """
153 config = cpPipe.BrighterFatterKernelSolveConfig()
154 config.correlationQuadraticFit = True
155 config.forceZeroSum = True
156 task = cpPipe.BrighterFatterKernelSolveTask(config=config)
158 ptc = ipIsr.PhotonTransferCurveDataset(ampNames=['amp 1'], ptcFitType='FULLCOVARIANCE',
159 covMatrixSide=3)
160 ptc.expIdMask['amp 1'] = np.array([False, True, True, True, True, True, True, True, True, True])
161 ptc.rawMeans['amp 1'] = np.array([1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000])
162 ptc.finalMeans['amp 1'] = ptc.rawMeans['amp 1'][ptc.expIdMask['amp 1']]
163 ptc.rawVars['amp 1'] = 9e-5 * np.square(np.array(ptc.rawMeans['amp 1'], dtype=float))
164 ptc.finalVars['amp 1'] = ptc.rawVars['amp 1'][ptc.expIdMask['amp 1']]
165 ptc.covariances['amp 1'] = []
166 for mean, variance in zip(ptc.rawMeans['amp 1'], ptc.rawVars['amp 1']):
167 residual = variance
168 covariance = [[variance, 0.5 * residual, 0.1 * residual],
169 [0.2 * residual, 0.1 * residual, 0.05 * residual],
170 [0.025 * residual, 0.015 * residual, 0.01 * residual]]
171 ptc.covariances['amp 1'].append(covariance)
173 ptc.gain['amp 1'] = 1.0
174 ptc.noise['amp 1'] = 5.0
176 results = task.run(ptc, ['this is a dummy exposure'], self.camera, {'detector': 1})
178 expectation = np.array([[4.05330882e-08, 2.26654412e-07, 5.66636029e-07, 7.56066176e-07,
179 5.66636029e-07, 2.26654412e-07, 4.05330882e-08],
180 [-6.45220588e-08, 2.99448529e-07, 1.28382353e-06, 1.89099265e-06,
181 1.28382353e-06, 2.99448529e-07, -6.45220588e-08],
182 [-5.98069853e-07, -1.14816176e-06, -2.12178309e-06, -4.75974265e-06,
183 -2.12178309e-06, -1.14816176e-06, -5.98069853e-07],
184 [-1.17959559e-06, -3.52224265e-06, -1.28630515e-05, -6.16863971e-05,
185 -1.28630515e-05, -3.52224265e-06, -1.17959559e-06],
186 [-5.98069853e-07, -1.14816176e-06, -2.12178309e-06, -4.75974265e-06,
187 -2.12178309e-06, -1.14816176e-06, -5.98069853e-07],
188 [-6.45220588e-08, 2.99448529e-07, 1.28382353e-06, 1.89099265e-06,
189 1.28382353e-06, 2.99448529e-07, -6.45220588e-08],
190 [4.05330882e-08, 2.26654412e-07, 5.66636029e-07, 7.56066176e-07,
191 5.66636029e-07, 2.26654412e-07, 4.05330882e-08]])
192 self.assertFloatsAlmostEqual(results.outputBFK.ampKernels['amp 1'], expectation, atol=1e-5)
195class TestMemory(lsst.utils.tests.MemoryTestCase):
196 pass
199def setup_module(module):
200 lsst.utils.tests.init()
203if __name__ == "__main__": 203 ↛ 204line 203 didn't jump to line 204, because the condition on line 203 was never true
204 lsst.utils.tests.init()
205 unittest.main()