#!/usr/bin/env python class srcList: #arguments are: #sources (string, filename of LAT source list fits file in catalog format) #ft1 (string, filename of event file for which the xml will be used, only used to extract ROI info) #out (string, name of output xml file, defaults to mymodel.xml) def __init__(self,sources,ft1,out='mymodel.xml',DRversion=3): if not fileCheck(sources): #check that file exists print("Error: %s not found."%sources) return if fileCheck(out): print('Warning: %s already exists, file will be overwritten if you proceed with makeModel.'%out) if DRversion not in [1,2,3]: print('Invalid choice, %i, of data release version. Must be 1, 2, or 3.\nWill produce file assuming DR3') self.DR=3 self.VarLim=24.725 else: self.DR=DRversion self.VarLim=(24.725 if DRversion==3 else 21.666 if DRversion==2 else 18.475) self.srcs=sources self.out=out self.roi=getPos(ft1) #define a quick print function to make sure everything looks irght def Print(self): print('Source list file: ',self.srcs) print('Output file name: ',self.out) print('Selecting %s degrees around (ra,dec)=(%s,%s)'%(self.roi[2],self.ra,self.dec)) #make the xml file #arguments are: #GDfile (str) -- optional, location and name of Galactic diffuse model to use #GDname (str) -- optional, name of Galactic diffuse component to use in xml model #ISOfile (str) -- optional, location and name of Isotropic diffuse template to use #ISOname (str) -- optional, name of Isotropic diffuse component to use in xml model #normsOnly (bool) -- optional, flag to only set normalizations parameters free #extDir (str) -- optional, directory with extended source templates #radLim (float) -- optional, radius in degrees from center of ROI beyond which source parameters are fixed #maxRad (float) -- optional, absolute maximum radius beyond which sources are fixed, this may be necessary when doing binned analysis and a variable source beyond radLim would be set free but this source is beyond the boundaries of the square region used for the binned likelihood #ExtraRad (float) -- optional, radius beyond ROI radius in event file out to which sources will be included with fixed parameters, defaul tof 10 is good for analyses starting around 100 MeV, but for higher energy fits this can be decreased #sigFree (float) -- optional, average significance, using FITS catalog file, below which source parameters are fixed, even if within radLim. This corresponds to TS_value if using the XML catalog file. #varFree (float) -- optional, variability index above which source parameters are free, if beyond radLim and/or below sigFree only the normalization parameter is set free #psForce (bool) -- optional, flag to force extended sources to be point sources #E2CAT (bool) -- optional, flag to force use catalog names for extended sources (only matters if using catalog FITS file) #makeRegion (bool) -- optional, flag to also generate ds9 region file #GIndexFree (bool) -- optional, the Galactic diffuse is given a power-law spectral shape but the by default the index is frozen, setting this flag to True allows that to be free for additional freedom in diffuse fit #oldNames (bool) -- optional, flag to use the naming convention from make1FGLxml.py and make2FGLxml.py with a leading underscore and no spaces def makeModel(self,GDfile="$(FERMI_DIR)/refdata/fermi/galdiffuse/gll_iem_v07.fits",GDname='gll_iem_v07',ISOfile="$(FERMI_DIR)/refdata/fermi/galdiffuse/iso_P8R3_SOURCE_V3_v1.txt",ISOname='iso_P8R3_SOURCE_V3_v1',normsOnly=False,extDir='',radLim=-1,maxRad=None,ExtraRad=10,sigFree=5,varFree=True,psForce=False,E2CAT=False,makeRegion=True,GIndexFree=False,wd='',oldNames=False): self.radLim=(self.roi[2] if radLim<=0 else radLim) self.maxRad=(self.radLim if maxRad==None else maxRad) if self.maxRad=sL.roi[2] or dist>=sL.maxRad: Sources[sname]['free']=False for p in specPars: specOut.appendChild(parameter_element("0","%s"%str(p.getAttribute('name')),"%s"%str(p.getAttribute('max')),"%s"%str(p.getAttribute('min')),"%s"%str(p.getAttribute('scale')),"%s"%str(p.getAttribute('value')))) elif dist>sL.radLim: if sL.var and varIdx>=varValue: Sources[sname]['free']=True for p in specPars: freeFlag=("1" if p.getAttribute('name')==spec[0].getAttribute('normPar') else "0") specOut.appendChild(parameter_element("%s"%freeFlag,"%s"%str(p.getAttribute('name')),"%s"%str(p.getAttribute('max')),"%s"%str(p.getAttribute('min')),"%s"%str(p.getAttribute('scale')),"%s"%str(p.getAttribute('value')))) else: Sources[sname]['free']=False for p in specPars: specOut.appendChild(parameter_element("0","%s"%str(p.getAttribute('name')),"%s"%str(p.getAttribute('max')),"%s"%str(p.getAttribute('min')),"%s"%str(p.getAttribute('scale')),"%s"%str(p.getAttribute('value')))) elif float(src.getAttribute('TS_value'))>=sL.sig: Sources[sname]['free']=True #specOut.setAttribute('apply_edisp',ed) for p in specPars: freeFlag=("1" if p.getAttribute('name')==spec[0].getAttribute('normPar') or (not sL.nO and p.getAttribute('free')=="1") else "0") specOut.appendChild(parameter_element("%s"%freeFlag,"%s"%str(p.getAttribute('name')),"%s"%str(p.getAttribute('max')),"%s"%str(p.getAttribute('min')),"%s"%str(p.getAttribute('scale')),"%s"%str(p.getAttribute('value')))) else: if sL.var and varIdx>=varValue: Sources[sname]['free']=True #specOut.setAttribute('apply_edisp',ed) for p in specPars: freeFlag=("1" if p.getAttribute('name')==spec[0].getAttribute('normPar') else "0") specOut.appendChild(parameter_element("%s"%freeFlag,"%s"%str(p.getAttribute('name')),"%s"%str(p.getAttribute('max')),"%s"%str(p.getAttribute('min')),"%s"%str(p.getAttribute('scale')),"%s"%str(p.getAttribute('value')))) else: Sources[sname]['free']=False #specOut.setAttribute('apply_edisp','false') for p in specPars: specOut.appendChild(parameter_element("0","%s"%str(p.getAttribute('name')),"%s"%str(p.getAttribute('max')),"%s"%str(p.getAttribute('min')),"%s"%str(p.getAttribute('scale')),"%s"%str(p.getAttribute('value')))) if Ext: spatial=src.getElementsByTagName('spatialModel') spatType=spatial[0].getAttribute('type') spatPars=spatial[0].getElementsByTagName('parameter') if str(spatType)=='SpatialMap': spatialOut.setAttribute('type','SpatialMap') spatialOut.setAttribute('map_based_integral','true') efile=sL.extD+spatial[0].getAttribute('file').split('/')[-1] spatialOut.setAttribute('file',efile) print('Extended source %s in ROI, make sure %s is the correct path to the extended template.'%(sname,efile)) else:#have to do above to get correct extended source template file localtion spatialOut.setAttribute('type',str(spatType)) for p in spatPars:#for radial disks and gaussians, can just do the following spatialOut.appendChild(parameter_element("0","%s"%str(p.getAttribute('name')),"%s"%str(p.getAttribute('max')),"%s"%str(p.getAttribute('min')),"%s"%str(p.getAttribute('scale')),"%s"%str(p.getAttribute('value')))) print('Extended source %s in ROI, with %s spatial model.'%(sname,str(spatType))) srcOut.setAttribute('type','DiffuseSource') extSrcNum+=1 #print('Extended source %s in ROI, make sure %s is the correct path to the extended template.'%(sname,efile)) else: spatialOut.setAttribute('type','SkyDirFunction') spatialOut.appendChild(parameter_element("0","RA","360.0","-360.0","1.0","%.4f"%srcRA)) spatialOut.appendChild(parameter_element("0","DEC","360.0","-360.0","1.0","%.4f"%srcDEC)) srcOut.setAttribute('type','PointSource') ptSrcNum+=1 srcOut.appendChild(specOut) srcOut.appendChild(spatialOut) outputXml.documentElement.appendChild(srcOut) gal=outputXml.createElement('source') gal.setAttribute('name',GDn) gal.setAttribute('type','DiffuseSource') galspec=outputXml.createElement('spectrum') galspec.setAttribute('type','PowerLaw') #galspec.setAttribute('apply_edisp','false') galspec.appendChild(parameter_element("1","Prefactor","10","0","1","1")) if sL.GIF: galspec.appendChild(parameter_element("1","Index","1","-1","1","0")) else: galspec.appendChild(parameter_element("0","Index","1","-1","1","0")) galspec.appendChild(parameter_element("0","Scale","1e6","2e1","1","100")) galspatial=outputXml.createElement('spatialModel') galspatial.setAttribute('type','MapCubeFunction') galspatial.setAttribute('file',GD) galspatial.appendChild(parameter_element("0","Normalization","1e3","1e-3","1","1")) gal.appendChild(galspec) gal.appendChild(galspatial) outputXml.documentElement.appendChild(gal) iso=outputXml.createElement('source') iso.setAttribute('name',ISOn) iso.setAttribute('type','DiffuseSource') isospec=outputXml.createElement('spectrum') isospec.setAttribute('type','FileFunction') isospec.setAttribute('file',ISO) isospec.setAttribute('apply_edisp','false') isospec.appendChild(parameter_element("1","Normalization","10","0.01","1","1")) isospatial=outputXml.createElement('spatialModel') isospatial.setAttribute('type','ConstantValue') isospatial.appendChild(parameter_element("0","Value","10","0","1","1")) iso.appendChild(isospec) iso.appendChild(isospatial) outputXml.documentElement.appendChild(iso) xmlStr=outputXml.toprettyxml(' ').splitlines(True) outStr=filter(lambda xmlStr: len(xmlStr) and not xmlStr.isspace(),xmlStr) outfile=open(sL.out,'w') outfile.write(''.join(outStr)) outfile.close() if not sL.psF: print('Added %i point sources and %i extended sources'%(ptSrcNum,extSrcNum)) if extSrcNum>0: print('If using unbinned likelihood you will need to rerun gtdiffrsp for the extended sources or rerun the makeModel function with optional argument psForce=True') else: print('Added %i point sources, note that any extended sources in ROI were modeled as point sources becaue psForce option was set to True'%ptSrcNum) if sL.reg: BuildRegion(sL,Sources) return #function to cycle through the source list and add point source entries def addSrcsFITS(sL,GD,GDn,ISO,ISOn,oldNames): model=open(sL.out,'w') #open file in write mode, overwrites other files of same name catfile=pyfits.open(sL.srcs) #open source list file and access necessary fields, requires LAT source catalog definitions and names data=catfile['LAT_Point_Source_Catalog'].data extendedinfo=catfile['ExtendedSources'].data extName=extendedinfo.field('Source_Name') extFile=extendedinfo.field('Spatial_Filename') extFunc=extendedinfo.field('Spatial_Function') extSize=extendedinfo.field('Model_SemiMajor') extRa=extendedinfo.field('RAJ2000') extDec=extendedinfo.field('DEJ2000') name=data.field('Source_Name') Sigvals=data.field('Signif_Avg') VarIdx=data.field('Variability_Index') EName=data.field('Extended_Source_Name') ra=data.field('RAJ2000') dec=data.field('DEJ2000') plflux=data.field('PL_Flux_Density') lpflux=data.field('LP_Flux_Density') coflux=data.field('PLEC_Flux_Density') pivot=data.field('Pivot_Energy') plIndex=data.field('PL_Index') lpIndex=data.field('LP_Index') lpbeta=data.field('LP_beta') if sL.DR==3: plecIndex=data.field('PLEC_IndexS') plecexpFact=data.field('PLEC_ExpfactorS') plecexpIndex=data.field('PLEC_Exp_Index') else: plecIndex=data.field('PLEC_Index') plecexpFact=data.field('PLEC_Expfactor') plecexpIndex=data.field('PLEC_Exp_Index') spectype=data.field('SpectrumType') model.write('\n') model.write('\n') model.write('\n\n') step=(sL.roi[2]+sL.ER)/5. #divide ROI radius plus ExtraRadius degrees into 5 steps for ordering of sources i=1 radii=[] ptSrcNum=0 extSrcNum=0 Sources={}#dictionary for sources, useful for creating region file later. while i<6: if i*step<=sL.roi[2]+sL.ER: radii+=[step*i] else: radii+=[sL.roi[2]+sL.ER] #just in case of rounding errors i+=1 for x in radii: if x==sL.roi[2]+sL.ER: model.write('\n\n' %(x-step,x)) else: model.write('\n\n' %(x-step,x)) for n,plf,lpf,cof,r,d,p,pli,lpi,lpb,pleci,plecef,plecei,t,TS,En,vi in zip(name,plflux,lpflux,coflux,ra,dec,pivot,plIndex,lpIndex,lpbeta,plecIndex,plecexpFact,plecexpIndex,spectype,Sigvals,EName,VarIdx): E=(True if n[-1]=='e' else False) dist=angsep(sL.roi[0],sL.roi[1],r,d) #check that source is within ROI radius + 10 degress of ROI center if r==sL.roi[0] and d==sL.roi[1]: dist=0.0 if (dist=x-step) or (x==sL.roi[2]+10. and dist==x): if E and not sL.psF: Sources[En]={'ra':r,'dec':d,'stype':t,'E':E} extSrcNum+=1 Name='\n' %(dist,En) else: if E and not sL.E2C:#even if forcing all to point sources, use extended name except if E2CAT flag is set Sources[En]={'ra':r,'dec':d,'stype':t,'E':E} Name='\n' %(dist,En) else: Sources[n]={'ra':r,'dec':d,'stype':t,'E':E} if oldNames: srcname='_' for N in n.split(' '): srcname+=N Name='\n' %(dist,srcname) else: Name='\n' %(dist,n) ptSrcNum+=1 if t=='PowerLaw': #uncomment out the two lines immediately following later spec,free=PLspec(sL,plf,pli,p,dist,TS,vi) elif t=='LogParabola': fixAll=(True if n=='4FGL J0534.5+2201i' else False) spec,free=LPspec(sL,lpf,lpi,p,lpb,dist,TS,vi,fixAll) else: if sL.DR==3: spec,free=CO4spec(sL,cof,pleci,p,plecef,plecei,dist,TS,vi) else: spec,free=CO2spec(sL,cof,pleci,p,plecef,plecei,dist,TS,vi) if E and not sL.E2C: Sources[En]['free']=free else: Sources[n]['free']=free if E and not sL.psF: efile=None efunc=None eSize=None eR=None eD=None for EXTNAME,EXTFILE,EXTFUNC,EXTSIZE,EXTRA,EXTDEC in zip(extName,extFile,extFunc,extSize,extRa,extDec): if En==EXTNAME: efunc=EXTFUNC efunc=('RadialGaussian' if efunc=='RadialGauss' else efunc) if efunc=='SpatialMap': efile=sL.extD+EXTFILE else: eSize=(EXTSIZE/(-2*log(0.32))**0.5 if efunc=='RadialGaussian' else EXTSIZE) eR=EXTRA eD=EXTDEC if efunc=='SpatialMap': if efile==None: print('could not find a match for',En,'in the list:') print(extName) efile='' skydir='\t\n'%(efile) print('Extended source %s in ROI, make sure %s is the correct path to the extended template.'%(En,efile)) skydir+='\t\t\n' skydir+='\t\n' else: skydir='\t\n'%efunc skydir+='\t\n'%eR skydir+='\t\n'%eD if efunc=='RadialDisk': skydir+='\t\n'%eSize else: skydir+='\t\n'%eSize skydir+='\t\n' print('Extended source %s in ROI with %s spatial model.'%(En,efunc)) else: skydir='\t\n' skydir+='\t\t\n' %r skydir+='\t\t\n' %d skydir+='\t\n' skydir+='' (src,)=(Name+spec+skydir,) ptsrc=pS(src).getElementsByTagName('source')[0] ptsrc.writexml(model) model.write('\n') catfile.close() #close file if not sL.psF: print('Added %i point sources and %i extended sources'%(ptSrcNum,extSrcNum)) if extSrcNum>0: print('If using unbinned likelihood you will need to rerun gtdiffrsp for the extended sources or rerun the makeModel function with optional argument psForce=True') else: print('Added %i point sources, note that any extended sources in ROI were modeled as point sources becaue psForce option was set to True'%ptSrcNum) #add galactic diffuse with PL spectrum, fix index to zero for general use, those who want it to be free can unfreeze parameter manually model.write('\n\n') Name='\n\n' %GDn spec='\t\n' spec+='\t\t\n' if sL.GIF: spec+='\t\t\n' else: spec+='\t\t\n' spec+='\t\t\n' spec+='\t\n' skydir='\t\n' %GD skydir+='\t\t\n' skydir+='\t\n' skydir+='' (src,)=(Name+spec+skydir,) galdiff=pS(src).getElementsByTagName('source')[0] galdiff.writexml(model) model.write('\n') Name='\n' %ISOn spec='\t\n' %ISO spec+='\t\t\n' spec+='\t\n' skydir='\t\n' skydir+='\t\t\n' skydir+='\t\n' skydir+='' (src,)=(Name+spec+skydir,) iso=pS(src).getElementsByTagName('source')[0] iso.writexml(model) model.write('\n') model.close() if sL.reg: BuildRegion(sL,Sources) return def BuildRegion(sL,Sources): myreg=open(sL.regFile,'w')#note that this will overwrite previous region files of the same name myreg.write('# Region File format: DS9 version ?')#I don't actually know, but I think it's one of the later ones, need to verify myreg.write('\n# Created by make3FGLxml.py') myreg.write('\nglobal font="roman 10 normal" move =0') for k in Sources.keys(): src=Sources[k] #get color based on if the source is free or not color=('green' if src['free'] else 'magenta') if src['E']:#if the source is extended, always have the point be a "big" box myreg.write('\nJ2000;point(%.3f,%.3f) # point = box 18 color = %s text={%s}'%(src['ra'],src['dec'],color,k)) else:#if the source is a point source, choose the point type based on spectral model ptype=('cross' if (src['stype']=='PLSuperExpCutoff' or src['stype']=='PLSuperExpCutoff2') else 'diamond' if src['stype']=='LogParabola' else 'circle') myreg.write('\nJ2000;point(%.3f,%.3f) # point = %s 15 color = %s text={%s}'%(src['ra'],src['dec'],ptype,color,k)) myreg.close() return def PLspec(sL,f,i,p,dist,TS,vi): varValue=sL.VarLim fscale=int(floor(log10(f))) spec='\t\n' spec+='\t\n' %dist if dist>sL.roi[2]: #if beyond ROI, shouldn't attempt to fit parameters spec+='\t\n' spec+='\t\t\n' %(fscale,f/10**fscale) spec+='\t\t\n' %i free=False elif(dist>sL.radLim): if dist>sL.maxRad: spec+='\t\n'%sL.radLim spec+='\t\t\n' %(fscale,f/10**fscale) free=False elif vi=0 else 2.)#some pulsars with index1 < 0 assuming standard convention, means rising counts spectrum at low E, odd if(dist>sL.roi[2]): #if beyond ROI, shouldn't attempt to fit parameters spec+='\t\n' free=False spec+='\t\t\n' %(fscale,f/10**fscale) spec+='\t\t\n' %i spec+='\t\t\n'%(a*1000.) elif(dist>sL.radLim): if dist>sL.maxRad: spec+='\t\n'%sL.radLim spec+='\t\t\n' %(fscale,f/10**fscale) free=False elif visL.roi[2]): #if beyond ROI, shouldn't attempt to fit parameters spec+='\t\n' free=False spec+='\t\t\n' %(fscale,f/10**fscale) spec+='\t\t\n' %i spec+='\t\t\n'%(a*10.) elif(dist>sL.radLim): if dist>sL.maxRad: spec+='\t\n'%sL.radLim spec+='\t\t\n' %(fscale,f/10**fscale) free=False elif visL.roi[2]): #if beyond ROI, shouldn't attempt to fit parameters spec+='\t\n' spec+='\t\t\n' %(fscale,f/10**fscale) spec+='\t\t\n' %i spec+='\t\t\n'%b free=False elif fixAll: #if set to fix all, should be only for crab IC component spec+='\t\n' spec+='\t\t\n' %(fscale,f/10**fscale) spec+='\t\t\n' %i spec+='\t\t\n'%b free=False elif(dist>sL.radLim): if dist>sL.maxRad: spec+='\t\n'%sL.radLim spec+='\t\t\n' %(fscale,f/10**fscale) free=False elif vi