redback 1.0.1__py3-none-any.whl → 1.0.3__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 +4 -0
- redback/constraints.py +46 -25
- redback/eos.py +1 -0
- redback/get_data/fink.py +1 -1
- redback/get_data/lasair.py +3 -4
- redback/get_data/swift.py +7 -7
- redback/interaction_processes.py +1 -4
- redback/likelihoods.py +207 -21
- redback/model_library.py +2 -2
- redback/plotting.py +10 -10
- redback/priors/bazin_sne.prior +5 -0
- redback/priors/csm_interaction.prior +6 -7
- redback/priors/csm_nickel.prior +3 -3
- redback/priors/csm_shock_and_arnett.prior +11 -0
- redback/priors/csm_shock_and_arnett_bolometric.prior +10 -0
- redback/priors/csm_shock_breakout.prior +7 -0
- redback/priors/nicholl_bns.prior +2 -1
- redback/priors/one_comp_kne_rosswog_heatingrate.prior +5 -0
- redback/priors/pwn.prior +7 -0
- redback/priors/shocked_cocoon.prior +6 -6
- redback/priors/sn_fallback.prior +8 -0
- redback/priors/stream_stream_tde.prior +10 -0
- redback/priors/stream_stream_tde_bolometric.prior +9 -0
- redback/priors/tde_analytical.prior +5 -5
- redback/priors/tde_analytical_bolometric.prior +6 -4
- redback/priors/tde_fallback.prior +9 -0
- redback/priors/tde_fallback_bolometric.prior +6 -0
- redback/priors/tde_synchrotron.prior +6 -0
- redback/priors/tophat_from_emulator.prior +9 -0
- redback/priors/two_comp_kne_rosswog_heatingrate.prior +9 -0
- redback/priors/two_layer_stratified_kilonova.prior +1 -1
- redback/priors/villar_sne.prior +7 -0
- redback/priors.py +12 -1
- redback/sed.py +194 -2
- redback/simulate_transients.py +71 -35
- redback/tables/GRBs_w_redshift.txt +430 -413
- redback/tables/LGRB_table.txt +70 -6
- redback/tables/SGRB_table.txt +139 -135
- redback/tables/filters.csv +14 -0
- redback/tables/qdot_rosswogkorobkin24.pck +0 -0
- redback/tables/ztf.tar.gz +0 -0
- redback/transient/afterglow.py +17 -7
- redback/transient/kilonova.py +6 -3
- redback/transient/prompt.py +14 -4
- redback/transient/supernova.py +7 -3
- redback/transient/tde.py +6 -3
- redback/transient/transient.py +29 -12
- redback/transient_models/afterglow_models.py +152 -146
- redback/transient_models/combined_models.py +69 -48
- redback/transient_models/extinction_models.py +6 -6
- redback/transient_models/general_synchrotron_models.py +518 -0
- redback/transient_models/integrated_flux_afterglow_models.py +2 -2
- redback/transient_models/kilonova_models.py +310 -61
- redback/transient_models/magnetar_driven_ejecta_models.py +2 -2
- redback/transient_models/magnetar_models.py +1 -1
- redback/transient_models/phenomenological_models.py +69 -1
- redback/transient_models/shock_powered_models.py +159 -110
- redback/transient_models/supernova_models.py +211 -43
- redback/transient_models/tde_models.py +975 -5
- redback/utils.py +309 -16
- {redback-1.0.1.dist-info → redback-1.0.3.dist-info}/METADATA +46 -6
- {redback-1.0.1.dist-info → redback-1.0.3.dist-info}/RECORD +65 -49
- {redback-1.0.1.dist-info → redback-1.0.3.dist-info}/WHEEL +1 -1
- redback/tables/ztf_obslog.csv +0 -106649
- {redback-1.0.1.dist-info → redback-1.0.3.dist-info}/LICENCE.md +0 -0
- {redback-1.0.1.dist-info → redback-1.0.3.dist-info}/top_level.txt +0 -0
|
@@ -3,7 +3,7 @@ import redback.interaction_processes as ip
|
|
|
3
3
|
import redback.sed as sed
|
|
4
4
|
import redback.photosphere as photosphere
|
|
5
5
|
from redback.utils import calc_kcorrected_properties, citation_wrapper, calc_tfb, lambda_to_nu, \
|
|
6
|
-
calc_ABmag_from_flux_density, calc_flux_density_from_ABmag
|
|
6
|
+
calc_ABmag_from_flux_density, calc_flux_density_from_ABmag, bands_to_frequency
|
|
7
7
|
import redback.constants as cc
|
|
8
8
|
import redback.transient_models.phenomenological_models as pm
|
|
9
9
|
|
|
@@ -262,6 +262,8 @@ def gaussianrise_cooling_envelope_bolometric(time, peak_time, sigma_t, mbh_6, st
|
|
|
262
262
|
bolometric version for fitting the bolometric lightcurve
|
|
263
263
|
|
|
264
264
|
:param time: time in source frame in days
|
|
265
|
+
:param peak_time: peak time in days
|
|
266
|
+
:param sigma_t: the sharpness of the Gaussian in days
|
|
265
267
|
:param mbh_6: mass of supermassive black hole in units of 10^6 solar mass
|
|
266
268
|
:param stellar_mass: stellar mass in units of solar masses
|
|
267
269
|
:param eta: SMBH feedback efficiency (typical range: etamin - 0.1)
|
|
@@ -299,6 +301,8 @@ def gaussianrise_cooling_envelope(time, redshift, peak_time, sigma_t, mbh_6, ste
|
|
|
299
301
|
|
|
300
302
|
:param time: time in observer frame in days
|
|
301
303
|
:param redshift: redshift
|
|
304
|
+
:param peak_time: peak time in days
|
|
305
|
+
:param sigma_t: the sharpness of the Gaussian in days
|
|
302
306
|
:param mbh_6: mass of supermassive black hole in units of 10^6 solar mass
|
|
303
307
|
:param stellar_mass: stellar mass in units of solar masses
|
|
304
308
|
:param eta: SMBH feedback efficiency (typical range: etamin - 0.1)
|
|
@@ -408,6 +412,127 @@ def gaussianrise_cooling_envelope(time, redshift, peak_time, sigma_t, mbh_6, ste
|
|
|
408
412
|
output.append(flux_den_interp_func[freq](tt * cc.day_to_s))
|
|
409
413
|
return np.array(output)
|
|
410
414
|
|
|
415
|
+
@citation_wrapper('https://arxiv.org/abs/2307.15121,https://ui.adsabs.harvard.edu/abs/2022arXiv220707136M/abstract')
|
|
416
|
+
def bpl_cooling_envelope(time, redshift, peak_time, alpha_1, alpha_2, mbh_6, stellar_mass, eta, alpha, beta, **kwargs):
|
|
417
|
+
"""
|
|
418
|
+
Full lightcurve, with gaussian rise till fallback time and then the metzger tde model,
|
|
419
|
+
photometric version where each band is fit/joint separately
|
|
420
|
+
|
|
421
|
+
:param time: time in observer frame in days
|
|
422
|
+
:param redshift: redshift
|
|
423
|
+
:param peak_time: peak time in days
|
|
424
|
+
:param alpha_1: power law index for first power law
|
|
425
|
+
:param alpha_2: power law index for second power law (should be positive)
|
|
426
|
+
:param mbh_6: mass of supermassive black hole in units of 10^6 solar mass
|
|
427
|
+
:param stellar_mass: stellar mass in units of solar masses
|
|
428
|
+
:param eta: SMBH feedback efficiency (typical range: etamin - 0.1)
|
|
429
|
+
:param alpha: disk viscosity
|
|
430
|
+
:param beta: TDE penetration factor (typical range: 1 - beta_max)
|
|
431
|
+
:param kwargs: Additional parameters
|
|
432
|
+
:param xi: Optional argument (default set to one) to change the point where lightcurve switches from Gaussian rise to cooling envelope.
|
|
433
|
+
stitching_point = xi * tfb (where tfb is fallback time). So a xi=1 means the stitching point is at fallback time.
|
|
434
|
+
:param frequency: Required if output_format is 'flux_density'.
|
|
435
|
+
frequency to calculate - Must be same length as time array or a single number).
|
|
436
|
+
:param bands: Required if output_format is 'magnitude' or 'flux'.
|
|
437
|
+
:param output_format: 'flux_density', 'magnitude', 'flux'
|
|
438
|
+
:param lambda_array: Optional argument to set your desired wavelength array (in Angstroms) to evaluate the SED on.
|
|
439
|
+
:param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
|
|
440
|
+
:return: set by output format - 'flux_density', 'magnitude', 'flux'
|
|
441
|
+
"""
|
|
442
|
+
binding_energy_const = kwargs.get('binding_energy_const', 0.8)
|
|
443
|
+
tfb_sf = calc_tfb(binding_energy_const, mbh_6, stellar_mass) # source frame
|
|
444
|
+
tfb_obf = tfb_sf * (1. + redshift) # observer frame
|
|
445
|
+
xi = kwargs.get('xi', 1.)
|
|
446
|
+
output = _cooling_envelope(mbh_6, stellar_mass, eta, alpha, beta, **kwargs)
|
|
447
|
+
cosmology = kwargs.get('cosmology', cosmo)
|
|
448
|
+
dl = cosmology.luminosity_distance(redshift).cgs.value
|
|
449
|
+
stitching_point = xi * tfb_obf
|
|
450
|
+
|
|
451
|
+
# normalisation term in observer frame
|
|
452
|
+
f1 = pm.exponential_powerlaw(time=stitching_point, a_1=1., tpeak=peak_time * cc.day_to_s,
|
|
453
|
+
alpha_1=alpha_1, alpha_2=alpha_2)
|
|
454
|
+
|
|
455
|
+
if kwargs['output_format'] == 'flux_density':
|
|
456
|
+
frequency = kwargs['frequency']
|
|
457
|
+
if isinstance(frequency, float):
|
|
458
|
+
frequency = np.ones(len(time)) * frequency
|
|
459
|
+
|
|
460
|
+
# convert to source frame time and frequency
|
|
461
|
+
frequency, time = calc_kcorrected_properties(frequency=frequency, redshift=redshift, time=time)
|
|
462
|
+
unique_frequency = np.sort(np.unique(frequency))
|
|
463
|
+
|
|
464
|
+
# source frame
|
|
465
|
+
f2 = sed.blackbody_to_flux_density(temperature=output.photosphere_temperature[0],
|
|
466
|
+
r_photosphere=output.photosphere_radius[0],
|
|
467
|
+
dl=dl, frequency=unique_frequency).to(uu.mJy)
|
|
468
|
+
norms = f2.value / f1
|
|
469
|
+
norm_dict = dict(zip(unique_frequency, norms))
|
|
470
|
+
|
|
471
|
+
# build flux density function for each frequency
|
|
472
|
+
flux_den_interp_func = {}
|
|
473
|
+
for freq in unique_frequency:
|
|
474
|
+
tt_pre_fb = np.linspace(0, stitching_point / cc.day_to_s, 200) * cc.day_to_s
|
|
475
|
+
tt_post_fb = xi * (output.time_temp * (1 + redshift))
|
|
476
|
+
total_time = np.concatenate([tt_pre_fb, tt_post_fb])
|
|
477
|
+
f1 = pm.exponential_powerlaw(time=tt_pre_fb, a_1=norm_dict[freq],
|
|
478
|
+
tpeak=peak_time * cc.day_to_s, alpha_1=alpha_1, alpha_2=alpha_2)
|
|
479
|
+
f2 = sed.blackbody_to_flux_density(temperature=output.photosphere_temperature,
|
|
480
|
+
r_photosphere=output.photosphere_radius,
|
|
481
|
+
dl=dl, frequency=freq).to(uu.mJy)
|
|
482
|
+
flux_den = np.concatenate([f1, f2.value])
|
|
483
|
+
flux_den_interp_func[freq] = interp1d(total_time, flux_den, fill_value='extrapolate')
|
|
484
|
+
|
|
485
|
+
# interpolate onto actual observed frequency and time values
|
|
486
|
+
flux_density = []
|
|
487
|
+
for freq, tt in zip(frequency, time):
|
|
488
|
+
flux_density.append(flux_den_interp_func[freq](tt * cc.day_to_s))
|
|
489
|
+
flux_density = flux_density * uu.mJy
|
|
490
|
+
return flux_density.to(uu.mJy).value
|
|
491
|
+
else:
|
|
492
|
+
bands = kwargs['bands']
|
|
493
|
+
if isinstance(bands, str):
|
|
494
|
+
bands = [str(bands) for x in range(len(time))]
|
|
495
|
+
|
|
496
|
+
unique_bands = np.unique(bands)
|
|
497
|
+
temp_kwargs = kwargs.copy()
|
|
498
|
+
temp_kwargs['bands'] = unique_bands
|
|
499
|
+
f2 = cooling_envelope(time=0., redshift=redshift,
|
|
500
|
+
mbh_6=mbh_6, stellar_mass=stellar_mass, eta=eta, alpha=alpha, beta=beta,
|
|
501
|
+
**temp_kwargs)
|
|
502
|
+
if kwargs['output_format'] == 'magnitude':
|
|
503
|
+
# make the normalisation in fmjy to avoid magnitude normalisation problems
|
|
504
|
+
_f2mjy = calc_flux_density_from_ABmag(f2).value
|
|
505
|
+
norms = _f2mjy / f1
|
|
506
|
+
else:
|
|
507
|
+
norms = f2 / f1
|
|
508
|
+
|
|
509
|
+
if isinstance(norms, float):
|
|
510
|
+
norms = np.ones(len(time)) * norms
|
|
511
|
+
norm_dict = dict(zip(unique_bands, norms))
|
|
512
|
+
|
|
513
|
+
flux_den_interp_func = {}
|
|
514
|
+
for band in unique_bands:
|
|
515
|
+
tt_pre_fb = np.linspace(0, stitching_point / cc.day_to_s, 100) * cc.day_to_s
|
|
516
|
+
tt_post_fb = output.time_temp * (1 + redshift)
|
|
517
|
+
total_time = np.concatenate([tt_pre_fb, tt_post_fb])
|
|
518
|
+
f1 = pm.exponential_powerlaw(time=tt_pre_fb, a_1=norm_dict[band],
|
|
519
|
+
tpeak=peak_time * cc.day_to_s, alpha_1=alpha_1, alpha_2=alpha_2)
|
|
520
|
+
if kwargs['output_format'] == 'magnitude':
|
|
521
|
+
f1 = calc_ABmag_from_flux_density(f1).value
|
|
522
|
+
temp_kwargs = kwargs.copy()
|
|
523
|
+
temp_kwargs['bands'] = band
|
|
524
|
+
f2 = cooling_envelope(time=output.time_since_fb / cc.day_to_s, redshift=redshift,
|
|
525
|
+
mbh_6=mbh_6, stellar_mass=stellar_mass, eta=eta, alpha=alpha, beta=beta,
|
|
526
|
+
**temp_kwargs)
|
|
527
|
+
flux_den = np.concatenate([f1, f2])
|
|
528
|
+
flux_den_interp_func[band] = interp1d(total_time, flux_den, fill_value='extrapolate')
|
|
529
|
+
|
|
530
|
+
# interpolate onto actual observed band and time values
|
|
531
|
+
output = []
|
|
532
|
+
for freq, tt in zip(bands, time):
|
|
533
|
+
output.append(flux_den_interp_func[freq](tt * cc.day_to_s))
|
|
534
|
+
return np.array(output)
|
|
535
|
+
|
|
411
536
|
@citation_wrapper('redback')
|
|
412
537
|
def tde_analytical_bolometric(time, l0, t_0_turn, **kwargs):
|
|
413
538
|
"""
|
|
@@ -471,7 +596,7 @@ def tde_analytical(time, redshift, l0, t_0_turn, **kwargs):
|
|
|
471
596
|
else:
|
|
472
597
|
time_obs = time
|
|
473
598
|
lambda_observer_frame = kwargs.get('lambda_array', np.geomspace(100, 60000, 100))
|
|
474
|
-
time_temp = np.geomspace(0.1,
|
|
599
|
+
time_temp = np.geomspace(0.1, 1000, 300) # in days
|
|
475
600
|
time_observer_frame = time_temp * (1. + redshift)
|
|
476
601
|
frequency, time = calc_kcorrected_properties(frequency=lambda_to_nu(lambda_observer_frame),
|
|
477
602
|
redshift=redshift, time=time_observer_frame)
|
|
@@ -494,6 +619,851 @@ def tde_analytical(time, redshift, l0, t_0_turn, **kwargs):
|
|
|
494
619
|
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
495
620
|
**kwargs)
|
|
496
621
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
622
|
+
def _initialize_mosfit_tde_model():
|
|
623
|
+
"""
|
|
624
|
+
Initializtion function to load/process data.
|
|
625
|
+
|
|
626
|
+
Loads and interpolates tde simulation data. Simulation data is
|
|
627
|
+
from Guillochon 2013 and can be found on astrocrash.net.
|
|
628
|
+
|
|
629
|
+
:return: Named tuple with several outputs
|
|
630
|
+
"""
|
|
631
|
+
|
|
632
|
+
import os
|
|
633
|
+
dirname = os.path.dirname(__file__)
|
|
634
|
+
data_dir = f"{dirname}/../tables/guillochon_tde_data"
|
|
635
|
+
G_cgs = cc.graviational_constant
|
|
636
|
+
Mhbase = 1.0e6 * cc.solar_mass
|
|
637
|
+
|
|
638
|
+
gammas = ['4-3', '5-3']
|
|
639
|
+
|
|
640
|
+
beta_slope = {gammas[0]: [], gammas[1]: []}
|
|
641
|
+
beta_yinter = {gammas[0]: [], gammas[1]: []}
|
|
642
|
+
sim_beta = {gammas[0]: [], gammas[1]: []}
|
|
643
|
+
mapped_time = {gammas[0]: [], gammas[1]: []}
|
|
644
|
+
premaptime = {gammas[0]: [], gammas[1]: []}
|
|
645
|
+
premapdmdt = {gammas[0]: [], gammas[1]: []}
|
|
646
|
+
|
|
647
|
+
for g in gammas:
|
|
648
|
+
dmdedir = os.path.join(data_dir, g)
|
|
649
|
+
|
|
650
|
+
sim_beta_files = os.listdir(dmdedir)
|
|
651
|
+
simbeta = [float(b[:-4]) for b in sim_beta_files]
|
|
652
|
+
sortedindices = np.argsort(simbeta)
|
|
653
|
+
simbeta = [simbeta[i] for i in sortedindices]
|
|
654
|
+
sim_beta_files = [sim_beta_files[i] for i in sortedindices]
|
|
655
|
+
sim_beta[g].extend(simbeta)
|
|
656
|
+
|
|
657
|
+
time = {}
|
|
658
|
+
dmdt = {}
|
|
659
|
+
ipeak = {}
|
|
660
|
+
_mapped_time = {}
|
|
661
|
+
|
|
662
|
+
e, d = np.loadtxt(os.path.join(dmdedir, sim_beta_files[0]))
|
|
663
|
+
ebound = e[e < 0]
|
|
664
|
+
dmdebound = d[e < 0]
|
|
665
|
+
|
|
666
|
+
if min(dmdebound) < 0:
|
|
667
|
+
print('beta, gamma, negative dmde bound:', sim_beta[g], g, dmdebound[dmdebound < 0])
|
|
668
|
+
|
|
669
|
+
dedt = (1.0 / 3.0) * (-2.0 * ebound) ** (5.0 / 2.0) / (2.0 * np.pi * G_cgs * Mhbase)
|
|
670
|
+
time['lo'] = np.log10((2.0 * np.pi * G_cgs * Mhbase) * (-2.0 * ebound) ** (-3.0 / 2.0))
|
|
671
|
+
dmdt['lo'] = np.log10(dmdebound * dedt)
|
|
672
|
+
|
|
673
|
+
ipeak['lo'] = np.argmax(dmdt['lo'])
|
|
674
|
+
|
|
675
|
+
time['lo'] = np.array([time['lo'][:ipeak['lo']], time['lo'][ipeak['lo']:]], dtype=object)
|
|
676
|
+
dmdt['lo'] = np.array([dmdt['lo'][:ipeak['lo']], dmdt['lo'][ipeak['lo']:]], dtype=object)
|
|
677
|
+
|
|
678
|
+
premaptime[g].append(np.copy(time['lo']))
|
|
679
|
+
premapdmdt[g].append(np.copy(dmdt['lo']))
|
|
680
|
+
|
|
681
|
+
for i in range(1, len(sim_beta[g])):
|
|
682
|
+
e, d = np.loadtxt(os.path.join(dmdedir, sim_beta_files[i]))
|
|
683
|
+
ebound = e[e < 0]
|
|
684
|
+
dmdebound = d[e < 0]
|
|
685
|
+
|
|
686
|
+
if min(dmdebound) < 0:
|
|
687
|
+
print('beta, gamma, negative dmde bound:', sim_beta[g], g, dmdebound[dmdebound < 0])
|
|
688
|
+
|
|
689
|
+
dedt = (1.0 / 3.0) * (-2.0 * ebound) ** (5.0 / 2.0) / (2.0 * np.pi * G_cgs * Mhbase)
|
|
690
|
+
time['hi'] = np.log10((2.0 * np.pi * G_cgs * Mhbase) * (-2.0 * ebound) ** (-3.0 / 2.0))
|
|
691
|
+
dmdt['hi'] = np.log10(dmdebound * dedt)
|
|
692
|
+
|
|
693
|
+
ipeak['hi'] = np.argmax(dmdt['hi'])
|
|
694
|
+
|
|
695
|
+
time['hi'] = np.array([time['hi'][:ipeak['hi']], time['hi'][ipeak['hi']:]], dtype=object)
|
|
696
|
+
dmdt['hi'] = np.array([dmdt['hi'][:ipeak['hi']], dmdt['hi'][ipeak['hi']:]], dtype=object)
|
|
697
|
+
|
|
698
|
+
premapdmdt[g].append(np.copy(dmdt['hi']))
|
|
699
|
+
premaptime[g].append(np.copy(time['hi']))
|
|
700
|
+
|
|
701
|
+
_mapped_time['hi'] = []
|
|
702
|
+
_mapped_time['lo'] = []
|
|
703
|
+
|
|
704
|
+
beta_slope[g].append([])
|
|
705
|
+
beta_yinter[g].append([])
|
|
706
|
+
mapped_time[g].append([])
|
|
707
|
+
|
|
708
|
+
for j in [0, 1]:
|
|
709
|
+
if len(time['lo'][j]) < len(time['hi'][j]):
|
|
710
|
+
interp = 'lo'
|
|
711
|
+
nointerp = 'hi'
|
|
712
|
+
else:
|
|
713
|
+
interp = 'hi'
|
|
714
|
+
nointerp = 'lo'
|
|
715
|
+
|
|
716
|
+
_mapped_time[nointerp].append(
|
|
717
|
+
1. / (time[nointerp][j][-1] - time[nointerp][j][0]) *
|
|
718
|
+
(time[nointerp][j] - time[nointerp][j][0]))
|
|
719
|
+
_mapped_time[interp].append(
|
|
720
|
+
1. / (time[interp][j][-1] - time[interp][j][0]) *
|
|
721
|
+
(time[interp][j] - time[interp][j][0]))
|
|
722
|
+
|
|
723
|
+
_mapped_time[interp][j][0] = 0
|
|
724
|
+
_mapped_time[interp][j][-1] = 1
|
|
725
|
+
_mapped_time[nointerp][j][0] = 0
|
|
726
|
+
_mapped_time[nointerp][j][-1] = 1
|
|
727
|
+
|
|
728
|
+
func = interp1d(_mapped_time[interp][j], dmdt[interp][j])
|
|
729
|
+
dmdtinterp = func(_mapped_time[nointerp][j])
|
|
730
|
+
|
|
731
|
+
if interp == 'hi':
|
|
732
|
+
slope = ((dmdtinterp - dmdt['lo'][j]) /
|
|
733
|
+
(sim_beta[g][i] - sim_beta[g][i - 1]))
|
|
734
|
+
else:
|
|
735
|
+
slope = ((dmdt['hi'][j] - dmdtinterp) /
|
|
736
|
+
(sim_beta[g][i] - sim_beta[g][i - 1]))
|
|
737
|
+
beta_slope[g][-1].append(slope)
|
|
738
|
+
|
|
739
|
+
yinter1 = (dmdt[nointerp][j] - beta_slope[g][-1][j] *
|
|
740
|
+
sim_beta[g][i - 1])
|
|
741
|
+
yinter2 = (dmdtinterp - beta_slope[g][-1][j] *
|
|
742
|
+
sim_beta[g][i])
|
|
743
|
+
beta_yinter[g][-1].append((yinter1 + yinter2) / 2.0)
|
|
744
|
+
mapped_time[g][-1].append(
|
|
745
|
+
np.array(_mapped_time[nointerp][j]))
|
|
746
|
+
|
|
747
|
+
time['lo'] = np.copy(time['hi'])
|
|
748
|
+
dmdt['lo'] = np.copy(dmdt['hi'])
|
|
749
|
+
|
|
750
|
+
outs = namedtuple('sim_outputs', ['beta_slope', 'beta_yinter', 'sim_beta', 'mapped_time',
|
|
751
|
+
'premaptime', 'premapdmdt'])
|
|
752
|
+
outs = outs(beta_slope=beta_slope, beta_yinter=beta_yinter, sim_beta=sim_beta,
|
|
753
|
+
mapped_time=mapped_time,premaptime=premaptime, premapdmdt=premapdmdt)
|
|
754
|
+
return outs
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def _tde_mosfit_engine(times, mbh6, mstar, b, efficiency, leddlimit, **kwargs):
|
|
758
|
+
"""
|
|
759
|
+
Produces the processed outputs from simulation data for the TDE model.
|
|
760
|
+
|
|
761
|
+
:param times: A dense array of times in rest frame in days
|
|
762
|
+
:param mbh6: black hole mass in units of 10^6 solar masses
|
|
763
|
+
:param mstar: star mass in units of solar masses
|
|
764
|
+
:param b: Relates to beta and gamma values for the star that's disrupted
|
|
765
|
+
:param efficiency: efficiency of the BH
|
|
766
|
+
:param leddlimit: eddington limit for the BH
|
|
767
|
+
:param kwargs: Additional keyword arguments
|
|
768
|
+
:return: Named tuple with several outputs
|
|
769
|
+
"""
|
|
770
|
+
beta_interp = True
|
|
771
|
+
|
|
772
|
+
outs = _initialize_mosfit_tde_model()
|
|
773
|
+
beta_slope = outs.beta_slope
|
|
774
|
+
beta_yinter = outs.beta_yinter
|
|
775
|
+
sim_beta = outs.sim_beta
|
|
776
|
+
mapped_time = outs.mapped_time
|
|
777
|
+
premaptime = outs.premaptime
|
|
778
|
+
premapdmdt = outs.premapdmdt
|
|
779
|
+
|
|
780
|
+
Mhbase = 1.0e6 # in units of Msolar, this is generic Mh used in astrocrash sims
|
|
781
|
+
Mstarbase = 1.0 # in units of Msolar
|
|
782
|
+
Rstarbase = 1.0 # in units of Rsolar
|
|
783
|
+
starmass = mstar
|
|
784
|
+
|
|
785
|
+
# Calculate beta values
|
|
786
|
+
if 0 <= b < 1:
|
|
787
|
+
beta43 = 0.6 + 1.25 * b
|
|
788
|
+
beta53 = 0.5 + 0.4 * b
|
|
789
|
+
betas = {'4-3': beta43, '5-3': beta53}
|
|
790
|
+
elif 1 <= b <= 2:
|
|
791
|
+
beta43 = 1.85 + 2.15 * (b - 1)
|
|
792
|
+
beta53 = 0.9 + 1.6 * (b - 1)
|
|
793
|
+
betas = {'4-3': beta43, '5-3': beta53}
|
|
794
|
+
else:
|
|
795
|
+
raise ValueError('b outside range, bmin = 0; bmax = 2')
|
|
796
|
+
|
|
797
|
+
# Determine gamma values
|
|
798
|
+
gamma_interp = False
|
|
799
|
+
if starmass <= 0.3 or starmass >= 22:
|
|
800
|
+
gammas = ['5-3']
|
|
801
|
+
beta = betas['5-3']
|
|
802
|
+
elif 1 <= starmass <= 15:
|
|
803
|
+
gammas = ['4-3']
|
|
804
|
+
beta = betas['4-3']
|
|
805
|
+
elif 0.3 < starmass < 1:
|
|
806
|
+
gamma_interp = True
|
|
807
|
+
gammas = ['4-3', '5-3']
|
|
808
|
+
gfrac = (starmass - 1.) / (0.3 - 1.)
|
|
809
|
+
beta = betas['5-3'] + (betas['4-3'] - betas['5-3']) * (1. - gfrac)
|
|
810
|
+
elif 15 < starmass < 22:
|
|
811
|
+
gamma_interp = True
|
|
812
|
+
gammas = ['4-3', '5-3']
|
|
813
|
+
gfrac = (starmass - 15.) / (22. - 15.)
|
|
814
|
+
beta = betas['5-3'] + (betas['4-3'] - betas['5-3']) * (1. - gfrac)
|
|
815
|
+
|
|
816
|
+
timedict = {}
|
|
817
|
+
dmdtdict = {}
|
|
818
|
+
|
|
819
|
+
sim_beta = outs.sim_beta
|
|
820
|
+
for g in gammas:
|
|
821
|
+
for i in range(len(sim_beta[g])):
|
|
822
|
+
if betas[g] == sim_beta[g][i]:
|
|
823
|
+
beta_interp = False
|
|
824
|
+
interp_index_low = i
|
|
825
|
+
break
|
|
826
|
+
if betas[g] < sim_beta[g][i]:
|
|
827
|
+
interp_index_high = i
|
|
828
|
+
interp_index_low = i - 1
|
|
829
|
+
beta_interp = True
|
|
830
|
+
break
|
|
831
|
+
|
|
832
|
+
if beta_interp:
|
|
833
|
+
dmdt = np.array([
|
|
834
|
+
beta_yinter[g][interp_index_low][0] + beta_slope[g][interp_index_low][0] * betas[g],
|
|
835
|
+
beta_yinter[g][interp_index_low][1] + beta_slope[g][interp_index_low][1] * betas[g]
|
|
836
|
+
], dtype=object)
|
|
837
|
+
|
|
838
|
+
time = []
|
|
839
|
+
for i in [0, 1]:
|
|
840
|
+
time_betalo = (mapped_time[g][interp_index_low][i] * (
|
|
841
|
+
premaptime[g][interp_index_low][i][-1] - premaptime[g][interp_index_low][i][0]) +
|
|
842
|
+
premaptime[g][interp_index_low][i][0])
|
|
843
|
+
time_betahi = (mapped_time[g][interp_index_low][i] * (
|
|
844
|
+
premaptime[g][interp_index_high][i][-1] - premaptime[g][interp_index_high][i][0]) +
|
|
845
|
+
premaptime[g][interp_index_high][i][0])
|
|
846
|
+
time.append(time_betalo + (time_betahi - time_betalo) * (betas[g] - sim_beta[g][interp_index_low]) / (
|
|
847
|
+
sim_beta[g][interp_index_high] - sim_beta[g][interp_index_low]))
|
|
848
|
+
time = np.array(time, dtype=object)
|
|
849
|
+
|
|
850
|
+
timedict[g] = time
|
|
851
|
+
dmdtdict[g] = dmdt
|
|
852
|
+
else:
|
|
853
|
+
timedict[g] = np.copy(premaptime[g][interp_index_low])
|
|
854
|
+
dmdtdict[g] = np.copy(premapdmdt[g][interp_index_low])
|
|
855
|
+
|
|
856
|
+
if gamma_interp:
|
|
857
|
+
mapped_time = {'4-3': [], '5-3': []}
|
|
858
|
+
time = []
|
|
859
|
+
dmdt = []
|
|
860
|
+
for j in [0, 1]:
|
|
861
|
+
if len(timedict['4-3'][j]) < len(timedict['5-3'][j]):
|
|
862
|
+
interp = '4-3'
|
|
863
|
+
nointerp = '5-3'
|
|
864
|
+
else:
|
|
865
|
+
interp = '5-3'
|
|
866
|
+
nointerp = '4-3'
|
|
867
|
+
|
|
868
|
+
mapped_time[nointerp].append(1. / (timedict[nointerp][j][-1] - timedict[nointerp][j][0]) * (
|
|
869
|
+
timedict[nointerp][j] - timedict[nointerp][j][0]))
|
|
870
|
+
mapped_time[interp].append(1. / (timedict[interp][j][-1] - timedict[interp][j][0]) * (
|
|
871
|
+
timedict[interp][j] - timedict[interp][j][0]))
|
|
872
|
+
mapped_time[interp][j][0] = 0
|
|
873
|
+
mapped_time[interp][j][-1] = 1
|
|
874
|
+
mapped_time[nointerp][j][0] = 0
|
|
875
|
+
mapped_time[nointerp][j][-1] = 1
|
|
876
|
+
|
|
877
|
+
func = interp1d(mapped_time[interp][j], dmdtdict[interp][j])
|
|
878
|
+
dmdtdict[interp][j] = func(mapped_time[nointerp][j])
|
|
879
|
+
|
|
880
|
+
if interp == '5-3':
|
|
881
|
+
time53 = (mapped_time['4-3'][j] * (timedict['5-3'][j][-1] - timedict['5-3'][j][0]) + timedict['5-3'][j][
|
|
882
|
+
0])
|
|
883
|
+
time.extend(10 ** (timedict['4-3'][j] + (time53 - timedict['4-3'][j]) * gfrac))
|
|
884
|
+
else:
|
|
885
|
+
time43 = (mapped_time['5-3'][j] * (timedict['4-3'][j][-1] - timedict['4-3'][j][0]) + timedict['4-3'][j][
|
|
886
|
+
0])
|
|
887
|
+
time.extend(10 ** (time43 + (timedict['5-3'][j] - time43) * gfrac))
|
|
888
|
+
|
|
889
|
+
dmdt.extend(10 ** (dmdtdict['4-3'][j] + (dmdtdict['5-3'][j] - dmdtdict['4-3'][j]) * gfrac))
|
|
890
|
+
else:
|
|
891
|
+
time = np.concatenate((timedict[g][0], timedict[g][1]))
|
|
892
|
+
time = 10 ** time
|
|
893
|
+
dmdt = np.concatenate((dmdtdict[g][0], dmdtdict[g][1]))
|
|
894
|
+
dmdt = 10 ** dmdt
|
|
895
|
+
|
|
896
|
+
time = np.array(time)
|
|
897
|
+
dmdt = np.array(dmdt)
|
|
898
|
+
|
|
899
|
+
Mh = mbh6 * 1.0e6
|
|
900
|
+
|
|
901
|
+
if starmass < 0.1:
|
|
902
|
+
Mstar_Tout = 0.1
|
|
903
|
+
else:
|
|
904
|
+
Mstar_Tout = starmass
|
|
905
|
+
|
|
906
|
+
Z = 0.0134
|
|
907
|
+
log10_Z_02 = np.log10(Z / 0.02)
|
|
908
|
+
|
|
909
|
+
Tout_theta = (
|
|
910
|
+
1.71535900 + 0.62246212 * log10_Z_02 - 0.92557761 * log10_Z_02 ** 2 - 1.16996966 * log10_Z_02 ** 3 - 0.30631491 * log10_Z_02 ** 4)
|
|
911
|
+
Tout_l = (
|
|
912
|
+
6.59778800 - 0.42450044 * log10_Z_02 - 12.13339427 * log10_Z_02 ** 2 - 10.73509484 * log10_Z_02 ** 3 - 2.51487077 * log10_Z_02 ** 4)
|
|
913
|
+
Tout_kpa = (
|
|
914
|
+
10.08855000 - 7.11727086 * log10_Z_02 - 31.67119479 * log10_Z_02 ** 2 - 24.24848322 * log10_Z_02 ** 3 - 5.33608972 * log10_Z_02 ** 4)
|
|
915
|
+
Tout_lbda = (
|
|
916
|
+
1.01249500 + 0.32699690 * log10_Z_02 - 0.00923418 * log10_Z_02 ** 2 - 0.03876858 * log10_Z_02 ** 3 - 0.00412750 * log10_Z_02 ** 4)
|
|
917
|
+
Tout_mu = (
|
|
918
|
+
0.07490166 + 0.02410413 * log10_Z_02 + 0.07233664 * log10_Z_02 ** 2 + 0.03040467 * log10_Z_02 ** 3 + 0.00197741 * log10_Z_02 ** 4)
|
|
919
|
+
Tout_nu = 0.01077422
|
|
920
|
+
Tout_eps = (
|
|
921
|
+
3.08223400 + 0.94472050 * log10_Z_02 - 2.15200882 * log10_Z_02 ** 2 - 2.49219496 * log10_Z_02 ** 3 - 0.63848738 * log10_Z_02 ** 4)
|
|
922
|
+
Tout_o = (
|
|
923
|
+
17.84778000 - 7.45345690 * log10_Z_02 - 48.9606685 * log10_Z_02 ** 2 - 40.05386135 * log10_Z_02 ** 3 - 9.09331816 * log10_Z_02 ** 4)
|
|
924
|
+
Tout_pi = (
|
|
925
|
+
0.00022582 - 0.00186899 * log10_Z_02 + 0.00388783 * log10_Z_02 ** 2 + 0.00142402 * log10_Z_02 ** 3 - 0.00007671 * log10_Z_02 ** 4)
|
|
926
|
+
|
|
927
|
+
Rstar = ((
|
|
928
|
+
Tout_theta * Mstar_Tout ** 2.5 + Tout_l * Mstar_Tout ** 6.5 + Tout_kpa * Mstar_Tout ** 11 + Tout_lbda * Mstar_Tout ** 19 + Tout_mu * Mstar_Tout ** 19.5) / (
|
|
929
|
+
Tout_nu + Tout_eps * Mstar_Tout ** 2 + Tout_o * Mstar_Tout ** 8.5 + Mstar_Tout ** 18.5 + Tout_pi * Mstar_Tout ** 19.5))
|
|
930
|
+
|
|
931
|
+
dmdt = (dmdt * np.sqrt(Mhbase / Mh) * (starmass / Mstarbase) ** 2.0 * (Rstarbase / Rstar) ** 1.5)
|
|
932
|
+
time = (time * np.sqrt(Mh / Mhbase) * (Mstarbase / starmass) * (Rstar / Rstarbase) ** 1.5)
|
|
933
|
+
|
|
934
|
+
DAY_CGS = 86400
|
|
935
|
+
time = time / DAY_CGS
|
|
936
|
+
tfallback = np.copy(time[0])
|
|
937
|
+
|
|
938
|
+
time = time - tfallback
|
|
939
|
+
tpeak = time[np.argmax(dmdt)]
|
|
940
|
+
|
|
941
|
+
timeinterpfunc = interp1d(time, dmdt)
|
|
942
|
+
lengthpretimes = len(np.where(times < time[0])[0])
|
|
943
|
+
lengthposttimes = len(np.where(times > time[-1])[0])
|
|
944
|
+
|
|
945
|
+
dmdt2 = timeinterpfunc(times[lengthpretimes:(len(times) - lengthposttimes)])
|
|
946
|
+
dmdt1 = np.zeros(lengthpretimes)
|
|
947
|
+
dmdt3 = np.zeros(lengthposttimes)
|
|
948
|
+
|
|
949
|
+
dmdtnew = np.append(dmdt1, dmdt2)
|
|
950
|
+
dmdtnew = np.append(dmdtnew, dmdt3)
|
|
951
|
+
dmdtnew[dmdtnew < 0] = 0
|
|
952
|
+
|
|
953
|
+
kappa_t = 0.2 * (1 + 0.74)
|
|
954
|
+
Ledd = (4 * np.pi * cc.graviational_constant * Mh * cc.solar_mass * cc.speed_of_light / kappa_t)
|
|
955
|
+
|
|
956
|
+
luminosities = (efficiency * dmdtnew * cc.speed_of_light * cc.speed_of_light)
|
|
957
|
+
luminosities = (luminosities * leddlimit * Ledd / (luminosities + leddlimit * Ledd))
|
|
958
|
+
luminosities = [0.0 if np.isnan(x) else x for x in luminosities]
|
|
959
|
+
|
|
960
|
+
ProcessedData = namedtuple('ProcessedData', [
|
|
961
|
+
'luminosities', 'Rstar', 'tpeak', 'beta', 'starmass', 'dmdt', 'Ledd', 'tfallback'])
|
|
962
|
+
ProcessedData = ProcessedData(luminosities=luminosities, Rstar=Rstar, tpeak=tpeak, beta=beta, starmass=starmass,
|
|
963
|
+
dmdt=dmdtnew, Ledd=Ledd, tfallback=float(tfallback))
|
|
964
|
+
return ProcessedData
|
|
965
|
+
|
|
966
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2019ApJ...872..151M/abstract, https://ui.adsabs.harvard.edu/abs/2013ApJ...767...25G/abstract, https://ui.adsabs.harvard.edu/abs/2018ApJS..236....6G/abstract')
|
|
967
|
+
def _tde_fallback_all_outputs(time, mbh6, mstar, tvisc, bb, eta, leddlimit, **kwargs):
|
|
968
|
+
"""
|
|
969
|
+
Identical to the Mosfit model following Guillochon+ 2013 apart from doing the fallback rate fudge in mosfit.
|
|
970
|
+
|
|
971
|
+
:param time: A dense array of times in rest frame in days
|
|
972
|
+
:param mbh6: black hole mass in units of 10^6 solar masses
|
|
973
|
+
:param mstar: star mass in units of solar masses
|
|
974
|
+
:param tvisc: viscous timescale in days
|
|
975
|
+
:param bb: Relates to beta and gamma values for the star that's disrupted
|
|
976
|
+
:param eta: efficiency of the BH
|
|
977
|
+
:param leddlimit: eddington limit for the BH
|
|
978
|
+
:param kwargs: Additional keyword arguments
|
|
979
|
+
:return: bolometric luminosity
|
|
980
|
+
"""
|
|
981
|
+
_interaction_process = kwargs.get("interaction_process", ip.Viscous)
|
|
982
|
+
dense_resolution = kwargs.get("dense_resolution", 1000)
|
|
983
|
+
dense_times = np.linspace(0, time[-1] + 100, dense_resolution)
|
|
984
|
+
outs = _tde_mosfit_engine(times=dense_times, mbh6=mbh6, mstar=mstar, b=bb, efficiency=eta,
|
|
985
|
+
leddlimit=leddlimit, **kwargs)
|
|
986
|
+
dense_lbols = outs.luminosities
|
|
987
|
+
interaction_class = _interaction_process(time=time, dense_times=dense_times, luminosity=dense_lbols, t_viscous=tvisc, **kwargs)
|
|
988
|
+
lbol = interaction_class.new_luminosity
|
|
989
|
+
return lbol, outs
|
|
990
|
+
|
|
991
|
+
def tde_fallback_bolometric(time, mbh6, mstar, tvisc, bb, eta, leddlimit, **kwargs):
|
|
992
|
+
"""
|
|
993
|
+
Identical to the Mosfit model following Guillochon+ 2013 apart from doing the fallback rate fudge in mosfit.
|
|
994
|
+
|
|
995
|
+
:param time: A dense array of times in rest frame in days
|
|
996
|
+
:param mbh6: black hole mass in units of 10^6 solar masses
|
|
997
|
+
:param mstar: star mass in units of solar masses
|
|
998
|
+
:param tvisc: viscous timescale in days
|
|
999
|
+
:param bb: Relates to beta and gamma values for the star that's disrupted
|
|
1000
|
+
:param eta: efficiency of the BH
|
|
1001
|
+
:param leddlimit: eddington limit for the BH
|
|
1002
|
+
:param kwargs: Additional keyword arguments
|
|
1003
|
+
:return: bolometric luminosity
|
|
1004
|
+
"""
|
|
1005
|
+
lbol, _ = _tde_fallback_all_outputs(time=time, mbh6=mbh6, mstar=mstar, tvisc=tvisc, bb=bb, eta=eta,
|
|
1006
|
+
leddlimit=leddlimit, **kwargs)
|
|
1007
|
+
return lbol
|
|
1008
|
+
|
|
1009
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2019ApJ...872..151M/abstract, https://ui.adsabs.harvard.edu/abs/2013ApJ...767...25G/abstract, https://ui.adsabs.harvard.edu/abs/2018ApJS..236....6G/abstract')
|
|
1010
|
+
def tde_fallback(time, redshift, mbh6, mstar, tvisc, bb, eta, leddlimit, rph0, lphoto, **kwargs):
|
|
1011
|
+
"""
|
|
1012
|
+
Identical to the Mosfit model following Guillochon+ 2013 apart from doing the fallback rate fudge in mosfit.
|
|
1013
|
+
|
|
1014
|
+
:param time: Times in observer frame in days
|
|
1015
|
+
:param redshift: redshift of the transient
|
|
1016
|
+
:param mbh6: black hole mass in units of 10^6 solar masses
|
|
1017
|
+
:param mstar: star mass in units of solar masses
|
|
1018
|
+
:param tvisc: viscous timescale in days
|
|
1019
|
+
:param bb: Relates to beta and gamma values for the star that's disrupted
|
|
1020
|
+
:param eta: efficiency of the BH
|
|
1021
|
+
:param leddlimit: eddington limit for the BH
|
|
1022
|
+
:param rph0: initial photosphere radius
|
|
1023
|
+
:param lphoto: photosphere luminosity
|
|
1024
|
+
:param kwargs: Additional keyword arguments
|
|
1025
|
+
:return: set by output format - 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
1026
|
+
"""
|
|
1027
|
+
|
|
1028
|
+
kwargs['interaction_process'] = kwargs.get("interaction_process", ip.Viscous)
|
|
1029
|
+
kwargs['photosphere'] = kwargs.get("photosphere", photosphere.TDEPhotosphere)
|
|
1030
|
+
kwargs['sed'] = kwargs.get("sed", sed.Blackbody)
|
|
1031
|
+
cosmology = kwargs.get('cosmology', cosmo)
|
|
1032
|
+
dl = cosmology.luminosity_distance(redshift).cgs.value
|
|
1033
|
+
|
|
1034
|
+
if kwargs['output_format'] == 'flux_density':
|
|
1035
|
+
frequency = kwargs['frequency']
|
|
1036
|
+
frequency, time = calc_kcorrected_properties(frequency=frequency, redshift=redshift, time=time)
|
|
1037
|
+
lbol, outs = _tde_fallback_all_outputs(time=time, mbh6=mbh6, mstar=mstar, tvisc=tvisc, bb=bb, eta=eta,
|
|
1038
|
+
leddlimit=leddlimit, **kwargs)
|
|
1039
|
+
photo = kwargs['photosphere'](time=time, luminosity=lbol, mass_bh=mbh6*1e6,
|
|
1040
|
+
mass_star=mstar, star_radius=outs.Rstar,
|
|
1041
|
+
tpeak=outs.tpeak, rph_0=rph0, lphoto=lphoto, beta=outs.beta, **kwargs)
|
|
1042
|
+
sed_1 = kwargs['sed'](time=time, temperature=photo.photosphere_temperature, r_photosphere=photo.r_photosphere,
|
|
1043
|
+
frequency=frequency, luminosity_distance=dl)
|
|
1044
|
+
flux_density = sed_1.flux_density
|
|
1045
|
+
flux_density = np.nan_to_num(flux_density)
|
|
1046
|
+
return flux_density.to(uu.mJy).value
|
|
1047
|
+
else:
|
|
1048
|
+
time_obs = time
|
|
1049
|
+
lambda_observer_frame = kwargs.get('lambda_array', np.geomspace(100, 60000, 100))
|
|
1050
|
+
time_temp = np.geomspace(0.1, 1000, 300)
|
|
1051
|
+
time_observer_frame = time_temp * (1. + redshift)
|
|
1052
|
+
frequency, time = calc_kcorrected_properties(frequency=lambda_to_nu(lambda_observer_frame),
|
|
1053
|
+
redshift=redshift, time=time_observer_frame)
|
|
1054
|
+
lbol, outs = _tde_fallback_all_outputs(time=time, mbh6=mbh6, mstar=mstar, tvisc=tvisc, bb=bb, eta=eta,
|
|
1055
|
+
leddlimit=leddlimit, **kwargs)
|
|
1056
|
+
photo = kwargs['photosphere'](time=time, luminosity=lbol, mass_bh=mbh6*1e6, mass_star=mstar,
|
|
1057
|
+
star_radius=outs.Rstar, tpeak=outs.tpeak, rph_0=rph0, lphoto=lphoto,
|
|
1058
|
+
beta=outs.beta,**kwargs)
|
|
1059
|
+
sed_1 = kwargs['sed'](time=time, temperature=photo.photosphere_temperature, r_photosphere=photo.r_photosphere,
|
|
1060
|
+
frequency=frequency[:, None], luminosity_distance=dl)
|
|
1061
|
+
fmjy = sed_1.flux_density.T
|
|
1062
|
+
spectra = fmjy.to(uu.mJy).to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
|
|
1063
|
+
equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom))
|
|
1064
|
+
if kwargs['output_format'] == 'spectra':
|
|
1065
|
+
return namedtuple('output', ['time', 'lambdas', 'spectra'])(time=time_observer_frame,
|
|
1066
|
+
lambdas=lambda_observer_frame,
|
|
1067
|
+
spectra=spectra)
|
|
1068
|
+
else:
|
|
1069
|
+
return sed.get_correct_output_format_from_spectra(time=time_obs, time_eval=time_observer_frame,
|
|
1070
|
+
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
1071
|
+
**kwargs)
|
|
1072
|
+
|
|
1073
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2024arXiv240815048M/abstract')
|
|
1074
|
+
def fitted(time, redshift, log_mh, a_bh, m_disc, r0, tvi, t_form, incl, **kwargs):
|
|
1075
|
+
"""
|
|
1076
|
+
An import of FitTeD to model the plateau phase
|
|
1077
|
+
|
|
1078
|
+
:param time: observer frame time in days
|
|
1079
|
+
:param redshift: redshift
|
|
1080
|
+
:param log_mh: log of the black hole mass (solar masses)
|
|
1081
|
+
:param a_bh: black hole spin parameter (dimensionless)
|
|
1082
|
+
:param m_disc: initial mass of disc ring (solar masses)
|
|
1083
|
+
:param r0: initial radius of disc ring (gravitational radii)
|
|
1084
|
+
:param tvi: viscous timescale of disc evolution (days)
|
|
1085
|
+
:param t_form: time of ring formation prior to t = 0 (days)
|
|
1086
|
+
:param incl: disc-observer inclination angle (radians)
|
|
1087
|
+
:param kwargs: Must be all the kwargs required by the specific output_format
|
|
1088
|
+
:param output_format: 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
1089
|
+
:param frequency: Required if output_format is 'flux_density'.
|
|
1090
|
+
frequency to calculate - Must be same length as time array or a single number).
|
|
1091
|
+
:param bands: Required if output_format is 'magnitude' or 'flux'.
|
|
1092
|
+
:param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
|
|
1093
|
+
:return: set by output format - 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
1094
|
+
"""
|
|
1095
|
+
import fitted #user needs to have downloaded and compiled FitTeD in order to run this model
|
|
1096
|
+
cosmology = kwargs.get('cosmology', cosmo)
|
|
1097
|
+
dl = cosmology.luminosity_distance(redshift).cgs.value
|
|
1098
|
+
ang = 180.0/np.pi*incl
|
|
1099
|
+
m = fitted.models.GR_disc()
|
|
1100
|
+
|
|
1101
|
+
if kwargs['output_format'] == 'flux_density':
|
|
1102
|
+
frequency = kwargs['frequency']
|
|
1103
|
+
frequency, time = calc_kcorrected_properties(frequency=frequency, redshift=redshift, time=time)
|
|
1104
|
+
freqs_un = np.unique(frequency)
|
|
1105
|
+
nulnus = np.zeros(len(time))
|
|
1106
|
+
if len(freqs_un) == 1:
|
|
1107
|
+
nulnus = m.model_UV(time, log_mh, a_bh, m_disc, r0, tvi, t_form, ang, frequency)
|
|
1108
|
+
else:
|
|
1109
|
+
for i in range(0,len(freqs_un)):
|
|
1110
|
+
inds = np.where(frequency == freqs_un[i])[0]
|
|
1111
|
+
nulnus[inds] = m.model_UV([time[j] for j in inds], log_mh, a_bh, m_disc, r0, tvi, t_form, ang, freqs_un[i])
|
|
1112
|
+
flux_density = nulnus/(4.0 * np.pi * dl**2 * frequency)
|
|
1113
|
+
return flux_density/1.0e-26
|
|
1114
|
+
|
|
1115
|
+
else:
|
|
1116
|
+
time_obs = time
|
|
1117
|
+
lambda_observer_frame = kwargs.get('lambda_array', np.geomspace(100, 60000, 100))
|
|
1118
|
+
time_temp = np.geomspace(0.1, 3000, 300) # in days
|
|
1119
|
+
time_observer_frame = time_temp * (1. + redshift)
|
|
1120
|
+
frequency, time = calc_kcorrected_properties(frequency=lambda_to_nu(lambda_observer_frame),
|
|
1121
|
+
redshift=redshift, time=time_observer_frame)
|
|
1122
|
+
nulnus = m.model_SEDs(time, log_mh, a_bh, m_disc, r0, tvi, t_form, ang, frequency)
|
|
1123
|
+
flux_density = (nulnus/(4.0 * np.pi * dl**2 * frequency[:,np.newaxis] * 1.0e-26))
|
|
1124
|
+
fmjy = flux_density.T
|
|
1125
|
+
spectra = (fmjy * uu.mJy).to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
|
|
1126
|
+
equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom))
|
|
1127
|
+
if kwargs['output_format'] == 'spectra':
|
|
1128
|
+
return namedtuple('output', ['time', 'lambdas', 'spectra'])(time=time_observer_frame,
|
|
1129
|
+
lambdas=lambda_observer_frame,
|
|
1130
|
+
spectra=spectra)
|
|
1131
|
+
else:
|
|
1132
|
+
return sed.get_correct_output_format_from_spectra(time=time_obs, time_eval=time_observer_frame,
|
|
1133
|
+
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
1134
|
+
**kwargs)
|
|
1135
|
+
|
|
1136
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2024arXiv240815048M/abstract')
|
|
1137
|
+
def fitted_pl_decay(time, redshift, log_mh, a_bh, m_disc, r0, tvi, t_form, incl, log_L, t_decay, p, log_T, sigma, t_peak, **kwargs):
|
|
1138
|
+
"""
|
|
1139
|
+
An import of FitTeD to model the plateau phase, with a gaussian rise and power-law decay
|
|
1140
|
+
|
|
1141
|
+
:param time: observer frame time in days
|
|
1142
|
+
:param redshift: redshift
|
|
1143
|
+
:param log_mh: log of the black hole mass (solar masses)
|
|
1144
|
+
:param a_bh: black hole spin parameter (dimensionless)
|
|
1145
|
+
:param m_disc: initial mass of disc ring (solar masses)
|
|
1146
|
+
:param r0: initial radius of disc ring (gravitational radii)
|
|
1147
|
+
:param tvi: viscous timescale of disc evolution (days)
|
|
1148
|
+
:param t_form: time of ring formation prior to t = 0 (days)
|
|
1149
|
+
:param incl: disc-observer inclination angle (radians)
|
|
1150
|
+
:param log_L: single temperature blackbody amplitude for decay model (log_10 erg/s)
|
|
1151
|
+
:param t_decay: fallback timescale (days)
|
|
1152
|
+
:param p: power-law decay index
|
|
1153
|
+
:param log_T: single temperature blackbody temperature for decay model (log_10 Kelvin)
|
|
1154
|
+
:param sigma: gaussian rise timescale (days)
|
|
1155
|
+
:param t_peak: time of light curve peak (days)
|
|
1156
|
+
:param kwargs: Must be all the kwargs required by the specific output_format
|
|
1157
|
+
:param output_format: 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
1158
|
+
:param frequency: Required if output_format is 'flux_density'.
|
|
1159
|
+
frequency to calculate - Must be same length as time array or a single number).
|
|
1160
|
+
:param bands: Required if output_format is 'magnitude' or 'flux'.
|
|
1161
|
+
:param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
|
|
1162
|
+
:return: set by output format - 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
1163
|
+
"""
|
|
1164
|
+
import fitted #user needs to have downloaded and compiled FitTeD in order to run this model
|
|
1165
|
+
cosmology = kwargs.get('cosmology', cosmo)
|
|
1166
|
+
dl = cosmology.luminosity_distance(redshift).cgs.value
|
|
1167
|
+
ang = 180.0/np.pi*incl
|
|
1168
|
+
m = fitted.models.GR_disc(decay_type='pl', rise=True)
|
|
1169
|
+
|
|
1170
|
+
if kwargs['output_format'] == 'flux_density':
|
|
1171
|
+
frequency = kwargs['frequency']
|
|
1172
|
+
frequency, time = calc_kcorrected_properties(frequency=frequency, redshift=redshift, time=time)
|
|
1173
|
+
freqs_un = np.unique(frequency)
|
|
1174
|
+
|
|
1175
|
+
#initialize arrays
|
|
1176
|
+
nulnus_plateau = np.zeros(len(time))
|
|
1177
|
+
nulnus_rise = np.zeros(len(time))
|
|
1178
|
+
nulnus_decay = np.zeros(len(time))
|
|
1179
|
+
|
|
1180
|
+
if len(freqs_un) == 1:
|
|
1181
|
+
nulnus_plateau = m.model_UV(time, log_mh, a_bh, m_disc, r0, tvi, t_form, ang, frequency)
|
|
1182
|
+
nulnus_decay = m.decay_model(time, log_L, tdecay, p, t_peak, log_T, v=freqs_un[0])
|
|
1183
|
+
nulnus_rise = m.rise_model(time, log_L, sigma, t_peak, log_T, v=freqs_un[0])
|
|
1184
|
+
else:
|
|
1185
|
+
for i in range(0,len(freqs_un)):
|
|
1186
|
+
inds = np.where(frequency == freqs_un[i])[0]
|
|
1187
|
+
nulnus[inds] = m.model_UV([time[j] for j in inds], log_mh, a_bh, m_disc, r0, tvi, t_form, ang, freqs_un[i])
|
|
1188
|
+
nulnus_decay[inds] = m.decay_model([time[j] for j in inds], log_L, t_decay, p, t_peak, log_T, v=freqs_un[i])
|
|
1189
|
+
nulnus_rise[inds] = m.rise_model([time[j] for j in inds], log_L, sigma, t_peak, log_T, v=freqs_un[i])
|
|
1190
|
+
nulnus = nulnus_plateau + nulnus_rise + nulnus_decay
|
|
1191
|
+
flux_density = nulnus/(4.0 * np.pi * dl**2 * frequency)
|
|
1192
|
+
return flux_density/1.0e-26
|
|
1193
|
+
|
|
1194
|
+
else:
|
|
1195
|
+
time_obs = time
|
|
1196
|
+
lambda_observer_frame = kwargs.get('lambda_array', np.geomspace(100, 60000, 100))
|
|
1197
|
+
time_temp = np.geomspace(0.1, 3000, 300) # in days
|
|
1198
|
+
time_observer_frame = time_temp * (1. + redshift)
|
|
1199
|
+
frequency, time = calc_kcorrected_properties(frequency=lambda_to_nu(lambda_observer_frame),
|
|
1200
|
+
redshift=redshift, time=time_observer_frame)
|
|
1201
|
+
nulnus_plateau = m.model_SEDs(time, log_mh, a_bh, m_disc, r0, tvi, t_form, ang, frequency)
|
|
1202
|
+
nulnus_risedecay = np.zeros((100, 300))
|
|
1203
|
+
for i in range(0,len(frequency)):
|
|
1204
|
+
nulnus_risedecay[i,:] = m.decay_model(time, log_L, t_decay, p, t_peak, log_T, v=frequency[i]) + m.rise_model(time, log_L, sigma, t_peak, log_T, v=frequency[i])
|
|
1205
|
+
flux_density = ((nulnus_risedecay + nulnus_plateau)/(4.0 * np.pi * dl**2 * frequency[:,np.newaxis] * 1.0e-26))
|
|
1206
|
+
fmjy = flux_density.T
|
|
1207
|
+
spectra = (fmjy * uu.mJy).to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
|
|
1208
|
+
equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom))
|
|
1209
|
+
if kwargs['output_format'] == 'spectra':
|
|
1210
|
+
return namedtuple('output', ['time', 'lambdas', 'spectra'])(time=time_observer_frame,
|
|
1211
|
+
lambdas=lambda_observer_frame,
|
|
1212
|
+
spectra=spectra)
|
|
1213
|
+
else:
|
|
1214
|
+
return sed.get_correct_output_format_from_spectra(time=time_obs, time_eval=time_observer_frame,
|
|
1215
|
+
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
1216
|
+
**kwargs)
|
|
1217
|
+
|
|
1218
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2024arXiv240815048M/abstract')
|
|
1219
|
+
def fitted_exp_decay(time, redshift, log_mh, a_bh, m_disc, r0, tvi, t_form, incl, log_L, t_decay, log_T, sigma, t_peak, **kwargs):
|
|
1220
|
+
"""
|
|
1221
|
+
An import of FitTeD to model the plateau phase, with a gaussian rise and exponential decay
|
|
1222
|
+
|
|
1223
|
+
:param time: observer frame time in days
|
|
1224
|
+
:param redshift: redshift
|
|
1225
|
+
:param log_mh: log of the black hole mass (solar masses)
|
|
1226
|
+
:param a_bh: black hole spin parameter (dimensionless)
|
|
1227
|
+
:param m_disc: initial mass of disc ring (solar masses)
|
|
1228
|
+
:param r0: initial radius of disc ring (gravitational radii)
|
|
1229
|
+
:param tvi: viscous timescale of disc evolution (days)
|
|
1230
|
+
:param t_form: time of ring formation prior to t = 0 (days)
|
|
1231
|
+
:param incl: disc-observer inclination angle (radians)
|
|
1232
|
+
:param log_L: single temperature blackbody amplitude for decay model (log_10 erg/s)
|
|
1233
|
+
:param t_decay: fallback timescale (days)
|
|
1234
|
+
:param log_T: single temperature blackbody temperature for decay model (log_10 Kelvin)
|
|
1235
|
+
:param sigma: gaussian rise timescale (days)
|
|
1236
|
+
:param t_peak: time of light curve peak (days)
|
|
1237
|
+
:param kwargs: Must be all the kwargs required by the specific output_format
|
|
1238
|
+
:param output_format: 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
1239
|
+
:param frequency: Required if output_format is 'flux_density'.
|
|
1240
|
+
frequency to calculate - Must be same length as time array or a single number).
|
|
1241
|
+
:param bands: Required if output_format is 'magnitude' or 'flux'.
|
|
1242
|
+
:param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
|
|
1243
|
+
:return: set by output format - 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
1244
|
+
"""
|
|
1245
|
+
import fitted #user needs to have downloaded and compiled FitTeD in order to run this model
|
|
1246
|
+
cosmology = kwargs.get('cosmology', cosmo)
|
|
1247
|
+
dl = cosmology.luminosity_distance(redshift).cgs.value
|
|
1248
|
+
ang = 180.0/np.pi*incl
|
|
1249
|
+
m = fitted.models.GR_disc(decay_type='exp', rise=True)
|
|
1250
|
+
|
|
1251
|
+
if kwargs['output_format'] == 'flux_density':
|
|
1252
|
+
frequency = kwargs['frequency']
|
|
1253
|
+
frequency, time = calc_kcorrected_properties(frequency=frequency, redshift=redshift, time=time)
|
|
1254
|
+
freqs_un = np.unique(frequency)
|
|
1255
|
+
|
|
1256
|
+
#initialize arrays
|
|
1257
|
+
nulnus_plateau = np.zeros(len(time))
|
|
1258
|
+
nulnus_rise = np.zeros(len(time))
|
|
1259
|
+
nulnus_decay = np.zeros(len(time))
|
|
1260
|
+
|
|
1261
|
+
if len(freqs_un) == 1:
|
|
1262
|
+
nulnus_plateau = m.model_UV(time, log_mh, a_bh, m_disc, r0, tvi, t_form, ang, frequency)
|
|
1263
|
+
nulnus_decay = m.decay_model(time, log_L, tdecay, t_peak, log_T, v=freqs_un[0])
|
|
1264
|
+
nulnus_rise = m.rise_model(time, log_L, sigma, t_peak, log_T, v=freqs_un[0])
|
|
1265
|
+
else:
|
|
1266
|
+
for i in range(0,len(freqs_un)):
|
|
1267
|
+
inds = np.where(frequency == freqs_un[i])[0]
|
|
1268
|
+
nulnus[inds] = m.model_UV([time[j] for j in inds], log_mh, a_bh, m_disc, r0, tvi, t_form, ang, freqs_un[i])
|
|
1269
|
+
nulnus_decay[inds] = m.decay_model([time[j] for j in inds], log_L, t_decay, t_peak, log_T, v=freqs_un[i])
|
|
1270
|
+
nulnus_rise[inds] = m.rise_model([time[j] for j in inds], log_L, sigma, t_peak, log_T, v=freqs_un[i])
|
|
1271
|
+
nulnus = nulnus_plateau + nulnus_rise + nulnus_decay
|
|
1272
|
+
flux_density = nulnus/(4.0 * np.pi * dl**2 * frequency)
|
|
1273
|
+
return flux_density/1.0e-26
|
|
1274
|
+
|
|
1275
|
+
else:
|
|
1276
|
+
time_obs = time
|
|
1277
|
+
lambda_observer_frame = kwargs.get('lambda_array', np.geomspace(100, 60000, 100))
|
|
1278
|
+
time_temp = np.geomspace(0.1, 3000, 300) # in days
|
|
1279
|
+
time_observer_frame = time_temp * (1. + redshift)
|
|
1280
|
+
frequency, time = calc_kcorrected_properties(frequency=lambda_to_nu(lambda_observer_frame),
|
|
1281
|
+
redshift=redshift, time=time_observer_frame)
|
|
1282
|
+
nulnus_plateau = m.model_SEDs(time, log_mh, a_bh, m_disc, r0, tvi, t_form, ang, frequency)
|
|
1283
|
+
nulnus_risedecay = np.zeros((100, 300))
|
|
1284
|
+
for i in range(0,len(frequency)):
|
|
1285
|
+
nulnus_risedecay[i,:] = m.decay_model(time, log_L, t_decay, t_peak, log_T, v=frequency[i]) + m.rise_model(time, log_L, sigma, t_peak, log_T, v=frequency[i])
|
|
1286
|
+
flux_density = ((nulnus_risedecay + nulnus_plateau)/(4.0 * np.pi * dl**2 * frequency[:,np.newaxis] * 1.0e-26))
|
|
1287
|
+
fmjy = flux_density.T
|
|
1288
|
+
spectra = (fmjy * uu.mJy).to(uu.erg / uu.cm ** 2 / uu.s / uu.Angstrom,
|
|
1289
|
+
equivalencies=uu.spectral_density(wav=lambda_observer_frame * uu.Angstrom))
|
|
1290
|
+
if kwargs['output_format'] == 'spectra':
|
|
1291
|
+
return namedtuple('output', ['time', 'lambdas', 'spectra'])(time=time_observer_frame,
|
|
1292
|
+
lambdas=lambda_observer_frame,
|
|
1293
|
+
spectra=spectra)
|
|
1294
|
+
else:
|
|
1295
|
+
return sed.get_correct_output_format_from_spectra(time=time_obs, time_eval=time_observer_frame,
|
|
1296
|
+
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
1297
|
+
**kwargs)
|
|
1298
|
+
|
|
1299
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2015ApJ...806..164P/abstract, https://ui.adsabs.harvard.edu/abs/2020ApJ...904...73R/abstract')
|
|
1300
|
+
def _stream_stream_collision(mbh_6, mstar, c1, f, h_r, inc_tcool, del_omega):
|
|
1301
|
+
"""
|
|
1302
|
+
A TDE model based on stream-stream collisions. Used as input for the bolometric and broadband versions.
|
|
1303
|
+
|
|
1304
|
+
:param mbh_6: black hole mass (10^6 solar masses)
|
|
1305
|
+
:param mstar: mass of the disrupted star (solar masses)
|
|
1306
|
+
:param c1: characteristic distance scale of the emission region in units of the apocenter distance of the most tightly bound debris
|
|
1307
|
+
:param f: fraction of the bound mass within the semimajor axis of the most tightly bound debris at peak mass return time
|
|
1308
|
+
:param h_r: aspect ratio used to calculate t_cool. This is only used when include_tcool_tdyn_ratio = 1
|
|
1309
|
+
:param inc_tcool: if include_tcool_tdyn_ratio = 1, the luminosity is limited by the Eddington luminosity if t_cool / t_dyn < 1.0
|
|
1310
|
+
:param del_omega: solid angle (in units of pi) of radiation from the emission region
|
|
1311
|
+
:return: physical outputs
|
|
1312
|
+
"""
|
|
1313
|
+
kappa = 0.34
|
|
1314
|
+
t_ratio = 0.0
|
|
1315
|
+
factor = 1.0
|
|
1316
|
+
tcool = 0.0
|
|
1317
|
+
|
|
1318
|
+
rstar = 0.93 * mstar ** (8.0 / 9.0)
|
|
1319
|
+
mstar_max = 15.0
|
|
1320
|
+
Xi = (1.27 - 0.3 *(mbh_6)**0.242 )*((0.620 + np.exp((min(mstar_max,mstar) - 0.674)/0.212))
|
|
1321
|
+
/ (1.0 + 0.553 *np.exp((min(mstar,mstar_max) - 0.674)/0.212)))
|
|
1322
|
+
r_tidal = (mbh_6 * 1e6/ mstar)**(1.0/3.0) * rstar * cc.solar_radius
|
|
1323
|
+
|
|
1324
|
+
epsilon = cc.graviational_constant * (mbh_6 * 1e6 * cc.solar_mass) * (rstar * cc.solar_radius) / r_tidal ** 2.0
|
|
1325
|
+
a0 = cc.graviational_constant * (mbh_6 * 1e6 * cc.solar_mass)/ (Xi * epsilon)
|
|
1326
|
+
|
|
1327
|
+
t_dyn = np.pi / np.sqrt(2.0) * a0 ** 1.5 / np.sqrt(cc.graviational_constant * (mbh_6 * 1e6 * cc.solar_mass))
|
|
1328
|
+
t_peak = (3.0/2.0)*t_dyn
|
|
1329
|
+
mdotmax = mstar * cc.solar_mass / t_dyn / 3.0
|
|
1330
|
+
factor_denom = del_omega * cc.sigma_sb * c1**2 * a0**2
|
|
1331
|
+
|
|
1332
|
+
if inc_tcool == 1:
|
|
1333
|
+
semi = a0 / 2.0 #semimajor axis of the most bound debris
|
|
1334
|
+
area = np.pi * ( c1 * semi ) **2 # emitting area
|
|
1335
|
+
tau = kappa * (f * mstar * cc.solar_mass / 2.0) / area / 2.0 # the characteristic vertical optical depth to the midplane of a circular disk with radius ∼ semi. The first "/2.0" comes from the fact that we consider only the bound mass = mstar / 2. The second "/2.0" comes from the fact that the optical depth was integrated to the mid-plane.
|
|
1336
|
+
tcool = tau * (h_r) * c1 * semi / cc.speed_of_light
|
|
1337
|
+
t_ratio = tcool / t_dyn
|
|
1338
|
+
factor = 2.0 / (1.0 + t_ratio)
|
|
1339
|
+
factor_denom *= (1.0 + 2.0 * h_r) / 4.0
|
|
1340
|
+
|
|
1341
|
+
t_output = np.linspace(t_peak, 1500*cc.day_to_s, 1000)
|
|
1342
|
+
Lmax = mdotmax * (Xi * epsilon) / c1
|
|
1343
|
+
Lobs = Lmax * (t_output / t_peak)**(-5.0/3.0) * factor
|
|
1344
|
+
Tobs = (Lobs / factor_denom )**(1.0/4.0)
|
|
1345
|
+
|
|
1346
|
+
output = namedtuple('output', ['bolometric_luminosity', 'photosphere_temperature',
|
|
1347
|
+
'Smbh_6_accretion_rate_max', 'time_temp', 'cooling_time',
|
|
1348
|
+
'dynamical_time', 'r_tidal','debris_energy'])
|
|
1349
|
+
output.bolometric_luminosity = Lobs
|
|
1350
|
+
output.photosphere_temperature = Tobs
|
|
1351
|
+
output.Smbh_6_accretion_rate_max = mdotmax
|
|
1352
|
+
output.time_temp = t_output
|
|
1353
|
+
output.cooling_time = tcool
|
|
1354
|
+
output.dynamical_time = t_dyn
|
|
1355
|
+
output.r_tidal = r_tidal
|
|
1356
|
+
output.debris_energy = Xi * epsilon
|
|
1357
|
+
return output
|
|
1358
|
+
|
|
1359
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2015ApJ...806..164P/abstract, https://ui.adsabs.harvard.edu/abs/2020ApJ...904...73R/abstract')
|
|
1360
|
+
def stream_stream_tde_bolometric(time, mbh_6, mstar, c1, f, h_r, inc_tcool, del_omega, sigma_t, peak_time, **kwargs):
|
|
1361
|
+
"""
|
|
1362
|
+
A bolometric TDE model based on stream-stream collisions. The early emission follows a gaussian rise.
|
|
1363
|
+
|
|
1364
|
+
:param time: observer frame time in days
|
|
1365
|
+
:param mbh_6: black hole mass (10^6 solar masses)
|
|
1366
|
+
:param mstar: mass of the disrupted star (solar masses)
|
|
1367
|
+
:param c1: characteristic distance scale of the emission region in units of the apocenter distance of the most tightly bound debris
|
|
1368
|
+
:param f: fraction of the bound mass within the semimajor axis of the most tightly bound debris at peak mass return time
|
|
1369
|
+
:param h_r: aspect ratio used to calculate t_cool. This is only used when include_tcool_tdyn_ratio = 1
|
|
1370
|
+
:param inc_tcool: if include_tcool_tdyn_ratio = 1, the luminosity is limited by the Eddington luminosity if t_cool / t_dyn < 1.0
|
|
1371
|
+
:param del_omega: solid angle (in units of pi) of radiation from the emission region
|
|
1372
|
+
:param peak_time: peak time in days
|
|
1373
|
+
:param sigma_t: the sharpness of the Gaussian in days
|
|
1374
|
+
:return: bolometric luminosity
|
|
1375
|
+
"""
|
|
1376
|
+
output = _stream_stream_collision(mbh_6, mstar, c1, f, h_r, inc_tcool, del_omega)
|
|
1377
|
+
f1 = pm.gaussian_rise(time=output.time_temp[0] / cc.day_to_s, a_1=1, peak_time=peak_time, sigma_t=sigma_t)
|
|
1378
|
+
norm = output.bolometric_luminosity[0] / f1
|
|
1379
|
+
|
|
1380
|
+
#evaluate giant array of bolometric luminosities
|
|
1381
|
+
tt_pre_fb = np.linspace(0, output.time_temp[0], 100)
|
|
1382
|
+
tt_post_fb = output.time_temp
|
|
1383
|
+
full_time = np.concatenate([tt_pre_fb, tt_post_fb])
|
|
1384
|
+
f1 = pm.gaussian_rise(time=tt_pre_fb, a_1=norm,
|
|
1385
|
+
peak_time=peak_time * cc.day_to_s, sigma_t=sigma_t * cc.day_to_s)
|
|
1386
|
+
f2 = output.bolometric_luminosity
|
|
1387
|
+
full_lbol = np.concatenate([f1, f2])
|
|
1388
|
+
lbol_func = interp1d(full_time, y=full_lbol, fill_value='extrapolate')
|
|
1389
|
+
return lbol_func(time*cc.day_to_s)
|
|
1390
|
+
|
|
1391
|
+
@citation_wrapper('https://ui.adsabs.harvard.edu/abs/2015ApJ...806..164P/abstract, https://ui.adsabs.harvard.edu/abs/2020ApJ...904...73R/abstract')
|
|
1392
|
+
def stream_stream_tde(time, redshift, mbh_6, mstar, c1, f, h_r, inc_tcool, del_omega, sigma_t, peak_time, **kwargs):
|
|
1393
|
+
"""
|
|
1394
|
+
A TDE model based on stream-stream collisions. The early emission follows a constant temperature gaussian rise.
|
|
1395
|
+
|
|
1396
|
+
:param time: observer frame time in days
|
|
1397
|
+
:param redshift: redshift
|
|
1398
|
+
:param mbh_6: black hole mass (10^6 solar masses)
|
|
1399
|
+
:param mstar: mass of the disrupted star (solar masses)
|
|
1400
|
+
:param c1: characteristic distance scale of the emission region in units of the apocenter distance of the most tightly bound debris
|
|
1401
|
+
:param f: fraction of the bound mass within the semimajor axis of the most tightly bound debris at peak mass return time
|
|
1402
|
+
:param h_r: aspect ratio used to calculate t_cool. This is only used when include_tcool_tdyn_ratio = 1
|
|
1403
|
+
:param inc_tcool: if include_tcool_tdyn_ratio = 1, the luminosity is limited by the Eddington luminosity if t_cool / t_dyn < 1.0
|
|
1404
|
+
:param del_omega: solid angle (in units of pi) of radiation from the emission region
|
|
1405
|
+
:param peak_time: peak time in days
|
|
1406
|
+
:param sigma_t: the sharpness of the Gaussian in days
|
|
1407
|
+
:param kwargs: Must be all the kwargs required by the specific output_format
|
|
1408
|
+
:param output_format: 'flux_density', 'magnitude', 'spectra', 'flux', 'sncosmo_source'
|
|
1409
|
+
:param frequency: Required if output_format is 'flux_density'.
|
|
1410
|
+
frequency to calculate - Must be same length as time array or a single number).
|
|
1411
|
+
:param bands: Required if output_format is 'magnitude' or 'flux'.
|
|
1412
|
+
:param cosmology: Cosmology to use for luminosity distance calculation. Defaults to Planck18. Must be a astropy.cosmology object.
|
|
1413
|
+
:return: set by output format - 'flux_density' or 'magnitude'
|
|
1414
|
+
"""
|
|
1415
|
+
|
|
1416
|
+
cosmology = kwargs.get('cosmology', cosmo)
|
|
1417
|
+
dl = cosmology.luminosity_distance(redshift).cgs.value
|
|
1418
|
+
output = _stream_stream_collision(mbh_6, mstar, c1, f, h_r, inc_tcool, del_omega)
|
|
1419
|
+
|
|
1420
|
+
#get bolometric and temperature info
|
|
1421
|
+
f1 = pm.gaussian_rise(time=output.time_temp[0] / cc.day_to_s, a_1=1, peak_time=peak_time, sigma_t=sigma_t)
|
|
1422
|
+
norm = output.bolometric_luminosity[0] / f1
|
|
1423
|
+
tt_pre_fb = np.linspace(0, output.time_temp[0], 100)
|
|
1424
|
+
tt_post_fb = output.time_temp
|
|
1425
|
+
full_time = np.concatenate([tt_pre_fb, tt_post_fb])
|
|
1426
|
+
f1_src = pm.gaussian_rise(time=tt_pre_fb, a_1=norm,
|
|
1427
|
+
peak_time=peak_time * cc.day_to_s, sigma_t=sigma_t * cc.day_to_s)
|
|
1428
|
+
f2_src = output.bolometric_luminosity
|
|
1429
|
+
full_lbol = np.concatenate([f1_src, f2_src])
|
|
1430
|
+
|
|
1431
|
+
temp1 = np.ones(100) * output.photosphere_temperature[0]
|
|
1432
|
+
temp2 = output.photosphere_temperature
|
|
1433
|
+
full_temp = np.concatenate([temp1, temp2])
|
|
1434
|
+
r_eff = np.sqrt(full_lbol / (np.pi * cc.sigma_sb * full_temp**4.0))
|
|
1435
|
+
|
|
1436
|
+
if kwargs['output_format'] == 'flux_density':
|
|
1437
|
+
frequency = kwargs['frequency']
|
|
1438
|
+
if isinstance(frequency, float):
|
|
1439
|
+
frequency = np.ones(len(time)) * frequency
|
|
1440
|
+
|
|
1441
|
+
else:
|
|
1442
|
+
bands = kwargs['bands']
|
|
1443
|
+
if isinstance(bands, str):
|
|
1444
|
+
bands = [str(bands) for x in range(len(time))]
|
|
1445
|
+
frequency=bands_to_frequency(bands)
|
|
1446
|
+
|
|
1447
|
+
# convert to source frame time and frequency
|
|
1448
|
+
frequency, time = calc_kcorrected_properties(frequency=frequency, redshift=redshift, time=time)
|
|
1449
|
+
unique_frequency = np.sort(np.unique(frequency))
|
|
1450
|
+
|
|
1451
|
+
# build flux density function for each frequency
|
|
1452
|
+
flux_den_interp_func = {}
|
|
1453
|
+
total_time = full_time * (1 + redshift)
|
|
1454
|
+
for freq in unique_frequency:
|
|
1455
|
+
flux_den = sed.blackbody_to_flux_density(temperature=full_temp,
|
|
1456
|
+
r_photosphere=r_eff,
|
|
1457
|
+
dl=dl, frequency=freq).to(uu.mJy)
|
|
1458
|
+
flux_den_interp_func[freq] = interp1d(total_time, flux_den, fill_value='extrapolate')
|
|
1459
|
+
|
|
1460
|
+
# interpolate onto actual observed frequency and time values
|
|
1461
|
+
flux_density = []
|
|
1462
|
+
for freq, tt in zip(frequency, time):
|
|
1463
|
+
flux_density.append(flux_den_interp_func[freq](tt * cc.day_to_s))
|
|
1464
|
+
flux_density = flux_density * uu.mJy
|
|
1465
|
+
|
|
1466
|
+
if kwargs['output_format'] == 'flux_density':
|
|
1467
|
+
return flux_density.to(uu.mJy).value
|
|
1468
|
+
else:
|
|
1469
|
+
return calc_ABmag_from_flux_density(flux_density.to(uu.mJy).value).value
|