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.
Files changed (38) hide show
  1. redback/__init__.py +1 -1
  2. redback/filters.py +57 -45
  3. redback/likelihoods.py +274 -6
  4. redback/model_library.py +2 -2
  5. redback/plotting.py +5 -3
  6. redback/priors/blackbody_spectrum_at_z.prior +3 -0
  7. redback/priors/bpl_cooling_envelope.prior +9 -0
  8. redback/priors/gaussianrise_cooling_envelope.prior +1 -5
  9. redback/priors/gaussianrise_cooling_envelope_bolometric.prior +1 -5
  10. redback/priors/powerlaw_plus_blackbody.prior +12 -0
  11. redback/priors/powerlaw_plus_blackbody_spectrum_at_z.prior +13 -0
  12. redback/priors/smooth_exponential_powerlaw_cooling_envelope_bolometric.prior +9 -0
  13. redback/priors/sn_nickel_fallback.prior +9 -0
  14. redback/priors/wr_bh_merger.prior +10 -0
  15. redback/priors/wr_bh_merger_bolometric.prior +8 -0
  16. redback/priors.py +14 -3
  17. redback/sed.py +185 -41
  18. redback/simulate_transients.py +13 -3
  19. redback/tables/filters.csv +260 -258
  20. redback/tables/qdot_rosswogkorobkin24.npz +0 -0
  21. redback/transient_models/afterglow_models.py +32 -16
  22. redback/transient_models/combined_models.py +16 -11
  23. redback/transient_models/extinction_models.py +310 -84
  24. redback/transient_models/gaussianprocess_models.py +1 -12
  25. redback/transient_models/kilonova_models.py +3 -3
  26. redback/transient_models/phase_models.py +97 -43
  27. redback/transient_models/phenomenological_models.py +172 -0
  28. redback/transient_models/spectral_models.py +101 -0
  29. redback/transient_models/stellar_interaction_models.py +254 -0
  30. redback/transient_models/supernova_models.py +272 -33
  31. redback/transient_models/tde_models.py +193 -54
  32. redback/utils.py +34 -7
  33. {redback-1.12.0.dist-info → redback-1.12.1.dist-info}/METADATA +7 -4
  34. {redback-1.12.0.dist-info → redback-1.12.1.dist-info}/RECORD +37 -28
  35. {redback-1.12.0.dist-info → redback-1.12.1.dist-info}/WHEEL +1 -1
  36. redback/tables/qdot_rosswogkorobkin24.pck +0 -0
  37. {redback-1.12.0.dist-info → redback-1.12.1.dist-info}/licenses/LICENCE.md +0 -0
  38. {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
- for ii in range(len(frequency)):
837
- ss = kwargs['sed'](time=time, temperature=photo.photosphere_temperature,
838
- r_photosphere=photo.r_photosphere, frequency=frequency[ii],
839
- luminosity_distance=dl, cutoff_wavelength=cutoff_wavelength, luminosity=lbol)
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
- equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom))
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: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
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
- full_sed = np.zeros((len(time), len(frequency)))
1469
- for ii in range(len(frequency)):
1470
- ss = sed.CutoffBlackbody(time=time, temperature=photo.photosphere_temperature,
1471
- r_photosphere=photo.r_photosphere, frequency=frequency[ii],
1472
- luminosity_distance=dl, cutoff_wavelength=cutoff_wavelength, luminosity=lbol)
1473
- sed_2 = sed.Line(time=time, luminosity=lbol, frequency=frequency[ii], luminosity_distance=dl,
1474
- sed=ss, line_wavelength=line_wavelength,
1475
- line_width=line_width, line_time=line_time,
1476
- line_duration=line_duration, line_amplitude=line_amplitude)
1477
- full_sed[:,ii] = sed_2.flux_density.to(uu.mJy).value
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(wav=lambdas_observer_frame * uu.Angstrom))
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
- for ii in range(len(frequency)):
1800
- ss = kwargs['sed'](time=time_temp/day_to_s, temperature=photo.photosphere_temperature,
1801
- r_photosphere=photo.r_photosphere, frequency=frequency[ii],
1802
- luminosity_distance=dl, cutoff_wavelength=cutoff_wavelength, luminosity=output.bolometric_luminosity)
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
+ )