Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-11-27 03:17:54

0001 #!/bin/env python3
0002 
0003 import argparse
0004 import FWCore.ParameterSet.Config as cms
0005 from importlib import import_module
0006 import os
0007 import sys
0008 import xml.etree.ElementTree as ET
0009 
0010 
0011 # Pairwise generator: returns pairs of adjacent elements in a list / other iterable
0012 def pairwiseGen(aList):
0013     for i in range(len(aList)-1):
0014         yield (aList[i], aList[i+1])
0015 
0016 def parseOfflineLUTfile(aRelPath, aExpectedSize, aPaddingValue = None, aTruncate = False):
0017     # Find file by looking under directories listed in 'CMSSW_SEARCH_PATH' as outlined in https://twiki.cern.ch/twiki/bin/view/CMSPublic/SWGuideEdmFileInPath
0018     searchPaths = os.getenv('CMSSW_SEARCH_PATH').split(':')
0019     resolvedPath = None
0020     for baseDir in searchPaths:
0021         print("Looking for '" + aRelPath + "' under '" + baseDir + "'")
0022         if os.path.isfile(os.path.join(baseDir, aRelPath)):
0023             print("   success!")
0024             resolvedPath = os.path.join(baseDir, aRelPath)
0025             break
0026     if resolvedPath is None:
0027         raise RuntimeError("Could not find LUT file '" + aRelPath + "' under directories in 'CMSSW_SEARCH_PATH'")
0028 
0029     with open(resolvedPath) as f:
0030         entries = []
0031         line_nr = 0
0032         for line in f:
0033             line_nr += 1
0034             # Ignore comment lines
0035             if line.startswith('#') or line == '\n':
0036                 continue
0037 
0038             # Remove trailing comments from data lines
0039             stripped_line = line[:line.find('#')]
0040 
0041             # Split line into list of whitespace-separated items
0042             items = stripped_line.split()
0043             if len(items) != 2:
0044                print("ERROR parsing file", resolvedPath, "on line", line_nr, "'" + line + "' : Splitting on whitespace produced", len(items), "items")
0045                sys.exit(1)
0046 
0047             entries.append( (int(items[0]), int(items[1])) )
0048 
0049         # Sort the LUT
0050         entries.sort(key= lambda x : x[0])  
0051         # Check that the LUT is not empty
0052         if len(entries) == 0:
0053             print("ERROR parsing file", resolvedPath, ": No LUT entries defined in the file")
0054             sys.exit(1)
0055 
0056         # Check that no items from the LUT are missing
0057         if entries[0][0] != 0:
0058             print("ERROR parsing file", resolvedPath, ": LUT entries before index", entries[0][0], "are not defined")
0059             sys.exit(1)
0060          
0061         for x1, x2 in pairwiseGen(entries):
0062             if x1[0] != (x2[0]-1):
0063                 print("ERROR parsing file", resolvedPath, ": ", x2[0] - x1[0] - 1,"LUT entries between indices", x1[0], "and", x2[0], "are not defined")
0064                 sys.exit(1)
0065 
0066         result = [x[1] for x in entries]
0067 
0068         if (len(result) < aExpectedSize) and not (aPaddingValue is None):
0069             print ("WARNING : Padding", str(len(result))+"-entry LUT with value", aPaddingValue, "to have", aExpectedSize, "entries")
0070             result += ([aPaddingValue] * (aExpectedSize - len(result)))
0071         elif (len(result) > aExpectedSize) and aTruncate:
0072             print ("WARNING : Truncating", str(len(result))+"-entry LUT to have", aExpectedSize, "entries")
0073             result = result[0:aExpectedSize]
0074         elif len(result) != aExpectedSize:
0075             print ("ERROR parsing file", resolvedPath, ": Expected LUT of size", aExpectedSize, ", but", len(result), "entries were specified (and no padding/truncation requested)")
0076             sys.exit(1)
0077 
0078         return result
0079 
0080 
0081 def getFullListOfParameters(aModule):
0082 
0083     def divideByEgLsb(aParam):
0084         return int(aParam.value() / aModule.egLsb.value())
0085 
0086     def divideByTauLsb(aParam):
0087         return int(aParam.value() / aModule.tauLsb.value())
0088 
0089     def divideByJetLsb(aParam):
0090         return int(aParam.value() / aModule.jetLsb.value())
0091 
0092 
0093     result = [
0094       (('mp_common', 'sdfile'),               None,                         ''),
0095       (('mp_common', 'algoRev'),              None,                         ''),
0096       (('mp_common', 'leptonSeedThreshold'),  '2_ClusterSeedThreshold.mif', divideByEgLsb(aModule.egSeedThreshold)),
0097       (('mp_common', 'leptonTowerThreshold'), '3_ClusterThreshold.mif',     divideByEgLsb(aModule.egNeighbourThreshold)),
0098       (('mp_common', 'pileUpTowerThreshold'), '4_PileUpThreshold.mif',      0x0)
0099     ]
0100 
0101     result += [
0102       (('mp_egamma', 'egammaRelaxationThreshold'),  '10_EgRelaxThr.mif',              divideByEgLsb(aModule.egMaxPtHOverE)),
0103       (('mp_egamma', 'egammaMaxEta'),               'egammaMaxEta.mif',          aModule.egEtaCut.value()),
0104       (('mp_egamma', 'egammaBypassCuts'),           'BypassEgVeto.mif',               bool(aModule.egBypassEGVetos.value())),
0105       (('mp_egamma', 'egammaBypassShape'),          'BypassEgShape.mif',              bool(aModule.egBypassShape.value())),
0106       (('mp_egamma', 'egammaBypassEcalFG'),         'BypassEcalFG.mif',               bool(aModule.egBypassECALFG.value())),
0107       (('mp_egamma', 'egammaBypassExtendedHOverE'), '_BypassExtHE.mif',               bool(aModule.egBypassExtHOverE)),
0108       (('mp_egamma', 'egammaHOverECut_iEtaLT15'),   '_RatioCutLt15.mif',              aModule.egHOverEcutBarrel.value()),
0109       (('mp_egamma', 'egammaHOverECut_iEtaGTEq15'), '_RatioCutGe15.mif',              aModule.egHOverEcutEndcap.value()),
0110       (('mp_egamma', 'egammaEnergyCalibLUT'),       'C_EgammaCalibration_12to18.mif', parseOfflineLUTfile(aModule.egCalibrationLUTFile.value(), 4096)),
0111       (('mp_egamma', 'egammaIsoLUT1'),              'D_EgammaIsolation1_13to9.mif',   parseOfflineLUTfile(aModule.egIsoLUTFile.value(), 8192)),
0112       (('mp_egamma', 'egammaIsoLUT2'),              'D_EgammaIsolation2_13to9.mif',   parseOfflineLUTfile(aModule.egIsoLUTFile2.value(), 8192))
0113     ]
0114 
0115     result += [
0116       (('mp_tau', 'tauMaxEta'),         'tauMaxEta.mif',               aModule.isoTauEtaMax.value()),
0117       (('mp_tau', 'tauEnergyCalibLUT'), 'I_TauCalibration_11to18.mif', parseOfflineLUTfile(aModule.tauCalibrationLUTFile.value(), 2048, 0x0)),
0118       (('mp_tau', 'tauIsoLUT'),         'H_TauIsolation_12to9.mif',    parseOfflineLUTfile(aModule.tauIsoLUTFile.value(), 4096)),
0119       (('mp_tau', 'tauTrimmingLUT'),    'P_TauTrimming_13to8.mif',     parseOfflineLUTfile(aModule.tauTrimmingShapeVetoLUTFile.value(), 8192))
0120     ]
0121 
0122     result += [
0123       (('mp_jet', 'jetSeedThreshold'),   '1_JetSeedThreshold.mif',      divideByJetLsb(aModule.jetSeedThreshold)),
0124       (('mp_jet', 'jetMaxEta'),          '6_JetEtaMax.mif',             0x00028),
0125       (('mp_jet', 'jetBypassPileUpSub'), 'BypassJetPUS.mif',            bool(aModule.jetBypassPUS.value())),
0126       (('mp_jet', 'jetPUSUsePhiRing'), 'PhiRingPUS.mif',        bool(aModule.jetPUSUsePhiRing.value())),
0127       (('mp_jet', 'jetEnergyCalibLUT'),  'L_JetCalibration_11to18.mif', parseOfflineLUTfile(aModule.jetCalibrationLUTFile.value(), 4096)),
0128       (('mp_jet', 'jetCompressPtLUT'),  'K_EnergyCompression_8to4.mif', parseOfflineLUTfile(aModule.jetCompressPtLUTFile.value(), 256)),
0129       (('mp_jet', 'jetCompressEtaLUT'),  'J_EtaCompression_6to6.mif', parseOfflineLUTfile(aModule.jetCompressEtaLUTFile.value(), 64)),
0130       (('mp_jet', 'HTMHT_maxJetEta'),    'HTMHT_maxJetEta.mif',         aModule.etSumEtaMax[1]), # assert == etSumEtaMax[3] ?
0131       (('mp_jet', 'HT_jetThreshold'),    '8_HtThreshold.mif',           int(aModule.etSumEtThreshold[1] / aModule.etSumLsb.value())),
0132       (('mp_jet', 'MHT_jetThreshold'),   '9_MHtThreshold.mif',          int(aModule.etSumEtThreshold[3] / aModule.etSumLsb.value())),
0133     ]
0134 
0135     result += [
0136       (('mp_sums', 'towerCountThreshold'),      'HeavyIonThr.mif',       int(aModule.etSumEtThreshold[4] / aModule.etSumLsb.value()) ),
0137       (('mp_sums', 'towerCountMaxEta'),         'HeavyIonEta.mif',       aModule.etSumEtaMax[4]),
0138       (('mp_sums', 'ETMET_maxTowerEta'),        'ETMET_maxTowerEta.mif', aModule.etSumEtaMax[0]), # assert == etSumEtaMax[2] ?
0139       (('mp_sums', 'ecalET_towerThresholdLUT'), 'X_EcalTHR_11to9.mif',   parseOfflineLUTfile(aModule.etSumEcalSumPUSLUTFile.value(), 2048, aTruncate = True)),
0140       (('mp_sums', 'ET_towerThresholdLUT'),     'X_ETTHR_11to9.mif',     parseOfflineLUTfile(aModule.etSumEttPUSLUTFile.value(), 2048, aTruncate = True)),
0141       (('mp_sums', 'MET_towerThresholdLUT'),    'X_METTHR_11to9.mif',    parseOfflineLUTfile(aModule.etSumMetPUSLUTFile.value(), 2048))
0142     ]
0143 
0144     result += [
0145       (('demux', 'sdfile'),  None, ''),
0146       (('demux', 'algoRev'), None, 0xcafe),
0147       (('demux', 'ET_centralityLowerThresholds'), 'CentralityLowerThrs.mif', [ int(round(loBound / aModule.etSumLsb.value())) for loBound in aModule.etSumCentralityLower.value()]),
0148       (('demux', 'ET_centralityUpperThresholds'), 'CentralityUpperThrs.mif', [ int(round(upBound / aModule.etSumLsb.value())) for upBound in aModule.etSumCentralityUpper.value()]),
0149       #(('demux', 'MET_energyCalibLUT'),       'M_METnoHFenergyCalibration_12to18.mif',     parseOfflineLUTfile(aModule.metCalibrationLUTFile.value(), 4096, aTruncate = True)),
0150       #(('demux', 'METHF_energyCalibLUT'),     'M_METwithHFenergyCalibration_12to18.mif',   parseOfflineLUTfile(aModule.metHFCalibrationLUTFile.value(), 4096, aTruncate = True)),
0151       #(('demux', 'ET_energyCalibLUT'),        'S_ETcalibration_12to18.mif',       parseOfflineLUTfile(aModule.etSumEttCalibrationLUTFile.value(), 4096, aTruncate = True)),
0152       #(('demux', 'ecalET_energyCalibLUT'),    'R_EcalCalibration_12to18.mif',     parseOfflineLUTfile(aModule.etSumEcalSumCalibrationLUTFile.value(), 4096, aTruncate = True)),
0153       #(('demux', 'MET_phiCalibLUT'),          'Q_METnoHFphiCalibration_12to18.mif',     parseOfflineLUTfile(aModule.metPhiCalibrationLUTFile.value(), 4096, aTruncate = True)),
0154       #(('demux', 'METHF_phiCalibLUT'),        'Q_METwithHFphiCalibration_12to18.mif',   parseOfflineLUTfile(aModule.metHFPhiCalibrationLUTFile.value(), 4096, aTruncate = True)),
0155     ]
0156 
0157     result = [(a, b, parseOfflineLUTfile(c.value()) if isinstance(c, cms.FileInPath) else c) for a, b, c in result]
0158 
0159     return result
0160 
0161 
0162 def getXmlParameterMap(aModule):
0163     result = {}
0164     for xmlDetails, mifName, value in getFullListOfParameters(aModule):
0165         if xmlDetails is not None:
0166             if xmlDetails[0] in result:
0167                 result[xmlDetails[0]] += [(xmlDetails[1], value)]
0168             else:
0169                 result[xmlDetails[0]] = [(xmlDetails[1], value)]
0170 
0171     return result
0172 
0173 
0174 def getMifParameterMap(aModule):
0175 
0176     fullList = getFullListOfParameters(aModule)
0177 
0178     return {mifFileName : value for (_, mifFileName, value) in fullList if mifFileName is not None}
0179 
0180 
0181 # Stolen from https://stackoverflow.com/questions/3095434/inserting-newlines-in-xml-file-generated-via-xml-etree-elementtree-in-python
0182 def indent(elem, level=0):
0183     i = "\n" + level*"  "
0184     if len(elem):
0185         if not elem.text or not elem.text.strip():
0186             elem.text = i + "  "
0187         if not elem.tail or not elem.tail.strip():
0188             elem.tail = i
0189         for elem in elem:
0190             indent(elem, level+1)
0191         if not elem.tail or not elem.tail.strip():
0192             elem.tail = i
0193     else:
0194         if level and (not elem.tail or not elem.tail.strip()):
0195             elem.tail = i
0196 
0197 def createMIF(aFilePath, aValue):
0198     print("Writing MIF file:", aFilePath)
0199     with open(aFilePath, 'w') as f:
0200         if isinstance(aValue, bool):
0201             aValue = (1 if aValue else 0)
0202 
0203         if isinstance(aValue, int):
0204             f.write( hex(aValue) )
0205         elif isinstance(aValue, list):
0206             f.write("\n".join([hex(x) for x in aValue]))
0207         else:
0208             raise RuntimeError("Do not know how to deal with parameter of type " + str(type(aValue)))
0209 
0210 
0211 def createXML(parameters, contextId, outputFilePath):
0212     topNode = ET.Element('algo', id='calol2')
0213     contextNode = ET.SubElement(topNode, 'context', id=contextId)
0214     for paramId, value in parameters:
0215         if isinstance(value, bool):
0216             ET.SubElement(contextNode, 'param', id=paramId, type='bool').text = str(value).lower()
0217         elif isinstance(value, int):
0218             ET.SubElement(contextNode, 'param', id=paramId, type='uint').text = "0x{0:05X}".format(value)
0219         elif isinstance(value, str):
0220             ET.SubElement(contextNode, 'param', id=paramId, type='string').text = value
0221         elif isinstance(value, list):
0222             ET.SubElement(contextNode, 'param', id=paramId, type='vector:uint').text  = "\n      " + ",\n      ".join(["0x{0:05X}".format(x) for x in value]) + "\n    "
0223         else:
0224             raise RuntimeError("Do not know how to deal with parameter '" + paramId + "' of type " + str(type(value)))
0225     indent(topNode)
0226 
0227     print("Writing XML file:", outputFilePath)
0228     with open(outputFilePath, 'w') as f:
0229         f.write(ET.tostring(topNode, encoding="unicode"))
0230 
0231 
0232 
0233 if __name__ == '__main__':
0234 
0235     parser = argparse.ArgumentParser()
0236 
0237     parser.add_argument('params_cfi', help='Name of CMSSW cfi python file specifying the values for the calo parameters')
0238     parser.add_argument('output_dir', help='Directory for MIF/XML output files')
0239 
0240     outputFormatGroup = parser.add_mutually_exclusive_group(required=True)
0241     outputFormatGroup.add_argument('--mif', action='store_true')
0242     outputFormatGroup.add_argument('--xml', action='store_true')
0243 
0244     args = parser.parse_args()
0245 
0246     moduleName = 'L1Trigger.L1TCalorimeter.' + args.params_cfi
0247     print("Importing calo params from module:", moduleName)
0248     caloParams = import_module(moduleName).caloStage2Params
0249 
0250     os.mkdir(args.output_dir)
0251 
0252     if args.mif:
0253         for fileName, value in getMifParameterMap(caloParams).items():
0254             createMIF(args.output_dir + '/' + fileName, value) 
0255     else:
0256         for fileTag, paramList in getXmlParameterMap(caloParams).items():
0257             createXML(paramList, 'MainProcessor' if fileTag.startswith('mp') else 'Demux', args.output_dir + '/algo_' + fileTag + '.xml')