redback 1.12.0__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/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 +272 -33
- redback/transient_models/tde_models.py +193 -54
- redback/utils.py +34 -7
- {redback-1.12.0.dist-info → redback-1.12.1.dist-info}/METADATA +7 -4
- {redback-1.12.0.dist-info → redback-1.12.1.dist-info}/RECORD +37 -28
- {redback-1.12.0.dist-info → redback-1.12.1.dist-info}/WHEEL +1 -1
- redback/tables/qdot_rosswogkorobkin24.pck +0 -0
- {redback-1.12.0.dist-info → redback-1.12.1.dist-info}/licenses/LICENCE.md +0 -0
- {redback-1.12.0.dist-info → redback-1.12.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from collections import namedtuple
|
|
3
|
+
import redback.interaction_processes as ip
|
|
4
|
+
import redback.sed as sed
|
|
5
|
+
import redback.photosphere as photosphere
|
|
6
|
+
from astropy.cosmology import Planck18 as cosmo
|
|
7
|
+
from redback.utils import calc_kcorrected_properties, citation_wrapper, lambda_to_nu
|
|
8
|
+
from redback.constants import *
|
|
9
|
+
from scipy.interpolate import interp1d
|
|
10
|
+
|
|
11
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2022ApJ...932...84M')
|
|
12
|
+
def _wr_bh_merger(time, M_star, M_bh, M_fast, M_pre, v_fast, v_slow, alpha, eta, theta, phi_0, kappa_s, kappa_f, kappa_x, N, **kwargs):
|
|
13
|
+
"""
|
|
14
|
+
Parameters:
|
|
15
|
+
:param time: time in source frame in seconds
|
|
16
|
+
:param M_star: Mass of the Wolf-Rayet star in solar masses
|
|
17
|
+
:param M_bh: Mass of the black hole in solar masses
|
|
18
|
+
:param M_fast: Mass of the fast component in solar masses
|
|
19
|
+
:param M_pre: Mass of the pre-merger CSM in solar masses
|
|
20
|
+
:param v_fast: Velocity of the fast component in units of c
|
|
21
|
+
:param v_slow: Velocity of the slow component in km/s
|
|
22
|
+
:param alpha: Viscosity parameter
|
|
23
|
+
:param eta: Efficiency of conversion of accretion energy to radiation
|
|
24
|
+
:param theta: Disk aspect ratio
|
|
25
|
+
:param phi_0: Solid angle of the slow component
|
|
26
|
+
:param kappa_s: Opacity of the slow component
|
|
27
|
+
:param kappa_f: Opacity of the fast component
|
|
28
|
+
:param kappa_x: Opacity of the x-ray component
|
|
29
|
+
:param N: Number of pre-merger orbits
|
|
30
|
+
:param kwargs: Additional parameters
|
|
31
|
+
"""
|
|
32
|
+
# Calculate constants
|
|
33
|
+
M_acc = M_acc = 0.05 * (M_bh/10.0)**0.6 * (M_star/10.0)**0.65
|
|
34
|
+
M_slow = M_star - M_fast - M_acc
|
|
35
|
+
|
|
36
|
+
t_visc = 0.55 * day_to_s * (alpha / 0.1)**-1 * (M_star / 10.0)**0.87 * (M_bh / 10.0)**-0.5 * (theta / 0.33)**-2.0
|
|
37
|
+
L_acc0 = 1.6e44 * (M_bh / 10.0)**0.03 * (M_star / 10.0)**1.63 * (eta / 1e-2) * (alpha / 0.1)**-1.1 * (theta / 0.33)**-2.3
|
|
38
|
+
L_acc_tvisc = L_acc0 * (t_visc / (3.0 * day_to_s))**-2.1 * np.exp(-1)
|
|
39
|
+
vesc_trun = 9e13 * (N / 100) * (M_star / 10.0)**0.58
|
|
40
|
+
|
|
41
|
+
# Time-dependent properties
|
|
42
|
+
mask = time < t_visc
|
|
43
|
+
e_slow = 0.5 * (M_slow * solar_mass) * (v_slow * km_cgs)**2
|
|
44
|
+
e_fast = ((1.0 - v_fast**2)**-0.5 - 1.0) * (M_fast * solar_mass) * speed_of_light**2.0
|
|
45
|
+
|
|
46
|
+
rad_slow = v_slow * km_cgs * time
|
|
47
|
+
t_diff_slow = (M_slow * solar_mass) * kappa_s / (4.0 * np.pi * rad_slow * speed_of_light)
|
|
48
|
+
t_lc_slow = rad_slow / speed_of_light
|
|
49
|
+
|
|
50
|
+
rad_fast = v_fast * speed_of_light * time
|
|
51
|
+
t_diff_fast = (M_fast * solar_mass) * kappa_f / (4.0 * np.pi * rad_fast * speed_of_light)
|
|
52
|
+
t_lc_fast = rad_fast / speed_of_light
|
|
53
|
+
tau_x = kappa_x * (M_fast * solar_mass) / (4.0 * np.pi * rad_fast**2)
|
|
54
|
+
|
|
55
|
+
L_sh = 0.5 * (M_pre * solar_mass) * (v_slow * km_cgs)**3 / (vesc_trun) * np.exp(-v_slow * km_cgs * time / vesc_trun)
|
|
56
|
+
L_acc = L_acc0 * (time / (3.0 * day_to_s))**-2.1 * np.exp(-(time / t_visc)**0.28)
|
|
57
|
+
L_acc[mask] = L_acc_tvisc
|
|
58
|
+
L_acc_th = phi_0 * (1 - np.exp(-tau_x)) * L_acc + phi_0 * L_acc
|
|
59
|
+
L_x = phi_0 * np.exp(-tau_x) * L_acc
|
|
60
|
+
|
|
61
|
+
# Initialize arrays
|
|
62
|
+
energy_fast = [e_fast]
|
|
63
|
+
energy_slow = [e_slow]
|
|
64
|
+
L_opt_sh = []
|
|
65
|
+
L_opt_rep = []
|
|
66
|
+
|
|
67
|
+
for i in range(len(time)):
|
|
68
|
+
if i > 0:
|
|
69
|
+
dt = time[i] - time[i - 1]
|
|
70
|
+
de_slow_dt = -e_slow / time[i] - lum_opt_sh + L_sh[i]
|
|
71
|
+
de_fast_dt = -e_fast / time[i] - lum_opt_rep + L_acc_th[i]
|
|
72
|
+
e_slow += de_slow_dt * dt
|
|
73
|
+
if e_slow < 0:
|
|
74
|
+
e_slow = 0
|
|
75
|
+
e_fast += de_fast_dt * dt
|
|
76
|
+
if e_fast < 0:
|
|
77
|
+
e_fast = 0
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
lum_opt_sh = e_slow / (t_lc_slow[i] + t_diff_slow[i])
|
|
81
|
+
lum_opt_rep = e_fast / (t_lc_fast[i] + t_diff_fast[i])
|
|
82
|
+
L_opt_sh.append(lum_opt_sh)
|
|
83
|
+
L_opt_rep.append(lum_opt_rep)
|
|
84
|
+
energy_fast.append(e_fast)
|
|
85
|
+
energy_slow.append(e_slow)
|
|
86
|
+
|
|
87
|
+
dynamics_output = namedtuple('dynamics_output', ['time', 'energy_fast', 'energy_slow', 'rad_fast', 'rad_slow',
|
|
88
|
+
'reprocessed_luminosity', 'shock_powered_luminosity', 'optical_luminosity',
|
|
89
|
+
'x_ray_luminosity', 'accretion_luminosity', 'erad_opt_total'])
|
|
90
|
+
|
|
91
|
+
dynamics_output.time = time
|
|
92
|
+
dynamics_output.energy_fast = energy_fast
|
|
93
|
+
dynamics_output.energy_slow = energy_slow
|
|
94
|
+
dynamics_output.rad_fast = rad_fast
|
|
95
|
+
dynamics_output.rad_slow = rad_slow
|
|
96
|
+
dynamics_output.reprocessed_luminosity = L_opt_rep
|
|
97
|
+
dynamics_output.shock_powered_luminosity = L_opt_sh
|
|
98
|
+
dynamics_output.optical_luminosity = np.array(L_opt_rep) + np.array(L_opt_sh)
|
|
99
|
+
dynamics_output.x_ray_luminosity = L_x
|
|
100
|
+
dynamics_output.accretion_luminosity = L_acc
|
|
101
|
+
dynamics_output.erad_opt_total = np.trapz(np.array(L_opt_sh) + np.array(L_opt_sh), x=time)
|
|
102
|
+
return dynamics_output
|
|
103
|
+
|
|
104
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2022ApJ...932...84M')
|
|
105
|
+
def wr_bh_merger_bolometric(time, M_star, M_bh, M_fast, M_pre, v_fast, v_slow, alpha, eta, **kwargs):
|
|
106
|
+
"""
|
|
107
|
+
Parameters:
|
|
108
|
+
:param time: time in source frame in days
|
|
109
|
+
:param M_star: Mass of the Wolf-Rayet star in solar masses
|
|
110
|
+
:param M_bh: Mass of the black hole in solar masses
|
|
111
|
+
:param M_fast: Mass of the fast component in solar masses
|
|
112
|
+
:param M_pre: Mass of the pre-merger CSM in solar masses
|
|
113
|
+
:param v_fast: Velocity of the fast component in units of c
|
|
114
|
+
:param v_slow: Velocity of the slow component in km/s
|
|
115
|
+
:param alpha: Viscosity parameter
|
|
116
|
+
:param eta: Efficiency of conversion of accretion energy to radiation
|
|
117
|
+
:param kwargs: Additional parameters
|
|
118
|
+
:param output_format: whether to output dynamics or bolometric luminosity
|
|
119
|
+
:param theta: Disk aspect ratio
|
|
120
|
+
:param phi_0: Solid angle of the slow component
|
|
121
|
+
:param kappa_s: Opacity of the slow component
|
|
122
|
+
:param kappa_f: Opacity of the fast component
|
|
123
|
+
:param kappa_x: Opacity of the x-ray component
|
|
124
|
+
:param N: Number of pre-merger orbits
|
|
125
|
+
:return: bolometric luminosity or dynamics output
|
|
126
|
+
"""
|
|
127
|
+
theta = kwargs.get('theta', 0.33)
|
|
128
|
+
phi_0 = kwargs.get('phi_0', 0.5)
|
|
129
|
+
kappa_s = kwargs.get('kappa_s', 0.03)
|
|
130
|
+
kappa_f = kwargs.get('kappa_f', 0.2)
|
|
131
|
+
kappa_x = kwargs.get('kappa_x', 0.4)
|
|
132
|
+
N = kwargs.get('N', 30)
|
|
133
|
+
|
|
134
|
+
time_temp = np.geomspace(1e0, 1e8, 2000)
|
|
135
|
+
dynamics_output = _wr_bh_merger(time_temp, M_star, M_bh, M_fast, M_pre, v_fast, v_slow, alpha, eta, theta, phi_0, kappa_s, kappa_f, kappa_x, N, **kwargs)
|
|
136
|
+
lbol_func = interp1d(time_temp, y=dynamics_output.optical_luminosity)
|
|
137
|
+
time = time * day_to_s
|
|
138
|
+
lbol = lbol_func(time)
|
|
139
|
+
if kwargs['output_format'] == 'dynamics_output':
|
|
140
|
+
return dynamics_output
|
|
141
|
+
else:
|
|
142
|
+
return lbol
|
|
143
|
+
|
|
144
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2022ApJ...932...84M')
|
|
145
|
+
def wr_bh_merger(time, redshift, M_star, M_bh, M_fast, M_pre, v_fast, v_slow, alpha, eta, **kwargs):
|
|
146
|
+
"""
|
|
147
|
+
Parameters:
|
|
148
|
+
:param time: time in source frame in days
|
|
149
|
+
:param redshift: redshift
|
|
150
|
+
:param M_star: Mass of the Wolf-Rayet star in solar masses
|
|
151
|
+
:param M_bh: Mass of the black hole in solar masses
|
|
152
|
+
:param M_fast: Mass of the fast component in solar masses
|
|
153
|
+
:param M_pre: Mass of the pre-merger CSM in solar masses
|
|
154
|
+
:param v_fast: Velocity of the fast component in units of c
|
|
155
|
+
:param v_slow: Velocity of the slow component in km/s
|
|
156
|
+
:param alpha: Viscosity parameter
|
|
157
|
+
:param eta: Efficiency of conversion of accretion energy to radiation
|
|
158
|
+
:param kwargs: Additional parameters - Must be all the kwargs required by the specific photosphere, sed methods used
|
|
159
|
+
e.g., for TemperatureFloor: vej (km/s) and temperature_floor
|
|
160
|
+
:param theta: Disk aspect ratio
|
|
161
|
+
:param phi_0: Solid angle of the slow component
|
|
162
|
+
:param kappa_s: Opacity of the slow component
|
|
163
|
+
:param kappa_f: Opacity of the fast component
|
|
164
|
+
:param kappa_x: Opacity of the x-ray component
|
|
165
|
+
:param N: Number of pre-merger orbits
|
|
166
|
+
:param photosphere: Default is TemperatureFloor.
|
|
167
|
+
kwargs must have vej or relevant parameters if using different photosphere model
|
|
168
|
+
:param frequency: Required if output_format is 'flux_density'.
|
|
169
|
+
frequency to calculate - Must be same length as time array or a single number).
|
|
170
|
+
:param bands: Required if output_format is 'magnitude' or 'flux'.
|
|
171
|
+
:param output_format: 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
172
|
+
:param lambda_array: Optional argument to set your desired wavelength array (in Angstroms) to evaluate the SED on.
|
|
173
|
+
:param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
|
|
174
|
+
:return: set by output format - 'flux_density', 'magnitude', 'dynamics_output', 'spectra', 'flux', 'sncosmo_source'
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
theta = kwargs.get('theta', 0.33)
|
|
178
|
+
phi_0 = kwargs.get('phi_0', 0.5)
|
|
179
|
+
kappa_s = kwargs.get('kappa_s', 0.03)
|
|
180
|
+
kappa_f = kwargs.get('kappa_f', 0.2)
|
|
181
|
+
kappa_x = kwargs.get('kappa_x', 0.4)
|
|
182
|
+
N = kwargs.get('N', 30)
|
|
183
|
+
kwargs['photosphere'] = kwargs.get("photosphere", photosphere.TemperatureFloor)
|
|
184
|
+
kwargs['sed'] = kwargs.get("sed", sed.Blackbody)
|
|
185
|
+
cosmology = kwargs.get('cosmology', cosmo)
|
|
186
|
+
dl = cosmology.luminosity_distance(redshift).cgs.value
|
|
187
|
+
|
|
188
|
+
time_temp = np.geomspace(1e0, 1e8, 2000)
|
|
189
|
+
if kwargs['output_format'] == 'flux_density':
|
|
190
|
+
frequency = kwargs['frequency']
|
|
191
|
+
frequency, time = calc_kcorrected_properties(frequency=frequency, redshift=redshift, time=time)
|
|
192
|
+
output = _wr_bh_merger(time_temp, M_star, M_bh, M_fast, M_pre, v_fast, v_slow, alpha, eta, theta, phi_0, kappa_s, kappa_f, kappa_x, N, **kwargs)
|
|
193
|
+
photo_fast = kwargs['photosphere'](time=time_temp/day_to_s, luminosity=np.array(output.reprocessed_luminosity), vej = v_fast * speed_of_light / km_cgs, **kwargs)
|
|
194
|
+
photo_slow = kwargs['photosphere'](time=time_temp/day_to_s, luminosity=np.array(output.shock_powered_luminosity), vej = v_slow, **kwargs)
|
|
195
|
+
temp_func_fast = interp1d(time_temp/day_to_s, y=photo_fast.photosphere_temperature)
|
|
196
|
+
temp_func_slow = interp1d(time_temp/day_to_s, y=photo_slow.photosphere_temperature)
|
|
197
|
+
rad_func_fast = interp1d(time_temp/day_to_s, y=photo_fast.r_photosphere)
|
|
198
|
+
rad_func_slow = interp1d(time_temp/day_to_s, y=photo_slow.r_photosphere)
|
|
199
|
+
temp_fast = temp_func_fast(time)
|
|
200
|
+
temp_slow = temp_func_slow(time)
|
|
201
|
+
rad_fast = rad_func_fast(time)
|
|
202
|
+
rad_slow = rad_func_slow(time)
|
|
203
|
+
sed_fast = kwargs['sed'](temperature=temp_fast, r_photosphere=rad_fast,
|
|
204
|
+
frequency=frequency, luminosity_distance=dl)
|
|
205
|
+
sed_slow = kwargs['sed'](temperature=temp_slow, r_photosphere=rad_slow,
|
|
206
|
+
frequency=frequency, luminosity_distance=dl)
|
|
207
|
+
flux_density = sed_fast.flux_density + sed_slow.flux_density
|
|
208
|
+
return flux_density.to(uu.mJy).value
|
|
209
|
+
|
|
210
|
+
else:
|
|
211
|
+
time_obs = time
|
|
212
|
+
lambda_observer_frame = kwargs.get('lambda_array', np.geomspace(500, 60000, 200))
|
|
213
|
+
time_temp = np.geomspace(1e0, 1e8, 2000)
|
|
214
|
+
time_observer_frame = time_temp * (1. + redshift)
|
|
215
|
+
frequency, time = calc_kcorrected_properties(frequency=lambda_to_nu(lambda_observer_frame),
|
|
216
|
+
redshift=redshift, time=time_observer_frame)
|
|
217
|
+
output = _wr_bh_merger(time_temp, M_star, M_bh, M_fast, M_pre, v_fast, v_slow, alpha, eta, theta, phi_0, kappa_s, kappa_f, kappa_x, N, **kwargs)
|
|
218
|
+
photo_fast = kwargs['photosphere'](time=time_temp/day_to_s, luminosity=np.array(output.reprocessed_luminosity), vej = v_fast * speed_of_light / km_cgs, **kwargs)
|
|
219
|
+
photo_slow = kwargs['photosphere'](time=time_temp/day_to_s, luminosity=np.array(output.shock_powered_luminosity), vej = v_slow, **kwargs)
|
|
220
|
+
if kwargs['output_format'] == 'dynamics_output':
|
|
221
|
+
dynamics_output = namedtuple('dynamics_output', ['time', 'temperature_fast', 'temperature_slow','r_photosphere_fast',
|
|
222
|
+
'r_photosphere_slow','energy_fast','energy_slow','optical_luminosity',
|
|
223
|
+
'reprocessed_luminosity','shock_powered_luminosity','x_ray_luminosity',
|
|
224
|
+
'accretion_luminosity','erad_opt_total'])
|
|
225
|
+
dynamics_output.time = output.time
|
|
226
|
+
dynamics_output.temperature_fast = photo_fast.photosphere_temperature
|
|
227
|
+
dynamics_output.temperature_slow = photo_slow.photosphere_temperature
|
|
228
|
+
dynamics_output.r_photosphere_fast = photo_fast.r_photosphere
|
|
229
|
+
dynamics_output.r_photosphere_slow = photo_slow.r_photosphere
|
|
230
|
+
dynamics_output.energy_fast = output.energy_fast
|
|
231
|
+
dynamics_output.energy_slow = output.energy_slow
|
|
232
|
+
dynamics_output.optical_luminosity = output.optical_luminosity
|
|
233
|
+
dynamics_output.reprocessed_luminosity = output.reprocessed_luminosity
|
|
234
|
+
dynamics_output.shock_powered_luminosity = output.shock_powered_luminosity
|
|
235
|
+
dynamics_output.x_ray_luminosity = output.x_ray_luminosity
|
|
236
|
+
dynamics_output.accretion_luminosity = output.accretion_luminosity
|
|
237
|
+
dynamics_output.erad_opt_total = output.erad_opt_total
|
|
238
|
+
return dynamics_output
|
|
239
|
+
sed_fast = kwargs['sed'](temperature=photo_fast.photosphere_temperature, r_photosphere=photo_fast.r_photosphere,
|
|
240
|
+
frequency=frequency[:,None], luminosity_distance=dl)
|
|
241
|
+
sed_slow = kwargs['sed'](temperature=photo_slow.photosphere_temperature, r_photosphere=photo_slow.r_photosphere,
|
|
242
|
+
frequency=frequency[:,None], luminosity_distance=dl)
|
|
243
|
+
fmjy = sed_fast.flux_density + sed_slow.flux_density
|
|
244
|
+
fmjy = fmjy.T
|
|
245
|
+
spectra = fmjy.to(uu.mJy).to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
|
|
246
|
+
equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom))
|
|
247
|
+
if kwargs['output_format'] == 'spectra':
|
|
248
|
+
return namedtuple('output', ['time', 'lambdas', 'spectra'])(time=time_observer_frame,
|
|
249
|
+
lambdas=lambda_observer_frame,
|
|
250
|
+
spectra=spectra)
|
|
251
|
+
else:
|
|
252
|
+
return sed.get_correct_output_format_from_spectra(time=time_obs, time_eval=time_observer_frame/day_to_s,
|
|
253
|
+
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
254
|
+
**kwargs)
|
|
@@ -13,7 +13,7 @@ from redback.constants import day_to_s, solar_mass, km_cgs, au_cgs, speed_of_lig
|
|
|
13
13
|
from inspect import isfunction
|
|
14
14
|
import astropy.units as uu
|
|
15
15
|
from collections import namedtuple
|
|
16
|
-
from scipy.interpolate import interp1d
|
|
16
|
+
from scipy.interpolate import interp1d, RegularGridInterpolator
|
|
17
17
|
|
|
18
18
|
homologous_expansion_models = ['exponential_powerlaw_bolometric', 'arnett_bolometric',
|
|
19
19
|
'basic_magnetar_powered_bolometric','slsn_bolometric',
|
|
@@ -228,6 +228,66 @@ def sn_fallback(time, redshift, logl1, tr, **kwargs):
|
|
|
228
228
|
return sed.get_correct_output_format_from_spectra(time=time_obs, time_eval=time_observer_frame,
|
|
229
229
|
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
230
230
|
**kwargs)
|
|
231
|
+
|
|
232
|
+
def sn_nickel_fallback(time, redshift, mej, f_nickel, logl1, tr, **kwargs):
|
|
233
|
+
"""
|
|
234
|
+
:param time: observer frame time in days
|
|
235
|
+
:param redshift: source redshift
|
|
236
|
+
:param mej: total ejecta mass in solar masses
|
|
237
|
+
:param f_nickel: fraction of nickel mass
|
|
238
|
+
:param logl1: bolometric luminosity scale in log10 (cgs)
|
|
239
|
+
:param tr: transition time for luminosity
|
|
240
|
+
:param kwargs: Must be all the kwargs required by the specific interaction_process, photosphere, sed methods used
|
|
241
|
+
e.g., for Diffusion and TemperatureFloor: kappa, kappa_gamma, mej (solar masses), vej (km/s), floor temperature
|
|
242
|
+
:param interaction_process: Default is Diffusion.
|
|
243
|
+
Can also be None in which case the output is just the raw engine luminosity, or another interaction process.
|
|
244
|
+
:param photosphere: Default is TemperatureFloor.
|
|
245
|
+
kwargs must vej or relevant parameters if using different photosphere model
|
|
246
|
+
:param sed: Default is blackbody.
|
|
247
|
+
:param frequency: Required if output_format is ‘flux_density’.
|
|
248
|
+
frequency to calculate - Must be same length as time array or a single number).
|
|
249
|
+
:param bands: Required if output_format is ‘magnitude’ or ‘flux’.
|
|
250
|
+
:param output_format: ‘flux_density’, ‘magnitude’, ‘spectra’, ‘flux’, ‘sncosmo_source’
|
|
251
|
+
:param lambda_array: Optional argument to set your desired wavelength array (in Angstroms) to evaluate the SED on.
|
|
252
|
+
:param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
|
|
253
|
+
:return: set by output format - ‘flux_density’, ‘magnitude’, ‘spectra’, ‘flux’, ‘sncosmo_source’
|
|
254
|
+
"""
|
|
255
|
+
kwargs["interaction_process"] = kwargs.get("interaction_process", ip.Diffusion)
|
|
256
|
+
kwargs["photosphere"] = kwargs.get("photosphere", photosphere.TemperatureFloor)
|
|
257
|
+
kwargs["sed"] = kwargs.get("sed", sed.Blackbody)
|
|
258
|
+
cosmology = kwargs.get("cosmology", cosmo)
|
|
259
|
+
dl = cosmology.luminosity_distance(redshift).cgs.value
|
|
260
|
+
if kwargs['output_format'] == 'flux_density':
|
|
261
|
+
frequency = kwargs['frequency']
|
|
262
|
+
frequency, time = calc_kcorrected_properties(frequency=frequency, redshift=redshift, time=time)
|
|
263
|
+
lbol = fallback_lbol(time=time, logl1=logl1, tr=tr) + _nickelcobalt_engine(time=time, f_nickel=f_nickel, mej=mej)
|
|
264
|
+
photo = kwargs['photosphere'](time=time, luminosity=lbol, **kwargs)
|
|
265
|
+
sed_1 = kwargs['sed'](temperature=photo.photosphere_temperature, r_photosphere=photo.r_photosphere,
|
|
266
|
+
frequency=frequency, luminosity_distance=dl)
|
|
267
|
+
flux_density = sed_1.flux_density
|
|
268
|
+
return flux_density.to(uu.mJy).value
|
|
269
|
+
else:
|
|
270
|
+
time_obs = time
|
|
271
|
+
lambda_observer_frame = kwargs.get('lambda_array', np.geomspace(100, 60000, 100))
|
|
272
|
+
time_temp = np.geomspace(0.1, 3000, 300) # in days
|
|
273
|
+
time_observer_frame = time_temp * (1. + redshift)
|
|
274
|
+
frequency, time = calc_kcorrected_properties(frequency=lambda_to_nu(lambda_observer_frame),
|
|
275
|
+
redshift=redshift, time=time_observer_frame)
|
|
276
|
+
lbol = fallback_lbol(time=time, logl1=logl1, tr=tr) + _nickelcobalt_engine(time=time, f_nickel=f_nickel, mej=mej)
|
|
277
|
+
photo = kwargs['photosphere'](time=time, luminosity=lbol, **kwargs)
|
|
278
|
+
sed_1 = kwargs['sed'](temperature=photo.photosphere_temperature, r_photosphere=photo.r_photosphere,
|
|
279
|
+
frequency=frequency[:,None], luminosity_distance=dl)
|
|
280
|
+
fmjy = sed_1.flux_density.T
|
|
281
|
+
spectra = fmjy.to(uu.mJy).to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
|
|
282
|
+
equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom))
|
|
283
|
+
if kwargs['output_format'] == 'spectra':
|
|
284
|
+
return namedtuple('output', ['time', 'lambdas', 'spectra'])(time=time_observer_frame,
|
|
285
|
+
lambdas=lambda_observer_frame,
|
|
286
|
+
spectra=spectra)
|
|
287
|
+
else:
|
|
288
|
+
return sed.get_correct_output_format_from_spectra(time=time_obs, time_eval=time_observer_frame,
|
|
289
|
+
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
290
|
+
**kwargs)
|
|
231
291
|
|
|
232
292
|
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2018ApJS..236....6G/abstract')
|
|
233
293
|
def sn_exponential_powerlaw(time, redshift, lbol_0, alpha_1, alpha_2, tpeak_d, **kwargs):
|
|
@@ -833,13 +893,12 @@ def slsn(time, redshift, p0, bp, mass_ns, theta_pb,**kwargs):
|
|
|
833
893
|
lbol = slsn_bolometric(time=time, p0=p0, bp=bp, mass_ns=mass_ns, theta_pb=theta_pb, **kwargs)
|
|
834
894
|
photo = kwargs['photosphere'](time=time, luminosity=lbol, **kwargs)
|
|
835
895
|
full_sed = np.zeros((len(time), len(frequency)))
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
full_sed[:, ii] = ss.flux_density.to(uu.mJy).value
|
|
896
|
+
ss = kwargs['sed'](time=time, temperature=photo.photosphere_temperature,
|
|
897
|
+
r_photosphere=photo.r_photosphere, frequency=frequency[:, None],
|
|
898
|
+
luminosity_distance=dl, cutoff_wavelength=cutoff_wavelength, luminosity=lbol)
|
|
899
|
+
full_sed = ss.flux_density.to(uu.mJy).value.T
|
|
841
900
|
spectra = (full_sed * uu.mJy).to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
|
|
842
|
-
|
|
901
|
+
equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom))
|
|
843
902
|
if kwargs['output_format'] == 'spectra':
|
|
844
903
|
return namedtuple('output', ['time', 'lambdas', 'spectra'])(time=time_observer_frame,
|
|
845
904
|
lambdas=lambda_observer_frame,
|
|
@@ -1423,17 +1482,17 @@ def type_1a(time, redshift, f_nickel, mej, **kwargs):
|
|
|
1423
1482
|
:param line_duration: line duration, default is 25
|
|
1424
1483
|
:param line_amplitude: line amplitude, default is 0.3
|
|
1425
1484
|
:param frequency: Required if output_format is 'flux_density'.
|
|
1426
|
-
frequency to calculate - Must be same length as time array or a single number
|
|
1485
|
+
frequency to calculate - Must be same length as time array or a single number.
|
|
1427
1486
|
:param bands: Required if output_format is 'magnitude' or 'flux'.
|
|
1428
1487
|
:param output_format: 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
1429
1488
|
:param lambda_array: Optional argument to set your desired wavelength array (in Angstroms) to evaluate the SED on.
|
|
1430
|
-
:param cosmology:
|
|
1489
|
+
:param cosmology: cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be an astropy.cosmology object.
|
|
1431
1490
|
:return: set by output format - 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
1432
1491
|
"""
|
|
1433
1492
|
cosmology = kwargs.get('cosmology', cosmo)
|
|
1434
1493
|
dl = cosmology.luminosity_distance(redshift).cgs.value
|
|
1435
1494
|
cutoff_wavelength = kwargs.get('cutoff_wavelength', 3000)
|
|
1436
|
-
line_wavelength = kwargs.get('line_wavelength',7.5e3)
|
|
1495
|
+
line_wavelength = kwargs.get('line_wavelength', 7.5e3)
|
|
1437
1496
|
line_width = kwargs.get('line_width', 500)
|
|
1438
1497
|
line_time = kwargs.get('line_time', 50)
|
|
1439
1498
|
line_duration = kwargs.get('line_duration', 25)
|
|
@@ -1447,13 +1506,13 @@ def type_1a(time, redshift, f_nickel, mej, **kwargs):
|
|
|
1447
1506
|
|
|
1448
1507
|
photo = photosphere.TemperatureFloor(time=time, luminosity=lbol, **kwargs)
|
|
1449
1508
|
sed_1 = sed.CutoffBlackbody(time=time, luminosity=lbol, temperature=photo.photosphere_temperature,
|
|
1450
|
-
r_photosphere=photo.r_photosphere,frequency=frequency, luminosity_distance=dl,
|
|
1509
|
+
r_photosphere=photo.r_photosphere, frequency=frequency, luminosity_distance=dl,
|
|
1451
1510
|
cutoff_wavelength=cutoff_wavelength)
|
|
1452
1511
|
sed_2 = sed.Line(time=time, luminosity=lbol, frequency=frequency, luminosity_distance=dl,
|
|
1453
1512
|
sed=sed_1, line_wavelength=line_wavelength,
|
|
1454
1513
|
line_width=line_width, line_time=line_time,
|
|
1455
1514
|
line_duration=line_duration, line_amplitude=line_amplitude)
|
|
1456
|
-
flux_density = sed_2.flux_density
|
|
1515
|
+
flux_density = sed_2.flux_density.flatten()
|
|
1457
1516
|
return flux_density.to(uu.mJy).value
|
|
1458
1517
|
else:
|
|
1459
1518
|
time_obs = time
|
|
@@ -1465,18 +1524,23 @@ def type_1a(time, redshift, f_nickel, mej, **kwargs):
|
|
|
1465
1524
|
lbol = arnett_bolometric(time=time, f_nickel=f_nickel, mej=mej,
|
|
1466
1525
|
interaction_process=ip.Diffusion, **kwargs)
|
|
1467
1526
|
photo = photosphere.TemperatureFloor(time=time, luminosity=lbol, **kwargs)
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1527
|
+
# Here we construct the CutoffBlackbody SED with frequency reshaped to (n_freq, 1)
|
|
1528
|
+
ss = sed.CutoffBlackbody(time=time, temperature=photo.photosphere_temperature,
|
|
1529
|
+
r_photosphere=photo.r_photosphere, frequency=frequency[:, None],
|
|
1530
|
+
luminosity_distance=dl, cutoff_wavelength=cutoff_wavelength, luminosity=lbol)
|
|
1531
|
+
line_sed = sed.Line(time=time, luminosity=lbol, frequency=frequency[:, None],
|
|
1532
|
+
luminosity_distance=dl, sed=ss,
|
|
1533
|
+
line_wavelength=line_wavelength,
|
|
1534
|
+
line_width=line_width,
|
|
1535
|
+
line_time=line_time,
|
|
1536
|
+
line_duration=line_duration,
|
|
1537
|
+
line_amplitude=line_amplitude)
|
|
1538
|
+
full_sed = line_sed.flux_density.to(uu.mJy).value
|
|
1539
|
+
# The following line converts the full SED (in mJy) to erg/s/cm^2/Angstrom.
|
|
1478
1540
|
spectra = (full_sed * uu.mJy).to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
|
|
1479
|
-
equivalencies=uu.spectral_density(
|
|
1541
|
+
equivalencies=uu.spectral_density(
|
|
1542
|
+
wav=(lambdas_observer_frame.reshape(-1, 1) * uu.Angstrom))).T
|
|
1543
|
+
print(spectra.shape)
|
|
1480
1544
|
if kwargs['output_format'] == 'spectra':
|
|
1481
1545
|
return namedtuple('output', ['time', 'lambdas', 'spectra'])(time=time_observer_frame,
|
|
1482
1546
|
lambdas=lambdas_observer_frame,
|
|
@@ -1487,6 +1551,7 @@ def type_1a(time, redshift, f_nickel, mej, **kwargs):
|
|
|
1487
1551
|
**kwargs)
|
|
1488
1552
|
|
|
1489
1553
|
|
|
1554
|
+
|
|
1490
1555
|
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2018ApJS..236....6G/abstract')
|
|
1491
1556
|
def type_1c(time, redshift, f_nickel, mej, pp, **kwargs):
|
|
1492
1557
|
"""
|
|
@@ -1636,7 +1701,7 @@ def general_magnetar_slsn(time, redshift, l0, tsd, nn, ** kwargs):
|
|
|
1636
1701
|
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
1637
1702
|
**kwargs)
|
|
1638
1703
|
|
|
1639
|
-
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2024MNRAS.527.6455O/abstract')
|
|
1704
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2022MNRAS.516.4949S/abstract, https://ui.adsabs.harvard.edu/abs/2024MNRAS.527.6455O/abstract')
|
|
1640
1705
|
def general_magnetar_driven_supernova_bolometric(time, mej, E_sn, kappa, l0, tau_sd, nn, kappa_gamma, **kwargs):
|
|
1641
1706
|
"""
|
|
1642
1707
|
:param time: time in observer frame in days
|
|
@@ -1675,8 +1740,6 @@ def general_magnetar_driven_supernova_bolometric(time, mej, E_sn, kappa, l0, tau
|
|
|
1675
1740
|
time = time * day_to_s
|
|
1676
1741
|
lbol = lbol_func(time)
|
|
1677
1742
|
v_ej = vej_func(time)
|
|
1678
|
-
erot_total = np.trapz(magnetar_luminosity, x=time_temp)
|
|
1679
|
-
erad_total = np.trapz(output.bolometric_luminosity, x=time_temp)
|
|
1680
1743
|
|
|
1681
1744
|
dynamics_output = namedtuple('dynamics_output', ['v_ej', 'tau', 'time', 'bolometric_luminosity', 'kinetic_energy', 'erad_total',
|
|
1682
1745
|
'thermalisation_efficiency', 'magnetar_luminosity', 'erot_total'])
|
|
@@ -1696,7 +1759,7 @@ def general_magnetar_driven_supernova_bolometric(time, mej, E_sn, kappa, l0, tau
|
|
|
1696
1759
|
else:
|
|
1697
1760
|
return lbol
|
|
1698
1761
|
|
|
1699
|
-
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2024MNRAS.527.6455O/abstract')
|
|
1762
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2022MNRAS.516.4949S/abstract, https://ui.adsabs.harvard.edu/abs/2024MNRAS.527.6455O/abstract')
|
|
1700
1763
|
def general_magnetar_driven_supernova(time, redshift, mej, E_sn, kappa, l0, tau_sd, nn, kappa_gamma, **kwargs):
|
|
1701
1764
|
"""
|
|
1702
1765
|
:param time: time in observer frame in days
|
|
@@ -1796,11 +1859,10 @@ def general_magnetar_driven_supernova(time, redshift, mej, E_sn, kappa, l0, tau_
|
|
|
1796
1859
|
return dynamics_output
|
|
1797
1860
|
else:
|
|
1798
1861
|
full_sed = np.zeros((len(time), len(frequency)))
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
full_sed[:, ii] = ss.flux_density.to(uu.mJy).value
|
|
1862
|
+
ss = kwargs['sed'](time=time_temp/day_to_s, temperature=photo.photosphere_temperature,
|
|
1863
|
+
r_photosphere=photo.r_photosphere, frequency=frequency[:, None],
|
|
1864
|
+
luminosity_distance=dl, cutoff_wavelength=cutoff_wavelength, luminosity=output.bolometric_luminosity)
|
|
1865
|
+
full_sed = ss.flux_density.to(uu.mJy).value.T
|
|
1804
1866
|
spectra = (full_sed * uu.mJy).to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
|
|
1805
1867
|
equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom))
|
|
1806
1868
|
if kwargs['output_format'] == 'spectra':
|
|
@@ -2057,4 +2119,181 @@ def shocked_cocoon_and_arnett(time, redshift, mej_c, vej_c, eta, tshock, shocked
|
|
|
2057
2119
|
else:
|
|
2058
2120
|
return sed.get_correct_output_format_from_spectra(time=time_obs, time_eval=time_observer_frame,
|
|
2059
2121
|
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
2060
|
-
**kwargs)
|
|
2122
|
+
**kwargs)
|
|
2123
|
+
|
|
2124
|
+
@citation_wrapper("https://ui.adsabs.harvard.edu/abs/2025arXiv250602107S/abstract, https://ui.adsabs.harvard.edu/abs/2023PASJ...75..634M/abstract")
|
|
2125
|
+
def typeII_bolometric(time, progenitor, ni_mass, log10_mdot, beta, rcsm, esn, **kwargs):
|
|
2126
|
+
"""
|
|
2127
|
+
Bolometric luminosity for a Type II supernova based on Sarin et al. 2025 surrogate model
|
|
2128
|
+
to stella grid in Moriya et al. 2023
|
|
2129
|
+
|
|
2130
|
+
:param time: Time in days in source frame
|
|
2131
|
+
:param progenitor: in solar masses
|
|
2132
|
+
:param ni_mass: in solar masses
|
|
2133
|
+
:param log10_mdot: in solar masses per year
|
|
2134
|
+
:param beta: dimensionless
|
|
2135
|
+
:param rcsm: in 10^14 cm
|
|
2136
|
+
:param esn: in 10^51
|
|
2137
|
+
:param kwargs: None
|
|
2138
|
+
:return: bolometric luminosity in erg/s
|
|
2139
|
+
"""
|
|
2140
|
+
from redback_surrogates.supernovamodels import typeII_lbol
|
|
2141
|
+
tt, lbol = typeII_lbol(time=time, progenitor=progenitor, ni_mass=ni_mass,
|
|
2142
|
+
log10_mdot=log10_mdot, beta=beta, rcsm=rcsm, esn=esn, **kwargs)
|
|
2143
|
+
lbol_func = interp1d(tt, y=lbol, bounds_error=False, fill_value='extrapolate')
|
|
2144
|
+
return lbol_func(time)
|
|
2145
|
+
|
|
2146
|
+
@citation_wrapper("https://ui.adsabs.harvard.edu/abs/2025arXiv250602107S/abstract, https://ui.adsabs.harvard.edu/abs/2023PASJ...75..634M/abstract")
|
|
2147
|
+
def typeII_photosphere_properties(time, progenitor, ni_mass, log10_mdot, beta, rcsm, esn, **kwargs):
|
|
2148
|
+
"""
|
|
2149
|
+
Photosphere properties for a Type II supernova based on Sarin et al. 2025 surrogate model
|
|
2150
|
+
to stella grid in Moriya et al. 2023
|
|
2151
|
+
|
|
2152
|
+
:param time: Time in days in source frame
|
|
2153
|
+
:param progenitor: in solar masses
|
|
2154
|
+
:param ni_mass: in solar masses
|
|
2155
|
+
:param log10_mdot: in solar masses per year
|
|
2156
|
+
:param beta: dimensionless
|
|
2157
|
+
:param rcsm: in 10^14 cm
|
|
2158
|
+
:param esn: in 10^51
|
|
2159
|
+
:param kwargs: None
|
|
2160
|
+
:return: photosphere properties (temperature in K, radius in cm)
|
|
2161
|
+
"""
|
|
2162
|
+
from redback_surrogates.supernovamodels import typeII_photosphere
|
|
2163
|
+
tt, temp, rad = typeII_photosphere(time=time, progenitor=progenitor, ni_mass=ni_mass,
|
|
2164
|
+
log10_mdot=log10_mdot, beta=beta, rcsm=rcsm, esn=esn, **kwargs)
|
|
2165
|
+
temp_func = interp1d(tt, y=temp, bounds_error=False, fill_value='extrapolate')
|
|
2166
|
+
rad_func = interp1d(tt, y=rad, bounds_error=False, fill_value='extrapolate')
|
|
2167
|
+
return temp_func(time), rad_func(time)
|
|
2168
|
+
|
|
2169
|
+
@citation_wrapper("https://ui.adsabs.harvard.edu/abs/2025arXiv250602107S/abstract, https://ui.adsabs.harvard.edu/abs/2023PASJ...75..634M/abstract")
|
|
2170
|
+
def typeII_surrogate_sarin25(time, redshift, progenitor, ni_mass, log10_mdot, beta, rcsm, esn, **kwargs):
|
|
2171
|
+
"""
|
|
2172
|
+
Type II supernova model based on Sarin et al. 2025 surrogate model
|
|
2173
|
+
to stella grid in Moriya et al. 2023
|
|
2174
|
+
|
|
2175
|
+
:param time: Time in days in observer frame
|
|
2176
|
+
:param redshift: redshift
|
|
2177
|
+
:param progenitor: in solar masses
|
|
2178
|
+
:param ni_mass: in solar masses
|
|
2179
|
+
:param log10_mdot: in solar masses per year
|
|
2180
|
+
:param beta: dimensionless
|
|
2181
|
+
:param rcsm: in 10^14 cm
|
|
2182
|
+
:param esn: in 10^51
|
|
2183
|
+
:param kwargs: Additional parameters for the model, such as:
|
|
2184
|
+
:param frequency: Required if output_format is 'flux_density'.
|
|
2185
|
+
frequency to calculate - Must be same length as time array or a single number).
|
|
2186
|
+
:param bands: Required if output_format is 'magnitude' or 'flux'.
|
|
2187
|
+
:param output_format: 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
2188
|
+
:param lambda_array: Optional argument to set your desired wavelength array (in Angstroms) to evaluate the SED on.
|
|
2189
|
+
:param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
|
|
2190
|
+
:return: set by output format - 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
2191
|
+
"""
|
|
2192
|
+
from redback_surrogates.supernovamodels import typeII_spectra
|
|
2193
|
+
cosmology = kwargs.get('cosmology', cosmo)
|
|
2194
|
+
dl = cosmology.luminosity_distance(redshift).cgs
|
|
2195
|
+
|
|
2196
|
+
# Get the rest-frame spectrum using typeII_spectra
|
|
2197
|
+
spectra_output = typeII_spectra(
|
|
2198
|
+
progenitor=progenitor,
|
|
2199
|
+
ni_mass=ni_mass,
|
|
2200
|
+
log10_mdot=log10_mdot,
|
|
2201
|
+
beta=beta,
|
|
2202
|
+
rcsm=rcsm,
|
|
2203
|
+
esn=esn,
|
|
2204
|
+
**kwargs
|
|
2205
|
+
)
|
|
2206
|
+
|
|
2207
|
+
# Extract components from the output
|
|
2208
|
+
rest_spectrum = spectra_output.spectrum # erg/s/Hz in rest frame
|
|
2209
|
+
standard_freqs = spectra_output.frequency.value # Angstrom in rest frame
|
|
2210
|
+
standard_times = spectra_output.time.value # days in rest frame
|
|
2211
|
+
|
|
2212
|
+
# Apply cosmological dimming
|
|
2213
|
+
observed_spectrum = rest_spectrum / (4 * np.pi * dl ** 2)
|
|
2214
|
+
|
|
2215
|
+
# Handle different output formats
|
|
2216
|
+
if kwargs.get('output_format') == 'flux_density':
|
|
2217
|
+
# Use redback's K-correction utilities
|
|
2218
|
+
frequency = kwargs['frequency']
|
|
2219
|
+
frequency, time = calc_kcorrected_properties(frequency=frequency, time=time, redshift=redshift)
|
|
2220
|
+
|
|
2221
|
+
# Convert wavelengths to frequencies for interpolation
|
|
2222
|
+
nu_array = lambda_to_nu(standard_freqs)
|
|
2223
|
+
|
|
2224
|
+
# Convert spectrum from erg/s/Hz to erg/s/cm²/Hz (already done above)
|
|
2225
|
+
# Convert to wavelength density for astropy conversion
|
|
2226
|
+
spectra_lambda = spectra_lambda.to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom)
|
|
2227
|
+
|
|
2228
|
+
# Convert to mJy using astropy
|
|
2229
|
+
fmjy = spectra_lambda.to(uu.mJy,
|
|
2230
|
+
equivalencies=uu.spectral_density(wav=standard_freqs * uu.Angstrom)).value
|
|
2231
|
+
|
|
2232
|
+
# Create interpolator
|
|
2233
|
+
flux_interpolator = RegularGridInterpolator(
|
|
2234
|
+
(standard_times, nu_array),
|
|
2235
|
+
fmjy,
|
|
2236
|
+
bounds_error=False,
|
|
2237
|
+
fill_value=0.0
|
|
2238
|
+
)
|
|
2239
|
+
|
|
2240
|
+
# Prepare points for interpolation
|
|
2241
|
+
if isinstance(frequency, (int, float)):
|
|
2242
|
+
frequency = np.ones_like(time) * frequency
|
|
2243
|
+
|
|
2244
|
+
# Create points for evaluation
|
|
2245
|
+
points = np.column_stack((time, frequency))
|
|
2246
|
+
|
|
2247
|
+
# Return interpolated flux
|
|
2248
|
+
return flux_interpolator(points)
|
|
2249
|
+
|
|
2250
|
+
else:
|
|
2251
|
+
# Create denser grid for output (in rest frame)
|
|
2252
|
+
new_rest_times = np.geomspace(np.min(standard_times), np.max(standard_times), 200)
|
|
2253
|
+
new_rest_freqs = np.geomspace(np.min(standard_freqs), np.max(standard_freqs), 200)
|
|
2254
|
+
|
|
2255
|
+
# Create interpolator for the spectrum in rest frame
|
|
2256
|
+
spectra_func = RegularGridInterpolator(
|
|
2257
|
+
(standard_times, standard_freqs),
|
|
2258
|
+
observed_spectrum.value,
|
|
2259
|
+
bounds_error=False,
|
|
2260
|
+
fill_value=0.0
|
|
2261
|
+
)
|
|
2262
|
+
|
|
2263
|
+
# Create meshgrid for new grid points
|
|
2264
|
+
tt_mesh, ff_mesh = np.meshgrid(new_rest_times, new_rest_freqs, indexing='ij')
|
|
2265
|
+
points_to_evaluate = np.column_stack((tt_mesh.ravel(), ff_mesh.ravel()))
|
|
2266
|
+
|
|
2267
|
+
# Interpolate spectrum onto new grid
|
|
2268
|
+
interpolated_values = spectra_func(points_to_evaluate)
|
|
2269
|
+
interpolated_spectrum = interpolated_values.reshape(tt_mesh.shape) * observed_spectrum.unit
|
|
2270
|
+
|
|
2271
|
+
# Convert times to observer frame
|
|
2272
|
+
time_observer_frame = new_rest_times * (1 + redshift)
|
|
2273
|
+
|
|
2274
|
+
# Convert wavelengths to observer frame
|
|
2275
|
+
lambda_observer_frame = new_rest_freqs * (1 + redshift)
|
|
2276
|
+
|
|
2277
|
+
# Convert spectrum units using astropy
|
|
2278
|
+
interpolated_spectrum = interpolated_spectrum.to(
|
|
2279
|
+
uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
|
|
2280
|
+
equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom)
|
|
2281
|
+
)
|
|
2282
|
+
|
|
2283
|
+
# Create output structure
|
|
2284
|
+
if kwargs.get('output_format') == 'spectra':
|
|
2285
|
+
return namedtuple('output', ['time', 'lambdas', 'spectra'])(
|
|
2286
|
+
time=time_observer_frame,
|
|
2287
|
+
lambdas=lambda_observer_frame,
|
|
2288
|
+
spectra=interpolated_spectrum
|
|
2289
|
+
)
|
|
2290
|
+
else:
|
|
2291
|
+
# Get correct output format using redback utility
|
|
2292
|
+
return sed.get_correct_output_format_from_spectra(
|
|
2293
|
+
time=time, # Original observer frame time for evaluation
|
|
2294
|
+
time_eval=time_observer_frame,
|
|
2295
|
+
spectra=interpolated_spectrum,
|
|
2296
|
+
lambda_array=lambda_observer_frame,
|
|
2297
|
+
time_spline_degree=1,
|
|
2298
|
+
**kwargs
|
|
2299
|
+
)
|