def filter_vega_zp(filterfile, filterzero): ''' ###################################################### # Input # # -------------------------------------------------- # # filterfile: file containing filter function # # filterzero: flux corresponding to mag=0 # # -------------------------------------------------- # # Output # # -------------------------------------------------- # # mag_0: magnitude of Vega in filter system. # ###################################################### ''' from synphot import SourceSpectrum, SpectralElement, Observation #load Vega spectrum spec = SourceSpectrum.from_vega() #Load ascii filter function if filterfile.split('/')[-1][:6] == 'Bessel': filt = SpectralElement.from_file(filterfile, wave_unit='nm') else: filt = SpectralElement.from_file(filterfile, wave_unit='AA') wave = filt.waveset #Synthetic observation obs = Observation(spec, filt) flux = obs.effstim(flux_unit='jy', waverange=(wave[0],wave[-1])) #Calibrate to zero point (zp) mag_0 = -2.512*np.log10(flux/filterzero) return mag_0
def filter_mag(filterfile, filterzero, syn_spec, syn_err=None, wrange=None): ''' ###################################################### # Input # # -------------------------------------------------- # # filterfile: file containing filter function # # filterzero: flux [Jy] corresponding to mag=0 # # syn_spec: synphot spectrum object of source # # wrange: (start,end) observed wavelength range # # -------------------------------------------------- # # Output # # -------------------------------------------------- # # mag: magnitude of source in filter system. # ###################################################### ''' from synphot import SpectralElement, Observation, Empirical1D, SourceSpectrum import astropy.units as u #Load ascii filter function if filterfile.split('/')[-1][:6] == 'Bessel': filt = SpectralElement.from_file(filterfile, wave_unit='nm') else: filt = SpectralElement.from_file(filterfile, wave_unit='AA') if wrange is None: fwave = filt.waveset swave = syn_spec.waveset wrange = (max(fwave[0],swave[0]),min(fwave[-1],swave[-1])) waves = np.linspace(wrange[0], wrange[-1], 10000) #Synthetic observation obs = Observation(syn_spec, filt, force='extrap') flux = obs.effstim(flux_unit='jy', waverange=wrange).value #flux = obs.effstim(flux_unit='jy', wavelengths=waves).value #Calibrate magnitude with zero point mag = -2.512*np.log10(flux/filterzero) #Synthetic observation of error spectrum if syn_err is not None: #square filter and error spectrum filt2 = SpectralElement(Empirical1D, points=fwave, lookup_table=np.square(filt(fwave))) pseudo_flux = np.square(syn_err(swave,flux_unit='jy')).value syn_err2 = SourceSpectrum(Empirical1D, points=swave, lookup_table=pseudo_flux) #sum errors in quadrature obs = Observation(syn_err2, filt2, force='extrap') #flux_err = np.sqrt(obs.effstim(waverange=wrange).value) flux_err = np.sqrt(obs.effstim(wavelengths=waves).value) #convert to magnitude error mag_err = (2.5/np.log(10))*(flux_err/flux) return mag, mag_err else: return mag
def filter_flux(filterfile, syn_spec, syn_err=None, wrange=None): ''' ###################################################### # Input # # -------------------------------------------------- # # filterfile: file containing filter function # # syn_spec: synphot spectrum object of source # # wrange: (start,end) observed wavelength range # # -------------------------------------------------- # # Output # # -------------------------------------------------- # # flux: flux of source in filter system. # ###################################################### ''' from synphot import SpectralElement, Observation, Empirical1D, SourceSpectrum import astropy.units as u #Load ascii filter function if filterfile.split('/')[-1][:6] == 'Bessel': filt = SpectralElement.from_file(filterfile, wave_unit='nm') else: filt = SpectralElement.from_file(filterfile, wave_unit='AA') if wrange is None: fwave = filt.waveset swave = syn_spec.waveset wrange = (max(fwave[0],swave[0]),min(fwave[-1],swave[-1])) wlengths = fwave[np.logical_and(fwave>wrange[0], fwave<wrange[1])] else: fwave = filt.waveset wlengths = fwave[np.logical_and(fwave>wrange[0]*u.AA, fwave<wrange[1]*u.AA)] #Synthetic observation obs = Observation(syn_spec, filt, force='extrap') flux = obs.effstim(flux_unit='jy', waverange=wrange).value #flux = obs.effstim(flux_unit='jy', wavelengths=wlengths).value #Synthetic observation of error spectrum if syn_err is not None: #square filter and error spectrum filt2 = SpectralElement(Empirical1D, points=fwave, lookup_table=np.square(filt(fwave))) pseudo_flux = np.square(syn_err(swave,flux_unit='jy')).value syn_err2 = SourceSpectrum(Empirical1D, points=swave, lookup_table=pseudo_flux) #sum errors in quadrature obs = Observation(syn_err2, filt2, force='extrap') flux_err = np.sqrt(obs.effstim(waverange=wrange).value) return flux, flux_err else: return flux
def _map_filter_to_standard(self, filtername): """Maps a passed <filtername> to a standard Johnson/Cousins/Bessell filter profile. Returns a SpectralElement, defaulting to V""" band_mapping = { 'U': 'johnson_u', 'B': 'johnson_b', 'V': 'johnson_v', 'R': conf.bessell_R_file, 'Rc': 'cousins_r', 'I': conf.bessell_I_file, 'Ic': 'cousins_i', 'J': 'bessel_j', 'H': 'bessel_h', 'K': 'bessel_k', 'G': conf.gaiadr2_g_file } band_name = band_mapping.get(self._convert_filtername(filtername), 'johnson_v') if band_name.startswith('http://svo'): band = SpectralElement.from_file(band_name) else: band = SpectralElement.from_filter(band_name) return band
def get_phot(waves, exts, bands): """ Compute the extinction in the requested bands Parameters ---------- waves : numpy.ndarray The wavelengths exts : numpy.ndarray The extinction values at wavelengths "waves" bands: list of strings Bands requested Outputs ------- band extinctions : numpy array Calculated band extinctions """ # create a SourceSpectrum object from the extinction curve spectrum = SourceSpectrum( Empirical1D, points=waves * 1e4, lookup_table=exts, ) # path for band response curves band_path = ( "/Users/mdecleir/measure_extinction/measure_extinction/data/Band_RespCurves/" ) # dictionary linking the bands to their response curves bandnames = { "J": "2MASSJ", "H": "2MASSH", "K": "2MASSKs", "IRAC1": "IRAC1", "IRAC2": "IRAC2", "WISE1": "WISE1", "WISE2": "WISE2", "L": "AAOL", "M": "AAOM", } # compute the extinction value in each band band_ext = np.zeros(len(bands)) for k, band in enumerate(bands): # create the bandpass (as a SpectralElement object) bp = SpectralElement.from_file( "%s%s.dat" % (band_path, bandnames[band])) # assumes wavelengths are in Angstrom!! # integrate the extinction curve over the bandpass, only if the bandpass fully overlaps with the extinction curve (this actually excludes WISE2) if bp.check_overlap(spectrum) == "full": obs = Observation(spectrum, bp) band_ext[k] = obs.effstim().value else: band_ext[k] = np.nan return band_ext
def get_phot(spec, bands): """ Compute the fluxes in the requested bands. Parameters ---------- spec : SpecData object the spectrum bands: list of strings bands requested Outputs ------- band fluxes : numpy array calculated band fluxes """ # create a SourceSpectrum object from the spectrum, excluding the bad regions spectrum = SourceSpectrum( Empirical1D, points=spec.waves.to(u.Angstrom)[spec.npts != 0], lookup_table=spec.fluxes[spec.npts != 0], ) # path for band response curves band_path = pkg_resources.resource_filename("measure_extinction", "data/Band_RespCurves/") # dictionary linking the bands to their response curves bandnames = { "J": "2MASSJ", "H": "2MASSH", "K": "2MASSKs", "IRAC1": "IRAC1", "IRAC2": "IRAC2", "WISE1": "WISE1", "WISE2": "WISE2", "L": "AAOL", "M": "AAOM", } # define the units of the output fluxes funit = u.erg / (u.s * u.cm * u.cm * u.Angstrom) # compute the flux in each band fluxes = np.zeros(len(bands)) for k, band in enumerate(bands): # create the bandpass (as a SpectralElement object) bp = SpectralElement.from_file( "%s%s.dat" % (band_path, bandnames[band])) # assumes wavelengths are in Angstrom!! # integrate the spectrum over the bandpass, only if the bandpass fully overlaps with the spectrum (this actually excludes WISE2) if bp.check_overlap(spectrum) == "full": obs = Observation(spectrum, bp) fluxes[k] = obs.effstim(funit).value else: fluxes[k] = np.nan return fluxes
def get_phot(mwave, mflux, band_names, band_resp_filenames): """ Compute the magnitudes in the requested bands. Parameters ---------- mwave : vector wavelengths of model flux mflux : vector model fluxes band_names: list of strings names of bands requested band_resp_filename : list of strings filenames of band response functions for the requested bands Outputs ------- bandinfo : BandData object """ # get a BandData object bdata = BandData("BAND") # path for non-HST band response curves data_path = pkg_resources.resource_filename("measure_extinction", "data/Band_RespCurves/") # compute the fluxes in each band for k, cband in enumerate(band_names): if "HST" in cband: bp_info = cband.split("_") bp = STS.band("%s,%s,%s" % (bp_info[1], bp_info[2], bp_info[3])) ncband = "%s_%s" % (bp_info[1], bp_info[3]) else: bp = SpectralElement.from_file("%s%s" % (data_path, band_resp_filenames[k])) ncband = cband # check if the wavelength units are in microns instead of Angstroms # may not work if max(bp.waveset) < 500 * u.Angstrom: print("filter wavelengths not in angstroms") exit() # a.waveset *= 1e4 iresp = bp(mwave) bflux = np.sum(iresp * mflux) / np.sum(iresp) bflux_unc = 0.0 bdata.band_fluxes[ncband] = (bflux, bflux_unc) # calculate the band magnitudes from the fluxes bdata.get_band_mags_from_fluxes() # get the band fluxes from the magnitudes # partially redundant, but populates variables useful later bdata.get_band_fluxes() return bdata
def __init__(self): self.verbose = False self.telescope_area = 25.326 * 10000. #JWST primary area in cm^2 self.wave_bins = np.arange(0.5, 5, 0.1) * u.micron self.vega = SourceSpectrum.from_vega() self.g2v = STS.grid_to_spec('ck04models', 5860, 0., 4.40) self.kband = SpectralElement.from_filter('bessel_k') self.bpdir = './' self.bpfile = os.path.join( self.bpdir, 'F335M_nircam_plus_ote_throughput_moda_sorted.txt') self.bp = SpectralElement.from_file(self.bpfile, wave_unit=u.micron)
def plot_bandpasses(bands): # create the figure plt.figure(figsize=(15, 5)) # plot all response curves for band in bands: bp = SpectralElement.from_file("%s%s.dat" % (band_path, band)) if "MIPS" in band: wavelengths = bp.waveset * 10**4 else: wavelengths = bp.waveset plt.plot(wavelengths, bp(bp.waveset), label=band) plt.xlabel(r"$\lambda$ [$\AA$]", size=15) plt.ylabel("transmission", size=15) plt.legend(ncol=2, loc=1) plt.show()
def synphot_calcs(self, bpfile): """ Calculate zeropoint for a given bandpass in several photometric systems Arguments: ---------- bpfile -- Text file containing the throuput table for a single bandpass Returns: -------- Bandpass zeropoint value in Vega mags, AB mags, ST mags, photflam, photfnu, and megajansky. Also returns pivot wavelength """ # Define wavelength list to use #wave_bins = np.arange(0.5, 5, 0.1) * u.micron # Use refactored synphot to calculate zeropoints orig_bp = SpectralElement.from_file(bpfile) # Now reduce the PCE curve by a factor equal to # the gain, in order to get the output to translate # from ADU/sec rather than e/sec bp = orig_bp / self.gain #bp.model.lookup_table = bp.model.lookup_table / self.gain photflam = bp.unit_response(self.telescope_area) photplam = bp.pivot() st_zpt = -2.5 * np.log10(photflam.value) - 21.1 ab_zpt = (-2.5 * np.log10(photflam.value) - 21.1 - 5 * np.log10(photplam.value) + 18.6921) #mjy_zpt = units.convert_flux(photplam,photflam, 'MJy') mjy_zpt = photflam.to(u.MJy, u.spectral_density(photplam)) obs = Observation(self.vega, bp, binset=wave_bins) vega_zpt = -obs.effstim(flux_unit='obmag', area=self.telescope_area) photfnu = units.convert_flux(photplam, photflam, units.FNU) return (vega_zpt.value, ab_zpt, st_zpt, photflam.value, photfnu.value, mjy_zpt.value, photplam.value)
def grism_cal(self, throughput_files): """ Calculate flux cal outputs for grism mode. Input files should contain the total system throughput for the grism plus crossing filter. The input file list should contain throughputs for all orders of just a single grism+crossing filter. Arguments: ---------- throughput_files -- list of text files containing filter throughputs Returns: -------- Astropy table containing flux calibration information for grism mode """ print('need to update this for synphot') allrows = [] filters = np.array([]) pupils = np.array([]) orders = np.array([]) fluxes = np.array([]) uncs = np.array([]) nelems = np.array([]) waves = np.array([]) resps = np.array([]) #waves = np.expand_dims(waves,axis=0) #resps = np.expand_dims(resps,axis=0) for file in throughput_files: # Read in necessary file info with fits.open(file) as h: cname = h[0].header['COMPNAME'] junk, filter, mod = cname.split('_') mod = mod[-1].upper() pupil = 'GRISMR' print('Eventually need to be smarter about pupil value!!!!!') print("opening {}".format(file)) bp = SpectralElement.from_file(file) # Now reduce the PCE curve by a factor equal to # the gain, in order to get the output to translate # from ADU/sec rather than from e/sec bp.model.lookup_table = bp.model.lookup_table / self.gain # Pivot wavelength in microns pwave = bp.pivot() #pwave = pwave.to(u.micron) # Need to find the order here, so that the proper # dispersion value is used. ord = file.find('order') order = file[ord + 5] dispersion = self.disp[order] #find pivot? mean? center? effective? wavelength #denom = self.h * self.c / eff_lambda #countratedensity = self.telescope_area * tp['Throughput'] * vegaflux / denom #countratedensityflux,countratedensitywave,pwave = self.getcountsflux(bp) #totalrate = self.integrate(countratedensity) #Is the variable below used at all? #grism_total_rate += totalrate obs = self.getcountsflux(bp) # From observation, get the flux in counts countratedensityflux = obs(obs.binset, flux_unit='count', area=self.telescope_area) print('countratedensityflux', countratedensityflux) # Multiply by dispersion countratedensityflux *= dispersion # Countrate density at the pivot wavelength print('pivot', pwave.value) print('countratedensityflux*dispersion', countratedensityflux) print('obs.binset', obs.binset) #cnorm = np.interp(pwave.value,obs.binset,countratedensityflux) cnorm = obs(pwave, flux_unit='count', area=self.telescope_area) * dispersion print('cnorm', cnorm) # Vega flux value at pivot wavelength, convert to Jansky vega_pivot = self.vega(pwave) j0 = units.convert_flux(pwave, vega_pivot, 'MJy') # Now we need to divide by the area of a pixel in # sterradians, so we can eventually get MJy/str per ADU/sec j0 /= self.pixel_area_sr #vega_pivotorig = np.interp(pwave.value,self.vega.waveset,self.vega(self.vega.waveset)) #print("units of vega flux are: {}".format(self.vega(self.vega.waveset).unit)) #j0 = self.toJansky(vega_pivot.value,pwave.value) / 1.e6 # Ratio of Vega flux to the countrate density at pivot wavelength ratio = j0 / cnorm print('') print('PHOTMJSR', ratio) print( 'NIRISS values are 0.01 to 2.0. I would expect ours to be similar!' ) print('') # Define a set of wavelengths to evaluate relative fluxcal goodpts = bp(bp.waveset) > 0.0001 minwave = np.min(bp.waveset[goodpts]) maxwave = np.max(bp.waveset[goodpts]) w = minwave.value allwaves = np.array([w]) while (w < maxwave.value): delt = w / (np.absolute(np.int(order)) * self.resolving_power) allwaves = np.append(allwaves, w + delt) w += delt # Calculate Vega flux at each wavelength nelem = len(allwaves) allfluxes = self.vega(allwaves) alljansky = units.convert_flux(allwaves, allfluxes, 'MJy') allcounts = np.interp(allwaves, obs.binset, countratedensityflux) #allfluxes = np.interp(allwaves,self.vega.waveset,self.vega(self.vega.waveset)) #alljansky = self.toJansky(allfluxes,allwaves) / 1.e6 # Normalize the Vega counts at all wavelengths by the value at the pivot # wavelength allratio = alljansky.value / allcounts / ratio / self.pixel_area_sr #print(np.min(allwaves),np.max(allwaves),allwaves[0],allwaves[-1]) #f,a = plt.subplots() #a.plot(allwaves,allratio,color='red') #a.set_xlabel('Wavelength ({})'.format(bp.waveset.unit)) #a.set_ylabel('Normalized Ratio (MJy/str per count/sec)') #a.set_title("{}, Order {}".format(filter.upper(),order)) #f.savefig(os.path.split(file)[-1]+'_allratios.pdf') #f,a = plt.subplots() #a.plot(allwaves,alljansky,color='red') #a.set_xlabel('Wavelength ({})'.format(bp.waveset.unit)) #a.set_ylabel('Vega Flux (MJy)') #a.set_title("{}, Order {}".format(filter.upper(),order)) #f.savefig(os.path.split(file)[-1]+'_alljansky.pdf') #f,a = plt.subplots() #a.plot(allwaves,allcounts,color='red') #a.set_xlabel('Wavelength ({})'.format(bp.waveset.unit)) #a.set_ylabel('Counts (count/sec)') #a.set_title("{}, Order {}".format(filter.upper(),order)) #f.savefig(os.path.split(file)[-1]+'_allcounts.pdf') if np.min(allcounts) < 0: print('WARNING: counts<0 for {},{}'.format( filter.upper(), order)) stop if '444' in filter: print("") print('444!!!!!!') print(allratio.value) print(len(allratio.value)) print(type(allratio.value)) #bad = allratio.value > 1e5 bad = np.where(allratio.value > 1e5) print(len(bad[0])) print(type(bad[0])) print(alljansky.value[bad[0][0:10]]) print(allcounts[bad[0][0:10]]) print(ratio) print(self.str_per_detector) print(allratio.value[bad[0][0:10]]) print(allwaves) stop # Pad allwaves and allratio to be the length needed by the # photom reference file. Also convert wavelengths to microns. allwaves = np.pad(allwaves / 1.e4, (0, self.maxlen - nelem), 'constant') allratio = np.pad(allratio, (0, self.maxlen - nelem), 'constant') print(allwaves[100:105]) print(allcounts[100:105]) print(alljansky[100:105]) print(alljansky[100:105] / allcounts[100:105]) print(ratio) print(allratio[100:105]) print("******************") print("******************") print("******************") print("need real conversion factor and uncertainty!!!") conversion_factor = 1000. uncertainty = ratio * .1 #row = [filter,pupil,np.int(order),ratio*conversion_factor,uncertainty,nelem,allwaves,allratio] # Populate lists that will be used to create the final table filters = np.append(filters, filter) pupils = np.append(pupils, pupil) orders = np.append(orders, np.int(order)) fluxes = np.append(fluxes, ratio * conversion_factor) uncs = np.append(uncs, uncertainty) nelems = np.append(nelems, nelem) print(allwaves.shape) if len(waves) == 0: waves = allwaves waves = np.expand_dims(waves, axis=0) resps = allratio resps = np.expand_dims(resps, axis=0) else: waves = np.append(waves, np.expand_dims(allwaves, axis=0), axis=0) resps = np.append(resps, np.expand_dims(allratio, axis=0), axis=0) print('waves.shape', waves.shape) print('resps.shape', resps.shape) print(filters) print(pupils) print(orders) print(fluxes) print(uncs) print(nelems) print(waves[0, 40:45]) print(resps[0, 40:45]) # Zip all data together alldata = np.array(zip(np.array(filters), np.array(pupils), np.array(fluxes), np.array(uncs), np.array(orders), np.array(nelems), np.array(waves), np.array(resps)), dtype=self.model.phot_table.dtype) return alldata
def from_file(cls, filename, **kwargs): modelclass = SpectralElement.from_file(filename, **kwargs) return cls(modelclass=modelclass)
def flux2ABmag(wave, flux, band, mag, plot=False): # "band" should be either "sdss_g" or "V" for the moment # The input "wave" should be in nm # Define bandpass: if band == "sdss_g": bp = SpectralElement.from_file('../utility/photometry/g.dat') magvega = -0.08 elif band == "V": bp = SpectralElement.from_filter('johnson_v') magvega = 0.03 # SDSS g-band magnitude of Vega #gmagvega=-0.08 # Read the spectrum of Vega sp_vega = SourceSpectrum.from_vega() wv_vega = sp_vega.waveset fl_vega = sp_vega(wv_vega, flux_unit=units.FLAM) ## Convolve with the bandpass obs_vega = Observation(sp_vega, bp) ## Integrated flux fluxtot_vega = obs_vega.integrate() # Read the synthetic spectrum sp = SourceSpectrum(Empirical1D, points = wave * 10., \ lookup_table=flux*units.FLAM) wv = sp.waveset fl = sp(wv, flux_unit=units.FLAM) ## Convolve with the bandpass obs = Observation(sp, bp, force='extrap') ## Integrated g-band flux fluxtot = obs.integrate() # Scaling factor to make the flux compatible with the desired magnitude dm = mag - magvega const = fluxtot_vega * 10**(-0.4 * dm) / fluxtot # Scale the original flux by const fl_scale = const * fl # Convert to ABmag fl_scale_mag = units.convert_flux(wv, fl_scale, out_flux_unit='abmag') sp_scaled_mag = SourceSpectrum(Empirical1D, points=wv, lookup_table=fl_scale_mag) # Plot if plot == True: fig, ax = plt.subplots(2, 1, sharex=True) ax[0].plot(wave * 10., flux, linestyle="-", marker="") ax[1].plot(wv[1:-1], sp_scaled_mag(wv, flux_unit=u.ABmag)[1:-1], linestyle="-", marker="") ax[1].set_xlabel("Angstrom") ax[0].set_ylabel("$F_{\lambda}$") ax[1].set_ylabel("ABmag") ax[1].set_ylim(mag + 3., mag - 2.0) #plt.show() return (wv[1:-1] / 10., sp_scaled_mag(wv, flux_unit=u.ABmag)[1:-1])