CMS 3D CMS Logo

genericValidation.py
Go to the documentation of this file.
1 from __future__ import print_function
2 from __future__ import absolute_import
3 from builtins import range
4 from abc import ABCMeta, abstractmethod, abstractproperty
5 import os
6 import re
7 import json
8 from . import globalDictionaries
9 from . import configTemplates
10 from .dataset import Dataset
11 from .helperFunctions import replaceByMap, addIndex, getCommandOutput2, boolfromstring, pythonboolstring
12 from .TkAlExceptions import AllInOneError
13 
14 class ValidationMetaClass(ABCMeta):
15  sets = ["mandatories", "optionals", "needpackages"]
16  dicts = ["defaults"]
17  def __new__(cls, clsname, bases, dct):
18  for setname in cls.sets:
19  if setname not in dct: dct[setname] = set()
20  dct[setname] = set.union(dct[setname], *(getattr(base, setname) for base in bases if hasattr(base, setname)))
21 
22  for dictname in cls.dicts:
23  if dictname not in dct: dct[dictname] = {}
24  for base in bases:
25  if not hasattr(base, dictname): continue
26  newdict = getattr(base, dictname)
27  for key in set(newdict) & set(dct[dictname]):
28  if newdict[key] != dct[dictname][key]:
29  raise ValueError("Inconsistent values of defaults[{}]: {}, {}".format(key, newdict[key], dct[dictname][key]))
30  dct[dictname].update(newdict)
31 
32  for setname in cls.sets: #e.g. removemandatories, used in preexistingvalidation
33  #use with caution
34  if "remove"+setname not in dct: dct["remove"+setname] = set()
35  dct["remove"+setname] = set.union(dct["remove"+setname], *(getattr(base, "remove"+setname) for base in bases if hasattr(base, "remove"+setname)))
36 
37  dct[setname] -= dct["remove"+setname]
38 
39  return super(ValidationMetaClass, cls).__new__(cls, clsname, bases, dct)
40 
42  __metaclass__ = ValidationMetaClass
43  defaultReferenceName = "DEFAULT"
44  mandatories = set()
45  defaults = {
46  "cmssw": os.environ['CMSSW_BASE'],
47  "parallelJobs": "1",
48  "jobid": "",
49  "needsproxy": "false",
50  }
51  needpackages = {"Alignment/OfflineValidation"}
52  optionals = {"jobmode"}
53 
54  def __init__(self, valName, alignment, config):
55  import random
56  self.name = valName
57  self.alignmentToValidate = alignment
58  self.general = config.getGeneral()
59  self.randomWorkdirPart = "%0i"%random.randint(1,10e9)
60  self.configFiles = []
61  self.config = config
62  self.jobid = ""
63 
64  theUpdate = config.getResultingSection(self.valType+":"+self.name,
65  defaultDict = self.defaults,
66  demandPars = self.mandatories)
67  self.general.update(theUpdate)
68  self.jobmode = self.general["jobmode"]
69  self.NJobs = int(self.general["parallelJobs"])
70  self.needsproxy = boolfromstring(self.general["needsproxy"], "needsproxy")
71 
72  # limit maximum number of parallel jobs to 40
73  # (each output file is approximately 20MB)
74  maximumNumberJobs = 40
75  if self.NJobs > maximumNumberJobs:
76  msg = ("Maximum allowed number of parallel jobs "
77  +str(maximumNumberJobs)+" exceeded!!!")
78  raise AllInOneError(msg)
79  if self.NJobs > 1 and not isinstance(self, ParallelValidation):
80  raise AllInOneError("Parallel jobs not implemented for {}!\n"
81  "Please set parallelJobs = 1.".format(type(self).__name__))
82 
83  self.jobid = self.general["jobid"]
84  if self.jobid:
85  try: #make sure it's actually a valid jobid
86  output = getCommandOutput2("bjobs %(jobid)s 2>&1"%self.general)
87  if "is not found" in output: raise RuntimeError
88  except RuntimeError:
89  raise AllInOneError("%s is not a valid jobid.\nMaybe it finished already?"%self.jobid)
90 
91  self.cmssw = self.general["cmssw"]
92  badcharacters = r"\'"
93  for character in badcharacters:
94  if character in self.cmssw:
95  raise AllInOneError("The bad characters " + badcharacters + " are not allowed in the cmssw\n"
96  "path name. If you really have it in such a ridiculously named location,\n"
97  "try making a symbolic link somewhere with a decent name.")
98  try:
99  os.listdir(self.cmssw)
100  except OSError:
101  raise AllInOneError("Your cmssw release " + self.cmssw + ' does not exist')
102 
103  if self.cmssw == os.environ["CMSSW_BASE"]:
104  self.scramarch = os.environ["SCRAM_ARCH"]
105  self.cmsswreleasebase = os.environ["CMSSW_RELEASE_BASE"]
106  else:
107  command = ("cd '" + self.cmssw + "' && eval `scramv1 ru -sh 2> /dev/null`"
108  ' && echo "$CMSSW_BASE\n$SCRAM_ARCH\n$CMSSW_RELEASE_BASE"')
109  commandoutput = getCommandOutput2(command).split('\n')
110  self.cmssw = commandoutput[0]
111  self.scramarch = commandoutput[1]
112  self.cmsswreleasebase = commandoutput[2]
113 
114  self.packages = {}
115  for package in self.needpackages:
116  for placetolook in self.cmssw, self.cmsswreleasebase:
117  pkgpath = os.path.join(placetolook, "src", package)
118  if os.path.exists(pkgpath):
119  self.packages[package] = pkgpath
120  break
121  else:
122  raise AllInOneError("Package {} does not exist in {} or {}!".format(package, self.cmssw, self.cmsswreleasebase))
123 
124  self.AutoAlternates = True
125  if config.has_option("alternateTemplates","AutoAlternates"):
126  try:
127  self.AutoAlternates = json.loads(config.get("alternateTemplates","AutoAlternates").lower())
128  except ValueError:
129  raise AllInOneError("AutoAlternates needs to be true or false, not %s" % config.get("alternateTemplates","AutoAlternates"))
130 
131  knownOpts = set(self.defaults.keys())|self.mandatories|self.optionals
132  ignoreOpts = []
133  config.checkInput(self.valType+":"+self.name,
134  knownSimpleOptions = knownOpts,
135  ignoreOptions = ignoreOpts)
136 
137  def getRepMap(self, alignment = None):
138  from .plottingOptions import PlottingOptions
139  if alignment == None:
140  alignment = self.alignmentToValidate
141  try:
142  result = PlottingOptions(self.config, self.valType)
143  except KeyError:
144  result = {}
145  result.update(alignment.getRepMap())
146  result.update(self.general)
147  result.update({
148  "workdir": os.path.join(self.general["workdir"],
149  self.randomWorkdirPart),
150  "datadir": self.general["datadir"],
151  "logdir": self.general["logdir"],
152  "CommandLineTemplate": ("#run configfile and post-proccess it\n"
153  "cmsRun %(cfgFile)s\n"
154  "%(postProcess)s "),
155  "CMSSW_BASE": self.cmssw,
156  "SCRAM_ARCH": self.scramarch,
157  "CMSSW_RELEASE_BASE": self.cmsswreleasebase,
158  "alignmentName": alignment.name,
159  "condLoad": alignment.getConditions(),
160  "LoadGlobalTagTemplate": configTemplates.loadGlobalTagTemplate,
161  })
162  result.update(self.packages)
163  return result
164 
165  @abstractproperty
166  def filesToCompare(self):
167  pass
168 
169  def getCompareStrings( self, requestId = None, plain = False ):
170  result = {}
171  repMap = self.getRepMap().copy()
172  for validationId in self.filesToCompare:
173  repMap["file"] = self.filesToCompare[ validationId ]
174  if repMap["file"].startswith( "/castor/" ):
175  repMap["file"] = "rfio:%(file)s"%repMap
176  elif repMap["file"].startswith( "/store/" ):
177  repMap["file"] = "root://eoscms.cern.ch//eos/cms%(file)s"%repMap
178  if plain:
179  result[validationId]=repMap["file"]
180  else:
181  result[validationId]= "%(file)s=%(title)s|%(color)s|%(style)s"%repMap
182  if requestId == None:
183  return result
184  else:
185  if not "." in requestId:
186  requestId += ".%s"%self.defaultReferenceName
187  if not requestId.split(".")[-1] in result:
188  msg = ("could not find %s in reference Objects!"
189  %requestId.split(".")[-1])
190  raise AllInOneError(msg)
191  return result[ requestId.split(".")[-1] ]
192 
193  def createFiles(self, fileContents, path, repMap = None, repMaps = None):
194  """repMap: single map for all files
195  repMaps: a dict, with the filenames as the keys"""
196  if repMap is not None and repMaps is not None:
197  raise AllInOneError("createFiles can only take repMap or repMaps (or neither), not both")
198  result = []
199  for fileName in fileContents:
200  filePath = os.path.join(path, fileName)
201  result.append(filePath)
202 
203  for (i, filePathi) in enumerate(addIndex(filePath, self.NJobs)):
204  theFile = open( filePathi, "w" )
205  fileContentsi = fileContents[ fileName ]
206  if repMaps is not None:
207  repMap = repMaps[fileName]
208  if repMap is not None:
209  repMap.update({"nIndex": str(i)})
210  fileContentsi = replaceByMap(fileContentsi, repMap)
211  theFile.write( fileContentsi )
212  theFile.close()
213 
214  return result
215 
216  def createConfiguration(self, fileContents, path, schedule = None, repMap = None, repMaps = None):
217  self.configFiles = self.createFiles(fileContents,
218  path, repMap = repMap, repMaps = repMaps)
219  if not schedule == None:
220  schedule = [os.path.join( path, cfgName) for cfgName in schedule]
221  for cfgName in schedule:
222  if not cfgName in self.configFiles:
223  msg = ("scheduled %s missing in generated configfiles: %s"
224  %(cfgName, self.configFiles))
225  raise AllInOneError(msg)
226  for cfgName in self.configFiles:
227  if not cfgName in schedule:
228  msg = ("generated configuration %s not scheduled: %s"
229  %(cfgName, schedule))
230  raise AllInOneError(msg)
231  self.configFiles = schedule
232  return self.configFiles
233 
234  def createScript(self, fileContents, path, downloadFiles=[], repMap = None, repMaps = None):
235  self.scriptFiles = self.createFiles(fileContents,
236  path, repMap = repMap, repMaps = repMaps)
237  for script in self.scriptFiles:
238  for scriptwithindex in addIndex(script, self.NJobs):
239  os.chmod(scriptwithindex,0o755)
240  return self.scriptFiles
241 
242  def createCrabCfg(self, fileContents, path ):
243  if self.NJobs > 1:
244  msg = ("jobmode 'crab' not supported for parallel validation."
245  " Please set parallelJobs = 1.")
246  raise AllInOneError(msg)
247  self.crabConfigFiles = self.createFiles(fileContents, path)
248  return self.crabConfigFiles
249 
250 
252  """
253  Subclass of `GenericValidation` which is the base for validations using
254  datasets.
255  """
256  needParentFiles = False
257  mandatories = {"dataset", "maxevents"}
258  defaults = {
259  "runRange": "",
260  "firstRun": "",
261  "lastRun": "",
262  "begin": "",
263  "end": "",
264  "JSON": "",
265  "dasinstance": "prod/global",
266  "ttrhbuilder":"WithAngleAndTemplate",
267  "usepixelqualityflag": "True",
268  }
269  optionals = {"magneticfield"}
270 
271  def __init__(self, valName, alignment, config):
272  """
273  This method adds additional items to the `self.general` dictionary
274  which are only needed for validations using datasets.
275 
276  Arguments:
277  - `valName`: String which identifies individual validation instances
278  - `alignment`: `Alignment` instance to validate
279  - `config`: `BetterConfigParser` instance which includes the
280  configuration of the validations
281  """
282 
283  super(GenericValidationData, self).__init__(valName, alignment, config)
284 
285  # if maxevents is not specified, cannot calculate number of events for
286  # each parallel job, and therefore running only a single job
287  if int( self.general["maxevents"] ) < 0 and self.NJobs > 1:
288  msg = ("Maximum number of events (maxevents) not specified: "
289  "cannot use parallel jobs.")
290  raise AllInOneError(msg)
291  if int( self.general["maxevents"] ) / self.NJobs != float( self.general["maxevents"] ) / self.NJobs:
292  msg = ("maxevents has to be divisible by parallelJobs")
293  raise AllInOneError(msg)
294 
295  tryPredefinedFirst = (not self.jobmode.split( ',' )[0] == "crab" and self.general["JSON"] == ""
296  and self.general["firstRun"] == "" and self.general["lastRun"] == ""
297  and self.general["begin"] == "" and self.general["end"] == "")
298 
299  if self.general["dataset"] not in globalDictionaries.usedDatasets:
300  globalDictionaries.usedDatasets[self.general["dataset"]] = {}
301 
302  if self.cmssw not in globalDictionaries.usedDatasets[self.general["dataset"]]:
303  if globalDictionaries.usedDatasets[self.general["dataset"]] != {}:
304  print(("Warning: you use the same dataset '%s' in multiple cmssw releases.\n"
305  "This is allowed, but make sure it's not a mistake") % self.general["dataset"])
306  globalDictionaries.usedDatasets[self.general["dataset"]][self.cmssw] = {False: None, True: None}
307 
308  Bfield = self.general.get("magneticfield", None)
309  if globalDictionaries.usedDatasets[self.general["dataset"]][self.cmssw][tryPredefinedFirst] is None:
310  dataset = Dataset(
311  self.general["dataset"], tryPredefinedFirst = tryPredefinedFirst,
312  cmssw = self.cmssw, cmsswrelease = self.cmsswreleasebase, magneticfield = Bfield,
313  dasinstance = self.general["dasinstance"])
314  globalDictionaries.usedDatasets[self.general["dataset"]][self.cmssw][tryPredefinedFirst] = dataset
315  if tryPredefinedFirst and not dataset.predefined(): #No point finding the data twice in that case
316  globalDictionaries.usedDatasets[self.general["dataset"]][self.cmssw][False] = dataset
317 
318  self.dataset = globalDictionaries.usedDatasets[self.general["dataset"]][self.cmssw][tryPredefinedFirst]
319  self.general["magneticField"] = self.dataset.magneticField()
320  self.general["defaultMagneticField"] = "MagneticField"
321  if self.general["magneticField"] == "unknown":
322  print("Could not get the magnetic field for this dataset.")
323  print("Using the default: ", self.general["defaultMagneticField"])
324  self.general["magneticField"] = '.oO[defaultMagneticField]Oo.'
325 
326  if not self.jobmode.split( ',' )[0] == "crab":
327  try:
328  self.general["datasetDefinition"] = self.dataset.datasetSnippet(
329  jsonPath = self.general["JSON"],
330  firstRun = self.general["firstRun"],
331  lastRun = self.general["lastRun"],
332  begin = self.general["begin"],
333  end = self.general["end"],
334  parent = self.needParentFiles )
335  except AllInOneError as e:
336  msg = "In section [%s:%s]: "%(self.valType, self.name)
337  msg += str(e)
338  raise AllInOneError(msg)
339  else:
340  if self.dataset.predefined():
341  msg = ("For jobmode 'crab' you cannot use predefined datasets "
342  "(in your case: '%s')."%( self.dataset.name() ))
343  raise AllInOneError( msg )
344  try:
345  theUpdate = config.getResultingSection(self.valType+":"+self.name,
346  demandPars = ["parallelJobs"])
347  except AllInOneError as e:
348  msg = str(e)[:-1]+" when using 'jobmode: crab'."
349  raise AllInOneError(msg)
350  self.general.update(theUpdate)
351  if self.general["begin"] or self.general["end"]:
352  ( self.general["begin"],
353  self.general["end"],
354  self.general["firstRun"],
355  self.general["lastRun"] ) = self.dataset.convertTimeToRun(
356  firstRun = self.general["firstRun"],
357  lastRun = self.general["lastRun"],
358  begin = self.general["begin"],
359  end = self.general["end"],
360  shortTuple = False)
361  if self.general["begin"] == None:
362  self.general["begin"] = ""
363  if self.general["end"] == None:
364  self.general["end"] = ""
365  self.general["firstRun"] = str( self.general["firstRun"] )
366  self.general["lastRun"] = str( self.general["lastRun"] )
367  if ( not self.general["firstRun"] ) and \
368  ( self.general["end"] or self.general["lastRun"] ):
369  self.general["firstRun"] = str(
370  self.dataset.runList()[0]["run_number"])
371  if ( not self.general["lastRun"] ) and \
372  ( self.general["begin"] or self.general["firstRun"] ):
373  self.general["lastRun"] = str(
374  self.dataset.runList()[-1]["run_number"])
375  if self.general["firstRun"] and self.general["lastRun"]:
376  if int(self.general["firstRun"]) > int(self.general["lastRun"]):
377  msg = ( "The lower time/runrange limit ('begin'/'firstRun') "
378  "chosen is greater than the upper time/runrange limit "
379  "('end'/'lastRun').")
380  raise AllInOneError( msg )
381  self.general["runRange"] = (self.general["firstRun"]
382  + '-' + self.general["lastRun"])
383  try:
384  self.general["datasetDefinition"] = self.dataset.datasetSnippet(
385  jsonPath = self.general["JSON"],
386  firstRun = self.general["firstRun"],
387  lastRun = self.general["lastRun"],
388  begin = self.general["begin"],
389  end = self.general["end"],
390  crab = True )
391  except AllInOneError as e:
392  msg = "In section [%s:%s]: "%(self.valType, self.name)
393  msg += str( e )
394  raise AllInOneError( msg )
395 
396  self.general["usepixelqualityflag"] = pythonboolstring(self.general["usepixelqualityflag"], "usepixelqualityflag")
397 
398  def getRepMap(self, alignment = None):
399  result = super(GenericValidationData, self).getRepMap(alignment)
400  outputfile = os.path.expandvars(replaceByMap(
401  "%s_%s_.oO[name]Oo..root" % (self.outputBaseName, self.name)
402  , result))
403  resultfile = os.path.expandvars(replaceByMap(("/store/group/alca_trackeralign/AlignmentValidation/.oO[eosdir]Oo./" +
404  "%s_%s_.oO[name]Oo..root" % (self.resultBaseName, self.name))
405  , result))
406  result.update({
407  "resultFile": ".oO[resultFiles[.oO[nIndex]Oo.]]Oo.",
408  "resultFiles": addIndex(resultfile, self.NJobs),
409  "finalResultFile": resultfile,
410  "outputFile": ".oO[outputFiles[.oO[nIndex]Oo.]]Oo.",
411  "outputFiles": addIndex(outputfile, self.NJobs),
412  "finalOutputFile": outputfile,
413  "ProcessName": self.ProcessName,
414  "Bookkeeping": self.Bookkeeping,
415  "LoadBasicModules": self.LoadBasicModules,
416  "TrackSelectionRefitting": self.TrackSelectionRefitting,
417  "ValidationConfig": self.ValidationTemplate,
418  "FileOutputTemplate": self.FileOutputTemplate,
419  "DefinePath": self.DefinePath,
420  })
421  return result
422 
423  @property
424  def cfgName(self):
425  return "%s.%s.%s_cfg.py"%( self.configBaseName, self.name,
426  self.alignmentToValidate.name )
427  @abstractproperty
428  def ProcessName(self):
429  pass
430 
431  @property
432  def cfgTemplate(self):
433  return configTemplates.cfgTemplate
434 
435  @abstractproperty
437  pass
438 
439  @property
440  def filesToCompare(self):
441  return {self.defaultReferenceName: self.getRepMap()["finalResultFile"]}
442 
443  def createConfiguration(self, path ):
444  repMap = self.getRepMap()
445  cfgs = {self.cfgName: self.cfgTemplate}
446  super(GenericValidationData, self).createConfiguration(cfgs, path, repMap=repMap)
447 
448  def createScript(self, path, template = configTemplates.scriptTemplate, downloadFiles=[], repMap = None, repMaps = None):
449  scriptName = "%s.%s.%s.sh"%(self.scriptBaseName, self.name,
450  self.alignmentToValidate.name )
451  if repMap is None and repMaps is None:
452  repMap = self.getRepMap()
453  repMap["CommandLine"]=""
454  for cfg in self.configFiles:
455  repMap["CommandLine"]+= repMap["CommandLineTemplate"]%{"cfgFile":addIndex(cfg, self.NJobs, ".oO[nIndex]Oo."),
456  "postProcess":""
457  }
458  scripts = {scriptName: template}
459  return super(GenericValidationData, self).createScript(scripts, path, downloadFiles = downloadFiles,
460  repMap = repMap, repMaps = repMaps)
461 
462  def createCrabCfg(self, path, crabCfgBaseName):
463  """
464  Method which creates a `crab.cfg` for a validation on datasets.
465 
466  Arguments:
467  - `path`: Path at which the file will be stored.
468  - `crabCfgBaseName`: String which depends on the actual type of
469  validation calling this method.
470  """
471  crabCfgName = "crab.%s.%s.%s.cfg"%( crabCfgBaseName, self.name,
472  self.alignmentToValidate.name )
473  repMap = self.getRepMap()
474  repMap["script"] = "dummy_script.sh"
475  # repMap["crabOutputDir"] = os.path.basename( path )
476  repMap["crabWorkingDir"] = crabCfgName.split( '.cfg' )[0]
477  self.crabWorkingDir = repMap["crabWorkingDir"]
478  repMap["numberOfJobs"] = self.general["parallelJobs"]
479  repMap["cfgFile"] = self.configFiles[0]
480  repMap["queue"] = self.jobmode.split( ',' )[1].split( '-q' )[1]
481  if self.dataset.dataType() == "mc":
482  repMap["McOrData"] = "events = .oO[nEvents]Oo."
483  elif self.dataset.dataType() == "data":
484  repMap["McOrData"] = "lumis = -1"
485  if self.jobmode.split( ',' )[0] == "crab":
486  print ("For jobmode 'crab' the parameter 'maxevents' will be "
487  "ignored and all events will be processed.")
488  else:
489  raise AllInOneError("Unknown data type! Can't run in crab mode")
490  crabCfg = {crabCfgName: replaceByMap( configTemplates.crabCfgTemplate,
491  repMap ) }
492  return super(GenericValidationData, self).createCrabCfg( crabCfg, path )
493 
494  @property
495  def Bookkeeping(self):
496  return configTemplates.Bookkeeping
497  @property
498  def LoadBasicModules(self):
499  return configTemplates.LoadBasicModules
500  @abstractproperty
502  pass
503  @property
505  return configTemplates.FileOutputTemplate
506  @abstractproperty
507  def DefinePath(self):
508  pass
509 
510 class GenericValidationData_CTSR(GenericValidationData):
511  #common track selection and refitting
512  defaults = {
513  "momentumconstraint": "None",
514  "openmasswindow": "False",
515  "cosmicsdecomode": "True",
516  "removetrackhitfiltercommands": "",
517  "appendtrackhitfiltercommands": "",
518  }
519  def getRepMap(self, alignment=None):
520  result = super(GenericValidationData_CTSR, self).getRepMap(alignment)
521 
522  from .trackSplittingValidation import TrackSplittingValidation
523  result.update({
524  "ValidationSequence": self.ValidationSequence,
525  "istracksplitting": str(isinstance(self, TrackSplittingValidation)),
526  "cosmics0T": str(self.cosmics0T),
527  "use_d0cut": str(self.use_d0cut),
528  "ispvvalidation": str(self.isPVValidation)
529  })
530 
531  commands = []
532  for removeorappend in "remove", "append":
533  optionname = removeorappend + "trackhitfiltercommands"
534  if result[optionname]:
535  for command in result[optionname].split(","):
536  command = command.strip()
537  commands.append('process.TrackerTrackHitFilter.commands.{}("{}")'.format(removeorappend, command))
538  result["trackhitfiltercommands"] = "\n".join(commands)
539 
540  return result
541  @property
542  def use_d0cut(self):
543  return "Cosmics" not in self.general["trackcollection"] #use it for collisions only
544  @property
545  def isPVValidation(self):
546  return False # only for PV Validation sequence
547  @property
549  return configTemplates.CommonTrackSelectionRefitting
550  @property
551  def DefinePath(self):
552  return configTemplates.DefinePath_CommonSelectionRefitting
553  @abstractproperty
555  pass
556  @property
557  def cosmics0T(self):
558  if "Cosmics" not in self.general["trackcollection"]: return False
559  Bfield = self.dataset.magneticFieldForRun()
560  if Bfield < 0.5: return True
561  if isinstance(Bfield, str):
562  if "unknown " in Bfield:
563  msg = Bfield.replace("unknown ","",1)
564  elif Bfield == "unknown":
565  msg = "Can't get the B field for %s." % self.dataset.name()
566  else:
567  msg = "B field = {}???".format(Bfield)
568  raise AllInOneError(msg + "\n"
569  "To use this dataset, specify magneticfield = [value] in your .ini config file.")
570  return False
571 
573  @classmethod
574  def initMerge(cls):
575  return ""
576  @abstractmethod
577  def appendToMerge(self):
578  pass
579 
580  @classmethod
581  def doInitMerge(cls):
582  from .plottingOptions import PlottingOptions
583  result = cls.initMerge()
584  result = replaceByMap(result, PlottingOptions(None, cls))
585  if result and result[-1] != "\n": result += "\n"
586  return result
587  def doMerge(self):
588  result = self.appendToMerge()
589  if result[-1] != "\n": result += "\n"
590  result += ("if [[ tmpMergeRetCode -eq 0 ]]; then\n"
591  " xrdcp -f .oO[finalOutputFile]Oo. root://eoscms//eos/cms.oO[finalResultFile]Oo.\n"
592  "fi\n"
593  "if [[ ${tmpMergeRetCode} -gt ${mergeRetCode} ]]; then\n"
594  " mergeRetCode=${tmpMergeRetCode}\n"
595  "fi\n")
596  result = replaceByMap(result, self.getRepMap())
597  return result
598 
600  @classmethod
601  def runPlots(cls, validations):
602  return ("rfcp .oO[plottingscriptpath]Oo. .\n"
603  "root -x -b -q .oO[plottingscriptname]Oo.++")
604  @abstractmethod
605  def appendToPlots(self):
606  pass
607  @abstractmethod
609  """override with a classmethod"""
610  @abstractmethod
612  """override with a classmethod"""
613  @abstractmethod
614  def plotsdirname(cls):
615  """override with a classmethod"""
616 
617  @classmethod
618  def doRunPlots(cls, validations):
619  from .plottingOptions import PlottingOptions
620  cls.createPlottingScript(validations)
621  result = cls.runPlots(validations)
622  result = replaceByMap(result, PlottingOptions(None, cls))
623  if result and result[-1] != "\n": result += "\n"
624  return result
625  @classmethod
626  def createPlottingScript(cls, validations):
627  from .plottingOptions import PlottingOptions
628  repmap = PlottingOptions(None, cls).copy()
629  filename = replaceByMap(".oO[plottingscriptpath]Oo.", repmap)
630  repmap["PlottingInstantiation"] = "\n".join(
631  replaceByMap(v.appendToPlots(), v.getRepMap()).rstrip("\n")
632  for v in validations
633  )
634  plottingscript = replaceByMap(cls.plottingscripttemplate(), repmap)
635  with open(filename, 'w') as f:
636  f.write(plottingscript)
637 
640  def __init__(self, name, values, format=None, latexname=None, latexformat=None):
641  """
642  name: name of the summary item, goes on top of the column
643  values: value for each alignment (in order of rows)
644  format: python format string (default: {:.3g}, meaning up to 3 significant digits)
645  latexname: name in latex form, e.g. if name=sigma you might want latexname=\sigma (default: name)
646  latexformat: format for latex (default: format)
647  """
648  if format is None: format = "{:.3g}"
649  if latexname is None: latexname = name
650  if latexformat is None: latexformat = format
651 
652  self.__name = name
653  self.__values = values
654  self.__format = format
655  self.__latexname = latexname
656  self.__latexformat = latexformat
657 
658  def name(self, latex=False):
659  if latex:
660  return self.__latexname
661  else:
662  return self.__name
663 
664  def format(self, value, latex=False):
665  if latex:
666  fmt = self.__latexformat
667  else:
668  fmt = self.__format
669  if re.match(".*[{][^}]*[fg][}].*", fmt):
670  value = float(value)
671  return fmt.format(value)
672 
673  def values(self, latex=False):
674  result = [self.format(v, latex=latex) for v in self.__values]
675  return result
676 
677  def value(self, i, latex):
678  return self.values(latex)[i]
679 
680  @abstractmethod
681  def getsummaryitems(cls, folder):
682  """override with a classmethod that returns a list of SummaryItems
683  based on the plots saved in folder"""
684 
685  __summaryitems = None
686  __lastfolder = None
687 
688  @classmethod
689  def summaryitemsstring(cls, folder=None, latex=False, transpose=True):
690  if folder is None: folder = cls.plotsdirname()
691  if folder.startswith( "/castor/" ):
692  folder = "rfio:%(file)s"%repMap
693  elif folder.startswith( "/store/" ):
694  folder = "root://eoscms.cern.ch//eos/cms%(file)s"%repMap
695 
696  if cls.__summaryitems is None or cls.__lastfolder != folder:
697  cls.__lastfolder = folder
698  cls.__summaryitems = cls.getsummaryitems(folder)
699 
700  summaryitems = cls.__summaryitems
701 
702  if not summaryitems:
703  raise AllInOneError("No summary items!")
704  size = {len(_.values(latex)) for _ in summaryitems}
705  if len(size) != 1:
706  raise AllInOneError("Some summary items have different numbers of values\n{}".format(size))
707  size = size.pop()
708 
709  if transpose:
710  columnwidths = ([max(len(_.name(latex)) for _ in summaryitems)]
711  + [max(len(_.value(i, latex)) for _ in summaryitems) for i in range(size)])
712  else:
713  columnwidths = [max(len(entry) for entry in [_.name(latex)] + _.values(latex)) for _ in summaryitems]
714 
715  if latex:
716  join = " & "
717  else:
718  join = " "
719  row = join.join("{{:{}}}".format(width) for width in columnwidths)
720 
721  if transpose:
722  rows = [row.format(*[_.name(latex)]+_.values(latex)) for _ in summaryitems]
723  else:
724  rows = []
725  rows.append(row.format(*(_.name for _ in summaryitems)))
726  for i in range(size):
727  rows.append(row.format(*(_.value(i, latex) for _ in summaryitems)))
728 
729  if latex:
730  join = " \\\\\n"
731  else:
732  join = "\n"
733  result = join.join(rows)
734  if latex:
735  result = (r"\begin{{tabular}}{{{}}}".format("|" + "|".join("c"*(len(columnwidths))) + "|") + "\n"
736  + result + "\n"
737  + r"\end{tabular}")
738  return result
739 
740  @classmethod
741  def printsummaryitems(cls, *args, **kwargs):
742  print(cls.summaryitemsstring(*args, **kwargs))
743  @classmethod
744  def writesummaryitems(cls, filename, *args, **kwargs):
745  with open(filename, "w") as f:
746  f.write(cls.summaryitemsstring(*args, **kwargs)+"\n")
747 
749  @classmethod
750  def getsummaryitems(cls, folder):
751  result = []
752  with open(os.path.join(folder, "{}Summary.txt".format(cls.__name__))) as f:
753  for line in f:
754  split = line.rstrip("\n").split("\t")
755  kwargs = {}
756  for thing in split[:]:
757  if thing.startswith("format="):
758  kwargs["format"] = thing.replace("format=", "", 1)
759  split.remove(thing)
760  if thing.startswith("latexname="):
761  kwargs["latexname"] = thing.replace("latexname=", "", 1)
762  split.remove(thing)
763  if thing.startswith("latexformat="):
764  kwargs["latexformat"] = thing.replace("latexformat=", "", 1)
765  split.remove(thing)
766 
767  name = split[0]
768  values = split[1:]
769  result.append(cls.SummaryItem(name, values, **kwargs))
770  return result
771 
773  @classmethod
774  def doComparison(cls, validations):
775  from .plottingOptions import PlottingOptions
776  repmap = PlottingOptions(None, cls).copy()
777  repmap["compareStrings"] = " , ".join(v.getCompareStrings("OfflineValidation") for v in validations)
778  repmap["compareStringsPlain"] = " , ".join(v.getCompareStrings("OfflineValidation", True) for v in validations)
779  comparison = replaceByMap(cls.comparisontemplate(), repmap)
780  return comparison
781 
782  @classmethod
784  return configTemplates.compareAlignmentsExecution
785  @classmethod
787  return ".oO[Alignment/OfflineValidation]Oo./scripts/.oO[compareAlignmentsName]Oo."
788  @abstractmethod
790  """classmethod"""
791 
792 class ValidationForPresentation(ValidationWithPlots):
793  @abstractmethod
795  """classmethod"""
def __init__(self, valName, alignment, config)
def pythonboolstring(string, name)
def copy(args, dbName)
def createConfiguration(self, fileContents, path, schedule=None, repMap=None, repMaps=None)
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:66
def getCommandOutput2(command)
def createScript(self, fileContents, path, downloadFiles=[], repMap=None, repMaps=None)
def writesummaryitems(cls, filename, args, kwargs)
def __init__(self, valName, alignment, config)
def addIndex(filename, njobs, index=None)
def createCrabCfg(self, path, crabCfgBaseName)
def __new__(cls, clsname, bases, dct)
def summaryitemsstring(cls, folder=None, latex=False, transpose=True)
def PlottingOptions(config, valType)
def replaceByMap(target, the_map)
— Helpers —############################
def createFiles(self, fileContents, path, repMap=None, repMaps=None)
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
def __init__(self, name, values, format=None, latexname=None, latexformat=None)
def boolfromstring(string, name)
def createScript(self, path, template=configTemplates.scriptTemplate, downloadFiles=[], repMap=None, repMaps=None)
def createCrabCfg(self, fileContents, path)
#define update(a, b)
def getRepMap(self, alignment=None)
def getCompareStrings(self, requestId=None, plain=False)
#define str(s)
double split
Definition: MVATrainer.cc:139