def scint(self, psr, snr): """ Add scintillation effects and modify the pulsar's S/N""" # calculate the scintillation strength (commonly "u") # first, calculate scint BW, assume Kolmogorov, C=1.16 if hasattr(psr, 't_scatter'): tscat = go.scale_bhat(psr.t_scatter, self.freq, psr.scindex) else: tscat = go.scatter_bhat(psr.dm, psr.scindex, self.freq) # convert to seconds tscat /= 1000. scint_bandwidth = 1.16 / 2.0 / math.pi / tscat # BW in Hz scint_bandwidth /= 1.0E6 # convert to MHz (self.freq is in MHz) scint_strength = math.sqrt(self.freq / scint_bandwidth) if scint_strength < 1.0: # weak scintillation # modulation index u_term = math.pow(scint_strength, 1.666666) mod_indx = math.sqrt(u_term) else: # strong scintillation # m^2 = m_riss^2 + m_diss^2 + m_riss * m_diss # e.g. Lorimer and Kramer ~eq 4.44 m_riss = math.pow(scint_strength, -0.33333) # lorimer & kramer eq 4.44 kappa = 0.15 # taking this as avrg for now # calculate scintillation timescale scint_ts, scint_bw = go.ne2001_scint_time_bw( psr.dtrue, psr.gl, psr.gb, self.freq) # calc n_t and n_f if scint_ts is None: n_t = 1. else: n_t = self._calc_n_t(kappa, scint_ts) if scint_bw is None: n_f = 1. else: n_f = self._calc_n_f(kappa, scint_bw) # finally calc m_diss m_diss = 1. / math.sqrt(n_t * n_f) m_tot_sq = m_diss * m_diss + m_riss * m_riss + m_riss * m_diss # modulation index for strong scintillation mod_indx = math.sqrt(m_tot_sq) return self._modulate_flux_scint(snr, mod_indx)
def scint(self, psr, snr): """ Add scintillation effects and modify the pulsar's S/N""" # calculate the scintillation strength (commonly "u") # first, calculate scint BW, assume Kolmogorov, C=1.16 if hasattr(psr, "t_scatter"): tscat = go.scale_bhat(psr.t_scatter, self.freq, psr.scindex) else: tscat = go.scatter_bhat(psr.dm, psr.scindex, self.freq) # convert to seconds tscat /= 1000.0 scint_bandwidth = 1.16 / 2.0 / math.pi / tscat # BW in Hz scint_bandwidth /= 1.0e6 # convert to MHz (self.freq is in MHz) scint_strength = math.sqrt(self.freq / scint_bandwidth) if scint_strength < 1.0: # weak scintillation # modulation index u_term = math.pow(scint_strength, 1.666666) mod_indx = math.sqrt(u_term) else: # strong scintillation # m^2 = m_riss^2 + m_diss^2 + m_riss * m_diss # e.g. Lorimer and Kramer ~eq 4.44 m_riss = math.pow(scint_strength, -0.33333) # lorimer & kramer eq 4.44 kappa = 0.15 # taking this as avrg for now # calculate scintillation timescale scint_ts, scint_bw = go.ne2001_scint_time_bw(psr.dtrue, psr.gl, psr.gb, self.freq) # calc n_t and n_f if scint_ts is None: n_t = 1.0 else: n_t = self._calc_n_t(kappa, scint_ts) if scint_bw is None: n_f = 1.0 else: n_f = self._calc_n_f(kappa, scint_bw) # finally calc m_diss m_diss = 1.0 / math.sqrt(n_t * n_f) m_tot_sq = m_diss * m_diss + m_riss * m_riss + m_riss * m_diss # modulation index for strong scintillation mod_indx = math.sqrt(m_tot_sq) return self._modulate_flux_scint(snr, mod_indx)
def calc_delta(pulsar): bw_chan = 3.0 # MHz freq = 1374.0 # MHz # calc dispersion smearing across single channel tdm = 8.3E6 * pulsar.dm * bw_chan / math.pow(freq, 3.0) # ms tsamp = 0.25 # ms # calculate bhat et al scattering time (inherited from GalacticOps) # in units of ms if hasattr(pulsar, 't_scatter'): tscat = go.scale_bhat(pulsar.t_scatter, freq, pulsar.scindex) else: tscat = go.scatter_bhat(pulsar.dm, pulsar.scindex, freq) # Calculate the effective width width_ms = pulsar.width_degree * pulsar.period / 360.0 weff_ms = math.sqrt(width_ms**2 + tsamp**2 + tdm**2 + tscat**2) # calculate duty cycle (period is in ms) pulsar.delta = weff_ms / pulsar.period
def SNRcalc(self, pulsar, pop, accelsearch=False, jerksearch=False, rratssearch=False): """Calculate the S/N ratio of a given pulsar in the survey""" # if not in region, S/N = 0 # if we have a list of pointings, use this bit of code # haven't tested yet, but presumably a lot slower # (loops over the list of pointings....) if rratssearch: if pulsar.br is None: print "Population doesn't have a burst rate" print "Use populate with --singlepulse" sys.exit() pulsar.pop_time = np.random.poisson(pulsar.br * self.tobs) if pulsar.dead: return 0. # otherwise check if pulsar is in entire region if self.inRegion(pulsar): # If pointing list is provided, check how close nearest # pointing is if self.pointingslist is not None: # convert offset from degree to arcmin offset = self.inPointing_new(pulsar) * 60.0 else: # calculate offset as a random offset within FWHM/2 offset = self.fwhm * math.sqrt(random.random()) / 2.0 else: return -2 if rratssearch == True and pulsar.pop_time < 1.0: return -3 # Get degfac depending on self.gainpat if self.gainpat == 'airy': conv = math.pi / (60 * 180.) # Conversion arcmins -> radians eff_diam = 3.0e8 / (self.freq * self.fwhm * conv * 1.0e6 ) # Also MHz -> Hz a = eff_diam / 2. # Effective radius of telescope lamda = 3.0e8 / (self.freq * 1.0e6) # Obs. wavelength kasin = (2 * math.pi * a / lamda) * np.sin(offset * conv) degfac = 4 * (j1(kasin) / kasin)**2 else: degfac = math.exp(-2.7726 * offset * offset / (self.fwhm * self.fwhm)) # calc dispersion smearing across single channel tdm = self._dmsmear(pulsar) # calculate bhat et al scattering time (inherited from GalacticOps) # in units of ms if hasattr(pulsar, 't_scatter'): tscat = go.scale_bhat(pulsar.t_scatter, self.freq, pulsar.scindex) else: tscat = go.scatter_bhat(pulsar.dm, pulsar.scindex, self.freq) # Calculate the effective width width_ms = pulsar.width_degree * pulsar.period / 360.0 weff_ms = math.sqrt(width_ms**2 + self.tsamp**2 + tdm**2 + tscat**2) # calculate duty cycle (period is in ms) delta = weff_ms / pulsar.period # if pulse is smeared out, return -1.0 if delta > 1.0: #and pulsar.pop_time >= 1.0: # print width_ms, self.tsamp, tdm, tscat return -1 # radiometer signal to noise if rratssearch == False: sig_to_noise = rad.calcSNR(self.calcflux(pulsar, pop.ref_freq), self.beta, self.tsys, self.tskypy(pulsar), self.gain, self.npol, self.tobs, self.bw, delta) else: #find number of times the pulse will pop up! #pop_time=int(pulsar.br*self.tobs) if pulsar.pop_time >= 1.0: pulse_snr = np.zeros(pulsar.pop_time) fluxes = np.zeros(pulsar.pop_time) lums = [] mu = math.log10(pulsar.lum_inj_mu) sig = mu / pulsar.lum_sig # Draw from luminosity dist. for burst_times in range(pulsar.pop_time): pulsar.lum_1400 = dist.drawlnorm(mu, sig) lums.append(pulsar.lum_1400) flux = self.calcflux(pulsar, pop.ref_freq) fluxes[burst_times] = flux pulse_snr[burst_times] = rad.single_pulse_snr( self.npol, self.bw, weff_ms * 1e-3, (self.tsys + self.tskypy(pulsar)), self.gain, flux, self.beta) pulsar.lum_1400 = np.max(lums) sig_to_noise = np.max(pulse_snr) if sig_to_noise >= self.SNRlimit: pulsar.det_pulses = fluxes[pulse_snr >= self.SNRlimit] pulsar.det_nos = len(pulsar.det_pulses) else: pulsar.det_pulses = None pulsar.det_nos = 0 else: return -3 # account for aperture array, if needed if self.AA and sig_to_noise > 0.0: sig_to_noise *= self._AA_factor(pulsar) # account for binary motion if pulsar.is_binary: # print "the pulsar is a binary!" if jerksearch: print "jerk" gamma = degradation.gamma3(pulsar, self.tobs, 1) elif accelsearch: print "accel" gamma = degaadation.gamma2(pulsar, self.tobs, 1) else: print "norm" gamma = degradation.gamma1(pulsar, self.tobs, 1) print "gamma harm1 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 2) print "gamma harm2 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 3) print "gamma harm3 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 4) print "gamma harm4 = ", gamma # return the S/N accounting for beam offset return sig_to_noise * degfac
def SNRcalc(self, pulsar, pop, accelsearch=False, jerksearch=False): """Calculate the S/N ratio of a given pulsar in the survey""" # if not in region, S/N = 0 # if we have a list of pointings, use this bit of code # haven't tested yet, but presumably a lot slower # (loops over the list of pointings....) if pulsar.dead: return 0. # otherwise check if pulsar is in entire region if self.inRegion(pulsar): # If pointing list is provided, check how close nearest # pointing is if self.pointingslist is not None: # convert offset from degree to arcmin offset = self.inPointing_new(pulsar) * 60.0 else: # calculate offset as a random offset within FWHM/2 offset = self.fwhm * math.sqrt(random.random()) / 2.0 else: return -2 # Get degfac depending on self.gainpat if self.gainpat == 'airy': conv = math.pi / (60 * 180.) # Conversion arcmins -> radians eff_diam = 3.0e8 / (self.freq * self.fwhm * conv * 1.0e6 ) # Also MHz -> Hz a = eff_diam / 2. # Effective radius of telescope lamda = 3.0e8 / (self.freq * 1.0e6) # Obs. wavelength kasin = (2 * math.pi * a / lamda) * np.sin(offset * conv) degfac = 4 * (j1(kasin) / kasin)**2 else: degfac = math.exp(-2.7726 * offset * offset / (self.fwhm * self.fwhm)) # calc dispersion smearing across single channel tdm = self._dmsmear(pulsar) # calculate bhat et al scattering time (inherited from GalacticOps) # in units of ms if hasattr(pulsar, 't_scatter'): tscat = go.scale_bhat(pulsar.t_scatter, self.freq, pulsar.scindex) else: tscat = go.scatter_bhat(pulsar.dm, pulsar.scindex, self.freq) # Calculate the effective width width_ms = pulsar.width_degree * pulsar.period / 360.0 weff_ms = math.sqrt(width_ms**2 + self.tsamp**2 + tdm**2 + tscat**2) # calculate duty cycle (period is in ms) delta = weff_ms / pulsar.period # if pulse is smeared out, return -1.0 if delta > 1.0: #print width_ms, self.tsamp, tdm, tscat return -1 #radiometer signal to noise sig_to_noise = rad.calcSNR(self.calcflux(pulsar, pop.ref_freq), self.beta, self.tsys, self.tskypy(pulsar), self.gain, self.npol, self.tobs, self.bw, delta) # account for aperture array, if needed if self.AA: sig_to_noise *= self._AA_factor(pulsar) # account for binary motion if pulsar.is_binary: #print "the pulsar is a binary!" if jerksearch: print "jerk" gamma = degradation.gamma3(pulsar, self.tobs, 1) elif accelsearch: print "accel" gamma = degradation.gamma2(pulsar, self.tobs, 1) else: print "norm" gamma = degradation.gamma1(pulsar, self.tobs, 1) print "gamma harm1 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 2) print "gamma harm2 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 3) print "gamma harm3 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 4) print "gamma harm4 = ", gamma # return the S/N accounting for beam offset return sig_to_noise * degfac
def SNRcalc(self, pulsar, pop, accelsearch=False, jerksearch=False): """Calculate the S/N ratio of a given pulsar in the survey""" # if not in region, S/N = 0 # if we have a list of pointings, use this bit of code # haven't tested yet, but presumably a lot slower # (loops over the list of pointings....) if pulsar.dead: return 0. # otherwise check if pulsar is in entire region if self.inRegion(pulsar): # If pointing list is provided, check how close nearest # pointing is if self.pointingslist is not None: # convert offset from degree to arcmin offset = self.inPointing_new(pulsar) * 60.0 else: # calculate offset as a random offset within FWHM/2 offset = self.fwhm * math.sqrt(random.random()) / 2.0 else: return -2 # Get degfac depending on self.gainpat if self.gainpat == 'airy': conv = math.pi/(60*180.) # Conversion arcmins -> radians eff_diam = 3.0e8/(self.freq*self.fwhm*conv*1.0e6) # Also MHz -> Hz a = eff_diam/2. # Effective radius of telescope lamda = 3.0e8/(self.freq*1.0e6) # Obs. wavelength kasin = (2*math.pi*a/lamda)*np.sin(offset*conv) degfac = 4*(j1(kasin)/kasin)**2 else: degfac = math.exp( -2.7726 * offset * offset / (self.fwhm * self.fwhm)) # calc dispersion smearing across single channel tdm = self._dmsmear(pulsar) # calculate bhat et al scattering time (inherited from GalacticOps) # in units of ms if hasattr(pulsar, 't_scatter'): tscat = go.scale_bhat(pulsar.t_scatter, self.freq, pulsar.scindex) else: tscat = go.scatter_bhat(pulsar.dm, pulsar.scindex, self.freq) # Calculate the effective width width_ms = pulsar.width_degree * pulsar.period / 360.0 weff_ms = math.sqrt(width_ms**2 + self.tsamp**2 + tdm**2 + tscat**2) # calculate duty cycle (period is in ms) delta = weff_ms / pulsar.period # if pulse is smeared out, return -1.0 if delta > 1.0: # print width_ms, self.tsamp, tdm, tscat return -1 # radiometer signal to noise sig_to_noise = rad.calcSNR(self.calcflux(pulsar, pop.ref_freq), self.beta, self.tsys, self.tskypy(pulsar), self.gain, self.npol, self.tobs, self.bw, delta) # account for aperture array, if needed if self.AA: sig_to_noise *= self._AA_factor(pulsar) # account for binary motion if pulsar.is_binary: # print "the pulsar is a binary!" if jerksearch: print "jerk" gamma = degradation.gamma3(pulsar, self.tobs, 1) elif accelsearch: print "accel" gamma = degradation.gamma2(pulsar, self.tobs, 1) else: print "norm" gamma = degradation.gamma1(pulsar, self.tobs, 1) print "gamma harm1 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 2) print "gamma harm2 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 3) print "gamma harm3 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 4) print "gamma harm4 = ", gamma # return the S/N accounting for beam offset return sig_to_noise * degfac
def SNRcalc(self, pulsar, pop, accelsearch=False, jerksearch=False, rratssearch=False, giantpulse=False): """Calculate the S/N ratio of a given pulsar in the survey""" # if not in region, S/N = 0 # if we have a list of pointings, use this bit of code # haven't tested yet, but presumably a lot slower # (loops over the list of pointings....) if rratssearch: if pulsar.br is None: print("Population doesn't have a burst rate") print("Use populate with --singlepulse") sys.exit() pulsar.pop_time = np.random.poisson(pulsar.br * self.tobs) if pulsar.dead: return 0. # otherwise check if pulsar is in entire region if self.inRegion(pulsar): # If pointing list is provided, check how close nearest # pointing is if self.pointingslist is not None: # convert offset from degree to arcmin offset = self.inPointing_new(pulsar) * 60.0 else: # calculate offset as a random offset within FWHM/2 offset = self.fwhm * math.sqrt(random.random()) / 2.0 else: return -2 if rratssearch == True and pulsar.pop_time < 1.0: return -3 # Get degfac depending on self.gainpat if self.gainpat == 'airy': conv = math.pi / (60 * 180.) # Conversion arcmins -> radians eff_diam = 3.0e8 / (self.freq * self.fwhm * conv * 1.0e6 ) # Also MHz -> Hz a = eff_diam / 2. # Effective radius of telescope lamda = 3.0e8 / (self.freq * 1.0e6) # Obs. wavelength kasin = (2 * math.pi * a / lamda) * np.sin(offset * conv) degfac = 4 * (j1(kasin) / kasin)**2 else: degfac = math.exp(-2.7726 * offset * offset / (self.fwhm * self.fwhm)) # calc dispersion smearing across single channel tdm = self._dmsmear(pulsar) # calculate bhat et al scattering time (inherited from GalacticOps) # in units of ms if hasattr(pulsar, 't_scatter'): tscat = go.scale_bhat(pulsar.t_scatter, self.freq, pulsar.scindex) else: tscat = go.scatter_bhat(pulsar.dm, pulsar.scindex, self.freq) # Calculate the effective width width_ms = pulsar.width_degree * pulsar.period / 360.0 weff_ms = math.sqrt(width_ms**2 + self.tsamp**2 + tdm**2 + tscat**2) # calculate duty cycle (period is in ms) delta = weff_ms / pulsar.period # if pulse is smeared out, return -1.0 #don't really get smearing with giant pulse or rrat search right? if (delta > 1.0) & (not giantpulse) & ( not rratssearch): #and pulsar.pop_time >= 1.0: # print width_ms, self.tsamp, tdm, tscat return -1 # radiometer signal to noise if (rratssearch == False) & (giantpulse == False): if self.surveyName == 'CHIME': #print('CHIME') sig_to_noise = rad.single_pulse_snr( self.npol, self.bw, weff_ms * 1e3, (self.tsys + self.tskypy(pulsar)), self.gain, self.calcflux(pulsar, pop.ref_freq), self.beta) else: sig_to_noise = rad.calcSNR(self.calcflux(pulsar, pop.ref_freq), self.beta, self.tsys, self.tskypy(pulsar), self.gain, self.npol, self.tobs, self.bw, delta) elif giantpulse: #check how many times it burst #use 0.001 as the fraction of periods to emit a GP for now - place holder #values here currently taken from Popov et al 2007 if pulsar.period < 100: pulses = 0.001 * (self.tobs / (1e-3 * pulsar.period)) GP_flux = np.zeros(int(pulses)) GP_lum = np.zeros(int(pulses)) GP_snr = np.zeros(int(pulses)) if pulses > 1: for i in range(int(pulses)): #parameters needs to be changed for later #working in units of Jy us fluence = dist.power_law_dual(1000, 11000, 2000, 200, -3.2, -1.9) #use pulse width of 55ms for now #the flux calculated here is for the Crab, so we'll have to use a different distance measure. average_flux = fluence / (55) #2 kpc away to get luminosity at 1400Mhz from 1200 Mhz, hard code this for now lum_1400 = average_flux * (2**2) * ( 1400 / 1200)**pulsar.spindex #scale down/up to survey frequency flux = self._GPFlux(pulsar, lum_1400, 1400) GP_lum[i] = lum_1400 GP_flux[i] = flux GP_snr[i] = rad.single_pulse_snr( self.npol, self.bw, weff_ms * 1e3, (self.tsys + self.tskypy(pulsar)), self.gain, flux, self.beta) pulsar.lum_1400 = np.max(GP_lum) sig_to_noise = np.max(GP_snr) if sig_to_noise >= self.SNRlimit: pulsar.det_pulses = GP_flux[GP_snr >= self.SNRlimit] pulsar.det_nos = len(pulsar.det_pulses) else: pulsar.det_pulses = None pulsar.det_nos = 0 else: return -3 else: return -3 elif rratssearch: #find number of times the pulse will pop up! #pop_time=int(pulsar.br*self.tobs) #Need to optimise this code... if pulsar.pop_time >= 1.0: pulse_snr = np.zeros(pulsar.pop_time) fluxes = np.zeros(pulsar.pop_time) lums = [] mu = math.log10(pulsar.lum_inj_mu) sig = mu / pulsar.lum_sig # Draw from luminosity dist. #ADAM EDIT it would be nice to make this run on multiple cores... chime has so much observation time for burst_times in range(pulsar.pop_time): pulsar.lum_1400 = dist.drawlnorm(mu, sig) lums.append(pulsar.lum_1400) flux = self.calcflux(pulsar, pop.ref_freq) fluxes[burst_times] = flux #ADAM EDIT: width changed to seconds instead of miliseconds??? pulse_snr[burst_times] = rad.single_pulse_snr( self.npol, self.bw, weff_ms * 1e3, (self.tsys + self.tskypy(pulsar)), self.gain, flux, self.beta) pulsar.lum_1400 = np.max(lums) sig_to_noise = np.max(pulse_snr) if sig_to_noise >= self.SNRlimit: pulsar.det_pulses = fluxes[pulse_snr >= self.SNRlimit] pulsar.det_nos = len(pulsar.det_pulses) else: pulsar.det_pulses = None pulsar.det_nos = 0 else: return -3 # account for aperture array, if needed if self.AA and sig_to_noise > 0.0: sig_to_noise *= self._AA_factor(pulsar) # account for binary motion if pulsar.is_binary: # print "the pulsar is a binary!" if jerksearch: #print "jerk" gamma = degradation.gamma3(pulsar, self.tobs, 1) elif accelsearch: #print "accel" #Adam fix bug gamma = degradation.gamma2(pulsar, self.tobs, 1) else: #print "norm" gamma = degradation.gamma1(pulsar, self.tobs, 1) #print "gamma harm1 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 2) #print "gamma harm2 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 3) #print "gamma harm3 = ", gamma gamma = degradation.gamma1(pulsar, self.tobs, 4) #print "gamma harm4 = ", gamma # return the S/N accounting for beam offset return sig_to_noise * degfac