def setUp(self): self.sp = S.BlackBody(5000) self.bp = S.ObsBandpass('Johnson,V') self.oldref = S.refs.getref() S.setref(comptable='$PYSYN_CDBS/mtab/t260548pm_tmc.fits') self.tda = dict(spectrum=str(self.sp), bp=str(self.bp)) self.tda.update(refs.getref())
def test_instrument_source_pysynphot(): """ Tests the ability to provide a source as a pysynphot.Spectrum object """ # 5700 K blackbody + Johnson B filter wavelengths = np.array([ 3.94000000e-07, 4.22000000e-07, 4.50000000e-07, 4.78000000e-07, 5.06000000e-07 ]) weights = np.array( [0.18187533, 0.29036168, 0.26205719, 0.1775512, 0.0881546]) inst = instrument.Instrument() inst.filter = 'B' psf_weights_explicit = inst.calc_psf(source=(wavelengths, weights), fov_pixels=FOV_PIXELS, detector_oversample=2, fft_oversample=2, nlambda=5) psf_weights_pysynphot = inst.calc_psf(source=pysynphot.BlackBody(5700), fov_pixels=FOV_PIXELS, detector_oversample=2, fft_oversample=2, nlambda=5) assert psf_weights_pysynphot[0].header['NWAVES'] == len(wavelengths), \ "Number of wavelengths in PSF header does not match number requested" assert np.allclose( psf_weights_explicit[0].data, psf_weights_pysynphot[0].data, rtol=1e-4 ), ( # Slightly larger tolerance to accomodate minor changes w/ pysynphot versions "PySynphot multiwavelength PSF does not match the weights and wavelengths pre-computed for " "a 5500 K blackbody in Johnson B (has pysynphot changed?)") return psf_weights_pysynphot
def setUpClass(cls): #Write the file cls.sp = S.BlackBody(5500) cls.fname = '/tmp/t163_spcase.fits' cls.sp.writefits(cls.fname) #Read the header cls.f = pyfits.open(cls.fname) cls.h0 = cls.f[0].header cls.h1 = cls.f[1].header
def test_blackbody_pysynphot(): bb = pysynphot.BlackBody(5000) bb.convert('flam') my_bb = physics.blackbody_flux(bb.wave * u.AA, 5000 * u.K, 1 * u.Rsun, 1 * u.kpc) my_bb_vals = my_bb.to(u.erg / u.s / u.cm**2 / u.AA).value assert np.all( (my_bb_vals - bb.flux) / bb.flux < 0.005 ), 'Blackbody flux discrepancy exceeds 0.5% between PySynphot and ours'
def setUp(self): self.bb = S.BlackBody(5000) self.em = S.GaussianSource(3300, 1, 1) self.flat = S.FlatSpectrum(10) self.pl = S.PowerLaw(5000, -2) self.tspec = S.ArraySpectrum(self.bb.wave, self.bb.flux, fluxunits=self.bb.fluxunits) self.pl.writefits('ac_pl.fits') self.fspec = S.FileSpectrum('ac_pl.fits')
def setUpClass(cls): #Write the file cls.sp = S.BlackBody(5500) cls.fname = '/tmp/t163_spcase.fits' cls.addkeys = dict(sptype=('blackbody', 'Type of spectrum'), bbtemp=(5500, )) cls.sp.writefits(cls.fname, hkeys=cls.addkeys) #Read the header cls.f = pyfits.open(cls.fname) cls.h0 = cls.f[0].header
def test_values(self): ref = np.array([ 0.00019318, 0.00019623, 0.0001993, 0.00020238, 0.00020549, 0.00020861, 0.00021175, 0.00021491, 0.00021809, 0.00022128 ]) bb = S.BlackBody(5500) test = bb.sample(self.wave[:10]) self.assertApproxNumpy(test, ref, accuracy=3.e-5)
def setUp(self): self.sp = S.BlackBody(4400) #Note that this bandpass has a simple 1A-spacing binwave self.bp = S.ObsBandpass('acs,hrc,f555w') self.refwave = 5500 #Angstroms self.obs = S.Observation(self.sp, self.bp) self.obs.convert('counts') self.tda = dict(thresh=0.001) self.tra = dict() self.tda['spectrum'] = str(self.sp) self.tra['bandpass'] = str(self.bp)
def test_merge_wave_sets(self): bb = S.BlackBody(20000) ext = S.Extinction(0.04, 'gal1') new_wave = S.spectrum.MergeWaveSets(bb.wave, ext.wave) delta = new_wave[1:] - new_wave[:-1] self.assertTrue((delta > S.spectrum.MERGETHRESH).all(), msg='Deltas should be < %g, min delta = %f' % (S.spectrum.MERGETHRESH, delta.min()))
def compute_zpts(instrument, detector, band, mjd): bandpass = pyS.ObsBandpass('{:s},{:s},{:s},mjd#{:d}'.format( instrument, detector, band, mjd)) spec_bb = pyS.BlackBody(50000) spec_bb_norm = spec_bb.renorm(1, 'counts', bandpass) obs = pyS.Observation(spec_bb_norm, bandpass) zps = {} zps['vega'] = obs.effstim('vegamag') zps['st'] = obs.effstim('stmag') zps['ab'] = obs.effstim('abmag') return zps
def setUp(self): #Use a defined comptab here: we're examining native arrays self.oldref = S.refs.getref() S.setref(comptable='$PYSYN_CDBS/mtab/u4c18498m_tmc.fits') self.sp = S.BlackBody(4400) self.bp = S.ObsBandpass('stis,fuvmama,g140m,c1470,s52x01') self.obs = S.Observation(self.sp, self.bp) self.refwave = 1450 self.tda = dict(thresh=0.001) self.tra = dict() self.tda['spectrum'] = str(self.sp) self.tra['bandpass'] = str(self.bp)
def get_all_thermalbb_flatT(N=100, TMIN=3000., TMAX=50000.): all_sed = [] all_random_values = np.random.random_sample((N, )) all_random_Temp = TMIN + (TMAX - TMIN) * all_random_values for index in np.arange(N): T = all_random_Temp[index] sed = S.BlackBody(T) sed.convert('flam') # to be sure every spectrum is in flam unit all_sed.append(sed) return all_sed
def get_synthetic_mag_bb(T, mag_in, filt_in="sdss,r", system_in="vegamag", filt_out="sdss,g", system_out="vegamag"): ''' Provided a black body temperature, it normalizes the emission to one input magnitude in a given photometric system, and produces another synthetic magnitude in the output band and photometric system. Parameters ---------- T : float Black body temperature mag_in: float magnitude that the black body spectrum will be normalized to filt_in: string Name of the band where the input magnitude is in. system_in: photometric system for the input mangitude (agmag, vegamag, stmag...) filt_out: string Name of the band where the out magnitude will be computed. system_out: photometric system for the output mangitude (agmag, vegamag, stmag...) Returns ------- mag : float The synthetic magnitude of the black body spectrum for that photometric band and system. Examples -------- >>> phot_utils.get_filter_mag_bb(6000, 19, "V", "vegamag", "sdss,g", "abmag") >>> 19.311307758370692 ''' sp = ps.BlackBody(T) sp.convert('flam') if (filt_in in banddic.keys()): bp_in = ps.FileBandpass(banddic[filt_out]) else: bp_in = ps.ObsBandpass(filt_out) if (filt_out in banddic.keys()): bp_out = ps.FileBandpass(banddic[filt_out]) else: bp_out = ps.ObsBandpass(filt_out) sp_norm = sp.renorm(mag_in, system_in, bp_in) obs = ps.Observation(sp_norm, bp_out, binset=sp_norm.wave) mag = obs.effstim(system_out) return mag
def get_all_thermalbb(N=10, T0=6000., sigT=100.): all_sed = [] all_random_values = np.random.standard_normal(N) for index in np.arange(N): T = T0 + all_random_values[index] * sigT print T sed = S.BlackBody(T) sed.convert('flam') # to be sure every spectrum is in flam unit all_sed.append(sed) return all_sed
def test_pysynphot_spectra_cache(): """ The result of the Pysynphot calculation is cached. This ensures the appropriate key appears in the cache after one calculation, and that subsequent calculations proceed without errors (exercising the cache lookup code). """ import pysynphot source = pysynphot.BlackBody(5700) nlambda = 2 ins = instrument.Instrument() cache_key = ins._get_spec_cache_key(source, nlambda) assert cache_key not in ins._spectra_cache, "How is the cache populated already?" psf = ins.calc_psf(nlambda=2, source=source, fov_pixels=2) assert cache_key in ins._spectra_cache, "Cache was not populated" psf2 = ins.calc_psf(nlambda=2, source=source, fov_pixels=2) maxdiff = np.abs(psf[0].data - psf2[0].data).max() assert(maxdiff < 1e-7), "PSF using cached spectrum differs from first PSF calculated"
""" # BORROWED FROM PHAT (on Github): # https://github.com/PanchromaticHubbleAndromedaTreasury/SEDFitting/blob/master/python/extinction.py # NOTE THAT PYSYNPHOT BY DEFAULT IMPLEMENTS THE CALZETTI+1994 DUST ATTENUATION LAW (with the xgal keyword)!! # EXTINCTION MANAGEMENT # Here are defined extinction functions that relies # on given laws. # # ================================================== """ import os, sys import numpy import pysynphot as S bb = S.BlackBody(5000.) # for its default wavelength set __version__ = '0.0.2' class ExtinctionLaw(object): """ Template class """ def __init__(self): self.name = 'None' def function(self, lamb, *arg, **kwargs): """ expected to contain a function of lambda that return the extinction values """ pass def inFilter(self, s, *args, **kwargs):
def _get_weights(self, source=None, nlambda=None, monochromatic=None, verbose=False): """ Return the set of discrete wavelengths, and weights for each wavelength, that should be used for a PSF calculation. Uses pysynphot (if installed), otherwise assumes simple-minded flat spectrum """ if nlambda is None or nlambda == 0: nlambda = self._get_default_nlambda(self.filter) if monochromatic is not None: poppy_core._log.info("Monochromatic calculation requested.") return (np.asarray([monochromatic]), np.asarray([1])) elif _HAS_PYSYNPHOT and (isinstance( source, pysynphot.spectrum.SourceSpectrum) or source is None): """ Given a pysynphot.SourceSpectrum object, perform synthetic photometry for nlambda bins spanning the wavelength range of interest. Because this calculation is kind of slow, cache results for reuse in the frequent case where one is computing many PSFs for the same spectral source. """ poppy_core._log.debug( "Calculating spectral weights using pysynphot, nlambda=%d, source=%s" % (nlambda, str(source))) if source is None: source = pysynphot.BlackBody(5700) poppy_core._log.info( "No source spectrum supplied, therefore defaulting to 5700 K blackbody" ) poppy_core._log.debug("Computing spectral weights for source = " + str(source)) try: key = self._get_spec_cache_key(source, nlambda) if key in self._spectra_cache: poppy_core._log.debug( "Previously computed spectral weights found in cache, just reusing those" ) return self._spectra_cache[key] except KeyError: pass # in case sourcespectrum lacks a name element so the above lookup fails - just do the below calc. poppy_core._log.info( "Computing wavelength weights using synthetic photometry for %s..." % self.filter) band = self._get_synphot_bandpass(self.filter) # choose reasonable min and max wavelengths w_above10 = np.where( band.throughput > 0.10 * band.throughput.max()) minwave = band.wave[w_above10].min() maxwave = band.wave[w_above10].max() poppy_core._log.debug("Min, max wavelengths = %f, %f" % (minwave / 1e4, maxwave / 1e4)) wave_bin_edges = np.linspace(minwave, maxwave, nlambda + 1) wavesteps = (wave_bin_edges[:-1] + wave_bin_edges[1:]) / 2 deltawave = wave_bin_edges[1] - wave_bin_edges[0] effstims = [] for wave in wavesteps: poppy_core._log.debug( "Integrating across band centered at %.2f microns with width %.2f" % (wave / 1e4, deltawave / 1e4)) box = pysynphot.Box(wave, deltawave) * band if box.throughput.max() == 0: # watch out for pathological cases with no overlap (happens with MIRI FND at high nlambda) result = 0.0 else: binset = np.linspace( wave - deltawave, wave + deltawave, 30 ) # what wavelens to use when integrating across the sub-band? result = pysynphot.Observation( source, box, binset=binset).effstim('counts') effstims.append(result) effstims = np.array(effstims) effstims /= effstims.sum() wave_m = band.waveunits.Convert(wavesteps, 'm') # convert to meters newsource = (wave_m, effstims) if verbose: _log.info( " Wavelengths and weights computed from pysynphot: " + str(newsource)) self._spectra_cache[self._get_spec_cache_key(source, nlambda)] = newsource return newsource elif isinstance(source, dict) and ('wavelengths' in source) and ('weights' in source): # Allow providing directly a set of specific weights and wavelengths, as in poppy.calc_psf source option #2 return source['wavelengths'], source['weights'] elif isinstance(source, tuple) and len(source) == 2: # Allow user to provide directly a tuple, as in poppy.calc_psf source option #3 return source else: # Fallback simple code for if we don't have pysynphot. poppy_core._log.warning( "Pysynphot unavailable (or invalid source supplied)! Assuming flat # of counts versus wavelength." ) # compute a source spectrum weighted by the desired filter curves. # The existing FITS files all have wavelength in ANGSTROMS since that is the pysynphot convention... filterfile = self._filters[self.filter].filename filterfits = fits.open(filterfile) filterdata = filterfits[1].data try: wavelengths = filterdata.WAVELENGTH.astype('=f8') throughputs = filterdata.THROUGHPUT.astype('=f8') except AttributeError: raise ValueError( "The supplied file, {0}, does not appear to be a FITS table with WAVELENGTH and " + "THROUGHPUT columns.".format(filterfile)) if 'WAVEUNIT' in filterfits[1].header: waveunit = filterfits[1].header['WAVEUNIT'].lower() if re.match(r'[Aa]ngstroms?', waveunit) is None: raise ValueError( "The supplied file, {0}, has WAVEUNIT='{1}'. Only WAVEUNIT = Angstrom supported " + "when Pysynphot is not installed.".format( filterfile, waveunit)) else: waveunit = 'Angstrom' poppy_core._log.warning( "CAUTION: no WAVEUNIT keyword found in filter file {0}. Assuming = {1} by default" .format(filterfile, waveunit)) poppy_core._log.warning( "CAUTION: Just interpolating rather than integrating filter profile, over {0} steps" .format(nlambda)) wtrans = np.where(throughputs > 0.4) lrange = wavelengths[ wtrans] * 1e-10 # convert from Angstroms to Meters # get evenly spaced points within the range of allowed lambdas, centered on each bin lambd = np.linspace( np.min(lrange), np.max(lrange), nlambda, endpoint=False) + ( np.max(lrange) - np.min(lrange)) / (2 * nlambda) filter_fn = scipy.interpolate.interp1d(wavelengths * 1e-10, throughputs, kind='cubic', bounds_error=False) weights = filter_fn(lambd) filterfits.close() return lambd, weights
def setUp(self): self.sp = S.BlackBody(30000) self.waveunits = 'flam' self.fluxunits = 'angstrom'
def testRenormSynPysyn(): jv = S.ObsBandpass('johnson,v') acs = S.ObsBandpass('acs,hrc,f555w') abox = S.Box(5500,1) #Removing vegamag from this list because it introduces # a data dependency. Pysynphot has upgraded the version of # vega that it uses for vegamag conversions (11/5/2010, r1629) # but synphot has not. This caused the emission line test # to fail (since the numbers were so small anyway, the small # difference exceeded the tolerance). uset = ['photlam', 'flam', 'photnu', 'fnu', 'jy', 'mjy', 'counts', 'stmag' ,'abmag', 'obmag'] #, 'vegamag' ucounts = ['counts', 'obmag'] bb = S.BlackBody(5000) bb.syndescrip = 'bb(5000)' em = S.GaussianSource(1e-13,5500,250) em.syndescrip = 'em(5500,250,1e-13,flam)' foo = {bb:'bb', em:'em'} rnfuncs = {renorm.StdRenorm:'Std'} #Just this one, # renorm.renormTweak:'tweak', # renorm.renormRevEng:'RevEng' # renorm.renormSVN:'SVN'} - ignore SVN for now, we know it doesn't work for method in rnfuncs: for sp in (bb, em): for u in uset: # ucounts rnunit = Units(u) if u == 'counts': bp = acs bp.syndescrip = "band(%s) " % bp elif rnunit.isMag: bp = jv bp.syndescrip = 'band(johnson,v)' else: bp = abox bp.syndescrip = 'box(5500,1)' def renorm_compare(sp, rnval, rnunits, bp, callable): """Sp and bp must have a special .syndescrip attribute that contains the string needed to make it with synphot""" # Renormalize the spectrum pysyn = callable(sp, bp, rnval, rnunits) #pysyn=sp.renorm(rnval,rnunits,bp) #pysyn = spectrum.StdRenorm(sp,bp,rnval,rnunits) userdir = tempfile.mkdtemp(suffix='pysynphot') old_cwd = os.getcwd() iraf.chdir(userdir) # Make a wavetable and a wavecat fname = "%s.fits" % rnunits pysyn.writefits(fname, clobber=True) wname = "%s.cat" % rnunits f = open(wname, 'w') f.write("box %s\n" % fname) f.close() oname = "syn_%s" % fname try: #Run countrate spstring = "rn(%s,%s,%s,%s) " % (sp.syndescrip, bp.syndescrip, rnval, rnunits) iraf.countrate(spectrum=spstring, magnitude="", instrument="box(15000,30000)", form=str(pysyn.fluxunits), wavecat=wname, output=oname) syn = S.FileSpectrum(oname) # Check that they have the same shape and fluxunits assert (syn.flux.shape == pysyn.flux.shape) assert (type(syn.fluxunits) == type(pysyn.fluxunits)) # Now a real test idx = np.where(syn.flux != 0) rat = (syn.flux[idx] / pysyn.flux[idx]) q = abs(1 - rat[2:-2]) qtrunc = (10**4*q).astype(np.int) assert np.alltrue(qtrunc < 110), \ "Min/max ratio = %f,%f" % (q.min(),q.max()) finally: iraf.chdir(old_cwd) shutil.rmtree(userdir) renorm_compare.description = ( "%s.test%s_%s_%s" % (__name__, rnfuncs[method], foo[sp], u)) yield renorm_compare, sp, 10, u, bp, method
def testcompspec(self): self.sp = S.BlackBody(5500) + S.FlatSpectrum(1) tst = self.sp(3000) assert True
def setUp(self): self.sp=S.BlackBody(5000)
for (specid, spec) in specs.items(): default_spectra['descs'][specid] = spec['desc'] fesc = 'fesc' in spec['file'] fits = spec['file'][-1].endswith('fits') if fits: default_spectra['specs'][specid] = load_pysfits(spec) elif fesc: default_spectra['specs'][specid] = load_fesc(spec) else: default_spectra['specs'][specid] = load_txtfile(spec) flatsp = pys.FlatSpectrum(30.0, fluxunits='abmag') flatsp = flatsp.renorm(30.0, 'abmag', pys.ObsBandpass('johnson,v')) flatsp.convert('abmag') flatsp.convert('nm') default_spectra['specs']['fab'] = pre_encode(flatsp) default_spectra['descs']['fab'] = 'Flat (AB)' flamsp = pys.FlatSpectrum(30.0, fluxunits='flam') flamsp = flamsp.renorm(30.0, 'abmag', pys.ObsBandpass('galex,fuv')) flamsp.convert('abmag') flamsp.convert('nm') default_spectra['specs']['flam'] = pre_encode(flamsp) default_spectra['descs']['flam'] = 'Flat in F_lambda' bb = pys.BlackBody(5000) bb.convert('abmag') bb.convert('nm') default_spectra['specs']['bb'] = pre_encode(bb) default_spectra['descs']['bb'] = 'Blackbody (5000K)'
def snr_osiris_imager(tint, mag, filter, phot_sys='vegamag'): ########## # Detector Characteristics: ########## # http://www.teledyne-si.com/imaging/H4RG%20Brochure%20-%20rev3%20v2-2%20-%20OSR.pdf # http://proceedings.spiedigitallibrary.org/proceeding.aspx?articleid=1363315 # Claire, personal communication (via Tuan) # Read noise (e- per read) read_noise = 5.0 # Dark current (e- per second) dark_current = 0.05 ########## # Throughput ########## # OSIRIS throughput # Keck is 77.9 m^2 (equivalent to 9.96 m circular aperture). # Assumed something between inscribed and circumscribed pupil cold stop. # Thanks to Tuan, Claire Max, and Fitz for this info. # Some from the OSIRIS manual. tp_window = 0.97 tp_stop = 0.95 tp_detector = 0.70 tp_mirrors = 0.99**7 osiris_throughput = tp_window * tp_stop * tp_mirrors * tp_detector ngao_throughput = 0.61 tel_throughput = 0.80 atm_throughput = 0.90 # Total system throughput (spectrograph + AO system + telescope) throughput = osiris_throughput * ngao_throughput * tel_throughput * atm_throughput ########## # Background ########## # Used Tuan's calculations: # Temperatures: Tel = 275 AO = 273 # Emissivities: Tel = 0.09 AO = 0.02 # Units: mag / arcsec^2 bkg_mag = { 'z': 18.778, 'y': 17.230, 'J': 16.510, # 'J': 18.96, 'H': 13.706, 'K': 13.855, 'Hcont': 13.607, 'Kcont': 13.948 } # Filter above the atmosphere (no telescope/instrument) #bandpass = synthetic.FilterNIRC2(filter) filterStr = filter if filter == 'z': filterStr = 'SDSS,z' if filter == 'y': filterStr = 'Stromgren,y' bandpass = pysynphot.ObsBandpass(filterStr) #star = pysynphot.Vega star = pysynphot.BlackBody(6000) star = star.renorm(mag, phot_sys, bandpass) # Observe the star through a filter star_in_filt = pysynphot.observation.Observation(star, bandpass, binset=bandpass.wave) # Integrate over the filter star_flux = star_in_filt.integrate( fluxunits='photlam') # photons / s / cm^2 print 'star_flux 1 = ', star_flux, ' photons s^-1 cm^-2' # Convert to observed flux on Keck primary mirror keck_area = 77.9 * 100.0**2 # cm^2 star_flux *= keck_area # photons / s print 'star_flux 2 = ', star_flux, ' photons s^-1' # Apply througput of atmosphere, telescope, AO, instrument. # This throughput already includes a quantum efficienciy correction. star_flux *= throughput # e- / s print 'star_flux 3 = ', star_flux, ' e- s^-1' # Do the same for the background bkg = pysynphot.FlatSpectrum(1, waveunits='angstrom', fluxunits='flam') bkg = bkg.renorm(bkg_mag[filter], phot_sys, bandpass) bkg_in_filt = pysynphot.observation.Observation(bkg, bandpass, binset=bandpass.wave) bkg_flux_dens = bkg_in_filt.integrate( fluxunits='photlam') # photons / s / cm^2 / arcsec^2 bkg_flux_dens *= keck_area * throughput # e- / s / arcsec^2 # Aperture information # technically there should be an aperture correction on the star as well. # In the NIRC2 calculator, they just multiply by the Strehl. pix_scale = 0.01 # arcsec aper_radius = 0.1 # arcsec aper_area = math.pi * aper_radius**2 # arcsec^2 npix = aper_area / pix_scale**2 print 'npix = ', npix # Calculate signal-to-noise in the specified integration time. signal = star_flux * tint noise_variance = star_flux * tint noise_variance += bkg_flux_dens * tint * aper_area noise_variance += (read_noise**2) * npix noise_variance += dark_current * tint * npix noise = math.sqrt(noise_variance) snr = signal / noise print 'signal = ', signal print 'noise = ', noise print 'snr = ', snr return star_in_filt
def setUp(self): self.sp = S.BlackBody(5500) self.sp.warnings['FakeWarn'] = True
def setUp(self): self.sp = S.BlackBody(30000) self.bp = S.ObsBandpass('johnson,v')
def setUp(self): self.sp = S.BlackBody(60000) + S.GaussianSource(1e-12, 5000, 30)
def setUp(self): self.sp=S.BlackBody(10000)*S.ObsBandpass('acs,hrc,f555w')
def test_c1280(self): self.obsmode = 'cos,fuv,g140l,c1280' self.tda['obsmode'] = self.obsmode bp = S.ObsBandpass(self.obsmode) obs = S.Observation(S.BlackBody(5500), bp)