1212import matplotlib .pyplot as plt
1313import os
1414from datetime import datetime
15-
15+ import warnings
1616
1717import holodeck as holo
1818from holodeck import utils , cosmo , log , plot , sam_cython
1919from holodeck .constants import MPC , YR
2020from holodeck .sams import cyutils as sam_cyutils
2121
22- import hasasia .sensitivity as hsen
23- import hasasia .sim as hsim
24- import hasasia as has
22+ try :
23+ from sympy import nsolve , Symbol
24+ import hasasia .sensitivity as hsen
25+ import hasasia .sim as hsim
26+ # import hasasia as has
27+ except ImportError as err :
28+ SUBMOD = "detstats"
29+ log .error (f"Failed to import some packages used in `{ SUBMOD } ` submodule!" )
30+ log .exception (err )
31+ log .error (
32+ f"Some required packages for `{ SUBMOD } ` have been temporarily disabled in the "
33+ "global 'requirements.txt' file, so they are not installed by default! Please install "
34+ "the required packages manually for now, and feel free to raise a github issue."
35+ )
36+ raise
2537
2638GAMMA_RHO_GRID_PATH = '/Users/emigardiner/GWs/holodeck/output/rho_gamma_grids'
2739HC_REF15_10YR = 11.2 * 10 ** - 15
@@ -183,7 +195,7 @@ def _white_noise(delta_t, sigma_i):
183195
184196######################## Power Spectral Density ########################
185197
186- def _power_spectral_density (hc_bg , freqs ):
198+ def _power_spectral_density (hc_bg , freqs , reshape_freqs = True ):
187199 """ Calculate the spectral density S_h(f_k) ~ S_h0(f_k) at the kth frequency
188200
189201 Parameters
@@ -202,8 +214,10 @@ def _power_spectral_density(hc_bg, freqs):
202214
203215 Follows Eq. (25) of Rosado et al. 2015
204216 """
217+ if reshape_freqs :
218+ freqs = freqs [:,np .newaxis ]
205219
206- S_h = hc_bg ** 2 / (12 * np .pi ** 2 * freqs [:, np . newaxis ] ** 3 )
220+ S_h = hc_bg ** 2 / (12 * np .pi ** 2 * freqs ** 3 )
207221 return S_h
208222
209223
@@ -488,8 +502,9 @@ def detect_bg(thetas, phis, sigmas, fobs, cad, hc_bg, alpha_0=0.001, ret = False
488502
489503
490504
491- def detect_bg_pta (pulsars , fobs , hc_bg , hc_ss , alpha_0 = 0.001 , ret_snr = False ,
492- red_amp = None , red_gamma = None , ss_noise = True ):
505+ def detect_bg_pta (pulsars , fobs , hc_bg , hc_ss = None , custom_noise = None ,
506+ alpha_0 = 0.001 , ret_snr = False ,
507+ red_amp = None , red_gamma = None , ss_noise = False ):
493508 """ Calculate the background detection probability, and all the intermediary steps
494509 from a list of hasasia.Pulsar objects.
495510
@@ -538,6 +553,25 @@ def detect_bg_pta(pulsars, fobs, hc_bg, hc_ss, alpha_0=0.001, ret_snr = False,
538553 Sh_bg = _power_spectral_density (hc_bg [:], fobs )
539554 Sh0_bg = Sh_bg # note this refers to same object, not a copy
540555
556+ # noise spectral density
557+ if custom_noise is not None :
558+ if custom_noise .shape != (len (pulsars ), len (fobs ), len (hc_bg [0 ])):
559+ err = f"{ custom_noise .shape = } , must be shape (P,F,R)=({ len (pulsars )} , { len (fobs )} , { len (hc_bg [0 ])} )"
560+ raise ValueError (err )
561+ noise = custom_noise
562+ else :
563+ # calculate white noise
564+ noise = _white_noise (cad , sigmas )[:,np .newaxis ] # P,1
565+
566+ # add red noise
567+ if (red_amp is not None ) and (red_gamma is not None ):
568+ red_noise = _red_noise (red_amp , red_gamma , fobs )[np .newaxis ,:] # (1,F,)
569+ noise = noise + red_noise # (P,F,)
570+
571+ # add single source noise
572+ noise = noise [:,:,np .newaxis ]
573+ if ss_noise :
574+ noise = noise + _Sh_ss_noise (hc_ss , fobs ) # (P, F, R)
541575 # calculate white noise
542576 noise = _white_noise (cad , sigmas )[:,np .newaxis ] # P,1
543577
@@ -872,7 +906,7 @@ def _antenna_pattern_functions(m_hat, n_hat, Omega_hat, pi_hat):
872906######################## Noise Spectral Density ########################
873907
874908
875- def _Sh_rest_noise (hc_ss , hc_bg , freqs ):
909+ def _Sh_rest_noise (hc_ss , hc_bg , freqs , nexcl = 0 ):
876910 """ Calculate the noise spectral density contribution from all but the current single source.
877911
878912 Parameters
@@ -883,6 +917,10 @@ def _Sh_rest_noise(hc_ss, hc_bg, freqs):
883917 Characteristic strain from all but loudest source at each frequency.
884918 freqs : (F,) 1Darray
885919 Frequency bin centers.
920+ exclude_loudest : int
921+ Number of loudest single sources to exclude from hc_rest noise, in addition
922+ to the source in question.
923+
886924
887925 Returns
888926 -------
@@ -891,10 +929,15 @@ def _Sh_rest_noise(hc_ss, hc_bg, freqs):
891929
892930 Follows Eq. (45) in Rosado et al. 2015.
893931 """
894- hc2_louds = np .sum (hc_ss ** 2 , axis = 2 ) # (F,R)
895- # subtract the single source from rest of loud sources and the background, for each single source
896- hc2_rest = hc_bg [:,:,np .newaxis ]** 2 + hc2_louds [:,:,np .newaxis ] - hc_ss ** 2 # (F,R,L)
897- Sh_rest = hc2_rest / freqs [:,np .newaxis ,np .newaxis ]** 3 / (12 * np .pi ** 2 ) # (F,R,L)
932+
933+ if nexcl > 0 :
934+ Sh_rest = cyutils .Sh_rest (hc_ss , hc_bg , freqs , nexcl )
935+ else :
936+ hc2_louds = np .sum (hc_ss ** 2 , axis = 2 ) # (F,R)
937+ # subtract the single source from rest of loud sources and the background, for each single source
938+ hc2_rest = hc_bg [:,:,np .newaxis ]** 2 + hc2_louds [:,:,np .newaxis ] - hc_ss ** 2 # (F,R,L)
939+ Sh_rest = hc2_rest / freqs [:,np .newaxis ,np .newaxis ]** 3 / (12 * np .pi ** 2 ) # (F,R,L)
940+
898941 return Sh_rest
899942
900943
@@ -913,7 +956,7 @@ def _Sh_ss_noise(hc_ss, freqs):
913956
914957 Returns
915958 -------
916- Sh_ss : (F,R,L ) NDarray of scalars
959+ Sh_ss : (F,R,R ) NDarray of scalars
917960 The noise in a single pulsar from other GW sources for detecting each single source.
918961
919962 Follows Eq. (45) in Rosado et al. 2015.
@@ -952,7 +995,8 @@ def _red_noise(red_amp, red_gamma, freqs, f_ref=1/YR):
952995
953996
954997
955- def _total_noise (delta_t , sigmas , hc_ss , hc_bg , freqs , red_amp = None , red_gamma = None ):
998+ def _total_noise (delta_t , sigmas , hc_ss , hc_bg , freqs , red_amp = None , red_gamma = None ,
999+ nexcl = 0 ):
9561000 """ Calculate the noise spectral density of each pulsar, as it pertains to single
9571001 source detections, i.e., including the background as a noise source.
9581002
@@ -968,6 +1012,9 @@ def _total_noise(delta_t, sigmas, hc_ss, hc_bg, freqs, red_amp=None, red_gamma=N
9681012 Characteristic strain from all but loudest source at each frequency.
9691013 freqs : (F,) 1Darray
9701014 Frequency bin centers.
1015+ exclude_loudest : int
1016+ Number of loudest single sources to exclude from hc_rest noise
1017+
9711018
9721019 Returns
9731020 -------
@@ -978,13 +1025,57 @@ def _total_noise(delta_t, sigmas, hc_ss, hc_bg, freqs, red_amp=None, red_gamma=N
9781025 """
9791026
9801027 noise = _white_noise (delta_t , sigmas ) # (P,)
981- Sh_rest = _Sh_rest_noise (hc_ss , hc_bg , freqs ) # (F,R,L,)
1028+ Sh_rest = _Sh_rest_noise (hc_ss , hc_bg , freqs , nexcl ) # (F,R,L,)
9821029 noise = noise [:,np .newaxis ,np .newaxis ,np .newaxis ] + Sh_rest [np .newaxis ,:,:,:] # (P,F,R,L)
9831030 if (red_amp is not None ) and (red_gamma is not None ):
9841031 red_noise = _red_noise (red_amp , red_gamma , freqs ) # (F,)
9851032 noise = noise + red_noise [np .newaxis ,:,np .newaxis ,np .newaxis ] # (P,F,R,L)
9861033 return noise
9871034
1035+ def psrs_spectra_gwbnoise (psrs , fobs , nreals , npsrs , divide_flag = False ):
1036+ """ Get GWBSensitivityCurve noise and spectra for psrs
1037+
1038+ Returns
1039+ -------
1040+ spectra :
1041+ noise_gsc : (P,F,R)
1042+ """
1043+ spectra = []
1044+ for psr in psrs :
1045+ sp = hsen .Spectrum (psr , freqs = fobs )
1046+ sp .NcalInv
1047+ spectra .append (sp )
1048+ sc_bg = hsen .GWBSensitivityCurve (spectra ).h_c
1049+ noise_gsc = sc_bg ** 2 / (12 * np .pi ** 2 * fobs ** 3 )
1050+ noise_gsc = np .repeat (noise_gsc , npsrs * nreals ).reshape (len (fobs ), npsrs , nreals ) # (F,P,R)
1051+ noise_gsc = np .swapaxes (noise_gsc , 0 , 1 ) # (P,F,R)
1052+
1053+ if divide_flag : noise_gsc *= npsrs * (npsrs - 1 )
1054+
1055+ return spectra , noise_gsc
1056+
1057+ def _dsc_noise (fobs , nreals , npsrs , nloudest , psrs = None , spectra = None , divide_flag = False ):
1058+ """ Get DeterSensitivityCurve noise using either psrs or spectra
1059+
1060+ Returns
1061+ -------
1062+ noise_dsc : (P,F,R,L) NDarray
1063+ """
1064+
1065+ if spectra is None :
1066+ assert psrs is not None , 'Must provide spectra or psrs'
1067+ spectra = []
1068+ for psr in psrs :
1069+ sp = hsen .Spectrum (psr , freqs = fobs )
1070+ sp .NcalInv
1071+ spectra .append (sp )
1072+ sc_ss = hsen .DeterSensitivityCurve (spectra ).h_c
1073+ noise_dsc = sc_ss ** 2 / (12 * np .pi ** 2 * fobs ** 3 )
1074+ noise_dsc = np .repeat (noise_dsc , npsrs * nreals * nloudest ).reshape (len (fobs ), npsrs , nreals , nloudest ) # (F,P,R,L)
1075+ noise_dsc = np .swapaxes (noise_dsc , 0 , 1 ) # (P,F,R,L)
1076+
1077+ if divide_flag : noise_dsc *= npsrs * (npsrs - 1 )
1078+ return noise_dsc
9881079
9891080
9901081################### GW polarization, phase, amplitude ###################
@@ -1191,7 +1282,7 @@ def _Fe_thresh(Num, alpha_0=0.001, guess=15):
11911282 Fe_bar = Symbol ('Fe_bar' )
11921283 func = 1 - (1 - (1 + Fe_bar )* np .e ** (- Fe_bar ))** Num - alpha_0
11931284 Fe_bar = nsolve (func , Fe_bar , guess ) # mod from
1194- return ( Fe_bar )
1285+ return Fe_bar
11951286
11961287def _I1_approx (xx ):
11971288 """ Modified Bessel Function of the first kind, first order, expansion for large values.
@@ -1354,7 +1445,7 @@ def _gamma_ssi_cython(rho, grid_path):
13541445 # interpolate for gamma in cython
13551446 rho_flat = rho [:,rr ].flatten ()
13561447 rsort = np .argsort (rho_flat )
1357- gamma_flat = sam_cyutils .gamma_of_rho_interp (rho_flat , rsort , rho_interp_grid , gamma_interp_grid )
1448+ gamma_flat = cyutils .gamma_of_rho_interp (rho_flat , rsort , rho_interp_grid , gamma_interp_grid )
13581449 gamma_ssi [:,rr ] = gamma_flat .reshape (rho [:,rr ].shape )
13591450
13601451
0 commit comments