| Classes | Job Modules | Data Objects | Services | Algorithms | Tools | Packages | Directories | Tracs |

In This Package:

Tools.py

Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Python tools for working with calibration parameters
00004 # 
00005 
00006 __all__ = [ "PmtTableWriterAlg", "mergeTables", "parseTable" ]
00007 
00008 # Load GaudiPython
00009 from DybPython.DybPythonAlg import DybPythonAlg
00010 from GaudiPython import SUCCESS, FAILURE
00011 from GaudiPython import gbl
00012 import GaudiKernel.SystemOfUnits as units
00013 import math
00014 
00015 # Shortcuts to DayaBay classes
00016 Detector = gbl.DayaBay.Detector
00017 Site = gbl.Site
00018 DetectorId = gbl.DetectorId
00019 Context = gbl.Context
00020 ServiceMode = gbl.ServiceMode
00021 PmtCalibData = gbl.DayaBay.PmtCalibData
00022 AdPmtSensor = gbl.DayaBay.AdPmtSensor
00023 
00024 # Generate PMT calibration table
00025 class PmtTableWriterAlg(DybPythonAlg):
00026     "Generate a calibration table from calibration run statistics"
00027     def __init__(self,name):
00028         DybPythonAlg.__init__(self,name)
00029         # Properties
00030         self.StatsPath = "/file0/pmtCalibLeadingEdge"
00031         self.OutputTableName = "pmtCalibTable.txt"
00032         self.IsDryAd = False
00033         self.SetDefaultCalib = True
00034         self.SourcePosition = [0.,0.,0.]
00035         return
00036 
00037     def initialize(self):
00038         status = DybPythonAlg.initialize(self)
00039         if status.isFailure(): return status
00040         self.info("initializing")
00041 
00042         # Initialize services
00043         self.cableSvc = self.svc('ICableSvc','StaticCableSvc')
00044         if self.cableSvc == None:
00045             self.error("Failed to get StaticCableSvc")
00046             return FAILURE
00047         self.statsSvc = self.svc('IStatisticsSvc','StatisticsSvc')
00048         if self.statsSvc == None:
00049             self.error("Failed to get StatisticsSvc")
00050             return FAILURE
00051         self.pmtGeomSvc = self.svc('IPmtGeomInfoSvc','PmtGeomInfoSvc')
00052         if self.pmtGeomSvc == None:
00053             self.error("Failed to get PmtGeomInfoSvc")
00054             return FAILURE
00055         return status
00056 
00057     def execute(self):
00058         self.info("executing")
00059         return SUCCESS
00060         
00061     def finalize(self):
00062         self.info("finalizing")
00063         # Generate the PMT calibration table, and write to file
00064         # Step 1: initialize the table
00065         pmtCalibList = self.initializeCalibTable()
00066         self.info("I initialized the table") #tmp
00067         # Step 2: incorporate data using the current calibration statistics
00068         status = self.updateCalibration(pmtCalibList)
00069         if not status.isSuccess(): return status
00070         # Step 3: Print out the table
00071         self.writeCalibTable(pmtCalibList)
00072         status = DybPythonAlg.finalize(self)
00073         return status
00074 
00075     def initializeCalibTable(self):
00076         # Initialize table of PMT calibrations
00077         context = Context()
00078         context.SetSite(Site.kAll)
00079         context.SetDetId(DetectorId.kAll)
00080         svcMode = ServiceMode(context, 0)  # Dummy ServiceMode
00081         adPmts = self.cableSvc.adPmtSensors(svcMode)
00082         poolPmts = self.cableSvc.poolPmtSensors(svcMode)
00083         adPmtCalibList = []
00084         for adPmt in adPmts:
00085             pmtCalib = PmtCalibData()
00086             pmtCalib.m_pmtId = adPmt
00087             if self.SetDefaultCalib: self.initializeCalib(pmtCalib)
00088             adPmtCalibList.append( pmtCalib )
00089         poolPmtCalibList = []
00090         for poolPmt in poolPmts:
00091             pmtCalib = PmtCalibData()
00092             pmtCalib.m_pmtId = poolPmt
00093             if self.SetDefaultCalib: self.initializeCalib(pmtCalib)
00094             poolPmtCalibList.append( pmtCalib )
00095         pmtCalibList = adPmtCalibList + poolPmtCalibList
00096         return pmtCalibList
00097 
00098     def initializeCalib(self, pmtCalib):
00099         # Initialize using an approximate default calibration
00100         pmtCalib.m_status = PmtCalibData.kGood
00101         pmtCalib.m_speHigh = 20.0
00102         pmtCalib.m_sigmaSpeHigh = 0.0
00103         pmtCalib.m_speLow = 2.0
00104         pmtCalib.m_timeOffset = 0.0
00105         pmtCalib.m_timeSpread = 0.0
00106         pmtCalib.m_efficiency = 1.0
00107         pmtCalib.m_prePulseProb = 0.0
00108         pmtCalib.m_afterPulseProb = 0.0
00109         pmtCalib.m_darkRate = 0.0
00110 
00111     def updateCalibration(self, pmtCalibList):
00112         # Process the calibration statistics and update the
00113         # calibration parameters
00114         detDirs = self.statsSvc.getSubFolders(self.StatsPath)
00115         for detDir in detDirs:
00116             detector = Detector(detDir)
00117             if( detector.site() == Site.kUnknown
00118                 or detector.detectorId() == DetectorId.kUnknown ):
00119                 self.error("Unknown detector: "+detDir)
00120                 return FAILURE
00121             else:
00122                 self.info("Processing: "+detDir)
00123             # Find the PMT IDs that are in the current detector
00124             pmtCalibs = []
00125             for pmtCalib in pmtCalibList:
00126                 pmtId = pmtCalib.m_pmtId
00127                 if (pmtId.site() == detector.site()
00128                     and pmtId.detectorId() == detector.detectorId()):
00129                     pmtCalibs.append(pmtCalib)
00130             if detector.isAD():
00131                 status = self.updateAdCalibration(detector, pmtCalibs)
00132                 if not status.isSuccess(): return status
00133             elif detector.isWaterShield():
00134                 status = self.updateWaterShieldCalibration(detector, pmtCalibs)
00135                 if not status.isSuccess(): return status
00136         return SUCCESS
00137 
00138     def updateAdCalibration(self, detector, pmtCalibList):
00139         print "I am in updateAdCalibration" #tmp
00140         # Loop over the pmts and process calibration stats
00141         meanTimeOffset = 0.0
00142         nMeanTimeOffset = 0
00143         relativeEfficiency = {}
00144         for ring in range(1,9): relativeEfficiency[ring] = []
00145         for pmtCalib in pmtCalibList:
00146 
00147             pmtId = pmtCalib.m_pmtId
00148             if pmtId.ring() < 1: continue  # Skip 2-inch calib pmts
00149             # Get the calibration histograms for this pmt
00150             [occupancyH, tdcOffsetH, adcMeanH, adcSigmaH] = self.getAdCalibHists(pmtId)
00151             if occupancyH==None or tdcOffsetH==None or adcMeanH==None:
00152                 self.error("Failed to get calibration histograms for "
00153                            +detector.detName())
00154                 return FAILURE
00155             occupancy = occupancyH.GetBinContent(pmtId.column())
00156             occupancyUncert = occupancyH.GetBinError(pmtId.column())
00157             tdcOffset = tdcOffsetH.GetBinContent(pmtId.column())
00158             tdcOffsetUncert = tdcOffsetH.GetBinError(pmtId.column())
00159             adcMean       = adcMeanH.GetBinContent(pmtId.column())            
00160             adcMeanUncert = adcMeanH.GetBinError(pmtId.column())
00161             adcReso       = adcSigmaH.GetBinContent(pmtId.column())
00162             adcResoUncert = adcSigmaH.GetBinError(pmtId.column())
00163             # Update PMT status
00164             pmtCalib.m_status = PmtCalibData.kUnknown
00165             if( occupancy < 0.01 or occupancyUncert > 0.1 ):
00166                 pmtCalib.m_status += PmtCalibData.kBadEfficiency
00167             if( tdcOffsetUncert > 20.0 ):
00168                 pmtCalib.m_status += PmtCalibData.kBadTiming
00169             if( adcMean < 10.0 or adcMean > 50.0 or adcMeanUncert > 4.0 ):
00170                 pmtCalib.m_status += PmtCalibData.kBadGain
00171             # If no problems found, PMT is good
00172             if( pmtCalib.m_status == PmtCalibData.kUnknown ):
00173                 pmtCalib.m_status = PmtCalibData.kGood
00174             # Convert time offset to nanoseconds
00175             tdcToNs = 1.5625
00176             lightTravelTime = self.timeToAdPmt(self.SourcePosition, pmtId)
00177             timeOffset = -tdcOffset * tdcToNs - lightTravelTime
00178             timeSpread = tdcOffsetUncert*tdcToNs
00179             pmtCalib.m_timeOffset = timeOffset
00180             pmtCalib.m_timeSpread = timeSpread
00181             # wangzhe: comment out temporarily
00182             # pmtCalib.m_efficiency = -math.log(1 - occupancy)
00183             # wz
00184             pmtCalib.m_speHigh = adcMean
00185             pmtCalib.m_sigmaSpeHigh = adcReso
00186             # FIXME: need dedicated Low-gain calibration:
00187             # According to DocDB 5038, for dry run it is set to 19.5
00188             pmtCalib.m_speLow = adcMean / 19.5
00189             # Update mean time
00190             if not (pmtCalib.m_status & PmtCalibData.kBadTiming):
00191                 meanTimeOffset += timeOffset
00192                 nMeanTimeOffset += 1
00193             # Update mean efficiency by ring
00194             if not (pmtCalib.m_status & PmtCalibData.kBadEfficiency):
00195                 relativeEfficiency[pmtId.ring()].append(pmtCalib.m_efficiency)
00196         # Adjust timing offset so mean over detector is zero
00197         if nMeanTimeOffset != 0:
00198             meanTimeOffset /= nMeanTimeOffset
00199         for pmtCalib in pmtCalibList:
00200             pmtId = pmtCalib.m_pmtId
00201             if pmtId.ring() < 1: continue  # Skip 2-inch calib pmts
00202             pmtCalib.m_timeOffset -= meanTimeOffset
00203         # Adjust the relative efficiency according to the mean for each ring
00204         meanEfficiency = {}
00205         for ring in relativeEfficiency.keys():
00206             relEff = relativeEfficiency[ring]
00207             meanEfficiency[ring] = 0
00208             if len(relEff) > 0:
00209                 meanEfficiency[ring] = sum(relEff)/len(relEff)
00210         for pmtCalib in pmtCalibList:
00211             pmtId = pmtCalib.m_pmtId
00212             if pmtId.ring() < 1: continue  # Skip 2-inch calib pmts
00213             if meanEfficiency[pmtId.ring()] != 0: #jpochoa
00214                 pmtCalib.m_efficiency /= meanEfficiency[pmtId.ring()]
00215             else:
00216                 pmtCalib.m_efficiency = -1
00217 
00218         
00219                 
00220         return SUCCESS
00221 
00222     def getAdCalibHists(self, pmtId):
00223         # Return the calibration histograms for this PMT
00224         path = self.StatsPath + "/" + pmtId.detName()
00225         path += "/ring_%d" % pmtId.ring()
00226         occupancyH = self.statsSvc.get(path + "/occupancy")
00227         tdcOffsetH = self.statsSvc.get(path + "/tdcOffset")
00228         if self.StatsPath=="/file0/pmtCalibLeadingEdge":
00229             adcMeanH =   self.statsSvc.get(path + "/adcMean")
00230         else:
00231             adcMeanH = self.statsSvc.get(path + "/adcMedian")
00232         adcSigmaH =  self.statsSvc.get(path + "/adcSigma")
00233         return [occupancyH, tdcOffsetH, adcMeanH, adcSigmaH]
00234     
00235     def getPmtDistance(self, position, pmtId):
00236         "Return the distance from position to the pmt with ID pmtID"
00237         # No Site.kSAB in current geometry tree. Change it to kDayaBay to work around
00238         site = pmtId.site()
00239         if site == Site.kSAB:
00240             site = Site.kDayaBay
00241         tmpPmtId = AdPmtSensor( pmtId.ring(), pmtId.column(), site, pmtId.detectorId() )
00242         pmtGeomInfo = self.pmtGeomSvc.get( tmpPmtId.fullPackedData() )
00243         Rpmt = pmtGeomInfo.localPosition()
00244         dR = [position[0] - Rpmt.x(),
00245               position[1] - Rpmt.y(),
00246               position[2] - Rpmt.z()]
00247         return math.sqrt(dR[0]*dR[0] + dR[1]*dR[1] + dR[2]*dR[2])
00248 
00249     def timeToAdPmt(self, position, pmtId):
00250         "Calculate the light travel time from source to pmt"
00251         dr = self.getPmtDistance(position, pmtId)
00252         lightSpeedInVac = 299.79 * units.mm/units.ns
00253         liquidIndexOfRefrac = 1.45
00254         lightSpeed = lightSpeedInVac
00255         if not self.IsDryAd:
00256             lightSpeed /= liquidIndexOfRefrac
00257         return dr / lightSpeed
00258 
00259 
00260     def updateWaterShieldCalibration(self, detector, pmtCalibList):
00261         # Update the calib data for this water shield based on calibration data
00262         self.error("Calibration of water shield not implemented")
00263         self.error("I am ignoring this for now") #jpochoa
00264         return SUCCESS #jpochoa
00265             
00266     def calibTableLine(self, pmtCalib):
00267         # Write a line for the calibration table
00268         pmtLabel = None
00269         pmtId = pmtCalib.m_pmtId
00270         if pmtId.isAD():
00271             pmtLabel = "%s-ring%02d-column%02d" % (pmtId.detName(),
00272                                                    pmtId.ring(),
00273                                                    pmtId.column())
00274         elif pmtId.isWaterShield():
00275             inward = "in"
00276             if not pmtId.inwardFacing(): inward = "out"
00277             pmtLabel = "%s-wall%02d-spot%02d-%s" % (pmtId.detName(),
00278                                                     pmtId.wallNumber(),
00279                                                     pmtId.wallSpot(),
00280                                                     inward)
00281         line=("%d %30s %2d %7.3f %5.3f %5.3f %7.3f %5.3f %5.3f %5.3f %5.3f %5.3f\n"
00282               % (pmtCalib.m_pmtId.fullPackedData(),
00283                  pmtLabel,
00284                  pmtCalib.m_status,
00285                  pmtCalib.m_speHigh,
00286                  pmtCalib.m_sigmaSpeHigh,
00287                  pmtCalib.m_speLow,
00288                  pmtCalib.m_timeOffset / units.ns,
00289                  pmtCalib.m_timeSpread / units.ns,
00290                  pmtCalib.m_efficiency,
00291                  pmtCalib.m_prePulseProb,
00292                  pmtCalib.m_afterPulseProb,
00293                  pmtCalib.m_darkRate / units.hertz
00294                  )
00295               )
00296         return line
00297 
00298     def writeCalibTable(self, pmtCalibList):
00299         # Write a calibration table using the list of pmt calibrations
00300         tableLines = []
00301         tableLines.append("# Table of Calibration constants for each channel\n")
00302         tableLines.append("# [pmtID] [description] [status] [speHigh] [sigmaSpe] [speLow] [timeOffset] [timeSpread] [efficiency] [prePulse] [afterPulse] [darkRate]\n")
00303 
00304         # sort them by id, otherwise it is out of control
00305         idCalib = {}
00306         for pmtCalib in pmtCalibList:
00307             idCalib[ pmtCalib.m_pmtId.fullPackedData() ] = pmtCalib
00308 
00309         for id in sorted( idCalib.keys() ):
00310             pmtCalib = idCalib[id]
00311             line = self.calibTableLine(pmtCalib)
00312             tableLines.append(line)
00313         outFile = open(self.OutputFileName,'w')
00314         outFile.writelines(tableLines)
00315         outFile.close()        
00316 
00317 
00318 # Merge calibration tables
00319 def mergeTables(primaryFile, changeMap, outputFile):
00320     "Simple Non-NuWa python function for merging calibration text tables"
00321     inputTable = parseTable(primaryFile)
00322     # Update table
00323     for changeFile in changeMap.keys():
00324         changeTable = parseTable(changeFile)
00325         changeFields = changeMap[changeFile]
00326         for fieldName in changeFields:
00327             fieldName = fieldName.strip()
00328             if not inputTable["columns"].has_key(fieldName):
00329                 print "Field \"",fieldName,"\" doesn't exist in input table."
00330                 return
00331             inputTable["columns"][fieldName] = changeTable["columns"][fieldName]
00332     # Write output table comments and header
00333     outputTableLines = []
00334     for comment in inputTable["comments"]:
00335         outputTableLines.append(comment)
00336     outputTableLines.append(inputTable["header"])
00337     # determine field lengths
00338     fieldLength = {}
00339     for fieldName in inputTable["fields"]:
00340         fieldLength[fieldName] = max( [len(data) for data in
00341                                        inputTable["columns"][fieldName] ] )
00342     # write data lines
00343     for rowIdx in range(inputTable["numberOfRows"]):
00344         line = ""
00345         for fieldName in inputTable["fields"]:
00346             format = "%"+str(fieldLength[fieldName])+"s " 
00347             line += format % inputTable["columns"][fieldName][rowIdx]
00348         outputTableLines.append(line+"\n")
00349     open(outputFile,"w").writelines(outputTableLines)
00350 
00351 def parseTable(filename):
00352     "Simple text table parser"
00353     tableLines = open(filename).readlines()
00354     fieldNames = []
00355     comments = []
00356     headerLine = None
00357     columns = {}
00358     nRows = 0
00359     for line in tableLines:
00360         data = line.strip()
00361         # Catch comments / header
00362         if len(data)>0 and data[0]=='#':
00363             comment = data[1:].strip()
00364             if len(comment)>0 and comment[0] =='[':
00365                 # Found header
00366                 if len(fieldNames) > 0:
00367                     print "Headers already defined by line \"",line,"\""
00368                     return None
00369                 tokens = comment.split(']')
00370                 if len(tokens[-1].strip())==0: tokens = tokens[:-1]
00371                 for token in tokens:
00372                     fieldStart = token.rfind('[')
00373                     if fieldStart<0:
00374                         print "Invalid token \"",token,"\" in line \"",line,"\""
00375                         return None
00376                     fieldName = token[fieldStart+1:].strip()
00377                     fieldNames.append(fieldName)
00378                     columns[fieldName] = []
00379                 headerLine = line
00380             else:
00381                 comments.append(line)
00382             continue
00383         # Data line
00384         fields = data.split()
00385         if len(fields)==0: continue  # Empty line
00386         if len(fields) != len(columns):
00387             print "Invalid number of fields in line \"",line,"\""
00388             return None
00389         for fieldIdx in range(len(fields)):
00390             columns[fieldNames[fieldIdx]].append( fields[fieldIdx] )
00391         nRows += 1
00392     return { "fields":fieldNames,
00393              "comments":comments,
00394              "header":headerLine,
00395              "columns":columns,
00396              "numberOfRows":nRows}
| Classes | Job Modules | Data Objects | Services | Algorithms | Tools | Packages | Directories | Tracs |

Generated on Mon Apr 11 20:29:39 2011 for CalibParam by doxygen 1.4.7