#!/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'): 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) 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, significance below which source parameters are fixed, even if within radLim #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, currently not implemented for building from xml catalog #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 #ApplyEDisp (boo) -- optional, flag to apply energy dispersion to free sources (except diffuse backgrounds) default is False. 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 or sname=='4FGL J0534.5+2201i': Sources[sname]['free']=False #specOut.setAttribute('apply_edisp','false')#source is fixed, so never apply edisp 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 #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')))) 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 file=pyfits.open(sL.srcs) #open source list file and access necessary fields, requires LAT source catalog definitions and names #mask=file[1].data.field('Signif_Avg')>=sL.sig #data=file[1].data[mask] data=file['LAT_Point_Source_Catalog'].data extendedinfo=file['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') try: VarIdx=data.field('Variability_Index') except: if sL.var==True: print("Error: requested to set variables sources free but 'Variability_Index' not found in %s"%sL.srcs) print("make sure you are using gll_psc_v19.fit or newer.") return else: VarIdx=[0.]*len(name) 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') 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. for ii in range(len(name)): if name[ii]=='4FGL J0534.5+2201i': Sigvals[ii]=-1 VarIdx[ii]=-1 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,f,i,r,d,p,c,t,b,TS,ei,vi,En in zip(name,flux,index,ra,dec,pivot,cutoff,spectype,beta,Sigvals,expIndex,VarIdx,EName): #for n,f,r,d,p,pli,lpi,lpb,pleci,plecef,plecei,t,TS,En,vi in zip(name,flux,ra,dec,pivot,plIndex,lpIndex,lpbeta,plecIndex,plecexpFact,plecexpIndex,spectype,Sigvals,EName,VarIdx): 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] in ['e','i'] 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:#uncomment this later when FSSC STs can deal with rosette nebula and two new spatial models 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 #fixAll=(True if n=='3FGL J0534.5+2201i' or En in ['Cygnus Cocoon','Vela X','MSH 15-52','gamma Cygni'] else False) #spec,free=PLspec(sL,f,pli,p,dist,TS,vi,fixAll) spec,free=PLspec(sL,plf,pli,p,dist,TS,vi,False) elif t=='PowerLaw2':#no value for flux from 100 MeV to 100 GeV in fits file if pli!=1.:#so calculate it by integrating PowerLaw spectral model F=plf*p**pli/(-pli+1.)*(1.e5**(-pli+1.)-1.e2**(-pli+1.)) else: F=plf*p*log(1.e3) spec,free=PL2spec(sL,F,pli,dist,TS,vi) #spec,free=PL2spec(sL,f100,i,dist,TS,vi) elif t=='LogParabola': spec,free=LPspec(sL,lpf,lpi,p,lpb,dist,TS,vi) else: ##spec,free=COspec(sL,f,pleci,p,plecef,plecei,dist,TS,vi) 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 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') file.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,fixAll): fscale=int(floor(log10(f))) spec='\t\n' spec+='\t\n' %dist if dist>sL.roi[2] or fixAll: #if beyond ROI, shouldn't attempt to fit parameters if fixAll: spec+='\t\n' else: 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 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 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 if c<=1e5: spec+='\t\t\n'%c else: spec+='\t\t\n'%(2.*c,c) 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' 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