Coverage for /Users/andrewkaiser/Documents/GradSchool/Research/gwent/gwent/binary.py : 87%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
"""Base Class for frequency domain strains from Binary Black Holes.
Parameters ---------- M : float Total mass of the black hole binary (m1+m2) q : float Mass ratio of the black hole binary (m1/m2, m1<m2) z : float Redshift of the black hole binary
load_location : string, optional the directory of the loaded file, (ie. '/path/to/file')
Notes ----- IMRPhenomD waveforms calibrated for q = m1/m2 < 18
"""
else: raise ValueError( "args must be a list of 3 ([M,q,z]) or 5 ([M,q,z,chi1,chi2])" )
def M(self):
def M(self, value):
def q(self):
def q(self, value):
def z(self):
def z(self, value):
def h_f(self): if not hasattr(self, "_h_f"): raise NotImplementedError( "The strain must be defined inside BBHFrequencyDomain or BBHTimeDomain classes." ) return self._h_f
def h_f(self, value): self._h_f = value
def f(self): if not hasattr(self, "_f"): raise NotImplementedError( "The frequency must be defined inside BBHFrequencyDomain or BBHTimeDomain classes." ) return self._f
def f(self, value): self._f = value
def var_dict(self):
def var_dict(self, value):
else: raise IOError( "File %s does not exist, please assign load_location a correct filepath." % self.load_location ) else: raise ValueError( 'load_location is not assigned, please set with name_of_BBH.load_location="path/to/file".' )
"""Subclass of BinaryBlackHole for BBH GWs generated in the frequency domain.
Parameters ---------- chi1 : float The dimensionless spin parameter abs(a/m) for black hole m1. chi2 : float The dimensionless spin parameter abs(a/m) for black hole m2
f_low : float, optional The lowest frequency in natural units (Mf, G=c=1) at which the BBH waveform is calculated nfreqs : int, optional The number of frequencies at which the BBH waveform is calculated instrument: object, optional If assigned, the optimal frequency (ie. most sensitive frequency) of the detector is used as the binary's GW frequency
Notes ----- IMRPhenomD waveforms calibrated for aligned spins chi_1, chi_2 = abs(a/m) <= 0.85 or if q=1 abs(a/m)<0.98
"""
elif keys == "f_gw": self.f_gw = value
def chi1(self):
def chi1(self, value):
def chi2(self):
def chi2(self, value):
def instrument(self):
def instrument(self, value):
def h_gw(self): else: raise ValueError( "No instrument assigned, please fix it. " 'Try: "source.instrument = instrument".' )
def h_gw(self, value):
def h_gw(self):
def f_gw(self): else: raise ValueError( "No GW frequency or instrument assigned, please fix it. " 'Try: assigning source.f_gw or source.instrument.' )
def f_gw(self, value): self._f_gw = value
def f_gw(self): del self._f_gw
def h_f(self): self.Get_PhenomD_Strain()
def h_f(self):
def f(self):
def f(self):
"""Loads Quasi-Normal Mode fitting files for speed later.""" load_directory, "PhenomDFiles/fitcoeffsWEB.dat" )
"""Gets the BBH's frequency and waveform from IMRPhenomD.""" self.Get_Fitcoeffs()
"""Calculates the time from merger of a binary black hole given a frequency.
Parameters ---------- freq : float the binary GW frequency in the corresponding frame. frame : str, {'observer','source'} Determines whether the given frequency is in the source or observer frame.
"""
else: raise ValueError("The reference frame can only be observer or source.")
"""Calculates the binary black hole's gravitational wave frequency given a time from merger
Parameters ---------- tau : int, float, Quantity the time from merger in the respective frame. If not an astropy quantity, assumed to be a time in seconds frame : str, {'observer','source'} Determines whether the given frequency is in the source or observer frame.
""" else: raise ValueError("The reference frame can only be observer or source.")
"""Checks the frequency evolution of the black hole binary.
Parameters ---------- T_evol : int,float, Quantity The length of time the binary may evolve T_evol_frame : str, {'observer','source'} Determines whether the given T_evol is in the source or observer frame. f_gw_frame : str, {'observer','source'} Determines whether the frequency is in the source or observer frame. May not be used if source has an instrument assigned.
Notes ----- If the frequency of the binary does evolve over more than one bin, (ie f(T_evol)-f(t_init) = delf_obs < 1/T_evol), it is monochromatic, so we set the frequency to the optimal frequency of the detector
Otherwise it is chirping and evolves over the observation and we set the starting frequency we observe it at to f(Tobs), which is the frequency at an observation time before merger
To get the change in frequency, we use eqn 41 from Hazboun,Romano, and Smith (2019) <https://arxiv.org/abs/1907.04341> which uses binomial expansion of f_T_evol_inst - f_init_inst and thus will never be imaginary
"""
t_init_source = self.Get_Time_From_Merger(self.f_gw,frame=f_gw_frame) else: # Assumes f_init is the optimal frequency in the instrument frame to get t_init_source # f(T_evol), the frequency of the source at T_evol before merger elif hasattr(self,"_f_gw") and not hasattr(self,"_instrument"): t_init_source = self.Get_Time_From_Merger(self.f_gw,frame=f_gw_frame) # f(T_evol), the frequency of the source at T_evol before merger f_T_evol_source = self.Get_Source_Freq(T_evol,frame=T_evol_frame) else: raise ValueError("Must either assign T_evol a value, or assign the source an instrument.") else: # Assumes f_init is the optimal frequency in the instrument frame to get t_init_source # f(T_evol), the frequency of the source at T_evol before merger else: raise ValueError("Must either assign T_evol a value, or assign the source an instrument.")
# Assumes t_init is in source frame, can either be randomly drawn # t_init_source = np.random.uniform(0,100)*u.yr
else: raise ValueError("The reference frame can only be observer or source.")
# f(T_evol) in the instrument frame (ie. the observed frequency)
# t_init_source = make_quant(t_init_source,'s') # f_init_source = self.Get_Source_Freq(t_init_source) # self.f_init = f_init_source/(1+self.z) # f_after_T_obs_source = self.Get_Source_Freq((t_init_source-T_obs_source)) # self.f_T_obs = f_after_T_obs_source/(1+self.z) # delf_obs_source_exact = f_after_T_obs_source-f_init_source
1.0 / 8.0 / np.pi / M_chirp_source * (5 * M_chirp_source / t_init_source) ** (3.0 / 8.0) * (3 * T_evol_source / 8 / t_init_source) )
else:
"""Subclass of BinaryBlackHole for input in the time domain"""
def t(self):
def h_plus_t(self):
def h_cross_t(self):
def h_f(self):
def h_f(self): del self._h_f
def f(self):
def f(self): del self._f
"""Converts dimensionless, time domain strain to frequency space using a windowed fft
Parameters ---------- interp_res : {'coarse','fine'}, optional 'coarse' uses maximum difference between subsequent time steps for interpolation 'fine' uses minimum difference between subsequent time steps for interpolation windowing : {'left','right','all'}, optional 'left' windows the left side of the time data 'right' windows the right side of the time data 'all' windows the both the left and right side of the time data
Returns ------- natural_f : array The frequency of the input source in natural units (G=c=1) natural_h : array The strain of the input source in natural units (G=c=1)
"""
# Interpolate time to evenly sampled data, can be fine or coarse dt = min(diff_t)
# interpolate strain to evenly sampled data for FFT
# Filter/Window ######################### """Applies window to first (left) half""" : int(len(interp_t) / 2) ] # Only need tapering on first half of waveform len(interp_t) - len(first_half) ) # no windowing on second half of waveform ######################### first_half, second_half ) # Only apply window to first half of waveform elif windowing == "right": ######################### """Applies window to second (right) half""" second_half = hann_window[ int(len(interp_t) / 2) : ] # Only need tapering on second half of waveform first_half = np.ones( len(interp_t) - len(second_half) ) # no windowing on first half of waveform ######################### window = np.append(first_half, second_half) elif windowing == "all": window = hann_window # Window!
# FFT the two polarizations
# cut = np.abs(freqs).argmax() #Cut off the negative frequencies freqs - f_cut_low ).argmin() # Cut off frequencies lower than a frequency freqs - f_cut_high ).argmin() # Cut off frequencies higher than a frequency # cut=int(len(freqs)*0.9) #Cut off percentage of frequencies
# Combine them for raw spectral power
"""Converts frequency and strain in natural units (G=c=1) to Hertz and dimensionless, respectively.
Parameters ---------- source Instance of gravitational wave source class natural_f : array [Mf] the frequency of the source in natural units (G=c=1) natural_h : array [Mf] the strain of the source in natural units (G=c=1)
"""
# frequency and strain of source in detector frame # Normalized factor to match Stationary phase approx at low frequencies? # Changed from sqrt(5/16/pi)
"""Converts source strain to characteristic strain
Parameters ---------- source : object Instance of gravitational wave source class
"""
"""Calculates the strain from a binary black hole.
Parameters ---------- source : object Instance of gravitational wave source class inc : float, optional The inclination of the source in radians. If inc is None, the strain is sky and inclination averaged strain from Robson et al. 2019 (eqn 27) <https://arxiv.org/pdf/1803.01944.pdf> 'Optimal' gives the optimally oriented, face-on, inclination (ie. inc=0) value frame : str, {'source','observer'} Determines whether the frequency is in the source or observer frame. May not be used if source has an instrument assigned.
Returns ------- float the strain of a monochromatic source in the dector frame
""" else: raise ValueError("The reference frame can only be observer or source.")
# Converts M = [M] to M = [sec]
raise ValueError( 'Inclination must be between -pi and pi.' ) else:
const_val * (const.c / DL) * (np.pi * f_gw) ** (2.0 / 3.0) * M_chirp ** (5.0 / 3.0) ) |