def test_bl(wavelength, temp): kelp_test = bl_test(wavelength, temp) check = BlackBody( temp * u.K, scale=1 * (u.W / (u.m ** 2 * u.nm * u.sr)) )(wavelength * u.m) np.testing.assert_allclose(check.si.value, kelp_test, rtol=1e-4)
def test_fit_blackbody(NGC4945_continuum_rest_frame): real_spectrum = NGC4945_continuum_rest_frame freq_axis = real_spectrum.frequency_axis sinthetic_model = BlackBody(1000 * u.K) sinthetic_flux = sinthetic_model(freq_axis) dispersion = 3.51714285129581 first_wave = 18940.578099674 dispersion_type = "LINEAR " spectrum_length = len(real_spectrum.flux) spectral_axis = (first_wave + dispersion * np.arange(0, spectrum_length)) * u.AA spec1d = su.Spectrum1D(flux=sinthetic_flux, spectral_axis=spectral_axis) frequency_axis = spec1d.spectral_axis.to(u.Hz) snth_blackbody = NirdustSpectrum( header=None, z=0, spectrum_length=spectrum_length, dispersion_key=None, first_wavelength=None, dispersion_type=dispersion_type, spec1d=spec1d, frequency_axis=frequency_axis, ) snth_bb_temp = (snth_blackbody.normalize().convert_to_frequency(). fit_blackbody(1200).temperature) np.testing.assert_almost_equal(snth_bb_temp.value, 1000, decimal=7)
def evaluate_bb_sed(nu, z, T_dt, R_dt, d_L): """evaluate the black body SED corresponding to the torus temperature""" nu *= 1 + z # geometrical factor for a source of size R_dt at distance d_L prefactor = np.pi * np.power((R_dt / d_L).to_value(""), 2) * u.sr I_nu = BlackBody().evaluate(nu, T_dt, scale=1) return (prefactor * nu * I_nu).to("erg cm-2 s-1")
def integrated_blackbody(self, n_theta, n_phi, f=2**-0.5, cython=True): """ Integral of the blackbody function convolved with a filter bandpass. Parameters ---------- n_theta : int Number of grid points in latitude n_phi : int Number of grid points in longitude f : float Greenhouse parameter (typically 1/sqrt(2)). Returns ------- interp_bb : function Interpolation function for the blackbody map as a function of latitude (theta) and longitude (phi) """ from .fast import _integrated_blackbody if cython: int_bb, func = _integrated_blackbody(self.hotspot_offset, self.omega_drag, self.alpha, self.C_ml, self.lmax, self.T_s, self.a_rs, self.rp_a, self.A_B, n_theta, n_phi, self.filt.wavelength.to( u.m).value, self.filt.transmittance, f=f) return int_bb, func else: T, theta_grid, phi_grid = self.temperature_map(n_theta, n_phi, f, cython=cython) if (T < 0).any(): return lambda theta, phi: np.inf bb_t = BlackBody(temperature=T * u.K) int_bb = np.trapz(bb_t(self.filt.wavelength[:, None, None]) * self.filt.transmittance[:, None, None], self.filt.wavelength, axis=0).si.value interp_bb = RectBivariateSpline(theta_grid, phi_grid, int_bb, kx=1, ky=1) return int_bb, lambda theta, phi: interp_bb(theta, phi)[0][0]
def OptThinMass(S, d=1 * u.kpc, wav=1 * u.mm, kappa=0.0114 * u.cm**2 / u.g, T=20 * u.K): bb = BlackBody(temperature=T) solar = (S * d**2 / kappa / bb(wav) / u.sr).to(u.M_sun) earthly = (S * d**2 / kappa / bb(wav) / u.sr).to(u.M_earth) return solar, earthly #returns mass in solar masses and earth masses
def test_call(self, unit): B = BlackBody(temperature=300 * u.K, scale=((const.R_sun.to('au') / const.au)**2) * u.Unit(unit)) w = np.logspace(-0.5, 3) * u.um f = B(w) * np.pi * u.sr BB = BlackbodySource(300 * u.K) test = BB(w, unit=f.unit).value assert np.allclose(test, f.value)
def blackbody_spectrum( temperature: float, scale: float, redshift: float = None, extinction_av: float = None, extinction_rv: float = None, get_bolometric_flux: bool = False, ): """ """ wavelengths, frequencies = get_wavelengths_and_frequencies() scale_lambda = 1 * FLAM / u.sr scale_lambda = 1 / scale * FLAM / u.sr scale_nu = 1 / scale * FNU / u.sr bb_nu = BlackBody(temperature=temperature * u.K, scale=scale_nu) flux_nu = bb_nu(wavelengths) * u.sr bolometric_flux = bb_nu.bolometric_flux #.value flux_lambda = flux_nu_to_lambda(flux_nu, wavelengths) if extinction_av is not None: flux_lambda_reddened = apply( calzetti00(np.asarray(wavelengths), extinction_av, extinction_rv), np.asarray(flux_lambda), ) flux_nu_reddened = flux_lambda_to_nu(flux_lambda_reddened, wavelengths) spectrum_reddened = sncosmo_spectral_v13.Spectrum( wave=wavelengths, flux=flux_nu_reddened, unit=FNU) spectrum_unreddened = sncosmo_spectral_v13.Spectrum(wave=wavelengths, flux=flux_nu, unit=FNU) if redshift is not None: if extinction_av is not None: spectrum_reddened.z = 0 spectrum_reddened_redshifted = spectrum_reddened.redshifted_to( redshift, cosmo=cosmo, ) outspectrum = spectrum_reddened_redshifted else: spectrum_unreddened.z = 0 spectrum_unreddened_redshifted = spectrum_unreddened.redshifted_to( redshift, cosmo=cosmo) outspectrum = spectrum_unreddened_redshifted else: if extinction_av is not None: outspectrum = spectrum_reddened else: outspectrum = spectrum_unreddened if get_bolometric_flux: return outspectrum, bolometric_flux else: return outspectrum
def test_blackbody(temperature): wngrid = np.linspace(200, 30000, 200) bb = black_body(wngrid, temperature)/np.pi bb_as = BlackBody(temperature=temperature * u.K) expect_flux = bb_as(wngrid * u.k).to(u.W/u.m**2/u.micron/u.sr, equivalencies=u.spectral_density( wngrid*u.k)) assert bb == pytest.approx(expect_flux.value, rel=1e-3)
def evaluate_multi_T_bb_sed(nu, z, M_BH, m_dot, R_in, R_out, d_L, mu_s=1): r"""Evaluate a multi-temperature black body SED in the case of the SS Disk. The SED is calculated for an observer far away from the disk (:math:`d_L \gg R`) with the following: .. math:: \nu F_{\nu} \approx \mu_s \, \nu \frac{2 \pi}{d_L^2} \int_{R_{\rm in}}^{R_{\rm out}}{\rm d}R \, R \, I_{\nu}(T(R)), where :math:`I_{\nu}` is Planck's law, :math:`R` the radial coordinate along the disk, and :math:`d_L` the luminosity distance. :math:`\mu_s` is the cosine of the angle between the disk axis and the observer's line of sight. Parameters ---------- nu : :class:`~astropy.units.Quantity` array of frequencies, in Hz, to compute the sed **note** these are observed frequencies (observer frame) z : float redshift of the source M_BH : :class:`~astropy.units.Quantity` Black Hole mass m_dot : float mass accretion rate R_in : :class:`~astropy.units.Quantity` inner disk radius R_out : :class:`~astropy.units.Quantity` outer disk radius d_L : :class:`~astropy.units.Quantity` luminosity of the source mu_s : float cosine of the angle between the observer line of sight and the disk axis """ # correct for redshift nu *= 1 + z # array to integrate R R = np.linspace(R_in, R_out, 100) _R, _nu = axes_reshaper(R, nu) _T = SSDisk.evaluate_T(_R, M_BH, m_dot, R_in) _I_nu = BlackBody().evaluate(_nu, _T, scale=1) integrand = _R / np.power(d_L, 2) * _I_nu * u.sr F_nu = 2 * np.pi * np.trapz(integrand, R, axis=0) return mu_s * (nu * F_nu).to("erg cm-2 s-1")
def from_blackbody(cls, T_s): """ Return PHOENIX model stellar spectrum for a star with a given effective temperature ``T_s`` and surface gravity ``log_g``. Parameters ---------- T_s : int Effective temperature log_g : float Surface gravity """ from astropy.modeling.models import BlackBody wavelengths = np.linspace(500, 55000, 1000) * u.Angstrom bb = np.pi * u.sr * BlackBody( T_s * u.K, scale=1 * u.W / u.m**3 / u.sr)(wavelengths) return cls(wavelengths, bb)
def get_J_CMB(): #returns the mean intensity for the CMB integrated over min_lam to #max_lam (i.e. returns erg/s/cm**2; the same thing as doing 4*sigma #T^4) min_lam = (1. * u.angstrom).to(u.micron) max_lam = (1 * u.cm).to(u.micron) wavelengths = np.linspace(min_lam, max_lam, 1.e5) # flux = blackbody_lambda(wavelengths,cfg.model.TCMB) # blackbody_lambda deprecated in astropy v4.3 ## equivalent function provided here https://docs.astropy.org/en/v4.1/modeling/blackbody_deprecated.html#blackbody-functions-deprecated bb_lam = BlackBody(cfg.model.TCMB * u.K, scale=1.0 * u.erg / (u.cm**2 * u.AA * u.s * u.sr)) flux = bb_lam(wavelengths) J = np.trapz(flux, wavelengths).to(u.erg / u.s / u.cm**2 / u.sr) solid_angle = 4. * np.pi * u.sr J = J * solid_angle return J
def energy_density_absorbed_by_CMB(): extinction_file = cfg.par.dustdir + cfg.par.dustfile mw_df = h5py.File(extinction_file, 'r') mw_o = mw_df['optical_properties'] mw_df_nu = mw_o['nu'] * u.Hz mw_df_chi = mw_o['chi'] * u.cm**2 / u.g # b_nu = blackbody_nu(mw_df_nu,cfg.model.TCMB) # blackbody_nu deprecated in astropy v4.3 ## equivalent function provided here https://docs.astropy.org/en/v4.1/modeling/blackbody_deprecated.html#blackbody-functions-deprecated bb_nu = BlackBody(cfg.model.TCMB * u.K) b_nu = bb_nu(mw_df_nu) #energy_density_absorbed = 4pi int b_nu * kappa_nu d_nu since b_nu #has units erg/s/cm^2/Hz/str and kappa_nu has units cm^2/g. this results in units erg/s/g steradians = 4 * np.pi * u.sr energy_density_absorbed = (steradians * np.trapz( (b_nu * mw_df_chi), mw_df_nu)).to(u.erg / u.s / u.g) return energy_density_absorbed
def thermal_phase_curve(self, xi, n_theta=20, n_phi=200, f=2 ** -0.5, cython=True, quad=False, check_sorted=True): r""" Compute the thermal phase curve of the system as a function of observer angle ``xi``. .. note:: The ``xi`` axis is assumed to be monotonically increasing when ``check_sorted=False``, ``cython=True`` and ``quad=False``. Parameters ---------- xi : array-like Orbital phase angle n_theta : int Number of grid points in latitude n_phi : int Number of grid points in longitude f : float Greenhouse parameter (typically 1/sqrt(2)). cython : bool Use Cython implementation of the `integrated_blackbody` function (deprecated). Default is True. quad : bool Use `dblquad` to integrate the temperature map if True, else use trapezoidal approximation. check_sorted : bool Check that the ``xi`` values are sorted before passing to cython (carefully turning this off will speed things up a bit) Returns ------- phase_curve : `~kelp.PhaseCurve` System fluxes as a function of phase angle :math:`\xi`. """ from .fast import _phase_curve rp_rs2 = (self.rp_a * self.a_rs) ** 2 if quad: fluxes = np.zeros(len(xi)) int_bb, interp_blackbody = self.integrated_blackbody(n_theta, n_phi, f, cython) def integrand(phi, theta, xi): return (interp_blackbody(theta, phi) * sin(theta) ** 2 * cos(phi + xi)) bb_ts = BlackBody(temperature=self.T_s * u.K) planck_star = np.trapz(self.filt.transmittance * bb_ts(self.filt.wavelength), self.filt.wavelength).si.value for i in range(len(xi)): fluxes[i] = dblquad(integrand, 0, np.pi, lambda x: -xi[i] - np.pi / 2, lambda x: -xi[i] + np.pi / 2, epsrel=100, args=(xi[i],) )[0] * rp_rs2 / np.pi / planck_star else: if check_sorted: if not np.all(np.diff(xi) >= 0): raise ValueError("xi array must be sorted") fluxes = _phase_curve( xi.astype(np.float64), self.hotspot_offset, self.omega_drag, self.alpha, self.C_ml, self.lmax, self.T_s, self.a_rs, self.rp_a, self.A_B, n_theta, n_phi, self.filt.wavelength.to(u.m).value, self.filt.transmittance, f, self.stellar_spectrum.wavelength.to(u.m).value, self.stellar_spectrum.spectral_flux_density.to(u.W/u.m**3) ) return PhaseCurve(xi, 1e6 * fluxes, channel=self.filt.name)
from astropy.io import fits from astropy.modeling.models import BlackBody, Gaussian1D, Gaussian2D from astropy.utils import NumpyRNGContext from astropy import units as u from gempy.library.fitting import fit_1D _RANDOM_SEED = 42 debug = False # Simulate an object spectrum. This black body spectrum is not particularly # meaningful physically; it's just a way to produce a credible continuum-like # curve using something other than the functions being fitted: cont_model = BlackBody(temperature=9500.*u.K, scale=5.e6) # Some sky-line-like features to be rejected / fitted: sky_model = (Gaussian1D(amplitude=2000., mean=5577., stddev=50.) + Gaussian1D(amplitude=1000., mean=6300., stddev=50.) + Gaussian1D(amplitude=300., mean=7914., stddev=50.) + Gaussian1D(amplitude=280., mean=8345., stddev=50.) + Gaussian1D(amplitude=310., mean=8827., stddev=50.)) class TestFit1D: """ Some tests for gempy.library.fitting.fit_1D with 1-2D data. """ def setup_class(self):
def get_delta_obs_given_mstars(m2, m3, m1=1.1, make_plot=0, verbose=1): """ You are given a hierarchical eclipsing binary system. Star 1 (TOI 837) is the main source of light. Stars 2 and 3 are eclipsing. Work out the observed eclipse depth of Star 3 in front of Star 2 in each of a number of bandpasses, assuming maximally large eclipses, and blackbodies. Ah, and also assuming MIST isochrones. """ # Given the stellar masses, get their effective temperatures and # luminosities. # # At ~50 Myr, M dwarf companion PMS contraction will matter for its # parameters. Use # data.companion_isochrones.MIST_plus_Baraffe_merged_3.5e+07.csv, # which was created during the dmag to companion mass conversion process. # mstars = nparr([m1, m2, m3]) icdir = os.path.join(DATADIR, 'companion_isochrones') df_ic = pd.read_csv( os.path.join(icdir, 'MIST_plus_Baraffe_merged_3.5e+07.csv')) teff1 = TEFF teff2 = _find_nearest(df_ic, 'teff', m2) teff3 = _find_nearest(df_ic, 'teff', m3) lum1 = (4 * np.pi * (RSTAR * u.Rsun)**2 * const.sigma_sb * (TEFF * u.K)**4).to(u.Lsun).value lum2 = _find_nearest(df_ic, 'lum', m2) lum3 = _find_nearest(df_ic, 'lum', m3) teffs = nparr([teff1, teff2, teff3]) lums = nparr([lum1, lum2, lum3]) # # initialization. other working bandpasses include Johnson_U, Johnson_V, # SDSS_g., and SDSS_z. # bpdir = os.path.join(DATADIR, 'bandpasses') bandpasses = [ 'Bessell_U', 'Bessell_B', 'Bessell_V', 'Cousins_R', 'Cousins_I', 'TESS', 'Johnson_B' ] bppaths = [ glob(os.path.join(bpdir, '*' + bp + '*csv'))[0] for bp in bandpasses ] wvlen = np.logspace(1, 5, 2000) * u.nm # # do the calculation # B_lambda_dict = {} for ix, temperature, luminosity in zip(range(len(teffs)), teffs * u.K, lums * u.Lsun): bb = BlackBody(temperature=temperature) B_nu_vals = bb(wvlen) B_lambda_vals = (B_nu_vals * (const.c / wvlen**2)).to( u.erg / u.nm / u.s / u.sr / u.cm**2) B_lambda_dict[ix] = B_lambda_vals # now get the flux in each bandpass F_X_dict = {} F_dict = {} M_X_dict = {} M_dict = {} T_dict = {} L_X_dict = {} for bp, bppath in zip(bandpasses, bppaths): bpdf = pd.read_csv(bppath) if 'nm' in bpdf: pass else: bpdf['nm'] = bpdf['angstrom'] / 10 bp_wvlen = nparr(bpdf['nm']) T_lambda = nparr(bpdf.Transmission) if np.nanmax(T_lambda) > 1.1: if np.nanmax(T_lambda) < 100: T_lambda /= 100 # unit convert else: raise NotImplementedError eps = 1e-6 if not np.all(np.diff(bp_wvlen) > eps): raise NotImplementedError interp_fn = interp1d(bp_wvlen, T_lambda, bounds_error=False, fill_value=0, kind='quadratic') T_lambda_interp = interp_fn(wvlen) T_dict[bp] = T_lambda_interp F_X_dict[bp] = {} M_X_dict[bp] = {} L_X_dict[bp] = {} # # for each star, calculate erg/s in bandpass # NOTE: the quantity of interest is in fact counts/sec. # (this is probably a small consideration, but could still be worth # checking) # rstars = [] for ix, temperature, luminosity in zip(range(len(teffs)), teffs * u.K, lums * u.Lsun): # flux (erg/s/cm^2) in bandpass _F_X = (4 * np.pi * u.sr * trapz(B_lambda_dict[ix] * T_lambda_interp, wvlen)) F_X_dict[bp][ix] = _F_X # bolometric flux, according to the blackbody function _F_bol = (4 * np.pi * u.sr * trapz( B_lambda_dict[ix] * np.ones_like(T_lambda_interp), wvlen)) # stefan-boltzman law to get rstar from the isochronal temp/lum. rstar = np.sqrt(luminosity / (4 * np.pi * const.sigma_sb * temperature**4)) rstars.append(rstar.to(u.Rsun).value) # erg/s in bandpass _L_X = _F_X * 4 * np.pi * rstar**2 L_X_dict[bp][ix] = _L_X.cgs if DEBUG: print(42 * '-') print(f'{_F_X:.2e}, {_F_bol:.2e}, {L_X_dict[bp][ix]:.2e}') if ix not in F_dict.keys(): F_dict[ix] = _F_bol # get bolometric magnitude of the star, in the bandpass, as a # sanity check M_bol_sun = 4.83 M_bol_star = (-5 / 2 * np.log10(luminosity / (1 * u.Lsun)) + M_bol_sun) M_X = M_bol_star - 5 / 2 * np.log10(F_X_dict[bp][ix] / F_dict[ix]) if ix not in M_dict.keys(): M_dict[ix] = M_bol_star.value M_X_dict[bp][ix] = M_X.value delta_obs_dict = {} for k in L_X_dict.keys(): # out of eclipse L_ooe = L_X_dict[k][0] + L_X_dict[k][1] + L_X_dict[k][2] # in eclipse. assume maximal depth f = (rstars[2] / rstars[1])**2 L_ie = L_X_dict[k][0] + L_X_dict[k][2] + (1 - f) * L_X_dict[k][1] # assume the tertiary is eclipsing the secondary. delta_obs_dict[k] = ((L_ooe - L_ie) / L_ooe).value if verbose: for k in delta_obs_dict.keys(): print(f'{k}: {delta_obs_dict[k]:.2e}') if make_plot: # # plot the result # from astropy.visualization import quantity_support plt.close('all') linestyles = ['-', '-', '--'] f, ax = plt.subplots(figsize=(4, 3)) with quantity_support(): for ix in range(3): l = f'{teffs[ix]:d} K, {mstars[ix]:.3f} M$_\odot$' ax.plot(wvlen, B_lambda_dict[ix], ls=linestyles[ix], label=l) ax.set_yscale('log') ax.set_ylim( [1e-3, 10 * np.nanmax(np.array(list(B_lambda_dict.values())))]) ax.set_xlabel('Wavelength [nm]') ax.legend(loc='best', fontsize='xx-small') ax.set_ylabel( '$B_\lambda$ [erg nm$^{-1}$ s$^{-1}$ sr$^{-1}$ cm$^{-2}$ ]') tax = ax.twinx() tax.set_ylabel('Transmission [%]') for k in T_dict.keys(): sel = T_dict[k] > 0 tax.plot(wvlen[sel], 100 * T_dict[k][sel], c='k', lw=0.5) tax.set_xscale('log') tax.set_ylim([0, 105]) ax.set_xlim([5e1, 1.1e4]) f.tight_layout() f.savefig( '../results/eclipse_depth_color_dependence/blackbody_transmission.png', dpi=300) return delta_obs_dict
def bb(self): from astropy.modeling.models import BlackBody return BlackBody(temperature=self.Td*u.K)
def bb(self): """ `astropy.modeling.models.BlackBody` function with ``self.Td`` dust temperature """ from astropy.modeling.models import BlackBody return BlackBody(temperature=self.Td * u.K)
def abs_mag_in_bandpass(lum, teff, bandpass='******'): """ lum: bolometric luminosity in units of Lsun teff: effective temperature in units of K bandpass: '******' or '832', nanometers. """ if bandpass not in ['562', '832', 'NIRC2_Kp']: raise ValueError if bandpass in ['562', '832']: bandpassdir = '../data/WASP4_zorro_speckle/filters/' bandpasspath = os.path.join(bandpassdir, 'filter_EO_{}.csv'.format(bandpass)) bpdf = pd.read_csv(bandpasspath, delim_whitespace=True) # the actual tabulated values here are bogus at the long wavelength end. # obvious from like... physics, assuming detectors are silicon. (confirmed # by Howell in priv. comm.) width = 100 # nanometers, around the bandpass middle sel = np.abs(float(bandpass) - bpdf.nm) < width bpdf = bpdf[sel] elif bandpass == 'NIRC2_Kp': # NIRC2 Kp band filter from # http://svo2.cab.inta-csic.es/theory/fps/getdata.php?format=ascii&id=Keck/NIRC2.Kp bandpassdir = '../data/WASP4_NIRC2/' bandpasspath = os.path.join(bandpassdir, 'Keck_NIRC2.Kp.dat') bpdf = pd.read_csv(bandpasspath, delim_whitespace=True, names=['wvlen_angst', 'Transmission']) bpdf['nm'] = bpdf.wvlen_angst / 10 else: raise NotImplementedError # # see /doc/20200121_blackbody_mag_derivn.pdf for relevant discussion of # units and where the equations come from. # from astropy.modeling.models import BlackBody M_Xs = [] for temperature, luminosity in zip(teff * u.K, lum * u.Lsun): bb = BlackBody(temperature=temperature) wvlen = nparr(bpdf.nm) * u.nm B_nu_vals = bb(wvlen) B_lambda_vals = B_nu_vals * (const.c / wvlen**2) T_lambda = nparr(bpdf.Transmission) F_X = 4 * np.pi * u.sr * trapz(B_lambda_vals * T_lambda, wvlen) F = const.sigma_sb * temperature**4 # https://nssdc.gsfc.nasa.gov/planetary/factsheet/sunfact.html M_bol_sun = 4.83 M_bol_star = (-5 / 2 * np.log10(luminosity / (1 * u.Lsun)) + M_bol_sun) # bolometric magnitude of the star, in the bandpass! M_X = M_bol_star - 5 / 2 * np.log10(F_X / F) M_Xs.append(M_X.value) return nparr(M_Xs)