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 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