Coverage for python/lsst/meas/algorithms/simple_curve.py : 29%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# # LSST Data Management System # # Copyright 2019 AURA/LSST. # # This product includes software developed by the # LSST Project (http://www.lsst.org/). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the LSST License Statement and # the GNU General Public License along with this program. If not, # see <https://www.lsstcorp.org/LegalNotices/>. #
""" An abstract class to represent an arbitrary curve with interpolation. """
if not (isinstance(wavelength, u.Quantity) and wavelength.unit.physical_type == 'length'): raise ValueError('The wavelength must be a quantity with a length sense.') if not isinstance(efficiency, u.Quantity) or efficiency.unit != u.percent: raise ValueError('The efficiency must be a quantity with units of percent.') self.wavelength = wavelength self.efficiency = efficiency # make sure needed metadata is set if built directly from ctor. metadata.update({'MODE': self.mode, 'TYPE': 'QE'}) self.metadata = metadata
def fromTable(cls, table): """Class method for constructing a `Curve` object.
Parameters ---------- table : `astropy.table.QTable` Table containing metadata and columns necessary for constructing a `Curve` object.
Returns ------- curve : `Curve` A `Curve` subclass of the appropriate type according to the table metadata """ pass
def toTable(self): """Convert this `Curve` object to an `astropy.table.QTable`.
Returns ------- table : `astropy.table.QTable` A table object containing the data from this `Curve`. """ pass
"""Interpolate the curve at the specified position and wavelength.
Parameters ---------- detector : `lsst.afw.cameraGeom.Detector` Is used to find the appropriate curve given the position for curves that vary over the detector. Ignored in the case where there is only a single curve per detector. position : `lsst.geom.Point2D` The position on the detector at which to evaluate the curve. wavelength : `astropy.units.Quantity` The wavelength(s) at which to make the interpolation. kind : `str` The type of interpolation to do. See documentation for `scipy.interpolate.interp1d` for accepted values.
Returns ------- value : `astropy.units.Quantity` Interpolated value(s). Number of values returned will match the length of `wavelength`. """ pass
def __init_subclass__(cls, **kwargs): """Register subclasses with the abstract base class""" raise ValueError(f'Class for mode, {cls.mode}, already defined')
def __eq__(self, other): """Define equality for this class""" pass
keys_to_compare=['MODE', 'TYPE', 'CALIBDATE', 'INSTRUME', 'OBSTYPE', 'DETECTOR']): """Compare metadata in this object to another.
Parameters ---------- other : `Curve` The object with which to compare metadata. keys_to_compare : `list` List of metadata keys to compare.
Returns ------- same : `bool` Are the metadata the same? """ for k in keys_to_compare: if self.metadata[k] != other.metadata[k]: return False return True
"""Interplate the curve at the specified wavelength(s).
Parameters ---------- wavelengths : `astropy.units.Quantity` The wavelength values for the curve. values : `astropy.units.Quantity` The y-values for the curve. wavelength : `astropy.units.Quantity` The wavelength(s) at which to make the interpolation. kind : `str` The type of interpolation to do. See documentation for `scipy.interpolate.interp1d` for accepted values.
Returns ------- value : `astropy.units.Quantity` Interpolated value(s) """ if not isinstance(wavelength, u.Quantity): raise ValueError("Wavelengths at which to interpolate must be astropy quantities") if not (isinstance(wavelengths, u.Quantity) and isinstance(values, u.Quantity)): raise ValueError("Model to be interpreted must be astropy quantities") interp_wavelength = wavelength.to(wavelengths.unit) f = interp1d(wavelengths, values, kind=kind) return f(interp_wavelength.value)*values.unit
"""Return metadata
Returns ------- metadata : `dict` Dictionary of metadata for this curve. """ # Needed to duck type as an object that can be ingested return self.metadata
def readText(cls, filename): """Class method for constructing a `Curve` object from the standardized text format.
Parameters ---------- filename : `str` Path to the text file to read.
Returns ------- curve : `Curve` A `Curve` subclass of the appropriate type according to the table metadata """ table = QTable.read(filename, format='ascii.ecsv') return cls.subclasses[table.meta['MODE']].fromTable(table)
def readFits(cls, filename): """Class method for constructing a `Curve` object from the standardized FITS format.
Parameters ---------- filename : `str` Path to the FITS file to read.
Returns ------- curve : `Curve` A `Curve` subclass of the appropriate type according to the table metadata """ table = QTable.read(filename, format='fits') return cls.subclasses[table.meta['MODE']].fromTable(table)
def _check_cols(cols, table): """Check that the columns are in the table""" for col in cols: if col not in table.columns: raise ValueError(f'The table must include a column named "{col}".')
"""Compute standard metadata before writing file out""" now = datetime.datetime.utcnow() table = self.toTable() metadata = table.meta metadata["DATE"] = now.isoformat() metadata["CALIB_CREATION_DATE"] = now.strftime("%Y-%m-%d") metadata["CALIB_CREATION_TIME"] = now.strftime("%T %Z").strip() return table
""" Write the `Curve` out to a text file.
Parameters ---------- filename : `str` Path to the text file to write.
Returns ------- filename : `str` Because this method forces a particular extension return the name of the file actually written. """ table = self._to_table_with_meta() # Force file extension to .ecsv path, ext = os.path.splitext(filename) filename = path + ".ecsv" table.write(filename, format="ascii.ecsv") return filename
""" Write the `Curve` out to a FITS file.
Parameters ---------- filename : `str` Path to the FITS file to write.
Returns ------- filename : `str` Because this method forces a particular extension return the name of the file actually written. """ table = self._to_table_with_meta() # Force file extension to .ecsv path, ext = os.path.splitext(filename) filename = path + ".fits" table.write(filename, format="fits") return filename
"""Subclass of `Curve` that represents a single curve per detector.
Parameters ---------- wavelength : `astropy.units.Quantity` Wavelength values for this curve efficiency : `astropy.units.Quantity` Quantum efficiency values for this curve metadata : `dict` Dictionary of metadata for this curve """
return (self.compare_metadata(other) and numpy.array_equal(self.wavelength, other.wavelength) and numpy.array_equal(self.wavelength, other.wavelength))
def fromTable(cls, table): # Docstring inherited from base classs cls._check_cols(['wavelength', 'efficiency'], table) return cls(table['wavelength'], table['efficiency'], table.meta)
# Docstring inherited from base classs return QTable({'wavelength': self.wavelength, 'efficiency': self.efficiency}, meta=self.metadata)
# Docstring inherited from base classs return self.interpolate(self.wavelength, self.efficiency, wavelength, kind=kind)
"""Subclass of `Curve` that represents a curve per amp.
Parameters ---------- amp_name_list : iterable of `str` The name of the amp for each entry wavelength : `astropy.units.Quantity` Wavelength values for this curve efficiency : `astropy.units.Quantity` Quantum efficiency values for this curve metadata : `dict` Dictionary of metadata for this curve """
super().__init__(wavelength, efficiency, metadata) amp_names = set(amp_name_list) self.data = {} for amp_name in amp_names: idx = numpy.where(amp_name_list == amp_name)[0] # Deal with the case where the keys are bytes from FITS name = amp_name if isinstance(name, bytes): name = name.decode() self.data[name] = (wavelength[idx], efficiency[idx])
if not self.compare_metadata(other): return False for k in self.data: if not numpy.array_equal(self.data[k][0], other.data[k][0]): return False if not numpy.array_equal(self.data[k][1], other.data[k][1]): return False return True
def fromTable(cls, table): # Docstring inherited from base classs cls._check_cols(['amp_name', 'wavelength', 'efficiency'], table) return cls(table['amp_name'], table['wavelength'], table['efficiency'], table.meta)
# Docstring inherited from base classs wavelength = None efficiency = None names = numpy.array([]) # Loop over the amps and concatenate into three same length columns to feed # to the Table constructor. for amp_name, val in self.data.items(): # This will preserve the quantity if wavelength is None: wunit = val[0].unit wavelength = val[0].value else: wavelength = numpy.concatenate([wavelength, val[0].value]) if efficiency is None: eunit = val[1].unit efficiency = val[1].value else: efficiency = numpy.concatenate([efficiency, val[1].value]) names = numpy.concatenate([names, numpy.full(val[0].shape, amp_name)]) names = numpy.array(names) # Note that in future, the astropy.unit should make it through concatenation return QTable({'amp_name': names, 'wavelength': wavelength*wunit, 'efficiency': efficiency*eunit}, meta=self.metadata)
# Docstring inherited from base classs amp = cgUtils.findAmp(detector, Point2I(position)) # cast to Point2I if Point2D passed w, e = self.data[amp.getName()] return self.interpolate(w, e, wavelength, kind=kind)
# Docstring inherited from base classs raise NotImplementedError()
# Docstring inherited from base classs raise NotImplementedError()
# Docstring inherited from base classs raise NotImplementedError() |