CMS 3D CMS Logo

mps_alisetup.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 from __future__ import print_function
4 import os
5 import re
6 import sys
7 if sys.version_info[0]>2:
8  import _pickle as cPickle
9 else:
10  import cPickle
11 import argparse
12 import itertools
13 import subprocess
14 import collections
15 import configparser as ConfigParser
16 import Alignment.MillePedeAlignmentAlgorithm.mpslib.tools as mps_tools
17 import Alignment.MillePedeAlignmentAlgorithm.mpslib.Mpslibclass as mpslib
18 import Alignment.MillePedeAlignmentAlgorithm.mpsvalidate.iniparser as mpsv_iniparser
19 import Alignment.MillePedeAlignmentAlgorithm.mpsvalidate.trackerTree as mpsv_trackerTree
20 from Alignment.MillePedeAlignmentAlgorithm.alignmentsetup.helper import checked_out_MPS
21 from functools import reduce
22 
23 
24 
25 def main(argv = None):
26  """Main routine. Not called, if this module is loaded via `import`.
27 
28  Arguments:
29  - `argv`: Command line arguments passed to the script.
30  """
31 
32  if argv == None:
33  argv = sys.argv[1:]
34 
35  setup_alignment = SetupAlignment(argv)
36  setup_alignment.setup()
37 
38 
39 
41  """Class encapsulating the alignment campaign setup procedure."""
42 
43  def __init__(self, argv):
44  """Constructor
45 
46  Arguments:
47  - `argv`: command line arguments
48  """
49 
50  self._argv = argv # raw command line arguments
51  self._args = None # parsed command line arguments
52  self._config = None # ConfigParser object
53  self._mss_dir = None # mass storage directory
54  self._datasets = collections.OrderedDict() # final dataset configs
55  self._first_run = None # first run for start geometry
56  self._cms_process = None # cms.Process extracted from CMSSW config
57  self._override_gt = None # snippet to append to config
58  self._pede_script = None # path to pede batch script template
59  self._weight_dict = collections.OrderedDict() # dictionary with dataset weights
60  self._mille_script = None # path to mille batch script template
61  self._mps_dir_name = None # MP campaign name (mp<ID>)
62  self._common_weights = {} # dictionary with dataset weights from [weights] section
63  self._weight_configs = [] # list with combinations of dataset weights
64  self._general_options = {} # general options extracted from ini file
65  self._external_datasets = collections.OrderedDict() # external dataset configs
66  self._first_pede_config = True # does a pede job exist already?
67 
68  self._create_config()
70  self._fetch_datasets()
71  self._construct_paths()
75 
76 
77  def setup(self):
78  """Setup the alignment campaign."""
79 
80  if self._args.weight:
82  else:
83  self._create_mille_jobs()
84  self._create_pede_jobs()
85 
86  if self._override_gt.strip() != "":
87  msg = ("Overriding global tag with single-IOV tags extracted from "
88  "'{}' for run number '{}'.".format(self._global_tag,
89  self._first_run))
90  print(msg)
91  print("-"*75)
92  print(self._override_gt)
93  print("="*75)
94 
95 
96  def _create_config(self):
97  """Create ConfigParser object from command line arguments."""
98 
99  helpEpilog ="""Builds the config-templates from a universal
100  config-template for each dataset specified in .ini-file that is passed
101  to this script. Then calls mps_setup.py for all datasets."""
102  parser = argparse.ArgumentParser(
103  description = ("Setup the alignment as configured in the "
104  "alignment_config file."),
105  epilog = helpEpilog)
106  parser.add_argument("-v", "--verbose", action="store_true",
107  help="display detailed output of mps_setup")
108  parser.add_argument("-w", "--weight", action="store_true",
109  help=("creates additional merge job(s) with "
110  "(possibly new) weights from .ini-config"))
111  parser.add_argument("alignmentConfig",
112  help=("name of the .ini config file that specifies "
113  "the datasets to be used"))
114 
115  self._args = parser.parse_args(self._argv)
116  self._config = ConfigParser.ConfigParser()
117  self._config.optionxform = str # default would give lowercase options
118  # -> not wanted
119  self._config.read(self._args.alignmentConfig)
120  self._config.config_path = self._args.alignmentConfig
121 
122 
123  def _construct_paths(self):
124  """Determine directory paths and create the ones that are needed."""
125 
126  mpsTemplates = os.path.join("src", "Alignment",
127  "MillePedeAlignmentAlgorithm", "templates")
128  if checked_out_MPS()[0]:
129  mpsTemplates = os.path.join(os.environ["CMSSW_BASE"], mpsTemplates)
130  else:
131  mpsTemplates = os.path.join(os.environ["CMSSW_RELEASE_BASE"], mpsTemplates)
132  self._mille_script = os.path.join(mpsTemplates, "mps_runMille_template.sh")
133  self._pede_script = os.path.join(mpsTemplates, "mps_runPede_rfcp_template.sh")
134 
135  # get working directory name
136  currentDir = os.getcwd()
137  match = re.search(re.compile('mpproduction\/mp(.+?)$', re.M|re.I),currentDir)
138  if match:
139  self._mps_dir_name = 'mp'+match.group(1)
140  else:
141  print("Current location does not seem to be a MillePede campaign directory:", end=' ')
142  print(currentDir)
143  sys.exit(1)
144 
145 
147  """Create and fill `general_options` dictionary."""
148 
149  print("="*75)
152  self._fetch_essentials()
153  self._fetch_defaults()
154 
155 
157  """Fetch information about external datasets."""
158 
159  if self._config.has_option("general", "externalDatasets"):
160  datasets = map(lambda x: x.strip(),
161  self._config.get("general",
162  "externalDatasets").split(","))
163  datasets = [x for x in datasets if len(x.strip()) > 0]
164  for item in datasets:
165  splitted = item.split("|")
166  dataset = splitted[0].strip()
167  dataset = os.path.expandvars(dataset)
168 
169  weight = splitted[1] if len(splitted) > 1 else None
170  config = ConfigParser.ConfigParser()
171  config.optionxform = str
172  config.read(dataset)
173  config.config_path = dataset
174  self._external_datasets[dataset] = {"config": config,
175  "weight": weight}
176 
177 
178 
180  """
181  Create MPS mass storage directory where, e.g., mille binaries are
182  stored.
183  """
184 
185  # set directory on eos
186  self._mss_dir = self._general_options.get("massStorageDir",
187  "/eos/cms/store/group/alca_millepede/")
188  self._mss_dir = os.path.join(self._mss_dir, "MPproductionFiles",
189  self._mps_dir_name)
190 
191  cmd = ["mkdir", "-p", self._mss_dir]
192 
193  # create directory
194  if not self._general_options.get("testMode", False):
195  try:
196  with open(os.devnull, "w") as dump:
197  subprocess.check_call(cmd, stdout = dump, stderr = dump)
198  except subprocess.CalledProcessError:
199  print("Failed to create mass storage directory:", self._mss_dir)
200  sys.exit(1)
201 
202 
204  """Extract different weight configurations from `self._config`."""
205 
206  weights_list = [[(name, weight) for weight in self._weight_dict[name]]
207  for name in self._weight_dict]
208 
209  common_weights_list = [[(name, weight)
210  for weight in self._common_weights[name]]
211  for name in self._common_weights]
212 
213  common_weights_dicts = []
214  for item in itertools.product(*common_weights_list):
215  d = {}
216  for name,weight in item:
217  d[name] = weight
218  common_weights_dicts.append(d)
219 
220  weight_configs = []
221  for weight_conf in itertools.product(*weights_list):
222  number_of_configs = len(weight_configs)
223  for common_weight in common_weights_dicts:
224  replaced_config \
225  = tuple([(dataset[0],
226  reduce(lambda x,y: mps_tools.replace_factors(x, y, common_weight[y]),
227  common_weight, dataset[1]))
228  for dataset in weight_conf])
229  if replaced_config not in weight_configs:
230  weight_configs.append(replaced_config)
231 
232  # default if config contains no common weights:
233  if len(weight_configs) == number_of_configs:
234  weight_configs.append(weight_conf)
235 
236  for weight_config in weight_configs:
237  resolved_weight_config \
238  = [(dataset[0], mps_tools.compute_product_string(dataset[1]))
239  for dataset in weight_config]
240  self._weight_configs.append(resolved_weight_config)
241 
242 
244  """Fetch 'pedesettings' from general section in `self._config`."""
245 
246  self._pede_settings \
247  = ([x.strip()
248  for x in self._config.get("general", "pedesettings").split(",")]
249  if self._config.has_option("general", "pedesettings") else [None])
250 
251 
253  """Create the mille jobs based on the [dataset:<name>] sections."""
254 
255  gt_regex = re.compile('setupGlobaltag\s*\=\s*[\"\'](.*?)[\"\']')
256  sg_regex = re.compile("setupRunStartGeometry\s*\=\s*.*$", re.M)
257  collection_regex = re.compile('setupCollection\s*\=\s*[\"\'](.*?)[\"\']')
258  czt_regex = re.compile('setupCosmicsZeroTesla\s*\=\s*.*$', re.M)
259  cdm_regex = re.compile('setupCosmicsDecoMode\s*\=\s*.*$', re.M)
260  pw_regex = re.compile('setupPrimaryWidth\s*\=\s*.*$', re.M)
261  json_regex = re.compile('setupJson\s*\=\s*.*$', re.M)
262 
263  first_dataset = True
264  for name, dataset in self._datasets.items():
265  print("="*75)
266  # Build config from template/Fill in variables
267  try:
268  with open(dataset["configTemplate"],"r") as f:
269  tmpFile = f.read()
270  except IOError:
271  print("The config-template called", end=' ')
272  print(dataset["configTemplate"], "cannot be found.")
273  sys.exit(1)
274 
275  tmpFile = re.sub(gt_regex,
276  'setupGlobaltag = \"'+dataset["globaltag"]+'\"',
277  tmpFile)
278  tmpFile = re.sub(sg_regex,
279  "setupRunStartGeometry = "+
280  self._general_options["FirstRunForStartGeometry"], tmpFile)
281  tmpFile = re.sub(collection_regex,
282  'setupCollection = \"'+dataset["collection"]+'\"',
283  tmpFile)
284  if "ALCARECOTkAlCosmics" in dataset["collection"]:
285  if dataset['cosmicsZeroTesla']:
286  tmpFile = re.sub(czt_regex,
287  'setupCosmicsZeroTesla = True',
288  tmpFile)
289  else :
290  tmpFile = re.sub(czt_regex,
291  'setupCosmicsZeroTesla = False',
292  tmpFile)
293 
294  if dataset['cosmicsDecoMode']:
295  tmpFile = re.sub(cdm_regex,
296  'setupCosmicsDecoMode = True',
297  tmpFile)
298  else:
299  tmpFile = re.sub(cdm_regex,
300  'setupCosmicsDecoMode = False',
301  tmpFile)
302 
303  if dataset['primaryWidth'] > 0.0:
304  tmpFile = re.sub(pw_regex,
305  'setupPrimaryWidth = '+str(dataset["primaryWidth"]),
306  tmpFile)
307  if dataset['json'] != '':
308  tmpFile = re.sub(json_regex,
309  'setupJson = \"'+dataset["json"]+'\"',
310  tmpFile)
311 
312  thisCfgTemplate = "tmp.py"
313  with open(thisCfgTemplate, "w") as f:
314  f.write(tmpFile)
315 
316 
317  # Set mps_setup append option for datasets following the first one
318  append = "-a"
319  if first_dataset:
320  append = ""
321  first_dataset = False
322  self._config_template = tmpFile
323  self._cms_process = mps_tools.get_process_object(thisCfgTemplate)
324  self._create_input_db()
325 
326  with open(thisCfgTemplate, "a") as f: f.write(self._override_gt)
327 
328 
329  # create mps_setup command
330  command = ["mps_setup.py",
331  "-m",
332  append,
333  "-M", self._general_options["pedeMem"],
334  "-N", name,
335  self._mille_script,
336  thisCfgTemplate,
337  dataset["inputFileList"],
338  str(dataset["njobs"]),
339  self._general_options["classInf"],
340  self._general_options["jobname"],
341  self._pede_script,
342  "cmscafuser:"+self._mss_dir]
343  if dataset["numberOfEvents"] > 0:
344  command.extend(["--max-events", str(dataset["numberOfEvents"])])
345  command = [x for x in command if len(x.strip()) > 0]
346 
347  # Some output:
348  print("Creating jobs for dataset:", name)
349  print("-"*75)
350  print("Baseconfig: ", dataset["configTemplate"])
351  print("Collection: ", dataset["collection"])
352  if "ALCARECOTkAlCosmics" in dataset["collection"]:
353  print("cosmicsDecoMode: ", dataset["cosmicsDecoMode"])
354  print("cosmicsZeroTesla: ", dataset["cosmicsZeroTesla"])
355  print("Globaltag: ", dataset["globaltag"])
356  print("Number of jobs: ", dataset["njobs"])
357  print("Inputfilelist: ", dataset["inputFileList"])
358  if dataset["json"] != "":
359  print("Jsonfile: ", dataset["json"])
360  if self._args.verbose:
361  print("Pass to mps_setup: ", " ".join(command))
362 
363  # call the command and toggle verbose output
364  self._handle_process_call(command, self._args.verbose)
365 
366  # remove temporary file
367  self._handle_process_call(["rm", thisCfgTemplate])
368 
369 
370  def _create_pede_jobs(self):
371  """Create pede jobs from the given input."""
372 
373  for setting in self._pede_settings:
374  print()
375  print("="*75)
376  if setting is None:
377  print("Creating pede job{}.".format(
378  "s" if len(self._pede_settings)*len(self._weight_configs) > 1 else ""))
379  print("-"*75)
380  else:
381  print("Creating pede jobs using settings from '{0}'.".format(setting))
382  for weight_conf in self._weight_configs:
383  # blank weights
384  self._handle_process_call(["mps_weight.pl", "-c"])
385 
386  thisCfgTemplate = "tmp.py"
387  with open(thisCfgTemplate, "w") as f: f.write(self._config_template)
388  if self._override_gt is None:
389  self._cms_process = mps_tools.get_process_object(thisCfgTemplate)
390  self._create_input_db()
391  with open(thisCfgTemplate, "a") as f: f.write(self._override_gt)
392 
393  for name,weight in weight_conf:
394  self._handle_process_call(["mps_weight.pl", "-N", name, weight], True)
395 
396  if not self._first_pede_config:
397  # create new mergejob
398  self._handle_process_call(["mps_setupm.pl"], self._args.verbose)
399 
400  # read mps.db to find directory of new mergejob
401  lib = mpslib.jobdatabase()
402  lib.read_db()
403 
404  # short cut for jobm path
405  jobm_path = os.path.join("jobData", lib.JOBDIR[-1])
406 
407  # delete old merge-config
408  command = ["rm", "-f", os.path.join(jobm_path, "alignment_merge.py")]
409  self._handle_process_call(command, self._args.verbose)
410 
411  # create new merge-config
412  command = [
413  "mps_merge.py",
414  "-w", thisCfgTemplate,
415  os.path.join(jobm_path, "alignment_merge.py"),
416  jobm_path,
417  str(lib.nJobs),
418  ]
419  if setting is not None: command.extend(["-a", setting])
420  print("-"*75)
421  print(" ".join(command))
422  self._handle_process_call(command, self._args.verbose)
423  self._create_tracker_tree()
424  if self._first_pede_config:
425  os.symlink(self._tracker_tree_path,
426  os.path.abspath(os.path.join(jobm_path,
427  ".TrackerTree.root")))
428  self._first_pede_config = False
429 
430  # store weights configuration
431  with open(os.path.join(jobm_path, ".weights.pkl"), "wb") as f:
432  cPickle.dump(weight_conf, f, 2)
433  print("="*75)
434 
435  # remove temporary file
436  self._handle_process_call(["rm", thisCfgTemplate])
437 
438 
440  """
441  Create pede jobs in addition to already existing ones. Return GT
442  override snippet.
443  """
444 
445  # do some basic checks
446  if not os.path.isdir("jobData"):
447  print("No jobData-folder found.", end=' ')
448  print("Properly set up the alignment before using the -w option.")
449  sys.exit(1)
450  if not os.path.exists("mps.db"):
451  print("No mps.db found.", end=' ')
452  print("Properly set up the alignment before using the -w option.")
453  sys.exit(1)
454 
455  firstDataset = next(iter(self._datasets.values()))
456  config_template = firstDataset["configTemplate"]
457  collection = firstDataset["collection"]
458 
459  try:
460  with open(config_template,"r") as f:
461  tmpFile = f.read()
462  except IOError:
463  print("The config-template '"+config_template+"' cannot be found.")
464  sys.exit(1)
465 
466  tmpFile = re.sub('setupGlobaltag\s*\=\s*[\"\'](.*?)[\"\']',
467  'setupGlobaltag = \"'+self._global_tag+'\"',
468  tmpFile)
469  tmpFile = re.sub('setupCollection\s*\=\s*[\"\'](.*?)[\"\']',
470  'setupCollection = \"'+collection+'\"',
471  tmpFile)
472  tmpFile = re.sub(re.compile("setupRunStartGeometry\s*\=\s*.*$", re.M),
473  "setupRunStartGeometry = "+self._first_run,
474  tmpFile)
475  self._config_template = tmpFile
476 
477  # first pede job exists already in this mode:
478  self._first_pede_config = False
479  self._create_pede_jobs()
480 
481 
482  def _handle_process_call(self, command, verbose = False):
483  """
484  Wrapper around subprocess calls which treats output depending on verbosity
485  level.
486 
487  Arguments:
488  - `command`: list of command items
489  - `verbose`: flag to turn on verbosity
490  """
491 
492  call_method = subprocess.check_call if verbose else subprocess.check_output
493  try:
494  call_method(command, stderr=subprocess.STDOUT)
495  except subprocess.CalledProcessError as e:
496  print("" if verbose else e.output)
497  print("Failed to execute command:", " ".join(command))
498  sys.exit(1)
499 
500 
501  def _create_input_db(self):
502  """
503  Create sqlite file with single-IOV tags and use it to override the
504  GT. If the GT is already customized by the user, the customization has
505  higher priority. Creates a snippet to be appended to the configuration
506  file.
507  """
508 
509  run_number = int(self._first_run)
510  if not run_number > 0:
511  print("'FirstRunForStartGeometry' must be positive, but is", run_number)
512  sys.exit(1)
513 
514  input_db_name = os.path.abspath("alignment_input.db")
515  tags = mps_tools.create_single_iov_db(self._check_iov_definition(),
516  run_number, input_db_name)
517 
518  self._override_gt = ""
519  for record,tag in tags.items():
520  if self._override_gt == "":
521  self._override_gt \
522  += ("\nimport "
523  "Alignment.MillePedeAlignmentAlgorithm.alignmentsetup."
524  "SetCondition as tagwriter\n")
525  self._override_gt += ("\ntagwriter.setCondition(process,\n"
526  " connect = \""+tag["connect"]+"\",\n"
527  " record = \""+record+"\",\n"
528  " tag = \""+tag["tag"]+"\")\n")
529 
530 
532  """
533  Check consistency of input alignment payloads and IOV definition.
534  Returns a dictionary with the information needed to override possibly
535  problematic input taken from the global tag.
536  """
537 
538  print("Checking consistency of IOV definition...")
539  iovs = mps_tools.make_unique_runranges(self._cms_process.AlignmentProducer)
540 
541  inputs = {
542  "TrackerAlignmentRcd": None,
543  "TrackerSurfaceDeformationRcd": None,
544  "TrackerAlignmentErrorExtendedRcd": None,
545  }
546 
547  for condition in self._cms_process.GlobalTag.toGet.value():
548  if condition.record.value() in inputs:
549  inputs[condition.record.value()] = {
550  "tag": condition.tag.value(),
551  "connect": ("pro"
552  if not condition.hasParameter("connect")
553  else condition.connect.value())
554  }
555 
556  inputs_from_gt = [record for record in inputs if inputs[record] is None]
557  inputs.update(
558  mps_tools.get_tags(self._cms_process.GlobalTag.globaltag.value(),
559  inputs_from_gt))
560 
561  if int(self._first_run) != iovs[0]: # simple consistency check
562  if iovs[0] == 1 and len(iovs) == 1:
563  print("Single IOV output detected in configuration and", end=' ')
564  print("'FirstRunForStartGeometry' is not 1.")
565  print("Creating single IOV output from input conditions in run", end=' ')
566  print(self._first_run+".")
567  for inp in inputs: inputs[inp]["problematic"] = True
568  else:
569  print("Value of 'FirstRunForStartGeometry' has to match first", end=' ')
570  print("defined output IOV:", end=' ')
571  print(self._first_run, "!=", iovs[0])
572  sys.exit(1)
573 
574  for inp in inputs.values():
575  inp["iovs"] = mps_tools.get_iovs(inp["connect"], inp["tag"])
576 
577  # check consistency of input with output
578  problematic_gt_inputs = {}
579  input_indices = {key: len(value["iovs"]) -1
580  for key,value in inputs.items()}
581  for iov in reversed(iovs):
582  for inp in inputs:
583  if inputs[inp].pop("problematic", False):
584  problematic_gt_inputs[inp] = inputs[inp]
585  if inp in problematic_gt_inputs: continue
586  if input_indices[inp] < 0:
587  print("First output IOV boundary at run", iov, end=' ')
588  print("is before the first input IOV boundary at", end=' ')
589  print(inputs[inp]["iovs"][0], "for '"+inp+"'.")
590  print("Please check your run range selection.")
591  sys.exit(1)
592  input_iov = inputs[inp]["iovs"][input_indices[inp]]
593  if iov < input_iov:
594  if inp in inputs_from_gt:
595  problematic_gt_inputs[inp] = inputs[inp]
596  print("Found problematic input taken from global tag.")
597  print("Input IOV boundary at run",input_iov, end=' ')
598  print("for '"+inp+"' is within output IOV starting", end=' ')
599  print("with run", str(iov)+".")
600  print("Deriving an alignment with coarse IOV", end=' ')
601  print("granularity starting from finer granularity", end=' ')
602  print("leads to wrong results.")
603  print("A single IOV input using the IOV of", end=' ')
604  print("'FirstRunForStartGeometry' ("+self._first_run+")", end=' ')
605  print("is automatically created and used.")
606  continue
607  print("Found input IOV boundary at run",input_iov, end=' ')
608  print("for '"+inp+"' which is within output IOV starting", end=' ')
609  print("with run", str(iov)+".")
610  print("Deriving an alignment with coarse IOV granularity", end=' ')
611  print("starting from finer granularity leads to wrong", end=' ')
612  print("results.")
613  print("Please check your run range selection.")
614  sys.exit(1)
615  elif iov == input_iov:
616  input_indices[inp] -= 1
617 
618  # check consistency of 'TrackerAlignmentRcd' with other inputs
619  input_indices = {key: len(value["iovs"]) -1
620  for key,value in inputs.items()
621  if (key != "TrackerAlignmentRcd")
622  and (inp not in problematic_gt_inputs)}
623  for iov in reversed(inputs["TrackerAlignmentRcd"]["iovs"]):
624  for inp in input_indices:
625  input_iov = inputs[inp]["iovs"][input_indices[inp]]
626  if iov < input_iov:
627  print("Found input IOV boundary at run",input_iov, end=' ')
628  print("for '"+inp+"' which is within 'TrackerAlignmentRcd'", end=' ')
629  print("IOV starting with run", str(iov)+".")
630  print("Deriving an alignment with inconsistent IOV boundaries", end=' ')
631  print("leads to wrong results.")
632  print("Please check your input IOVs.")
633  sys.exit(1)
634  elif iov == input_iov:
635  input_indices[inp] -= 1
636 
637  print(" -> IOV consistency check successful.")
638  print("="*75)
639 
640  return problematic_gt_inputs
641 
642 
644  """Method to create hidden 'TrackerTree.root'."""
645 
646  if self._global_tag is None or self._first_run is None:
647  print("Trying to create the tracker tree before setting the global", end=' ')
648  print("tag or the run to determine the geometry IOV.")
649  sys.exit(1)
650 
651  config = mpsv_iniparser.ConfigData()
652  config.jobDataPath = "." # current directory
653  config.globalTag = self._global_tag
654  config.firstRun = self._first_run
655  self._tracker_tree_path = mpsv_trackerTree.check(config)
656 
657 
658  def _fetch_essentials(self):
659  """Fetch general options from config file."""
660 
661  for var in ("classInf","pedeMem","jobname", "FirstRunForStartGeometry"):
662  try:
663  self._general_options[var] = self._config.get('general',var)
664  except ConfigParser.NoOptionError:
665  print("No", var, "found in [general] section.", end=' ')
666  print("Please check ini-file.")
667  sys.exit(1)
668  self._first_run = self._general_options["FirstRunForStartGeometry"]
669 
670 
671  def _fetch_defaults(self):
672  """Fetch default general options from config file."""
673 
674  for var in ("globaltag", "configTemplate", "json", "massStorageDir",
675  "testMode"):
676  try:
677  self._general_options[var] = self._config.get("general", var)
678  except ConfigParser.NoOptionError:
679  if var == "testMode": continue
680  print("No '" + var + "' given in [general] section.")
681 
682  for dataset in self._external_datasets.values():
683  dataset["general"] = {}
684  for var in ("globaltag", "configTemplate", "json"):
685  try:
686  dataset["general"][var] = dataset["config"].get("general", var)
687  except (ConfigParser.NoSectionError,ConfigParser.NoOptionError):
688  pass
689 
690 
692  """
693  Fetch 'datasetDir' variable from general section and add it to the
694  'os.environ' dictionary.
695  """
696 
697  if self._config.has_option("general", "datasetdir"):
698  dataset_directory = self._config.get("general", "datasetdir")
699  # add it to environment for later variable expansion:
700  os.environ["datasetdir"] = dataset_directory
701  self._general_options["datasetdir"] = dataset_directory
702  else:
703  print("No datasetdir given in [general] section.", end=' ')
704  print("Be sure to give a full path in inputFileList.")
705  self._general_options["datasetdir"] = ""
706 
707 
708  def _fetch_datasets(self):
709  """Fetch internal and external dataset configurations."""
710 
711  all_configs = collections.OrderedDict()
712  all_configs["main"] = {"config": self._config,
713  "general": self._general_options,
714  "weight": None}
715  all_configs.update(self._external_datasets)
716 
717  for config in all_configs.values():
718  global_weight = "1" if config["weight"] is None else config["weight"]
719  if global_weight+self._config.config_path in self._common_weights:
720  global_weight = self._common_weights[global_weight+
721  self._config.config_path]
722  elif global_weight in self._common_weights:
723  global_weight = self._common_weights[global_weight]
724  else:
725  global_weight = (global_weight,)
726  common_weights = {}
727  weight_dict = {}
728  for section in config["config"].sections():
729  cache_datasetdir = os.environ["datasetdir"]
730  if "general" in section:
731  if config["config"].has_option("general", "datasetdir"):
732  os.environ["datasetdir"] = config["config"].get("general", "datasetdir")
733  elif section == "weights":
734  for option in config["config"].options(section):
735  common_weights[option] \
736  = [x.strip() for x in
737  config["config"].get(section, option).split(",")]
738  elif section.startswith("dataset:"):
739  print("-"*75)
740  # set name from section-name
741  name = section[8:]
742  if name in self._datasets:
743  print("WARNING: Duplicate definition of dataset '{}'".format(name))
744  print(" -> Using defintion in '{}':\n".format(config["config"].config_path))
745  print(" [{}]".format(section))
746  for k,v in config["config"].items(section):
747  print(" ", k, "=", v)
748  print()
749  self._datasets[name] = {}
750 
751  # extract weight for the dataset
752  if config["config"].has_option(section, "weight"):
753  self._weight_dict[name] \
754  = [x.strip() for x in
755  config["config"].get(section, "weight").split(",")]
756  else:
757  self._weight_dict[name] = ["1.0"]
758  self._weight_dict[name] = [global_w+"*"+w
759  for w in self._weight_dict[name]
760  for global_w in global_weight]
761  weight_dict[name] = self._weight_dict[name]
762 
763  # extract essential variables
764  for var in ("inputFileList", "collection"):
765  try:
766  self._datasets[name][var] = config["config"].get(section, var)
767  except ConfigParser.NoOptionError:
768  print("No", var, "found in", section+". Please check ini-file.")
769  sys.exit(1)
770 
771  # get globaltag and configTemplate. If none in section, try to get
772  # default from [general] section.
773  for var in ("configTemplate", "globaltag"):
774  try:
775  self._datasets[name][var] = config["config"].get(section, var)
776  except (ConfigParser.NoSectionError,ConfigParser.NoOptionError):
777  try:
778  self._datasets[name][var] = config["general"][var]
779  except KeyError:
780  try:
781  self._datasets[name][var] \
782  = all_configs["main"]["general"][var]
783  except KeyError:
784  print("No",var,"found in ["+section+"]", end=' ')
785  print("and no default in [general] section.")
786  sys.exit(1)
787 
788  # extract non-essential options
789  if "ALCARECOTkAlCosmics" in self._datasets[name]["collection"]:
790  try:
791  self._datasets[name]["cosmicsZeroTesla"] \
792  = config["config"].getboolean(section,"cosmicsZeroTesla")
793  except ConfigParser.NoOptionError:
794  print("No option cosmicsZeroTesla found in", section,"even though it is required for dataset type", self._datasets[name]["collection"], ". Please check ini-file.")
795  sys.exit(1)
796  try:
797  self._datasets[name]["cosmicsDecoMode"] \
798  = config["config"].getboolean(section,"cosmicsDecoMode")
799  except ConfigParser.NoOptionError:
800  print("No option cosmicsDecoMode found in", section,"even though it is required for dataset type", self._datasets[name]["collection"], ".Please check ini-file.")
801  sys.exit(1)
802 
803  self._datasets[name]["primaryWidth"] = -1.0
804  if config["config"].has_option(section,"primaryWidth"):
805  self._datasets[name]["primaryWidth"] \
806  = config["config"].getfloat(section,"primaryWidth")
807 
808  self._datasets[name]["numberOfEvents"] = -1
809  if config["config"].has_option(section, "numberOfEvents"):
810  self._datasets[name]["numberOfEvents"] \
811  = config["config"].getint(section, "numberOfEvents")
812 
813  self._datasets[name]["json"] = ""
814  try:
815  self._datasets[name]["json"] = config["config"].get(section,"json")
816  except ConfigParser.NoOptionError:
817  try:
818  self._datasets[name]["json"] = config["general"]["json"]
819  except KeyError:
820  try:
821  self._datasets[name]["json"] \
822  = all_configs["main"]["general"]["json"]
823  except KeyError:
824  print("No json given in either [general] or", end=' ')
825  print("["+section+"] sections.")
826  print(" -> Proceeding without json-file.")
827 
828 
829  #replace ${datasetdir} and other variables, e.g. $CMSSW_BASE
830  for var in ("inputFileList", "json", "configTemplate"):
831  self._datasets[name][var] \
832  = os.path.expandvars(self._datasets[name][var])
833 
834 
835  # Get number of jobs from lines in inputfilelist
836  self._datasets[name]["njobs"] = 0
837  try:
838  with open(self._datasets[name]["inputFileList"], "r") as filelist:
839  for line in filelist:
840  if "CastorPool" in line:
841  continue
842  # ignore empty lines
843  if not line.strip()=="":
844  self._datasets[name]["njobs"] += 1
845  except IOError:
846  print("Inputfilelist", self._datasets[name]["inputFileList"], end=' ')
847  print("does not exist.")
848  sys.exit(1)
849  if self._datasets[name]["njobs"] == 0:
850  print("Number of jobs is 0. There may be a problem with the inputfilelist:")
851  print(self._datasets[name]["inputFileList"])
852  sys.exit(1)
853 
854  # Check if njobs gets overwritten in .ini-file
855  if config["config"].has_option(section, "njobs"):
856  if config["config"].getint(section, "njobs") <= self._datasets[name]["njobs"]:
857  self._datasets[name]["njobs"] = config["config"].getint(section, "njobs")
858  else:
859  print("'njobs' is bigger than the number of files for this", end=' ')
860  print("dataset:", self._datasets[name]["njobs"])
861  print("Using default.")
862  else:
863  print("No number of jobs specified. Using number of files in", end=' ')
864  print("inputfilelist as the number of jobs.")
865 
866  # check if local weights override global weights and resolve name clashes
867  for weight_name, weight_values in common_weights.items():
868  for key, weight in weight_dict.items():
869  if any([weight_name in w for w in weight]):
870  self._common_weights[weight_name+config["config"].config_path] = weight_values
871  self._weight_dict[key] = [mps_tools.replace_factors(w,
872  weight_name,
873  weight_name+config["config"].config_path)
874  for w in weight]
875  else:
876  self._common_weights[weight_name] = weight_values
877  self._weight_dict[key] = weight
878 
879  os.environ["datasetdir"] = cache_datasetdir
880 
881  if len(self._datasets) == 0:
882  print("No dataset section defined in '{0}'".format(
883  ", ".join([self._args.aligmentConfig]+self._external_datasets.keys())))
884  print("At least one section '[dataset:<name>]' is required.")
885  sys.exit(1)
886 
887  self._global_tag = self._datasets[name]["globaltag"]
888 
889 
890 
891 if __name__ == "__main__":
892  try:
893  main()
894  except KeyboardInterrupt:
895  pass
def _create_mass_storage_directory(self)
def checked_out_MPS()
Definition: helper.py:5
bool any(const std::vector< T > &v, const T &what)
Definition: ECalSD.cc:37
def __init__(self, argv)
Definition: mps_alisetup.py:43
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
def _handle_process_call(self, command, verbose=False)
def _create_additional_pede_jobs(self)
static std::string join(char **cmd)
Definition: RemoteFile.cc:21
Definition: main.py:1
def main(argv=None)
Definition: mps_alisetup.py:25
#define str(s)