LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
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 """
22 Photodiode storage class.
23 """
24 import numpy as np
25 from astropy.table import Table
26 
27 from 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  -------
95  calib : `lsst.ip.isr.PhotodiodeCalib`
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  -------
158  calib : `lsst.ip.isr.PhotodiodeCalib`
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  -------
208  calib : `lsst.ip.isr.PhotodiodeCalib`
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