def bandflux(self, band): """Perform synthentic photometry in a given bandpass. The bandpass transmission is interpolated onto the wavelength grid of the spectrum. The result is a weighted sum of the spectral flux density values (weighted by transmission values). Parameters ---------- band : Bandpass object or name of registered bandpass. Returns ------- bandflux : float Total flux in ph/s/cm^2. If part of bandpass falls outside the spectrum, `None` is returned instead. bandfluxerr : float Error on flux. Only returned if the `error` attribute is not `None`. """ band = get_bandpass(band) bwave, btrans = band.to_unit(self._wunit) if (bwave[0] < self._wave[0] or bwave[-1] > self._wave[-1]): return None idx = ((self._wave > bwave[0]) & (self._wave < bwave[-1])) d = self._wave[idx] f = self._flux[idx] #TODO: use spectral density equivalencies once they can do photons. # first convert to ergs / s /cm^2 / (wavelength unit) target_unit = u.erg / u.s / u.cm**2 / self._wunit if self._unit != target_unit: f = self._unit.to(target_unit, f, u.spectral_density(self._wunit, d)) # Then convert ergs to photons: photons = Energy / (h * nu) f = f / const.h.cgs.value / self._wunit.to(u.Hz, d, u.spectral()) trans = np.interp(d, bwave, btrans) binw = np.gradient(d) ftot = np.sum(f * trans * binw) if self._error is None: return ftot else: e = self._error[idx] # Do the same conversion as above if self._unit != target_unit: e = self._unit.to(target_unit, e, u.spectral_density(self._wunit, d)) e = e / const.h.cgs.value / self._wunit.to(u.Hz, d, u.spectral()) etot = np.sqrt(np.sum((e * binw) ** 2 * trans)) return ftot, etot
def test_spectraldensity3(): # Define F_nu in Jy f_nu = u.Jy # Define F_lambda in ergs / cm^2 / s / micron f_lambda = u.erg / u.cm ** 2 / u.s / u.micron # 1 GHz one_ghz = u.Quantity(1, u.GHz) # Convert to ergs / cm^2 / s / Hz assert_allclose(f_nu.to(u.erg / u.cm ** 2 / u.s / u.Hz, 1.), 1.e-23, 10) # Convert to ergs / cm^2 / s at 10 Ghz assert_allclose(f_nu.to(u.erg / u.cm ** 2 / u.s, 1., equivalencies=u.spectral_density(one_ghz * 10)), 1.e-13, 10) # Convert to F_lambda at 1 Ghz assert_allclose(f_nu.to(f_lambda, 1., equivalencies=u.spectral_density(one_ghz)), 3.335640951981521e-20, 10) # Convert to Jy at 1 Ghz assert_allclose(f_lambda.to(u.Jy, 1., equivalencies=u.spectral_density(one_ghz)), 1. / 3.335640951981521e-20, 10) # Convert to ergs / cm^2 / s at 10 microns assert_allclose(f_lambda.to(u.erg / u.cm ** 2 / u.s, 1., equivalencies=u.spectral_density(u.Quantity(10, u.micron))), 10., 10)
def set_units(self, disp_unit, data_unit): if self.dispersion_unit.is_equivalent(disp_unit, equivalencies=spectral()): self._dispersion = self.dispersion.data.to( disp_unit, equivalencies=spectral()).value # Finally, change the unit self.dispersion_unit = disp_unit else: logging.warning("Units are not compatible.") if self.unit.is_equivalent(data_unit, equivalencies=spectral_density( self.dispersion.data)): self._data = self.data.data.to( data_unit, equivalencies=spectral_density( self.dispersion.data)).value self._uncertainty = self._uncertainty.__class__( self.raw_uncertainty.data.to( data_unit, equivalencies=spectral_density( self.dispersion.data)).value) # Finally, change the unit self._unit = data_unit else: logging.warning("Units are not compatible.")
def test_conversion_to_and_from_physical_quantities(): """Ensures we can convert from regular quantities.""" mst = [10., 12., 14.] * u.STmag flux_lambda = mst.physical mst_roundtrip = flux_lambda.to(u.STmag) # check we return a logquantity; see #5178. assert isinstance(mst_roundtrip, u.Magnitude) assert mst_roundtrip.unit == mst.unit assert_allclose(mst_roundtrip.value, mst.value) wave = [4956.8, 4959.55, 4962.3] * u.AA flux_nu = mst.to(u.Jy, equivalencies=u.spectral_density(wave)) mst_roundtrip2 = flux_nu.to(u.STmag, u.spectral_density(wave)) assert isinstance(mst_roundtrip2, u.Magnitude) assert mst_roundtrip2.unit == mst.unit assert_allclose(mst_roundtrip2.value, mst.value)
def _bbChi(stuff, wave, flux): temp, const = stuff bb = BlackBody1D(temperature=temp * u.K) bbFlux = [x.value for x in bb(wave).to(FLAM, u.spectral_density(wave))] bbFlux = [x * const for x in bbFlux] return _chisq(bbFlux, flux)
def main(): # commandline parser parser = initialize_parser() args = parser.parse_args() # read an observed spectrum # read in the observed spectrum # assumed to be astropy table compatibile and include units specfile = args.spectrumfile outputname = specfile.split(".")[0] if not os.path.isfile(specfile): pack_path = pkg_resources.resource_filename("pahfit", "data/") test_specfile = "{}/{}".format(pack_path, specfile) if os.path.isfile(test_specfile): specfile = test_specfile else: raise ValueError( "Input spectrumfile {} not found".format(specfile)) # get the table format (from extension of filename) tformat = specfile.split(".")[-1] if tformat == "ecsv": tformat = "ascii.ecsv" obs_spectrum = Table.read(specfile, format=tformat) obs_x = obs_spectrum["wavelength"].to(u.micron, equivalencies=u.spectral()) obs_y = obs_spectrum["flux"].to(u.Jy, equivalencies=u.spectral_density(obs_x)) # strip units as the observed spectrum is in the internal units obs_x = obs_x.value obs_y = obs_y.value # read in the PAHFIT results pmodel = PAHFITBase(obs_x, obs_y, filename=args.fitfilename) # plot result fontsize = 18 font = {"size": fontsize} mpl.rc("font", **font) mpl.rc("lines", linewidth=2) mpl.rc("axes", linewidth=2) mpl.rc("xtick.major", width=2) mpl.rc("ytick.major", width=2) fig, ax = plt.subplots(figsize=(15, 10)) pmodel.plot(ax, obs_x, obs_y, pmodel.model) ax.set_yscale("linear") ax.set_xscale("log") # use the whitespace better fig.tight_layout() # show or save if args.savefig: fig.savefig("{}.{}".format(outputname, args.savefig)) else: plt.show()
def bbfunc(wavelengths, temperature): """Planck law for blackbody radiation in PHOTLAM per steradian. Parameters ---------- wavelengths : array_like or `astropy.units.quantity.Quantity` Wavelength values. If not a Quantity, assumed to be in Angstrom. temperature : float or `astropy.units.quantity.Quantity` Blackbody temperature. If not a Quantity, assumed to be in Kelvin. Returns ------- fluxes : `astropy.units.quantity.Quantity` Blackbody radiation in PHOTLAM per steradian. """ if not isinstance(wavelengths, u.Quantity): wavelengths = u.Quantity(wavelengths, unit=u.AA) # Calculations must use Hz freq = wavelengths.to(u.Hz, equivalencies=u.spectral()) # Calculations must use Kelvin temperature = units.validate_quantity(temperature, u.K) # Calculate blackbody radiation in FNU, then convert to PHOTLAM factor = np.expm1(const.h * freq / (const.k_B * temperature)) bb_nu = 2 * const.h * freq * freq * freq / (const.c ** 2 * factor) bb_lam = units.FNU.to(units.PHOTLAM, bb_nu.cgs.value, equivalencies=u.spectral_density(wavelengths)) return u.Quantity(bb_lam, unit=units.PHOTLAM/u.sr)
def calculate_f_xuv(spectrum): """ Calculates the total XUV flux given the spectrum at the planet (Equation 14 of Vissapragada et al. 2022, where the minimum threshold is 13.6 eV. This function currently assumes the spectrum is truncated at 13.6 eV and does not include lower energies. Parameters ---------- spectrum (``dict``): Spectrum of the host star arriving at the planet covering fluxes up to the wavelength corresponding to the energy to ionize hydrogen (13.6 eV, or 911.65 Angstrom). Can be generated using ``tools.make_spectrum_dict`` or ``tools.generate_muscles_spectrum``. Currently we assume that the spectrum does not include lower energies than 13.6 eV. Returns ------- f_xuv (``astropy.Quantity``): The integrated XUV flux. """ wav_grid = spectrum['wavelength'] * spectrum['wavelength_unit'] flux_grid = spectrum['flux_lambda'] * spectrum['flux_unit'] flux_grid = flux_grid.to(u.erg / u.s / u.cm / u.cm / u.Hz, equivalencies=u.spectral_density(wav_grid)) wavs_hz = wav_grid.to(u.Hz, equivalencies=u.spectral())[::-1] flux_grid = flux_grid[::-1] f_xuv = simpson(flux_grid, x=wavs_hz) * u.erg / u.s / u.cm**2 return f_xuv
def spec_av_cross(r_grid, spectrum, t_coef, species): """ Calculates the heating cross-section for photoionization using Equation (16) of Vissapragada et al. (2022). Parameters ---------- r_grid (``numpy.ndarray``): The radius grid for the calculation. An astropy unit (like u.Rjup) must be specified for each value on the grid. spectrum (``dict``): Spectrum of the host star arriving at the planet covering fluxes up to the wavelength corresponding to the energy to ionize hydrogen (13.6 eV, or 911.65 Angstrom). Can be generated using ``tools.make_spectrum_dict`` or ``tools.generate_muscles_spectrum``. Currently we assume that the spectrum does not include lower energies than 13.6 eV. t_coef (``numpy.ndarray``): The transmission coefficient profile for the wind as a function of frequency and altitude. In the optically-thin part of the outflow this should be very close to 1. species (``str``): The photoionzation target for which we are calculating the heating cross-section. Must be one of 'hydrogen', 'helium', or 'helium+'. Returns ------- cross (``astropy.Quantity``): Heating cross-section in cm**2 for the selected species. """ wav_grid = spectrum['wavelength'] * spectrum['wavelength_unit'] flux_grid = spectrum['flux_lambda'] * spectrum['flux_unit'] flux_grid = flux_grid.to(u.erg / u.s / u.cm / u.cm / u.Hz, equivalencies=u.spectral_density(wav_grid)) wavs_hz = wav_grid.to(u.Hz, equivalencies=u.spectral())[::-1] flux_grid = flux_grid[::-1] xx, yy = np.meshgrid(wavs_hz, r_grid) threshold = threshes[species] crosses = { 'hydrogen': h_photo_cross, 'helium': helium_photo_cross, 'helium+': heplus_photo_cross } cross = crosses[species] evgrid = xx.to(u.eV, equivalencies=u.spectral()) eta_grid = 1 - threshold / evgrid spec_grid, __ = np.meshgrid(flux_grid, r_grid) crossgrid = cross(xx) crossgrid[xx.to(u.eV, equivalencies = u.spectral()) < \ threshold] = 0.*u.cm**2 numgrid = eta_grid * spec_grid * crossgrid * t_coef numgrid = numgrid.to(u.erg / u.s / u.Hz) num = simpson(numgrid, x=wavs_hz, axis=-1) * u.erg / u.s F_XUV = calculate_f_xuv(spectrum) cross = num / F_XUV return cross.to(u.cm**2)
def flux2mag(flux, band): """ Converts flux in erg / s / cm^2 / Angstrom to AB magnitudes. flux : float Flux (erg / s / cm^2 / Angstrom). band : `igmtools.photometry.Passband` The passband. Returns ------- magnitude : float AB magnitude. """ if hasattr(flux, 'unit'): flux = flux.to(erg / s / cm**2 / angstrom) else: flux = flux * erg / s / cm**2 / angstrom fnu = flux.to(erg / s / cm**2 / Hz, equivalencies=spectral_density(band.effective_wavelength)) magnitude = -2.5 * log10(fnu.value) - 48.6 return magnitude
def mag2flux(magnitude, band): """ Converts a given AB magnitude into flux in the given band in erg / s / cm^2 / Angstrom. Parameters ---------- magnitude : float AB magnitude. band : `igmtools.photometry.Passband` The passband. Returns ------- flux : `astropy.units.Quantity` Flux in erg / s / cm^2 / Angstrom. """ fnu = 10**(-(magnitude + 48.6) / 2.5) * erg / s / cm**2 / Hz flux = fnu.to(erg / s / cm**2 / angstrom, equivalencies=spectral_density(band.effective_wavelength)) return flux
def _show_arithmetic_dialog(self): if self.viewer.current_layer is None: return if self.viewer._layer_arithmetic_dialog.exec_(): formula = self.viewer._layer_arithmetic_dialog\ .ui_layer_arithmetic_dialog.formulaLineEdit.text() current_window = self.viewer.current_sub_window current_layers = window_manager.get_layers(current_window) new_layer = layer_manager.add_from_formula(formula, layers=current_layers) if new_layer is None: logging.warning("Formula not valid.") return # If units match, plot the resultant on the same sub window, # otherwise create a new sub window to plot the spectra data_units_equiv = new_layer.data.unit.is_equivalent( current_window._plot_units[1], equivalencies=spectral_density(new_layer.dispersion)) disp_units_equiv = new_layer.dispersion.unit.is_equivalent( current_window._plot_units[0], equivalencies=spectral()) if data_units_equiv and disp_units_equiv: self.add_sub_window(layer=new_layer, window=current_window) else: logging.info("{} not equivalent to {}.".format( new_layer.data.unit, current_window._plot_units[1])) self.add_sub_window(layer=new_layer)
def get_thermal_emission(self, wvs, band="TwoMass-J"): ''' The telescope emission as a function of wavelength Outputs: thermal_emission - usnits of photons/s/cm**2/angstrom ''' diffraction_limit = (wvs / self.diameter.to(u.micron) * u.radian).to( u.arcsec) solidangle = diffraction_limit**2 * 1.13 # TODO: blackbody_lambda is deprecated, change to BlackBody #bb_lam = BlackBody(self.temperature,scale=1.0*u.erg/(u.cm**2*u.AA*u.s*u.sr)) #inst_therm = bb_lam(wvs) thermal_emission = blackbody_lambda(wvs, self.temperature) thermal_emission *= solidangle thermal_emission = thermal_emission.to( u.ph / (u.s * u.cm**2 * u.AA), equivalencies=u.spectral_density(wvs)) thermal_emission *= self.get_telescope_emissivity(wvs, band=band) return thermal_emission
def withFluxDensity(self, target_flux_density, wavelength): """ Return a new SED with flux density set to `target_flux_density` at wavelength `wavelength`. See ChromaticObject docstring for information about how SED normalization affects ChromaticObject normalization. @param target_flux_density The target normalization in photons/nm/cm^2/s. @param wavelength The wavelength, in nm, at which the flux density will be set. @returns the new normalized SED. """ from astropy import units _photons = units.astrophys.photon/(units.s * units.cm**2 * units.nm) if self.dimensionless: raise TypeError("Cannot set flux density of dimensionless SED.") if isinstance(wavelength, units.Quantity): wavelength_nm = wavelength.to(units.nm, units.spectral()) current_flux_density = self(wavelength_nm.value) else: wavelength_nm = wavelength * units.nm current_flux_density = self(wavelength) if isinstance(target_flux_density, units.Quantity): target_flux_density = target_flux_density.to( _photons, units.spectral_density(wavelength_nm)).value factor = target_flux_density / current_flux_density return self * factor
def plot_3cr(t): """ Reproduction of Stockton & Ridgway 1996 """ jy = t["S_178_"] # already in units of Janksy b/c Vizier astroquery # Convert Janksy to SI equivalent power = jy.to(u.W / u.m**2 / u.Hz, equivalencies=u.spectral_density(178 * u.MHz)) # Stockton & Ridgway assume H0 = 50 km/s/Mpc, we take 70 distance = [CosmologyCalculator(z, 70).DL_Mpc for z in t["z"]] t["P"] = power # already has units t["d"] = distance * u.Mpc # Sketchy factor pi. 4*pi would be understandable, but now plot matches # when H_0 = 50 p178 = numpy.pi*t["d"].to(u.m)**2 * t["P"] pyplot.figure(figsize=(12,12)) pyplot.scatter(t["z"], p178, marker="+", s=50, c="k") for i in ["405.0", "348.0", "123.0", "20.0", "427.1", "295.0", "265.0", "237.0", "268.1", "280.0"]: index = numpy.where(t["_3CR"] == i)[0][0] pyplot.text(t[index]["z"], p178[index].value, i[:-2] if i[-1]=="0" else i, fontsize=22) pyplot.xlim(-0.05, 1.1) pyplot.ylim(-1.5e27, 3.2e28) pyplot.xlabel(r"$z$") pyplot.ylabel(r"$P_{178}$ (W Hz$^{-1}$)") pyplot.savefig("out/3CR.pdf", dpi=300)
def _find_spectral_column(table, columns_to_search, spectral_axis): """ Figure out which column in a table holds the fluxes or uncertainties. Take the first column that has units compatible with u.spectral_density() equivalencies. If none meet that criterion, look for other likely length units such as 'adu' or 'cts/s'. """ additional_valid_units = [u.Unit('adu'), u.Unit('ct/s')] found_column = None # First, search for a column with units compatible with Janskies for c in columns_to_search: try: # Check for multi-D flux columns if table[c].ndim == 1: spec_ax = spectral_axis else: # Assume leading dimension corresponds to spectral_axis spec_shape = np.ones(table[c].ndim, dtype=np.int) spec_shape[0] = -1 spec_ax = spectral_axis.reshape(spec_shape) table[c].to("Jy", equivalencies=u.spectral_density(spec_ax)) found_column = c break except: continue # If no success there, check for other possible flux units if found_column is None: for c in columns_to_search: if table[c].unit in additional_valid_units: found_column = c break return found_column
def _calculate_spectrum(self, solar_path): """ Pre-calculates absolute surface brightness spectral flux density of the Zodiacal Light at the ecliptic poles. """ # Load absolute solar spectrum from Collina, Bohlin & Castelli (1996) sun = fits.open(solar_path) sun_waves = sun[1].data['WAVELENGTH'] * u.Angstrom # sfd = spectral flux density sun_sfd = sun[1].data[ 'FLUX'] * u.erg * u.second**-1 * u.cm**-2 * u.Angstrom**-1 self.waves = sun_waves.to(u.micron) # Covert to zodiacal light spectrym by following the normalisation and reddening # prescription of Leinert et al (1997) with the revised parameters from # Aldering (2001), as used in the HST ETC (Giavalsico, Sahi, Bohlin (2202)). # Reddening factor rfactor = np.where(sun_waves < ZodiacalLight.lambda_c, \ 1.0 + ZodiacalLight.f_blue * np.log(sun_waves/ZodiacalLight.lambda_c), \ 1.0 + ZodiacalLight.f_red * np.log(sun_waves/ZodiacalLight.lambda_c)) # Apply normalisation and reddening sfd = sun_sfd * ZodiacalLight.zl_normalisation * rfactor # #DownWithErgs self.sfd = sfd.to(u.Watt * u.m**-2 * u.arcsecond**-2 * u.micron**-1) # Also calculate in photon spectral flux density units. Fudge needed because equivalencies # don't currently include surface brightness units. fudge = sfd * u.arcsecond**2 fudge = fudge.to(u.photon * u.second**-1 * u.m**-2 * u.micron**-1, equivalencies=u.spectral_density(self.waves)) self.photon_sfd = fudge / u.arcsecond**2
def e490(smooth=False, unit=u.Unit('W/(m2 um)')): """The ASTM (2000) E490-00 solar spectrum (at 1 AU). Parameters ---------- smooth : bool Return a lower-resolution (histogrammed) spectrum (see Notes). unit : astropy Unit Return flux in these units (must be spectral flux density). Returns ------- w : Quantity Spectrum wavelength. f : Quantity Spectrum flux density. Notes ----- The smoothed spectrum, up to 10 um, is the original E490 table, rebinned. At > 10 um, the original resolution is retained. """ if smooth: w, f = np.loadtxt(_e490_sm).T else: w, f = np.loadtxt(_e490).T w = w * u.um f = f * u.W / u.m**2 / u.um if f.unit != unit: equiv = u.spectral_density(w.unit, w.value) f = f.to(unit, equivalencies=equiv) return w, f
def evaluate(x, temperature): """Evaluate the model. Parameters ---------- x : number or ndarray Wavelengths in Angstrom. temperature : number Temperature in Kelvin. Returns ------- y : number or ndarray Blackbody radiation in PHOTLAM per steradian. """ # Silence Numpy old_np_err_cfg = np.seterr(all='ignore') wave = u.Quantity(np.ascontiguousarray(x), unit=u.AA) bbnu_flux = blackbody_nu(wave, temperature) bbflux = (bbnu_flux * u.sr).to( units.PHOTLAM, u.spectral_density(wave)) / u.sr # PHOTLAM/sr # Restore Numpy settings dummy = np.seterr(**old_np_err_cfg) return bbflux.value
def spectral_density_vega(wav, vegaflux): """Flux equivalencies between PHOTLAM and VEGAMAG. Parameters ---------- wav : `~astropy.units.quantity.Quantity` Quantity associated with values being converted (e.g., wavelength or frequency). vegaflux : `~astropy.units.quantity.Quantity` Flux of Vega at ``wav``. Returns ------- eqv : list List of equivalencies. """ vega_photlam = vegaflux.to(PHOTLAM, equivalencies=u.spectral_density(wav)).value def converter(x): """Set nan/inf to -99 mag.""" val = -2.5 * np.log10(x / vega_photlam) result = np.zeros(val.shape, dtype=np.float64) - 99 mask = np.isfinite(val) result[mask] = val[mask] return result def iconverter(x): return vega_photlam * 10**(-0.4 * x) return [(PHOTLAM, VEGAMAG, converter, iconverter)]
def convert_value(self, value, wave=None, new_unit=None): if isinstance(value, Quantity): value = value.value elif not isinstance(value, int) \ and not isinstance(value, float) \ and not isinstance(value, np.ndarray): raise ValueError("Expected float or int, got {} instead.".format(type(value))) if self.type in [NONE_CubeVizUnit, UNKNOWN_CubeVizUnit]: return value new_value = value if new_unit is None: new_unit = self._unit if hasattr(u.spectral_density, "pixel_area"): u.spectral_density.pixel_area = self.controller.pixel_area if wave is not None: new_value = (value * self._original_unit).to(new_unit, equivalencies=u.spectral_density(wave)).value else: new_value = (value * self._original_unit).to(new_unit).value if isinstance(new_value, Quantity): new_value = new_value.value return new_value
def __init__(self, wave, flux, wave_unit=u.AA, unit=(u.erg / u.s / u.cm**2 / u.AA)): self.wave = np.asarray(wave, dtype=np.float64) self.flux = np.asarray(flux, dtype=np.float64) if self.wave.shape != self.flux.shape: raise ValueError('shape of wavelength and flux must match') if self.wave.ndim != 1: raise ValueError('only 1-d arrays supported') # internally, wavelength is in Angstroms: if wave_unit != u.AA: self.wave = wave_unit.to(u.AA, self.wave, u.spectral()) self._wave_unit = u.AA # internally, flux is in F_lambda: if unit != FLAMBDA_UNIT: self.flux = unit.to(FLAMBDA_UNIT, self.flux, u.spectral_density(u.AA, self.wave)) self._unit = FLAMBDA_UNIT # Set up interpolation. # This appears to be the fastest-evaluating interpolant in # scipy.interpolate. self._tck = splrep(self.wave, self.flux, k=1)
def __init__(self, *args, **kwargs): fwhm = kwargs.pop('fwhm', None) total_flux = kwargs.pop('total_flux', None) super(GaussianFlux1D, self).__init__(*args, **kwargs) if fwhm is None: fwhm = self.stddev * gaussian_sigma_to_fwhm else: self.stddev = fwhm * gaussian_fwhm_to_sigma gaussian_amp_to_totflux = np.sqrt(2.0 * np.pi) * self.stddev if total_flux is None: u_str = 'PHOTLAM' total_flux = self.amplitude * gaussian_amp_to_totflux else: u_str = 'FLAM' # total_flux is passed in unaltered, any conversion error would # happen here. tf_unit = u.erg / (u.cm * u.cm * u.s) if isinstance(total_flux, u.Quantity): total_flux = total_flux.to(tf_unit) else: total_flux = total_flux * tf_unit self.amplitude = (total_flux / (gaussian_amp_to_totflux * u.AA)).to( units.PHOTLAM, u.spectral_density(self.mean.value * u.AA)).value # noqa total_flux = total_flux.value self.meta['expr'] = 'em({0:g}, {1:g}, {2:g}, {3})'.format( self.mean.value, fwhm, total_flux, u_str)
def flux(self): """ Converts data_item.flux - which consists of the flux axis with units - into the new flux unit """ return self.data_item.flux.to(self.data_unit, equivalencies=spectral_density( self.data_item.spectral_axis)).value
def blackbody_lambda(in_x, temperature): """Like :func:`blackbody_nu` but for :math:`B_{\\lambda}(T)`. Parameters ---------- in_x : number, array-like, or `~astropy.units.Quantity` Frequency, wavelength, or wave number. If not a Quantity, it is assumed to be in Angstrom. temperature : number or `~astropy.units.Quantity` Blackbody temperature. If not a Quantity, it is assumed to be in Kelvin. Returns ------- flux : `~astropy.units.Quantity` Blackbody monochromatic flux in :math:`erg \\; cm^{-2} s^{-1} \\AA^{-1} sr^{-1}`. """ if getattr(in_x, 'unit', None) is None: in_x = u.Quantity(in_x, u.AA) bb_nu = blackbody_nu(in_x, temperature) * u.sr # Remove sr for conversion flux = bb_nu.to(FLAM, u.spectral_density(in_x)) return flux / u.sr # Add per steradian to output flux unit
def convert(self, wave, flux, exception=False): ''' Convert arrays to target units. Parameters ---------- wave: Quantity spectral coordinate array to be converted to target units flux: Quantity flux array to be converted to target units exception: boolean, optional, default=False if False, a units conversion exception will result in an error message being printed at the console. If True, the error message is printed AND the exception is raised again. This behavior helps in debugging scripts. Returns ------- two Quantity instances with the converted wave and flux arrays. In case of error, the original input references are returned. ''' try: converted_wave = wave.to(self._wunit, equivalencies=u.spectral()) converted_flux = flux.to(self._funit, equivalencies=u.spectral_density(wave)) return converted_wave, converted_flux except (ValueError, astropy.units.core.UnitsError) as e: print("UNITS CONVERSION ERROR: ", e, file=sys.stderr) if exception: raise e return wave, flux
def spectral_density_vega(wav, vegaflux): """Flux equivalencies between PHOTLAM and VEGAMAG. Parameters ---------- wav : `~astropy.units.quantity.Quantity` Quantity associated with values being converted (e.g., wavelength or frequency). vegaflux : `~astropy.units.quantity.Quantity` Flux of Vega at ``wav``. Returns ------- eqv : list List of equivalencies. """ vega_photlam = vegaflux.to( PHOTLAM, equivalencies=u.spectral_density(wav)).value def converter(x): """Set nan/inf to -99 mag.""" val = -2.5 * np.log10(x / vega_photlam) result = np.zeros(val.shape, dtype=np.float64) - 99 mask = np.isfinite(val) result[mask] = val[mask] return result def iconverter(x): return vega_photlam * 10**(-0.4 * x) return [(PHOTLAM, VEGAMAG, converter, iconverter)]
def convert_value(self, value, wave=None, new_unit=None): if isinstance(value, Quantity): value = value.value elif not isinstance(value, int) \ and not isinstance(value, float) \ and not isinstance(value, np.ndarray): raise ValueError("Expected float or int, got {} instead.".format( type(value))) if self.type in [NONE_CubeVizUnit, UNKNOWN_CubeVizUnit]: return value new_value = value if new_unit is None: new_unit = self._unit if hasattr(u.spectral_density, "pixel_area"): u.spectral_density.pixel_area = self.controller.pixel_area if wave is not None: new_value = (value * self._original_unit).to( new_unit, equivalencies=u.spectral_density(wave)).value else: new_value = (value * self._original_unit).to(new_unit).value if isinstance(new_value, Quantity): new_value = new_value.value return new_value
def calibrate_flux(self, hdu): ''' do flux calibration by undoing what the simulator did so far (as much as possible) input is the result of compute_snr() ''' data = hdu.data * u.electron # per dit, pixel, spectral channel, and M1-area mirr_list = self.cmds.mirrors_telescope mirr_area = np.pi / 4 * np.sum(mirr_list["Outer"]**2 - \ mirr_list["Inner"]**2) * u.m**2 data = data / (hdu.header['EXPTIME'] * u.s * (np.mean(self.wavelen)*u.um * (1.5 *u.km/u.s) / const.c).to(u.um) * mirr_area) # e-/s/um/m2 # wavelengths of data cube det_wavelen = (self.det_velocities * u.m/u.s).to(u.um, equivalencies=u.doppler_optical(self.restcoo)) # interpolate transmission onto wavelength-grid of detector: trans = np.interp(det_wavelen.value, self.wavelen, self.transmission) data /= trans[:, np.newaxis, np.newaxis] data = (data * u.photon/u.electron).to(u.Jy, equivalencies=u.spectral_density(self.restcoo)) data = data / (self.det_pixscale/1000. * u.arcsec)**2 # Jy/arcsec2 calhdu = fits.PrimaryHDU(data.value, header=hdu.header) calhdu.header['BUNIT'] = ('Jy/arcsec2', 'Jansky per arcsec**2') return calhdu
def withFluxDensity(self, target_flux_density, wavelength): """ Return a new SED with flux density set to `target_flux_density` at wavelength `wavelength`. See ChromaticObject docstring for information about how SED normalization affects ChromaticObject normalization. @param target_flux_density The target normalization in photons/nm/cm^2/s. @param wavelength The wavelength, in nm, at which the flux density will be set. @returns the new normalized SED. """ from astropy import units _photons = units.astrophys.photon / (units.s * units.cm**2 * units.nm) if self.dimensionless: raise TypeError("Cannot set flux density of dimensionless SED.") if isinstance(wavelength, units.Quantity): wavelength_nm = wavelength.to(units.nm, units.spectral()) current_flux_density = self(wavelength_nm.value) else: wavelength_nm = wavelength * units.nm current_flux_density = self(wavelength) if isinstance(target_flux_density, units.Quantity): target_flux_density = target_flux_density.to( _photons, units.spectral_density(wavelength_nm)).value factor = target_flux_density / current_flux_density return self * factor
def flux2mag(flux, band): """ Converts flux in erg / s / cm^2 / Angstrom to AB magnitudes. flux : float Flux (erg / s / cm^2 / Angstrom). band : `igmtools.photometry.Passband` The passband. Returns ------- magnitude : float AB magnitude. """ if hasattr(flux, "unit"): flux = flux.to(erg / s / cm ** 2 / angstrom) else: flux = flux * erg / s / cm ** 2 / angstrom fnu = flux.to(erg / s / cm ** 2 / Hz, equivalencies=spectral_density(band.effective_wavelength)) magnitude = -2.5 * log10(fnu.value) - 48.6 return magnitude
def flux_to_dust(flux, z, temp, distance, wavelength_observed): #convert lamda obs into lamda emmit as well as convert micron to m wavelength = (wavelength_observed * 1E-6) / (z + 1.0) d = distance*3.08567758E22 #converts Mpc to m #find conversion factor for jy to si lm3 = u.W*u.m**-2*u.m**-1 jy_to_si = u.Jy.to(lm3, equivalencies=u.spectral_density(u.m,wavelength)) #convert flux in Jy to Si Units s = flux*jy_to_si #find kappa note beta is fixed at 2 k_l = kappa(wavelength) #find value of plack equation b_l = planck(wavelength,temp) #caculate dust mass in kg mass = (s * d**2) / (k_l * b_l) #convert to log10(Dust/Msol) m = np.log10(mass/2E30) return m
def blackbody_lambda(wavelength, temperature): """ Calculate the blackbody spectral density per unit wavelength. Parameters ---------- wavelength : `~astropy.units.Quantity` Wavelength array to evaluate on. temperature : `~astropy.units.Quantity` Blackbody temperature. """ # Convert to units for calculations, also force double precision with u.add_enabled_equivalencies(u.spectral() + u.temperature()): freq = u.Quantity(wavelength, u.Hz, dtype=np.float64) temp = u.Quantity(temperature, u.K, dtype=np.float64) log_boltz = const.h * freq / (const.k_B * temp) boltzm1 = np.expm1(log_boltz) bb_nu = (2.0 * const.h * freq ** 3 / (const.c ** 2 * boltzm1)) flam = u.erg / (u.cm**2 * u.s * u.AA) flux = bb_nu.to(flam, u.spectral_density(wavelength)) return flux / u.sr # Add per steradian to output flux unit
def convert_from_original_unit(self, value, wave=None, **kwargs): """ Given a value from the data, convert it to current units. :param value: float :param wave: float: wavelength :param kwargs: :return: converted value """ if self.unit is None and wave is None: return value new_value = value new_value *= 10**(self._original_power - self.power) new_value *= self._original_spectral_flux_density.to(self.spectral_flux_density, equivalencies=u.spectral_density(wave)) if self.has_area: pixel_area = self.controller.pixel_area if self.area.decompose() == u.pix.decompose() \ and 'solid angle' in self._original_area.physical_type: area = (self._original_area / pixel_area).decompose() new_value /= area.to(self.area) elif 'solid angle' in self.area.physical_type \ and self._original_area.decompose() == u.pix.decompose(): area = (self._original_area * pixel_area).decompose() new_value /= area.to(self.area) else: new_value /= self._original_area.to(self.area) if isinstance(new_value, u.Quantity): new_value = new_value.value return new_value
def evaluate(x, temperature): """Evaluate the model. Parameters ---------- x : number or ndarray Wavelengths in Angstrom. temperature : number Temperature in Kelvin. Returns ------- y : number or ndarray Blackbody radiation in PHOTLAM per steradian. """ # Silence Numpy old_np_err_cfg = np.seterr(all="ignore") wave = np.ascontiguousarray(x) * u.AA bbnu_flux = blackbody_nu(wave, temperature) bbflux = (bbnu_flux * u.sr).to(units.PHOTLAM, u.spectral_density(wave)) / u.sr # PHOTLAM/sr # Restore Numpy settings np.seterr(**old_np_err_cfg) return bbflux.value
def convert_specific_intensity(wavelength: np.ndarray, specInt: np.ndarray, outUnits) -> units.quantity.Quantity: ''' Convert a specific intensity between different units. Parameters ---------- wavelength : np.ndarray or astropy.Quantity If no units are provided then this is assumed to be in nm. specInt : np.ndarray or astropy.Quantity If no units are provided then this is assumed to be in J/s/m2/sr/Hz, the default for Lightweaver. outUnits : str or astropy.Unit The units to convert specInt to e.g. 'erg/s/cm2/sr/A' Returns ------- result : astropy.Quantity specInt converted to the desired units. ''' if not isinstance(wavelength, units.Quantity): wavelength = wavelength << units.nm if not isinstance(specInt, units.Quantity): specInt = specInt << units.J / units.s / units.m**2 / units.sr / units.Hz return specInt.to(outUnits, equivalencies=units.spectral_density(wavelength))
def evaluate(x, temperature): """Evaluate the model. Parameters ---------- x : number or ndarray Wavelengths in Angstrom. temperature : number Temperature in Kelvin. Returns ------- y : number or ndarray Blackbody radiation in PHOTLAM per steradian. """ if ASTROPY_LT_2_0: from astropy.analytic_functions.blackbody import blackbody_nu else: from astropy.modeling.blackbody import blackbody_nu # Silence Numpy old_np_err_cfg = np.seterr(all='ignore') wave = np.ascontiguousarray(x) * u.AA bbnu_flux = blackbody_nu(wave, temperature) bbflux = (bbnu_flux * u.sr).to( units.PHOTLAM, u.spectral_density(wave)) / u.sr # PHOTLAM/sr # Restore Numpy settings np.seterr(**old_np_err_cfg) return bbflux.value
def _find_spectral_column(table,columns_to_search,spectral_axis): """ Figure out which column in a table holds the fluxes or uncertainties. Take the first column that has units compatible with u.spectral_density() equivalencies. If none meet that criterion, look for other likely length units such as 'adu' or 'cts/s'. """ additional_valid_units = [u.Unit('adu'),u.Unit('ct/s')] found_column = None # First, search for a column with units compatible with Janskies for c in columns_to_search: try: table[c].to("Jy",equivalencies=u.spectral_density(spectral_axis)) found_column = c break except: continue # If no success there, check for other possible flux units if found_column is None: for c in columns_to_search: if table[c].unit in additional_valid_units: found_column = c break return found_column
def create_flux_equivalencies_list(self): """ Gets all possible conversions for flux from current flux units. """ # Get unit equivalencies. curr_flux_unit_equivalencies = u.Unit( self.spectrum.flux.unit).find_equivalent_units( equivalencies=u.spectral_density( np.sum(self.spectrum.spectral_axis)), include_prefix_units=False) # Get local units. local_units = [ u.Unit(unit) for unit in self._locally_defined_flux_units() ] # Remove overlap units. curr_flux_unit_equivalencies = list( set(curr_flux_unit_equivalencies) - set(local_units)) # Convert equivalencies into readable versions of the units and sort them alphabetically. flux_unit_equivalencies_titles = sorted( self.convert_units_to_strings(curr_flux_unit_equivalencies)) # Concatenate both lists with the local units coming first. flux_unit_equivalencies_titles = sorted(self.convert_units_to_strings(local_units)) + \ flux_unit_equivalencies_titles return flux_unit_equivalencies_titles
def interpolate(self, target_spectrum): """ interpolate(target_spectrum,clone=False) Interpolate spectrum to match target spectrum resolution. Does not modify current spectrum, but replaces it with a new one, which is a copy of the current spectrum but with the interpolated data on the x and y fields. The target spectrum has to be using the compatible units on the x and y axes as the current spectrum, or the interpolation will fail (including, e.g., units of wavenumbers/frequency/wavelength). Parameters ---------- target_spectrum : `BaseSpectrum` The target spectrum which the x axis resolution of the current spectrum should be made to match. clone : `bool`, optional If set to True, returns a modified copy of the spectrum instead of operating on the existing spectrum. """ if not self.x.unit.is_equivalent(target_spectrum.x.unit, equivalencies=u.spectral()): raise u.UnitsError('Spectra have incompatible units on x axis!') if not self.y.unit.is_equivalent(target_spectrum.y.unit, equivalencies=u.spectral_density( self.x)): raise u.UnitsError('Spectra have incompatible units on y axis!') newX = target_spectrum.x.to(self.x.unit, equivalencies=u.spectral()) newY = np.interp(newX, self.x, self.y) self.x = newX self.y = newY self.name = '{0}(interpolated: {1})'.format(self.name, target_spectrum.name)
def blackbody_lambda(in_x, temperature): """Like :func:`blackbody_nu` but for :math:`B_{\\lambda}(T)`. Parameters ---------- in_x : number, array-like, or `~astropy.units.Quantity` Frequency, wavelength, or wave number. If not a Quantity, it is assumed to be in Angstrom. temperature : number, array-like, or `~astropy.units.Quantity` Blackbody temperature. If not a Quantity, it is assumed to be in Kelvin. Returns ------- flux : `~astropy.units.Quantity` Blackbody monochromatic flux in :math:`erg \\; cm^{-2} s^{-1} \\mathring{A}^{-1} sr^{-1}`. """ if getattr(in_x, 'unit', None) is None: in_x = u.Quantity(in_x, u.AA) bb_nu = blackbody_nu(in_x, temperature) * u.sr # Remove sr for conversion flux = bb_nu.to(FLAM, u.spectral_density(in_x)) return flux / u.sr # Add per steradian to output flux unit
def check_spectral(self): """Return boolean indicating if SED has units compatible with a spectral density.""" from astropy import units _photons = units.astrophys.photon / (units.s * units.cm**2 * units.nm) return self.flux_type.is_equivalent( _photons, units.spectral_density(1 * units.nm))
def fluxd(self, geom, wave, unit=u.Unit('W / (m2 um)')): from numpy import pi from ..calib import solar_flux if not np.iterable(wave): wave = np.array([wave.value]) * wave.unit delta = geom['delta'] phase = geom['phase'] fsun = solar_flux(wave, unit=unit) / geom['rh'].to(u.au).value**2 refl = 1 + (wave - 0.55 * u.um).value * self.S / 10. if np.any(refl > self.refl_max): refl[refl > self.refl_max] = self.refl_max if np.any(refl < 0.0): refl[refl < 0.0] = 0.0 #fsca = fsun * Ap * phasef(phase) * pi * R**2 / pi / delta**2 fsca = (fsun * self.Ap * refl * self.phasef(np.abs(phase.to(u.deg).value)) * (self.R / delta).decompose()**2) if unit != fsca.unit: fsca = fsca.to(unit, equivalencies=u.spectral_density(u.um, wave)) return fsca
def test_spectraldensity6(): """ Test surface brightness conversions. """ slam = u.erg / (u.cm**2 * u.s * u.AA * u.sr) snu = u.erg / (u.cm**2 * u.s * u.Hz * u.sr) wave = u.Quantity([4956.8, 4959.55, 4962.3], u.AA) sb_flam = [3.9135e-14, 4.0209e-14, 3.9169e-14] sb_fnu = [3.20735792e-25, 3.29903646e-25, 3.21727226e-25] # S(nu) <--> S(lambda) assert_allclose(snu.to(slam, sb_fnu, u.spectral_density(wave)), sb_flam, rtol=1e-6) assert_allclose(slam.to(snu, sb_flam, u.spectral_density(wave)), sb_fnu, rtol=1e-6)
def cohen_standard(star, unit=u.Unit('W/(m2 um)')): """Cohen spectral templates. Parameters ---------- star : string The name of a star. This must match the filename of a template. For example, use HD6112 or alpha-lyr. The suffix .tem is appended. unit : astropy Unit Return these units, must be spectral flux density. Returns ------- wave : Quantity The wavelengths. flux : Quantity The fluxes. Notes ----- The path to the templates is defined in `calib._midirdir`. """ import os import re templatefile = "{0}/cohen/{1}.tem".format(_midirdir, star) if not os.path.exists(templatefile): raise ValueError("{0} not found.".format(templatefile)) # Cohen template format: # 1-11 E11.4 um Lambda Wavelength # 12-22 E11.4 W/cm2/um F_Lambda Monochromatic specific intensity # 23-33 E11.4 W/cm2/um e_F_Lambda *Total uncertainty in F_Lambda # 34-44 E11.4 % Local Local bias # 45-55 E11.4 % Global Global bias # many of the template files have a header tableheader = re.compile("Wavelength.*Irradiance.*Total") with open(templatefile, 'r') as inf: lines = inf.readlines() for i, line in enumerate(lines): if len(tableheader.findall(line)) > 0: break if i == (len(lines) - 1): # no header, just read it in skiprows = 0 else: skiprows = i + 2 wave, fd, efd = np.loadtxt(templatefile, skiprows=skiprows, unpack=True, usecols=(0, 1, 2)) wave = wave * u.um fd = (fd * u.Unit('W/(cm2 um)')).to(unit, u.spectral_density(wave)) return wave, fd
def test_spectraldensity2(): # flux density flambda = u.erg / u.angstrom / u.cm ** 2 / u.s fnu = u.erg / u.Hz / u.cm ** 2 / u.s a = flambda.to(fnu, 1, u.spectral_density(u.Quantity(3500, u.AA))) assert_allclose(a, 4.086160166177361e-12) # luminosity density llambda = u.erg / u.angstrom / u.s lnu = u.erg / u.Hz / u.s a = llambda.to(lnu, 1, u.spectral_density(u.Quantity(3500, u.AA))) assert_allclose(a, 4.086160166177361e-12) a = lnu.to(llambda, 1, u.spectral_density(u.Quantity(3500, u.AA))) assert_allclose(a, 2.44728537142857e11)
def __init__(self,x,y,xUnit,yUnit,params={}): ''' Inputs: x = wavelength/frequency values, 1d array-like y = spectral flux density, 1d array-like xUnit = astropy.units.Unit of physical type length or frequency yUnit = astropy.units.Unit of physical type luminosity/flux density /length or frequency ''' self.type = 'spectrum' # Validate x input wavelengths = np.asarray(x) spec = np.asarray(y) if wavelengths.ndim!=1: raise ValueError('Wavelength/Frequency must be given as a 1d array') if wavelengths.shape!=spec.shape: raise ValueError('Wavelength/Frequency array and flux density array have different shapes.') if u.get_physical_type(xUnit) not in ['frequency','length']: raise TypeError(xUnit,' is neither a unit of frequency nor a unit of length. Get it together.') wavelengths = wavelengths * xUnit wavelengths.to('micron',equivalencies=u.spectral()) if not np.all(np.ediff1d(wavelengths)>0.): if u.get_physical_type(xUnit) == 'frequency': raise ValueError('Frequencies must be monotonically decreasing.') else: raise ValueError('Wavelengths must be monotonically increasing.') self.wavelengths = wavelengths # Validate y input spec = np.asarray(y) if spec.ndim!=1: raise ValueError('Spectrum must be given as a 1d array') # Define desired flux units from base units uFnu = u.Unit('erg')/u.Unit('s')/u.Unit('Hz') uFnuN = u.Unit('erg')/u.Unit('s')/u.Unit('Hz')/u.Unit('cm')**2 # astropy equivalency only works when area uncluded uFlam = u.Unit('erg')/u.Unit('s')/u.Unit('Angstrom') uFlamN = u.Unit('erg')/u.Unit('s')/u.Unit('Angstrom')/u.Unit('cm')**2 # Need F_nu, but if there's no area, need to add because too lazy to add equivalency if yUnit.is_equivalent(uFnu): spec = spec * yUnit / (4*np.pi*((10 * u.Unit('parsec')).to('cm'))**2) #spec = spec * yUnit / u.Unit('m')**2 elif yUnit.is_equivalent(uFlam): spec = spec * yUnit/ (4*np.pi*((10 * u.Unit('parsec')).to('cm'))**2) #spec = spec * yUnit/ u.Unit('m')**2 else: spec = (spec * yUnit) . to(uFnuN) if not spec.unit.is_equivalent(uFlamN) and not spec.unit.is_equivalent(uFnuN): raise ValueError(spec.unit,' not recognized as a unit of spectral flux density.') spec = spec.to(uFnuN,equivalencies=u.spectral_density(wavelengths)) if wavelengths[-1]<wavelengths[0]: # then we originally had flux units wavelengths = np.flipud(wavelengths) spec = np.flipud(spec) self.spec = spec self.params = params
def flux(self): """ Returns the fluxes of the underlying :class:`~specutils.Spectrum1D` object converted to the current data display units (given by `PlotDataItem.data_unit`). """ return self.data_item.flux.to(self.data_unit, equivalencies=spectral_density( self.data_item.spectral_axis)).value
def fluxd(self, geom, wave, unit=u.Jy): """Flux density. Parameters ---------- geom : dict of Quantities A dictionary-like object with the keys 'rh' (heliocentric distance), 'delta' (observer-target distance), and 'phase' (phase angle). wave : Quantity The wavelengths at which to compute the emission. unit : astropy Units, optional The return units. Must be spectral flux density. Returns ------- fluxd : Quantity The flux density from the whole asteroid. """ from numpy import pi from scipy.integrate import quad phase = geom['phase'] if not np.iterable(wave): wave = np.array([wave.value]) * wave.unit T0 = self.T0(geom['rh']).to(u.Kelvin).value fluxd = np.zeros(len(wave)) # Integrate theta from -pi/2 to pi/2: emission is emitted from # the daylit hemisphere: theta = (phase - pi/2) to (phase + # pi/2), therfore the theta limits become [-pi/2, pi/2 - # phase] # # Integrate phi from -pi/2 to pi/2 (or 2 * integral from 0 to # pi/2) # # Drop some units for efficiency phase_r = np.abs(phase.to(u.rad).value) wave_um = wave.to(u.um).value for i in range(len(wave_um)): fluxd[i] = quad(self._latitude_emission, -pi / 2.0 + phase_r, pi / 2.0, args=(wave_um[i], T0, phase_r), epsrel=self.tol)[0] fluxd *= (self.epsilon * (self.D / geom['delta'])**2 / pi / 2.0).decompose() # W/m^2/Hz fluxd = fluxd * u.Unit('W / (m2 Hz)') equiv = u.spectral_density(u.um, wave.to(u.um).value) fluxd = fluxd.to(unit, equivalencies=equiv) if len(fluxd) == 1: return fluxd[0] else: return fluxd
def cont(self, value): if not isinstance(value, Data): raise ValueError('cont must be an instance of a Data object') self._cont = value.to( erg / cm ** 2 / s / angstrom, equivalencies=spectral_density(self.wavelength_observed)) self.rcont = self._cont * (1 + self.redshift) ** 3
def __init__( self, wave, flux, error=None, unit=(u.erg / u.s / u.cm ** 2 / u.AA), wave_unit=u.AA, z=None, dist=None, meta=None, ): self.wave = np.asarray(wave, dtype=np.float64) self.flux = np.asarray(flux, dtype=np.float64) if self.wave.shape != self.flux.shape: raise ValueError("shape of wavelength and flux must match") if self.wave.ndim != 1: raise ValueError("only 1-d arrays supported") # internally, wavelength is in Angstroms: if wave_unit != u.AA: self.wave = wave_unit.to(u.AA, self.wave, u.spectral()) self._wave_unit = u.AA # internally, flux is in F_lambda: if unit != FLAMBDA_UNIT: self.flux = unit.to(FLAMBDA_UNIT, self.flux, u.spectral_density(u.AA, self.wave)) self._unit = FLAMBDA_UNIT # Set up interpolation. # This appears to be the fastest-evaluating interpolant in # scipy.interpolate. self._tck = splrep(self.wave, self.flux, k=1) # following are deprecated attributes: if z is not None: warn_once("z keyword in Spectrum", "1.4", "2.0") self._z = z if dist is not None: warn_once("dist keyword in Spectrum", "1.4", "2.0") self._dist = dist if error is not None: warn_once("error keyword in Spectrum", "1.4", "2.0") self._error = np.asarray(error) if self.wave.shape != self._error.shape: raise ValueError("shape of wavelength and variance must match") else: self._error = None if meta is None: self.meta = OrderedDict() else: warn_once("meta keyword in Spectrum", "1.4", "2.0") self.meta = deepcopy(meta)
def _rest_nm_to_photons(self, wave): from astropy import units _photons = units.astrophys.photon/(units.s * units.cm**2 * units.nm) wave_native_quantity = (wave * units.nm).to(self.wave_type, units.spectral()) wave_native_value = wave_native_quantity.value flux_native_quantity = self._spec(wave_native_value) * self.flux_type return (flux_native_quantity .to(_photons, units.spectral_density(wave_native_quantity)) .value)
def ccorrection(self, sf, channels=[1, 2, 3, 4]): """IRAC color correction. Seems to agree within 1% of the IRAC Instrument Handbook. Thier quoted values are good to ~1%. Parameters ---------- sf : function A function that generates source flux density as a Quantity given wavelength as a Quantity. channels : list, optional A list of the IRAC channels for which to compute the color correction, e.g., `[1, 2]` for 3.6 and 4.5 um. Returns ------- K : ndarray Color correction factor, where `Fcc = F / K`. """ from scipy import interpolate import astropy.constants as const from ..calib import filter_trans from ..util import davint, takefrom nu0 = (const.c.si / self.wave).to(u.teraHertz).value K = np.zeros(len(channels)) for ch in channels: tw, tr = filter_trans('IRAC CH{:}'.format(ch)) nu = (const.c / tw).to(u.teraHertz).value sfnu = sf(tw).to(u.Jy, u.spectral_density(tw)).value i = ch - 1 # self.wave index sfnu /= sf(self.wave[i]).to(u.Jy, u.spectral_density(self.wave[i])).value sfnu, tr, nu = takefrom((sfnu, tr, nu), nu.argsort()) K[i] = (davint(nu, sfnu * tr * nu0[i] / nu, nu[0], nu[-1]) / davint(nu, tr * (nu0[i] / nu)**2, nu[0], nu[-1])) return K
def bbfunc(wavelengths, temperature): """Planck law for blackbody radiation in PHOTLAM per steradian. .. warning:: Data points where overflow or underflow occurs will be set to zeroes. Parameters ---------- wavelengths : array_like or `~astropy.units.quantity.Quantity` Wavelength values. If not a Quantity, assumed to be in Angstrom. temperature : float or `~astropy.units.quantity.Quantity` Blackbody temperature. If not a Quantity, assumed to be in Kelvin. Returns ------- fluxes : `~astropy.units.quantity.Quantity` Blackbody radiation in PHOTLAM per steradian. """ # Silence Numpy old_np_err_cfg = np.seterr(all='ignore') # Calculations must use Angstrom wavelengths = units.validate_quantity( wavelengths, u.AA, equivalencies=u.spectral()).astype(np.float64) # Calculations must use Kelvin temperature = units.validate_quantity(temperature, u.K).astype(np.float64) x = wavelengths * temperature # Catch division by zero mask = x > 0 x = np.where(mask, units.HC / (const.k_B.cgs * x), 0.0) # Catch overflow/underflow mask = (x >= _VERY_SMALL) & (x < _VERY_LARGE) factor = np.where(mask, 1.0 / np.expm1(x), 0.0) # Convert FNU to PHOTLAM freq = u.Quantity(np.where( factor, wavelengths.to(u.Hz, equivalencies=u.spectral()), 0.0), u.Hz) bb_nu = 2.0 * const.h * factor * freq * freq * freq / const.c ** 2 bb_lam = np.where( factor, units.FNU.to(units.PHOTLAM, bb_nu.cgs.value, equivalencies=u.spectral_density(wavelengths)), 0.0) # Restore Numpy settings dummy = np.seterr(**old_np_err_cfg) return u.Quantity(bb_lam, unit=units.PHOTLAM/u.sr)
def test_spectraldensity5(): """ Test photon luminosity density conversions. """ L_la = u.erg / (u.s * u.AA) L_nu = u.erg / (u.s * u.Hz) phot_L_la = u.photon / (u.s * u.AA) phot_L_nu = u.photon / (u.s * u.Hz) wave = u.Quantity([4956.8, 4959.55, 4962.3], u.AA) flux_phot_L_la = [9.7654e-3, 1.003896e-2, 9.78473e-3] flux_phot_L_nu = [8.00335589e-14, 8.23668949e-14, 8.03700310e-14] flux_L_la = [3.9135e-14, 4.0209e-14, 3.9169e-14] flux_L_nu = [3.20735792e-25, 3.29903646e-25, 3.21727226e-25] # PHOTLAM <--> FLAM assert_allclose(phot_L_la.to( L_la, flux_phot_L_la, u.spectral_density(wave)), flux_L_la, rtol=1e-6) assert_allclose(L_la.to( phot_L_la, flux_L_la, u.spectral_density(wave)), flux_phot_L_la, rtol=1e-6) # PHOTLAM <--> FNU assert_allclose(phot_L_la.to( L_nu, flux_phot_L_la, u.spectral_density(wave)), flux_L_nu, rtol=1e-6) assert_allclose(L_nu.to( phot_L_la, flux_L_nu, u.spectral_density(wave)), flux_phot_L_la, rtol=1e-6) # PHOTLAM <--> PHOTNU assert_allclose(phot_L_la.to( phot_L_nu, flux_phot_L_la, u.spectral_density(wave)), flux_phot_L_nu, rtol=1e-6) assert_allclose(phot_L_nu.to( phot_L_la, flux_phot_L_nu, u.spectral_density(wave)), flux_phot_L_la, rtol=1e-6) # PHOTNU <--> FNU assert_allclose(phot_L_nu.to( L_nu, flux_phot_L_nu, u.spectral_density(wave)), flux_L_nu, rtol=1e-6) assert_allclose(L_nu.to( phot_L_nu, flux_L_nu, u.spectral_density(wave)), flux_phot_L_nu, rtol=1e-6) # PHOTNU <--> FLAM assert_allclose(phot_L_nu.to( L_la, flux_phot_L_nu, u.spectral_density(wave)), flux_L_la, rtol=1e-6) assert_allclose(L_la.to( phot_L_nu, flux_L_la, u.spectral_density(wave)), flux_phot_L_nu, rtol=1e-6)
def blackbody_nu(in_x, temperature): """Calculate blackbody flux per steradian, :math:`B_{\\nu}(T)`. .. note:: Use `numpy.errstate` to suppress Numpy warnings, if desired. .. warning:: Output values might contain ``nan`` and ``inf``. Parameters ---------- in_x : number, array-like, or `~astropy.units.Quantity` Frequency, wavelength, or wave number. If not a Quantity, it is assumed to be in Hz. temperature : number or `~astropy.units.Quantity` Blackbody temperature. If not a Quantity, it is assumed to be in Kelvin. Returns ------- flux : `~astropy.units.Quantity` Blackbody monochromatic flux in :math:`erg \\; cm^{-2} s^{-1} Hz^{-1} sr^{-1}`. Raises ------ ValueError Invalid temperature. ZeroDivisionError Wavelength is zero (when converting to frequency). """ # Convert to units for calculations, also force double precision with u.add_enabled_equivalencies(u.spectral() + u.temperature()): freq = u.Quantity(in_x, u.Hz, dtype=np.float64) temp = u.Quantity(temperature, u.K, dtype=np.float64) # Check if input values are physically possible if temp < 0: raise ValueError('Invalid temperature {0}'.format(temp)) if np.any(freq <= 0): # pragma: no cover warnings.warn('Input contains invalid wavelength/frequency value(s)', AstropyUserWarning) # Calculate blackbody flux bb_nu = (2.0 * const.h * freq ** 3 / (const.c ** 2 * np.expm1(const.h * freq / (const.k_B * temp)))) flux = bb_nu.to(FNU, u.spectral_density(freq)) return flux / u.sr # Add per steradian to output flux unit