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