CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
validation.py
Go to the documentation of this file.
1 import os
2 import re
3 import sys
4 import shutil
5 import subprocess
6 
7 import ROOT
8 ROOT.gROOT.SetBatch(True)
9 ROOT.PyConfig.IgnoreCommandLineOptions = True
10 
11 import plotting
12 import html
13 
14 # Mapping from releases to GlobalTags
15 _globalTags = {
16  "CMSSW_6_2_0": {"default": "PRE_ST62_V8"},
17  "CMSSW_6_2_0_SLHC15": {"UPG2019withGEM": "DES19_62_V8", "UPG2023SHNoTaper": "DES23_62_V1"},
18  "CMSSW_6_2_0_SLHC17": {"UPG2019withGEM": "DES19_62_V8", "UPG2023SHNoTaper": "DES23_62_V1"},
19  "CMSSW_6_2_0_SLHC20": {"UPG2019withGEM": "DES19_62_V8", "UPG2023SHNoTaper": "DES23_62_V1"},
20  "CMSSW_7_0_0": {"default": "POSTLS170_V3", "fullsim_50ns": "POSTLS170_V4"},
21  "CMSSW_7_0_0_AlcaCSA14": {"default": "POSTLS170_V5_AlcaCSA14", "fullsim_50ns": "POSTLS170_V6_AlcaCSA14"},
22  "CMSSW_7_0_7_pmx": {"default": "PLS170_V7AN1", "fullsim_50ns": "PLS170_V6AN1"},
23  "CMSSW_7_0_9_patch3": {"default": "PLS170_V7AN2", "fullsim_50ns": "PLS170_V6AN2"},
24  "CMSSW_7_0_9_patch3_Premix": {"default": "PLS170_V7AN2", "fullsim_50ns": "PLS170_V6AN2"},
25  "CMSSW_7_1_0": {"default": "POSTLS171_V15", "fullsim_50ns": "POSTLS171_V16"},
26  "CMSSW_7_1_9": {"default": "POSTLS171_V17", "fullsim_50ns": "POSTLS171_V18"},
27  "CMSSW_7_1_9_patch2": {"default": "POSTLS171_V17", "fullsim_50ns": "POSTLS171_V18"},
28  "CMSSW_7_1_10_patch2": {"default": "MCRUN2_71_V1", "fullsim_50ns": "MCRUN2_71_V0"},
29  "CMSSW_7_2_0_pre5": {"default": "POSTLS172_V3", "fullsim_50ns": "POSTLS172_V4"},
30  "CMSSW_7_2_0_pre7": {"default": "PRE_LS172_V11", "fullsim_50ns": "PRE_LS172_V12"},
31  "CMSSW_7_2_0_pre8": {"default": "PRE_LS172_V15", "fullsim_50ns": "PRE_LS172_V16"},
32  "CMSSW_7_2_0": {"default": "PRE_LS172_V15", "fullsim_50ns": "PRE_LS172_V16"},
33  "CMSSW_7_2_0_PHYS14": {"default": "PHYS14_25_V1_Phys14"},
34  "CMSSW_7_2_2_patch1": {"default": "MCRUN2_72_V1", "fullsim_50ns": "MCRUN2_72_V0"},
35  "CMSSW_7_2_2_patch1_Fall14DR": {"default": "MCRUN2_72_V3_71XGENSIM"},
36 # "CMSSW_7_3_0_pre1": {"default": "PRE_LS172_V15", "fullsim_25ns": "PRE_LS172_V15_OldPU", "fullsim_50ns": "PRE_LS172_V16_OldPU"},
37  "CMSSW_7_3_0_pre1": {"default": "PRE_LS172_V15", "fullsim_50ns": "PRE_LS172_V16"},
38 # "CMSSW_7_3_0_pre2": {"default": "MCRUN2_73_V1_OldPU", "fullsim_50ns": "MCRUN2_73_V0_OldPU"},
39  "CMSSW_7_3_0_pre2": {"default": "MCRUN2_73_V1", "fullsim_50ns": "MCRUN2_73_V0"},
40  "CMSSW_7_3_0_pre3": {"default": "MCRUN2_73_V5", "fullsim_50ns": "MCRUN2_73_V4"},
41  "CMSSW_7_3_0": {"default": "MCRUN2_73_V7", "fullsim_50ns": "MCRUN2_73_V6"},
42  "CMSSW_7_3_0_71XGENSIM": {"default": "MCRUN2_73_V7_71XGENSIM"},
43  "CMSSW_7_3_0_71XGENSIM_FIXGT": {"default": "MCRUN2_73_V9_71XGENSIM_FIXGT"},
44  "CMSSW_7_3_1_patch1": {"default": "MCRUN2_73_V9", "fastsim": "MCRUN2_73_V7"},
45  "CMSSW_7_3_1_patch1_GenSim_7113": {"default": "MCRUN2_73_V9_GenSim_7113"},
46  "CMSSW_7_3_3": {"default": "MCRUN2_73_V11", "fullsim_50ns": "MCRUN2_73_V10", "fastsim": "MCRUN2_73_V13"},
47  "CMSSW_7_4_0_pre1": {"default": "MCRUN2_73_V5", "fullsim_50ns": "MCRUN2_73_V4"},
48  "CMSSW_7_4_0_pre2": {"default": "MCRUN2_73_V7", "fullsim_50ns": "MCRUN2_73_V6"},
49  "CMSSW_7_4_0_pre2_73XGENSIM": {"default": "MCRUN2_73_V7_73XGENSIM_Pythia6", "fullsim_50ns": "MCRUN2_73_V6_73XGENSIM_Pythia6"},
50  "CMSSW_7_4_0_pre5": {"default": "MCRUN2_73_V7", "fullsim_50ns": "MCRUN2_73_V6"},
51  "CMSSW_7_4_0_pre5_BS": {"default": "MCRUN2_73_V9_postLS1beamspot", "fullsim_50ns": "MCRUN2_73_V8_postLS1beamspot"},
52  "CMSSW_7_4_0_pre6": {"default": "MCRUN2_74_V1", "fullsim_50ns": "MCRUN2_74_V0"},
53  "CMSSW_7_4_0_pre8": {"default": "MCRUN2_74_V7", "fullsim_25ns": "MCRUN2_74_V5_AsympMinGT", "fullsim_50ns": "MCRUN2_74_V4_StartupMinGT"},
54  "CMSSW_7_4_0_pre8_minimal": {"default": "MCRUN2_74_V5_MinGT", "fullsim_25ns": "MCRUN2_74_V5_AsympMinGT", "fullsim_50ns": "MCRUN2_74_V4_StartupMinGT"},
55  "CMSSW_7_4_0_pre8_25ns_asymptotic": {"default": "MCRUN2_74_V7"},
56  "CMSSW_7_4_0_pre8_50ns_startup": {"default": "MCRUN2_74_V6"},
57  "CMSSW_7_4_0_pre8_50ns_asympref": {"default": "MCRUN2_74_V5A_AsympMinGT"}, # for reference of 50ns asymptotic
58  "CMSSW_7_4_0_pre8_50ns_asymptotic": {"default": "MCRUN2_74_V7A_AsympGT"},
59  "CMSSW_7_4_0_pre8_ROOT6": {"default": "MCRUN2_74_V7"},
60  "CMSSW_7_4_0_pre8_pmx": {"default": "MCRUN2_74_V7", "fullsim_50ns": "MCRUN2_74_V6"},
61  "CMSSW_7_4_0_pre8_pmx_v2": {"default": "MCRUN2_74_V7_gs_pre7", "fullsim_50ns": "MCRUN2_74_V6_gs_pre7"},
62  "CMSSW_7_4_0_pre8_pmx_v3": {"default": "MCRUN2_74_V7_bis", "fullsim_50ns": "MCRUN2_74_V6_bis"},
63  "CMSSW_7_4_0_pre9": {"default": "MCRUN2_74_V7", "fullsim_50ns": "MCRUN2_74_V6"},
64  "CMSSW_7_4_0_pre9_ROOT6": {"default": "MCRUN2_74_V7", "fullsim_50ns": "MCRUN2_74_V6"},
65  "CMSSW_7_4_0_pre9_extended": {"default": "MCRUN2_74_V7_extended"},
66  "CMSSW_7_4_0": {"default": "MCRUN2_74_V7_gensim_740pre7", "fullsim_50ns": "MCRUN2_74_V6_gensim_740pre7", "fastsim": "MCRUN2_74_V7"},
67  "CMSSW_7_4_0_71XGENSIM": {"default": "MCRUN2_74_V7_GENSIM_7_1_15", "fullsim_50ns": "MCRUN2_74_V6_GENSIM_7_1_15"},
68  "CMSSW_7_4_0_71XGENSIM_PU": {"default": "MCRUN2_74_V7_gs7115_puProd", "fullsim_50ns": "MCRUN2_74_V6_gs7115_puProd"},
69  "CMSSW_7_4_0_71XGENSIM_PXworst": {"default": "MCRUN2_74_V7C_pxWorst_gs7115", "fullsim_50ns": "MCRUN2_74_V6A_pxWorst_gs7115"},
70  "CMSSW_7_4_0_71XGENSIM_PXbest": {"default": "MCRUN2_74_V7D_pxBest_gs7115", "fullsim_50ns": "MCRUN2_74_V6B_pxBest_gs7115"},
71  "CMSSW_7_4_0_pmx": {"default": "MCRUN2_74_V7", "fullsim_50ns": "MCRUN2_74_V6"},
72  "CMSSW_7_4_1": {"default": "MCRUN2_74_V9_gensim_740pre7", "fullsim_50ns": "MCRUN2_74_V8_gensim_740pre7", "fastsim": "MCRUN2_74_V9"},
73  "CMSSW_7_4_1_71XGENSIM": {"default": "MCRUN2_74_V9_gensim71X", "fullsim_50ns": "MCRUN2_74_V8_gensim71X"},
74  "CMSSW_7_4_1_extended": {"default": "MCRUN2_74_V9_extended"},
75  "CMSSW_7_4_3": {"default": "MCRUN2_74_V9", "fullsim_50ns": "MCRUN2_74_V8", "fastsim": "MCRUN2_74_V9", "fastsim_25ns": "MCRUN2_74_V9_fixMem"},
76  "CMSSW_7_4_3_extended": {"default": "MCRUN2_74_V9_ext","fastsim": "MCRUN2_74_V9_fixMem"},
77  "CMSSW_7_4_3_pmx": {"default": "MCRUN2_74_V9_ext", "fullsim_50ns": "MCRUN2_74_V8", "fastsim": "MCRUN2_74_V9_fixMem"},
78  "CMSSW_7_4_3_patch1_unsch": {"default": "MCRUN2_74_V9_unsch", "fullsim_50ns": "MCRUN2_74_V8_unsch"},
79  "CMSSW_7_4_4": {"default": "MCRUN2_74_V9_38Tbis", "fullsim_50ns": "MCRUN2_74_V8_38Tbis"},
80  "CMSSW_7_4_4_0T": {"default": "MCRUN2_740TV1_0Tv2", "fullsim_50ns": "MCRUN2_740TV0_0TV2", "fullsim_25ns": "MCRUN2_740TV1_0TV2"},
81  "CMSSW_7_4_6_patch6": {"default": "MCRUN2_74_V9_scheduled", "fullsim_50ns": "MCRUN2_74_V8_scheduled"},
82  "CMSSW_7_4_6_patch6_unsch": {"default": "MCRUN2_74_V9", "fullsim_50ns": "MCRUN2_74_V8"},
83  "CMSSW_7_4_6_patch6_noCCC": {"default": "MCRUN2_74_V9_unsch_noCCC", "fullsim_50ns": "MCRUN2_74_V8_unsch_noCCC"},
84  "CMSSW_7_4_6_patch6_noCCC_v3": {"default": "MCRUN2_74_V9_unsch_noCCC_v3", "fullsim_50ns": "MCRUN2_74_V8_unsch_noCCC_v3"},
85  "CMSSW_7_4_6_patch6_BS": {"default": "74X_mcRun2_asymptotic_realisticBS_v0_2015Jul24", "fullsim_50ns": "74X_mcRun2_startup_realisticBS_v0_2015Jul24PU", "fullsim_25ns": "74X_mcRun2_asymptotic_realisticBS_v0_2015Jul24PU"},
86  "CMSSW_7_4_8_patch1_MT": {"default": "MCRUN2_74_V11_mulTrh", "fullsim_50ns": "MCRUN2_74_V10_mulTrh",},
87  "CMSSW_7_4_12": {"default": "74X_mcRun2_asymptotic_v2", "fullsim_25ns": "74X_mcRun2_asymptotic_v2_v2", "fullsim_50ns": "74X_mcRun2_startup_v2_v2"},
88  "CMSSW_7_5_0_pre1": {"default": "MCRUN2_74_V7", "fullsim_50ns": "MCRUN2_74_V6"},
89  "CMSSW_7_5_0_pre2": {"default": "MCRUN2_74_V7", "fullsim_50ns": "MCRUN2_74_V6"},
90  "CMSSW_7_5_0_pre3": {"default": "MCRUN2_74_V7", "fullsim_50ns": "MCRUN2_74_V6"},
91  "CMSSW_7_5_0_pre4": {"default": "MCRUN2_75_V1", "fullsim_50ns": "MCRUN2_75_V0"},
92  "CMSSW_7_5_0_pre5": {"default": "MCRUN2_75_V5", "fullsim_50ns": "MCRUN2_75_V4"},
93  "CMSSW_7_5_0_pre6": {"default": "75X_mcRun2_asymptotic_v1", "fullsim_50ns": "75X_mcRun2_startup_v1"},
94  "CMSSW_7_5_0": {"default": "75X_mcRun2_asymptotic_v1", "fullsim_50ns": "75X_mcRun2_startup_v1"},
95  "CMSSW_7_5_0_71XGENSIM": {"default": "75X_mcRun2_asymptotic_v1_gs7115", "fullsim_50ns": "75X_mcRun2_startup_v1_gs7115"},
96  "CMSSW_7_5_1": {"default": "75X_mcRun2_asymptotic_v3", "fullsim_50ns": "75X_mcRun2_startup_v3"},
97  "CMSSW_7_5_1_71XGENSIM": {"default": "75X_mcRun2_asymptotic_v3_gs7118", "fullsim_50ns": "75X_mcRun2_startup_v3_gs7118"},
98  "CMSSW_7_5_2": {"default": "75X_mcRun2_asymptotic_v5", "fullsim_50ns": "75X_mcRun2_startup_v4"},
99  "CMSSW_7_6_0_pre1": {"default": "75X_mcRun2_asymptotic_v1", "fullsim_50ns": "75X_mcRun2_startup_v1"},
100  "CMSSW_7_6_0_pre2": {"default": "75X_mcRun2_asymptotic_v2", "fullsim_50ns": "75X_mcRun2_startup_v2"},
101  "CMSSW_7_6_0_pre3": {"default": "75X_mcRun2_asymptotic_v2", "fullsim_50ns": "75X_mcRun2_startup_v2"},
102  "CMSSW_7_6_0_pre4": {"default": "76X_mcRun2_asymptotic_v1", "fullsim_50ns": "76X_mcRun2_startup_v1"},
103  "CMSSW_7_6_0_pre5": {"default": "76X_mcRun2_asymptotic_v1", "fullsim_50ns": "76X_mcRun2_startup_v1"},
104 }
105 
106 _releasePostfixes = ["_AlcaCSA14", "_PHYS14", "_TEST", "_pmx_v2", "_pmx_v3", "_pmx", "_Fall14DR", "_71XGENSIM_FIXGT", "_71XGENSIM_PU", "_71XGENSIM_PXbest", "_71XGENSIM_PXworst", "_71XGENSIM", "_73XGENSIM", "_BS", "_GenSim_7113", "_extended",
107  "_25ns_asymptotic", "_50ns_startup", "_50ns_asympref", "_50ns_asymptotic", "_minimal", "_0T", "_unsch", "_noCCC_v3", "_noCCC", "_MT"]
108 def _stripRelease(release):
109  for pf in _releasePostfixes:
110  if pf in release:
111  return release.replace(pf, "")
112  return release
113 
114 
115 def _getGlobalTag(sample, release):
116  """Get a GlobalTag.
117 
118  Arguments:
119  sample -- Sample object
120  release -- CMSSW release string
121  """
122  if not release in _globalTags:
123  print "Release %s not found from globaltag map in validation.py" % release
124  sys.exit(1)
125  gtmap = _globalTags[release]
126  if sample.hasOverrideGlobalTag():
127  ogt = sample.overrideGlobalTag()
128  if release in ogt:
129  gtmap = _globalTags[ogt[release]]
130  if sample.fullsim():
131  if sample.hasScenario():
132  return gtmap[sample.scenario()]
133  if sample.hasPileup():
134  puType = sample.pileupType()
135  if "50ns" in puType:
136  return gtmap.get("fullsim_50ns", gtmap["default"])
137  if "25ns" in puType:
138  return gtmap.get("fullsim_25ns", gtmap["default"])
139  if sample.fastsim():
140  if sample.hasPileup():
141  puType = sample.pileupType()
142  if "25ns" in puType:
143  return gtmap.get("fastsim_25ns", gtmap["default"])
144  return gtmap.get("fastsim", gtmap["default"])
145  return gtmap["default"]
146 
147 # Mapping from release series to RelVal download URLs
148 _relvalUrls = {
149  "6_2_X": "https://cmsweb.cern.ch/dqm/relval/data/browse/ROOT/RelVal/CMSSW_6_2_x/",
150  "7_0_X": "https://cmsweb.cern.ch/dqm/relval/data/browse/ROOT/RelVal/CMSSW_7_0_x/",
151  "7_1_X": "https://cmsweb.cern.ch/dqm/relval/data/browse/ROOT/RelVal/CMSSW_7_1_x/",
152  "7_2_X": "https://cmsweb.cern.ch/dqm/relval/data/browse/ROOT/RelVal/CMSSW_7_2_x/",
153  "7_3_X": "https://cmsweb.cern.ch/dqm/relval/data/browse/ROOT/RelVal/CMSSW_7_3_x/",
154  "7_4_X": "https://cmsweb.cern.ch/dqm/relval/data/browse/ROOT/RelVal/CMSSW_7_4_x/",
155  "7_5_X": "https://cmsweb.cern.ch/dqm/relval/data/browse/ROOT/RelVal/CMSSW_7_5_x/",
156  "7_6_X": "https://cmsweb.cern.ch/dqm/relval/data/browse/ROOT/RelVal/CMSSW_7_6_x/",
157 }
158 
159 def _getRelValUrl(release):
160  """Get RelVal download URL for a given release."""
161  version_re = re.compile("CMSSW_(?P<X>\d+)_(?P<Y>\d+)")
162  m = version_re.search(release)
163  if not m:
164  raise Exception("Regex %s does not match to release version %s" % (version_re.pattern, release))
165  version = "%s_%s_X" % (m.group("X"), m.group("Y"))
166  if not version in _relvalUrls:
167  print "No RelVal URL for version %s, please update _relvalUrls" % version
168  sys.exit(1)
169  return _relvalUrls[version]
170 
171 class Sample:
172  """Represents a RelVal sample."""
173  def __init__(self, sample, append=None, midfix=None, putype=None,
174  fastsim=False, fastsimCorrespondingFullsimPileup=None,
175  version="v1", dqmVersion="0001", scenario=None, overrideGlobalTag=None):
176  """Constructor.
177 
178  Arguments:
179  sample -- String for name of the sample
180 
181  Keyword arguments
182  append -- String for a variable name within the DWM file names, to be directly appended to sample name (e.g. "HS"; default None)
183  midfix -- String for a variable name within the DQM file names, to be appended after underscore to "sample name+append" (e.g. "13", "UP15"; default None)
184  putype -- String for pileup type (e.g. "25ns"/"50ns" for FullSim, "AVE20" for FastSim; default None)
185  fastsim -- Bool indicating the FastSim status (default False)
186  fastsimCorrespondingFullSimPileup -- String indicating what is the FullSim pileup sample corresponding this FastSim sample. Must be set if fastsim=True and putype!=None (default None)
187  version -- String for dataset/DQM file version (default "v1")
188  scenario -- Geometry scenario for upgrade samples (default None)
189  overrideGlobalTag -- GlobalTag obtained from release information (in the form of {"release": "actualRelease"}; default None)
190  """
191  self._sample = sample
192  self._append = append
193  self._midfix = midfix
194  self._putype = putype
195  self._fastsim = fastsim
196  self._fastsimCorrespondingFullsimPileup = fastsimCorrespondingFullsimPileup
197  self._version = version
198  self._dqmVersion = dqmVersion
199  self._scenario = scenario
200  self._overrideGlobalTag = overrideGlobalTag
201 
202  if self._fastsim and self.hasPileup() and self._fastsimCorrespondingFullsimPileup is None:
204 
205  def digest(self):
206  """Return a tuple uniquely identifying the sample, to be used e.g. as a key to dict"""
207  return (self.name(), self.pileupType(), self.scenario(), self.fastsim())
208 
209  def sample(self):
210  """Get the sample name"""
211  return self._sample
212 
213  def name(self):
214  """Get the sample name"""
215  return self._sample
216 
217  def label(self):
218  return self._sample
219 
220  def hasPileup(self):
221  """Return True if sample has pileup"""
222  return self._putype is not None
223 
224  def pileup(self):
225  """Return "PU"/"noPU" corresponding the pileup status"""
226  if self.hasPileup():
227  return "PU"
228  else:
229  return "noPU"
230 
231  def pileupType(self, release=None):
232  """Return the pileup type"""
233  if isinstance(self._putype, dict):
234  return self._putype.get(release, self._putype["default"])
235  else:
236  return self._putype
237 
238  def version(self, release=None):
239  if isinstance(self._version, dict):
240  return self._version.get(release, self._version["default"])
241  else:
242  return self._version
243 
244  def hasScenario(self):
245  return self._scenario is not None
246 
247  def scenario(self):
248  return self._scenario
249 
251  return self._overrideGlobalTag is not None
252 
253  def overrideGlobalTag(self):
254  return self._overrideGlobalTag
255 
256  def fastsim(self):
257  """Return True for FastSim sample"""
258  return self._fastsim
259 
260  def fullsim(self):
261  """Return True for FullSim sample"""
262  return not self._fastsim
263 
266 
267  def dirname(self, newRepository, newRelease, newSelection):
268  """Return the output directory name
269 
270  Arguments:
271  newRepository -- String for base directory for output files
272  newRelease -- String for CMSSW release
273  newSelection -- String for histogram selection
274  """
275  pileup = ""
276  if self.hasPileup() and not self._fastsim:
277  pileup = "_"+self._putype
278  return "{newRepository}/{newRelease}/{newSelection}{pileup}/{sample}".format(
279  newRepository=newRepository, newRelease=newRelease, newSelection=newSelection,
280  pileup=pileup, sample=sample)
281 
282  def filename(self, newRelease):
283  """Return the DQM file name
284 
285  Arguments:
286  newRelease -- String for CMSSW release
287  """
288  pileup = ""
289  fastsim = ""
290  midfix = ""
291  scenario = ""
292  sample = self._sample
293  if self._append is not None:
294  midfix += self._append
295  if self._midfix is not None:
296  midfix += "_"+self._midfix
297  if self.hasPileup():
298  if self._fastsim:
299  #sample = sample.replace("RelVal", "RelValFS_")
300  # old style
301  #pileup = "PU_"
302  #midfix += "_"+self.pileupType(newRelease)
303  # new style
304  pileup = "PU"+self.pileupType(newRelease)+"_"
305  else:
306  pileup = "PU"+self.pileupType(newRelease)+"_"
307  if self._fastsim:
308  fastsim = "_FastSim"
309  if self._scenario is not None:
310  scenario = "_"+self._scenario
311 
312  globalTag = _getGlobalTag(self, newRelease)
313 
314  fname = 'DQM_V{dqmVersion}_R000000001__{sample}{midfix}__{newrelease}-{pileup}{globaltag}{scenario}{fastsim}-{version}__DQMIO.root'.format(
315  sample=sample, midfix=midfix, newrelease=_stripRelease(newRelease),
316  pileup=pileup, globaltag=globalTag, scenario=scenario, fastsim=fastsim,
317  version=self.version(newRelease), dqmVersion=self._dqmVersion
318  )
319 
320  return fname
321 
322  def datasetpattern(self, newRelease):
323  """Return the dataset pattern
324 
325  Arguments:
326  newRelease -- String for CMSSW release
327  """
328  pileup = ""
329  fastsim = ""
330  digi = ""
331  if self.hasPileup():
332  pileup = "-PU_"
333  if self._fastsim:
334  fastsim = "_FastSim-"
335  digi = "DIGI-"
336  else:
337  fastsim = "*"
338  globalTag = _getGlobalTag(self, newRelease)
339  return "{sample}/{newrelease}-{pileup}{globaltag}{fastsim}{version}/GEN-SIM-{digi}RECO".format(
340  sample=self._sample, newrelease=newRelease,
341  pileup=pileup, globaltag=globalTag, fastsim=fastsim, digi=digi,
342  version=self.version(newRelease)
343  )
344 
346  """Base class for Tracking/Vertex validation."""
347  def __init__(self, fullsimSamples, fastsimSamples, refRelease, refRepository, newRelease, newRepository, newFileModifier=None, selectionName=""):
348  """Constructor.
349 
350  Arguments:
351  fullsimSamples -- List of Sample objects for FullSim samples (may be empty)
352  fastsimSamples -- List of Sample objects for FastSim samples (may be empty)
353  refRelease -- String for reference CMSSW release
354  newRepository -- String for directory whete to put new files
355  newRelease -- CMSSW release to be validated
356  refRepository -- String for directory where reference root files are
357  newFileModifier -- If given, a function to modify the names of the new files (function takes a string and returns a string)
358  selectionName -- If given, use this string as the selection name (appended to GlobalTag for directory names)
359  """
360  try:
361  self._newRelease = os.environ["CMSSW_VERSION"]
362  except KeyError:
363  print >>sys.stderr, 'Error: CMSSW environment variables are not available.'
364  print >>sys.stderr, ' Please run cmsenv'
365  sys.exit()
366 
367  self._fullsimSamples = fullsimSamples
368  self._fastsimSamples = fastsimSamples
369  self._refRelease = refRelease
370  self._refRepository = refRepository
371  self._newRelease = newRelease
372  self._newBaseDir = os.path.join(newRepository, self._newRelease)
373  self._newFileModifier = newFileModifier
374  self._selectionName = selectionName
375 
376  def _getDirectoryName(self, *args, **kwargs):
377  return None
378 
379  def _getSelectionName(self, *args, **kwargs):
380  return self._selectionName
381 
382  def download(self):
383  """Download DQM files. Requires grid certificate and asks your password for it."""
384  filenames = [s.filename(self._newRelease) for s in self._fullsimSamples+self._fastsimSamples]
385  if self._newFileModifier is not None:
386  filenames = map(self._newFileModifier, filenames)
387  filenames = filter(lambda f: not os.path.exists(f), filenames)
388  if len(filenames) == 0:
389  print "All files already downloaded"
390  return
391 
392  relvalUrl = _getRelValUrl(self._newRelease)
393  urls = [relvalUrl+f for f in filenames]
394  certfile = os.path.join(os.environ["HOME"], ".globus", "usercert.pem")
395  if not os.path.exists(certfile):
396  print "Certificate file {certfile} does not exist, unable to download RelVal files from {url}".format(certfile=certfile, url=relvalUrl)
397  sys.exit(1)
398  keyfile = os.path.join(os.environ["HOME"], ".globus", "userkey.pem")
399  if not os.path.exists(certfile):
400  print "Private key file {keyfile} does not exist, unable to download RelVal files from {url}".format(keyfile=keyfile, url=relvalUrl)
401  sys.exit(1)
402 
403  cmd = ["curl", "--cert-type", "PEM", "--cert", certfile, "--key", keyfile, "-k"]
404  for u in urls:
405  cmd.extend(["-O", u])
406  print "Downloading %d files from RelVal URL %s:" % (len(filenames), relvalUrl)
407  print " "+"\n ".join(filenames)
408  print "Please provide your private key pass phrase when curl asks it"
409  ret = subprocess.call(cmd)
410  if ret != 0:
411  print "Downloading failed with exit code %d" % ret
412  sys.exit(1)
413 
414  # verify
415  allFine = True
416  for f in filenames:
417  p = subprocess.Popen(["file", f], stdout=subprocess.PIPE)
418  stdout = p.communicate()[0]
419  if p.returncode != 0:
420  print "file command failed with exit code %d" % p.returncode
421  sys.exit(1)
422  if not "ROOT" in stdout:
423  print "File {f} is not ROOT, please check the correct version, GobalTag etc. from {url}".format(f=f, url=relvalUrl)
424  allFine = False
425  if os.path.exists(f):
426  os.remove(f)
427  if not allFine:
428  sys.exit(1)
429 
430  def createHtmlReport(self):
431  baseUrl = "http://cmsdoc.cern.ch/cms/Physics/tracking/validation/MC/%s/" % self._newRelease
432  return html.HtmlReport(self._newRelease, self._newBaseDir, baseUrl)
433 
434  def doPlots(self, plotter, plotterDrawArgs={}, limitSubFoldersOnlyTo=None, htmlReport=html.HtmlReportDummy(), doFastVsFull=True):
435  """Create validation plots.
436 
437  Arguments:
438  plotter -- plotting.Plotter object that does the plotting
439 
440  Keyword arguments:
441  plotterDrawArgs -- Dictionary for additional arguments to Plotter.draw() (default: {})
442  limitSubFoldersOnlyTo -- If not None, should be a dictionary from string to an object. The string is the name of a PlotFolder, and the object is PlotFolder-type specific to limit the subfolders to be processed. In general case the object is a list of strings, but e.g. for track iteration plots it is a function taking the algo and quality as parameters.
443  htmlReport -- Object returned by createHtmlReport(), in case HTML report generation is desired
444  doFastVsFull -- Do FastSim vs. FullSim comparison? (default: True)
445  """
446  self._plotter = plotter
447  self._plotterDrawArgs = plotterDrawArgs
448 
449  # New vs. Ref
450  for sample in self._fullsimSamples+self._fastsimSamples:
451  # Check that the new DQM file exists
452  harvestedFile = sample.filename(self._newRelease)
453  if not os.path.exists(harvestedFile):
454  print "Harvested file %s does not exist!" % harvestedFile
455  sys.exit(1)
456 
457  plotterInstance = plotter.readDirs(harvestedFile)
458  htmlReport.beginSample(sample)
459  for plotterFolder, dqmSubFolder in plotterInstance.iterFolders(limitSubFoldersOnlyTo=limitSubFoldersOnlyTo):
460  if plotterFolder.onlyForPileup() and not sample.hasPileup():
461  continue
462  plotFiles = self._doPlots(sample, harvestedFile, plotterFolder, dqmSubFolder)
463  htmlReport.addPlots(plotterFolder, dqmSubFolder, plotFiles)
464  # TODO: the pileup case is still to be migrated
465 # if s.fullsim() and s.hasPileup():
466 # self._doPlotsPileup(a, q, s)
467 
468  # Fast vs. Full
469  if not doFastVsFull:
470  return
471  for fast in self._fastsimSamples:
472  correspondingFull = None
473  for full in self._fullsimSamples:
474  if fast.name() != full.name():
475  continue
476  if fast.hasPileup():
477  if not full.hasPileup():
478  continue
479  if fast.fastsimCorrespondingFullsimPileup() != full.pileupType():
480  continue
481  else:
482  if full.hasPileup():
483  continue
484 
485  if correspondingFull is None:
486  correspondingFull = full
487  else:
488  raise Exception("Got multiple compatible FullSim samples for FastSim sample %s %s" % (fast.name(), fast.pileup()))
489  if correspondingFull is None:
490  raise Exception("Did not find compatible FullSim sample for FastSim sample %s %s" % (fast.name(), fast.pileup()))
491 
492  # If we reach here, the harvestedFile must exist
493  harvestedFile = fast.filename(self._newRelease)
494  plotterInstance = plotter.readDirs(harvestedFile)
495  htmlReport.beginSample(fast, fastVsFull=True)
496  for plotterFolder, dqmSubFolder in plotterInstance.iterFolders(limitSubFoldersOnlyTo=limitSubFoldersOnlyTo):
497  if plotterFolder.onlyForPileup() and not fast.hasPileup():
498  continue
499  plotFiles = self._doPlotsFastFull(fast, correspondingFull, plotterFolder, dqmSubFolder)
500  htmlReport.addPlots(plotterFolder, dqmSubFolder, plotFiles)
501 
502  def _doPlots(self, sample, harvestedFile, plotterFolder, dqmSubFolder):
503  """Do the real plotting work for a given sample and DQM subfolder"""
504  # Get GlobalTags
505  refGlobalTag = _getGlobalTag(sample, self._refRelease)
506  newGlobalTag = _getGlobalTag(sample, self._newRelease)
507 
508  # Construct selection string
509  selectionNameBase = ""
510  if sample.hasScenario():
511  selectionNameBase += "_"+sample.scenario()
512  selectionNameBase += "_"+sample.pileup()
513  newSelection = newGlobalTag+selectionNameBase+plotterFolder.getSelectionName(dqmSubFolder)
514  if sample.hasPileup():
515  newPu = sample.pileupType(self._newRelease)
516  if newPu != "":
517  newSelection += "_"+newPu
518  def _createRefSelection(selectionName):
519  sel = refGlobalTag+selectionNameBase+selectionName
520  if sample.hasPileup():
521  refPu = sample.pileupType(self._refRelease)
522  if refPu != "":
523  sel += "_"+refPu
524  return sel
525  refSelection = _createRefSelection(plotterFolder.getSelectionName(dqmSubFolder))
526 
527  valname = "val.{sample}.root".format(sample=sample.name())
528 
529  # Construct reference directory name, and open reference file it it exists
530  refValFile = None
531  triedRefValFiles = []
532  tmp = [self._refRepository, self._refRelease]
533  if sample.fastsim():
534  tmp.extend(["fastsim", self._refRelease])
535  for selName in plotterFolder.getSelectionNameIterator(dqmSubFolder):
536  refSel = _createRefSelection(selName)
537  refdir = os.path.join(*(tmp+[refSel, sample.name()]))
538 
539  # Open reference file if it exists
540  refValFilePath = os.path.join(refdir, valname)
541  if os.path.exists(refValFilePath):
542  refSelection = refSel
543  refValFile = ROOT.TFile.Open(refValFilePath)
544  break
545  else:
546  triedRefValFiles.append(refValFilePath)
547  if refValFile is None:
548  if len(triedRefValFiles) == 1:
549  print "Reference file %s not found" % triedRefValFiles[0]
550  else:
551  print "None of the possible reference files %s not found" % ",".join(triedRefValFiles)
552 
553  # Construct new directory name
554  tmp = []
555  if sample.fastsim():
556  tmp.extend(["fastsim", self._newRelease])
557  tmp.extend([newSelection, sample.name()])
558  newsubdir = os.path.join(*tmp)
559  newdir = os.path.join(self._newBaseDir, newsubdir)
560 
561  # Copy the relevant histograms to a new validation root file
562  # TODO: treat the case where dqmSubFolder is empty
563  newValFile = _copySubDir(harvestedFile, valname, plotterFolder.getPossibleDQMFolders(), dqmSubFolder.subfolder if dqmSubFolder is not None else None)
564  fileList = []
565 
566  # Do the plots
567  print "Comparing ref and new {sim} {sample} {translatedFolder}".format(
568  sim="FullSim" if not sample.fastsim() else "FastSim",
569  sample=sample.name(), translatedFolder=str(dqmSubFolder.translated) if dqmSubFolder is not None else "")
570  plotterFolder.create([refValFile, newValFile], [
571  "%s, %s %s" % (sample.name(), _stripRelease(self._refRelease), refSelection),
572  "%s, %s %s" % (sample.name(), _stripRelease(self._newRelease), newSelection)
573  ],
574  dqmSubFolder,
575  isPileupSample=sample.hasPileup()
576  )
577  fileList.extend(plotterFolder.draw(**self._plotterDrawArgs))
578  fileList.append(valname)
579 
580  newValFile.Close()
581  if refValFile is not None:
582  refValFile.Close()
583 
584  # Move plots to new directory
585  print "Moving plots and %s to %s" % (valname, newdir)
586  if not os.path.exists(newdir):
587  os.makedirs(newdir)
588  for f in fileList:
589  shutil.move(f, os.path.join(newdir, f))
590  return map(lambda n: os.path.join(newsubdir, n), fileList)
591 
592  def _doPlotsFastFull(self, fastSample, fullSample, plotterFolder, dqmSubFolder):
593  """Do the real plotting work for FastSim vs. FullSim for a given algorithm, quality flag, and sample."""
594  # Get GlobalTags
595  fastGlobalTag = _getGlobalTag(fastSample, self._newRelease)
596  fullGlobalTag = _getGlobalTag(fullSample, self._newRelease)
597 
598  # Construct selection string
599  tmp = plotterFolder.getSelectionName(dqmSubFolder)
600  fastSelection = fastGlobalTag+"_"+fastSample.pileup()+tmp
601  fullSelection = fullGlobalTag+"_"+fullSample.pileup()+tmp
602  if fullSample.hasPileup():
603  fullSelection += "_"+fullSample.pileupType(self._newRelease)
604  fastSelection += "_"+fastSample.pileupType(self._newRelease)
605 
606  # Construct directories for FastSim, FullSim, and for the results
607  fastdir = os.path.join(self._newBaseDir, "fastsim", self._newRelease, fastSelection, fastSample.name())
608  fulldir = os.path.join(self._newBaseDir, fullSelection, fullSample.name())
609  newsubdir = os.path.join("fastfull", self._newRelease, fastSelection, fastSample.name())
610  newdir = os.path.join(self._newBaseDir, newsubdir)
611 
612  # Open input root files
613  valname = "val.{sample}.root".format(sample=fastSample.name())
614  fastValFilePath = os.path.join(fastdir, valname)
615  if not os.path.exists(fastValFilePath):
616  print "FastSim file %s not found" % fastValFilePath
617  fullValFilePath = os.path.join(fulldir, valname)
618  if not os.path.exists(fullValFilePath):
619  print "FullSim file %s not found" % fullValFilePath
620 
621  fastValFile = ROOT.TFile.Open(fastValFilePath)
622  fullValFile = ROOT.TFile.Open(fullValFilePath)
623 
624  # Do plots
625  print "Comparing FullSim and FastSim {sample} {translatedFolder}".format(
626  sample=fastSample.name(), translatedFolder=str(dqmSubFolder.translated) if dqmSubFolder is not None else "")
627  plotterFolder.create([fullValFile, fastValFile], [
628  "FullSim %s, %s %s" % (fullSample.name(), _stripRelease(self._newRelease), fullSelection),
629  "FastSim %s, %s %s" % (fastSample.name(), _stripRelease(self._newRelease), fastSelection),
630  ],
631  dqmSubFolder,
632  isPileupSample=fastSample.hasPileup(),
633  requireAllHistograms=True
634  )
635  fileList = plotterFolder.draw(**self._plotterDrawArgs)
636 
637  fullValFile.Close()
638  fastValFile.Close()
639 
640  # Move plots to new directory
641  print "Moving plots to %s" % (newdir)
642  if not os.path.exists(newdir):
643  os.makedirs(newdir)
644  for f in fileList:
645  shutil.move(f, os.path.join(newdir, f))
646  return map(lambda n: os.path.join(newsubdir, n), fileList)
647 
648  # TODO: this method is still to be migrated
649  def _doPlotsPileup(self, algo, quality, sample):
650  """Do the real plotting work for Old vs. New pileup scenarios for a given algorithm, quality flag, and sample."""
651  # Get GlobalTags
652  newGlobalTag = _getGlobalTag(sample, self._newRelease)
653  refGlobalTag = newGlobalTag + "_OldPU"
654 
655  # Construct selection string
656  tmp = self._getSelectionName(quality, algo)
657  refSelection = refGlobalTag+"_"+sample.pileup()+tmp+"_"+sample.pileupType(self._newRelease)
658  newSelection = newGlobalTag+"_"+sample.pileup()+tmp+"_"+sample.pileupType(self._newRelease)
659 
660  # Construct directories for FastSim, FullSim, and for the results
661  refdir = os.path.join(self._newBaseDir, refSelection, sample.name())
662  newdir = os.path.join(self._newBaseDir, newSelection, sample.name())
663  resdir = os.path.join(self._newBaseDir, "pileup", self._newRelease, newSelection, sample.name())
664 
665  # Open input root files
666  valname = "val.{sample}.root".format(sample=sample.name())
667  refValFilePath = os.path.join(refdir, valname)
668  if not os.path.exists(refValFilePath):
669  print "Ref pileup file %s not found" % refValFilePath
670  newValFilePath = os.path.join(newdir, valname)
671  if not os.path.exists(newValFilePath):
672  print "New pileup file %s not found" % newValFilePath
673 
674  refValFile = ROOT.TFile.Open(refValFilePath)
675  newValFile = ROOT.TFile.Open(newValFilePath)
676 
677  # Do plots
678  print "Comparing Old and New pileup {sample} {algo} {quality}".format(
679  sample=sample.name(), algo=algo, quality=quality)
680  self._plotter.create([refValFile, newValFile], [
681  "%d BX %s, %s %s" % ({"25ns": 10, "50ns": 20}[sample.pileupType(self._newRelease)], sample.name(), _stripRelease(self._newRelease), refSelection),
682  "35 BX %s, %s %s" % (sample.name(), _stripRelease(self._newRelease), newSelection),
683  ],
684  subdir = self._getDirectoryName(quality, algo))
685  fileList = self._plotter.draw(**self._plotterDrawArgs)
686 
687  newValFile.Close()
688  refValFile.Close()
689 
690  # Move plots to new directory
691  print "Moving plots to %s" % (resdir)
692  if not os.path.exists(resdir):
693  os.makedirs(resdir)
694  for f in fileList:
695  shutil.move(f, os.path.join(resdir, f))
696  subdir = newdir.replace(self._newBaseDir+"/", "")
697  return map(lambda n: os.path.join(subdir, n), fileList)
698 
699 def _copySubDir(oldfile, newfile, basenames, dirname):
700  """Copy a subdirectory from oldfile to newfile.
701 
702  Arguments:
703  oldfile -- String for source TFile
704  newfile -- String for destination TFile
705  basenames -- List of strings for base directories, first existing one is picked
706  dirname -- String for directory name under the base directory
707  """
708  oldf = ROOT.TFile.Open(oldfile)
709 
710  dirold = None
711  for basename in basenames:
712  dirold = oldf.GetDirectory(basename)
713  if dirold:
714  break
715  if not dirold:
716  raise Exception("Did not find any of %s directories from file %s" % (",".join(basenames), oldfile))
717  if dirname:
718  d = dirold.Get(dirname)
719  if not d:
720  raise Exception("Did not find directory %s under %s" % (dirname, dirold.GetPath()))
721  dirold = d
722 
723  newf = ROOT.TFile.Open(newfile, "RECREATE")
724  dirnew = newf
725  for d in basenames[0].split("/"):
726  dirnew = dirnew.mkdir(d)
727  if dirname:
728  dirnew = dirnew.mkdir(dirname)
729  _copyDir(dirold, dirnew)
730 
731  oldf.Close()
732  return newf
733 
734 def _copyDir(src, dst):
735  """Copy non-TTree objects from src TDirectory to dst TDirectory."""
736  keys = src.GetListOfKeys()
737  for key in keys:
738  classname = key.GetClassName()
739  cl = ROOT.TClass.GetClass(classname)
740  if not cl:
741  continue
742  if not (cl.InheritsFrom("TTree") and cl.InheritsFrom("TDirectory")):
743  dst.cd()
744  obj = key.ReadObj()
745  obj.Write()
746  obj.Delete()
747 
749  def __init__(self, label, name):
750  self._label = label
751  self._name = name
752 
753  def digest(self):
754  # Label should be unique among the plotting run, so it serves also as the digest
755  return self._label
756 
757  def label(self):
758  return self._label
759 
760  def name(self):
761  return self._name
762 
763  def fastsim(self):
764  # No need to emulate the release validation fastsim behaviour here
765  return False
766 
768  def __init__(self, files, labels, newdir):
769  self._files = files
770  self._labels = labels
771  self._newdir = newdir
772 
773  def createHtmlReport(self, baseUrl=None, validationName=""):
774  return html.HtmlReport(validationName, self._newdir, baseUrl)
775 
776  def doPlots(self, plotter, subdirprefix, plotterDrawArgs={}, htmlReport=html.HtmlReportDummy()):
777  self._subdirprefix=subdirprefix
778  self._plotterDrawArgs = plotterDrawArgs
779 
780  self._openFiles = []
781  for f in self._files:
782  if not os.path.exists(f):
783  print "File %s not found" % f
784  sys.exit(1)
785  self._openFiles.append(ROOT.TFile.Open(f))
786 
787  plotterInstance = plotter.readDirs(*self._openFiles)
788  for plotterFolder, dqmSubFolder in plotterInstance.iterFolders():
789  plotFiles = self._doPlots(plotterFolder, dqmSubFolder)
790  htmlReport.addPlots(plotterFolder, dqmSubFolder, plotFiles)
791 
792  for tf in self._openFiles:
793  tf.Close()
794  self._openFiles = []
795 
796  def _doPlots(self, plotterFolder, dqmSubFolder):
797  plotterFolder.create(self._openFiles, self._labels, dqmSubFolder)
798  fileList = plotterFolder.draw(**self._plotterDrawArgs)
799 
800  newsubdir = self._subdirprefix+plotterFolder.getSelectionName(dqmSubFolder)
801  newdir = os.path.join(self._newdir, newsubdir)
802 
803  print "Moving plots to %s" % newdir
804  if not os.path.exists(newdir):
805  os.makedirs(newdir)
806  for f in fileList:
807  shutil.move(f, os.path.join(newdir, f))
808  return map(lambda n: os.path.join(newsubdir, n), fileList)
_fastsimCorrespondingFullsimPileup
Definition: validation.py:196
def _copySubDir
Definition: validation.py:699
def _getGlobalTag
Definition: validation.py:115
def _stripRelease
Definition: validation.py:108
def fastsimCorrespondingFullsimPileup
Definition: validation.py:264
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
double split
Definition: MVATrainer.cc:139
def _getRelValUrl
Definition: validation.py:159
def _copyDir
Definition: validation.py:734