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
@@ -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,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
- logger.warning(f'No prior file found for model {model}. '
53
- f'Perhaps you also want to set up the prior for the base model? '
54
- f'Or you may need to set up your prior explicitly.')
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
- self.sed[self.mask] = \
355
- self.FLUX_CONST * (self.r_photosphere[self.mask]**2 / self.cutoff_wavelength /
356
- self.wavelength[self.mask] ** 4) \
357
- / np.expm1(self.X_CONST / self.wavelength[self.mask] / self.temperature[self.mask])
358
- self.sed[~self.mask] = \
359
- self.FLUX_CONST * (self.r_photosphere[~self.mask]**2 / self.wavelength[~self.mask]**5) \
360
- / np.expm1(self.X_CONST / self.wavelength[~self.mask] / self.temperature[~self.mask])
361
- # Apply renormalisation
362
- self.sed *= self.norms[np.searchsorted(self.unique_times, self.time)]
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, time: np.ndarray, luminosity: np.ndarray, frequency: Union[np.ndarray, float],
478
- sed: Union[_SED, Blackbody], luminosity_distance: float, line_wavelength: float = 7.5e3,
479
- line_width: float = 500, line_time: float = 50, line_duration: float = 25,
480
- line_amplitude: float = 0.3, **kwargs: None) -> None:
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 accounting for absorption lines
483
-
484
- :param time: time in source frame
485
- :param luminosity: luminosity in cgs
486
- :param frequency: frequency to calculate in Hz - Must be same length as time array or a single number.
487
- In source frame.
488
- :param sed: instantiated SED class object.
489
- :param luminosity_distance: luminosity_distance in cm
490
- :param line_wavelength: line wavelength in angstrom
491
- :param line_width: line width in angstrom
492
- :param line_time: line time
493
- :param line_duration: line duration
494
- :param line_amplitude: line amplitude
495
- :param kwargs: None
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
- super(Line, self).__init__(frequency=frequency, luminosity_distance=luminosity_distance, length=len(time))
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
- # Mostly from Mosfit/SEDs
634
+ """
635
+ Compute the modified SED with the absorption line and store the result internally.
636
+ """
516
637
  self._set_sed()
517
- return self.flux_density
638
+ return self._flux_density
518
639
 
519
640
  def _set_sed(self):
520
- amplitude = self.line_amplitude * np.exp(-0.5 * ((self.time - self.line_time) / self.line_duration) ** 2)
521
- self.sed = self.SED.sed * (1 - amplitude)
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
- amplitude *= self.luminosity / (self.line_width * (2 * np.pi) ** 0.5)
656
+ # Get the baseline SED (shape (n_freq, n_time))
657
+ sed_base = self.SED.sed
524
658
 
525
- amp_new = np.exp(-0.5 * ((self.wavelength - self.line_wavelength) / self.line_width) ** 2)
526
- self.sed += amplitude * amp_new
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')
@@ -1,5 +1,5 @@
1
1
  import numpy as np
2
- from sncosmo import TimeSeriesSource, Model, get_bandpass
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
- logger.info('Using custom model. Making a SNCosmo wrapper for this model')
195
- self.sncosmo_model = self._make_sncosmo_wrapper_for_user_model()
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):