Source code for elisa.models.add

"""Additive models."""

from __future__ import annotations

from typing import TYPE_CHECKING

import jax
import jax.numpy as jnp
from jax.scipy import stats

from elisa.models.model import (
    AnaIntAdditive,
    NumIntAdditive,
    ParamConfig,
)

if TYPE_CHECKING:
    from elisa.util.typing import CompEval, JAXArray, NameValMapping

__all__ = [
    'Band',
    'BandEp',
    'BentPL',
    'Blackbody',
    'BlackbodyRad',
    'BrokenPL',
    'Compt',
    'CutoffPL',
    'Gauss',
    'Lorentz',
    'OTTB',
    'OTTS',
    'PLEnFlux',
    'PLPhFlux',
    'PowerLaw',
    'SmoothlyBrokenPL',
]


def _powerlaw_integral(egrid: JAXArray, alpha: JAXArray) -> JAXArray:
    cond = jnp.full(len(egrid), jnp.not_equal(alpha, 1.0))

    one_minus_alpha = jnp.where(cond, 1.0 - alpha, 1.0)
    f1 = jnp.power(egrid, one_minus_alpha) / one_minus_alpha
    f1 = f1[1:] - f1[:-1]

    f2 = jnp.log(egrid)
    f2 = f2[1:] - f2[:-1]

    return jnp.where(cond[:-1], f1, f2)


