Coverage for python / lsst / cp / pipe / ptc / cpPtcFixupGainRatios.py: 48%
67 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 09:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 09:07 +0000
1# This file is part of cp_pipe.
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/>.
21#
22import copy
23import numpy as np
25from lsst.ip.isr import PhotonTransferCurveDataset
26import lsst.pex.config as pexConfig
27import lsst.pipe.base as pipeBase
28import lsst.pipe.base.connectionTypes as cT
30from deprecated.sphinx import deprecated
32from ..utils import ampOffsetGainRatioFixup
35__all__ = [
36 "PhotonTransferCurveFixupGainRatiosConfig",
37 "PhotonTransferCurveFixupGainRatiosTask",
38 "PhotonTransferCurveRenameConfig",
39 "PhotonTransferCurveRenameTask",
40]
43# TODO DM-52883: Remove deprecated tasks.
44@deprecated(reason="PhotonTransferCurveFixupGainRatiosTask is no longer used. "
45 "This Task will be removed after v30.",
46 version="v30.0", category=FutureWarning)
47class PhotonTransferCurveFixupGainRatiosConnections(
48 pipeBase.PipelineTaskConnections,
49 dimensions=("instrument", "detector")
50):
51 exposureMetadata = cT.Input(
52 name="cpPtcFixupGainRatiosIsrExp.metadata",
53 doc="Input exposures for gain ratio fixup.",
54 storageClass="PropertyList",
55 dimensions=("instrument", "exposure", "detector"),
56 multiple=True,
57 )
58 inputPtc = cT.PrerequisiteInput(
59 name="ptc",
60 doc="Input PTC to modify.",
61 storageClass="PhotonTransferCurveDataset",
62 dimensions=("instrument", "detector"),
63 isCalibration=True,
64 )
65 outputPtc = cT.Output(
66 name="ptcFixed",
67 doc="Output modified PTC.",
68 storageClass="PhotonTransferCurveDataset",
69 dimensions=("instrument", "detector"),
70 multiple=False,
71 isCalibration=True,
72 )
75# TODO DM-52883: Remove deprecated tasks.
76@deprecated(reason="PhotonTransferCurveFixupGainRatiosTask is no longer used. "
77 "This Task will be removed after v30.",
78 version="v30.0", category=FutureWarning)
79class PhotonTransferCurveFixupGainRatiosConfig(
80 pipeBase.PipelineTaskConfig,
81 pipelineConnections=PhotonTransferCurveFixupGainRatiosConnections,
82):
83 ampOffsetGainRatioMinAdu = pexConfig.Field(
84 dtype=float,
85 doc="Minimum number of adu to use for amp offset gain ratio fixup.",
86 default=1000.0,
87 )
88 ampOffsetGainRatioMaxAdu = pexConfig.Field(
89 dtype=float,
90 doc="Maximum number of adu to use for amp offset gain ratio fixup.",
91 default=40000.0,
92 )
95# TODO DM-52883: Remove deprecated tasks.
96@deprecated(reason="PhotonTransferCurveFixupGainRatiosTask is no longer used. "
97 "This Task will be removed after v30.",
98 version="v30.0", category=FutureWarning)
99class PhotonTransferCurveFixupGainRatiosTask(pipeBase.PipelineTask):
100 """Task to use on-sky amp ratios to fix up gain ratios in a PTC.
102 This uses the ampOffsetGainRatioFixup with on-sky data (preferably
103 twilight flats or similar) to update gain ratios.
104 """
105 ConfigClass = PhotonTransferCurveFixupGainRatiosConfig
106 _DefaultName = "cpPhotonTransferCurveFixupGainRatios"
108 def runQuantum(self, butlerQC, inputRefs, outputRefs):
109 # docstring inherited.
110 inputs = butlerQC.get(inputRefs)
111 outputs = self.run(inputPtc=inputs["inputPtc"], exposureMetadata=inputs["exposureMetadata"])
112 butlerQC.put(outputs, outputRefs)
114 def run(self, *, inputPtc, exposureMetadata):
115 """Run the gain ratio fixup task.
117 Parameters
118 ----------
119 inputPtc : `lsst.ip.isr.PhotonTransferCurveDataset`
120 Input PTC to modify.
121 exposureMetadata: `list` [`lsst.daf.base.PropertyList`]
122 Input exposure metadata.
124 Returns
125 -------
126 results : `lsst.pipe.base.Struct`
127 The output struct contains:
129 ``outputPtc``
130 The output modified ptc.
131 """
132 ampNames = inputPtc.ampNames
134 # Create a set of fake partial PTC datasets.
135 fakePtc = PhotonTransferCurveDataset(
136 ampNames=ampNames,
137 ptcFitType="FAKEPTC",
138 covMatrixSide=1,
139 covMatrixSideFullCovFit=1,
140 )
142 for i, metadata in enumerate(exposureMetadata):
143 fakePartialPtc = PhotonTransferCurveDataset(ampNames=ampNames, ptcFitType="PARTIAL")
145 for ampName in ampNames:
146 fakePartialPtc.setAmpValuesPartialDataset(
147 ampName,
148 inputExpIdPair=(2*i, 2*i + 1),
149 rawExpTime=float(i),
150 rawMean=metadata[f"LSST ISR FINAL MEDIAN {ampName}"],
151 rawVar=metadata[f"LSST ISR FINAL STDEV {ampName}"]**2.,
152 ampOffset=metadata[f"LSST ISR AMPOFFSET PEDESTAL {ampName}"],
153 expIdMask=True,
154 gain=inputPtc.gainUnadjusted[ampName],
155 noise=metadata[f"LSST ISR READNOISE {ampName}"]*inputPtc.gain[ampName],
156 covariance=np.zeros((1, 1)),
157 covSqrtWeights=np.zeros((1, 1)),
158 )
160 fakePtc.appendPartialPtc(fakePartialPtc)
162 detectorMeans = np.zeros(len(exposureMetadata))
164 for i in range(len(detectorMeans)):
165 arr = np.asarray([fakePtc.rawMeans[ampName][i] for ampName in ampNames])
166 detectorMeans[i] = np.nanmean(arr)
168 index = np.argsort(detectorMeans)
169 fakePtc.sort(index)
171 for ampName in ampNames:
172 fakePtc.finalMeans[ampName][:] = fakePtc.rawMeans[ampName].copy()
173 fakePtc.finalVars[ampName][:] = fakePtc.rawVars[ampName].copy()
174 fakePtc.gainUnadjusted[ampName] = inputPtc.gainUnadjusted[ampName]
175 fakePtc.gain[ampName] = inputPtc.gainUnadjusted[ampName]
177 ampOffsetGainRatioFixup(
178 fakePtc,
179 self.config.ampOffsetGainRatioMinAdu,
180 self.config.ampOffsetGainRatioMaxAdu,
181 log=self.log,
182 )
184 outputPtc = copy.copy(inputPtc)
186 # Replace the gain (leave gainUnadjusted alone).
187 for ampName in ampNames:
188 outputPtc.gain[ampName] = fakePtc.gain[ampName]
190 return pipeBase.Struct(
191 outputPtc=outputPtc,
192 )
195# TODO DM-52883: Remove deprecated tasks.
196@deprecated(reason="PhotonTransferCurveRenameTask is no longer used. "
197 "This Task will be removed after v30.",
198 version="v30.0", category=FutureWarning)
199class PhotonTransferCurveRenameConnections(
200 pipeBase.PipelineTaskConnections,
201 dimensions=("instrument", "detector")
202):
203 inputPtc = cT.PrerequisiteInput(
204 name="ptcFixed",
205 doc="Input PTC to rename.",
206 storageClass="PhotonTransferCurveDataset",
207 dimensions=("instrument", "detector"),
208 isCalibration=True,
209 )
210 outputPtc = cT.Output(
211 name="ptc",
212 doc="Output PTC that has been renamed.",
213 storageClass="PhotonTransferCurveDataset",
214 dimensions=("instrument", "detector"),
215 multiple=False,
216 isCalibration=True,
217 )
220# TODO DM-52883: Remove deprecated tasks.
221@deprecated(reason="PhotonTransferCurveRenameTask is no longer used. "
222 "This Task will be removed after v30.",
223 version="v30.0", category=FutureWarning)
224class PhotonTransferCurveRenameConfig(
225 pipeBase.PipelineTaskConfig,
226 pipelineConnections=PhotonTransferCurveRenameConnections,
227):
228 pass
231# TODO DM-52883: Remove deprecated tasks.
232@deprecated(reason="PhotonTransferCurveRenameTask is no longer used. "
233 "This Task will be removed after v30.",
234 version="v30.0", category=FutureWarning)
235class PhotonTransferCurveRenameTask(pipeBase.PipelineTask):
236 """Task to rename a ptcFixed into a ptc."""
237 ConfigClass = PhotonTransferCurveRenameConfig
238 _DefaultName = "cpPhotonTransferCurveRename"
240 def runQuantum(self, butlerQC, inputRefs, outputRefs):
241 # docstring inherited.
242 inputs = butlerQC.get(inputRefs)
244 outputs = pipeBase.Struct(outputPtc=inputs["inputPtc"])
245 butlerQC.put(outputs, outputRefs)
247 def run(self):
248 pass