LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
photodiode.py
Go to the documentation of this file.
1# This file is part of ip_isr.
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"""
22Photodiode storage class.
23"""
24import numpy as np
25from astropy.table import Table
26
27from lsst.ip.isr import IsrCalib
28
29
30__all__ = ["PhotodiodeCalib"]
31
32
34 """Independent current measurements from photodiode for linearity
35 calculations.
36
37 Parameters
38 ----------
39 timeSamples : `list` or `numpy.ndarray`
40 List of samples the photodiode was measured at.
41 currentSamples : `list` or `numpy.ndarray`
42 List of current measurements at each time sample.
43 log : `lsst.log.Log`, optional
44 Log to write messages to.
45 **kwargs :
46 Additional parameters. These will be passed to the parent
47 constructor with the exception of:
48
49 ``"integrationMethod"``
50 Name of the algorithm to use to integrate the current
51 samples. Allowed values are ``DIRECT_SUM`` and
52 ``TRIMMED_SUM`` (`str`).
53 """
54
55 _OBSTYPE = 'PHOTODIODE'
56 _SCHEMA = 'Photodiode'
57 _VERSION = 1.0
58
59 def __init__(self, timeSamples=None, currentSamples=None, **kwargs):
60 if timeSamples is not None and currentSamples is not None:
61 if len(timeSamples) != len(currentSamples):
62 raise RuntimeError(f"Inconsitent vector lengths: {len(timeSamples)} vs {len(currentSamples)}")
63 else:
64 self.timeSamplestimeSamples = np.array(timeSamples)
65 self.currentSamplescurrentSamples = np.array(currentSamples)
66 else:
67 self.timeSamplestimeSamples = np.array([])
68 self.currentSamplescurrentSamples = np.array([])
69
70 super().__init__(**kwargs)
71
72 if 'integrationMethod' in kwargs:
73 self.integrationMethodintegrationMethod = kwargs.pop('integrationMethod')
74 else:
75 self.integrationMethodintegrationMethod = 'DIRECT_SUM'
76
77 if 'day_obs' in kwargs:
78 self.updateMetadataupdateMetadata(day_obs=kwargs['day_obs'])
79 if 'seq_num' in kwargs:
80 self.updateMetadataupdateMetadata(seq_num=kwargs['seq_num'])
81
82 self.requiredAttributesrequiredAttributesrequiredAttributesrequiredAttributes.update(['timeSamples', 'currentSamples', 'integrationMethod'])
83
84 @classmethod
85 def fromDict(cls, dictionary):
86 """Construct a PhotodiodeCalib from a dictionary of properties.
87
88 Parameters
89 ----------
90 dictionary : `dict`
91 Dictionary of properties.
92
93 Returns
94 -------
96 Constructed photodiode data.
97
98 Raises
99 ------
100 RuntimeError :
101 Raised if the supplied dictionary is for a different
102 calibration type.
103 """
104 calib = cls()
105
106 if calib._OBSTYPE != dictionary['metadata']['OBSTYPE']:
107 raise RuntimeError(f"Incorrect photodiode supplied. Expected {calib._OBSTYPE}, "
108 f"found {dictionary['metadata']['OBSTYPE']}")
109
110 calib.setMetadata(dictionary['metadata'])
111
112 calib.timeSamples = np.array(dictionary['timeSamples'])
113 calib.currentSamples = np.array(dictionary['currentSamples'])
114 calib.integrationMethod = dictionary.get('integrationMethod', "DIRECT_SUM")
115
116 calib.updateMetadata()
117 return calib
118
119 def toDict(self):
120 """Return a dictionary containing the photodiode properties.
121
122 The dictionary should be able to be round-tripped through.
123 `fromDict`.
124
125 Returns
126 -------
127 dictionary : `dict`
128 Dictionary of properties.
129 """
130 self.updateMetadataupdateMetadata()
131
132 outDict = {}
133 outDict['metadata'] = self.getMetadatagetMetadata()
134
135 outDict['timeSamples'] = self.timeSamplestimeSamples.tolist()
136 outDict['currentSamples'] = self.currentSamplescurrentSamples.tolist()
137
138 outDict['integrationMethod'] = self.integrationMethodintegrationMethod
139
140 return outDict
141
142 @classmethod
143 def fromTable(cls, tableList):
144 """Construct calibration from a list of tables.
145
146 This method uses the `fromDict` method to create the
147 calibration after constructing an appropriate dictionary from
148 the input tables.
149
150 Parameters
151 ----------
152 tableList : `list` [`astropy.table.Table`]
153 List of tables to use to construct the crosstalk
154 calibration.
155
156 Returns
157 -------
159 The calibration defined in the tables.
160 """
161 dataTable = tableList[0]
162
163 metadata = dataTable.meta
164 inDict = {}
165 inDict['metadata'] = metadata
166 inDict['integrationMethod'] = metadata.pop('INTEGRATION_METHOD', 'DIRECT_SUM')
167
168 inDict['timeSamples'] = dataTable['TIME']
169 inDict['currentSamples'] = dataTable['CURRENT']
170
171 return cls().fromDict(inDict)
172
173 def toTable(self):
174 """Construct a list of tables containing the information in this
175 calibration.
176
177 The list of tables should create an identical calibration
178 after being passed to this class's fromTable method.
179
180 Returns
181 -------
182 tableList : `list` [`astropy.table.Table`]
183 List of tables containing the photodiode calibration
184 information.
185 """
186 self.updateMetadataupdateMetadata()
187 catalog = Table([{'TIME': self.timeSamplestimeSamples,
188 'CURRENT': self.currentSamplescurrentSamples}])
189 inMeta = self.getMetadatagetMetadata().toDict()
190 outMeta = {k: v for k, v in inMeta.items() if v is not None}
191 outMeta.update({k: "" for k, v in inMeta.items() if v is None})
192 outMeta['INTEGRATION_METHOD'] = self.integrationMethodintegrationMethod
193 catalog.meta = outMeta
194
195 return([catalog])
196
197 @classmethod
198 def readTwoColumnPhotodiodeData(cls, filename):
199 """Construct a PhotodiodeCalib by reading the simple column format.
200
201 Parameters
202 ----------
203 filename : `str`
204 File to read samples from.
205
206 Returns
207 -------
209 The calibration defined in the file.
210 """
211 import os.path
212
213 rawData = np.loadtxt(filename, dtype=[('time', 'float'), ('current', 'float')])
214
215 basename = os.path.basename(filename)
216 cleaned = os.path.splitext(basename)[0]
217 _, _, day_obs, seq_num = cleaned.split("_")
218
219 return cls(timeSamples=rawData['time'], currentSamples=rawData['current'],
220 day_obs=int(day_obs), seq_num=int(seq_num))
221
222 def integrate(self):
223 """Integrate the current.
224
225 Raises
226 ------
227 RuntimeError :
228 Raised if the integration method is not known.
229 """
230 if self.integrationMethodintegrationMethod == 'DIRECT_SUM':
231 return self.integrateDirectSumintegrateDirectSum()
232 elif self.integrationMethodintegrationMethod == 'TRIMMED_SUM':
233 return self.integrateTrimmedSumintegrateTrimmedSum()
234 else:
235 raise RuntimeError(f"Unknown integration method {self.integrationMethod}")
236
238 """Integrate points.
239
240 This uses numpy's trapezoidal integrator.
241
242 Returns
243 -------
244 sum : `float`
245 Total charge measured.
246 """
247 return np.trapz(self.currentSamplescurrentSamples, x=self.timeSamplestimeSamples)
248
250 """Integrate points with a baseline level subtracted.
251
252 This uses numpy's trapezoidal integrator.
253
254 Returns
255 -------
256 sum : `float`
257 Total charge measured.
258
259 See Also
260 --------
261 lsst.eotask.gen3.eoPtc
262 """
263 currentThreshold = ((max(self.currentSamplescurrentSamples) - min(self.currentSamplescurrentSamples))/5.0
264 + min(self.currentSamplescurrentSamples))
265 lowValueIndices = np.where(self.currentSamplescurrentSamples < currentThreshold)
266 baseline = np.median(self.currentSamplescurrentSamples[lowValueIndices])
267 return np.trapz(self.currentSamplescurrentSamples - baseline, self.timeSamplestimeSamples)
int min
int max
def requiredAttributes(self, value)
Definition: calibType.py:142
def updateMetadata(self, camera=None, detector=None, filterName=None, setCalibId=False, setCalibInfo=False, setDate=False, **kwargs)
Definition: calibType.py:181
def __init__(self, timeSamples=None, currentSamples=None, **kwargs)
Definition: photodiode.py:59
def readTwoColumnPhotodiodeData(cls, filename)
Definition: photodiode.py:198
This static class includes a variety of methods for interacting with the the logging module.
Definition: Log.h:724