CMS 3D CMS Logo

convertParamsToOnlineFormat.py
Go to the documentation of this file.
1 #!/bin/env python
2 
3 from __future__ import print_function
4 import argparse
5 import FWCore.ParameterSet.Config as cms
6 from importlib import import_module
7 import os
8 import sys
9 import xml.etree.ElementTree as ET
10 
11 import six
12 
13 # Pairwise generator: returns pairs of adjacent elements in a list / other iterable
14 def pairwiseGen(aList):
15  for i in xrange(len(aList)-1):
16  yield (aList[i], aList[i+1])
17 
18 def parseOfflineLUTfile(aRelPath, aExpectedSize, aPaddingValue = None, aTruncate = False):
19  # Find file by looking under directories listed in 'CMSSW_SEARCH_PATH' as outlined in https://twiki.cern.ch/twiki/bin/view/CMSPublic/SWGuideEdmFileInPath
20  searchPaths = os.getenv('CMSSW_SEARCH_PATH').split(':')
21  resolvedPath = None
22  for baseDir in searchPaths:
23  print("Looking for '" + aRelPath + "' under '" + baseDir + "'")
24  if os.path.isfile(os.path.join(baseDir, aRelPath)):
25  print(" success!")
26  resolvedPath = os.path.join(baseDir, aRelPath)
27  break
28  if resolvedPath is None:
29  raise RuntimeError("Could not find LUT file '" + aRelPath + "' under directories in 'CMSSW_SEARCH_PATH'")
30 
31  with open(resolvedPath) as f:
32  entries = []
33  line_nr = 0
34  for line in f:
35  line_nr += 1
36  # Ignore comment lines
37  if line.startswith('#') or line == '\n':
38  continue
39 
40  # Remove trailing comments from data lines
41  stripped_line = line[:line.find('#')]
42 
43  # Split line into list of whitespace-separated items
44  items = stripped_line.split()
45  if len(items) != 2:
46  print("ERROR parsing file", resolvedPath, "on line", line_nr, "'" + line + "' : Splitting on whitespace produced", len(items), "items")
47  sys.exit(1)
48 
49  entries.append( (int(items[0]), int(items[1])) )
50 
51  # Sort the LUT
52  entries.sort(key= lambda x : x[0])
53  # Check that the LUT is not empty
54  if len(entries) == 0:
55  print("ERROR parsing file", resolvedPath, ": No LUT entries defined in the file")
56  sys.exit(1)
57 
58  # Check that no items from the LUT are missing
59  if entries[0][0] != 0:
60  print("ERROR parsing file", resolvedPath, ": LUT entries before index", entries[0][0], "are not defined")
61  sys.exit(1)
62 
63  for x1, x2 in pairwiseGen(entries):
64  if x1[0] != (x2[0]-1):
65  print("ERROR parsing file", resolvedPath, ": ", x2[0] - x1[0] - 1,"LUT entries between indices", x1[0], "and", x2[0], "are not defined")
66  sys.exit(1)
67 
68  result = [x[1] for x in entries]
69 
70  if (len(result) < aExpectedSize) and not (aPaddingValue is None):
71  print ("WARNING : Padding", str(len(result))+"-entry LUT with value", aPaddingValue, "to have", aExpectedSize, "entries")
72  result += ([aPaddingValue] * (aExpectedSize - len(result)))
73  elif (len(result) > aExpectedSize) and aTruncate:
74  print ("WARNING : Truncating", str(len(result))+"-entry LUT to have", aExpectedSize, "entries")
75  result = result[0:aExpectedSize]
76  elif len(result) != aExpectedSize:
77  print ("ERROR parsing file", resolvedPath, ": Expected LUT of size", aExpectedSize, ", but", len(result), "entries were specified (and no padding/truncation requested)")
78  sys.exit(1)
79 
80  return result
81 
82 
84 
85  def divideByEgLsb(aParam):
86  return int(aParam.value() / aModule.egLsb.value())
87 
88  def divideByTauLsb(aParam):
89  return int(aParam.value() / aModule.tauLsb.value())
90 
91  def divideByJetLsb(aParam):
92  return int(aParam.value() / aModule.jetLsb.value())
93 
94 
95  result = [
96  (('mp_common', 'sdfile'), None, ''),
97  (('mp_common', 'algoRev'), None, ''),
98  (('mp_common', 'leptonSeedThreshold'), '2_ClusterSeedThreshold.mif', divideByEgLsb(aModule.egSeedThreshold)),
99  (('mp_common', 'leptonTowerThreshold'), '3_ClusterThreshold.mif', divideByEgLsb(aModule.egNeighbourThreshold)),
100  (('mp_common', 'pileUpTowerThreshold'), '4_PileUpThreshold.mif', 0x0)
101  ]
102 
103  result += [
104  (('mp_egamma', 'egammaRelaxationThreshold'), '10_EgRelaxThr.mif', divideByEgLsb(aModule.egMaxPtHOverE)),
105  (('mp_egamma', 'egammaMaxEta'), 'egammaMaxEta.mif', aModule.egEtaCut.value()),
106  (('mp_egamma', 'egammaBypassCuts'), 'BypassEgVeto.mif', bool(aModule.egBypassEGVetos.value())),
107  (('mp_egamma', 'egammaBypassShape'), 'BypassEgShape.mif', bool(aModule.egBypassShape.value())),
108  (('mp_egamma', 'egammaBypassEcalFG'), 'BypassEcalFG.mif', bool(aModule.egBypassECALFG.value())),
109  (('mp_egamma', 'egammaBypassExtendedHOverE'), '_BypassExtHE.mif', bool(aModule.egBypassExtHOverE)),
110  (('mp_egamma', 'egammaHOverECut_iEtaLT15'), '_RatioCutLt15.mif', aModule.egHOverEcutBarrel.value()),
111  (('mp_egamma', 'egammaHOverECut_iEtaGTEq15'), '_RatioCutGe15.mif', aModule.egHOverEcutEndcap.value()),
112  (('mp_egamma', 'egammaEnergyCalibLUT'), 'C_EgammaCalibration_12to18.mif', parseOfflineLUTfile(aModule.egCalibrationLUTFile.value(), 4096)),
113  (('mp_egamma', 'egammaIsoLUT1'), 'D_EgammaIsolation1_13to9.mif', parseOfflineLUTfile(aModule.egIsoLUTFile.value(), 8192)),
114  (('mp_egamma', 'egammaIsoLUT2'), 'D_EgammaIsolation2_13to9.mif', parseOfflineLUTfile(aModule.egIsoLUTFile2.value(), 8192))
115  ]
116 
117  result += [
118  (('mp_tau', 'tauMaxEta'), 'tauMaxEta.mif', aModule.isoTauEtaMax.value()),
119  (('mp_tau', 'tauEnergyCalibLUT'), 'I_TauCalibration_11to18.mif', parseOfflineLUTfile(aModule.tauCalibrationLUTFile.value(), 2048, 0x0)),
120  (('mp_tau', 'tauIsoLUT'), 'H_TauIsolation_12to9.mif', parseOfflineLUTfile(aModule.tauIsoLUTFile.value(), 4096)),
121  (('mp_tau', 'tauTrimmingLUT'), 'P_TauTrimming_13to8.mif', parseOfflineLUTfile(aModule.tauTrimmingShapeVetoLUTFile.value(), 8192))
122  ]
123 
124  result += [
125  (('mp_jet', 'jetSeedThreshold'), '1_JetSeedThreshold.mif', divideByJetLsb(aModule.jetSeedThreshold)),
126  (('mp_jet', 'jetMaxEta'), '6_JetEtaMax.mif', 0x00028),
127  (('mp_jet', 'jetBypassPileUpSub'), 'BypassJetPUS.mif', bool(aModule.jetBypassPUS.value())),
128  (('mp_jet', 'jetPUSUsePhiRing'), 'PhiRingPUS.mif', bool(aModule.jetPUSUsePhiRing.value())),
129  (('mp_jet', 'jetEnergyCalibLUT'), 'L_JetCalibration_11to18.mif', parseOfflineLUTfile(aModule.jetCalibrationLUTFile.value(), 2048)),
130  (('mp_jet', 'HTMHT_maxJetEta'), 'HTMHT_maxJetEta.mif', aModule.etSumEtaMax[1]), # assert == etSumEtaMax[3] ?
131  (('mp_jet', 'HT_jetThreshold'), '8_HtThreshold.mif', int(aModule.etSumEtThreshold[1] / aModule.etSumLsb.value())),
132  (('mp_jet', 'MHT_jetThreshold'), '9_MHtThreshold.mif', int(aModule.etSumEtThreshold[3] / aModule.etSumLsb.value())),
133  ]
134 
135  result += [
136  (('mp_sums', 'towerCountThreshold'), 'HeavyIonThr.mif', int(aModule.etSumEtThreshold[4] / aModule.etSumLsb.value()) ),
137  (('mp_sums', 'towerCountMaxEta'), 'HeavyIonEta.mif', aModule.etSumEtaMax[4]),
138  (('mp_sums', 'ETMET_maxTowerEta'), 'ETMET_maxTowerEta.mif', aModule.etSumEtaMax[0]), # assert == etSumEtaMax[2] ?
139  (('mp_sums', 'ecalET_towerThresholdLUT'), 'X_EcalTHR_11to9.mif', parseOfflineLUTfile(aModule.etSumEcalSumPUSLUTFile.value(), 2048, aTruncate = True)),
140  (('mp_sums', 'ET_towerThresholdLUT'), 'X_ETTHR_11to9.mif', parseOfflineLUTfile(aModule.etSumEttPUSLUTFile.value(), 2048, aTruncate = True)),
141  (('mp_sums', 'MET_towerThresholdLUT'), 'X_METTHR_11to9.mif', parseOfflineLUTfile(aModule.etSumMetPUSLUTFile.value(), 2048))
142  ]
143 
144  result += [
145  (('demux', 'sdfile'), None, ''),
146  (('demux', 'algoRev'), None, 0xcafe),
147  (('demux', 'ET_centralityLowerThresholds'), 'CentralityLowerThrs.mif', [ int(round(loBound / aModule.etSumLsb.value())) for loBound in aModule.etSumCentralityLower.value()]),
148  (('demux', 'ET_centralityUpperThresholds'), 'CentralityUpperThrs.mif', [ int(round(upBound / aModule.etSumLsb.value())) for upBound in aModule.etSumCentralityUpper.value()]),
149  (('demux', 'MET_energyCalibLUT'), 'M_METnoHFenergyCalibration_12to18.mif', parseOfflineLUTfile(aModule.metCalibrationLUTFile.value(), 4096, aTruncate = True)),
150  (('demux', 'METHF_energyCalibLUT'), 'M_METwithHFenergyCalibration_12to18.mif', parseOfflineLUTfile(aModule.metHFCalibrationLUTFile.value(), 4096, aTruncate = True)),
151  (('demux', 'ET_energyCalibLUT'), 'S_ETcalibration_12to18.mif', parseOfflineLUTfile(aModule.etSumEttCalibrationLUTFile.value(), 4096, aTruncate = True)),
152  (('demux', 'ecalET_energyCalibLUT'), 'R_EcalCalibration_12to18.mif', parseOfflineLUTfile(aModule.etSumEcalSumCalibrationLUTFile.value(), 4096, aTruncate = True)),
153  (('demux', 'MET_phiCalibLUT'), 'Q_METnoHFphiCalibration_12to18.mif', parseOfflineLUTfile(aModule.metPhiCalibrationLUTFile.value(), 4096, aTruncate = True)),
154  (('demux', 'METHF_phiCalibLUT'), 'Q_METwithHFphiCalibration_12to18.mif', parseOfflineLUTfile(aModule.metHFPhiCalibrationLUTFile.value(), 4096, aTruncate = True)),
155  ]
156 
157  result = [(a, b, parseOfflineLUTfile(c.value()) if isinstance(c, cms.FileInPath) else c) for a, b, c in result]
158 
159  return result
160 
161 
162 def getXmlParameterMap(aModule):
163  result = {}
164  for xmlDetails, mifName, value in getFullListOfParameters(aModule):
165  if xmlDetails is not None:
166  if xmlDetails[0] in result:
167  result[xmlDetails[0]] += [(xmlDetails[1], value)]
168  else:
169  result[xmlDetails[0]] = [(xmlDetails[1], value)]
170 
171  return result
172 
173 
174 def getMifParameterMap(aModule):
175 
176  fullList = getFullListOfParameters(aModule)
177 
178  return {mifFileName : value for (_, mifFileName, value) in fullList if mifFileName is not None}
179 
180 
181 # Stolen from https://stackoverflow.com/questions/3095434/inserting-newlines-in-xml-file-generated-via-xml-etree-elementtree-in-python
182 def indent(elem, level=0):
183  i = "\n" + level*" "
184  if len(elem):
185  if not elem.text or not elem.text.strip():
186  elem.text = i + " "
187  if not elem.tail or not elem.tail.strip():
188  elem.tail = i
189  for elem in elem:
190  indent(elem, level+1)
191  if not elem.tail or not elem.tail.strip():
192  elem.tail = i
193  else:
194  if level and (not elem.tail or not elem.tail.strip()):
195  elem.tail = i
196 
197 def createMIF(aFilePath, aValue):
198  print("Writing MIF file:", aFilePath)
199  with open(aFilePath, 'w') as f:
200  if isinstance(aValue, bool):
201  aValue = (1 if aValue else 0)
202 
203  if isinstance(aValue, int):
204  f.write( hex(aValue) )
205  elif isinstance(aValue, list):
206  f.write("\n".join([hex(x) for x in aValue]))
207  else:
208  raise RuntimeError("Do not know how to deal with parameter of type " + str(type(aValue)))
209 
210 
211 def createXML(parameters, contextId, outputFilePath):
212  topNode = ET.Element('algo', id='calol2')
213  contextNode = ET.SubElement(topNode, 'context', id=contextId)
214  for paramId, value in parameters:
215  if isinstance(value, bool):
216  ET.SubElement(contextNode, 'param', id=paramId, type='bool').text = str(value).lower()
217  elif isinstance(value, int):
218  ET.SubElement(contextNode, 'param', id=paramId, type='uint').text = "0x{0:05X}".format(value)
219  elif isinstance(value, str):
220  ET.SubElement(contextNode, 'param', id=paramId, type='string').text = value
221  elif isinstance(value, list):
222  ET.SubElement(contextNode, 'param', id=paramId, type='vector:uint').text = "\n " + ",\n ".join(["0x{0:05X}".format(x) for x in value]) + "\n "
223  else:
224  raise RuntimeError("Do not know how to deal with parameter '" + paramId + "' of type " + str(type(value)))
225  indent(topNode)
226 
227  print("Writing XML file:", outputFilePath)
228  with open(outputFilePath, 'w') as f:
229  f.write(ET.tostring(topNode))
230 
231 
232 
233 if __name__ == '__main__':
234 
235  parser = argparse.ArgumentParser()
236 
237  parser.add_argument('params_cfi', help='Name of CMSSW cfi python file specifying the values for the calo parameters')
238  parser.add_argument('output_dir', help='Directory for MIF/XML output files')
239 
240  outputFormatGroup = parser.add_mutually_exclusive_group(required=True)
241  outputFormatGroup.add_argument('--mif', action='store_true')
242  outputFormatGroup.add_argument('--xml', action='store_true')
243 
244  args = parser.parse_args()
245 
246  moduleName = 'L1Trigger.L1TCalorimeter.' + args.params_cfi
247  print("Importing calo params from module:", moduleName)
248  caloParams = import_module(moduleName).caloStage2Params
249 
250  print(caloParams.egCalibrationLUTFile.value())
251  print(caloParams.egIsoLUTFile.value())
252  print(caloParams.egIsoLUTFile2.value())
253  os.mkdir(args.output_dir)
254 
255  if args.mif:
256  for fileName, value in six.iteritems(getMifParameterMap(caloParams)):
257  createMIF(args.output_dir + '/' + fileName, value)
258  else:
259  for fileTag, paramList in six.iteritems(getXmlParameterMap(caloParams)):
260  createXML(paramList, 'MainProcessor' if fileTag.startswith('mp') else 'Demux', args.output_dir + '/algo_' + fileTag + '.xml')
electrons_cff.bool
bool
Definition: electrons_cff.py:372
convertParamsToOnlineFormat.indent
def indent(elem, level=0)
Definition: convertParamsToOnlineFormat.py:182
join
static std::string join(char **cmd)
Definition: RemoteFile.cc:17
cms::dd::split
std::vector< std::string_view > split(std::string_view, const char *)
convertParamsToOnlineFormat.parseOfflineLUTfile
def parseOfflineLUTfile(aRelPath, aExpectedSize, aPaddingValue=None, aTruncate=False)
Definition: convertParamsToOnlineFormat.py:18
str
#define str(s)
Definition: TestProcessor.cc:48
convertParamsToOnlineFormat.pairwiseGen
def pairwiseGen(aList)
Definition: convertParamsToOnlineFormat.py:14
createfilelist.int
int
Definition: createfilelist.py:10
edm::print
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:66
convertParamsToOnlineFormat.getMifParameterMap
def getMifParameterMap(aModule)
Definition: convertParamsToOnlineFormat.py:174
convertParamsToOnlineFormat.createMIF
def createMIF(aFilePath, aValue)
Definition: convertParamsToOnlineFormat.py:197
convertParamsToOnlineFormat.createXML
def createXML(parameters, contextId, outputFilePath)
Definition: convertParamsToOnlineFormat.py:211
format
convertParamsToOnlineFormat.getXmlParameterMap
def getXmlParameterMap(aModule)
Definition: convertParamsToOnlineFormat.py:162
convertParamsToOnlineFormat.getFullListOfParameters
def getFullListOfParameters(aModule)
Definition: convertParamsToOnlineFormat.py:83