def test_returns_correct_photon_counts_when_scaled_to_vega_zero(self): src = stars("Generic/Johnson.V", [0] * u.mag, ["A0V"], [0], [0]) filt = tcu.get_filter("Generic/Johnson.J") obs = sp.Observation(src.spectra[0], filt) assert obs.effstim(sp.units.PHOTLAM).value == approx(193, rel=0.01) filt = tcu.get_filter("Generic/Johnson.I") obs = sp.Observation(src.spectra[0], filt) assert obs.effstim(u.Jansky).value == approx(2367, rel=0.01)
def observe_bandpass(self, bp, unit=None, **kwargs): """Observe through a bandpass. Parameters ---------- bp : `~synphot.SpectralElement`, list, or tuple Bandpass. unit : string, `~astropy.units.Unit`, optional Spectral flux density units for the output. The default is W/(m2 μm). **kwargs Additional keyword arguments for `~synphot.observation.Observation`, e.g., ``force``. Returns ------- lambda_eff : `~astropy.units.Quantity` Effective wavelength(s) of the observation(s). fluxd : `~astropy.units.Quantity` The spectrum rebinned. """ from .. import units as sbu # avoid circular dependency # promote single bandpasses to a list, but preserve number of # dimensions if isinstance(bp, (SpectralElement, str)): ndim = 0 bp = [bp] else: ndim = np.ndim(bp) if unit is None: unit = u.Unit('W/(m2 um)') else: unit = u.Unit(unit) fluxd = np.ones(len(bp)) * unit for i in range(len(bp)): obs = synphot.Observation(self.source, bp[i], **kwargs) lambda_eff = obs.effective_wavelength() lambda_pivot = obs.bandpass.pivot() _fluxd = obs.effstim('W/(m2 um)') if unit.is_equivalent(sbu.VEGAmag): fluxd[i] = _fluxd.to(unit, sbu.spectral_density_vega(bp[i])) else: fluxd[i] = _fluxd.to(unit, u.spectral_density(lambda_pivot)) if np.ndim(fluxd) != ndim: fluxd = fluxd.squeeze() return lambda_eff, fluxd
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 filt(self, bp, unit='W / (m2 um)', **kwargs): """Observe source through a single filter. Parameters ---------- bp: string or `~synphot.SpectralElement` The name of a filter, or a transmission spectrum as a `~synphot.SpectralElement`. See Notes for built-in filter names. unit: string, `~astropy.units.Unit`, optional Spectral flux density units of the output. **kwargs Additional keyword arguments for `~synphot.observation.Observation`, e.g., ``force``. Returns ------- wave: `~astropy.units.Quantity` Effective wavelength. fluxd: `~astropy.units.Quantity` or float Spectral flux density. Notes ----- Filter reference data is from STScI's Calibration Reference Data System. * ``'bessel_j'`` (Bessel * J*) * ``'bessel_h'`` (Bessel * H*) * ``'bessel_k'`` (Bessel * K*) * ``'cousins_r'`` (Cousins * R*) * ``'cousins_i'`` (Cousins * I*) * ``'johnson_u'`` (Johnson * U*) * ``'johnson_b'`` (Johnson * B*) * ``'johnson_v'`` (Johnson * V*) * ``'johnson_r'`` (Johnson * R*) * ``'johnson_i'`` (Johnson * I*) * ``'johnson_j'`` (Johnson * J*) * ``'johnson_k'`` (Johnson * K*) """ import synphot from .. import units as sbu # avoid circular dependency if isinstance(bp, str): bp = synphot.SpectralElement.from_filter(bp) obs = synphot.Observation(self.source, bp, **kwargs) wave = obs.effective_wavelength() _unit = u.Unit(unit) if _unit.is_equivalent(sbu.VEGAmag): fluxd = obs.effstim('W/(m2 um)').to(_unit, sbu.spectral_density_vega(bp)) else: fluxd = obs.effstim(flux_unit=_unit) return wave, fluxd
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 test_returns_correct_photon_count_when_scaled_to_jansky(self): src = stars("Generic/Johnson.V", [3631] * u.Jansky, ["A0V"], [0], [0]) filt = tcu.get_filter("Paranal/HAWKI.J") obs = sp.Observation(src.spectra[0], filt) phs = obs.effstim(sp.units.PHOTLAM).value * src.fields[0]["weight"][0] assert phs == approx(210, rel=0.01)
def test_returns_correct_photon_count_when_initialised_in_hawki_j(self): src = stars("Paranal/HAWKI.J", [0] * u.mag, ["A0V"], [0], [0]) filt = tcu.get_filter("Paranal/HAWKI.J") obs = sp.Observation(src.spectra[0], filt) assert obs.effstim(sp.units.PHOTLAM).value == approx(210, rel=0.01)
def filt(self, bp, unit='W / (m2 um)', **kwargs): """Spectrum observed through a filter. Parameters ---------- bp : string or `~synphot.SpectralElement` The name of a filter, or a transmission spectrum as a `~synphot.SpectralElement`. See notes for built-in filter names. unit : string, `~astropy.units.Unit`, optional Spectral units of the output: flux density, 'vegamag', 'ABmag', or 'STmag'. See :ref:`sbpy_spectral_standards` for calibration notes. **kwargs Additional keyword arguments for `~synphot.observation.Observation`. Returns ------- wave : `~astropy.units.Quantity` Effective wavelength. fluxd : `~astropy.units.Quantity` or float Flux density or magnitude. Notes ----- Filter reference data is from STScI's Calibration Reference Data System. * ``'bessel_j'`` (Bessel *J*) * ``'bessel_h'`` (Bessel *H*) * ``'bessel_k'`` (Bessel *K*) * ``'cousins_r'`` (Cousins *R*) * ``'cousins_i'`` (Cousins *I*) * ``'johnson_u'`` (Johnson *U*) * ``'johnson_b'`` (Johnson *B*) * ``'johnson_v'`` (Johnson *V*) * ``'johnson_r'`` (Johnson *R*) * ``'johnson_i'`` (Johnson *I*) * ``'johnson_j'`` (Johnson *J*) * ``'johnson_k'`` (Johnson *K*) """ import synphot from synphot.units import VEGAMAG from .vega import Vega from ..units import spectral_density_vega if isinstance(bp, str): bp = synphot.SpectralElement.from_filter(bp) if not isinstance(bp, synphot.SpectralElement): raise ValueError('`bp` must be a string (filter name) or' ' `synphot.SpectralElement`.') obs = synphot.Observation(self.source, bp, **kwargs) wave = obs.effective_wavelength() _unit = u.Unit(unit) if _unit.is_equivalent(VEGAMAG): fluxd = obs.effstim('W/(m2 um)').to(_unit, spectral_density_vega(bp)) else: fluxd = obs.effstim(_unit) return wave, fluxd
def make_cluster_rates(self, masses, ins, bandpass=None): filter = ins.filter instrument = ins.DETECTOR try: coords = np.load(os.path.join(self.gridpath, 'input.npy'), allow_pickle=True) except UnicodeError: coords = np.load(os.path.join(self.gridpath, 'input.npy'), allow_pickle=True, encoding='bytes') m, t, g, i = self.get_star_info() temps = np.interp(masses, m, t) gravs = np.interp(masses, m, g) mags = np.interp(masses, m, i) metals = np.full_like(mags, self.metallicity) if os.path.exists( os.path.join( self.gridpath, 'result_{}_{}.npy'.format(instrument.lower(), filter.lower()))): values = np.load(os.path.join( self.gridpath, 'result_{}_{}.npy'.format(instrument.lower(), filter.lower())), allow_pickle=True) interpolation_function = RegularGridInterpolator( tuple([x for x in coords]), values) try: countrates = interpolation_function( np.array((metals, gravs, temps, mags)).T) except ValueError as v: self.log('error', 'Exception caught when interpolating: {}'.format(v)) min_mag = coords[-1][0] max_mag = coords[-1][-1] interpolation_function = RegularGridInterpolator( tuple([x for x in coords]), values, bounds_error=False, fill_value=0.) mags_min = np.full_like(mags, min_mag) mags_max = np.full_like(mags, max_mag) countrates = interpolation_function( np.array((metals, gravs, temps, mags)).T) countrates_min = interpolation_function( np.array((metals, gravs, temps, mags_min)).T) countrates_min = countrates_min * np.power( 10, -(mags - min_mag) / 2.512) countrates_max = interpolation_function( np.array((metals, gravs, temps, mags_max)).T) countrates_max = countrates_max * np.power( 10, -(mags - max_mag) / 2.512) countrates[np.where( mags < mags_min)] = countrates_min[np.where( mags < mags_min)] countrates[np.where( mags > mags_max)] = countrates_max[np.where( mags > mags_max)] else: self.log( 'warning', 'Could not find result file "result_{}_{}.npy" from {}'.format( instrument.lower(), filter.lower(), self.gridpath)) # raise FileNotFoundError('Could not find result file "result_{}_{}.npy" from {}'.format(instrument.lower(), filter.lower(), self.gridpath)) import synphot as syn import stsynphot as stsyn countrates = np.array(()) johnson_i = syn.SpectralElement.from_filter('johnson_i') for te, log_g, z, j_i in zip(temps, gravs, metals, mags): spectrum = stsyn.grid_to_spec('phoenix', te, z, log_g) spectrum = spectrum.normalize(u.Magnitude(j_i), johnson_i) obs = syn.Observation(spectrum, bandpass, binset=spectrum.waveset) countrates = np.append(countrates, obs.countrate(ins.AREA)) self.log( 'info', 'Manually created star {} of {}'.format( len(countrates), len(temps))) return countrates
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
counts = False if np.sum(spec(spec.waveset)) > 0.: counts = True except stsyn.exceptions.ParameterOutOfBounds: counts = False for l, mag in enumerate(coords[3]): msg = "\t\t\t{:6d} of {}: {}: Starting Z = {}, log(g) = {}, Teff = {:7.1f}, Mabs = {:>4}" print(msg.format(n+1, total, time.ctime(), Z, logg, teff, mag), end='') if counts: norm_value = mag*u.ABmag spec_norm = spec.normalize(norm_value, norm_bandpass) for instrument in instruments: for mode in modes[instrument.lower()]: for filter in filters[instrument.lower()][mode]: if counts: obs = syn.Observation(spec_norm, bandpasses[instrument.lower()][filter], binset=spec_norm.waveset) result_arrays[instrument.lower()][filter][i,j,k,l] = obs.countrate(area[instrument.lower()]).value print(".", end='') else: result_arrays[instrument.lower()][filter][i,j,k,l] = 0. print("x", end='') print("") n += 1 print("{}: Saving files...".format(time.ctime()), end='') with open(os.path.join(os.getcwd(), "grid", "VERSION.txt"), "wt") as outf: outf.write("Pandeia: {}\n".format(pandeia_version_info)) outf.write("STIPS: {}\n".format(stips_version_info)) np.save(os.path.join(os.getcwd(), 'grid', 'input.npy'), coords) for instrument in instruments: