B
    q\/              	   @   s  d Z ddlZddlmZ ddlZddlmZ ddlm	Z	 ddl
mZ ddl
mZ dd	lmZ d
ddgZejejd ej ej  Zejejd ej ej  Ze 2 ede eedpeedZW dQ R X G dd
 d
eZdd Z dd Z!dS )a  
Model and functions related to blackbody radiation.

.. _blackbody-planck-law:

Blackbody Radiation
-------------------

Blackbody flux is calculated with Planck law
(:ref:`Rybicki & Lightman 1979 <ref-rybicki1979>`):

.. math::

    B_{\lambda}(T) = \frac{2 h c^{2} / \lambda^{5}}{exp(h c / \lambda k T) - 1}

    B_{\nu}(T) = \frac{2 h \nu^{3} / c^{2}}{exp(h \nu / k T) - 1}

where the unit of :math:`B_{\lambda}(T)` is
:math:`erg \; s^{-1} cm^{-2} \mathring{A}^{-1} sr^{-1}`, and
:math:`B_{\nu}(T)` is :math:`erg \; s^{-1} cm^{-2} Hz^{-1} sr^{-1}`.
:func:`~astropy.modeling.blackbody.blackbody_lambda` and
:func:`~astropy.modeling.blackbody.blackbody_nu` calculate the
blackbody flux for :math:`B_{\lambda}(T)` and :math:`B_{\nu}(T)`,
respectively.

For blackbody representation as a model, see :class:`BlackBody1D`.

.. _blackbody-examples:

Examples
^^^^^^^^

>>> import numpy as np
>>> from astropy import units as u
>>> from astropy.modeling.blackbody import blackbody_lambda, blackbody_nu

Calculate blackbody flux for 5000 K at 100 and 10000 Angstrom while suppressing
any Numpy warnings:

>>> wavelengths = [100, 10000] * u.AA
>>> temperature = 5000 * u.K
>>> with np.errstate(all='ignore'):
...     flux_lam = blackbody_lambda(wavelengths, temperature)
...     flux_nu = blackbody_nu(wavelengths, temperature)
>>> flux_lam  # doctest: +FLOAT_CMP
<Quantity [  1.27452545e-108,  7.10190526e+005] erg / (Angstrom cm2 s sr)>
>>> flux_nu  # doctest: +FLOAT_CMP
<Quantity [  4.25135927e-123,  2.36894060e-005] erg / (cm2 Hz s sr)>

Plot a blackbody spectrum for 5000 K:

.. plot::

    import matplotlib.pyplot as plt
    import numpy as np
    from astropy import constants as const
    from astropy import units as u
    from astropy.modeling.blackbody import blackbody_lambda

    temperature = 5000 * u.K
    wavemax = (const.b_wien / temperature).to(u.AA)  # Wien's displacement law
    waveset = np.logspace(
        0, np.log10(wavemax.value + 10 * wavemax.value), num=1000) * u.AA
    with np.errstate(all='ignore'):
        flux = blackbody_lambda(waveset, temperature)

    fig, ax = plt.subplots(figsize=(8, 5))
    ax.plot(waveset.value, flux.value)
    ax.axvline(wavemax.value, ls='--')
    ax.get_yaxis().get_major_formatter().set_powerlimits((0, 1))
    ax.set_xlabel(r'$\lambda$ ({0})'.format(waveset.unit))
    ax.set_ylabel(r'$B_{\lambda}(T)$')
    ax.set_title('Blackbody, T = {0}'.format(temperature))

Note that an array of temperatures can also be given instead of a single
temperature. In this case, the Numpy broadcasting rules apply: for instance, if
the frequency and temperature have the same shape, the output will have this
shape too, while if the frequency is a 2-d array with shape ``(n, m)`` and the
temperature is an array with shape ``(m,)``, the output will have a shape
``(n, m)``.

See Also
^^^^^^^^

.. _ref-rybicki1979:

Rybicki, G. B., & Lightman, A. P. 1979, Radiative Processes in Astrophysics (New York, NY: Wiley)

    N)OrderedDict   )Fittable1DModel)	Parameter)	constants)units)AstropyUserWarningBlackBody1Dblackbody_nublackbody_lambda   ignorei  g    _Bc               @   sx   e Zd ZdZeddejdZeddejej	d  ej
 dZdZde iZd	d
 Zedd Zdd Zedd ZdS )r	   a(  
    One dimensional blackbody model.

    Parameters
    ----------
    temperature : :class:`~astropy.units.Quantity`
        Blackbody temperature.
    bolometric_flux : :class:`~astropy.units.Quantity`
        The bolometric flux of the blackbody (i.e., the integral over the
        spectral axis).

    Notes
    -----

    Model formula:

        .. math:: f(x) = \pi B_{\nu} f_{\text{bolometric}} / (\sigma  T^{4})

    Examples
    --------
    >>> from astropy.modeling import models
    >>> from astropy import units as u
    >>> bb = models.BlackBody1D()
    >>> bb(6000 * u.AA)  # doctest: +FLOAT_CMP
    <Quantity 1.3585381201978953e-15 erg / (cm2 Hz s)>

    .. plot::
        :include-source:

        import numpy as np
        import matplotlib.pyplot as plt

        from astropy.modeling.models import BlackBody1D
        from astropy.modeling.blackbody import FLAM
        from astropy import units as u
        from astropy.visualization import quantity_support

        bb = BlackBody1D(temperature=5778*u.K)
        wav = np.arange(1000, 110000) * u.AA
        flux = bb(wav).to(FLAM, u.spectral_density(wav))

        with quantity_support():
            plt.figure()
            plt.semilogx(wav, flux)
            plt.axvline(bb.lambda_max.to(u.AA).value, ls='--')
            plt.show()

    i  r   )defaultminunitr   r   Txc             C   s|   t |tjr"|jtjt d}nt|tj}tjtj t	|| t
j |d  dtj | }t|drr|S |jS dS )a  Evaluate the model.

        Parameters
        ----------
        x : float, `~numpy.ndarray`, or `~astropy.units.Quantity`
            Frequency at which to compute the blackbody. If no units are given,
            this defaults to Hz.

        temperature : float, `~numpy.ndarray`, or `~astropy.units.Quantity`
            Temperature of the blackbody. If no units are given, this defaults
            to Kelvin.

        bolometric_flux : float, `~numpy.ndarray`, or `~astropy.units.Quantity`
            Desired integral for the blackbody.

        Returns
        -------
        y : number or ndarray
            Blackbody spectrum. The units are determined from the units of
            ``bolometric_flux``.
        )Zequivalencies   r   r   N)
isinstanceuQuantitytoKtemperaturenpZpisrr
   constZsigma_sbHzhasattrvalue)selfr   r   bolometric_fluxZfnu r!   9lib/python3.7/site-packages/astropy/modeling/blackbody.pyevaluate   s    	.
zBlackBody1D.evaluatec             C   s
   dt jiS )Nr   )r   r   )r   r!   r!   r"   input_units   s    zBlackBody1D.input_unitsc             C   s    t dtjfd|d tj fgS )Nr   r    y)r   r   r   r   )r   Zinputs_unitZoutputs_unitr!   r!   r"   _parameter_units_for_data_units   s    
z+BlackBody1D._parameter_units_for_data_unitsc             C   s   t j| j S )z=Peak wavelength when the curve is expressed as power density.)r   Zb_wienr   )r   r!   r!   r"   
lambda_max   s    zBlackBody1D.lambda_maxN)__name__
__module____qualname____doc__r   r   r   r   ergcmsr    Z _input_units_allow_dimensionlessspectralZinput_units_equivalenciesr#   propertyr$   r&   r'   r!   r!   r!   r"   r	   w   s   0 2c       	   	   C   s6  t t  t   . t j| t jtjd}t j|t jtjd}W dQ R X t	|dk rft
d|tt|rt	|dkrtdt tj| tj|  }t|}trt|}t	|r|jrt|stj}ntj|tt| |@ < dtj |d  tjd |  }|tt |}|t j S )	aq  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, 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} Hz^{-1} sr^{-1}`.

    Raises
    ------
    ValueError
        Invalid temperature.

    ZeroDivisionError
        Wavelength is zero (when converting to frequency).

    )ZdtypeNr   z#Temperature should be positive: {0}z4Input contains invalid wavelength/frequency value(s)g       @   r   )r   Zadd_enabled_equivalenciesr/   r   r   r   r   Zfloat64r   any
ValueErrorformatallZisfinitewarningswarnr   r   hZk_Bexpm1_has_buggy_expm1isnanZisscalarinfwherecr   FNUspectral_densityr   )	in_xr   ZfreqZtempZ	log_boltzZboltzm1Zboltzm1_nansbb_nufluxr!   r!   r"   r
      s&    %


 c             C   sJ   t | dddkrt| tj} t| |tj }|tt| }|tj S )aI  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}`.

    r   N)	getattrr   r   AAr
   r   r   FLAMr@   )rA   r   rB   rC   r!   r!   r"   r   @  s
    )"r+   r6   collectionsr   Znumpyr   Zcorer   Z
parametersr   Zastropyr   r   r   r   Zastropy.utils.exceptionsr   __all__r,   r-   r.   r   r?   rE   rF   catch_warningssimplefilterRuntimeWarningr;   r9   r:   r	   r
   r   r!   r!   r!   r"   <module>Z   s$   

* G