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.
Files changed (49) hide show
  1. redback/__init__.py +1 -1
  2. redback/constraints.py +15 -0
  3. redback/eos.py +1 -0
  4. redback/get_data/fink.py +1 -1
  5. redback/model_library.py +2 -2
  6. redback/priors/bazin_sne.prior +5 -0
  7. redback/priors/csm_shock_and_arnett.prior +11 -0
  8. redback/priors/csm_shock_and_arnett_bolometric.prior +10 -0
  9. redback/priors/csm_shock_breakout.prior +7 -0
  10. redback/priors/nicholl_bns.prior +2 -1
  11. redback/priors/pwn.prior +7 -0
  12. redback/priors/shocked_cocoon.prior +6 -6
  13. redback/priors/sn_fallback.prior +8 -0
  14. redback/priors/stream_stream_tde.prior +10 -0
  15. redback/priors/stream_stream_tde_bolometric.prior +9 -0
  16. redback/priors/tde_fallback.prior +9 -0
  17. redback/priors/tde_fallback_bolometric.prior +6 -0
  18. redback/priors/tde_synchrotron.prior +6 -0
  19. redback/priors/two_comp_kne_rosswog_heatingrate.prior +2 -2
  20. redback/priors/villar_sne.prior +7 -0
  21. redback/priors.py +1 -1
  22. redback/simulate_transients.py +11 -4
  23. redback/tables/GRBs_w_redshift.txt +430 -413
  24. redback/tables/LGRB_table.txt +70 -6
  25. redback/tables/SGRB_table.txt +139 -135
  26. redback/tables/qdot_rosswogkorobkin24.pck +0 -0
  27. redback/transient/afterglow.py +14 -5
  28. redback/transient/kilonova.py +5 -2
  29. redback/transient/prompt.py +14 -4
  30. redback/transient/supernova.py +6 -2
  31. redback/transient/tde.py +5 -2
  32. redback/transient/transient.py +27 -10
  33. redback/transient_models/afterglow_models.py +110 -146
  34. redback/transient_models/combined_models.py +39 -33
  35. redback/transient_models/extinction_models.py +1 -2
  36. redback/transient_models/general_synchrotron_models.py +518 -0
  37. redback/transient_models/integrated_flux_afterglow_models.py +2 -2
  38. redback/transient_models/kilonova_models.py +88 -72
  39. redback/transient_models/magnetar_models.py +1 -1
  40. redback/transient_models/phenomenological_models.py +57 -2
  41. redback/transient_models/shock_powered_models.py +159 -110
  42. redback/transient_models/supernova_models.py +161 -7
  43. redback/transient_models/tde_models.py +849 -4
  44. redback/utils.py +28 -12
  45. {redback-1.0.2.dist-info → redback-1.0.31.dist-info}/METADATA +42 -5
  46. {redback-1.0.2.dist-info → redback-1.0.31.dist-info}/RECORD +49 -35
  47. {redback-1.0.2.dist-info → redback-1.0.31.dist-info}/WHEEL +1 -1
  48. {redback-1.0.2.dist-info → redback-1.0.31.dist-info}/LICENCE.md +0 -0
  49. {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
- @citation_wrapper('https://ui.adsabs.harvard.edu/abs/2019ApJ...872..151M/abstract')
623
- def tde_semianalytical():
624
- raise NotImplementedError("This model is not yet implemented.")
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