Coverage for /Users/andrewkaiser/Documents/GradSchool/Research/gwent/gwent/detector.py : 85%

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
r""" Class to make a PTA instrument using the methods of Hazboun, Romano, Smith (2019) <https://arxiv.org/abs/1907.04341>
Parameters ----------
name : string name of the instrument
n_p : int the number of pulsars in the PTA
T_obs : float,array, list, optional the observation time of the PTA in [years] If T_obs is a float, the observation time is used as the observation time for all pulsars If T_obs is a list of length of n_p, the observation times are used as the corresponding pulsar observation times. If T_obs is a list of length 2, it is assumed the values are the minimum and maximum observation time values (ie. [min,max]) from which individual pulsar observation times are uniformly sampled. sigma : float, array, list, optional the rms error on the pulsar TOAs in [sec] If sigma is a float, the given rms error is used for all pulsars. If sigma is a list of length of n_p, the amplitudes are used as the corresponding pulsar rms error. If sigma is a list of length 2, it is assumed the values are the minimum and maximum rms errors values (ie. [min,max]) from which individual pulsar rms errors are uniformly sampled. cadence : float, array, list, optional How often the pulsars are observed in [num/year] If cadence is a float, the given cadence is used for all pulsars. If cadence is a list of length of n_p, the amplitudes are used as the corresponding pulsar cadence. If cadence is a list of length 2, it is assumed the values are the minimum and maximum cadence values (ie. [min,max]) from which individual pulsar cadences are uniformly sampled. sb_amp : float, optional Amplitude of the Stochastic background added as red noise sb_alpha : float, optional the Stochastic background power law, if empty and sb_amp is set, it is assumed to be -2/3 rn_amp : array, list, optional Individual pulsar red noise amplitude. If rn_amp is a list of length of n_p, the amplitudes are used as the corresponding pulsar RN injection. If rn_amp is a list of length 2, it is assumed the values are the minimum and maximum RN amplitude values (ie. [min,max]) from which individual pulsar RN amplitudes are uniformly sampled. rn_alpha : array, list, optional Individual pulsar red noise alpha (power law spectral index). If rn_alpha is a list of length of n_p, the alpha indices are used as the corresponding pulsar RN injection. If rn_alpha is a list of length 2, it is assumed the values are the minimum and maximum RN alpha values (ie. [min,max]) from which individual pulsar RN alphas are uniformly sampled. phi : list, array, optional Individual pulsar longitude in ecliptic coordinates. If not defined, NANOGrav 11yr pulsar locations are used. If n_p > 34 (the number of pulsars in the 11yr dataset), it draws more pulsars from distributions based on the NANOGrav 11yr pulsars. theta : array, list, optional Individual pulsar colatitude in ecliptic coordinates. If not defined, NANOGrav 11yr pulsar locations are used. If n_p > 34 (the number of pulsars in the 11yr dataset), it draws more pulsars from distributions based on the NANOGrav 11yr pulsars. use_11yr : bool, optional Uses the NANOGrav 11yr noise as the individual pulsar noises, if n_p > 34 (the number of pulsars in the 11yr dataset), it draws more pulsars from distributions based on the NANOGrav 11yr pulsar noise use_rn : bool, optional If no rn_amp assigned, uses the NANOGrav 11yr noise as the individual pulsar RN noises, if n_p > 34 (the number of pulsars in the 11yr dataset), it draws more pulsars from distributions based on the NANOGrav 11yr pulsar noise load_location : string, optional If you want to load a PTA curve from a file, it's the file path I_type : string, {'E','A','h'} Sets the type of input data. 'E' is the effective strain spectral density $S_{n}(f)$ ('ENSD'), 'A' is the amplitude spectral density, $\sqrt{S_{n}(f)}$ ('ASD'), 'h' is the characteristic strain $h_{n}(f)$ ('h') f_low : float, optional Assigned lowest frequency of PTA (default assigns 1/(5*T_obs)) f_high : float, optional Assigned highest frequency of PTA (default is Nyquist freq cadence/2) nfreqs : int, optional Number of frequencies in logspace the sensitivity is calculated nbins : int, optional Used to add values to every bin for sampled parameters. Default is 8 for smooth, non-zero distributions. Changing this could change distribution, so be wary, not sure how much it affects anything. """
else:
else:
np.logspace( np.log10(self.f_low.value), np.log10(self.f_high.value), self.nfreqs ) * u.Hz )
def n_p(self):
def n_p(self, value):
def T_obs(self):
def T_obs(self, value):
def cadence(self):
def cadence(self, value):
def sigma(self):
def sigma(self, value):
def phi(self):
def phi(self, value):
def theta(self):
def theta(self, value):
def rn_amp(self):
def rn_amp(self, value):
def rn_alpha(self):
def rn_alpha(self, value):
def var_dict(self):
def var_dict(self, value):
def fT(self): # frequency sampled from 1/observation time to nyquist frequency (c/2) # 5 is the default value for now (from Hazboun et al. 2019) np.logspace( np.log10(1 / (5 * T_obs_sec)), np.log10(cadence_sec / 2), self.nfreqs, ) * u.Hz )
def fT(self, value):
def fT(self):
def h_n_f(self): """Effective Strain Noise Amplitude""" elif self._I_Type == "ASD": S_n_f_sqrt = self._I_data[:, 1] self._h_n_f = S_n_f_sqrt * np.sqrt(self.fT.value) else:
def h_n_f(self, value): self._h_n_f = value
def h_n_f(self):
def S_n_f(self): """Effective noise power amplitude""" S_n_f_sqrt = self._I_data[:, 1] self._S_n_f = S_n_f_sqrt ** 2 / u.Hz elif self._I_Type == "h": self._S_n_f = self.h_n_f ** 2 / self.fT else:
def S_n_f(self, value):
def S_n_f(self):
def f_opt(self): """The optimal frequency of the instrument ie. the frequecy at the lowest strain"""
"""Loads in NANOGrav 11yr data
Notes ----- The file is in the form of observation times (T_obs) in the first column, sky locations (phi,theta) in the second and third columns, Individual Pulsar cadences and WN RMS (sigmas) in the fourth and fifth, RN Amplitudes, and RN Alphas in the last two columns. """ load_directory, "InstrumentFiles/NANOGrav/NANOGrav_11yr_params.txt" )
"""Gets the noise parameter values (sigma, Rn_amplitudes,RN alphas) and sky locations (phis, thetas) and generates populated arrays from which distributions can be made. If no user values for a param are given, it uses the NANOGrav 11yr parameters.
var_name : string The name of the noise parameter to assign sampled parameters NG_11yr_idx : int Index of corresponding value in NANOGrav 11yr params """
# Add non-zero probability of picking 0 and 2pi # Add non-zero probability of picking 0 and pi elif var_name == "rn_amp": return np.append( samp_var, np.logspace( min(np.log10(samp_var)), max(np.log10(samp_var)), self.nbins, ), ) else: return np.append( samp_var, np.linspace(min(samp_var), max(samp_var), self.nbins) ) else: return np.ones(self.n_p) * var else: else: return np.append( var, np.logspace( min(np.log10(unique_vals)), max(np.log10(unique_vals)), self.nbins, ), ) else: var, np.linspace( min(unique_vals), max(unique_vals), self.nbins, ), ) else: raise ValueError( "{} must be a single value, or the same length as n_p: {}".format( var_name, self.n_p ) ) else: # Uniformly sample in logspace np.log10(var[0]), np.log10(var[1]), size=self.n_p ) else: # Uniformly Sample in linspace elif len(var) == self.n_p: samp_var = var else: raise ValueError( "To sample {}, it must be either [min,max], or an array of individual pulsar {} of length n_p: {}".format( var_name, var_name, self.n_p ) )
else: samp_var, np.linspace(min(samp_var), max(samp_var), self.nbins), ) else: else: self.var_dict[var_name]["sampled"] == True samp_var = self._NANOGrav_11yr_params[NG_11yr_idx] if var_name == "phi": # Add non-zero probability of picking 0 and 2pi return np.append( samp_var, np.linspace(0.0, 2 * np.pi, self.nbins) ) elif var_name == "theta": # Add non-zero probability of picking 0 and pi return np.append(samp_var, np.linspace(0.0, np.pi, self.nbins)) elif var_name == "rn_amp": return np.append( samp_var, np.logspace( min(np.log10(samp_var)), max(np.log10(samp_var)), self.nbins, ), ) else: return np.append( samp_var, np.linspace(min(samp_var), max(samp_var), self.nbins), )
"""For observation times, all noise parameters (sigma, Rn_amplitudes,RN alphas), cadence, and sky locations (phis, thetas), uses the individual parameter value ranges and generates distributions from which to draw new parameters.
Notes ----- To draw from the generated distributions, one does draws = self._distribution.rvs(size=sample_size) """
samp_var, bins=np.logspace( min(np.log10(samp_var)), max(np.log10(samp_var)), self.nbins ), density=True, ) else: else: return np.ones(num_draws) * samp_var[0] else:
"""Initializes a PTA in hasasia
Notes ----- Assigns pulsar parameters based on what the initial values were given per parameter. If necessary parameters are left unassigned, it uses 11yr values for n_p <= 34, and samples from the 11yr parameters if n_p > 34 If a range of values were given for a parameter, the per pulsar parameters are drawn from a uniform distribution assigns the new pulsar parameters to the corresponding PTA class parameter. See Hazboun, Romano, Smith (2019) <https://arxiv.org/abs/1907.04341> for details
"""
NG_T_obs, NG_phis, NG_thetas, NG_cadences, NG_sigmas, NG_rn_amps, NG_rn_alphas, ] = self._NANOGrav_11yr_params
else: setattr(self, var, prev_var[: self.n_p]) self, var, np.append(prev_var, var_draw), ) else: else: # Constant values for all pulsars else: # Constant values for all pulsars else: # Assign/sample values for values needed to make a sensitivity curve # 34 pulsars in the 11yr dataset (ie. len(phis)) self, var, self._NANOGrav_11yr_params[i][: self.n_p] ) else: n_added_p = self.n_p - len(self._NANOGrav_11yr_params[i]) var_draw = self.Get_Sample_Draws(var, n_added_p) setattr( self, var, np.append(self._NANOGrav_11yr_params[i], var_draw), ) else: else: self, var, self._NANOGrav_11yr_params[i][: self.n_p] ) else: n_added_p = self.n_p - len( self._NANOGrav_11yr_params[i] ) var_draw = self.Get_Sample_Draws(var, n_added_p) setattr( self, var, np.append(self._NANOGrav_11yr_params[i], var_draw), ) else: setattr(self, var, self.Get_Sample_Draws(var, self.n_p))
""" if hasattr(self, "sb_amp"): psrs = hassim.sim_pta( timespan=self.T_obs.value, cad=self.cadence.value, sigma=self.sigma.value, phi=self.phi, theta=self.theta, Npsrs=self.n_p, A_rn=self.rn_amp, alpha=self.rn_alpha, A_gwb=self.sb_amp, freqs=self.fT.value, ) else: """ timespan=self.T_obs.value, cad=self.cadence.value, sigma=self.sigma.value, phi=self.phi, theta=self.theta, Npsrs=self.n_p, A_rn=self.rn_amp, alpha=self.rn_alpha, freqs=self.fT.value, ) # Make a set of psrs with the same parameters with a sb as red noise timespan=self.T_obs.value, cad=self.cadence.value, sigma=self.sigma.value, phi=self.phi, theta=self.theta, Npsrs=self.n_p, A_rn=self.sb_amp, alpha=self.sb_alpha, freqs=self.fT.value, ) else: timespan=self.T_obs.value, cad=self.cadence.value, sigma=self.sigma.value, phi=self.phi, theta=self.theta, Npsrs=self.n_p, freqs=self.fT.value, ) # Turn of sampling for an initialized PTA (except if sampling n_p) else: # Get Spectra of pulsars
r""" Class to make an interferometer
Parameters ----------
name : string name of the instrument
T_obs : float the observation time of the PTA in [years]
load_location : string, optional If you want to load an instrument curve from a file, it's the file path I_type : string, {'E','A','h'} Sets the type of input data. 'E' is the effective strain spectral density $S_{n}(f)$ ('ENSD'), 'A' is the amplitude spectral density, $\sqrt{S_{n}(f)}$ ('ASD'), 'h' is the characteristic strain $h_{n}(f)$ ('h') f_low : float, optional Assigned lowest frequency of instrument (default is assigned in particular child classes) f_high : float, optional Assigned highest frequency of instrument (default is assigned in particular child classes) nfreqs : int, optional Number of frequencies in logspace the sensitivity is calculated (default is 1e3)
"""
def T_obs(self):
def T_obs(self, value):
def var_dict(self):
def var_dict(self, value):
def fT(self): self._fT = self._I_data[:, 0] * u.Hz np.logspace( np.log10(self.f_low.value), np.log10(self.f_high.value), self.nfreqs, ) * u.Hz )
def fT(self, value):
def fT(self):
def f_opt(self): """The optimal frequency of the instrument ie. the frequecy at the lowest strain"""
def P_n_f(self): """Strain power sensitivity. """ raise NotImplementedError( "Power Spectral Density method must be defined inside SpaceBased or GroundBased classes." )
def S_n_f(self): """Effective Noise Power Specral Density""" if not hasattr(self, "_S_n_f"): if hasattr(self, "_I_data"): if self._I_Type == "ASD": S_n_f_sqrt = self._I_data[:, 1] self._S_n_f = S_n_f_sqrt ** 2 / u.Hz elif self._I_Type == "ENSD": self._S_n_f = self._I_data[:, 1] / u.Hz elif self._I_Type == "h": self._S_n_f = self.h_n_f ** 2 / self.fT else: raise NotImplementedError( "Effective Noise Power Spectral Density method must be defined inside SpaceBased or GroundBased classes." ) return self._S_n_f
def S_n_f(self): del self._S_n_f
def h_n_f(self): """Characteristic Strain/effective strain noise amplitude""" self._h_n_f = self._I_data[:, 1] else:
def h_n_f(self):
""" Class to make a Ground Based Instrument using the Interferometer base class
Parameters ---------- noise_dict : dictionary, optional A nested noise dictionary that has the main variable parameter name(s) in the top level, the next level of the dictionary contains the subparameter variable name(s) and the desired value to which the subparameter will be changed. The subparameter value can also be an array/list of the [value,min,max] if one wishes to vary the instrument over then min/max range.
"""
else: raise ValueError(keys + " must be a dictionary of noise sources.")
self.Init_GroundBased() else:
def P_n_f(self): """Power Spectral Density. """
def S_n_f(self): """Effective Noise Power Spectral Density""" elif self._I_Type == "ENSD": self._S_n_f = self._I_data[:, 1] / u.Hz elif self._I_Type == "h": self._S_n_f = self.h_n_f ** 2 / self.fT else: hasattr(self, attr) for attr in ["_noise_budget", "_ifo", "_base_inst"] ): self.Init_GroundBased() self._noise_budget(self.fT.value, ifo=self._ifo).calc() / u.Hz )
def S_n_f(self):
"""Initialized the Ground Based detector in gwinc""" name for name in self.name.split() if name in gwinc.available_ifos() ] else: print( "You must select a base instrument model from ", [model for model in gwinc.available_ifos()], ) print( "Setting base instrument to aLIGO. To change base instrument, include different model in class name and reinitialize." ) self._base_inst = "aLIGO"
"""Sets new values in the nested dictionary of variable noise values
Parameters ----------
noise_dict : dictionary A nested noise dictionary that has the main variable parameter name(s) in the top level, the next level of the dictionary contains the subparameter variable name(s) and the desired value to which the subparameter will be changed. The subparameter value can also be an array/list of the [value,min,max] if one wishes to vary the instrument over then min/max range.
Examples -------- obj.Set_Noise_Dict({'Infrastructure':{'Length':[3000,1000,5000],'Temp':500},'Laser':{'Wavelength':1e-5,'Power':130}})
""" for sub_sub_noise, sub_sub_noise_val in sub_noise_val.items(): self.var_dict = [ base_noise + " " + sub_noise + " " + sub_sub_noise, sub_sub_noise_val, ] setattr( getattr(self._ifo, base_noise)[sub_noise], sub_sub_noise, self._return_value, ) else: base_noise + " " + sub_noise, sub_noise_val, ] getattr(self._ifo, base_noise), sub_noise, self._return_value, ) else: raise ValueError( sub_noise + " is not a subparameter variable noise source.\ Try calling Get_Noise_Dict on your GroundBased object to find acceptable variables." ) else: err_mssg = ( base_noise + " is not a valid parameter variable noise source.\n" ) err_mssg += "Try calling Get_Noise_Dict on your GroundBased object to find acceptable variables." raise ValueError(err_mssg) else: raise ValueError("Input must be a dictionary of noise sources.")
"""Gets and prints the available variable noises in the detector design""" " ", " ", key_3, ": array of shape", item_3.shape ) i += 1 print( " ", " ", key_3, ": array of shape", len(item_3) ) else:
""" Class to make a Space Based Instrument using the Interferometer base class
Parameters ---------- L : float the armlength the of detector in [meters] A_acc : float the Amplitude of the Acceleration Noise in [meters/second^2] f_acc_break_low : float the lower break frequency of the acceleration noise in [Hz] f_acc_break_high : float the higher break frequency of the acceleration noise in [Hz] A_IFO : float the amplitude of the interferometer
T_type : string, {'N','A'} Picks the transfer function generation method 'N' uses the numerically approximated method in Robson, Cornish, and Liu, 2019 'A' uses the analytic fit in Larson, Hiscock, and Hellings, 2000 Background : Boolean Add in a Galactic Binary Confusion Noise
"""
def L(self):
def L(self, value):
def A_acc(self):
def A_acc(self, value):
def f_acc_break_low(self):
def f_acc_break_low(self, value):
def f_acc_break_high(self):
def f_acc_break_high(self, value):
def A_IFO(self):
def A_IFO(self, value):
def f_IFO_break(self):
def f_IFO_break(self, value):
def P_n_f(self): """Power Spectral Density"""
self.A_acc ** 2 * (1 + (self.f_acc_break_low / self.fT) ** 2) * (1 + (self.fT / (self.f_acc_break_high)) ** 4) / (2 * np.pi * self.fT) ** 4 ) # Acceleration Noise 1 + (self.f_IFO_break / self.fT) ** 4 ) # Displacement noise of the interferometric TM--to-TM
(P_IMS + 2 * (1 + np.cos(self.fT.value / f_trans.value) ** 2) * P_acc) / self.L ** 2 / u.Hz )
def P_n_f(self):
def S_n_f(self): """Effective Noise Power Specral Density""" S_n_f_sqrt = self._I_data[:, 1] self._S_n_f = S_n_f_sqrt ** 2 / u.Hz elif self._I_Type == "h": self._S_n_f = self.h_n_f ** 2 / self.fT else: else:
def S_n_f(self):
# Numerical transfer function load_directory, "NumericalTransferFunction/transfer.dat" ) Numerical_Transfer_Function_filedirectory )
# 3/10 is normalization 2/5sin(openingangle) # Some papers use 3/20, not summing over 2 independent low-freq data channels np.sqrt(3 / 10) * self._transferfunctiondata[idx_f_5:idx_f_1, 1] )
# Response function approximation from Calculation described by Cornish, Robson, Liu 2019 np.logspace( np.log10(self.f_low.value), np.log10(self.f_high.value), self.nfreqs ) * u.Hz ) # 3/10 is normalization 2/5sin(openingangle)
else: print("\nYou can get the transfer function via 2 methods:") print( ' *To use the numerically approximated method in Robson, Cornish, and Liu, 2019, input "N".' ) print( ' *To use the analytic fit in Larson, Hiscock, and Hellings, 2000, input "A".' ) calc_type = input("Please select the calculation type: ") self.Set_T_Function_Type(calc_type)
""" Galactic confusions noise parameters for 6months, 1yr, 2yr, and 4yr corresponding to array index 0,1,2,3 respectively """
index = 0 index = 1 index = 2 else: A * np.exp(-(f ** a[index]) + (b[index] * f * np.sin(k[index] * f))) * (f ** (-7 / 3)) * (1 + np.tanh(g[index] * (f_k[index] - f))) * (1 / u.Hz) ) # White Dwarf Background Noise
""" Function to load in a file to initialize any detector.
Parameters ---------- detector : object Instance of a detector class
""" print("Is the data:") print(' *Effective Noise Spectral Density - "E"') print(' *Amplitude Spectral Density- "A"') print(' *Effective Strain - "h"') detector.I_type = input("Please enter one of the answers in quotations: ") Load_Data(detector)
else: print("Is the data:") print(' *Effective Noise Spectral Density - "E"') print(' *Amplitude Spectral Density- "A"') print(' *Effective Strain - "h"') detector.I_type = input("Please enter one of the answers in quotations: ") Load_Data(detector)
|