def setup_Throughputs(self, rootDir=None, verbose=True): """Read bandpasses and dark sky sed. Call this method first before doing anything else. """ self.filterlist = ('u', 'g', 'r', 'i', 'z', 'y') if rootDir == None: rootDir = os.getenv('LSST_THROUGHPUTS_DEFAULT') if rootDir == None: raise Exception('Either provide "rootDir" or set $LSST_THROUGHPUTS_DEFAULT') # Read in the total transmission curves. self.lsst = {} hardwarecomponentlist = ['detector.dat','lens1.dat', 'lens2.dat', 'lens3.dat', 'm1.dat', 'm2.dat', 'm3.dat'] commoncomponentlist = hardwarecomponentlist + ['atmos_10.dat',] for f in self.filterlist: componentlist = commoncomponentlist + ['filter_' +f +'.dat',] self.lsst[f] = Bandpass() self.lsst[f].readThroughputList(componentlist, rootDir=rootDir, verbose=verbose) # Read in the hardware-only transmission curves (no atmosphere). self.hardware = {} for f in self.filterlist: componentlist = hardwarecomponentlist + ['filter_' +f +'.dat',] self.hardware[f] = Bandpass() self.hardware[f].readThroughputList(componentlist, rootDir=rootDir, verbose=verbose) # Read in the dark sky SED. darkskyfile = 'darksky.dat' self.darksky = Sed() self.darksky.readSED_flambda(os.path.join(rootDir, darkskyfile)) # Set up a flat SED. self.flatsed = Sed() self.flatsed.setFlatSED() return
def ImportSEDfits(filename): sed = Sed() fits = pyfits.open(filename) crdelt = fits[0].header['CDELT1'] * .1 #Converts Angstroms to nanometers crval = fits[0].header['CRVAL1'] * .1 #same length = len(fits[0].data) wavelen = np.arange(crval,crval+length*crdelt,crdelt) sed.setSED(wavelen, fits[0].data) return(sed)
def ImportSEDfits(filename): sed = Sed() fits = pyfits.open(filename) crdelt = fits[0].header['CDELT1'] * .1 #Converts Angstroms to nanometers crval = fits[0].header['CRVAL1'] * .1 #same length = len(fits[0].data) wavelen = np.arange(crval, crval + length * crdelt, crdelt) sed.setSED(wavelen, fits[0].data) return (sed)
def setup_Throughputs(self, rootDir=None, verbose=True): """Read bandpasses and dark sky sed. Call this method first before doing anything else. """ self.filterlist = ('u', 'g', 'r', 'i', 'z', 'y') if rootDir == None: rootDir = os.getenv('LSST_THROUGHPUTS_DEFAULT') if rootDir == None: raise Exception( 'Either provide "rootDir" or set $LSST_THROUGHPUTS_DEFAULT') # Read in the total transmission curves. self.lsst = {} hardwarecomponentlist = [ 'detector.dat', 'lens1.dat', 'lens2.dat', 'lens3.dat', 'm1.dat', 'm2.dat', 'm3.dat' ] commoncomponentlist = hardwarecomponentlist + [ 'atmos_10.dat', ] for f in self.filterlist: componentlist = commoncomponentlist + [ 'filter_' + f + '.dat', ] self.lsst[f] = Bandpass() self.lsst[f].readThroughputList(componentlist, rootDir=rootDir, verbose=verbose) # Read in the hardware-only transmission curves (no atmosphere). self.hardware = {} for f in self.filterlist: componentlist = hardwarecomponentlist + [ 'filter_' + f + '.dat', ] self.hardware[f] = Bandpass() self.hardware[f].readThroughputList(componentlist, rootDir=rootDir, verbose=verbose) # Read in the dark sky SED. darkskyfile = 'darksky.dat' self.darksky = Sed() self.darksky.readSED_flambda(os.path.join(rootDir, darkskyfile)) # Set up a flat SED. self.flatsed = Sed() self.flatsed.setFlatSED() return
def calcZP_t(self, expTime=PhotometricDefaults.exptime, effarea=PhotometricDefaults.effarea, gain=PhotometricDefaults.gain): """ Calculate the instrumental zeropoint for a bandpass. """ # 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, expTime=expTime, effarea=effarea, gain=gain) # 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
def calcM5(self, skysed, hardware, expTime=PhotometricDefaults.exptime, nexp=PhotometricDefaults.nexp, readnoise=PhotometricDefaults.rdnoise, darkcurrent=PhotometricDefaults.darkcurrent, othernoise=PhotometricDefaults.othernoise, seeing=PhotometricDefaults.seeing['r'], platescale=PhotometricDefaults.platescale, gain=PhotometricDefaults.gain, effarea=PhotometricDefaults.effarea): """ Calculate the AB magnitude of a 5-sigma above sky background source. Pass into this function the bandpass, hardware only of bandpass, and sky sed objects. The exposure time, nexp, readnoise, darkcurrent, gain, seeing and platescale are also necessary. """ #This comes from equation 45 of the SNR document (v1.2, May 2010) #www.astro.washington.edu/users/ivezic/Astr511/LSST_SNRdoc.pdf #create a flat fnu source flatsource = Sed() flatsource.setFlatSED(wavelen_min=self.wavelen_min, wavelen_max=self.wavelen_max, wavelen_step=self.wavelen_step) snr = 5.0 v_n, noise_instr_sq, \ noise_sky_sq, noise_skymeasurement_sq, \ skycounts, neff = flatsource.calcNonSourceNoiseSq(skysed, hardware, readnoise, darkcurrent, othernoise, seeing, effarea, expTime, nexp, platescale, gain) counts_5sigma = (snr**2)/2.0/gain + numpy.sqrt((snr**4)/4.0/gain + (snr**2)*v_n) #renormalize flatsource so that it has the required counts to be a 5-sigma detection #given the specified background counts_flat = flatsource.calcADU(self, expTime=expTime*nexp, effarea=effarea, gain=gain) flatsource.multiplyFluxNorm(counts_5sigma/counts_flat) # Calculate the AB magnitude of this source. mag_5sigma = flatsource.calcMag(self) return mag_5sigma
class m5calculations(): def __init__(self, default_y='y4'): """Instantiate the object.""" self.default_y = default_y return def setup_Throughputs(self, rootDir=None, verbose=True): """Read bandpasses and dark sky sed. Call this method first before doing anything else. """ self.filterlist = ('u', 'g', 'r', 'i', 'z', 'y') if rootDir == None: rootDir = os.getenv('LSST_THROUGHPUTS_DEFAULT') if rootDir == None: raise Exception('Either provide "rootDir" or set $LSST_THROUGHPUTS_DEFAULT') # Read in the total transmission curves. self.lsst = {} hardwarecomponentlist = ['detector.dat','lens1.dat', 'lens2.dat', 'lens3.dat', 'm1.dat', 'm2.dat', 'm3.dat'] commoncomponentlist = hardwarecomponentlist + ['atmos_10.dat',] for f in self.filterlist: componentlist = commoncomponentlist + ['filter_' +f +'.dat',] self.lsst[f] = Bandpass() self.lsst[f].readThroughputList(componentlist, rootDir=rootDir, verbose=verbose) # Read in the hardware-only transmission curves (no atmosphere). self.hardware = {} for f in self.filterlist: componentlist = hardwarecomponentlist + ['filter_' +f +'.dat',] self.hardware[f] = Bandpass() self.hardware[f].readThroughputList(componentlist, rootDir=rootDir, verbose=verbose) # Read in the dark sky SED. darkskyfile = 'darksky.dat' self.darksky = Sed() self.darksky.readSED_flambda(os.path.join(rootDir, darkskyfile)) # Set up a flat SED. self.flatsed = Sed() self.flatsed.setFlatSED() return def setup_values(self, expTime=15.0, nexp=2., instrument_noise=None, instrument_noise_visit=None): """Set appropriate default values. Allows user to change expTime, nexp, or instrument_noise. Call this method second - and potentially many times. The exposure time is used to calculate dark current noise, m5 and Cm values, as well as ZP_t/ZP_h. These should be separated out when I have more time, to allow variable expTime in calcM5 below. """ # exposure time (seconds) self.expTime = expTime # number of exposures per visit (for calculating total m5 per visit rather than per exposure) self.nexp = nexp # Gain, electrons/adu. self.gain = 2.3 if instrument_noise != None: self.instrument_noise = instrument_noise self.othernoise = 0.0 self.rdnoise = self.instrument_noise elif instrument_noise_visit != None: self.instrument_noise = instrument_noise_visit / numpy.sqrt(self.nexp) self.othernoise = 0.0 self.rdnoise = self.instrument_noise else: # othernoise == camera electronics noise (electrons/exposure/pixel) self.othernoise = 3.9 # actual readnoise straight off the camera (electrons/exposure/pixel) self.rdnoise = 5.9 # total instrumental noise (camera readnoise + other). electrons/exposure/pixel. # add 0.5 * gain to (allow for potential undersampling of readnoise. with gain*0.5 term) #self.instrument_noise = numpy.sqrt(self.othernoise**2 + self.rdnoise**2 + (self.gain*0.5)**2) self.instrument_noise = numpy.sqrt(self.othernoise**2 + self.rdnoise**2) # Dark current, electrons/pix/second. self.dark_current = 0.2 # Plate scale, arcseconds per pixel. self.platescale = 0.2 # Telescope primary mirror diameter, effective collecting area (cm^2). self.effarea = numpy.pi*(6.5*100/2.0)**2 # Set default y band (for inputs where only 'y' is specified). self.default_y = 'y4' # Calculate Tb / Sb (integral of transmission/wavelength) as in LSE-40 (Table 7), to calculate kAtm. Tb = {} Sb = {} self.kAtm = {} stepsize = self.lsst[self.filterlist[0]].wavelen[1] - self.lsst[self.filterlist[0]].wavelen[0] for f in self.filterlist: self.kAtm[f] = [0.0, 0.0] Tb[f] = (self.lsst[f].sb / self.lsst[f].wavelen)*stepsize Sb[f] = (self.hardware[f].sb / self.hardware[f].wavelen)*stepsize if f.startswith('y'): condition = (self.lsst[f].wavelen >= 975) | (self.lsst[f].wavelen <= 800) self.kAtm[f][0] = -2.5*numpy.log10(Tb[f][condition].sum() / Sb[f][condition].sum()) condition = (self.lsst[f].wavelen > 800) & (self.lsst[f].wavelen < 975) self.kAtm[f][1] = -2.5*numpy.log10(Tb[f][condition].sum() / Sb[f][condition].sum()) else: self.kAtm[f][0] = -2.5*numpy.log10(Tb[f].sum() / Sb[f].sum()) # Calculate telescope zeropoints. self.zpT = {} self.zpH = {} for f in self.filterlist: self.zpT[f] = self.lsst[f].calcZP_t(expTime=self.nexp*self.expTime, effarea=self.effarea, gain=self.gain) self.zpH[f] = self.hardware[f].calcZP_t(expTime=self.nexp*self.expTime, effarea=self.effarea, gain=self.gain) return def print_values(self): """Print a report of the values used in the maglimit calculations.""" # Calculate some additional stuff which can be printed out. self.C_m = {} self.darkskymags = {} self.m5 = {} for f in self.filterlist: self.darkskymags[f] = self.darksky.calcMag(self.hardware[f]) self.m5[f] = self.lsst[f].calcM5(self.darksky, self.hardware[f], expTime=self.expTime, nexp=self.nexp, readnoise=self.rdnoise, othernoise=self.othernoise, darkcurrent=self.dark_current, gain=self.gain, effarea=self.effarea, seeing = 0.7, platescale = self.platescale) self.C_m[f] = self.m5[f] - 0.50*(self.darkskymags[f] - 21.0) # Start printing stuff to screen. print 'Exposure time ', self.expTime print 'Number of exposures per visit ', self.nexp print 'Gain ', self.gain print 'Camera readnoise ', self.rdnoise, ' and other readnoise ', self.othernoise print 'Instrumental noise per exposure ', self.instrument_noise print 'Dark current per second per pixel ', self.dark_current print 'Dark current per exposure ', self.dark_current*self.expTime print 'Total camera noise per visit (e/pix/visit) ', \ numpy.sqrt(self.nexp*self.expTime*self.dark_current + self.nexp*(self.instrument_noise)**2) print 'Platescale ', self.platescale print 'Telescope effective area ', self.effarea print 'Scaling relation C_m and kAtm values: ' for f in self.filterlist: print '\t \tin filter ', f, ' C_m=', self.C_m[f], ' kAtm= ', self.kAtm[f] print 'C_m=', self.C_m print 'kAtm=', self.kAtm print 'Telescope and Hardware zeropoints (%f sec visit):' %(self.expTime*self.nexp) for f in self.filterlist: print '\t\tin filter ', f, ' zpT = ', self.zpT[f], ' zpH = ', self.zpH[f] print 'Dark sky m5 limits :' for f in self.filterlist: print '\t\tin filter ', f, ' m5 = ', self.m5[f] print 'Dark sky noise (seeing=0.7 arcsec, @ zenith) & camera noise (both in electrons | ADU):' for f in self.filterlist: skynoise = numpy.sqrt(self.darksky.calcADU(self.hardware[f], expTime=self.nexp*self.expTime, gain=self.gain, effarea=self.effarea) \ * self.platescale**2 * self.gain) # electrons instnoise = numpy.sqrt(self.nexp *(self.dark_current*self.expTime + self.instrument_noise**2)) #electrons print '\t\tin filter ', f, ' skynoise = ', skynoise, '|', skynoise/self.gain, ' instnoise = ', instnoise, '|', instnoise/self.gain return def check_filter(self, filter): """Check filter array for consistency with internal set. Basically this means replace 'y' with the default y band choice. """ # Might have to generate a new filter array, if the length of the previous values was too short to hold default_y. newfilter = numpy.empty(len(filter), dtype=('str', len(self.default_y))) condition = (filter == 'y') newfilter[condition] = self.default_y condition = (filter != 'y') newfilter[condition] = filter[condition] filter = newfilter filters_used = numpy.unique(filter) for f in filters_used: if f not in self.filterlist: print 'Having a problem with %s, which has length %d (spaces?)' %(f, len(f)) indices = numpy.where(filter==f) print 'This pops up at observation(s) ', indices raise Exception('I do not recognize filter %s' %(f)) return filter def calc_maglimit(self, seeing, skybrightness, filter, airmass, snr=5.0): """Calculate limiting magnitude at snr, for nexp/expTime/gain/instNoise/zeropoint values of class, under conditions of seeing (arcseconds) and skybrightness (mag/arcsecond^2). Returns mag limit. """ # neff = 'effective' pixel area for a point source. (see LSE-40 - SNR doc - eqn 31). neff = 2.436 * (seeing/self.platescale)**2 # Calculate sky counts (counts/pixel/exp) in this bandpass from the skybrightness (mag/arcsecond^2). filters_used = numpy.unique(filter) skycounts = numpy.zeros(len(skybrightness), float) for f in filters_used: condition = (filter == f) # Convert skycounts from mags/''sq to counts/''sq for visit. skycounts[condition] = (10.**(-0.4*(skybrightness[condition] - self.zpH[f]))) # Convert to skycounts per pixel. skycounts = skycounts * self.platescale**2 #(mag/pixel) # Calculate the sky noise (squared) in ADU. skynoise_sq = skycounts / self.gain # Calculate noise (squared) from the instrument, converting result to ADU. instnoise_sq = self.nexp*(self.dark_current*self.expTime + self.instrument_noise**2) / (self.gain**2.0) # see equation 42 from SNR doc noise_sq = (skycounts / self.gain + instnoise_sq) * neff # Translate this to the required counts for a source using equations 45/46 from SNR doc. # Counts are in ADU using this formula. counts = (snr**2.0)/self.gain/2.0 + numpy.sqrt((snr**4.0)/(self.gain)**2/4.0 + (snr**2.0)*noise_sq) # And translate to counts, at this airmass in this filter. # Note we did not have to correct for extinction for the skycounts, because the sky background # is already extinction-corrected (which is part of the reason we must use hardware ZP only). mags = numpy.zeros(len(counts), 'float') for f in filters_used: condition = (filter == f) # Convert to magnitudes mags[condition] = -2.5*numpy.log10(counts[condition]) + self.zpT[f] # Correct for atmospheric extinction (note there is already a factor of X=1 in the zeropoint). mags[condition] = (mags[condition] - self.kAtm[f][0]*(airmass[condition]-1) - self.kAtm[f][1]*numpy.sqrt(airmass[condition]-1)) return mags
def ImportSED(filename,d_lambda): sed = Sed() sed.readSED_flambda(filename) sed.synchronizeSED(wavelen_step = d_lambda) return(sed)
def ImportSED(filename, d_lambda): sed = Sed() sed.readSED_flambda(filename) sed.synchronizeSED(wavelen_step=d_lambda) return (sed)
class m5calculations(): def __init__(self, default_y='y4'): """Instantiate the object.""" self.default_y = default_y return def setup_Throughputs(self, rootDir=None, verbose=True): """Read bandpasses and dark sky sed. Call this method first before doing anything else. """ self.filterlist = ('u', 'g', 'r', 'i', 'z', 'y') if rootDir == None: rootDir = os.getenv('LSST_THROUGHPUTS_DEFAULT') if rootDir == None: raise Exception( 'Either provide "rootDir" or set $LSST_THROUGHPUTS_DEFAULT') # Read in the total transmission curves. self.lsst = {} hardwarecomponentlist = [ 'detector.dat', 'lens1.dat', 'lens2.dat', 'lens3.dat', 'm1.dat', 'm2.dat', 'm3.dat' ] commoncomponentlist = hardwarecomponentlist + [ 'atmos_10.dat', ] for f in self.filterlist: componentlist = commoncomponentlist + [ 'filter_' + f + '.dat', ] self.lsst[f] = Bandpass() self.lsst[f].readThroughputList(componentlist, rootDir=rootDir, verbose=verbose) # Read in the hardware-only transmission curves (no atmosphere). self.hardware = {} for f in self.filterlist: componentlist = hardwarecomponentlist + [ 'filter_' + f + '.dat', ] self.hardware[f] = Bandpass() self.hardware[f].readThroughputList(componentlist, rootDir=rootDir, verbose=verbose) # Read in the dark sky SED. darkskyfile = 'darksky.dat' self.darksky = Sed() self.darksky.readSED_flambda(os.path.join(rootDir, darkskyfile)) # Set up a flat SED. self.flatsed = Sed() self.flatsed.setFlatSED() return def setup_values(self, expTime=15.0, nexp=2., instrument_noise=None, instrument_noise_visit=None): """Set appropriate default values. Allows user to change expTime, nexp, or instrument_noise. Call this method second - and potentially many times. The exposure time is used to calculate dark current noise, m5 and Cm values, as well as ZP_t/ZP_h. These should be separated out when I have more time, to allow variable expTime in calcM5 below. """ # exposure time (seconds) self.expTime = expTime # number of exposures per visit (for calculating total m5 per visit rather than per exposure) self.nexp = nexp # Gain, electrons/adu. self.gain = 2.3 if instrument_noise != None: self.instrument_noise = instrument_noise self.othernoise = 0.0 self.rdnoise = self.instrument_noise elif instrument_noise_visit != None: self.instrument_noise = instrument_noise_visit / numpy.sqrt( self.nexp) self.othernoise = 0.0 self.rdnoise = self.instrument_noise else: # othernoise == camera electronics noise (electrons/exposure/pixel) self.othernoise = 3.9 # actual readnoise straight off the camera (electrons/exposure/pixel) self.rdnoise = 5.9 # total instrumental noise (camera readnoise + other). electrons/exposure/pixel. # add 0.5 * gain to (allow for potential undersampling of readnoise. with gain*0.5 term) #self.instrument_noise = numpy.sqrt(self.othernoise**2 + self.rdnoise**2 + (self.gain*0.5)**2) self.instrument_noise = numpy.sqrt(self.othernoise**2 + self.rdnoise**2) # Dark current, electrons/pix/second. self.dark_current = 0.2 # Plate scale, arcseconds per pixel. self.platescale = 0.2 # Telescope primary mirror diameter, effective collecting area (cm^2). self.effarea = numpy.pi * (6.5 * 100 / 2.0)**2 # Set default y band (for inputs where only 'y' is specified). self.default_y = 'y4' # Calculate Tb / Sb (integral of transmission/wavelength) as in LSE-40 (Table 7), to calculate kAtm. Tb = {} Sb = {} self.kAtm = {} stepsize = self.lsst[self.filterlist[0]].wavelen[1] - self.lsst[ self.filterlist[0]].wavelen[0] for f in self.filterlist: self.kAtm[f] = [0.0, 0.0] Tb[f] = (self.lsst[f].sb / self.lsst[f].wavelen) * stepsize Sb[f] = (self.hardware[f].sb / self.hardware[f].wavelen) * stepsize if f.startswith('y'): condition = (self.lsst[f].wavelen >= 975) | (self.lsst[f].wavelen <= 800) self.kAtm[f][0] = -2.5 * numpy.log10( Tb[f][condition].sum() / Sb[f][condition].sum()) condition = (self.lsst[f].wavelen > 800) & (self.lsst[f].wavelen < 975) self.kAtm[f][1] = -2.5 * numpy.log10( Tb[f][condition].sum() / Sb[f][condition].sum()) else: self.kAtm[f][0] = -2.5 * numpy.log10(Tb[f].sum() / Sb[f].sum()) # Calculate telescope zeropoints. self.zpT = {} self.zpH = {} for f in self.filterlist: self.zpT[f] = self.lsst[f].calcZP_t(expTime=self.nexp * self.expTime, effarea=self.effarea, gain=self.gain) self.zpH[f] = self.hardware[f].calcZP_t(expTime=self.nexp * self.expTime, effarea=self.effarea, gain=self.gain) return def print_values(self): """Print a report of the values used in the maglimit calculations.""" # Calculate some additional stuff which can be printed out. self.C_m = {} self.darkskymags = {} self.m5 = {} for f in self.filterlist: self.darkskymags[f] = self.darksky.calcMag(self.hardware[f]) self.m5[f] = self.lsst[f].calcM5(self.darksky, self.hardware[f], expTime=self.expTime, nexp=self.nexp, readnoise=self.rdnoise, othernoise=self.othernoise, darkcurrent=self.dark_current, gain=self.gain, effarea=self.effarea, seeing=0.7, platescale=self.platescale) self.C_m[f] = self.m5[f] - 0.50 * (self.darkskymags[f] - 21.0) # Start printing stuff to screen. print 'Exposure time ', self.expTime print 'Number of exposures per visit ', self.nexp print 'Gain ', self.gain print 'Camera readnoise ', self.rdnoise, ' and other readnoise ', self.othernoise print 'Instrumental noise per exposure ', self.instrument_noise print 'Dark current per second per pixel ', self.dark_current print 'Dark current per exposure ', self.dark_current * self.expTime print 'Total camera noise per visit (e/pix/visit) ', \ numpy.sqrt(self.nexp*self.expTime*self.dark_current + self.nexp*(self.instrument_noise)**2) print 'Platescale ', self.platescale print 'Telescope effective area ', self.effarea print 'Scaling relation C_m and kAtm values: ' for f in self.filterlist: print '\t \tin filter ', f, ' C_m=', self.C_m[ f], ' kAtm= ', self.kAtm[f] print 'C_m=', self.C_m print 'kAtm=', self.kAtm print 'Telescope and Hardware zeropoints (%f sec visit):' % ( self.expTime * self.nexp) for f in self.filterlist: print '\t\tin filter ', f, ' zpT = ', self.zpT[ f], ' zpH = ', self.zpH[f] print 'Dark sky m5 limits :' for f in self.filterlist: print '\t\tin filter ', f, ' m5 = ', self.m5[f] print 'Dark sky noise (seeing=0.7 arcsec, @ zenith) & camera noise (both in electrons | ADU):' for f in self.filterlist: skynoise = numpy.sqrt(self.darksky.calcADU(self.hardware[f], expTime=self.nexp*self.expTime, gain=self.gain, effarea=self.effarea) \ * self.platescale**2 * self.gain) # electrons instnoise = numpy.sqrt(self.nexp * (self.dark_current * self.expTime + self.instrument_noise**2)) #electrons print '\t\tin filter ', f, ' skynoise = ', skynoise, '|', skynoise / self.gain, ' instnoise = ', instnoise, '|', instnoise / self.gain return def check_filter(self, filter): """Check filter array for consistency with internal set. Basically this means replace 'y' with the default y band choice. """ # Might have to generate a new filter array, if the length of the previous values was too short to hold default_y. newfilter = numpy.empty(len(filter), dtype=('str', len(self.default_y))) condition = (filter == 'y') newfilter[condition] = self.default_y condition = (filter != 'y') newfilter[condition] = filter[condition] filter = newfilter filters_used = numpy.unique(filter) for f in filters_used: if f not in self.filterlist: print 'Having a problem with %s, which has length %d (spaces?)' % ( f, len(f)) indices = numpy.where(filter == f) print 'This pops up at observation(s) ', indices raise Exception('I do not recognize filter %s' % (f)) return filter def calc_maglimit(self, seeing, skybrightness, filter, airmass, snr=5.0): """Calculate limiting magnitude at snr, for nexp/expTime/gain/instNoise/zeropoint values of class, under conditions of seeing (arcseconds) and skybrightness (mag/arcsecond^2). Returns mag limit. """ # neff = 'effective' pixel area for a point source. (see LSE-40 - SNR doc - eqn 31). neff = 2.436 * (seeing / self.platescale)**2 # Calculate sky counts (counts/pixel/exp) in this bandpass from the skybrightness (mag/arcsecond^2). filters_used = numpy.unique(filter) skycounts = numpy.zeros(len(skybrightness), float) for f in filters_used: condition = (filter == f) # Convert skycounts from mags/''sq to counts/''sq for visit. skycounts[condition] = (10.**( -0.4 * (skybrightness[condition] - self.zpH[f]))) # Convert to skycounts per pixel. skycounts = skycounts * self.platescale**2 #(mag/pixel) # Calculate the sky noise (squared) in ADU. skynoise_sq = skycounts / self.gain # Calculate noise (squared) from the instrument, converting result to ADU. instnoise_sq = self.nexp * (self.dark_current * self.expTime + self.instrument_noise**2) / (self.gain** 2.0) # see equation 42 from SNR doc noise_sq = (skycounts / self.gain + instnoise_sq) * neff # Translate this to the required counts for a source using equations 45/46 from SNR doc. # Counts are in ADU using this formula. counts = (snr**2.0) / self.gain / 2.0 + numpy.sqrt( (snr**4.0) / (self.gain)**2 / 4.0 + (snr**2.0) * noise_sq) # And translate to counts, at this airmass in this filter. # Note we did not have to correct for extinction for the skycounts, because the sky background # is already extinction-corrected (which is part of the reason we must use hardware ZP only). mags = numpy.zeros(len(counts), 'float') for f in filters_used: condition = (filter == f) # Convert to magnitudes mags[condition] = -2.5 * numpy.log10( counts[condition]) + self.zpT[f] # Correct for atmospheric extinction (note there is already a factor of X=1 in the zeropoint). mags[condition] = ( mags[condition] - self.kAtm[f][0] * (airmass[condition] - 1) - self.kAtm[f][1] * numpy.sqrt(airmass[condition] - 1)) return mags
for f in filterlist: Sb[f] = (hardware[f].sb / hardware[f].wavelen).sum() * hardware[f].wavelen_step Tb[f] = (total[f].sb / total[f].wavelen).sum() * total[f].wavelen_step writestring1 = 'Sb: ' writestring2 = 'Tb: ' for f in filterlist: writestring1 += '%s %.3f ' %(f, Sb[f]) writestring2 += '%s %.3f ' %(f, Tb[f]) print writestring1 print writestring2 # Set up to calculate m5. # Read in the dark sky SED darksky = Sed() darksky.readSED_flambda(os.path.join(throughputsDir, 'darksky.dat')) # Set up a range of exposure times (per exposure, not per visit) exptimes = numpy.arange(0.1, 100., 5.) # Set a range of readnoise values and zero out other noise contributions (I think this is what you want, to isolate readnoise completely) # (plus the camera team includes 'othernoise' into their 'instrumental noise' value .. this is another potential source of confusion when talking # to them about this .. usually when they say 'readnoise' they actually mean the full instrumental noise, but do not consider dark current) othernoise = 0 darkcurrent = 0 readnoises = [10., 13.] # noise per VISIT (so set nexp=1 and then exptime = visit time) nexp = 1 # Calculate m5 values for each of these exposure times. for exptime in exptimes: for readnoise in readnoises: