Source code for skysurvey.tools.blackbody

"""
This module contains black-body related functions.
"""

# Units
import numpy as np
import warnings

from astropy import units as u
from astropy import constants

_fnu_units = u.erg / (u.cm**2 * u.s * u.Hz)
_flam_units = u.erg / (u.cm**2 * u.s * u.AA)



[docs] def get_blackbody_transient_source(phase, temperature, amplitude, lbda="1_000:10_000:1000j", zero_before=True, name="bb_transient"): """ Get an evolving blackbody `sncosmo.TimeSeriesSource`. Parameters ---------- phase: array-like Phase. temperature: float or array-like Temperature of the blackbody as a function of phase, in Kelvin. amplitude: float or array-like Amplitude of the blackbody peak amplitude (Wein's lbda_max), in flux units. If array-like, must have the same length as phase. lbda: str or array-like Wavelength in Angstrom. If str, it is assumed to by a `np.r_` format. Default is "1_000:10_000:1000j". zero_before: bool If True, flux is zero before the first phase; otherwise the first flux value is used. Default is True. name: str Source name. Returns ------- `sncosmo.TimeSeriesSource` """ from sncosmo import TimeSeriesSource if type(lbda) is str: # assumed r_ input lbda = eval(f"np.r_[{lbda}]") fluxes = get_blackbody_transient_flux(lbda, temperature=temperature, amplitude=amplitude) bb_source = TimeSeriesSource(phase=phase, wave=lbda, flux=fluxes, zero_before=zero_before, name=name) return bb_source
[docs] def get_blackbody_transient_flux(lbda, temperature, amplitude, normed=True): """ Provide a 2D flux grid assuming blackbody temperature and amplitude evolution. Parameters ---------- lbda: number, array-like, or `astropy.units.Quantity` Wavelength. 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. amplitude: number, array-like, or `astropy.units.Quantity` Amplitude of the blackbody. If array-like, must have the same length as temperature. normed: bool, default is True If True, each blackbody flux is normalized to its peak value (given by Wein's lambda_max), and the returned array is dimensionless. If False, returns the flux with units. Default is True. Returns ------- 2d-array Blackbody monochromatic flux normed, and scaled by its amplitude. If normed is True, returns a dimensionless array normalized to peak flux. If normed is False, returns a `astropy.units.Quantity` in :math:`erg \\; cm^{-2} s^{-1} \\AA^{-1} sr^{-1}`. """ normed_blackbody = blackbody_lambda(lbda, temperature=np.atleast_1d(temperature)[:,None], normed=normed) amplitude = np.atleast_1d(amplitude) return normed_blackbody*amplitude[:,None]
[docs] def blackbody_nu(freq, 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 ---------- freq : number, array-like, or `astropy.units.Quantity` Frequency, wavelength, or wave number. If not a Quantity, it is assumed to be in Hertz. 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 | float64 required by astropy units with u.add_enabled_equivalencies(u.spectral() + u.temperature()): freq = u.Quantity(freq, u.Hz, dtype="float64") temp = u.Quantity(temperature, u.K, dtype="float64") # Check if input values are physically possible if np.any(freq <= 0): warnings.warn("freq contains invalid values (<= 0)") # Calculate blackbody flux bb_nu = (2.0 * constants.h * freq ** 3 / (constants.c ** 2 * np.expm1(constants.h * freq / (constants.k_B * temp)))) flux = bb_nu.to(_fnu_units, u.spectral_density(freq)) return flux / u.sr # Add per steradian to output flux unit
[docs] def blackbody_lambda(lbda, temperature, normed=True): """Like :func:`blackbody_nu` but for :math:`B_{\\lambda}(T)`. Parameters ---------- lbda: number, array-like, or `astropy.units.Quantity` Wavelength. 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. normed: bool If True, the blackbody flux is normalized to its peak value (given by Wein's lambda_max), and the returned array is dimensionless. If False, returns the flux with units. Default is True. Returns ------- flux: ndarray or `astropy.units.Quantity` Blackbody monochromatic flux. If normed is True, returns a dimensionless array normalized to peak flux. If normed is False, returns a astropy.units.Quantity in :math:`erg \\; cm^{-2} s^{-1} \\AA^{-1} sr^{-1}`. """ if not hasattr(lbda, 'unit'): # assumed Angstrom lbda = u.Quantity(lbda, u.AA) bb_nu = blackbody_nu(lbda, temperature) * u.sr # Remove sr for conversion flux = bb_nu.to(_flam_units, u.spectral_density(lbda)) / u.sr # Add per steradian to output flux unit if normed: lbda_max = get_wein_lbdamax(temperature) f_max = blackbody_lambda(lbda_max, temperature, normed=False) flux = (flux/f_max).value return flux
[docs] def get_wein_lbdamax(temperature): r""" Return the wavelength of maximum emission for a blackbody at a given temperature using Wien's law. .. math:: \lambda_{\mathrm{max}} = \frac{h c}{4.96511423174 \, k_B T} Parameters ---------- temperature: number or `astropy.units.Quantity` Blackbody temperature. If not a Quantity, it is assumed to be in Kelvin. Returns ------- lbda: `astropy.units.Quantity` Wavelength of maximum emission, in Angstrom. """ if not hasattr(temperature, 'unit'): # assumed Kelvin temperature = u.Quantity(temperature, u.Kelvin) lbda = constants.h*constants.c/(4.96511423174*constants.k_B * temperature) return lbda.to(u.Angstrom)