test
CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
mps_alisetup.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 #############################################################################
4 ## Builds the config-templates from the universal config-template for each
5 ## dataset specified in .ini-file that is passed to this script as argument.
6 ## Then calls mps_setup.pl for all datasets.
7 ##
8 ## Usage:
9 ## mps_alisetup.py [-h] [-v] myconfig.ini
10 ##
11 
12 import argparse
13 import os
14 import re
15 import importlib
16 import subprocess
17 import ConfigParser
18 import sys
19 import itertools
20 import collections
21 import Alignment.MillePedeAlignmentAlgorithm.mpslib.Mpslibclass as mpslib
22 import Alignment.MillePedeAlignmentAlgorithm.mpslib.tools as mps_tools
23 from Alignment.MillePedeAlignmentAlgorithm.alignmentsetup.helper import checked_out_MPS
24 from functools import reduce
25 
26 
27 def get_weight_configs(config):
28  """Extracts different weight configurations from `config`.
29 
30  Arguments:
31  - `config`: ConfigParser object containing the alignment configuration
32  """
33 
34  weight_dict = collections.OrderedDict()
35  common_weights = {}
36 
37  # loop over datasets to reassign weights
38  for section in config.sections():
39  if 'general' in section:
40  continue
41  elif section == "weights":
42  for option in config.options(section):
43  common_weights[option] = [x.strip() for x in
44  config.get(section, option).split(",")]
45  elif section.startswith("dataset:"):
46  name = section[8:] # get name of dataset by stripping of "dataset:"
47  if config.has_option(section,'weight'):
48  weight_dict[name] = [x.strip() for x in
49  config.get(section, "weight").split(",")]
50  else:
51  weight_dict[name] = ['1.0']
52 
53  weights_list = [[(name, weight) for weight in weight_dict[name]]
54  for name in weight_dict]
55 
56  common_weights_list = [[(name, weight) for weight in common_weights[name]]
57  for name in common_weights]
58  common_weights_dicts = []
59  for item in itertools.product(*common_weights_list):
60  d = {}
61  for name,weight in item:
62  d[name] = weight
63  common_weights_dicts.append(d)
64 
65  configs = []
66  for weight_conf in itertools.product(*weights_list):
67  if len(common_weights) > 0:
68  for common_weight in common_weights_dicts:
69  configs.append([(dataset[0],
70  reduce(lambda x,y: x.replace(y, common_weight[y]),
71  common_weight, dataset[1]))
72  for dataset in weight_conf])
73  else:
74  configs.append(weight_conf)
75 
76  return configs
77 
78 
79 def create_input_db(cfg, run_number):
80  """
81  Create sqlite file with single-IOV tags and use it to override the GT. If
82  the GT is already customized by the user, the customization has higher
83  priority. Returns a snippet to be appended to the configuration file
84 
85  Arguments:
86  - `cfg`: path to python configuration
87  - `run_number`: run from which to extract the alignment payloads
88  """
89 
90  sys.path.append(os.path.dirname(cfg))
91  cache_stdout = sys.stdout
92  sys.stdout = open(os.devnull, "w") # suppress unwanted output
93  __configuration = \
94  importlib.import_module(os.path.splitext(os.path.basename(cfg))[0])
95  sys.stdout = cache_stdout
96 
97  run_number = int(run_number)
98  if not run_number > 0:
99  print "'FirstRunForStartGeometry' must be positive, but is", run_number
100  sys.exit(1)
101 
102  global_tag = __configuration.process.GlobalTag.globaltag.value()
103  input_db_name = os.path.abspath("alignment_input.db")
104  tags = mps_tools.create_single_iov_db(global_tag, run_number, input_db_name)
105 
106  for condition in __configuration.process.GlobalTag.toGet.value():
107  if condition.record.value() in tags: del tags[condition.record.value()]
108 
109  result = ""
110  for record,tag in tags.iteritems():
111  if result == "":
112  result += ("\nimport "
113  "Alignment.MillePedeAlignmentAlgorithm.alignmentsetup."
114  "SetCondition as tagwriter\n")
115  result += ("\ntagwriter.setCondition(process,\n"
116  " connect = \""+tag["connect"]+"\",\n"
117  " record = \""+record+"\",\n"
118  " tag = \""+tag["tag"]+"\")\n")
119 
120  os.remove(cfg+"c")
121  return result
122 
123 # ------------------------------------------------------------------------------
124 # set up argument parser and config parser
125 
126 helpEpilog ='''Builds the config-templates from a universal config-template for each
127 dataset specified in .ini-file that is passed to this script.
128 Then calls mps_setup.pl for all datasets.'''
130  description='Setup the alignment as configured in the alignment_config file.',
131  epilog=helpEpilog)
132 # optional argmuent: verbose (toggles output of mps_setup)
133 parser.add_argument('-v', '--verbose', action='store_true',
134  help='display detailed output of mps_setup')
135 # optional argmuent: weight (new weights for additional merge job)
136 parser.add_argument('-w', '--weight', action='store_true',
137  help='create an additional mergejob with (possibly new) weights from .ini-config')
138 # positional argument: config file
139 parser.add_argument('alignmentConfig', action='store',
140  help='name of the .ini config file that specifies the datasets to be used')
141 # parse argument
142 args = parser.parse_args()
143 aligmentConfig = args.alignmentConfig
144 
145 # parse config file
146 config = ConfigParser.ConfigParser()
147 config.optionxform = str # default would give lowercase options -> not wanted
148 config.read(aligmentConfig)
149 
150 
151 
152 #------------------------------------------------------------------------------
153 # construct directories
154 
155 # set variables that are not too specific (millescript, pedescript, etc.)
156 mpsTemplates = os.path.join("src", "Alignment", "MillePedeAlignmentAlgorithm", "templates")
157 if checked_out_MPS()[0]:
158  mpsTemplates = os.path.join(os.environ["CMSSW_BASE"], mpsTemplates)
159 else:
160  mpsTemplates = os.path.join(os.environ["CMSSW_RELEASE_BASE"], mpsTemplates)
161 milleScript = os.path.join(mpsTemplates, "mps_runMille_template.sh")
162 pedeScript = os.path.join(mpsTemplates, "mps_runPede_rfcp_template.sh")
163 
164 # get working directory name
165 currentDir = os.getcwd()
166 mpsdirname = ''
167 match = re.search(re.compile('mpproduction\/mp(.+?)$', re.M|re.I),currentDir)
168 if match:
169  mpsdirname = 'mp'+match.group(1)
170 else:
171  print "Current location does not seem to be a MillePede campaign directory:",
172  print currentDir
173  sys.exit(1)
174 
175 # set directory on eos
176 mssDir = '/store/caf/user/'+os.environ['USER']+'/MPproduction/'+mpsdirname
177 
178 # create directory on eos if it doesn't exist already
179 eos = '/afs/cern.ch/project/eos/installation/cms/bin/eos.select'
180 os.system(eos+' mkdir -p '+mssDir)
181 
182 
183 
184 #------------------------------------------------------------------------------
185 # read general-section
186 generalOptions = {}
187 
188 # essential variables
189 for var in ["classInf","pedeMem","jobname", "FirstRunForStartGeometry"]:
190  try:
191  generalOptions[var] = config.get('general',var)
192  except ConfigParser.NoOptionError:
193  print "No", var, "found in [general] section. Please check ini-file."
194  raise SystemExit
195 
196 # check if datasetdir is given
197 generalOptions['datasetdir'] = ''
198 if config.has_option('general','datasetdir'):
199  generalOptions['datasetdir'] = config.get('general','datasetdir')
200  # add it to environment for later variable expansion:
201  os.environ["datasetdir"] = generalOptions['datasetdir']
202 else:
203  print "No datasetdir given in [general] section.",
204  print "Be sure to give a full path in inputFileList."
205 
206 # check for default options
207 for var in ['globaltag','configTemplate','json']:
208  try:
209  generalOptions[var] = config.get('general',var)
210  except ConfigParser.NoOptionError:
211  print 'No default', var, 'given in [general] section.'
212 
213 
214 
215 pedesettings = ([x.strip() for x in config.get("general", "pedesettings").split(",")]
216  if config.has_option("general", "pedesettings") else [None])
217 
218 weight_confs = get_weight_configs(config)
219 
220 
221 #------------------------------------------------------------------------------
222 # -w option: Get new weights from .ini and create additional mergejob with these
223 if args.weight:
224 
225  # do some basic checks
226  if not os.path.isdir("jobData"):
227  print "No jobData-folder found. Properly set up the alignment before using the -w option."
228  raise SystemExit
229  if not os.path.exists("mps.db"):
230  print "No mps.db found. Properly set up the alignment before using the -w option."
231  raise SystemExit
232 
233  # check if default configTemplate is given
234  try:
235  configTemplate = config.get('general','configTemplate')
236  except ConfigParser.NoOptionError:
237  print 'No default configTemplate given in [general] section.'
238  print 'When using -w, a default configTemplate is needed to build a merge-config.'
239  raise SystemExit
240 
241  # check if default globaltag is given
242  try:
243  globalTag = config.get('general','globaltag')
244  except ConfigParser.NoOptionError:
245  print "No default 'globaltag' given in [general] section."
246  print "When using -w, a default configTemplate is needed to build a merge-config."
247  raise SystemExit
248 
249  try:
250  with open(configTemplate,"r") as f:
251  tmpFile = f.read()
252  except IOError:
253  print "The config-template '"+configTemplate+"' cannot be found."
254  raise SystemExit
255 
256  tmpFile = re.sub('setupGlobaltag\s*\=\s*[\"\'](.*?)[\"\']',
257  'setupGlobaltag = \"'+globalTag+'\"',
258  tmpFile)
259 
260  thisCfgTemplate = "tmp.py"
261  with open(thisCfgTemplate, "w") as f:
262  f.write(tmpFile)
263 
264  for setting in pedesettings:
265  print
266  print "="*60
267  if setting is None:
268  print "Creating pede job."
269  else:
270  print "Creating pede jobs using settings from '{0}'.".format(setting)
271  for weight_conf in weight_confs:
272  print "-"*60
273  # blank weights
274  os.system("mps_weight.pl -c > /dev/null")
275 
276  for name,weight in weight_conf:
277  os.system("mps_weight.pl -N "+name+" "+weight)
278 
279  # create new mergejob
280  os.system("mps_setupm.pl")
281 
282  # read mps.db to find directory of new mergejob
283  lib = mpslib.jobdatabase()
284  lib.read_db()
285 
286  # delete old merge-config
287  command = "rm -f jobData/"+lib.JOBDIR[-1]+"/alignment_merge.py"
288  print command
289  os.system(command)
290 
291  # create new merge-config
292  command = ("mps_merge.py -w "+thisCfgTemplate+" jobData/"+
293  lib.JOBDIR[-1]+"/alignment_merge.py jobData/"+
294  lib.JOBDIR[-1]+" "+str(lib.nJobs))
295  if setting is not None: command += " -a "+setting
296  print command
297  if args.verbose:
298  subprocess.call(command, stderr=subprocess.STDOUT, shell=True)
299  else:
300  with open(os.devnull, 'w') as FNULL:
301  subprocess.call(command, stdout=FNULL,
302  stderr=subprocess.STDOUT, shell=True)
303 
304  # remove temporary file
305  os.system("rm "+thisCfgTemplate)
306  sys.exit()
307 
308 
309 #------------------------------------------------------------------------------
310 # loop over dataset-sections
311 firstDataset = True
312 overrideGT = ""
313 for section in config.sections():
314  if 'general' in section:
315  continue
316  elif section.startswith("dataset:"):
317  datasetOptions={}
318  print "-"*60
319 
320  # set name from section-name
321  datasetOptions['name'] = section[8:]
322 
323  # extract essential variables
324  for var in ['inputFileList','collection']:
325  try:
326  datasetOptions[var] = config.get(section,var)
327  except ConfigParser.NoOptionError:
328  print 'No', var, 'found in', section+'. Please check ini-file.'
329  raise SystemExit
330 
331  # get globaltag and configTemplate. If none in section, try to get default from [general] section.
332  for var in ['configTemplate','globaltag']:
333  if config.has_option(section,var):
334  datasetOptions[var] = config.get(section,var)
335  else:
336  try:
337  datasetOptions[var] = generalOptions[var]
338  except KeyError:
339  print "No",var,"found in ["+section+"]",
340  print "and no default in [general] section."
341  raise SystemExit
342 
343  # extract non-essential options
344  datasetOptions['cosmicsZeroTesla'] = False
345  if config.has_option(section,'cosmicsZeroTesla'):
346  datasetOptions['cosmicsZeroTesla'] = config.getboolean(section,'cosmicsZeroTesla')
347 
348  datasetOptions['cosmicsDecoMode'] = False
349  if config.has_option(section,'cosmicsDecoMode'):
350  datasetOptions['cosmicsDecoMode'] = config.getboolean(section,'cosmicsDecoMode')
351 
352  datasetOptions['primaryWidth'] = -1.0
353  if config.has_option(section,'primaryWidth'):
354  datasetOptions['primaryWidth'] = config.getfloat(section,'primaryWidth')
355 
356  datasetOptions['json'] = ''
357  if config.has_option(section, 'json'):
358  datasetOptions['json'] = config.get(section,'json')
359  else:
360  try:
361  datasetOptions['json'] = generalOptions['json']
362  except KeyError:
363  print "No json given in either [general] or ["+section+"] sections.",
364  print "Proceeding without json-file."
365 
366 
367  # replace '${datasetdir}' and other variables in inputFileList-path
368  datasetOptions['inputFileList'] = os.path.expandvars(datasetOptions['inputFileList'])
369 
370  # replace variables in configTemplate-path, e.g. $CMSSW_BASE
371  datasetOptions['configTemplate'] = os.path.expandvars(datasetOptions['configTemplate'])
372 
373 
374  # Get number of jobs from lines in inputfilelist
375  datasetOptions['njobs'] = 0
376  try:
377  with open(datasetOptions['inputFileList'],'r') as filelist:
378  for line in filelist:
379  if 'CastorPool' in line:
380  continue
381  # ignore empty lines
382  if not line.strip()=='':
383  datasetOptions['njobs'] += 1
384  except IOError:
385  print 'Inputfilelist', datasetOptions['inputFileList'], 'does not exist.'
386  raise SystemExit
387  if datasetOptions['njobs'] == 0:
388  print 'Number of jobs is 0. There may be a problem with the inputfilelist:'
389  print datasetOptions['inputFileList']
390  raise SystemExit
391 
392  # Check if njobs gets overwritten in .ini-file
393  if config.has_option(section,'njobs'):
394  if config.getint(section,'njobs')<=datasetOptions['njobs']:
395  datasetOptions['njobs'] = config.getint(section,'njobs')
396  else:
397  print 'njobs is bigger than the default',datasetOptions['njobs'],'. Using default.'
398  else:
399  print 'No number of jobs specified. Using number of files in inputfilelist as the number of jobs.'
400 
401 
402  # Build config from template/Fill in variables
403  try:
404  with open(datasetOptions['configTemplate'],'r') as INFILE:
405  tmpFile = INFILE.read()
406  except IOError:
407  print 'The config-template called',datasetOptions['configTemplate'],'cannot be found.'
408  raise SystemExit
409 
410  tmpFile = re.sub('setupGlobaltag\s*\=\s*[\"\'](.*?)[\"\']',
411  'setupGlobaltag = \"'+datasetOptions['globaltag']+'\"',
412  tmpFile)
413  tmpFile = re.sub('setupCollection\s*\=\s*[\"\'](.*?)[\"\']',
414  'setupCollection = \"'+datasetOptions['collection']+'\"',
415  tmpFile)
416  if datasetOptions['cosmicsZeroTesla']:
417  tmpFile = re.sub(re.compile('setupCosmicsZeroTesla\s*\=\s*.*$', re.M),
418  'setupCosmicsZeroTesla = True',
419  tmpFile)
420  if datasetOptions['cosmicsDecoMode']:
421  tmpFile = re.sub(re.compile('setupCosmicsDecoMode\s*\=\s*.*$', re.M),
422  'setupCosmicsDecoMode = True',
423  tmpFile)
424  if datasetOptions['primaryWidth'] > 0.0:
425  tmpFile = re.sub(re.compile('setupPrimaryWidth\s*\=\s*.*$', re.M),
426  'setupPrimaryWidth = '+str(datasetOptions['primaryWidth']),
427  tmpFile)
428  if datasetOptions['json'] != '':
429  tmpFile = re.sub(re.compile('setupJson\s*\=\s*.*$', re.M),
430  'setupJson = \"'+datasetOptions['json']+'\"',
431  tmpFile)
432 
433  thisCfgTemplate = 'tmp.py'
434  with open(thisCfgTemplate, 'w') as OUTFILE:
435  OUTFILE.write(tmpFile)
436 
437 
438  # Set mps_setup append option for datasets following the first one
439  append = ' -a'
440  if firstDataset:
441  append = ''
442  firstDataset = False
443  configTemplate = tmpFile
444  overrideGT = create_input_db(thisCfgTemplate,
445  generalOptions["FirstRunForStartGeometry"])
446 
447  with open(thisCfgTemplate, "a") as f: f.write(overrideGT)
448 
449 
450  # create mps_setup command
451  command = 'mps_setup.pl -m%s -M %s -N %s %s %s %s %d %s %s %s cmscafuser:%s' % (
452  append,
453  generalOptions['pedeMem'],
454  datasetOptions['name'],
455  milleScript,
456  thisCfgTemplate,
457  datasetOptions['inputFileList'],
458  datasetOptions['njobs'],
459  generalOptions['classInf'],
460  generalOptions['jobname'],
461  pedeScript,
462  mssDir)
463  # Some output:
464  print 'Submitting dataset:', datasetOptions['name']
465  print 'Baseconfig: ', datasetOptions['configTemplate']
466  print 'Collection: ', datasetOptions['collection']
467  if datasetOptions["collection"] in ("ALCARECOTkAlCosmicsCTF0T",
468  "ALCARECOTkAlCosmicsInCollisions"):
469  print 'cosmicsDecoMode: ', datasetOptions['cosmicsDecoMode']
470  print 'cosmicsZeroTesla: ', datasetOptions['cosmicsZeroTesla']
471  print 'Globaltag: ', datasetOptions['globaltag']
472  print 'Number of jobs: ', datasetOptions['njobs']
473  print 'Inputfilelist: ', datasetOptions['inputFileList']
474  if datasetOptions['json'] != '':
475  print 'Jsonfile: ', datasetOptions['json']
476  print 'Pass to mps_setup: ', command
477 
478  # call the command and toggle verbose output
479  if args.verbose:
480  subprocess.call(command, stderr=subprocess.STDOUT, shell=True)
481  else:
482  with open(os.devnull, 'w') as FNULL:
483  subprocess.call(command, stdout=FNULL,
484  stderr=subprocess.STDOUT, shell=True)
485 
486  # remove temporary file
487  os.system("rm "+thisCfgTemplate)
488 
489 if firstDataset:
490  print "No dataset section defined in '{0}'".format(aligmentConfig)
491  print "At least one section '[dataset:<name>]' is required."
492  sys.exit(1)
493 
494 firstPedeConfig = True
495 for setting in pedesettings:
496  print
497  print "="*60
498  if setting is None:
499  print "Creating pede job."
500  else:
501  print "Creating pede jobs using settings from '{0}'.".format(setting)
502  for weight_conf in weight_confs:
503  print "-"*60
504  # blank weights
505  os.system("mps_weight.pl -c > /dev/null")
506 
507  for name,weight in weight_conf:
508  os.system("mps_weight.pl -N "+name+" "+weight)
509 
510  if firstPedeConfig:
511  firstPedeConfig = False
512  else:
513  # create new mergejob
514  os.system("mps_setupm.pl")
515 
516  # read mps.db to find directory of new mergejob
517  lib = mpslib.jobdatabase()
518  lib.read_db()
519 
520  # delete old merge-config
521  command = "rm -f jobData/"+lib.JOBDIR[-1]+"/alignment_merge.py"
522  print command
523  os.system(command)
524 
525  thisCfgTemplate = "tmp.py"
526  with open(thisCfgTemplate, "w") as f:
527  f.write(configTemplate+overrideGT)
528 
529  # create new merge-config
530  command = ("mps_merge.py -w "+thisCfgTemplate+" jobData/"+lib.JOBDIR[-1]+
531  "/alignment_merge.py jobData/"+lib.JOBDIR[-1]+" "+
532  str(lib.nJobs))
533  if setting is not None: command += " -a "+setting
534  print command
535  if args.verbose:
536  subprocess.call(command, stderr=subprocess.STDOUT, shell=True)
537  else:
538  with open(os.devnull, 'w') as FNULL:
539  subprocess.call(command, stdout=FNULL,
540  stderr=subprocess.STDOUT, shell=True)
541 
542  # remove temporary file
543  os.system("rm "+thisCfgTemplate)
544 
545 if overrideGT.strip() != "":
546  print "="*60
547  msg = ("Overriding global tag with single-IOV tags extracted from '{}' for "
548  "run number '{}'.".format(generalOptions["globaltag"],
549  generalOptions["FirstRunForStartGeometry"]))
550  print msg
551  print "-"*60
552  print overrideGT
def get_weight_configs
Definition: mps_alisetup.py:27
def create_input_db
Definition: mps_alisetup.py:79
def checked_out_MPS
Definition: helper.py:3
double split
Definition: MVATrainer.cc:139