redback 1.0.2__py3-none-any.whl → 1.0.31__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- redback/__init__.py +1 -1
- redback/constraints.py +15 -0
- redback/eos.py +1 -0
- redback/get_data/fink.py +1 -1
- redback/model_library.py +2 -2
- redback/priors/bazin_sne.prior +5 -0
- 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/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_fallback.prior +9 -0
- redback/priors/tde_fallback_bolometric.prior +6 -0
- redback/priors/tde_synchrotron.prior +6 -0
- redback/priors/two_comp_kne_rosswog_heatingrate.prior +2 -2
- redback/priors/villar_sne.prior +7 -0
- redback/priors.py +1 -1
- redback/simulate_transients.py +11 -4
- redback/tables/GRBs_w_redshift.txt +430 -413
- redback/tables/LGRB_table.txt +70 -6
- redback/tables/SGRB_table.txt +139 -135
- redback/tables/qdot_rosswogkorobkin24.pck +0 -0
- redback/transient/afterglow.py +14 -5
- redback/transient/kilonova.py +5 -2
- redback/transient/prompt.py +14 -4
- redback/transient/supernova.py +6 -2
- redback/transient/tde.py +5 -2
- redback/transient/transient.py +27 -10
- redback/transient_models/afterglow_models.py +110 -146
- redback/transient_models/combined_models.py +39 -33
- redback/transient_models/extinction_models.py +1 -2
- 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 +88 -72
- redback/transient_models/magnetar_models.py +1 -1
- redback/transient_models/phenomenological_models.py +57 -2
- redback/transient_models/shock_powered_models.py +159 -110
- redback/transient_models/supernova_models.py +161 -7
- redback/transient_models/tde_models.py +849 -4
- redback/utils.py +28 -12
- {redback-1.0.2.dist-info → redback-1.0.31.dist-info}/METADATA +42 -5
- {redback-1.0.2.dist-info → redback-1.0.31.dist-info}/RECORD +49 -35
- {redback-1.0.2.dist-info → redback-1.0.31.dist-info}/WHEEL +1 -1
- {redback-1.0.2.dist-info → redback-1.0.31.dist-info}/LICENCE.md +0 -0
- {redback-1.0.2.dist-info → redback-1.0.31.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
|
|
|
@@ -619,6 +619,851 @@ def tde_analytical(time, redshift, l0, t_0_turn, **kwargs):
|
|
|
619
619
|
spectra=spectra, lambda_array=lambda_observer_frame,
|
|
620
620
|
**kwargs)
|
|
621
621
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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
|