Coverage for python/lsst/sims/photUtils/Bandpass.py : 66%

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 2008, 2009, 2010, 2011, 2012 LSST Corporation. # # 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 <http://www.lsstcorp.org/LegalNotices/>. #
bandpass -
Class data: wavelen (nm) sb (Transmission, 0-1) phi (Normalized system response) wavelen/sb are guaranteed gridded. phi will be None until specifically needed; any updates to wavelen/sb within class will reset phi to None. the name of the bandpass file
Note that Bandpass objects are required to maintain a uniform grid in wavelength, rather than being allowed to have variable wavelength bins. This is because of the method used in 'Sed' to calculate magnitudes, but is simpler to enforce here.
Methods: __init__ : pass wavelen/sb arrays and set values (on grid) OR set data to None's setWavelenLimits / getWavelenLimits: set or get the wavelength limits of bandpass setBandpass: set bandpass using wavelen/sb input values getBandpass: return copies of wavelen/sb values imsimBandpass : set up a bandpass which is 0 everywhere but one wavelength (this can be useful for imsim magnitudes) readThroughput : set up a bandpass by reading data from a single file readThroughtputList : set up a bandpass by reading data from many files and multiplying the individual throughputs resampleBandpass : use linear interpolation to resample wavelen/sb arrays onto a regular grid (grid is specified by min/max/step size) sbTophi : calculate phi from sb - needed for calculating magnitudes multiplyThroughputs : multiply self.wavelen/sb by given wavelen/sb and return new wavelen/sb arrays (gridded like self) calcZP_t : calculate instrumental zeropoint for this bandpass calcEffWavelen: calculate the effective wavelength (using both Sb and Phi) for this bandpass writeThroughput : utility to write bandpass information to file
"""
""" Class for holding and utilizing telescope bandpasses. """ wavelen_min=None, wavelen_max=None, wavelen_step=None): """ Initialize bandpass object, with option to pass wavelen/sb arrays in directly.
Also can specify wavelength grid min/max/step or use default - sb and wavelen will be resampled to this grid. If wavelen/sb are given, these will be set, but phi will be set to None. Otherwise all set to None and user should call readThroughput, readThroughputList, or imsimBandpass to populate bandpass data. """
else: else: else:
## getters and setters """ Set internal records of wavelen limits, _min, _max, _step. """ # If we've been given values for wavelen_min, _max, _step, set them here.
""" Return appropriate wavelen limits (_min, _max, _step) if passed None values. """
wavelen_min=None, wavelen_max=None, wavelen_step=None): """ Populate bandpass data with wavelen/sb arrays.
Sets self.wavelen/sb on a grid of wavelen_min/max/step. Phi set to None. """ # Check data type. raise ValueError("Wavelen and sb arrays must be numpy arrays.") # Check data matches in length. raise ValueError("Wavelen and sb arrays must have the same length.") # Data seems ok then, make a new copy of this data for self. # Resample wavelen/sb onto grid.
wavelen_min=None, wavelen_max=None, wavelen_step=None): """ Populate bandpass data with sb=0 everywhere except sb=1 at imsimwavelen.
Sets wavelen/sb, with grid min/max/step as optional parameters. Does NOT set phi. """ # Set up arrays. self.wavelen_step, dtype='float') # Set sb.
""" Populate bandpass data with data (wavelen/sb) read from file, resample onto grid.
Sets wavelen/sb, with grid min/max/step as optional parameters. Does NOT set phi. """ # Set self values to None in case of file read error. # Check for filename error. # If given list of filenames, pass to (and return from) readThroughputList. warnings.warn("Was given list of files, instead of a single file. Using readThroughputList instead") self.readThroughputList(componentList=filename, wavelen_min=self.wavelen_min, wavelen_max=self.wavelen_max, wavelen_step=self.wavelen_step) # Filename is single file, now try to open file and read data. f = gzip.open(filename, 'rt') else: except IOError: try: if filename.endswith('.gz'): f = open(filename[:-3], 'r') else: f = gzip.open(filename+'.gz', 'rt') except IOError: raise IOError('The throughput file %s does not exist' %(filename)) # The throughput file should have wavelength(A), throughput(Sb) as first two columns. continue continue continue # Set up wavelen/sb. # Check that wavelength is monotonic increasing and non-repeating in wavelength. (Sort on wavelength). raise ValueError('The wavelength values in file %s are non-unique.' %(filename)) # Sort values. # Resample throughput onto grid. raise Exception("Bandpass data from %s has no throughput in " "desired grid range %f, %f" %(filename, wavelen_min, wavelen_max))
'lens2.dat', 'lens3.dat', 'm1.dat', 'm2.dat', 'm3.dat', 'atmos_std.dat'], rootDir = '.', wavelen_min=None, wavelen_max=None, wavelen_step=None): """ Populate bandpass data by reading from a series of files with wavelen/Sb data.
Multiplies throughputs (sb) from each file to give a final bandpass throughput. Sets wavelen/sb, with grid min/max/step as optional parameters. Does NOT set phi. """ # ComponentList = names of files in that directory. # A typical component list of all files to build final component list, including filter, might be: # componentList=['detector.dat', 'lens1.dat', 'lens2.dat', 'lens3.dat', # 'm1.dat', 'm2.dat', 'm3.dat', 'atmos_std.dat', 'ideal_g.dat'] # # Set wavelen limits for this object, if any updates have been given. # Set up wavelen/sb on grid. dtype='float') # Set up a temporary bandpass object to hold data from each file. wavelen_step=self.wavelen_step) # Read data from file. # Multiply self by new sb values.
wavelen = numpy.copy(self.wavelen) sb = numpy.copy(self.sb) return wavelen, sb
## utilities
""" Simple utility to check if should be using self.wavelen/sb or passed arrays.
Useful for other methods in this class. Also does data integrity check on wavelen/sb if not self. """ # Then one of the arrays was not passed - check if true for both. # Then only one of the arrays was passed - raise exception. raise ValueError("Must either pass *both* wavelen/sb pair, or use self defaults") # Okay, neither wavelen or sb was passed in - using self only. else: # Both of the arrays were passed in - check their validity. if (isinstance(wavelen, numpy.ndarray)==False) or (isinstance(sb, numpy.ndarray)==False): raise ValueError("Must pass wavelen/sb as numpy arrays") if len(wavelen)!=len(sb): raise ValueError("Must pass equal length wavelen/sb arrays")
wavelen_min=None, wavelen_max=None, wavelen_step=None): """ Return true/false of whether wavelen need to be resampled onto a grid.
Given wavelen OR defaults to self.wavelen/sb - return True/False check on whether the arrays need to be resampled to match wavelen_min/max/step grid. """ # Thought about adding wavelen_match option here (to give this an array to match to, rather than # the grid parameters .. but then thought bandpass always needs to be on a regular grid (because # of magnitude calculations). So, this will stay match to the grid parameters only. # Check wavelength limits. # Check if method acting on self or other data (here, using data type checks primarily). # Start check if data is already gridded. # First check minimum/maximum and first step in array. # Then check on step size. need_regrid = False # At this point, need_grid=True unless it's proven to be False, so return value.
wavelen_min=None, wavelen_max=None, wavelen_step=None): """ Resamples wavelen/sb (or self.wavelen/sb) onto grid defined by min/max/step.
Either returns wavelen/sb (if given those arrays) or updates wavelen / Sb in self. If updating self, resets phi to None. """ # Check wavelength limits. # Is method acting on self.wavelen/sb or passed in wavelen/sb? Sort it out. # Now, on with the resampling. raise Exception("No overlap between known wavelength range and desired wavelength range.") # Set up gridded wavelength. # Do the interpolation of wavelen/sb onto the grid. (note wavelen/sb type failures will die here). # Update self values if necessary. return wavelen_grid, sb_grid
## more complicated bandpass functions
""" Calculate and set phi - the normalized system response.
This function only pdates self.phi. """ # The definition of phi = (Sb/wavelength)/\int(Sb/wavelength)dlambda. # Due to definition of class, self.sb and self.wavelen are guaranteed equal-gridded. # Normalize phi so that the integral of phi is 1. raise Exception("Phi is poorly defined (nearly 0) over bandpass range.")
""" Multiply self.sb by another wavelen/sb pair, return wavelen/sb arrays.
The returned arrays will be gridded like this bandpass. This method does not affect self. """ # Resample wavelen_other/sb_other to match this bandpass. if self.needResample(wavelen=wavelen_other): wavelen_other, sb_other = self.resampleBandpass(wavelen=wavelen_other, sb=sb_other) # Make new memory copy of wavelen. wavelen_new = numpy.copy(self.wavelen) # Calculate new transmission - this is also new memory. sb_new = self.sb * sb_other return wavelen_new, sb_new
""" Calculate the instrumental zeropoint for a bandpass.
@param [in] photometricParameters is an instantiation of the PhotometricParameters class that carries details about the photometric response of the telescope. Defaults to LSST values. """ # ZP_t is the magnitude of a (F_nu flat) source which produced 1 count per second. # This is often also known as the 'instrumental zeropoint'. # Set gain to 1 if want to explore photo-electrons rather than adu. # The typical LSST exposure time is 15s and this is default here, but typical zp_t definition is for 1s. # SED class uses flambda in ergs/cm^2/s/nm, so need effarea in cm^2. # # Check dlambda value for integral. dlambda = self.wavelen[1] - self.wavelen[0] # Set up flat source of arbitrary brightness, # but where the units of fnu are Jansky (for AB mag zeropoint = -8.9). flatsource = Sed() flatsource.setFlatSED(wavelen_min=self.wavelen_min, wavelen_max=self.wavelen_max, wavelen_step=self.wavelen_step) adu = flatsource.calcADU(self, photParams=photometricParameters) # Scale fnu so that adu is 1 count/expTime. flatsource.fnu = flatsource.fnu * (1/adu) # Now need to calculate AB magnitude of the source with this fnu. if self.phi is None: self.sbTophi() zp_t = flatsource.calcMag(self) return zp_t
""" Calculate effective wavelengths for filters. """ # This is useful for summary numbers for filters. # Calculate effective wavelength of filters. if self.phi is None: self.sbTophi() effwavelenphi = (self.wavelen*self.phi).sum()/self.phi.sum() effwavelensb = (self.wavelen*self.sb).sum()/self.sb.sum() return effwavelenphi, effwavelensb
""" Write throughput to a file. """ # Useful if you build a throughput up from components and need to record the combined value. f = open(filename, 'w') # Print header. if print_header is not None: if not print_header.startswith('#'): print_header = '#' + print_header f.write(print_header) if write_phi: if self.phi is None: self.sbTophi() print("# Wavelength(nm) Throughput(0-1) Phi", file=f) else: print("# Wavelength(nm) Throughput(0-1)", file=f) # Loop through data, printing out to file. for i in range(0, len(self.wavelen), 1): if write_phi: print(self.wavelen[i], self.sb[i], self.phi[i], file=f) else: print(self.wavelen[i], self.sb[i], file=f) f.close() return |