24 __all__ = [
"Curve",
"AmpCurve",
"DetectorCurve",
"ImageCurve"]
26 from scipy.interpolate
import interp1d
27 from astropy.table
import QTable
28 import astropy.units
as u
29 from abc
import ABC, abstractmethod
39 """ An abstract class to represent an arbitrary curve with 45 def __init__(self, wavelength, efficiency, metadata):
46 if not (isinstance(wavelength, u.Quantity)
and wavelength.unit.physical_type ==
'length'):
47 raise ValueError(
'The wavelength must be a quantity with a length sense.')
48 if not isinstance(efficiency, u.Quantity)
or efficiency.unit != u.percent:
49 raise ValueError(
'The efficiency must be a quantity with units of percent.')
53 metadata.update({
'MODE': self.
mode,
'TYPE':
'QE'})
59 """Class method for constructing a `Curve` object. 63 table : `astropy.table.QTable` 64 Table containing metadata and columns necessary 65 for constructing a `Curve` object. 70 A `Curve` subclass of the appropriate type according 77 """Convert this `Curve` object to an `astropy.table.QTable`. 81 table : `astropy.table.QTable` 82 A table object containing the data from this `Curve`. 87 def evaluate(self, detector, position, wavelength, kind='linear'):
88 """Interpolate the curve at the specified position and wavelength. 92 detector : `lsst.afw.cameraGeom.Detector` 93 Is used to find the appropriate curve given the position for 94 curves that vary over the detector. Ignored in the case where 95 there is only a single curve per detector. 96 position : `lsst.geom.Point2D` 97 The position on the detector at which to evaluate the curve. 98 wavelength : `astropy.units.Quantity` 99 The wavelength(s) at which to make the interpolation. 101 The type of interpolation to do. See documentation for 102 `scipy.interpolate.interp1d` for accepted values. 106 value : `astropy.units.Quantity` 107 Interpolated value(s). Number of values returned will match the 108 length of `wavelength`. 114 """Register subclasses with the abstract base class""" 116 if cls.
mode in Curve.subclasses:
117 raise ValueError(f
'Class for mode, {cls.mode}, already defined')
118 Curve.subclasses[cls.
mode] = cls
122 """Define equality for this class""" 126 keys_to_compare=['MODE', 'TYPE', 'CALIBDATE', 'INSTRUME', 'OBSTYPE', 'DETECTOR']):
127 """Compare metadata in this object to another. 132 The object with which to compare metadata. 133 keys_to_compare : `list` 134 List of metadata keys to compare. 139 Are the metadata the same? 141 for k
in keys_to_compare:
142 if self.
metadata[k] != other.metadata[k]:
147 """Interplate the curve at the specified wavelength(s). 151 wavelengths : `astropy.units.Quantity` 152 The wavelength values for the curve. 153 values : `astropy.units.Quantity` 154 The y-values for the curve. 155 wavelength : `astropy.units.Quantity` 156 The wavelength(s) at which to make the interpolation. 158 The type of interpolation to do. See documentation for 159 `scipy.interpolate.interp1d` for accepted values. 163 value : `astropy.units.Quantity` 164 Interpolated value(s) 166 if not isinstance(wavelength, u.Quantity):
167 raise ValueError(
"Wavelengths at which to interpolate must be astropy quantities")
168 if not (isinstance(wavelengths, u.Quantity)
and isinstance(values, u.Quantity)):
169 raise ValueError(
"Model to be interpreted must be astropy quantities")
170 interp_wavelength = wavelength.to(wavelengths.unit)
171 f = interp1d(wavelengths, values, kind=kind)
172 return f(interp_wavelength.value)*values.unit
180 Dictionary of metadata for this curve. 187 """Class method for constructing a `Curve` object from 188 the standardized text format. 193 Path to the text file to read. 198 A `Curve` subclass of the appropriate type according 199 to the table metadata 201 table = QTable.read(filename, format=
'ascii.ecsv')
206 """Class method for constructing a `Curve` object from 207 the standardized FITS format. 212 Path to the FITS file to read. 217 A `Curve` subclass of the appropriate type according 218 to the table metadata 220 table = QTable.read(filename, format=
'fits')
224 def _check_cols(cols, table):
225 """Check that the columns are in the table""" 227 if col
not in table.columns:
228 raise ValueError(f
'The table must include a column named "{col}".')
230 def _to_table_with_meta(self):
231 """Compute standard metadata before writing file out""" 232 now = datetime.datetime.utcnow()
234 metadata = table.meta
235 metadata[
"DATE"] = now.isoformat()
236 metadata[
"CALIB_CREATION_DATE"] = now.strftime(
"%Y-%m-%d")
237 metadata[
"CALIB_CREATION_TIME"] = now.strftime(
"%T %Z").
strip()
241 """ Write the `Curve` out to a text file. 246 Path to the text file to write. 251 Because this method forces a particular extension return 252 the name of the file actually written. 256 path, ext = os.path.splitext(filename)
257 filename = path +
".ecsv" 258 table.write(filename, format=
"ascii.ecsv")
262 """ Write the `Curve` out to a FITS file. 267 Path to the FITS file to write. 272 Because this method forces a particular extension return 273 the name of the file actually written. 277 path, ext = os.path.splitext(filename)
278 filename = path +
".fits" 279 table.write(filename, format=
"fits")
284 """Subclass of `Curve` that represents a single curve per detector. 288 wavelength : `astropy.units.Quantity` 289 Wavelength values for this curve 290 efficiency : `astropy.units.Quantity` 291 Quantum efficiency values for this curve 293 Dictionary of metadata for this curve 299 numpy.array_equal(self.
wavelength, other.wavelength)
and 300 numpy.array_equal(self.
wavelength, other.wavelength))
305 cls.
_check_cols([
'wavelength',
'efficiency'], table)
306 return cls(table[
'wavelength'], table[
'efficiency'], table.meta)
312 def evaluate(self, detector, position, wavelength, kind='linear'):
318 """Subclass of `Curve` that represents a curve per amp. 322 amp_name_list : iterable of `str` 323 The name of the amp for each entry 324 wavelength : `astropy.units.Quantity` 325 Wavelength values for this curve 326 efficiency : `astropy.units.Quantity` 327 Quantum efficiency values for this curve 329 Dictionary of metadata for this curve 333 def __init__(self, amp_name_list, wavelength, efficiency, metadata):
334 super().
__init__(wavelength, efficiency, metadata)
335 amp_names =
set(amp_name_list)
337 for amp_name
in amp_names:
338 idx = numpy.where(amp_name_list == amp_name)[0]
341 if isinstance(name, bytes):
343 self.
data[name] = (wavelength[idx], efficiency[idx])
349 if not numpy.array_equal(self.
data[k][0], other.data[k][0]):
351 if not numpy.array_equal(self.
data[k][1], other.data[k][1]):
358 cls.
_check_cols([
'amp_name',
'wavelength',
'efficiency'], table)
359 return cls(table[
'amp_name'], table[
'wavelength'],
360 table[
'efficiency'], table.meta)
366 names = numpy.array([])
371 if wavelength
is None:
373 wavelength = val[0].value
375 wavelength = numpy.concatenate([wavelength, val[0].value])
376 if efficiency
is None:
378 efficiency = val[1].value
380 efficiency = numpy.concatenate([efficiency, val[1].value])
381 names = numpy.concatenate([names, numpy.full(val[0].shape, amp_name)])
382 names = numpy.array(names)
384 return QTable({
'amp_name': names,
'wavelength': wavelength*wunit,
'efficiency': efficiency*eunit},
387 def evaluate(self, detector, position, wavelength, kind='linear'):
389 amp = cgUtils.findAmp(detector,
Point2I(position))
390 w, e = self.
data[amp.getName()]
391 return self.
interpolate(w, e, wavelength, kind=kind)
399 raise NotImplementedError()
403 raise NotImplementedError()
405 def evaluate(self, detector, position, wavelength, kind='linear'):
407 raise NotImplementedError()
def _check_cols(cols, table)
def evaluate(self, detector, position, wavelength, kind='linear')
def evaluate(self, detector, position, wavelength, kind='linear')
def fromTable(cls, table)
def compare_metadata(self, other, keys_to_compare=['MODE', TYPE, CALIBDATE, INSTRUME, OBSTYPE, DETECTOR)
def fromTable(cls, table)
def readText(cls, filename)
def evaluate(self, detector, position, wavelength, kind='linear')
std::vector< SchemaItem< Flag > > * items
def fromTable(self, table)
daf::base::PropertySet * set
def writeText(self, filename)
def __init__(self, wavelength, efficiency, metadata)
def evaluate(self, detector, position, wavelength, kind='linear')
def fromTable(cls, table)
def __init_subclass__(cls, kwargs)
def __init__(self, amp_name_list, wavelength, efficiency, metadata)
def interpolate(self, wavelengths, values, wavelength, kind)
def readFits(cls, filename)
def _to_table_with_meta(self)
def writeFits(self, filename)