redback 1.1__py3-none-any.whl → 1.12.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- redback/__init__.py +1 -1
- redback/filters.py +57 -45
- redback/likelihoods.py +274 -6
- redback/model_library.py +2 -2
- redback/plotting.py +5 -3
- redback/priors/blackbody_spectrum_at_z.prior +3 -0
- redback/priors/bpl_cooling_envelope.prior +9 -0
- redback/priors/gaussianrise_cooling_envelope.prior +1 -5
- redback/priors/gaussianrise_cooling_envelope_bolometric.prior +1 -5
- redback/priors/powerlaw_plus_blackbody.prior +12 -0
- redback/priors/powerlaw_plus_blackbody_spectrum_at_z.prior +13 -0
- redback/priors/salt2.prior +6 -0
- redback/priors/shock_cooling_and_arnett_bolometric.prior +11 -0
- redback/priors/smooth_exponential_powerlaw_cooling_envelope_bolometric.prior +9 -0
- redback/priors/sn_nickel_fallback.prior +9 -0
- redback/priors/wr_bh_merger.prior +10 -0
- redback/priors/wr_bh_merger_bolometric.prior +8 -0
- redback/priors.py +14 -3
- redback/sed.py +185 -41
- redback/simulate_transients.py +13 -3
- redback/tables/filters.csv +260 -258
- redback/tables/qdot_rosswogkorobkin24.npz +0 -0
- redback/transient_models/afterglow_models.py +32 -16
- redback/transient_models/combined_models.py +16 -11
- redback/transient_models/extinction_models.py +310 -84
- redback/transient_models/gaussianprocess_models.py +1 -12
- redback/transient_models/kilonova_models.py +3 -3
- redback/transient_models/phase_models.py +97 -43
- redback/transient_models/phenomenological_models.py +172 -0
- redback/transient_models/spectral_models.py +101 -0
- redback/transient_models/stellar_interaction_models.py +254 -0
- redback/transient_models/supernova_models.py +349 -62
- redback/transient_models/tde_models.py +193 -54
- redback/utils.py +34 -7
- {redback-1.1.dist-info → redback-1.12.1.dist-info}/METADATA +7 -4
- {redback-1.1.dist-info → redback-1.12.1.dist-info}/RECORD +39 -28
- {redback-1.1.dist-info → redback-1.12.1.dist-info}/WHEEL +1 -1
- redback/tables/qdot_rosswogkorobkin24.pck +0 -0
- {redback-1.1.dist-info → redback-1.12.1.dist-info}/licenses/LICENCE.md +0 -0
- {redback-1.1.dist-info → redback-1.12.1.dist-info}/top_level.txt +0 -0
|
@@ -5,8 +5,4 @@ mbh_6 = LogUniform(0.01, 20, name='mbh_6', latex_label = r'$M_{\mathrm{BH}}~(10^
|
|
|
5
5
|
stellar_mass = LogUniform(0.1, 10, name='stellar_mass', latex_label = r'$M_{\mathrm{star}}~(M_\odot)$')
|
|
6
6
|
eta = LogUniform(1e-4, 0.1, name='eta', latex_label=r'$\\eta$')
|
|
7
7
|
alpha = LogUniform(0.1, 1, name='alpha', latex_label=r'$\\alpha$')
|
|
8
|
-
beta = Uniform(1, 5, name='beta', latex_label=r'$\\beta$')
|
|
9
|
-
beta = 0.9
|
|
10
|
-
eta = 0.1
|
|
11
|
-
alpha = 0.1
|
|
12
|
-
mbh_6 = 1
|
|
8
|
+
beta = Uniform(1, 5, name='beta', latex_label=r'$\\beta$')
|
|
@@ -4,8 +4,4 @@ mbh_6 = LogUniform(0.01, 20, name='mbh_6', latex_label = r'$M_{\mathrm{BH}}~(10^
|
|
|
4
4
|
stellar_mass = LogUniform(0.1, 10, name='stellar_mass', latex_label = r'$M_{\mathrm{star}}~(M_\odot)$')
|
|
5
5
|
eta = LogUniform(1e-4, 0.1, name='eta', latex_label=r'$\\eta$')
|
|
6
6
|
alpha = LogUniform(0.1, 1, name='alpha', latex_label=r'$\\alpha$')
|
|
7
|
-
beta = Uniform(1, 5, name='beta', latex_label=r'$\\beta$')
|
|
8
|
-
beta = 0.9
|
|
9
|
-
eta = 0.1
|
|
10
|
-
alpha = 0.1
|
|
11
|
-
mbh_6 = 1
|
|
7
|
+
beta = Uniform(1, 5, name='beta', latex_label=r'$\\beta$')
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
redshift = Uniform(0.01, 1.0, 'redshift', latex_label = r'$z$')
|
|
2
|
+
pl_amplitude = LogUniform(1e-20, 1e-12, 'pl_amplitude', latex_label = r'$A_{\mathrm{pl}}~(\mathrm{erg~s^{-1}~cm^{-2}~\AA^{-1}})$')
|
|
3
|
+
pl_slope = Uniform(-2.0, 3.0, 'pl_slope', latex_label = r'$\\alpha_{\mathrm{pl}}$')
|
|
4
|
+
pl_evolution_index = Uniform(0.3, 3.0, 'pl_evolution_index', latex_label = r'$\\beta_{\mathrm{pl}}$')
|
|
5
|
+
temperature_0 = Uniform(3000, 20000, 'temperature_0', latex_label = r'$T_0~(\mathrm{K})$')
|
|
6
|
+
radius_0 = LogUniform(1e13, 1e17, 'radius_0', latex_label = r'$R_0~(\mathrm{cm})$')
|
|
7
|
+
temp_rise_index = Uniform(0.0, 2.0, 'temp_rise_index', latex_label = r'$\\alpha_{T,\mathrm{rise}}$')
|
|
8
|
+
temp_decline_index = Uniform(0.0, 3.0, 'temp_decline_index', latex_label = r'$\\alpha_{T,\mathrm{dec}}$')
|
|
9
|
+
temp_peak_time = LogUniform(0.1, 10.0, 'temp_peak_time', latex_label = r'$t_{T,\mathrm{peak}}~(\mathrm{days})$')
|
|
10
|
+
radius_rise_index = Uniform(0.0, 2.0, 'radius_rise_index', latex_label = r'$\\alpha_{R,\mathrm{rise}}$')
|
|
11
|
+
radius_decline_index = Uniform(0.0, 2.0, 'radius_decline_index', latex_label = r'$\\alpha_{R,\mathrm{dec}}$')
|
|
12
|
+
radius_peak_time = LogUniform(0.1, 10.0, 'radius_peak_time', latex_label = r'$t_{R,\mathrm{peak}}~(\mathrm{days})$')
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
redshift = Uniform(0.01, 1.0, 'redshift', latex_label = r'$z$')
|
|
2
|
+
pl_amplitude = LogUniform(1e-20, 1e-12, 'pl_amplitude', latex_label = r'$A_{\mathrm{pl}}~(\mathrm{erg~s^{-1}~cm^{-2}~\AA^{-1}})$')
|
|
3
|
+
pl_slope = Uniform(-2.0, 3.0, 'pl_slope', latex_label = r'$\\alpha_{\mathrm{pl}}$')
|
|
4
|
+
pl_evolution_index = Uniform(0.3, 3.0, 'pl_evolution_index', latex_label = r'$\\beta_{\mathrm{pl}}$')
|
|
5
|
+
temperature_0 = Uniform(3000, 20000, 'temperature_0', latex_label = r'$T_0~(\mathrm{K})$')
|
|
6
|
+
radius_0 = LogUniform(1e13, 1e17, 'radius_0', latex_label = r'$R_0~(\mathrm{cm})$')
|
|
7
|
+
temp_rise_index = Uniform(0.0, 2.0, 'temp_rise_index', latex_label = r'$\\alpha_{T,\mathrm{rise}}$')
|
|
8
|
+
temp_decline_index = Uniform(0.0, 3.0, 'temp_decline_index', latex_label = r'$\\alpha_{T,\mathrm{dec}}$')
|
|
9
|
+
temp_peak_time = LogUniform(0.1, 10.0, 'temp_peak_time', latex_label = r'$t_{T,\mathrm{peak}}~(\mathrm{days})$')
|
|
10
|
+
radius_rise_index = Uniform(0.0, 2.0, 'radius_rise_index', latex_label = r'$\\alpha_{R,\mathrm{rise}}$')
|
|
11
|
+
radius_decline_index = Uniform(0.0, 2.0, 'radius_decline_index', latex_label = r'$\\alpha_{R,\mathrm{dec}}$')
|
|
12
|
+
radius_peak_time = LogUniform(0.1, 10.0, 'radius_peak_time', latex_label = r'$t_{R,\mathrm{peak}}~(\mathrm{days})$')
|
|
13
|
+
time = Uniform(0.5, 10.0, 'time', latex_label = r'$t~(\mathrm{days})$')
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
redshift = Uniform(0.1, 0.2, name='redshift', latex_label=r'$z$')
|
|
2
|
+
peak_time = Uniform(10, 20, name='peak_time', latex_label=r'$t_{\mathrm{peak}}~(\mathrm{days})$')
|
|
3
|
+
t0 = Uniform(0, 1, name='t0', latex_label=r'$t_0~(\mathrm{days})$')
|
|
4
|
+
x0 = Uniform(1e-10, 1e-1, name='x0', latex_label=r'$x_0$')
|
|
5
|
+
x1 = Normal(0, 1, name='x1', latex_label=r'$x_1$')
|
|
6
|
+
c = Normal(0, 0.1, name='c', latex_label=r'$c$')
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
log10_mass = Uniform(-2, 3, 'log10_mass', latex_label=r'$\log_{10}~m_{\mathrm{en}} / M_\odot$')
|
|
2
|
+
log10_radius = Uniform(10, 14, 'log10_radius', latex_label=r'$\log_{10}~R_{\mathrm{en}} / {\mathrm{cm}}^{-3}$')
|
|
3
|
+
log10_energy = Uniform(40, 52, 'log10_energy', latex_label=r'$\log_{10}~E_{\mathrm{en}} / {\mathrm{erg}}$')
|
|
4
|
+
nn = Uniform(8, 12, 'nn', latex_label=r'$nn$')
|
|
5
|
+
delta = Uniform(1, 1.5, 'delta', latex_label=r'$\\Delta$')
|
|
6
|
+
f_nickel = LogUniform(1e-3,1,name='f_nickel', latex_label = r'$f_{\mathrm{Ni}}$')
|
|
7
|
+
mej = LogUniform(1e-4, 100, 'mej', latex_label = r'$M_{\mathrm{ej} }~(M_\odot)$')
|
|
8
|
+
vej = LogUniform(1e3, 1e5, 'vej', latex_label = r'$v_{\mathrm{ej}}~(\mathrm{km}/\mathrm{s})$')
|
|
9
|
+
kappa = Uniform(0.05, 2, 'kappa', latex_label = r'$\\kappa~(\mathrm{cm}^{2}/\mathrm{g})$')
|
|
10
|
+
kappa_gamma = LogUniform(1e-4, 1e4, 'kappa_gamma', latex_label = r'$\\kappa_\\gamma~(\mathrm{cm}^{2}/\mathrm{g})$')
|
|
11
|
+
temperature_floor = LogUniform(1e3,1e5,name = 'temperature_floor', latex_label = r'$T_{\mathrm{floor}}~(\mathrm{K})$')
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
peak_time = LogUniform(0.1,60, name='peak_time', latex_label = r'$t_{\mathrm{peak}}~(\mathrm{day})$')
|
|
2
|
+
alpha_1 = Uniform(0.5, 3, name='alpha_1', latex_label=r'$\\alpha_{1}$')
|
|
3
|
+
alpha_2 = Uniform(-3, -0.5, name='alpha_2', latex_label=r'$\\alpha_{2}$')
|
|
4
|
+
smoothing_factor = Uniform(0.5, 2.0, name='smoothing_factor', latex_label=r'$s_{\mathrm{smooth}}$')
|
|
5
|
+
mbh_6 = LogUniform(0.01, 20, name='mbh_6', latex_label = r'$M_{\mathrm{BH}}~(10^{6}~M_\odot)$')
|
|
6
|
+
stellar_mass = LogUniform(0.1, 10, name='stellar_mass', latex_label = r'$M_{\mathrm{star}}~(M_\odot)$')
|
|
7
|
+
eta = LogUniform(1e-4, 0.1, name='eta', latex_label=r'$\\eta$')
|
|
8
|
+
alpha = LogUniform(0.1, 1, name='alpha', latex_label=r'$\\alpha$')
|
|
9
|
+
beta = Uniform(1, 5, name='beta', latex_label=r'$\\beta$')
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
redshift = Uniform(1e-3,3,name='redshift', latex_label = r'$z$')
|
|
2
|
+
logl1 = Uniform(51, 57, name='logl1', latex_label=r'$\log_{10}L_{1} [erg~s^{-1}]$')
|
|
3
|
+
tr = LogUniform(1e-4, 100, name='tr', latex_label=r'T_{r} [day]')
|
|
4
|
+
mej = LogUniform(1e-4, 100, 'mej', latex_label = r'$M_{\mathrm{ej}}~(M_\odot)$')
|
|
5
|
+
f_nickel = LogUniform(1e-3,1,name='f_nickel', latex_label = r'$f_{\mathrm{Ni}}$')
|
|
6
|
+
vej = LogUniform(1e3, 1e5, 'vej', latex_label = r'$v_{\mathrm{ej}}~(\mathrm{km}/\mathrm{s})$')
|
|
7
|
+
kappa = Uniform(0.05, 2, 'kappa', latex_label = r'$\\kappa~(\mathrm{cm}^{2}/\mathrm{g})$')
|
|
8
|
+
kappa_gamma = LogUniform(1e-4, 1e4, 'kappa_gamma', latex_label = r'$\\kappa_{\\gamma}~(\mathrm{cm}^{2}/\mathrm{g})$')
|
|
9
|
+
temperature_floor = LogUniform(1e3,1e5,name = 'temperature_floor', latex_label = r'$T_{\mathrm{floor}}~(\mathrm{K})$')
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
redshift = Uniform(1e-3,3,name='redshift', latex_label = r'$z$')
|
|
2
|
+
M_star = Uniform(5, 50, name='M_star', latex_label = r'$M_{\mathrm{*}}~[M_{\odot}]$')
|
|
3
|
+
M_bh = Uniform(5, 50, name='M_star', latex_label = r'$M_{\mathrm{BH}}~[M_{\odot}]$')
|
|
4
|
+
M_fast = Uniform(0.01, 1, name='M_fast', latex_label = r'$M_{\mathrm{fast}}~[M_{\odot}]$')
|
|
5
|
+
M_pre = Uniform(0.1, 5, name='M_pre', latex_label = r'$M_{\mathrm{pre-SN}}~[M_{\odot}]$')
|
|
6
|
+
v_fast = Uniform(0.05, 0.3, name='v_fast', latex_label = r'$v_{\mathrm{fast}}~[c]$')
|
|
7
|
+
v_slow = LogUniform(1e3, 1e4, 'v_slow', latex_label = r'$v_{\mathrm{slow}}~[\mathrm{km}/\mathrm{s}]$')
|
|
8
|
+
eta = LogUniform(1e-4, 0.1, name='eta', latex_label=r'$\\eta$')
|
|
9
|
+
alpha = LogUniform(0.1, 1, name='alpha', latex_label=r'$\\alpha$')
|
|
10
|
+
temperature_floor = LogUniform(1e3,1e4,name = 'temperature_floor', latex_label = r'$T_{\mathrm{floor}}~(\mathrm{K})$')
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
M_star = Uniform(5, 50, name='M_star', latex_label = r'$M_{\mathrm{*}}~[M_{\odot}]$')
|
|
2
|
+
M_bh = Uniform(5, 50, name='M_star', latex_label = r'$M_{\mathrm{BH}}~[M_{\odot}]$')
|
|
3
|
+
M_fast = Uniform(0.01, 1, name='M_fast', latex_label = r'$M_{\mathrm{fast}}~[M_{\odot}]$')
|
|
4
|
+
M_pre = Uniform(0.1, 5, name='M_pre', latex_label = r'$M_{\mathrm{pre-SN}}~[M_{\odot}]$')
|
|
5
|
+
v_fast = Uniform(0.05, 0.3, name='v_fast', latex_label = r'$v_{\mathrm{fast}}~[c]$')
|
|
6
|
+
v_slow = LogUniform(1e3, 1e4, 'v_slow', latex_label = r'$v_{\mathrm{slow}}~[\mathrm{km}/\mathrm{s}]$')
|
|
7
|
+
eta = LogUniform(1e-4, 0.1, name='eta', latex_label=r'$\\eta$')
|
|
8
|
+
alpha = LogUniform(0.1, 1, name='alpha', latex_label=r'$\\alpha$')
|
redback/priors.py
CHANGED
|
@@ -45,14 +45,25 @@ def get_priors(model, times=None, y=None, yerr=None, dt=None, **kwargs):
|
|
|
45
45
|
logger.info(f'Setting up prior for base model {model}.')
|
|
46
46
|
logger.info(f'You will need to explicitly set a prior on t0 and or extinction if relevant')
|
|
47
47
|
|
|
48
|
+
# Try loading from main priors folder first
|
|
48
49
|
try:
|
|
49
50
|
filename = os.path.join(os.path.dirname(__file__), 'priors', f'{model}.prior')
|
|
50
51
|
priors.from_file(filename)
|
|
52
|
+
return priors
|
|
51
53
|
except FileNotFoundError:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
pass # Continue to try the non_default_priors folder
|
|
55
|
+
|
|
56
|
+
# Try loading from non_default_priors subfolder
|
|
57
|
+
try:
|
|
58
|
+
filename = os.path.join(os.path.dirname(__file__), 'priors', 'non_default_priors', f'{model}.prior')
|
|
59
|
+
priors.from_file(filename)
|
|
60
|
+
return priors
|
|
61
|
+
except FileNotFoundError:
|
|
62
|
+
logger.warning(f'No prior file found for model {model} in either priors or non_default_priors folders. '
|
|
63
|
+
f'Perhaps you also want to set up the prior for the base model? '
|
|
64
|
+
f'Or you may need to set up your prior explicitly.')
|
|
55
65
|
logger.info('Returning Empty PriorDict.')
|
|
66
|
+
|
|
56
67
|
return priors
|
|
57
68
|
|
|
58
69
|
|
redback/sed.py
CHANGED
|
@@ -6,6 +6,7 @@ from sncosmo import TimeSeriesSource
|
|
|
6
6
|
from redback.constants import *
|
|
7
7
|
from redback.utils import nu_to_lambda, bandpass_magnitude_to_flux
|
|
8
8
|
|
|
9
|
+
|
|
9
10
|
def _bandflux_single_redback(model, band, time_or_phase):
|
|
10
11
|
"""
|
|
11
12
|
|
|
@@ -338,8 +339,10 @@ class CutoffBlackbody(_SED):
|
|
|
338
339
|
@property
|
|
339
340
|
def wavelength(self):
|
|
340
341
|
if len(self.frequency) == 1:
|
|
341
|
-
self.frequency = np.ones(len(self.time)) * self.frequency
|
|
342
|
+
self.frequency = np.ones(len(self.time)) * self.frequency
|
|
342
343
|
wavelength = nu_to_lambda(self.frequency) * angstrom_cgs
|
|
344
|
+
if len(wavelength) != len(self.time):
|
|
345
|
+
wavelength = np.tile(wavelength.T, (len(self.time), 1)).T
|
|
343
346
|
return wavelength
|
|
344
347
|
|
|
345
348
|
@property
|
|
@@ -351,15 +354,28 @@ class CutoffBlackbody(_SED):
|
|
|
351
354
|
return self.X_CONST * np.array(range(1, 11))
|
|
352
355
|
|
|
353
356
|
def _set_sed(self):
|
|
354
|
-
|
|
355
|
-
self.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
357
|
+
if np.size(self.wavelength) != np.size(self.time):
|
|
358
|
+
self.sed = np.zeros((len(self.frequency), len(self.time)))
|
|
359
|
+
self.r_photosphere = np.tile(self.r_photosphere, (len(self.frequency), 1))
|
|
360
|
+
self.temperature = np.tile(self.temperature, (len(self.frequency), 1))
|
|
361
|
+
self.sed[self.mask] = \
|
|
362
|
+
self.FLUX_CONST * (self.r_photosphere[self.mask]**2 / self.cutoff_wavelength /
|
|
363
|
+
self.wavelength[self.mask] ** 4) \
|
|
364
|
+
/ np.expm1(self.X_CONST / self.wavelength[self.mask] / self.temperature[self.mask])
|
|
365
|
+
self.sed[~self.mask] = \
|
|
366
|
+
self.FLUX_CONST * (self.r_photosphere[~self.mask]**2 / self.wavelength[~self.mask]**5) \
|
|
367
|
+
/ np.expm1(self.X_CONST / self.wavelength[~self.mask] / self.temperature[~self.mask])
|
|
368
|
+
self.sed *= self.norms[np.searchsorted(self.unique_times, self.time)]
|
|
369
|
+
else:
|
|
370
|
+
self.sed[self.mask] = \
|
|
371
|
+
self.FLUX_CONST * (self.r_photosphere[self.mask]**2 / self.cutoff_wavelength /
|
|
372
|
+
self.wavelength[self.mask] ** 4) \
|
|
373
|
+
/ np.expm1(self.X_CONST / self.wavelength[self.mask] / self.temperature[self.mask])
|
|
374
|
+
self.sed[~self.mask] = \
|
|
375
|
+
self.FLUX_CONST * (self.r_photosphere[~self.mask]**2 / self.wavelength[~self.mask]**5) \
|
|
376
|
+
/ np.expm1(self.X_CONST / self.wavelength[~self.mask] / self.temperature[~self.mask])
|
|
377
|
+
# Apply renormalisation
|
|
378
|
+
self.sed *= self.norms[np.searchsorted(self.unique_times, self.time)]
|
|
363
379
|
|
|
364
380
|
def _set_norm(self):
|
|
365
381
|
self.norms = self.luminosity[self.uniq_is] / \
|
|
@@ -389,6 +405,52 @@ class CutoffBlackbody(_SED):
|
|
|
389
405
|
return self.flux_density
|
|
390
406
|
|
|
391
407
|
|
|
408
|
+
class PowerlawPlusBlackbody:
|
|
409
|
+
"""SED class for power law + blackbody combination with time-evolving power law"""
|
|
410
|
+
|
|
411
|
+
def __init__(self, temperature, r_photosphere, pl_amplitude, pl_slope, pl_evolution_index, time,
|
|
412
|
+
reference_wavelength, frequency, luminosity_distance):
|
|
413
|
+
self.temperature = temperature
|
|
414
|
+
self.r_photosphere = r_photosphere
|
|
415
|
+
self.pl_amplitude = pl_amplitude
|
|
416
|
+
self.pl_slope = pl_slope
|
|
417
|
+
self.pl_evolution_index = pl_evolution_index
|
|
418
|
+
self.time = time
|
|
419
|
+
self.reference_wavelength = reference_wavelength
|
|
420
|
+
self.frequency = frequency
|
|
421
|
+
self.luminosity_distance = luminosity_distance
|
|
422
|
+
|
|
423
|
+
# Calculate combined flux density
|
|
424
|
+
self._calculate_flux_density()
|
|
425
|
+
|
|
426
|
+
def _calculate_flux_density(self):
|
|
427
|
+
"""Calculate power law + blackbody flux density with time-evolving power law"""
|
|
428
|
+
|
|
429
|
+
# Blackbody component using the provided function
|
|
430
|
+
bb_flux_density = blackbody_to_flux_density(temperature=self.temperature,
|
|
431
|
+
r_photosphere=self.r_photosphere,
|
|
432
|
+
dl=self.luminosity_distance,
|
|
433
|
+
frequency=self.frequency)
|
|
434
|
+
|
|
435
|
+
# Time-evolving power law component
|
|
436
|
+
# Convert frequency to wavelength for power law calculation
|
|
437
|
+
wavelength = (speed_of_light * 1e8) / self.frequency # Angstroms
|
|
438
|
+
|
|
439
|
+
# Calculate time-evolved power law amplitude
|
|
440
|
+
pl_amplitude_evolved = self.pl_amplitude * (self.time / 1.0) ** (-self.pl_evolution_index)
|
|
441
|
+
|
|
442
|
+
# Calculate power law in F_lambda
|
|
443
|
+
pl_flux_lambda = pl_amplitude_evolved * (wavelength / self.reference_wavelength) ** self.pl_slope
|
|
444
|
+
|
|
445
|
+
# Convert power law from F_lambda to F_nu
|
|
446
|
+
pl_flux_density = pl_flux_lambda * wavelength ** 2 / (speed_of_light * 1e8)
|
|
447
|
+
pl_flux_density = pl_flux_density * uu.erg / uu.s / uu.cm ** 2 / uu.Hz
|
|
448
|
+
|
|
449
|
+
# Combine components
|
|
450
|
+
total_flux_density = bb_flux_density + pl_flux_density
|
|
451
|
+
|
|
452
|
+
self.flux_density = total_flux_density
|
|
453
|
+
|
|
392
454
|
class Blackbody(object):
|
|
393
455
|
|
|
394
456
|
reference = "It is a blackbody - Do you really need a reference for this?"
|
|
@@ -471,59 +533,140 @@ class Synchrotron(_SED):
|
|
|
471
533
|
|
|
472
534
|
|
|
473
535
|
class Line(_SED):
|
|
536
|
+
"""
|
|
537
|
+
A class that modifies an input SED by incorporating a time‐dependent absorption line feature.
|
|
538
|
+
|
|
539
|
+
Reference:
|
|
540
|
+
https://ui.adsabs.harvard.edu/abs/2018ApJS..236....6G/abstract
|
|
541
|
+
"""
|
|
474
542
|
|
|
475
543
|
reference = "https://ui.adsabs.harvard.edu/abs/2018ApJS..236....6G/abstract"
|
|
476
544
|
|
|
477
|
-
def __init__(self,
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
545
|
+
def __init__(self,
|
|
546
|
+
time: np.ndarray,
|
|
547
|
+
luminosity: np.ndarray,
|
|
548
|
+
frequency: Union[np.ndarray, float],
|
|
549
|
+
sed: Union[_SED, Blackbody],
|
|
550
|
+
luminosity_distance: float,
|
|
551
|
+
line_wavelength: float = 7.5e3,
|
|
552
|
+
line_width: float = 500,
|
|
553
|
+
line_time: float = 50,
|
|
554
|
+
line_duration: float = 25,
|
|
555
|
+
line_amplitude: float = 0.3,
|
|
556
|
+
**kwargs) -> None:
|
|
481
557
|
"""
|
|
482
|
-
Modifies the input SED by
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
558
|
+
Modifies the input SED by imposing a time-dependent absorption line.
|
|
559
|
+
|
|
560
|
+
Parameters
|
|
561
|
+
----------
|
|
562
|
+
time : np.ndarray
|
|
563
|
+
1D array of times (in source frame).
|
|
564
|
+
luminosity : np.ndarray
|
|
565
|
+
1D array of luminosities (in cgs units) corresponding to each time.
|
|
566
|
+
frequency : Union[np.ndarray, float]
|
|
567
|
+
Frequency (in Hz, source frame) at which to calculate the flux.
|
|
568
|
+
If an array is provided, its shape must be broadcastable with time.
|
|
569
|
+
sed : Union[_SED, Blackbody]
|
|
570
|
+
An instantiated SED object (e.g. a CutoffBlackbody).
|
|
571
|
+
luminosity_distance : float
|
|
572
|
+
Luminosity distance in cm.
|
|
573
|
+
line_wavelength : float, optional
|
|
574
|
+
Central wavelength of the line in angstrom (default 7500 Å).
|
|
575
|
+
line_width : float, optional
|
|
576
|
+
Line width (sigma) in angstrom (default 500 Å).
|
|
577
|
+
line_time : float, optional
|
|
578
|
+
The time when the line is strongest (default 50).
|
|
579
|
+
line_duration : float, optional
|
|
580
|
+
The characteristic duration of the line (default 25).
|
|
581
|
+
line_amplitude : float, optional
|
|
582
|
+
The maximum amplitude of the line absorption (default 0.3).
|
|
583
|
+
kwargs : dict, optional
|
|
584
|
+
Other keyword arguments.
|
|
496
585
|
"""
|
|
497
|
-
|
|
498
|
-
self.time = time
|
|
499
|
-
self.luminosity = luminosity
|
|
586
|
+
# Reshape time and luminosity to column vectors (n_time, 1).
|
|
587
|
+
self.time = np.atleast_1d(time).reshape(-1, 1)
|
|
588
|
+
self.luminosity = np.atleast_1d(luminosity).reshape(-1, 1)
|
|
589
|
+
|
|
590
|
+
# Convert frequency to a row vector (1, n_freq)
|
|
591
|
+
freq_arr = np.atleast_1d(frequency)
|
|
592
|
+
if freq_arr.ndim == 1:
|
|
593
|
+
freq_arr = freq_arr.reshape(1, -1)
|
|
594
|
+
self.frequency = freq_arr # shape (1, n_freq)
|
|
595
|
+
|
|
596
|
+
# Call the parent initializer.
|
|
597
|
+
super().__init__(frequency=self.frequency,
|
|
598
|
+
luminosity_distance=luminosity_distance,
|
|
599
|
+
length=self.time.shape[0])
|
|
600
|
+
|
|
601
|
+
# Keep a reference to the SED to modify.
|
|
500
602
|
self.SED = sed
|
|
501
|
-
self.sed = None
|
|
603
|
+
self.sed = None # This will be computed in _set_sed().
|
|
604
|
+
|
|
605
|
+
# Save the line parameters.
|
|
502
606
|
self.line_wavelength = line_wavelength
|
|
503
607
|
self.line_width = line_width
|
|
504
608
|
self.line_time = line_time
|
|
505
609
|
self.line_duration = line_duration
|
|
506
610
|
self.line_amplitude = line_amplitude
|
|
507
611
|
|
|
612
|
+
# Use an internal attribute to store the computed flux density.
|
|
613
|
+
self._flux_density = None
|
|
614
|
+
|
|
615
|
+
# Calculate the flux density.
|
|
508
616
|
self.calculate_flux_density()
|
|
509
617
|
|
|
618
|
+
@property
|
|
619
|
+
def flux_density(self):
|
|
620
|
+
"""
|
|
621
|
+
Returns the computed flux density; this property wraps an internal attribute.
|
|
622
|
+
"""
|
|
623
|
+
return self._flux_density
|
|
624
|
+
|
|
510
625
|
@property
|
|
511
626
|
def wavelength(self):
|
|
627
|
+
"""
|
|
628
|
+
Returns the wavelength corresponding to self.frequency (using nu_to_lambda),
|
|
629
|
+
while preserving broadcasting.
|
|
630
|
+
"""
|
|
512
631
|
return nu_to_lambda(self.frequency)
|
|
513
632
|
|
|
514
633
|
def calculate_flux_density(self):
|
|
515
|
-
|
|
634
|
+
"""
|
|
635
|
+
Compute the modified SED with the absorption line and store the result internally.
|
|
636
|
+
"""
|
|
516
637
|
self._set_sed()
|
|
517
|
-
return self.
|
|
638
|
+
return self._flux_density
|
|
518
639
|
|
|
519
640
|
def _set_sed(self):
|
|
520
|
-
|
|
521
|
-
|
|
641
|
+
"""
|
|
642
|
+
Compute and set the modified SED (flux density) with proper units.
|
|
643
|
+
Assumes:
|
|
644
|
+
- self.SED.sed has shape (n_freq, n_time) (e.g., (100,300))
|
|
645
|
+
- self.time and self.luminosity are processed so that the time‐axis is the second axis.
|
|
646
|
+
"""
|
|
647
|
+
# Transpose time and luminosity to have time along the second axis
|
|
648
|
+
time_vals = self.time.T # Shape (1, n_time)
|
|
649
|
+
lum_vals = self.luminosity.T # Shape (1, n_time)
|
|
650
|
+
|
|
651
|
+
# Compute a time-dependent amplitude (expected shape (1, n_time))
|
|
652
|
+
amplitude_time = self.line_amplitude * np.exp(
|
|
653
|
+
-0.5 * ((time_vals - self.line_time) / self.line_duration) ** 2
|
|
654
|
+
)
|
|
522
655
|
|
|
523
|
-
|
|
656
|
+
# Get the baseline SED (shape (n_freq, n_time))
|
|
657
|
+
sed_base = self.SED.sed
|
|
524
658
|
|
|
525
|
-
|
|
526
|
-
|
|
659
|
+
# Modify the SED by attenuating with the time-dependent factor
|
|
660
|
+
sed_modified = sed_base * (1 - amplitude_time)
|
|
661
|
+
|
|
662
|
+
# Compute additional scaling factors as needed (example below)
|
|
663
|
+
amplitude_scaled = amplitude_time * lum_vals / (self.line_width * np.sqrt(2 * np.pi))
|
|
664
|
+
line_profile = np.exp(-0.5 * ((self.wavelength - self.line_wavelength) / self.line_width) ** 2)
|
|
665
|
+
sed_modified += amplitude_scaled * line_profile
|
|
666
|
+
|
|
667
|
+
# IMPORTANT: Convert sed_modified (a numpy array) to an astropy Quantity.
|
|
668
|
+
# Here, we assume the flux density is in units of erg/s/cm^2/Hz.
|
|
669
|
+
self._flux_density = sed_modified * uu.erg / uu.s / uu.cm ** 2 / uu.Hz
|
|
527
670
|
|
|
528
671
|
def get_correct_output_format_from_spectra(time, time_eval, spectra, lambda_array, **kwargs):
|
|
529
672
|
"""
|
|
@@ -542,8 +685,9 @@ def get_correct_output_format_from_spectra(time, time_eval, spectra, lambda_arra
|
|
|
542
685
|
spectra = np.nan_to_num(spectra)
|
|
543
686
|
spectra[spectra.value == np.nan_to_num(np.inf)] = 1e-30 * np.mean(spectra[5])
|
|
544
687
|
spectra[spectra.value == 0.] = 1e-30 * np.mean(spectra[5])
|
|
545
|
-
|
|
546
|
-
source = RedbackTimeSeriesSource(phase=time_eval, wave=lambda_array, flux=spectra
|
|
688
|
+
time_spline_degree = kwargs.get('time_spline_degree', 3)
|
|
689
|
+
source = RedbackTimeSeriesSource(phase=time_eval, wave=lambda_array, flux=spectra,
|
|
690
|
+
time_spline_degree=time_spline_degree)
|
|
547
691
|
if kwargs['output_format'] == 'flux':
|
|
548
692
|
bands = kwargs['bands']
|
|
549
693
|
magnitude = source.bandmag(phase=time, band=bands, magsys='ab')
|
redback/simulate_transients.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
-
from
|
|
2
|
+
from redback.sed import RedbackTimeSeriesSource
|
|
3
3
|
import redback
|
|
4
4
|
import pandas as pd
|
|
5
5
|
from redback.utils import logger, calc_flux_density_error_from_monochromatic_magnitude, calc_flux_density_from_ABmag
|
|
@@ -191,8 +191,16 @@ class SimulateOpticalTransient(object):
|
|
|
191
191
|
self.sncosmo_model = self.model(_time_array, **parameters, **model_kwargs)
|
|
192
192
|
elif callable(model):
|
|
193
193
|
self.model = model
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
if kwargs['redback_compatible_model']:
|
|
195
|
+
model_kwargs['output_format'] = 'sncosmo_source'
|
|
196
|
+
logger.info("Model is consistent with redback model format. Using simplified model wrapper.")
|
|
197
|
+
model_end_time = model_kwargs.get('end_time', 400.0)
|
|
198
|
+
_time_array = np.linspace(0.1, model_end_time, 1000)
|
|
199
|
+
sncosmomodel = self.model(_time_array, **parameters, **model_kwargs)
|
|
200
|
+
self.sncosmo_model = sncosmomodel
|
|
201
|
+
else:
|
|
202
|
+
logger.info('Model is inconsistent with redback model format. Making a custom wrapper for this model')
|
|
203
|
+
self.sncosmo_model = self._make_sncosmo_wrapper_for_user_model()
|
|
196
204
|
else:
|
|
197
205
|
raise ValueError("The user needs to specify model as either a string or function.")
|
|
198
206
|
|
|
@@ -649,6 +657,8 @@ class SimulateOpticalTransient(object):
|
|
|
649
657
|
detected[mask_snr] = 0
|
|
650
658
|
observation_dataframe['detected'] = detected
|
|
651
659
|
observation_dataframe['limiting_magnitude'] = overlapping_database['fiveSigmaDepth'].values
|
|
660
|
+
observation_dataframe['flux_limit'] = redback.utils.bandpass_magnitude_to_flux(
|
|
661
|
+
overlapping_database['fiveSigmaDepth'].values, filters)
|
|
652
662
|
return observation_dataframe
|
|
653
663
|
|
|
654
664
|
def _make_observations(self):
|