[docs] class Band(NumIntAdditive): r"""Gamma-ray burst continuum developed by Band et al. (1993) [1]_. .. math:: N(E) = K \begin{cases} \bigl(\frac{E}{E_0}\bigr)^\alpha \exp\bigl(-\frac{E}{E_\mathrm{c}}\bigr), &\text{if } E < (\alpha-\beta) E_\mathrm{c}, \\\\ \bigl(\frac{E}{E_0}\bigr)^\beta \exp(\beta-\alpha) \left[ \frac{(\alpha-\beta)E_\mathrm{c}}{E_0} \right]^{\alpha-\beta}, &\text{otherwise,} \end{cases} where :math:`E_0` is the reference energy fixed at 100 keV. Parameters ---------- alpha : Parameter, optional The low-energy power law index :math:`\alpha`, dimensionless. beta : Parameter, optional The high-energy power law index :math:`\beta`, dimensionless. Ec : Parameter, optional The characteristic energy :math:`E_\mathrm{c}`, in units of keV. K : Parameter, optional The amplitude :math:`K`, in units of ph cm⁻² s⁻¹ keV⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. References ---------- .. [1] `Band, D., et al. 1993, ApJ, 413, 281 <https://adsabs.harvard.edu/full/1993ApJ...413..281B>`__ """ _config = ( ParamConfig('alpha', r'\alpha', '', -1.0, -10.0, 5.0), ParamConfig('beta', r'\beta', '', -2.0, -10.0, 10.0), ParamConfig('Ec', r'E_\mathrm{c}', 'keV', 300.0, 10.0, 1e4), ParamConfig('K', 'K', 'ph cm^-2 s^-1 keV^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: alpha = params['alpha'] beta = params['beta'] Ec = params['Ec'] K = params['K'] e0 = 100.0 # workaround for beta > alpha, as in XSPEC amb_ = alpha - beta inv_Ec = 1.0 / Ec amb = jnp.where(jnp.less(amb_, inv_Ec), inv_Ec, amb_) Ebreak = Ec * amb log = jnp.where( jnp.less(egrid, Ebreak), alpha * jnp.log(egrid / e0) - egrid / Ec, amb * jnp.log(amb * Ec / e0) - amb + beta * jnp.log(egrid / e0), ) return K * jnp.exp(log)
[docs] class BandEp(NumIntAdditive): r"""Gamma-ray burst continuum developed by Band et al. (1993) [1]_, parametrized by the peak of :math:`\nu F_\nu`. .. math:: N(E) = K \begin{cases} \bigl(\frac{E}{E_0}\bigr)^\alpha \exp\left[-\frac{(2+\alpha)E}{E_\mathrm{p}}\right], &\text{if } E < \frac{(\alpha-\beta)E_\mathrm{p}}{2+\alpha}, \\\\ \bigl(\frac{E}{E_0}\bigr)^\beta \exp(\beta-\alpha) \left[ \frac{(\alpha-\beta)E_\mathrm{p}}{(2+\alpha)E_0} \right]^{\alpha-\beta}, &\text{otherwise}, \end{cases} where :math:`E_0` is the reference energy fixed at 100 keV. .. warning:: This model requires the low-energy power law index :math:`\alpha` to be greater than -2.0. Parameters ---------- alpha : Parameter, optional The low-energy power law index :math:`\alpha`, dimensionless. beta : Parameter, optional The high-energy power law index :math:`\beta`, dimensionless. Ep : Parameter, optional The peak energy :math:`E_\mathrm{p}` of :math:`\nu F_\nu`, in units of keV. K : Parameter, optional The amplitude :math:`K`, in units of ph cm⁻² s⁻¹ keV⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. References ---------- .. [1] `Band, D., et al. 1993, ApJ, 413, 281 <https://adsabs.harvard.edu/full/1993ApJ...413..281B>`__ """ _config = ( ParamConfig('alpha', r'\alpha', '', -1.0, -1.99, 10.0), ParamConfig('beta', r'\beta', '', -2.0, -10.0, 10.0), ParamConfig('Ep', r'E_\mathrm{p}', 'keV', 300.0, 10.0, 1e4), ParamConfig('K', 'K', 'ph cm^-2 s^-1 keV^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: alpha = params['alpha'] beta = params['beta'] Ep = params['Ep'] K = params['K'] e0 = 100.0 Ec = Ep / (2.0 + alpha) Ebreak = (alpha - beta) * Ec # workaround for beta > alpha, as in XSPEC amb_ = alpha - beta inv_Ec = 1.0 / Ec amb = jnp.where(jnp.less(amb_, inv_Ec), inv_Ec, amb_) log = jnp.where( jnp.less(egrid, Ebreak), alpha * jnp.log(egrid / e0) - egrid / Ec, amb * jnp.log(amb * Ec / e0) - amb + beta * jnp.log(egrid / e0), ) return K * jnp.exp(log)
[docs] class BentPL(NumIntAdditive): r"""Bent power law. .. math:: N(E) = 2K \left[ 1 + \left(\frac{E}{E_\mathrm{b}}\right)^\alpha \right]^{-1} Parameters ---------- alpha : Parameter, optional The power law photon index :math:`\alpha`, dimensionless. Eb : Parameter, optional The break energy :math:`E_\mathrm{b}`, in units of keV. K : Parameter, optional The amplitude :math:`K`, in units of ph cm⁻² s⁻¹ keV⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. """ _config = ( ParamConfig('alpha', 'alpha', '', 1.5, -3.0, 10.0), ParamConfig('Eb', r'E_\mathrm{b}', 'keV', 5.0, 1e-2, 1e6), ParamConfig('K', 'K', 'ph cm^-2 s^-1 keV^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: alpha = params['alpha'] Eb = params['Eb'] K = params['K'] return K * 2.0 / (1.0 + jnp.power(egrid / Eb, alpha))
[docs] class Blackbody(NumIntAdditive): r"""Blackbody function. .. math:: N(E) = \frac{C K E^2}{(kT)^4 [\exp(E/kT)-1]}, where :math:`C=8.0525` ph. Parameters ---------- kT : Parameter, optional The temperature :math:`kT`, in units of keV. K : Parameter, optional The amplitude :math:`K = L_{39}/D_{10}^2`, where :math:`L_{39}` is the source luminosity in units of 10³⁹ erg s⁻¹ and :math:`D_{10}` is the distance to the source in units of 10 kpc. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. """ _config = ( ParamConfig('kT', 'kT', 'keV', 3.0, 1e-4, 200.0), ParamConfig('K', 'K', '10^37 erg s^-1 kpc^-2', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: kT = params['kT'] K = params['K'] x = egrid / kT tmp = 8.0525 * K * egrid / (kT * kT * kT) x_ = jnp.where( jnp.greater_equal(x, 50.0), 1.0, # avoid exponential overflow x, ) return jnp.where( jnp.less_equal(x, 1e-4), tmp, jnp.where( jnp.greater_equal(x, 50.0), 0.0, # avoid exponential overflow tmp * x / jnp.expm1(x_), ), )
# return 8.0525 * K * e*e / (kT*kT*kT*kT * jnp.expm1(e / kT))
[docs] class BlackbodyRad(NumIntAdditive): r"""Blackbody function with normalization proportional to the surface area. .. math:: N(E) = \frac{C K E^2}{\exp(E/kT)-1}, where :math:`C=1.0344 \times 10^{-3}` ph cm⁻² s⁻¹ keV⁻³. Parameters ---------- kT : Parameter, optional The temperature :math:`kT`, in units of keV. K : Parameter, optional The amplitude :math:`K = R_\mathrm{km}^2/D_{10}^2`, where :math:`R_\mathrm{km}` is the source radius in km and :math:`D_{10}` is the distance to the source in units of 10 kpc. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. """ _config = ( ParamConfig('kT', 'kT', 'keV', 3.0, 1e-4, 200.0), ParamConfig('K', 'K', '', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: kT = params['kT'] K = params['K'] x = egrid / kT tmp = 1.0344e-3 * K * egrid x_ = jnp.where( jnp.greater_equal(x, 50.0), 1.0, # avoid exponential overflow x, ) return jnp.where( jnp.less_equal(x, 1e-4), tmp * kT, jnp.where( jnp.greater_equal(x, 50.0), 0.0, # avoid exponential overflow tmp * egrid / jnp.expm1(x_), ), )
# return 1.0344e-3 * K * e*e / jnp.expm1(e / kT)
[docs] class BrokenPL(AnaIntAdditive): r"""Broken power law. .. math:: N(E) = K \begin{cases} \left(\frac{E}{E_\mathrm{b}}\right)^{-\alpha_1}, &\text{if } E \le E_\mathrm{b}, \\\\ \left(\frac{E}{E_\mathrm{b}}\right)^{-\alpha_2}, &\text{otherwise}. \end{cases} Parameters ---------- alpha1 : Parameter, optional The low-energy power law photon index :math:`\alpha_1`, dimensionless. alpha2 : Parameter, optional The high-energy power law photon index :math:`\alpha_2`, dimensionless. Eb : Parameter, optional The break energy :math:`E_\mathrm{b}`, in units of keV. K : Parameter, optional The amplitude :math:`K`, in units of ph cm⁻² s⁻¹ keV⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. """ _config = ( ParamConfig('alpha1', 'alpha_1', '', 1.0, -3.0, 10.0), ParamConfig('Eb', r'E_\mathrm{b}', 'keV', 5.0, 1e-2, 1e6), ParamConfig('alpha2', 'alpha_2', '', 2.0, -3.0, 10.0), ParamConfig('K', 'K', 'ph cm^-2 s^-1 keV^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def integral(egrid: JAXArray, params: NameValMapping) -> JAXArray: alpha1 = params['alpha1'] Eb = params['Eb'] alpha2 = params['alpha2'] K = params['K'] mask = egrid[:-1] <= Eb egrid_ = egrid / Eb p1 = _powerlaw_integral(egrid_, alpha1) p2 = _powerlaw_integral(egrid_, alpha2) f = jnp.where(mask, p1, p2) idx = jnp.flatnonzero(mask, size=egrid.size - 1)[-1] pb1 = _powerlaw_integral(jnp.hstack([egrid_[idx], Eb]), alpha1) pb2 = _powerlaw_integral(jnp.hstack([Eb, egrid_[idx + 1]]), alpha2) f.at[idx].set(pb1[0] + pb2[0]) return K * f
class DoubleBrokenPL(AnaIntAdditive): pass class DoubleSmoothlyBrokenPL(NumIntAdditive): pass
[docs] class CutoffPL(NumIntAdditive): r"""Power law with high-energy exponential cutoff. .. math:: N(E) = K \left(\frac{E}{E_0}\right)^{-\alpha} \exp \left(-\frac{E}{E_\mathrm{c}}\right), where :math:`E_0` is the reference energy fixed at 1 keV. Parameters ---------- alpha : Parameter, optional The power law photon index :math:`\alpha`, dimensionless. Ec : Parameter, optional The e-folding energy of exponential cutoff :math:`E_\mathrm{c}`, in units of keV. K : Parameter, optional The amplitude :math:`K`, in units of ph cm⁻² s⁻¹ keV⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. """ _config = ( ParamConfig('alpha', r'\alpha', '', 1.0, -3.0, 10.0), ParamConfig('Ec', r'E_\mathrm{c}', 'keV', 15.0, 0.01, 1e4), ParamConfig('K', 'K', 'ph cm^-2 s^-1 keV^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: alpha = params['alpha'] Ec = params['Ec'] K = params['K'] return K * jnp.power(egrid, -alpha) * jnp.exp(-egrid / Ec)
[docs] class Compt(NumIntAdditive): r"""Power law with high-energy exponential cutoff, parametrized by the peak of :math:`\nu F_\nu`. .. math:: N(E) = K \left(\frac{E}{E_0}\right)^{-\alpha} \exp \left[-\frac{(2-\alpha)E}{E_\mathrm{p}}\right], where :math:`E_0` is the reference energy fixed at 1 keV. Parameters ---------- alpha : Parameter, optional The power law photon index :math:`\alpha`, dimensionless. Ep : Parameter, optional The peak energy :math:`E_\mathrm{p}` of :math:`\nu F_\nu`, in units of keV. K : Parameter, optional The amplitude :math:`K`, in units of ph cm⁻² s⁻¹ keV⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. """ _config = ( ParamConfig('alpha', r'\alpha', '', 1.0, -3.0, 10.0), ParamConfig('Ep', r'E_\mathrm{p}', 'keV', 15.0, 0.01, 1e4), ParamConfig('K', 'K', 'ph cm^-2 s^-1 keV^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: alpha = params['alpha'] Ep = params['Ep'] K = params['K'] neg_inv_Ec = (alpha - 2.0) / Ep return K * jnp.power(egrid, -alpha) * jnp.exp(egrid * neg_inv_Ec)
[docs] class Gauss(NumIntAdditive): r"""Gaussian line profile. .. math:: N(E) = \frac{K}{\sqrt{2\pi} \sigma} \exp\left[ -\frac{\left(E - E_\mathrm{l}\right)^2}{2 \sigma^2} \right]. Parameters ---------- El : Parameter, optional The line energy :math:`E_\mathrm{l}`, in units of keV. sigma : Parameter, optional The line width :math:`\sigma`, in units of keV. K : Parameter, optional The total photon flux :math:`K` of the line, in units of ph cm⁻² s⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. """ _config = ( ParamConfig('El', r'E_\mathrm{l}', 'keV', 6.5, 0.0, 1e6), ParamConfig('sigma', r'\sigma', 'keV', 0.1, 0.0, 20), ParamConfig('K', 'K', 'ph cm^-2 s^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: El = params['El'] sigma = params['sigma'] K = params['K'] return K * stats.norm.pdf(x=egrid, loc=El, scale=sigma)
class LogParabola(NumIntAdditive): pass
[docs] class Lorentz(AnaIntAdditive): r"""Lorentzian line profile. .. math:: N(E) = \frac{K/\mathcal{F}}{(E - E_\mathrm{l})^2 + (\sigma/2)^2}, where .. math:: \mathcal{F}=\int_0^{+\infty} \frac{1}{(E - E_\mathrm{l})^2 + (\sigma/2)^2} \ \mathrm{d}E. Parameters ---------- El : Parameter, optional The line energy :math:`E_\mathrm{l}`, in units of keV. sigma : Parameter, optional The FWHM line width :math:`\sigma`, in units of keV. K : Parameter, optional The total photon flux :math:`K` of the line, in units of ph cm⁻² s⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. """ _config = ( ParamConfig('El', r'E_\mathrm{l}', 'keV', 6.5, 1e-6, 1e6), ParamConfig('sigma', r'\sigma', 'keV', 0.1, 1e-3, 20), ParamConfig('K', 'K', 'ph cm^-2 s^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def integral(egrid: JAXArray, params: NameValMapping) -> JAXArray: El = params['El'] sigma = params['sigma'] K = params['K'] x = 2.0 * (egrid - El) / sigma integral = jnp.arctan(x) norm = 2.0 * K / (jnp.pi + 2 * jnp.arctan(2 * El / sigma)) return norm * (integral[1:] - integral[:-1])
[docs] class OTTB(NumIntAdditive): r"""Optically-thin thermal bremsstrahlung. .. math:: N(E) = K \left(\frac{E}{E_0}\right)^{-1} \exp\left(-\frac{E}{kT}\right) \exp\left(\frac{E_0}{kT}\right), where :math:`E_0` is the reference energy fixed at 1 keV. Parameters ---------- kT : Parameter, optional The electron energy :math:`kT`, in units of keV. K : Parameter, optional The amplitude :math:`K`, in units of ph cm⁻² s⁻¹ keV⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. """ _config = ( ParamConfig('kT', 'kT', 'keV', 30.0, 0.1, 1e3), ParamConfig('K', 'K', 'ph cm^-2 s^-1 keV^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: kT = params['kT'] K = params['K'] return K * jnp.exp((1.0 - egrid) / kT) / egrid
[docs] class OTTS(NumIntAdditive): r"""Optically-thin thermal synchrotron [1]_. .. math:: N(E) = K \exp\left[-\left(\frac{E}{E_\mathrm{c}}\right)^{1/3}\right]. Parameters ---------- Ec : Parameter, optional The energy scale :math:`E_\mathrm{c}`, in units of keV. K : Parameter, optional The amplitude :math:`K`, in units of ph cm⁻² s⁻¹ keV⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. References ---------- .. [1] `Liang, E. P., et al., 1983, ApJ, 271, 776 <https://adsabs.harvard.edu/full/1983ApJ...271..766L>`__ """ _config = ( ParamConfig('Ec', r'E_\mathrm{c}', 'keV', 100.0, 1e-3, 1e3), ParamConfig('K', 'K', 'ph cm^-2 s^-1 keV^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: Ec = params['Ec'] K = params['K'] return K * jnp.exp(-jnp.power(egrid / Ec, 1.0 / 3.0))
[docs] class PowerLaw(AnaIntAdditive): r"""Power law function. .. math:: N(E) = K \left(\frac{E}{E_0}\right)^{-\alpha}, where :math:`E_0` is the reference energy fixed at 1 keV. Parameters ---------- alpha : Parameter, optional Power law photon index :math:`\alpha`, dimensionless. K : Parameter, optional Amplitude :math:`K`, in units of ph cm⁻² s⁻¹ keV⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. """ _config = ( ParamConfig('alpha', r'\alpha', '', 1.01, -3.0, 10.0), ParamConfig('K', 'K', 'ph cm^-2 s^-1 keV^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def integral(egrid: JAXArray, params: NameValMapping) -> JAXArray: return params['K'] * _powerlaw_integral(egrid, params['alpha'])
class PLFluxNorm(AnaIntAdditive): _args = ('emin', 'emax') _energy: bool def __init__( self, emin: float | int, emax: float | int, params: dict, latex: str | None, ): emin = float(emin) emax = float(emax) if emin >= emax: raise ValueError('emin must be less than emax') self._emin = emin self._emax = emax super().__init__(params, latex) @property def eval(self) -> CompEval: if self._integral_jit is None: emin = self._emin emax = self._emax energy = self._energy fn = jax.jit(self.integral) def integral(egrid: JAXArray, params: NameValMapping) -> JAXArray: if energy: keV_to_erg = 1.602176634e-9 params_ = {'alpha': params['alpha'] - 1.0} f = fn(jnp.array([emin, emax]), params_) factor = params['F'] / (f * keV_to_erg) else: f = fn(jnp.array([emin, emax]), params) factor = params['F'] / f return factor * fn(egrid, params) self._integral_jit = jax.jit(integral) return self._integral_jit @staticmethod def integral(egrid: JAXArray, params: NameValMapping) -> JAXArray: return _powerlaw_integral(egrid, params['alpha'])
[docs] class PLPhFlux(PLFluxNorm): r"""Power law function with photon flux used as normalization. .. math:: N(E) &= \mathcal{F}_\mathrm{ph} \left[ \int_{E_\mathrm{min}}^{E_\mathrm{max}} \left(\frac{E}{E_0}\right)^{-\alpha} \, \mathrm{d}E \right]^{-1} \left(\frac{E}{E_0}\right)^{-\alpha}\\ &= \mathcal{F}_\mathrm{ph} (1 - \alpha) \left[ \left(\frac{E_\mathrm{max}}{E_0}\right)^{1 - \alpha} - \left(\frac{E_\mathrm{min}}{E_0}\right)^{1 - \alpha} \right]^{-1} \left(\frac{E}{E_0}\right)^{-\alpha}, where :math:`E_0` is the reference energy fixed at 1 keV. Parameters ---------- emin : float or int Minimum energy of the band to calculate the flux, in units of keV. emax : float or int Maximum energy of the band to calculate the flux, in units of keV. alpha : Parameter, optional Power law photon index :math:`\alpha`, dimensionless. F : Parameter, optional Photon flux :math:`\mathcal{F}_\mathrm{ph}`, in units of ph cm⁻² s⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. """ _energy: bool = False _config = ( ParamConfig('alpha', r'\alpha', '', 1.01, -3.0, 10.0), ParamConfig( 'F', r'\mathcal{F}_\mathrm{ph}', 'ph cm^-2 s^-1', 1.0, 0.01, 1e10 ), )
[docs] class PLEnFlux(PLFluxNorm): r"""Power law function with energy flux used as normalization. .. math:: N(E) &= \mathcal{F}_\mathrm{en} \left[ \int_{E_\mathrm{min}}^{E_\mathrm{max}} \left(\frac{E}{E_0}\right)^{-\alpha} \, E \, \mathrm{d}E \right]^{-1} \left(\frac{E}{E_0}\right)^{-\alpha}\\ &= \mathcal{F}_\mathrm{en} (2 - \alpha) \left[ \left(\frac{E_\mathrm{max}}{E_0}\right)^{2 - \alpha} - \left(\frac{E_\mathrm{min}}{E_0}\right)^{2 - \alpha} \right]^{-1} \left(\frac{E}{E_0}\right)^{-\alpha}, where :math:`E_0` is the reference energy fixed at 1 keV. Parameters ---------- emin : float or int Minimum energy of the band to calculate the flux, in units of keV. emax : float or int Maximum energy of the band to calculate the flux, in units of keV. alpha : Parameter, optional Power law photon index :math:`\alpha`, dimensionless. F : Parameter, optional Energy flux :math:`\mathcal{F}_\mathrm{en}`, in units of erg cm⁻² s⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. """ _energy: bool = True _config = ( ParamConfig('alpha', r'\alpha', '', 1.01, -3.0, 10.0), ParamConfig( 'F', r'\mathcal{F}_\mathrm{en}', 'erg cm^-2 s^-1', 1e-12, 1e-30, 1e30, log=True, ), )
[docs] class SmoothlyBrokenPL(NumIntAdditive): r"""Smoothly broken power law. .. math:: N(E) = K \left( \frac{E}{E_0} \right) ^ {-\alpha_1} \left\{ 2\left[ 1 + \left(\frac{E}{E_\mathrm{b}} \right)^{\left( \alpha_2 - \alpha_1 \right) / \rho} \right] \right\}^{-\rho}, where :math:`E_0` is the reference energy fixed at 1 keV. Parameters ---------- alpha1 : Parameter, optional The low-energy power law index :math:`\alpha_1`, dimensionless. Eb : Parameter, optional The break energy :math:`E_\mathrm{b}`, in units of keV. alpha2 : Parameter, optional The high-energy power law index :math:`\alpha_2`, dimensionless. rho : Parameter, optional The smoothness parameter :math:`\rho`, dimensionless. K : Parameter, optional The amplitude :math:`K`, in units of ph cm⁻² s⁻¹ keV⁻¹. latex : str, optional :math:`\LaTeX` format of the component. Defaults to class name. method : {'trapz', 'simpson'}, optional Numerical integration method. Defaults to 'trapz'. """ _config = ( ParamConfig('alpha1', 'alpha_1', '', 1.0, -3.0, 10.0), ParamConfig('Eb', r'E_\mathrm{b}', 'keV', 5.0, 1e-2, 1e6), ParamConfig('alpha2', 'alpha_2', '', 2.0, -3.0, 10.0), ParamConfig('rho', r'\rho', '', 1.0, 1e-2, 1e2, fixed=True), ParamConfig('K', 'K', 'ph cm^-2 s^-1 keV^-1', 1.0, 1e-10, 1e10), )
[docs] @staticmethod def continuum(egrid: JAXArray, params: NameValMapping) -> JAXArray: alpha1 = params['alpha1'] Eb = params['Eb'] alpha2 = params['alpha2'] rho = params['rho'] K = params['K'] e = egrid / Eb x = (alpha2 - alpha1) / rho * jnp.log(e) threshold = 30 alpha = jnp.where(x > threshold, alpha2, alpha1) r = jnp.where(jnp.abs(x) > threshold, 0.5, 0.5 * (1.0 + jnp.exp(x))) return K * jnp.power(e, -alpha) * jnp.power(r, -rho)