Source code for uvex_transients.models.supernovae.villar

r"""Supernova SED models from the Villar et al. (2019) parametric light curve."""

from typing import ClassVar

from astropy import units as u

from uvex_transients.models._typing import CGSParameterValue, FloatArray
from uvex_transients.models._util_functions import cooling_temperature_cgs
from uvex_transients.models.core.base import SpectralModel
from uvex_transients.models.core.parameters import Parameter
from uvex_transients.models.core.priors import LogNormalPrior
from uvex_transients.models.lightcurves.generic import VillarLightcurve
from uvex_transients.models.spectra.thermal import BlackbodySpectrum

__all__ = [
    "VillarCoolingBlackbodySED",
]


[docs] class VillarCoolingBlackbodySED(SpectralModel): r""" Supernova light curve with an evolving blackbody photosphere. This model combines the phenomenological bolometric light curve of :class:`~uvex_transients.models.lightcurves.generic.VillarLightcurve` (the parametric form introduced by :footcite:p:`2019ApJ...884...83V`) with a time-dependent blackbody spectral shape, .. math:: L_\nu(\nu, t) = L_\mathrm{bol}(t)\, S_\mathrm{BB}\!\left[\nu, T(t)\right], where :math:`S_\mathrm{BB}` is the normalized blackbody spectrum provided by :class:`~uvex_transients.models.spectra.thermal.BlackbodySpectrum`. The photospheric temperature evolves according to .. math:: T(t) = T_\mathrm{floor} + \left(T_0-T_\mathrm{floor}\right) \left(1+\frac{t}{\tau_T}\right)^{-\alpha_T}. Thus, the temperature begins at :math:`T_0` at :math:`t=0` and approaches :math:`T_\mathrm{floor}` asymptotically at late times. The parameters :math:`\tau_T` and :math:`\alpha_T` control the characteristic cooling timescale and the rate of the decline, respectively. Because the blackbody shape is normalized independently of luminosity, the bolometric and spectral components remain exactly separable: .. math:: \int L_\nu(\nu,t)\,d\nu = L_\mathrm{bol}(t). Consequently, :meth:`_eval_bolometric` delegates directly to :class:`~uvex_transients.models.lightcurves.generic.VillarLightcurve`, while :meth:`_eval_spectrum` evaluates :class:`~uvex_transients.models.spectra.thermal.BlackbodySpectrum` at the time-dependent temperature :math:`T(t)`. No numerical frequency integration is required to recover the bolometric luminosity. The model is intended as a generic phenomenological description of supernova-like transients whose continuum can be approximated by a cooling photosphere. It does not attempt to model photospheric-radius evolution, line blanketing, recombination physics, nebular emission, or detailed radiative transfer. .. rubric:: Parameters The model parameters are summarized below. .. list-table:: :header-rows: 1 :widths: 18 18 64 * - Parameter - Symbol - Description * - ``amplitude`` - :math:`A` - Overall luminosity normalization. * - ``t0`` - :math:`t_0` - Reference time at which the logistic rise is centered. * - ``gamma`` - :math:`\gamma` - Duration of the plateau, measured from t0. * - ``beta`` - :math:`\beta` - Linear slope of the plateau. * - ``tau_rise`` - :math:`\tau_\mathrm{rise}` - Logistic rise timescale. * - ``tau_fall`` - :math:`\tau_\mathrm{fall}` - Exponential decline timescale, after the plateau. * - ``T0`` - :math:`T_0` - Photospheric temperature at t=0 (the T(t) -> T0 limit, not literally T at peak). * - ``T_floor`` - :math:`T_\mathrm{floor}` - Asymptotic late-time photospheric temperature (T(t) -> T_floor as t -> infinity). * - ``tau_T`` - :math:`\tau_T` - Photospheric cooling timescale. * - ``alpha_T`` - :math:`\alpha_T` - Photospheric cooling power-law index. Notes ----- The default parameter distributions are broad, phenomenological starting points rather than subtype-specific empirical priors. Subclasses may define only the entries of :attr:`_DEFAULT_PARAMETERS` they wish to modify. Their parameter definitions are merged with the complete parameter set of ``VillarCoolingBlackbodySED`` during subclass creation. This makes it straightforward to define supernova-subtype variants by changing only the parameters whose distributions differ between populations. The temperature law assumes :math:`T_0 > T_\mathrm{floor}` for a cooling photosphere, although this ordering is not enforced by the model itself. References ---------- .. footbibliography:: """ _DEFAULT_PARAMETERS: ClassVar[dict[str, Parameter]] = { **VillarLightcurve._DEFAULT_PARAMETERS, "T0": Parameter( prior=LogNormalPrior(mean=0.0, sigma=0.3), scale=1.2e4 * u.K, description="Photospheric temperature at t=0 (the T(t) -> T0 limit, not literally T at peak).", latex=r"T_0", ), "T_floor": Parameter( prior=LogNormalPrior(mean=0.0, sigma=0.3), scale=6e3 * u.K, description="Asymptotic late-time photospheric temperature (T(t) -> T_floor as t -> infinity).", latex=r"T_\mathrm{floor}", ), "tau_T": Parameter( prior=LogNormalPrior(mean=0.0, sigma=0.5), scale=15.0 * u.day, description="Photospheric cooling timescale.", latex=r"\tau_T", ), "alpha_T": Parameter( prior=LogNormalPrior(mean=0.0, sigma=0.3), scale=1.5, description="Photospheric cooling power-law index.", latex=r"\alpha_T", ), } # -------------------------------------- # # Subclass Merging # # -------------------------------------- # def __init_subclass__(cls, **kwargs) -> None: """ Merge a subtype subclass's own `_DEFAULT_PARAMETERS` on top of this class's full parameter set. Parameters ---------- **kwargs Forwarded to :meth:`object.__init_subclass__` unchanged; this class declares no class-keyword-argument options of its own. """ super().__init_subclass__(**kwargs) own = cls.__dict__.get("_DEFAULT_PARAMETERS") if own is not None: cls._DEFAULT_PARAMETERS = { **VillarCoolingBlackbodySED._DEFAULT_PARAMETERS, **own, } # -------------------------------------- # # Cooling Law: T(t) # # -------------------------------------- # @classmethod def _temperature_cgs( cls, t: FloatArray, *, T0: CGSParameterValue, T_floor: CGSParameterValue, tau_T: CGSParameterValue, alpha_T: CGSParameterValue, **_ignored: CGSParameterValue, ) -> FloatArray: r""" :math:`T(t) = T_\mathrm{floor} + (T_0 - T_\mathrm{floor})(1 + t/\tau_T)^{-\alpha_T}`. Parameters ---------- t : numpy.ndarray Time since explosion, in seconds. T0, T_floor, tau_T, alpha_T : float or numpy.ndarray This model's parameter values, in cgs units; see the class docstring. **_ignored Any other model parameter values, ignored. Returns ------- numpy.ndarray :math:`T(t)`, in Kelvin. """ return cooling_temperature_cgs(t, T0=T0, T_floor=T_floor, timescale=tau_T, alpha=alpha_T) # -------------------------------------- # # Bolometric Luminosity: L_bol(t) # # -------------------------------------- # @classmethod def _eval_bolometric(cls, t: FloatArray, **parameters: CGSParameterValue) -> FloatArray: r""" :math:`\log L_\mathrm{bol}(t)`, delegated directly to :class:`VillarLightcurve`. Exact -- no integration needed. Parameters ---------- t : numpy.ndarray Time since explosion, in seconds. **parameters This model's parameter values, in cgs units. Returns ------- numpy.ndarray The natural log of :math:`L_\mathrm{bol}(t)`, in erg/s. """ lightcurve_parameters = {name: parameters[name] for name in VillarLightcurve._DEFAULT_PARAMETERS} return VillarLightcurve._eval(t, **lightcurve_parameters) # -------------------------------------- # # Normalized Spectral Shape: S(nu, t) # # -------------------------------------- # @classmethod def _eval_spectrum(cls, nu: FloatArray, t: FloatArray, **parameters: CGSParameterValue) -> FloatArray: r""" :math:`\log S(\nu, T(t))`, delegated to :class:`BlackbodySpectrum`. Evaluated at this ``t``'s own cooling-law temperature. Parameters ---------- nu : numpy.ndarray Frequency, in Hz. t : numpy.ndarray Time since explosion, in seconds. **parameters This model's parameter values, in cgs units. Returns ------- numpy.ndarray The natural log of the normalized spectral shape, in 1/Hz. """ temperature = cls._temperature_cgs(t, **parameters) return BlackbodySpectrum._eval(nu, temperature=temperature) # -------------------------------------- # # Spectral Luminosity: L_nu(nu, t) # # -------------------------------------- # @classmethod def _eval(cls, nu: FloatArray, t: FloatArray, **parameters: CGSParameterValue) -> FloatArray: r""" :math:`\log L_\nu(\nu, t) = \log L_\mathrm{bol}(t) + \log S(\nu, T(t))`. Parameters ---------- nu : numpy.ndarray Frequency, in Hz. t : numpy.ndarray Time since explosion, in seconds. **parameters This model's parameter values, in cgs units. Returns ------- numpy.ndarray The natural log of :math:`L_\nu(\nu, t)`, in erg/s/Hz. """ return cls._eval_bolometric(t, **parameters) + cls._eval_spectrum(nu, t, **parameters)