Coverage for tests/test_calibQuantityProfile.py: 93%
42 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-20 10:14 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-20 10:14 +0000
1# This file is part of analysis_tools.
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"""Tests for the calibQuantityProfile tools on cp_verify results tables.
24These catch rows with no panel key (the per-detector and per-exposure rows,
25masked in the amplifier column) breaking the repack, and with it
26analyzeFlatDetCore's flatTestsByDate plot.
27"""
29import unittest
31import matplotlib
32import matplotlib.pyplot as plt
33from astropy.table import Table, vstack
34from matplotlib.figure import Figure
36import lsst.utils.tests
37from lsst.analysis.tools.atools.calibQuantityProfile import CalibAmpScatterTool, SingleValueRepacker
39matplotlib.use("Agg")
41AMP_NAMES = [f"C{i}{j}" for i in (0, 1) for j in range(8)]
42DETECTORS = (90, 91)
43MJD = 61224.4
46def makeResultsTable() -> Table:
47 """Make a table shaped like cp_verify's ``verifyFlatResults``.
49 Returns
50 -------
51 table : `astropy.table.Table`
52 One row per amplifier per detector, plus a per-detector row for each
53 detector and a per-exposure row. Those last have no amplifier, so are
54 masked in the ``amplifier`` column.
55 """
56 nAmpRows = len(DETECTORS) * len(AMP_NAMES)
57 ampRows = Table(
58 {
59 "detector": [detector for detector in DETECTORS for _ in AMP_NAMES],
60 "amplifier": AMP_NAMES * len(DETECTORS),
61 "mjd": [MJD] * nAmpRows,
62 "FLAT_VERIFY_NOISE": [True] * nAmpRows,
63 }
64 )
65 detectorRows = Table(
66 {"detector": list(DETECTORS), "mjd": [MJD] * len(DETECTORS), "FLAT_DET_SCATTER": [0.01, 0.01]}
67 )
68 exposureRow = Table({"detector": [DETECTORS[0]], "flat_SCATTER": [0.001]})
69 return vstack([ampRows, detectorRows, exposureRow])
72class SingleValueRepackerTestCase(lsst.utils.tests.TestCase):
73 def testSkipsRowsWithoutPanelKey(self) -> None:
74 # The rows with no amplifier used to raise
75 # "TypeError: unhashable type: 'MaskedConstant'", so
76 # analyzeFlatDetCore never wrote its plot.
77 table = makeResultsTable()
78 # Without masked rows in the fixture this test pins nothing.
79 self.assertTrue(table["amplifier"].mask.any())
80 repacker = SingleValueRepacker(panelKey="amplifier", dataKey="mjd", quantityKey="FLAT_VERIFY_NOISE")
81 repacked = repacker(table)
82 self.assertEqual(set(repacked), {f"{amp}{suffix}" for amp in AMP_NAMES for suffix in ("", "_x")})
83 for amp in AMP_NAMES:
84 self.assertEqual(repacked[f"{amp}_x"], [MJD] * len(DETECTORS))
85 self.assertEqual(repacked[amp], [True] * len(DETECTORS))
88class CalibAmpScatterToolTestCase(lsst.utils.tests.TestCase):
89 def testPlotsResultsTable(self) -> None:
90 # The whole tool, configured as analyzeFlatDetCore's flatTestsByDate
91 # in cpCore.yaml: a masked amplifier anywhere along the way must not
92 # stop the plot being made.
93 tool = CalibAmpScatterTool()
94 tool.prep.quantityKey = "FLAT_VERIFY_NOISE"
95 tool.finalize()
96 results = tool(makeResultsTable())
97 self.assertIsInstance(results["GridPlot"], Figure)
98 plt.close(results["GridPlot"])
101class MemoryTester(lsst.utils.tests.MemoryTestCase):
102 pass
105def setup_module(module):
106 lsst.utils.tests.init()
109if __name__ == "__main__": 109 ↛ 110line 109 didn't jump to line 110 because the condition on line 109 was never true
110 lsst.utils.tests.init()
111 unittest.main()