"""
This module provides the `SNeIa` class, a pre-defined Transient class.
It means the basic ``_MODEL`` functionality has been defined.
See the corresponding documentation page in "List of transient classes" for more detail on this Transient class.
The `SNeIa` is defined as such:
.. code-block:: python
:caption: The SNeIa class.
class SNeIa( Transient ):
_KIND = "SNIa"
_TEMPLATE_SOURCE = "salt2"
_VOLUME_RATE = 2.35 * 10**4 # Perley 2020
_MODEL = dict( redshift ={"param":{"zmax":0.2}, "as":"z"},
x1={"model":"nicolas2021"},
c={"model":"intrinsic_and_dust"},
t0={"model":"uniform",
"param":{"mjd_range":[59000, 59000+365*4]} },
magabs={"model":"tripp1998",
"input":["x1","c"]
},
magobs={"model":"magabs_to_magobs",
"input":["z", "magabs"]},
x0={"model":"magobs_to_x0",
"input":["magobs"]},
radec={"model":"random",
"param":dict(ra_range=[0, 360],
dec_range=[-30, 90]),
"as":["ra","dec"]}
)
Pre-defined models for the `SNeIa` parameters (color, stretch, magnitude) are also defined:
"""
import numpy as np
from scipy import stats
from .core import Transient
from ..tools.utils import random_radec
RNG = np.random.default_rng()
# ================== #
# #
# Pre-Defined models #
# #
# ================== #
[docs]
class SNeIaColor( object ):
"""A class to model the color of SNe Ia."""
[docs]
@staticmethod
def color_rvs(size, a=3.63, loc=-0.416, scale=1.62):
"""Draw random variates from an alpha function.
This is used to model the color of SNe Ia.
Parameters
----------
size : int
The number of random variates to draw.
a : float, optional
The alpha parameter of the alpha function. The default is 3.63.
loc : float, optional
The location parameter of the alpha function. The default is -0.416.
scale : float, optional
The scale parameter of the alpha function. The default is 1.62.
Returns
-------
ndarray
The drawn random variates.
"""
return stats.alpha.rvs(size=size, a=a, loc=loc, scale=scale)
[docs]
@staticmethod
def asymetric_gaussian(xx="-0.3:1:0.001", cint=-0.05, sigmalow=0.03, sigmahigh=0.1):
"""Get an asymetric gaussian distribution.
As in Scolnic and Kessler 2016 (https://arxiv.org/pdf/1603.01559).
Parameters
----------
xx : str, optional
The x-axis of the color distribution. It should be a string with
the format "min:max:step". The default is "-0.3:1:0.01".
This is evaluated as `np.r_[xx]`.
cint : float, optional
The mean of the intrinsic color distribution. The default is -0.05.
sigmalow : float, optional
The standard deviation for the bluer tails. The default is 0.03.
sigmahigh : float, optional
The standard deviation for the redder tails. The default is 0.1.
Returns
-------
tuple
A tuple containing the x-axis and the pdf.
"""
if type(xx) is str: # assumed r_ input
xx = eval(f"np.r_[{xx}]")
from scipy import stats
# full blue
pdf = stats.norm.pdf(xx, loc=cint, scale=sigmalow)
redder = (xx>=cint)
pdf[redder] = stats.norm.pdf(xx[redder], loc=cint, scale=sigmahigh)
return xx, pdf
[docs]
@staticmethod
def intrinsic_and_dust(xx="-0.3:1:0.001", cint=-0.075, sigmaint=0.05, tau=0.14):
"""Get an exponential decay convolved with and intrinsic gaussian color distribution.
As in Ginolin et al. 2024 (https://arxiv.org/pdf/2406.02072).
Parameters
----------
xx : str, optional
The x-axis of the color distribution. It should be a string with
the format "min:max:step". The default is "-0.3:1:0.01".
This is evaluated as `np.r_[xx]`.
cint : float, optional
The mean of the intrinsic color distribution. The default is -0.075.
sigmaint : float, optional
The standard deviation of the intrinsic color distribution.
The default is 0.05.
tau : float, optional
The decay constant of the dust distribution. The default is 0.14.
Returns
-------
tuple
A tuple containing the x-axis and the pdf.
"""
if type(xx) is str: # assumed r_ input
xx = eval(f"np.r_[{xx}]")
from scipy import stats
from scipy.ndimage import gaussian_filter1d
# exponential decay center on cint
expon = stats.expon.pdf(xx, loc=cint, scale=tau)
# applying gaussian filtering
# - which require sigmaint in pixel.
sigmaint_inpix = sigmaint/(xx[1]-xx[0]) # assuming constant step
pdf = gaussian_filter1d(expon, sigmaint_inpix)
return xx, pdf
[docs]
class SNeIaStretch( object ):
"""A class to model the stretch of SNe Ia."""
[docs]
@staticmethod
def nicolas2021( xx="-4:4:0.005",
mu1=0.33, sigma1=0.64,
mu2=-1.50, sigma2=0.58, a=0.45,
fprompt=0.5):
"""Get the pdf of the Nicolas (2021) model.
Parameters
----------
xx : str or array, optional
Definition range for the parameters. Draws will be done from this
array given the pdf that will be estimated for it. If a string is
given, it is evaluated as `np.r_[xx]`. The default is
"-4:4:0.005".
mu1 : float, optional
The mean of the first gaussian. The default is 0.33.
sigma1 : float, optional
The standard deviation of the first gaussian. The default is 0.64.
mu2 : float, optional
The mean of the second gaussian. The default is -1.50.
sigma2 : float, optional
The standard deviation of the second gaussian. The default is 0.58.
a : float, optional
The relative influence of both modes (1 or 2) in the delayed
environment. `a>0.5` means more mode 1. The default is 0.45.
fprompt : float, optional
The fraction of prompt SNe Ia. This is ignored if `redshift` is
given. The default is 0.5.
Returns
-------
tuple
A tuple containing the x-axis and the pdf.
"""
from scipy.stats import norm
if type(xx) is str: # assumed r_ input
xx = eval(f"np.r_[{xx}]")
mode1 = norm.pdf(xx, loc=mu1, scale=sigma1)
mode2 = norm.pdf(xx, loc=mu2, scale=sigma2)
if type(fprompt) is not float:
fprompt = np.asarray(fprompt)[:,None]
pdf = fprompt*mode1 + (1-fprompt)*(a*mode1 + (1-a)*mode2)
return xx, pdf
[docs]
class SNeIaMagnitude( object ):
"""A class to model the magnitude of SNe Ia."""
[docs]
@staticmethod
def tripp1998( x1, c,
mabs=-19.3, sigmaint=0.10,
alpha=-0.14, beta=3.15, rng=None):
"""Get the 2-parameter absolute (natural) SNe Ia magnitude.
Parameters
----------
x1 : array
The lightcurve stretch. `x1` and `c` must have the same size.
c : array
The lightcurve color. `x1` and `c` must have the same size.
mabs : float, optional
The average absolute magnitude at `c=0` and `x1=0`. The default is
-19.3.
sigmaint : float, optional
The scale of the normal grey scatter (on `mabs`). The default is
0.10.
alpha : float, optional
The stretch linear law coefficient. The default is -0.14.
beta : float, optional
The color linear law coeeficient. The default is 3.15.
rng: None, int, Generator
Random number generator seed.
(docstring extracted from `np.random.default_rng()`, see this for complete documentation).
If None, then fresh, unpredictable entropy will be pulled from the OS.
If an ``int``, then the seed will start from this.
If passed a `Generator`, it will be returned unaltered.
Returns
-------
array
The absolute magnitude, with the same format as `x1` and `c`.
"""
rng = np.random.default_rng(rng)
mabs = rng.normal(loc=mabs, scale=sigmaint, size=len(x1))
mabs_notstandard = mabs + (x1*alpha + c*beta)
return mabs_notstandard
[docs]
@classmethod
def tripp_and_step( cls, x1, c, isup,
mabs=-19.3, sigmaint=0.10,
alpha=-0.14, beta=3.15, gamma=0.1,
rng=None):
"""Get the 2-parameter and step absolute (natural) SNe Ia magnitude.
Parameters
----------
x1 : array
The lightcurve stretch. `x1` and `c` must have the same size.
c : array
The lightcurve color. `x1` and `c` must have the same size.
isup : array
An array of 0 or 1, flagging which target has `+gamma/2` (1) or
`-gamma/2` (0).
mabs : float, optional
The average absolute magnitude at `c=0` and `x1=0`. The default is
-19.3.
sigmaint : float, optional
The scale of the normal grey scatter (on `mabs`). The default is
0.10.
alpha : float, optional
The stretch linear law coefficient. The default is -0.14.
beta : float, optional
The color linear law coefficient. The default is 3.15.
gamma : float, optional
The step's amplitude. The default is 0.1.
Returns
-------
array
The absolute magnitude, with the same format as `x1`, `c` and `isup`.
"""
tripp_mabs = cls.tripp1998( x1, c,
mabs=mabs, sigmaint=sigmaint,
alpha=alpha, beta=beta, rng=rng)
return tripp_mabs + (isup-0.5)*gamma # 0 gets -gamma/2 and 1 get +gamma/2
[docs]
@classmethod
def tripp_and_massstep( cls, x1, c, hostmass,
mabs=-19.3, sigmaint=0.10,
alpha=-0.14, beta=3.15,
gamma=0.1, split=10,
rng=None):
"""Get the 2-parameter and mass step absolute (natural) SNe Ia magnitude.
Parameters
----------
x1 : array
The lightcurve stretch. `x1` and `c` must have the same size.
c : array
The lightcurve color. `x1` and `c` must have the same size.
hostmass : array
The host stellar mass.
mabs : float, optional
The average absolute magnitude at `c=0` and `x1=0`. The default is
-19.3.
sigmaint : float, optional
The scale of the normal grey scatter (on `mabs`). The default is
0.10.
alpha : float, optional
The stretch linear law coefficient. The default is -0.14.
beta : float, optional
The color linear law coeeficient. The default is 3.15.
gamma : float, optional
The step's amplitude. The default is 0.1.
split : float, optional
The host mass boundary between low-mass and high-mass hosts.
The default is 10.
Returns
-------
array
The absolute magnitude, with the same format as `x1`, `c` and `isup`.
"""
isup = np.asarray( hostmass>split, dtype=float)
return cls.tripp_and_step( x1, c, isup,
mabs=mabs, sigmaint=sigmaint,
alpha=alpha, beta=beta, gamma=gamma,
rng=rng)
# ================== #
# #
# SNeIa Target #
# #
# ================== #
[docs]
class SNeIa( Transient ):
"""
A class to model SNe Ia.
Parameters
----------
_KIND : str, optional
The kind of transient. The default is "SNIa".
_TEMPLATE : str, optional
The template to use. The default is "salt2".
_RATE : float, optional
The rate of SNe Ia. The default is 2.35 * 10**4.
_AMPLITUDE_NAME : str, optional
The name of the amplitude. The default is "x0"
_MODEL : dict, optional
The model to use. The default is a dictionary with the following
keys:
- `redshift`: The redshift of the SNe Ia.
- `x1`: The stretch of the SNe Ia.
- `c`: The color of the SNe Ia.
- `t0`: The time of maximum of the SNe Ia.
- `magabs`: The absolute magnitude of the SNe Ia.
- `magobs`: The observed magnitude of the SNe Ia.
- `x0`: The amplitude of the SNe Ia.
- `radec`: The ra and dec of the SNe Ia.
"""
_KIND = "SNIa"
_TEMPLATE = "salt2"
_RATE = 2.35 * 10**4 # Perley 2020
_AMPLITUDE_NAME = "x0"
# {'name': {func: ,'kwargs': {}, 'as': str_or_list }}
_MODEL = dict( redshift = {"func": "draw_redshift", # implicit
"kwargs": {"zmax":0.2},
"as":"z"},
x1 = {"func": SNeIaStretch.nicolas2021},
c = {"func": SNeIaColor.intrinsic_and_dust},
t0 = {"func": RNG.uniform,
"kwargs": {"low":56_000, "high":56_200} },
magabs = {"func": SNeIaMagnitude.tripp1998,
"kwargs": {"x1": "@x1", "c": "@c",
"mabs":-19.3, "sigmaint":0.10}
},
magobs = {"func": "magabs_to_magobs", # str-> method of the class
"kwargs": {"z":"@z", "magabs":"@magabs"},
},
radec = {"func": random_radec,
"kwargs": {},
"as": ["ra","dec"]
},
)