def test_from_mag_unit_error(self): bp = synphot.SpectralElement(synphot.Box1D, x_0=11.7 * u.um, width=0.1 * u.um) with pytest.raises(ValueError): Efrho.from_mag(5, 'asdf', 1 * u.arcsec, bp, dict(rh=1.5 * u.au, delta=1.0 * u.au))
def test_from_mag_bandpass(self, unit, efrho0): aper = 1 * u.arcsec # width = 0.1 um for speed bp = synphot.SpectralElement(synphot.Box1D, x_0=11.7 * u.um, width=0.1 * u.um) eph = dict(rh=1.0 * u.au, delta=1.0 * u.au) Tscale = 1.1 efrho = Efrho.from_mag(5, unit, aper, eph, bandpass=bp, Tscale=Tscale) assert np.isclose(efrho.cm, efrho0, rtol=0.001)
def test_observe_bandpass(self): w = u.Quantity(np.linspace(0.3, 1.0), 'um') f = u.Quantity(np.ones(len(w)), 'W/(m2 um)') s = Star.from_array(w, f) bp = synphot.SpectralElement(synphot.Box1D, x_0=0.55 * u.um, width=0.1 * u.um) fluxd = s.observe(bp) assert np.allclose(fluxd.value, [1]) bps = [ synphot.SpectralElement(synphot.Box1D, x_0=0.55 * u.um, width=0.1 * u.um), synphot.SpectralElement(synphot.Box1D, x_0=0.65 * u.um, width=0.1 * u.um) ] fluxd = s.observe(bps, unit='W/(m2 um)') assert np.allclose(fluxd.value, [1, 1])
def __call__(self, wave_or_freq, unit=None): """Evaluate the source spectrum. Parameters ---------- wave_or_freq : `~astropy.units.Quantity` Requested wavelength or frequencies of the resulting spectrum. If an array, `wave_or_freq` specifies bin centers. If a single value, fluxd will be interpolated to this wavelength/frequency. unit : string, `~astropy.units.Unit`, optional Spectral units of the output: flux density, 'vegamag', 'ABmag', or 'STmag'. If ``None``, return units are W/(m2 μm) for ``wave_or_freq`` as wavelength, otherwise return Jy. Returns ------- fluxd : `~astropy.units.Quantity` The spectrum binned to match `wave_or_freq`. If a single point is requested, the original spectrum is interpolated to it. """ import numpy as np import synphot if unit is None: if wave_or_freq.unit.is_equivalent('m'): unit = u.Unit('W/(m2 um)') else: unit = u.Jy if np.size(wave_or_freq) > 1: # Method adapted from http://www.astrobetter.com/blog/2013/08/12/python-tip-re-sampling-spectra-with-pysynphot/ specele = synphot.SpectralElement(synphot.ConstFlux1D(1)) obs = synphot.Observation(self.source, specele, binset=wave_or_freq, force='taper') # The following is the same as obs.binflux, except sample_binned # will do the unit coversions. fluxd = obs.sample_binned(flux_unit=unit) else: fluxd = self.source(wave_or_freq, unit) return fluxd
def test_mag(self): # compare to sbpy g = {'rh': 2.0 * u.au, 'delta': 1.0 * u.au, 'phase': 0 * u.deg} afrho = Afrho(100 * 2**-2, 'cm') tab = np.loadtxt(os.path.join(__path__[0], 'lsst-total-r.dat')).T r = synphot.SpectralElement(synphot.Empirical1D, points=tab[0] * u.nm, lookup_table=tab[1]) rap = 1 * u.arcsec m0 = afrho.to_fluxd(r, rap, g, unit=u.ABmag).value comet = Comet(R=1, afrho1=100, k=-2) m = comet.mag(g, 'r', rap=rap.value, nucleus=False) assert np.isclose(m, m0, atol=0.05)
def test_mag_fluxd0_bandpass(self): # comapre with test_mag_bandpass aper = 1 * u.arcsec # width = 0.1 um for speed bp = synphot.SpectralElement(synphot.Box1D, x_0=11.7 * u.um, width=0.1 * u.um) eph = dict(rh=1.0 * u.au, delta=1.0 * u.au) Tscale = 1.1 fluxd0 = u.Quantity(3631, 'Jy') efrho = Efrho(78750, 'cm') mag = efrho.mag(None, aper, eph, bandpass=bp, fluxd0=fluxd0, Tscale=Tscale) assert np.isclose(mag, 5, rtol=0.001)
def observe(self, wfb, unit=None, **kwargs): """Observe source as through filters or spectrometer. Parameters ---------- wfb : `~astropy.units.Quantity`, `~synphot.SpectralElement` Wavelengths, frequencies, or bandpasses. May also be a list of ``SpectralElement``s. unit : string, `~astropy.units.Unit`, optional Spectral units of the output (flux density). If ``None``, the default depends on ``wfb``: W/(m2 μm) for wavelengths or bandpasses, Jy for frequencies. **kwargs Additional keyword arguments for `~synphot.observation.Observation`, e.g., ``force``. Returns ------- fluxd : `~astropy.units.Quantity` The spectrum rebinned. Raises ------ SinglePointSpectrumError - If requested wavelengths or frequencies has only one value. Notes ----- Method adapted from AstroBetter post by Jessica Lu: http://www.astrobetter.com/blog/2013/08/12/python-tip-re-sampling-spectra-with-pysynphot/ """ import synphot from .. import units as sbu # avoid circular dependency # promote single bandpasses to a list, but preserve number of # dimensions if isinstance(wfb, (synphot.SpectralElement, str)): ndim = 0 wfb = [wfb] else: ndim = np.ndim(wfb) if isinstance(wfb, (tuple, list)): if unit is None: unit = u.Unit('W/(m2 um)') else: unit = u.Unit(unit) fluxd = np.ones(len(wfb)) * unit for i in range(len(wfb)): fluxd[i] = self.filt(wfb[i], unit=unit, **kwargs)[1] else: if np.size(wfb) == 1: raise SinglePointSpectrumError( 'Multiple wavelengths or frequencies required for ' 'observe. Consider interpolation with {}() instead.'. format(self.__class__.__name__)) if unit is None: if wfb.unit.is_equivalent('m'): unit = u.Unit('W/(m2 um)') else: unit = u.Jy else: unit = u.Unit(unit) specele = synphot.SpectralElement(synphot.ConstFlux1D(1)) # Use force='taper' to prevent PartialOverlap execption. # Specele is defined over all wavelengths, but most # spectral standards are not. kwargs['force'] = kwargs.get('force', 'taper') obs = synphot.Observation(self.source, specele, binset=wfb, **kwargs) if unit.is_equivalent(sbu.VEGAmag): fluxd = obs.sample_binned(flux_unit='W/(m2 um)').to( unit, sbu.spectral_density_vega(wfb)) else: fluxd = obs.sample_binned(flux_unit=unit) if np.ndim(fluxd) != ndim: # likely need a squeeze fluxd = fluxd.squeeze() return fluxd
def box(center, width): return synphot.SpectralElement( synphot.Box1D, x_0=center * u.um, width=width * u.um)
def observe_spectrum(self, wave_or_freq, unit=None, **kwargs): """Observe source as through a spectrometer. .. Important:: This method works best when the requested spectral resolution is lower than the spectral resolution of the internal data. If the requested wavelengths/frequencies are exactly the same as the internal spectrum, then the internal spectrum will be returned without binning. This special case does not work for subsets of the wavelengths. Parameters ---------- wave_or_freq : `~astropy.units.Quantity` Wavelengths or frequencies of the spectrum. Spectral bins will be centered at these values. The length must be larger than 1. unit : string, `~astropy.units.Unit`, optional Spectral flux density units for the output. If ``None``, the default is W/(m2 μm) for wavelengths, Jy for frequencies. **kwargs Additional keyword arguments for `~synphot.observation.Observation`, e.g., ``force``. Returns ------- fluxd : `~astropy.units.Quantity` The spectrum rebinned. Raises ------ SinglePointSpectrumError - If requested wavelengths or frequencies has only one value. Notes ----- Method for spectra adapted from AstroBetter post by Jessica Lu: https://www.astrobetter.com/blog/2013/08/12/python-tip-re-sampling-spectra-with-pysynphot/ """ from .. import units as sbu # avoid circular dependency if np.size(wave_or_freq) == 1: raise SinglePointSpectrumError( 'Multiple wavelengths or frequencies required for ' 'observe_spectrum. Instead consider interpolation ' 'with {}().'.format(self.__class__.__name__)) if unit is None: if wave_or_freq.unit.is_equivalent('m'): unit = u.Unit('W/(m2 um)') else: unit = u.Jy else: unit = u.Unit(unit) specele = synphot.SpectralElement(synphot.ConstFlux1D, amplitude=1) # Specele is defined over all wavelengths, but most spectral # standards are not. force='taper' will affect retrieving # flux densities at the edges of the spectrum, but is # preferred to avoid wild extrapolation. kwargs['force'] = kwargs.get('force', 'taper') obs = synphot.Observation(self.source, specele, binset=wave_or_freq, **kwargs) if unit.is_equivalent(sbu.VEGAmag): fluxd = obs.sample_binned(flux_unit='W/(m2 um)').to( unit, sbu.spectral_density_vega(wave_or_freq)) else: fluxd = obs.sample_binned(flux_unit=unit) return fluxd
def get_sky_spectrum(self, return_type="table", filename=None): from astropy import table if filename is None: filename = "skycalc_temp.fits" if not self.validate_params(): raise ValueError("Object contains invalid parameters. " "Not calling ESO") skm = SkyModel() skm.callwith(self.values) skm.write(filename) with fits.open(filename) as hdu: tbl = table.Table(hdu[1].data) tbl["lam"].unit = u.um for colname in tbl.colnames: if "flux" in colname: tbl[colname].unit = u.Unit("ph s-1 m-2 um-1 arcsec-2") date_created = dt.now().strftime('%Y-%m-%dT%H:%M:%S') meta_data = { "DESCRIPT": "Sky transmission and emission curves", "SOURCE": "ESO Skycalc utility", "AUTHOR": "ESO Skycalc utility", "STATUS": "Tested - Generated from ESO Skycalc utility", "DATE_CRE": date_created, "ETYPE": "TERCurve", "EDIM": 1 } params = {k: (self.values[k], self.comments[k]) for k in self.keys} meta_data.update(params) if "tab" in return_type: tbl.meta.update(meta_data) if "ext" in return_type: tbl_return = tbl else: tbl_small = table.Table() tbl_small.add_columns([tbl["lam"], tbl["trans"], tbl["flux"]]) tbl_return = tbl_small return tbl_return elif "arr" in return_type: wave = tbl["lam"].data * tbl["lam"].unit trans = tbl["trans"].data flux = tbl["flux"].data * tbl["flux"].unit return wave, trans, flux elif "syn" in return_type: import synphot as sp trans = sp.SpectralElement(sp.Empirical1D, points=tbl["lam"].data * tbl["lam"].unit, lookup_table=tbl["trans"].data) funit = u.Unit("ph s-1 m-2 um-1") flux = sp.SourceSpectrum(sp.Empirical1D, points=tbl["lam"].data * tbl["lam"].unit, lookup_table=tbl["flux"].data * funit) print("Warning: synphot doesn't accept surface brightnesses \n" "The resulting spectrum should be multiplied by arcsec-2") return trans, flux elif "fit" in return_type: hdu0 = fits.PrimaryHDU() for key in meta_data: hdu0.header[key] = meta_data[key] hdu1 = fits.table_to_hdu(tbl) hdu = fits.HDUList([hdu0, hdu1]) return hdu