def SNRcalc(self, pulsar, pop): """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....) # 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(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: #### NOTE! HERE I WANT TO CHECK UNITS OF FWHM (ARCMIN???) degfac = math.exp(-2.7726 * offset * offset / (self.fwhm *self.fwhm)) # Dunc's code here uses a ^-2.6 to convert frequencies # don't think I need to do this - I'm using the frequency in call Ttot = self.tsys + self.tskypy(pulsar) # calc dispersion smearing across single channel tdm = self._dmsmear(pulsar) # calculate bhat et al scattering time (inherited from GalacticOps) # in units of ms tscat = go.scatter_bhat(pulsar.dm, pulsar.scindex, self.freq) # Calculate the effective width weff_ms = math.sqrt(pulsar.width_ms()**2 + self.tsamp**2 + tdm**2 + tscat**2) # calculate duty cycle (period is in ms) delt = weff_ms / pulsar.period #print weff_ms, pulsar.period # if pulse is smeared out, return -1.0 if delt > 1.0: #print weff_ms, tscat, pulsar.dm, pulsar.gl, pulsar.gb, pulsar.dtrue return -1 else: return self._SNfac(pulsar, pop.ref_freq, degfac, Ttot) \ * math.sqrt((1.0 -delt)/delt)
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 generate(ngen, surveyList=None, pDistType='lnorm', radialDistType='lfl06', radialDistPars=7.5, electronModel='ne2001', pDistPars=[2.7, -0.34], siDistPars=[-1.6, 0.35], lumDistType='lnorm', lumDistPars=[-1.1, 0.9], zscaleType='exp', zscale=0.33, duty_percent=6., scindex=-3.86, gpsArgs=[None, None], doubleSpec=[None, None], nostdout=False, pattern='gaussian', orbits=False): """ Generate a population of pulsars. Keyword args: ngen -- the number of pulsars to generate (or detect) surveyList -- a list of surveys you want to use to try and detect the pulsars pDistType -- the pulsar period distribution model to use (def=lnorm) radialDistType -- radial distribution model electronModel -- mode to use for Galactic electron distribution pDistPars -- parameters to use for period distribution siDistPars -- parameters to use for spectral index distribution lumDistPars -- parameters to use for luminosity distribution radialDistPars -- parameters for radial distribution zscale -- if using exponential z height, set it here (in kpc) scindex -- spectral index of the scattering model gpsArgs -- add GPS-type spectrum sources doubleSpec -- add double-spectrum type sources nostdout -- (bool) switch off stdout """ pop = Population() # check that the distribution types are supported.... if lumDistType not in ['lnorm', 'pow']: print "Unsupported luminosity distribution: {0}".format(lumDistType) if pDistType not in ['lnorm', 'norm', 'cc97', 'lorimer12']: print "Unsupported period distribution: {0}".format(pDistType) if radialDistType not in ['lfl06', 'yk04', 'isotropic', 'slab', 'disk', 'gauss']: print "Unsupported radial distribution: {0}".format(radialDistType) if electronModel not in ['ne2001', 'lmt85']: print "Unsupported electron model: {0}".format(electronModel) if pattern not in ['gaussian', 'airy']: print "Unsupported gain pattern: {0}".format(pattern) if duty_percent < 0.: print "Unsupported value of duty cycle: {0}".format(duty_percent) # need to use properties in this class so they're get/set-type props pop.pDistType = pDistType pop.radialDistType = radialDistType pop.electronModel = electronModel pop.lumDistType = lumDistType pop.rsigma = radialDistPars pop.pmean, pop.psigma = pDistPars pop.simean, pop.sisigma = siDistPars pop.gpsFrac, pop.gpsA = gpsArgs pop.brokenFrac, pop.brokenSI = doubleSpec if pop.lumDistType == 'lnorm': pop.lummean, pop.lumsigma = \ lumDistPars[0], lumDistPars[1] else: try: pop.lummin, pop.lummax, pop.lumpow = \ lumDistPars[0], lumDistPars[1], lumDistPars[2] except ValueError: raise PopulateException('Not enough lum distn parameters') pop.zscaleType = zscaleType pop.zscale = zscale # store the dict of arguments inside the model. Could be useful. try: argspec = inspect.getargspec(generate) key_values = [(arg, locals()[arg]) for arg in argspec.args] pop.arguments = {key: value for (key, value) in key_values} except SyntaxError: pass if not nostdout: print "\tGenerating pulsars with parameters:" param_string_list = [] for key, value in key_values: s = ": ".join([key, str(value)]) param_string_list.append(s) # join this list of strings, and print it s = "\n\t\t".join(param_string_list) print "\t\t{0}".format(s) # set up progress bar for fun :) prog = ProgressBar(min_value=0, max_value=ngen, width=65, mode='dynamic') # create survey objects here and put them in a list if surveyList is not None: surveys = [Survey(s, pattern) for s in surveyList] # initialise these counters to zero for surv in surveys: surv.ndet = 0 # number detected surv.nout = 0 # number outside survey region surv.nsmear = 0 # number smeared out surv.ntf = 0 # number too faint # surv.gainpat=pattern else: # make an empty list here - makes some code just a little # simpler - can still loop over an empty list (ie zero times) surveys = [] while pop.ndet < ngen: # Declare new pulsar object p = Pulsar() # period, alpha, rho, width distribution calls # Start creating the pulsar! if pop.pDistType == 'lnorm': p.period = dists.drawlnorm(pop.pmean, pop.psigma) elif pop.pDistType == 'norm': p.period = random.gauss(pop.pmean, pop.psigma) elif pop.pDistType == 'cc97': p.period = _cc97() elif pop.pDistType == 'gamma': print "Gamma function not yet supported" sys.exit() elif pop.pDistType == 'lorimer12': p.period = _lorimer2012_msp_periods() if duty_percent > 0.: # use a simple duty cycle for each pulsar # with a log-normal scatter width = (float(duty_percent)/100.) * p.period**0.9 width = math.log10(width) width = dists.drawlnorm(width, 0.3) p.width_degree = width*360./p.period else: # use the model to caculate if beaming p.alpha = _genAlpha() p.rho, p.width_degree = _genRhoWidth(p) if p.width_degree == 0.0 and p.rho == 0.0: continue # is pulsar beaming at us? If not, move on! p.beaming = _beaming(p) if not p.beaming: continue # Spectral index stuff here # suppose it might be nice to be able to have GPS sources # AND double spectra. But for now I assume only have one or # none of these types. if random.random() > pop.gpsFrac: # This will evaluate true when gpsArgs[0] is NoneType # might have to change in future p.gpsFlag = 0 else: p.gpsFlag = 1 p.gpsA = pop.gpsA if random.random() > pop.brokenFrac: p.brokenFlag = 0 else: p.brokenFlag = 1 p.brokenSI = pop.brokenSI p.spindex = random.gauss(pop.simean, pop.sisigma) # get galactic position # first, Galactic distribution models if pop.radialDistType == 'isotropic': # calculate gl and gb randomly p.gb = math.degrees(math.asin(random.random())) if random.random() < 0.5: p.gb = 0.0 - p.gb p.gl = random.random() * 360.0 # use gl and gb to compute galactic coordinates # pretend the pulsar is at distance of 1kpc # not sure why, ask Dunc! p.galCoords = go.lb_to_xyz(p.gl, p.gb, 1.0) elif pop.radialDistType == 'slab': p.galCoords = go.slabDist() p.gl, p.gb = go.xyz_to_lb(p.galCoords) elif pop.radialDistType == 'disk': p.galCoords = go.diskDist() p.gl, p.gb = go.xyz_to_lb(p.galCoords) else: # we want to use exponential z and a radial dist if pop.radialDistType == 'lfl06': p.r0 = go.lfl06() elif pop.radialDistType == 'yk04': p.r0 = go.ykr() elif pop.radialDistType == 'gauss': # guassian of mean 0 # and stdDev given by parameter (kpc) p.r0 = random.gauss(0., pop.rsigma) # then calc xyz,distance, l and b if pop.zscaleType == 'exp': zheight = go._double_sided_exp(zscale) else: zheight = random.gauss(0., zscale) gx, gy = go.calcXY(p.r0) p.galCoords = gx, gy, zheight p.gl, p.gb = go.xyz_to_lb(p.galCoords) p.dtrue = go.calc_dtrue(p.galCoords) # then calc DM using fortran libs if pop.electronModel == 'ne2001': p.dm = go.ne2001_dist_to_dm(p.dtrue, p.gl, p.gb) elif pop.electronModel == 'lmt85': p.dm = go.lmt85_dist_to_dm(p.dtrue, p.gl, p.gb) p.scindex = scindex # then calc scatter time p.t_scatter = go.scatter_bhat(p.dm, p.scindex) if pop.lumDistType == 'lnorm': p.lum_1400 = dists.drawlnorm(pop.lummean, pop.lumsigma) else: p.lum_1400 = dists.powerlaw(pop.lummin, pop.lummax, pop.lumpow) # add in orbital parameters if orbits: orbitalparams.test_1802_2124(p) print p.gb, p.gl # if no surveys, just generate ngen pulsars if surveyList is None: pop.population.append(p) pop.ndet += 1 if not nostdout: prog.increment_amount() print prog, '\r', sys.stdout.flush() # if surveys are given, check if pulsar detected or not # in ANY of the surveys else: # just a flag to increment if pulsar is detected detect_int = 0 for surv in surveys: # do SNR calculation SNR = surv.SNRcalc(p, pop) if SNR > surv.SNRlimit: # SNR is over threshold # increment the flag # and survey ndetected detect_int += 1 surv.ndet += 1 continue elif SNR == -1: # pulse is smeared out surv.nsmear += 1 continue elif SNR == -2: # pulsar is outside survey region surv.nout += 1 continue else: # pulsar is just too faint surv.ntf += 1 continue # add the pulsar to the population pop.population.append(p) # if detected, increment ndet (for whole population) # and redraw the progress bar if detect_int: pop.ndet += 1 if not nostdout: prog.increment_amount() print prog, '\r', sys.stdout.flush() # print info to stdout if not nostdout: print "\n" print " Total pulsars = {0}".format(len(pop.population)) print " Total detected = {0}".format(pop.ndet) # print " Number not beaming = {0}".format(surv.nnb) for surv in surveys: print "\n Results for survey '{0}'".format(surv.surveyName) print " Number detected = {0}".format(surv.ndet) print " Number too faint = {0}".format(surv.ntf) print " Number smeared = {0}".format(surv.nsmear) print " Number outside survey area = {0}".format(surv.nout) return pop
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 generate(ngen, surveyList=None, pDistType='lnorm', radialDistType='lfl06', radialDistPars=7.5, electronModel='ne2001', pDistPars=[2.7, -0.34], siDistPars=[-1.6, 0.35], lumDistType='lnorm', lumDistPars=[-1.1, 0.9], zscaleType='exp', zscale=0.33, duty_percent=6., scindex=-3.86, gpsArgs=[-1, -1], doubleSpec=[-1, -1], nostdout=False, pattern='gaussian', orbits=False, dgf=None, singlepulse=False, giantpulse=False, dither=False, accelsearch=False, jerksearch=False, sig_factor=10.0, bns = False, orbparams = {'m': [1, 5], 'm1': [1.0, 2.4], 'm2': [0.2, 1e9], 'om': [0, 360.], 'inc': [0, 90], 'ec': [0., 1.], 'pod': [1e-3, 1e3]}, brDistType='log_unif'): """ Generate a population of pulsars. Keyword args: ngen -- the number of pulsars to generate (or detect) surveyList -- a list of surveys you want to use to try and detect the pulsars pDistType -- the pulsar period distribution model to use (def=lnorm) radialDistType -- radial distribution model electronModel -- mode to use for Galactic electron distribution pDistPars -- parameters to use for period distribution siDistPars -- parameters to use for spectral index distribution lumDistPars -- parameters to use for luminosity distribution radialDistPars -- parameters for radial distribution zscale -- if using exponential z height, set it here (in kpc) scindex -- spectral index of the scattering model gpsArgs -- add GPS-type spectrum sources doubleSpec -- add double-spectrum type sources nostdout -- (bool) switch off stdout """ pop = Population() # check that the distribution types are supported.... if 'd_g' in (pDistType,lumDistType,zscaleType,radialDistType,brDistType) and dgf is None: print("Provide the distribution generation file") sys.exit() elif dgf != None: try: f = open(dgf, 'rb') except IOError: print("Could not open file {0}.".format(dgf)) sys.exit() dgf_pop_load = cPickle.load(f) f.close() if lumDistType not in ['lnorm', 'pow', 'log_unif', 'd_g', 'log_st']: print("Unsupported luminosity distribution: {0}".format(lumDistType)) if brDistType not in ['log_unif', 'd_g']: print("Unsupported burst rate distribution: {0}".format(brDistType)) if pDistType not in ['lnorm', 'norm', 'cc97', 'lorimer12','unif', 'd_g']: print("Unsupported period distribution: {0}".format(pDistType)) if radialDistType not in ['lfl06', 'yk04', 'isotropic', 'slab', 'disk','unif' ,'gauss','d_g', 'gamma']: print("Unsupported radial distribution: {0}".format(radialDistType)) if electronModel not in ['ne2001', 'lmt85','ymw16']: print("Unsupported electron model: {0}".format(electronModel)) if pattern not in ['gaussian', 'airy']: print("Unsupported gain pattern: {0}".format(pattern)) if duty_percent < 0.: print("Unsupported value of duty cycle: {0}".format(duty_percent)) # need to use properties in this class so they're get/set-type props pop.pDistType = pDistType pop.radialDistType = radialDistType pop.electronModel = electronModel pop.lumDistType = lumDistType pop.rsigma = radialDistPars pop.pmean, pop.psigma = pDistPars pop.simean, pop.sisigma = siDistPars pop.gpsFrac, pop.gpsA = gpsArgs pop.brokenFrac, pop.brokenSI = doubleSpec #Set whether system is BNS: pop.bns = bns pop.orbparams = orbparams if pop.lumDistType == 'lnorm': pop.lummean, pop.lumsigma = \ lumDistPars[0], lumDistPars[1] elif pop.lumDistType == 'log_unif': pop.lumlow, pop.lumhigh = \ lumDistPars[0], lumDistPars[1] elif pop.lumDistType == 'log_st': try: pop.lumlow, pop.lumhigh, pop.lumslope = \ lumDistPars[0], lumDistPars[1], lumDistPars[2] except ValueError: raise PopulateException('Not enough lum distn parameters') elif pop.lumDistType == 'd_g': pass else: try: pop.lummin, pop.lummax, pop.lumpow = \ lumDistPars[0], lumDistPars[1], lumDistPars[2] except ValueError: raise PopulateException('Not enough lum distn parameters') pop.zscaleType = zscaleType pop.zscale = zscale # store the dict of arguments inside the model. Could be useful. try: if sys.version_info[0] > 3: argspec = inspect.getfullargspec(generate) else: argspec = inspect.getargspec(generate) lcl = locals() key_values = [(arg, lcl[arg]) for arg in argspec.args] #key_values = [(arg, locals()['argspec'][arg]) for arg in argspec.args] #pop.arguments = {key: value for (key, value) in key_values} except SyntaxError: pass if not nostdout: print("\tGenerating pulsars with parameters:") param_string_list = [] for key, value in key_values: s = ": ".join([key, str(value)]) param_string_list.append(s) # join this list of strings, and print it s = "\n\t\t".join(param_string_list) print("\t\t{0}".format(s)) # set up progress bar for fun :) prog = ProgressBar(min_value=0, max_value=ngen, width=65, mode='dynamic') # create survey objects here and put them in a list if surveyList is not None: surveys = [Survey(s, pattern) for s in surveyList] # initialise these counters to zero for surv in surveys: surv.ndet = 0 # number detected surv.nout = 0 # number outside survey region surv.nsmear = 0 # number smeared out surv.ntf = 0 # number too faint surv.nbr = 0 #number didn't burst # surv.gainpat=pattern else: # make an empty list here - makes some code just a little # simpler - can still loop over an empty list (ie zero times) surveys = [] while pop.ndet < ngen: # Declare new pulsar object p = Pulsar() # period, alpha, rho, width distribution calls # Start creating the pulsar! if pop.pDistType == 'lnorm': p.period = dists.drawlnorm(pop.pmean, pop.psigma) elif pop.pDistType == 'unif': p.period = dists.uniform(pop.pmean,pop.psigma) elif pop.pDistType == 'norm': p.period = random.gauss(pop.pmean, pop.psigma) elif pop.pDistType == 'cc97': p.period = _cc97() elif pop.pDistType == 'gamma': print("Gamma function not yet supported") sys.exit() elif pop.pDistType == 'd_g': Pbin_num=dists.draw1d(dgf_pop_load['pHist']) Pmin=dgf_pop_load['pBins'][0] Pmax=dgf_pop_load['pBins'][-1] p.period = Pmin + (Pmax-Pmin)*(Pbin_num+random.random())/len(dgf_pop_load['pHist']) # p.period = dg.gen_nos(dgf_pop_load['pHist'],dgf_pop_load['pBins']) elif pop.pDistType == 'lorimer12': p.period = _lorimer2012_msp_periods() if duty_percent > 0.: # use a simple duty cycle for each pulsar # with a log-normal scatter width = (float(duty_percent)/100.) * p.period**0.9 width = math.log10(width) width = dists.drawlnorm(width, 0.3) # following are the numbers for the RRATs # from the period pulse width relation if singlepulse: width = 1000 while width > 100: A1=random.gauss(0.49986684,0.13035004) A2=random.gauss(-0.77626305,0.4222085) width = 10**(A1*math.log10(p.period)+ A2) p.width_degree = width*360./p.period else: # use the model to caculate if beaming p.alpha = _genAlpha() p.rho, p.width_degree = _genRhoWidth(p) if p.width_degree == 0.0 and p.rho == 0.0: continue # is pulsar beaming at us? If not, move on! p.beaming = _beaming(p) if not p.beaming: continue # Spectral index stuff here # suppose it might be nice to be able to have GPS sources # AND double spectra. But for now I assume only have one or # none of these types. if random.random() > pop.gpsFrac: # This will evaluate true when gpsArgs[0] is -1 # might have to change in future p.gpsFlag = 0 else: p.gpsFlag = 1 p.gpsA = pop.gpsA if random.random() > pop.brokenFrac: p.brokenFlag = 0 else: p.brokenFlag = 1 p.brokenSI = pop.brokenSI p.spindex = random.gauss(pop.simean, pop.sisigma) # get galactic position # first, Galactic distribution models if pop.radialDistType == 'isotropic': # calculate gl and gb randomly p.gb = math.degrees(math.asin(random.random())) if random.random() < 0.5: p.gb = 0.0 - p.gb p.gl = random.random() * 360.0 # use gl and gb to compute galactic coordinates # pretend the pulsar is at distance of 1kpc # not sure why, ask Dunc! #Adam comment ,1 Kpc makes no sense here... p.galCoords = go.lb_to_xyz(p.gl, p.gb, 1.0) elif pop.radialDistType == 'slab': p.galCoords = go.slabdist() p.gl, p.gb = go.xyz_to_lb(p.galCoords) elif pop.radialDistType == 'disk': p.galCoords = go.diskdist() p.gl, p.gb = go.xyz_to_lb(p.galCoords) else: if pop.radialDistType == 'lfl06': p.r0 = go.lfl06() elif pop.radialDistType == 'gamma': fit_alpha, fit_loc, fit_beta=1050.312227, -8.12315263841, 0.00756010693478 p.r0 = 8.5*stats.gamma.rvs(fit_alpha, loc=fit_loc, scale=fit_beta, size=1) + 8.5 elif pop.radialDistType == 'd_g': Rbin_num=dists.draw1d(dgf_pop_load['RHist']) Rmin=dgf_pop_load['RBins'][0] Rmax=dgf_pop_load['RBins'][-1] #p.r0 = dists.yet_another_try(dgf_pop_load['RHist'], dgf_pop_load['RBins']) p.r0 = Rmin + (Rmax-Rmin)*(Rbin_num+random.random())/len(dgf_pop_load['RHist']) elif pop.radialDistType == 'yk04': p.r0 = go.ykr() elif pop.radialDistType == 'unif': p.r0 = random.uniform(0,12.3) elif pop.radialDistType == 'gauss': # guassian of mean 0 # and stdDev given by parameter (kpc) p.r0 = random.gauss(0., pop.rsigma) # then calc xyz,distance, l and b if pop.zscaleType == 'exp': zheight = go._double_sided_exp(zscale) elif pop.zscaleType == 'unif': zheight = random.uniform(-1.06489765758, 1.91849552866) elif pop.zscaleType == 'd_g': zbin_num=dists.draw1d(dgf_pop_load['ZHist']) logzmin=dgf_pop_load['ZBins'][0] logzmax=dgf_pop_load['ZBins'][-1] logz = logzmin + (logzmax-logzmin)*(zbin_num+random.random())/len(dgf_pop_load['ZHist']) zheight = logz #zheight = dg.gen_nos(dgf_pop_load['ZHist'],dgf_pop_load['ZBins']) else: zheight = random.gauss(0., zscale) gx, gy = go.calcXY(p.r0) p.galCoords = gx, gy, zheight p.gl, p.gb = go.xyz_to_lb(p.galCoords) p.dtrue = go.calc_dtrue(p.galCoords) if pop.lumDistType == 'lnorm': p.lum_1400 = dists.drawlnorm(pop.lummean, pop.lumsigma) elif pop.lumDistType == 'pow': p.lum_1400 = dists.powerlaw(pop.lummin, pop.lummax, pop.lumpow) elif pop.lumDistType == 'log_st': low_lim=pop.lumlow upr_lim=pop.lumhigh slope = pop.lumslope #-0.10227965 p.lum_1400= 10.0**(low_lim + dists.st_line(slope,(upr_lim-low_lim))) elif pop.lumDistType == 'log_unif': p.lum_1400 = 10.0**dists.uniform(pop.lumlow,pop.lumhigh) elif pop.lumDistType == 'd_g': lbin_num=dists.draw1d(dgf_pop_load['lHist']) lmin=dgf_pop_load['lBins'][0] lmax=dgf_pop_load['lBins'][-1] logl = lmin + (lmax-lmin)*(lbin_num+random.random())/len(dgf_pop_load['lHist']) p.lum_1400 = 10.0**logl p.lum_inj_mu=p.lum_1400 # add in orbital parameters if pop.bns: assert isinstance(orbparams, dict), "Orbital parameter distribution limits should be a dictionary of form {'name of orb_param': [min, max]}" #These represents the range in which NN was trained. #DO NOT GO OUTSIDE THESE BOUNDS!! default_orbparams = {'m': [1, 5], 'm1': [1.0, 2.4], 'm2': [0.2, 1e9], 'om': [0, 360.], 'inc': [0, 90], 'ec': [0., 1.], 'pod': [1e-3, 1e3]} if len(pop.orbparams) == 0: print("Warning: Supplied orbparams dict is empty; Setting ranges to default") pop.orbparams = default_orbparams else: temp_opd = dict(default_orbparams) temp_opd.update(orbparams) pop.orbparams = temp_opd #Draw a value for each of the orbital parameters from a uniform distribution p.m = np.int(np.random.uniform(pop.orbparams['m'][0], pop.orbparams['m'][1], size = 1)) #Should typically fix this to one value! p.m1 = np.random.uniform(pop.orbparams['m1'][0], pop.orbparams['m1'][1], size = 1) p.m2 = np.random.uniform(pop.orbparams['m2'][0], pop.orbparams['m2'][1], size = 1) p.om = np.random.uniform(pop.orbparams['om'][0], pop.orbparams['om'][1], size = 1) p.inc = np.random.uniform(pop.orbparams['inc'][0], pop.orbparams['inc'][1], size = 1) p.ec = np.random.uniform(pop.orbparams['ec'][0], pop.orbparams['ec'][1], size = 1) p.pod = np.random.uniform(pop.orbparams['pod'][0], pop.orbparams['pod'][1], size = 1) #dither the distance if dither: # find flux flux = p.lum_inj_mu/(p.dtrue**2) # dithered distance p.dold = p.dtrue p.dtrue += random.gauss(0.0,0.2*p.dtrue) # new luminosity p.lum_1400 = flux*p.dtrue**2 p.lum_inj_mu=p.lum_1400 # new R and z p.galCoords = go.lb_to_xyz(p.gl, p.gb, p.dtrue) p.r0=np.sqrt(p.galCoords[0]**2 + p.galCoords[1]**2) #define a burst rate if single pulse option is on if singlepulse: if brDistType == 'd_g': brbin_num=dists.draw1d(dgf_pop_load['brHist']) brmin=dgf_pop_load['brBins'][0] brmax=dgf_pop_load['brBins'][-1] p.br = 10**(brmin + (brmax-brmin)*(brbin_num+random.random())/len(dgf_pop_load['brHist'])) else: p.br=_burst() p.lum_sig=sig_factor p.det_nos=0 else: p.br=None p.det_nos=None # then calc DM using fortran libs if pop.electronModel == 'ne2001': p.dm = go.ne2001_dist_to_dm(p.dtrue, p.gl, p.gb) elif pop.electronModel == 'lmt85': p.dm = go.lmt85_dist_to_dm(p.dtrue, p.gl, p.gb) elif pop.electronModel == 'ymw16': p.dm = go.ymw16_dist_to_dm(p.dtrue, p.gl, p.gb) p.scindex = scindex # then calc scatter time p.t_scatter = go.scatter_bhat(p.dm, p.scindex) # if no surveys, just generate ngen pulsars if surveyList is None: pop.population.append(p) pop.ndet += 1 if not nostdout: prog.increment_amount() print(prog, '\r',) sys.stdout.flush() # if surveys are given, check if pulsar detected or not # in ANY of the surveys else: # just a flag to increment if pulsar is detected detect_int = 0 for surv in surveys: # do SNR calculation if singlepulse: #pop_time = int(p.br*surv.tobs) #if pop_time >= 1.0: SNR = surv.SNRcalc(p, pop,rratssearch=True) #else: # SNR = -3 elif accelsearch: SNR = surv.SNRcalc(p, pop, accelsearch=True) elif jerksearch: SNR = surv.SNRcalc(p, pop, jerksearch=True) else: SNR = surv.SNRcalc(p, pop, rratssearch=False) if SNR > surv.SNRlimit: # SNR is over threshold # increment the flag # and survey ndetected detect_int += 1 surv.ndet += 1 continue elif SNR == -1: # pulse is smeared out surv.nsmear += 1 continue elif SNR == -2: # pulsar is outside survey region surv.nout += 1 continue elif SNR == -3: # rrat didn't burst surv.nbr += 1 continue else: # pulsar is just too faint surv.ntf += 1 continue # add the pulsar to the population pop.population.append(p) # if detected, increment ndet (for whole population) # and redraw the progress bar if detect_int: pop.ndet += 1 if not nostdout: prog.increment_amount() print(prog, '\r',) sys.stdout.flush() # print info to stdout if not nostdout: print("\n") print(" Total pulsars = {0}".format(len(pop.population))) print(" Total detected = {0}".format(pop.ndet)) # print " Number not beaming = {0}".format(surv.nnb) for surv in surveys: print("\n Results for survey '{0}'".format(surv.surveyName)) print(" Number detected = {0}".format(surv.ndet)) print(" Number too faint = {0}".format(surv.ntf)) print(" Number smeared = {0}".format(surv.nsmear)) print(" Number outside survey area = {0}".format(surv.nout)) if singlepulse: print(" Number didn't burst = {0}".format(surv.nbr)) return pop
def generate(ngen, surveyList=None, pDistType='lnorm', radialDistType='lfl06', radialDistPars=7.5, electronModel='ne2001', pDistPars=[2.7, -0.34], siDistPars=[-1.6, 0.35], lumDistType='lnorm', lumDistPars=[-1.1, 0.9], zscaleType='exp', zscale=0.33, duty_percent=6., scindex=-3.86, gpsArgs=[None, None], doubleSpec=[None, None], nostdout=False, pattern='gaussian', orbits=False): """ Generate a population of pulsars. Keyword args: ngen -- the number of pulsars to generate (or detect) surveyList -- a list of surveys you want to use to try and detect the pulsars pDistType -- the pulsar period distribution model to use (def=lnorm) radialDistType -- radial distribution model electronModel -- mode to use for Galactic electron distribution pDistPars -- parameters to use for period distribution siDistPars -- parameters to use for spectral index distribution lumDistPars -- parameters to use for luminosity distribution radialDistPars -- parameters for radial distribution zscale -- if using exponential z height, set it here (in kpc) scindex -- spectral index of the scattering model gpsArgs -- add GPS-type spectrum sources doubleSpec -- add double-spectrum type sources nostdout -- (bool) switch off stdout """ pop = Population() # check that the distribution types are supported.... if lumDistType not in ['lnorm', 'pow']: print "Unsupported luminosity distribution: {0}".format(lumDistType) #if pDistType not in ['lnorm', 'norm', 'cc97', 'lorimer12']: # print "Unsupported period distribution: {0}".format(pDistType) if pDistType not in ['lnorm', 'norm', 'cc97', 'lorimer12', 'lorimer15']: print "Unsupported period distribution: {0}".format(pDistType) if radialDistType not in [ 'lfl06', 'yk04', 'isotropic', 'slab', 'disk', 'gauss' ]: print "Unsupported radial distribution: {0}".format(radialDistType) # Edited by Shi Dai, 2017/03/22 if electronModel not in ['ne2001', 'lmt85', 'ymw16']: print "Unsupported electron model: {0}".format(electronModel) if pattern not in ['gaussian', 'airy']: print "Unsupported gain pattern: {0}".format(pattern) if duty_percent < 0.: print "Unsupported value of duty cycle: {0}".format(duty_percent) # need to use properties in this class so they're get/set-type props pop.pDistType = pDistType pop.radialDistType = radialDistType pop.electronModel = electronModel pop.lumDistType = lumDistType pop.rsigma = radialDistPars pop.pmean, pop.psigma = pDistPars pop.simean, pop.sisigma = siDistPars pop.gpsFrac, pop.gpsA = gpsArgs pop.brokenFrac, pop.brokenSI = doubleSpec if pop.lumDistType == 'lnorm': pop.lummean, pop.lumsigma = \ lumDistPars[0], lumDistPars[1] else: try: pop.lummin, pop.lummax, pop.lumpow = \ lumDistPars[0], lumDistPars[1], lumDistPars[2] except ValueError: raise PopulateException('Not enough lum distn parameters') pop.zscaleType = zscaleType pop.zscale = zscale # store the dict of arguments inside the model. Could be useful. try: argspec = inspect.getargspec(generate) key_values = [(arg, locals()[arg]) for arg in argspec.args] pop.arguments = {key: value for (key, value) in key_values} except SyntaxError: pass if not nostdout: print "\tGenerating pulsars with parameters:" param_string_list = [] for key, value in key_values: s = ": ".join([key, str(value)]) param_string_list.append(s) # join this list of strings, and print it s = "\n\t\t".join(param_string_list) print "\t\t{0}".format(s) # set up progress bar for fun :) prog = ProgressBar(min_value=0, max_value=ngen, width=65, mode='dynamic') # create survey objects here and put them in a list if surveyList is not None: surveys = [Survey(s, absolute_importpattern) for s in surveyList] # initialise these counters to zero for surv in surveys: surv.ndet = 0 # number detected surv.nout = 0 # number outside survey region surv.nsmear = 0 # number smeared out surv.ntf = 0 # number too faint # surv.gainpat=pattern else: # make an empty list here - makes some code just a little # simpler - can still loop over an empty list (ie zero times) surveys = [] Lorimer15 = np.loadtxt( '/Users/dai02a/Soft/psrpop/PsrPopPy/lib/python/lorimer15') while pop.ndet < ngen: # Declare new pulsar object p = Pulsar() # period, alpha, rho, width distribution calls # Start creating the pulsar! if pop.pDistType == 'lnorm': p.period = dists.drawlnorm(pop.pmean, pop.psigma) elif pop.pDistType == 'norm': p.period = random.gauss(pop.pmean, pop.psigma) elif pop.pDistType == 'cc97': p.period = _cc97() elif pop.pDistType == 'gamma': print "Gamma function not yet supported" sys.exit() elif pop.pDistType == 'lorimer12': p.period = _lorimer2012_msp_periods() elif pop.pDistType == 'lorimer15': #p.period = _lorimer2015_msp_periods() p.period = Lorimer15[pop.ndet] if duty_percent > 0.: # use a simple duty cycle for each pulsar # with a log-normal scatter width = (float(duty_percent) / 100.) * p.period**0.9 width = math.log10(width) width = dists.drawlnorm(width, 0.3) p.width_degree = width * 360. / p.period else: # use the model to caculate if beaming p.alpha = _genAlpha() p.rho, p.width_degree = _genRhoWidth(p) if p.width_degree == 0.0 and p.rho == 0.0: continue # is pulsar beaming at us? If not, move on! p.beaming = _beaming(p) if not p.beaming: continue # Spectral index stuff here # suppose it might be nice to be able to have GPS sources # AND double spectra. But for now I assume only have one or # none of these types. if random.random() > pop.gpsFrac: # This will evaluate true when gpsArgs[0] is NoneType # might have to change in future p.gpsFlag = 0 else: p.gpsFlag = 1 p.gpsA = pop.gpsA if random.random() > pop.brokenFrac: p.brokenFlag = 0 else: p.brokenFlag = 1 p.brokenSI = pop.brokenSI p.spindex = random.gauss(pop.simean, pop.sisigma) # get galactic position # first, Galactic distribution models if pop.radialDistType == 'isotropic': # calculate gl and gb randomly p.gb = math.degrees(math.asin(random.random())) if random.random() < 0.5: p.gb = 0.0 - p.gb p.gl = random.random() * 360.0 # use gl and gb to compute galactic coordinates # pretend the pulsar is at distance of 1kpc # not sure why, ask Dunc! p.galCoords = go.lb_to_xyz(p.gl, p.gb, 1.0) elif pop.radialDistType == 'slab': p.galCoords = go.slabDist() p.gl, p.gb = go.xyz_to_lb(p.galCoords) elif pop.radialDistType == 'disk': p.galCoords = go.diskDist() p.gl, p.gb = go.xyz_to_lb(p.galCoords) else: # we want to use exponential z and a radial dist if pop.radialDistType == 'lfl06': p.r0 = go.lfl06() elif pop.radialDistType == 'yk04': p.r0 = go.ykr() elif pop.radialDistType == 'gauss': # guassian of mean 0 # and stdDev given by parameter (kpc) p.r0 = random.gauss(0., pop.rsigma) # then calc xyz,distance, l and b if pop.zscaleType == 'exp': zheight = go._double_sided_exp(zscale) else: zheight = random.gauss(0., zscale) gx, gy = go.calcXY(p.r0) p.galCoords = gx, gy, zheight p.gl, p.gb = go.xyz_to_lb(p.galCoords) p.dtrue = go.calc_dtrue(p.galCoords) # Edited by Shi Dai, 2017/03/22 # then calc DM using fortran libs #if pop.electronModel == 'ne2001': # p.dm = go.ne2001_dist_to_dm(p.dtrue, p.gl, p.gb) #elif pop.electronModel == 'lmt85': # p.dm = go.lmt85_dist_to_dm(p.dtrue, p.gl, p.gb) #p.scindex = scindex ## then calc scatter time #p.t_scatter = go.scatter_bhat(p.dm, p.scindex) if pop.electronModel == 'ne2001': p.dm = go.ne2001_dist_to_dm(p.dtrue, p.gl, p.gb) p.t_scatter = go.scatter_bhat(p.dm, p.scindex) elif pop.electronModel == 'lmt85': p.dm = go.lmt85_dist_to_dm(p.dtrue, p.gl, p.gb) p.t_scatter = go.scatter_bhat(p.dm, p.scindex) elif pop.electronModel == 'ymw16': p.dm, p.t_scatter = go.ymw16_dist_to_dm(p.dtrue, p.gl, p.gb) p.scindex = scindex # then calc scatter time if pop.lumDistType == 'lnorm': p.lum_1400 = dists.drawlnorm(pop.lummean, pop.lumsigma) else: p.lum_1400 = dists.powerlaw(pop.lummin, pop.lummax, pop.lumpow) # add in orbital parameters if orbits: orbitalparams.test_1802_2124(p) print p.gb, p.gl # if no surveys, just generate ngen pulsars if surveyList is None: calc_delta(p) pop.population.append(p) pop.ndet += 1 if not nostdout: prog.increment_amount() print prog, '\r', sys.stdout.flush() # if surveys are given, check if pulsar detected or not # in ANY of the surveys else: # just a flag to increment if pulsar is detected detect_int = 0 for surv in surveys: # do SNR calculation SNR = surv.SNRcalc(p, pop) if SNR > surv.SNRlimit: # SNR is over threshold # increment the flag # and survey ndetected detect_int += 1 surv.ndet += 1 continue elif SNR == -1: # pulse is smeared out surv.nsmear += 1 continue elif SNR == -2: # pulsar is outside survey region surv.nout += 1 continue else: # pulsar is just too faint surv.ntf += 1 continue # added by Shi Dai, 2017/02/07 #p.snr = SNR # add the pulsar to the population pop.population.append(p) # if detected, increment ndet (for whole population) # and redraw the progress bar if detect_int: pop.ndet += 1 if not nostdout: prog.increment_amount() print prog, '\r', sys.stdout.flush() # print info to stdout if not nostdout: print "\n" print " Total pulsars = {0}".format(len(pop.population)) print " Total detected = {0}".format(pop.ndet) # print " Number not beaming = {0}".format(surv.nnb) for surv in surveys: print "\n Results for survey '{0}'".format(surv.surveyName) print " Number detected = {0}".format(surv.ndet) print " Number too faint = {0}".format(surv.ntf) print " Number smeared = {0}".format(surv.nsmear) print " Number outside survey area = {0}".format(surv.nout) return pop
def generate(ngen, surveyList=None, age_max=1.0E9, pDistPars=[0.3, 0.15], pDistType = 'norm' , # P distirbution for MSPs (Lorimer et al. 2015) bFieldPars=[12.65, 0.55], birthVPars=[0.0, 180.], siDistPars=[-1.6, 0.35], alignModel='orthogonal', lumDistType='fk06', lumDistPars=[-1.5, 0.5], alignTime=None, spinModel='fk06', beamModel='tm98', birthVModel='gaussian', electronModel='ne2001', braking_index=0, zscale=0.05, duty=5., scindex=-3.86, widthModel=None, nodeathline=False, efficiencycut=None, nostdout=False, nospiralarms=False, keepdead=False, dosurveyList = None, makepop=False): pop = Population() # set the parameters in the population object pop.pmean, pop.psigma = pDistPars pop.bmean, pop.bsigma = bFieldPars if lumDistType == 'pow': try: pop.lummin, pop.lummax, pop.lumpow = \ lumDistPars[0], lumDistPars[1], lumDistPars[2] except ValueError: raise EvolveException('Not enough lum distn parameters for "pow"') elif lumDistType == 'fk06': pop.lumPar1, pop.lumPar2 = lumDistPars[0], lumDistPars[1] if len(lumDistPars) == 3: pop.lumPar3 = lumDistPars[2] else: pop.lumPar3 = 0.18 else: pop.lumPar1, pop.lumPar2 = lumDistPars pop.simean, pop.sisigma = siDistPars pop.birthvmean, pop.birthvsigma = birthVPars pop.alignModel = alignModel pop.alignTime = alignTime pop.spinModel = spinModel pop.beamModel = beamModel pop.birthVModel = birthVModel pop.electronModel = electronModel pop.braking_index = braking_index pop.deathline = not nodeathline pop.nospiralarms = nospiralarms pop.zscale = zscale if widthModel == 'kj07': print "\tLoading KJ07 models...." kj_p_vals, kj_pdot_vals, kj_dists = beammodels.load_kj2007_models() print "\tDone\n" if not nostdout: print "\tGenerating evolved pulsars with parameters:" print "\t\tngen = {0}".format(ngen) print "\t\tUsing electron distn model {0}".format( pop.electronModel) print "\n\t\tPeriod mean, sigma = {0}, {1}".format( pop.pmean, pop.psigma) print "\t\tLuminosity mean, sigma = {0}, {1}".format( pop.lummean, pop.lumsigma) print "\t\tSpectral index mean, sigma = {0}, {1}".format( pop.simean, pop.sisigma) print "\t\tGalactic z scale height = {0} kpc".format( pop.zscale) if widthModel is None: print "\t\tWidth {0}% ".format(duty) else: print "\t\tUsing Karastergiou & Johnston beam width model" # set up progress bar for fun :) prog = ProgressBar(min_value=0, max_value=ngen, width=65, mode='dynamic') # create survey objects here and put them in a list if surveyList is not None: surveys = [Survey(s) for s in surveyList] else: # make an empty list here - makes some code just a little # simpler - can still loop over an empty list (ie zero times) surveys = [] # initialise these counters to zero for surv in surveys: surv.ndet = 0 # number detected surv.nout = 0 # number outside survey region surv.nsmear = 0 # number smeared out surv.ntf = 0 # number too faint # create dosurvey objects here and put them in a list if dosurveyList is not None: dosurveys = [Survey(s) for s in dosurveyList] # initialise these counters to zero for surv in dosurveys: surv.ndet = 0 # number detected surv.nout = 0 # number outside survey region surv.nsmear = 0 # number smeared out surv.ntf = 0 # number too faint # surv.gainpat=pattern else: # make an empty list here - makes some code just a little # simpler - can still loop over an empty list (ie zero times) dosurveys = [] # this is the nitty-gritty loop for generating the pulsars ntot = 0 #total number of pulsars generated by the while loop (including dead, and the ones not beaming towards us) n_alive = 0 #total number of pulsars which are alive (beaming + not beaming towards Earth) n_alive_beam = 0 #total number of alive pulsars beaming towards Earth while pop.ndet < ngen: pulsar = Pulsar() # initial age for pulsar pulsar.age = random.random() * age_max # initial period pulsar.p0 = -1. while (pulsar.p0 <= 0.): if(pDistType == 'norm'): pulsar.p0 = random.gauss(pop.pmean, pop.psigma) elif(pDistType == 'drl15'): L_p0 = np.random.lognormal(mean = pop.pmean, sigma = pop.psigma) pulsar.p0 = np.exp(L_p0)/1000 # period in seconds # initial magnetic field (in Gauss) pulsar.bfield_init = 10**random.gauss(pop.bmean, pop.bsigma) # aligment angle alignpulsar(pulsar, pop) # braking index if pop.braking_index == 0: pulsar.braking_index = 2.5 + 0.5 * random.random() else: pulsar.braking_index = float(pop.braking_index) # apply relevant spin down model pulsar.dead = False # pulsar should start alive! if pop.spinModel == 'fk06': spindown_fk06(pulsar) # apply deathline if relevant if pop.deathline: bhattacharya_deathperiod_92(pulsar) elif pop.spinModel == 'tk01': spindown_tk01(pulsar) # apply deathline if relevant if pop.deathline: bhattacharya_deathperiod_92(pulsar) elif pop.spinModel == 'cs06': # contopoulos and spitkovsky spindown_cs06(pulsar, pop) # if period > 10 seconds, just try a new one if pulsar.period > 10000.0 or pulsar.period < 1.5: continue # cut on pdot too - this doesn't help if pulsar.pdot > 1.e-11 or pulsar.pdot < 1.e-21: continue # define pulse width (default = 6% = 18 degrees) if widthModel is None: width = (float(duty)/100.) * pulsar.period**0.9 width = math.log10(width) width = dists.drawlnorm(width, 0.3) elif widthModel == 'kj07': # Karastergiou & Johnston beam model # find closest p, pdot in the kj lists logp = math.log10(pulsar.period) logpdot = math.log10(pulsar.pdot) p_idx = (np.abs(kj_p_vals - logp)).argmin() pd_idx = (np.abs(kj_pdot_vals - logpdot)).argmin() # pick a width from relevant model dist = kj_dists[p_idx][pd_idx][2] width = np.random.choice(dist) """ width = beammodels.kj2007_width(pulsar) no_width = 0 while width == 0.0: no_width+=1 width = beammodels.kj2007_width(pulsar) # if we get to five, then try a new pulsar if no_width == 5: no_width = 0 continue """ else: print "Undefined width model!" sys.exit() # print width # print pulsar.period, width, pulsar.pdot if width == 0.0: # some kj2007 models make many zero-width sources. Skip! continue pulsar.width_degree = 360. * width / pulsar.period # incrementing the total number of physical pulsars generated by # the model after the unphysical ones are removed ntot += 1 # plough on - only if the pulsar isn't dead! if not pulsar.dead or keepdead: n_alive+=1 # is the pulsar beaming? pulsar_beaming(pulsar, pop.beamModel) # if not, then skip onto next pulsar if not pulsar.beaming: continue # position of the pulsar galacticDistribute(pulsar, pop) # birthvelocity birthVelocity(pulsar, pop) # model the xyz velocity go.vxyz(pulsar) # luminosity if lumDistType == 'fk06': luminosity_fk06(pulsar, alpha=pop.lumPar1, beta=pop.lumPar2, gamma=pop.lumPar3) elif lumDistType == 'lnorm': pulsar.lum_1400 = dists.drawlnorm(pop.lumPar1, pop.lumPar2) elif lumDistType == 'pow': pulsar.lum_1400 = dists.powerlaw(pop.lummin, pop.lummax, pop.lumpow) else: # something's wrong! raise EvolveException('Invalid luminosity distn selected') # apply efficiency cutoff if efficiencycut is not None: if pulsar.efficiency() > efficiencycut: pulsar.dead = True if not keepdead: continue # spectral index pulsar.spindex = random.gauss(pop.simean, pop.sisigma) # calculate galactic coords and distance pulsar.gl, pulsar.gb = go.xyz_to_lb(pulsar.galCoords) pulsar.dtrue = go.calc_dtrue(pulsar.galCoords) # then calc DM using fortran libs if pop.electronModel == 'ne2001': pulsar.dm = go.ne2001_dist_to_dm(pulsar.dtrue, pulsar.gl, pulsar.gb) elif pop.electronModel == 'lmt85': pulsar.dm = go.lmt85_dist_to_dm(pulsar.dtrue, pulsar.gl, pulsar.gb) else: raise EvolveException('Invalid electron dist model selected') pulsar.scindex = scindex pulsar.t_scatter = go.scatter_bhat(pulsar.dm, pulsar.scindex) # if surveys are given, check if pulsar detected or not # in ANY of the surveys if surveyList is not None: detect_int = 0 # just a flag if pulsar is detected for surv in surveys: SNR = surv.SNRcalc(pulsar, pop) if SNR > surv.SNRlimit: # SNR is over threshold # increment the flag # and survey ndetected detect_int += 1 surv.ndet += 1 continue elif SNR == -1: # pulse is smeared out surv.nsmear += 1 continue elif SNR == -2: # pulsar is outside survey region surv.nout += 1 continue else: # pulsar is just too faint surv.ntf += 1 continue # if detected, increment ndet (for whole population) # and redraw the progress bar if detect_int: pop.ndet += 1 # update the counter if not nostdout: prog.increment_amount() print prog, '\r', sys.stdout.flush() else: # no survey list, just add the pulsar to population, # and increment number of pulsars pop.ndet += 1 # update the counter if not nostdout: prog.increment_amount() print prog, '\r', sys.stdout.flush() # increment the total number of alive pulsars beaming towards Earth # should be different from n_alive (as the loop was stopped if pulsar # was found to not beam towards Earth) # is incremented only when pulsar was beaming towards us n_alive_beam += 1 # pulsar isn't dead, and makepop True, add the pulsar to population! if makepop == True: pop.population.append(pulsar) # if dosurveys are given, check if pulsar detected or not # in each of the surveys or any of the survey # just a flag to increment if pulsar is detected if dosurveyList is not None: for surv in dosurveys: SNR = surv.SNRcalc(pulsar, pop) if SNR > surv.SNRlimit: # SNR is over threshold # increment survey ndetected surv.ndet += 1 continue elif SNR == -1: # pulse is smeared out surv.nsmear += 1 continue elif SNR == -2: # pulsar is outside survey region surv.nout += 1 continue else: # pulsar is just too faint surv.ntf += 1 continue else: # pulsar is dead. If no survey list, # just increment number of pulsars if surveyList is None: pop.ndet = ntot # update the counter if not nostdout: prog.increment_amount() print prog, '\r', sys.stdout.flush() if not nostdout: print "\n\n" # print " Total pulsars = {0}".format(len(pop.population)) # print " Total detected = {0}".format(pop.ndet) print " Total pulsars generated = {0}".format(ntot) print " Total living pulsars = {0}".format(n_alive) print " Total living pulsars beaming towards Earth = {0}".format(n_alive_beam) if surveyList is not None: print " Total detected by all surveys = {0}".format(pop.ndet) for surv in surveys: print "\n Results for survey '{0}'".format(surv.surveyName) print " Number detected = {0}".format(surv.ndet) print " Number too faint = {0}".format(surv.ntf) print " Number smeared = {0}".format(surv.nsmear) print " Number outside survey area = {0}".format(surv.nout) for surv in dosurveys: print "\n Dosurvey Results for survey '{0}'".format(surv.surveyName) print " Number detected = {0}".format(surv.ndet) print " Number too faint = {0}".format(surv.ntf) print " Number smeared = {0}".format(surv.nsmear) print " Number outside survey area = {0}".format(surv.nout) dosurvey_result = [] for surv in dosurveys: dosurvey_result.append([surv.surveyName, surv.ndet, surv.ntf, surv.nsmear, surv.nout]) # save list of arguments into the pop #try: # argspec = inspect.getargspec(generate) # key_values = [(arg, locals()[arg]) for arg in argspec.args] # pop.arguments = {key: value for (key, value) in key_values} #except SyntaxError: # pass return pop, dosurvey_result
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 generate(ngen, surveyList=None, age_max=1.0E9, pDistPars=[.3, .15], bFieldPars=[12.65, 0.55], birthVPars=[0.0, 180.], siDistPars=[-1.6, 0.35], alignModel='orthogonal', lumDistType='fk06', lumDistPars=[-1.5, 0.5], alignTime=None, spinModel='fk06', beamModel='tm98', birthVModel='gaussian', electronModel='ne2001', braking_index=0, zscale=0.05, duty=5., scindex=-3.86, widthModel=None, nodeathline=False, efficiencycut=None, nostdout=False, nospiralarms=False, keepdead=False): pop = Population() # set the parameters in the population object pop.pmean, pop.psigma = pDistPars pop.bmean, pop.bsigma = bFieldPars if lumDistType == 'pow': try: pop.lummin, pop.lummax, pop.lumpow = \ lumDistPars[0], lumDistPars[1], lumDistPars[2] except ValueError: raise EvolveException('Not enough lum distn parameters for "pow"') elif lumDistType == 'fk06': pop.lumPar1, pop.lumPar2 = lumDistPars[0], lumDistPars[1] if len(lumDistPars) == 3: pop.lumPar3 = lumDistPars[2] else: pop.lumPar3 = 0.18 else: pop.lumPar1, pop.lumPar2 = lumDistPars pop.simean, pop.sisigma = siDistPars pop.birthvmean, pop.birthvsigma = birthVPars pop.alignModel = alignModel pop.alignTime = alignTime pop.spinModel = spinModel pop.beamModel = beamModel pop.birthVModel = birthVModel pop.electronModel = electronModel pop.braking_index = braking_index pop.deathline = not nodeathline pop.nospiralarms = nospiralarms pop.zscale = zscale if widthModel == 'kj07': print "\tLoading KJ07 models...." kj_p_vals, kj_pdot_vals, kj_dists = beammodels.load_kj2007_models() print "\tDone\n" if not nostdout: print "\tGenerating evolved pulsars with parameters:" print "\t\tngen = {0}".format(ngen) print "\t\tUsing electron distn model {0}".format( pop.electronModel) print "\n\t\tPeriod mean, sigma = {0}, {1}".format( pop.pmean, pop.psigma) print "\t\tLuminosity mean, sigma = {0}, {1}".format( pop.lummean, pop.lumsigma) print "\t\tSpectral index mean, sigma = {0}, {1}".format( pop.simean, pop.sisigma) print "\t\tGalactic z scale height = {0} kpc".format( pop.zscale) if widthModel is None: print "\t\tWidth {0}% ".format(duty) else: print "\t\tUsing Karastergiou & Johnston beam width model" # set up progress bar for fun :) prog = ProgressBar(min_value=0, max_value=ngen, width=65, mode='dynamic') # create survey objects here and put them in a list if surveyList is not None: surveys = [Survey(s) for s in surveyList] else: # make an empty list here - makes some code just a little # simpler - can still loop over an empty list (ie zero times) surveys = [] # initialise these counters to zero for surv in surveys: surv.ndet = 0 # number detected surv.nout = 0 # number outside survey region surv.nsmear = 0 # number smeared out surv.ntf = 0 # number too faint # this is the nitty-gritty loop for generating the pulsars while pop.ndet < ngen: pulsar = Pulsar() # initial age for pulsar pulsar.age = random.random() * age_max # initial period pulsar.p0 = -1. while pulsar.p0 <= 0.: pulsar.p0 = random.gauss(pop.pmean, pop.psigma) # initial magnetic field (in Gauss) pulsar.bfield_init = 10**random.gauss(pop.bmean, pop.bsigma) # aligment angle alignpulsar(pulsar, pop) # braking index if pop.braking_index == 0: pulsar.braking_index = 2.5 + 0.5 * random.random() else: pulsar.braking_index = float(pop.braking_index) # apply relevant spin down model pulsar.dead = False # pulsar should start alive! if pop.spinModel == 'fk06': spindown_fk06(pulsar) # apply deathline if relevant if pop.deathline: bhattacharya_deathperiod_92(pulsar) elif pop.spinModel == 'cs06': # contopoulos and spitkovsky spindown_cs06(pulsar, pop) # if period > 10 seconds, just try a new one if pulsar.period > 10000.0 or pulsar.period < 10.: continue # cut on pdot too - this doesn't help if pulsar.pdot > 1.e-11 or pulsar.pdot < 1.e-18: continue # define pulse width (default = 6% = 18 degrees) if widthModel is None: width = (float(duty)/100.) * pulsar.period**0.9 width = math.log10(width) width = dists.drawlnorm(width, 0.3) elif widthModel == 'kj07': # Karastergiou & Johnston beam model # find closest p, pdot in the kj lists logp = math.log10(pulsar.period) logpdot = math.log10(pulsar.pdot) p_idx = (np.abs(kj_p_vals - logp)).argmin() pd_idx = (np.abs(kj_pdot_vals - logpdot)).argmin() # pick a width from relevant model dist = kj_dists[p_idx][pd_idx][2] width = np.random.choice(dist) """ width = beammodels.kj2007_width(pulsar) no_width = 0 while width == 0.0: no_width+=1 width = beammodels.kj2007_width(pulsar) # if we get to five, then try a new pulsar if no_width == 5: no_width = 0 continue """ else: print "Undefined width model!" sys.exit() # print width # print pulsar.period, width, pulsar.pdot if width == 0.0: # some kj2007 models make many zero-width sources. Skip! continue pulsar.width_degree = 360. * width / pulsar.period # plough on - only if the pulsar isn't dead! if not pulsar.dead or keepdead: # is the pulsar beaming? pulsar_beaming(pulsar, pop.beamModel) # if not, then skip onto next pulsar if not pulsar.beaming: continue # position of the pulsar galacticDistribute(pulsar, pop) # birthvelocity birthVelocity(pulsar, pop) # model the xyz velocity go.vxyz(pulsar) # luminosity if lumDistType == 'fk06': luminosity_fk06(pulsar, alpha=pop.lumPar1, beta=pop.lumPar2, gamma=pop.lumPar3) elif lumDistType == 'lnorm': pulsar.lum_1400 = dists.drawlnorm(pop.lumPar1, pop.lumPar2) elif lumDistType == 'pow': pulsar.lum_1400 = dists.powerlaw(pop.lummin, pop.lummax, pop.lumpow) else: # something's wrong! raise EvolveException('Invalid luminosity distn selected') # apply efficiency cutoff if efficiencycut is not None: if pulsar.efficiency() > efficiencycut: pulsar.dead = True if not keepdead: continue # spectral index pulsar.spindex = random.gauss(pop.simean, pop.sisigma) # calculate galactic coords and distance pulsar.gl, pulsar.gb = go.xyz_to_lb(pulsar.galCoords) pulsar.dtrue = go.calc_dtrue(pulsar.galCoords) # then calc DM using fortran libs if pop.electronModel == 'ne2001': pulsar.dm = go.ne2001_dist_to_dm(pulsar.dtrue, pulsar.gl, pulsar.gb) elif pop.electronModel == 'lmt85': pulsar.dm = go.lmt85_dist_to_dm(pulsar.dtrue, pulsar.gl, pulsar.gb) else: raise EvolveException('Invalid electron dist model selected') pulsar.scindex = scindex pulsar.t_scatter = go.scatter_bhat(pulsar.dm, pulsar.scindex) # if surveys are given, check if pulsar detected or not # in ANY of the surveys if surveyList is not None: detect_int = 0 # just a flag if pulsar is detected for surv in surveys: SNR = surv.SNRcalc(pulsar, pop) if SNR > surv.SNRlimit: # SNR is over threshold # increment the flag # and survey ndetected detect_int += 1 surv.ndet += 1 continue elif SNR == -1: # pulse is smeared out surv.nsmear += 1 continue elif SNR == -2: # pulsar is outside survey region surv.nout += 1 continue else: # pulsar is just too faint surv.ntf += 1 continue # if detected, increment ndet (for whole population) # and redraw the progress bar if detect_int: pop.ndet += 1 # update the counter if not nostdout: prog.increment_amount() print prog, '\r', sys.stdout.flush() else: # no survey list, just add the pulsar to population, # and increment number of pulsars pop.ndet += 1 # update the counter if not nostdout: prog.increment_amount() print prog, '\r', sys.stdout.flush() # pulsar isn't dead, add to population! pop.population.append(pulsar) else: # pulsar is dead. If no survey list, # just increment number of pulsars if surveyList is None: pop.ndet += 1 # update the counter if not nostdout: prog.increment_amount() print prog, '\r', sys.stdout.flush() if not nostdout: print "\n\n" print " Total pulsars = {0}".format(len(pop.population)) print " Total detected = {0}".format(pop.ndet) for surv in surveys: print "\n Results for survey '{0}'".format(surv.surveyName) print " Number detected = {0}".format(surv.ndet) print " Number too faint = {0}".format(surv.ntf) print " Number smeared = {0}".format(surv.nsmear) print " Number outside survey area = {0}".format(surv.nout) # save list of arguments into the pop try: argspec = inspect.getargspec(generate) key_values = [(arg, locals()[arg]) for arg in argspec.args] pop.arguments = {key: value for (key, value) in key_values} except SyntaxError: pass return pop
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