3 from __future__
import print_function
7 if sys.version_info[0]>2:
8 import _pickle
as cPickle
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
26 """Main routine. Not called, if this module is loaded via `import`.
29 - `argv`: Command line arguments passed to the script.
36 setup_alignment.setup()
41 """Class encapsulating the alignment campaign setup procedure."""
47 - `argv`: command line arguments
78 """Setup the alignment campaign."""
87 msg = (
"Overriding global tag with single-IOV tags extracted from "
97 """Create ConfigParser object from command line arguments."""
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."),
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"))
116 self.
_config = ConfigParser.ConfigParser()
124 """Determine directory paths and create the ones that are needed."""
126 mpsTemplates = os.path.join(
"src",
"Alignment",
127 "MillePedeAlignmentAlgorithm",
"templates")
129 mpsTemplates = os.path.join(os.environ[
"CMSSW_BASE"], mpsTemplates)
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")
136 currentDir = os.getcwd()
137 match = re.search(re.compile(
'mpproduction\/mp(.+?)$', re.M|re.I),currentDir)
141 print(
"Current location does not seem to be a MillePede campaign directory:", end=
' ')
147 """Create and fill `general_options` dictionary."""
157 """Fetch information about external datasets."""
159 if self.
_config.has_option(
"general",
"externalDatasets"):
160 datasets =
map(
lambda x: x.strip(),
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)
169 weight = splitted[1]
if len(splitted) > 1
else None
170 config = ConfigParser.ConfigParser()
171 config.optionxform = str
173 config.config_path = dataset
181 Create MPS mass storage directory where, e.g., mille binaries are
187 "/eos/cms/store/group/alca_millepede/")
191 cmd = [
"mkdir",
"-p", self.
_mss_dir]
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)
204 """Extract different weight configurations from `self._config`."""
206 weights_list = [[(name, weight)
for weight
in self.
_weight_dict[name]]
209 common_weights_list = [[(name, weight)
213 common_weights_dicts = []
214 for item
in itertools.product(*common_weights_list):
216 for name,weight
in item:
218 common_weights_dicts.append(d)
221 for weight_conf
in itertools.product(*weights_list):
222 number_of_configs = len(weight_configs)
223 for common_weight
in common_weights_dicts:
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)
233 if len(weight_configs) == number_of_configs:
234 weight_configs.append(weight_conf)
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]
244 """Fetch 'pedesettings' from general section in `self._config`."""
246 self._pede_settings \
248 for x
in self.
_config.get(
"general",
"pedesettings").
split(
",")]
249 if self.
_config.has_option(
"general",
"pedesettings")
else [
None])
253 """Create the mille jobs based on the [dataset:<name>] sections."""
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)
268 with open(dataset[
"configTemplate"],
"r")
as f:
271 print(
"The config-template called", end=
' ')
272 print(dataset[
"configTemplate"],
"cannot be found.")
275 tmpFile = re.sub(gt_regex,
276 'setupGlobaltag = \"'+dataset[
"globaltag"]+
'\"',
278 tmpFile = re.sub(sg_regex,
279 "setupRunStartGeometry = "+
281 tmpFile = re.sub(collection_regex,
282 'setupCollection = \"'+dataset[
"collection"]+
'\"',
284 if "ALCARECOTkAlCosmics" in dataset[
"collection"]:
285 if dataset[
'cosmicsZeroTesla']:
286 tmpFile = re.sub(czt_regex,
287 'setupCosmicsZeroTesla = True',
290 tmpFile = re.sub(czt_regex,
291 'setupCosmicsZeroTesla = False',
294 if dataset[
'cosmicsDecoMode']:
295 tmpFile = re.sub(cdm_regex,
296 'setupCosmicsDecoMode = True',
299 tmpFile = re.sub(cdm_regex,
300 'setupCosmicsDecoMode = False',
303 if dataset[
'primaryWidth'] > 0.0:
304 tmpFile = re.sub(pw_regex,
305 'setupPrimaryWidth = '+
str(dataset[
"primaryWidth"]),
307 if dataset[
'json'] !=
'':
308 tmpFile = re.sub(json_regex,
309 'setupJson = \"'+dataset[
"json"]+
'\"',
312 thisCfgTemplate =
"tmp.py"
313 with open(thisCfgTemplate,
"w")
as f:
321 first_dataset =
False
323 self.
_cms_process = mps_tools.get_process_object(thisCfgTemplate)
326 with open(thisCfgTemplate,
"a")
as f: f.write(self.
_override_gt)
330 command = [
"mps_setup.py",
337 dataset[
"inputFileList"],
338 str(dataset[
"njobs"]),
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]
348 print(
"Creating jobs for dataset:", name)
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))
371 """Create pede jobs from the given input."""
373 for setting
in self._pede_settings:
378 "s" if len(self._pede_settings)*len(self.
_weight_configs) > 1
else ""))
381 print(
"Creating pede jobs using settings from '{0}'.".
format(setting))
386 thisCfgTemplate =
"tmp.py"
389 self.
_cms_process = mps_tools.get_process_object(thisCfgTemplate)
391 with open(thisCfgTemplate,
"a")
as f: f.write(self.
_override_gt)
393 for name,weight
in weight_conf:
401 lib = mpslib.jobdatabase()
405 jobm_path = os.path.join(
"jobData", lib.JOBDIR[-1])
408 command = [
"rm",
"-f", os.path.join(jobm_path,
"alignment_merge.py")]
414 "-w", thisCfgTemplate,
415 os.path.join(jobm_path,
"alignment_merge.py"),
419 if setting
is not None: command.extend([
"-a", setting])
426 os.path.abspath(os.path.join(jobm_path,
427 ".TrackerTree.root")))
431 with open(os.path.join(jobm_path,
".weights.pkl"),
"wb")
as f:
432 cPickle.dump(weight_conf, f, 2)
441 Create pede jobs in addition to already existing ones. Return GT
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.")
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.")
456 config_template = firstDataset[
"configTemplate"]
457 collection = firstDataset[
"collection"]
460 with open(config_template,
"r")
as f:
463 print(
"The config-template '"+config_template+
"' cannot be found.")
466 tmpFile = re.sub(
'setupGlobaltag\s*\=\s*[\"\'](.*?)[\"\']',
469 tmpFile = re.sub(
'setupCollection\s*\=\s*[\"\'](.*?)[\"\']',
470 'setupCollection = \"'+collection+
'\"',
472 tmpFile = re.sub(re.compile(
"setupRunStartGeometry\s*\=\s*.*$", re.M),
484 Wrapper around subprocess calls which treats output depending on verbosity
488 - `command`: list of command items
489 - `verbose`: flag to turn on verbosity
492 call_method = subprocess.check_call
if verbose
else subprocess.check_output
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))
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
510 if not run_number > 0:
511 print(
"'FirstRunForStartGeometry' must be positive, but is", run_number)
514 input_db_name = os.path.abspath(
"alignment_input.db")
516 run_number, input_db_name)
519 for record,tag
in tags.items():
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")
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.
538 print(
"Checking consistency of IOV definition...")
539 iovs = mps_tools.make_unique_runranges(self.
_cms_process.AlignmentProducer)
542 "TrackerAlignmentRcd":
None,
543 "TrackerSurfaceDeformationRcd":
None,
544 "TrackerAlignmentErrorExtendedRcd":
None,
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(),
552 if not condition.hasParameter(
"connect")
553 else condition.connect.value())
556 inputs_from_gt = [record
for record
in inputs
if inputs[record]
is None]
558 mps_tools.get_tags(self.
_cms_process.GlobalTag.globaltag.value(),
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=
' ')
567 for inp
in inputs: inputs[inp][
"problematic"] =
True
569 print(
"Value of 'FirstRunForStartGeometry' has to match first", end=
' ')
570 print(
"defined output IOV:", end=
' ')
574 for inp
in inputs.values():
575 inp[
"iovs"] = mps_tools.get_iovs(inp[
"connect"], inp[
"tag"])
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):
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.")
592 input_iov = inputs[inp][
"iovs"][input_indices[inp]]
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=
' ')
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=
' ')
605 print(
"is automatically created and used.")
607 print(
"Found input IOV boundary at run",input_iov, end=
' ')
608 print(
"for '"+inp+
"' which is within output IOV starting", end=
' ')
610 print(
"Deriving an alignment with coarse IOV granularity", end=
' ')
611 print(
"starting from finer granularity leads to wrong", end=
' ')
613 print(
"Please check your run range selection.")
615 elif iov == input_iov:
616 input_indices[inp] -= 1
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]]
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.")
634 elif iov == input_iov:
635 input_indices[inp] -= 1
637 print(
" -> IOV consistency check successful.")
640 return problematic_gt_inputs
644 """Method to create hidden 'TrackerTree.root'."""
647 print(
"Trying to create the tracker tree before setting the global", end=
' ')
648 print(
"tag or the run to determine the geometry IOV.")
651 config = mpsv_iniparser.ConfigData()
652 config.jobDataPath =
"."
659 """Fetch general options from config file."""
661 for var
in (
"classInf",
"pedeMem",
"jobname",
"FirstRunForStartGeometry"):
664 except ConfigParser.NoOptionError:
665 print(
"No", var,
"found in [general] section.", end=
' ')
666 print(
"Please check ini-file.")
672 """Fetch default general options from config file."""
674 for var
in (
"globaltag",
"configTemplate",
"json",
"massStorageDir",
678 except ConfigParser.NoOptionError:
679 if var ==
"testMode":
continue
680 print(
"No '" + var +
"' given in [general] section.")
683 dataset[
"general"] = {}
684 for var
in (
"globaltag",
"configTemplate",
"json"):
686 dataset[
"general"][var] = dataset[
"config"].get(
"general", var)
687 except (ConfigParser.NoSectionError,ConfigParser.NoOptionError):
693 Fetch 'datasetDir' variable from general section and add it to the
694 'os.environ' dictionary.
697 if self.
_config.has_option(
"general",
"datasetdir"):
698 dataset_directory = self.
_config.get(
"general",
"datasetdir")
700 os.environ[
"datasetdir"] = dataset_directory
703 print(
"No datasetdir given in [general] section.", end=
' ')
704 print(
"Be sure to give a full path in inputFileList.")
709 """Fetch internal and external dataset configurations."""
711 all_configs = collections.OrderedDict()
712 all_configs[
"main"] = {
"config": self.
_config,
717 for config
in all_configs.values():
718 global_weight =
"1" if config[
"weight"]
is None else config[
"weight"]
725 global_weight = (global_weight,)
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:"):
743 print(
"WARNING: Duplicate definition of dataset '{}'".
format(name))
744 print(
" -> Using defintion in '{}':\n".
format(config[
"config"].config_path))
746 for k,v
in config[
"config"].
items(section):
747 print(
" ", k,
"=", v)
752 if config[
"config"].has_option(section,
"weight"):
754 = [x.strip()
for x
in
755 config[
"config"].get(section,
"weight").
split(
",")]
760 for global_w
in global_weight]
764 for var
in (
"inputFileList",
"collection"):
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.")
773 for var
in (
"configTemplate",
"globaltag"):
775 self.
_datasets[name][var] = config[
"config"].get(section, var)
776 except (ConfigParser.NoSectionError,ConfigParser.NoOptionError):
778 self.
_datasets[name][var] = config[
"general"][var]
782 = all_configs[
"main"][
"general"][var]
784 print(
"No",var,
"found in ["+section+
"]", end=
' ')
785 print(
"and no default in [general] section.")
789 if "ALCARECOTkAlCosmics" in self.
_datasets[name][
"collection"]:
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.")
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.")
803 self.
_datasets[name][
"primaryWidth"] = -1.0
804 if config[
"config"].has_option(section,
"primaryWidth"):
806 = config[
"config"].getfloat(section,
"primaryWidth")
808 self.
_datasets[name][
"numberOfEvents"] = -1
809 if config[
"config"].has_option(section,
"numberOfEvents"):
811 = config[
"config"].getint(section,
"numberOfEvents")
815 self.
_datasets[name][
"json"] = config[
"config"].get(section,
"json")
816 except ConfigParser.NoOptionError:
818 self.
_datasets[name][
"json"] = config[
"general"][
"json"]
822 = all_configs[
"main"][
"general"][
"json"]
824 print(
"No json given in either [general] or", end=
' ')
825 print(
"["+section+
"] sections.")
826 print(
" -> Proceeding without json-file.")
830 for var
in (
"inputFileList",
"json",
"configTemplate"):
832 = os.path.expandvars(self.
_datasets[name][var])
838 with open(self.
_datasets[name][
"inputFileList"],
"r")
as filelist:
839 for line
in filelist:
840 if "CastorPool" in line:
843 if not line.strip()==
"":
846 print(
"Inputfilelist", self.
_datasets[name][
"inputFileList"], end=
' ')
847 print(
"does not exist.")
850 print(
"Number of jobs is 0. There may be a problem with the inputfilelist:")
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")
859 print(
"'njobs' is bigger than the number of files for this", end=
' ')
861 print(
"Using default.")
863 print(
"No number of jobs specified. Using number of files in", end=
' ')
864 print(
"inputfilelist as the number of jobs.")
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
873 weight_name+config[
"config"].config_path)
879 os.environ[
"datasetdir"] = cache_datasetdir
882 print(
"No dataset section defined in '{0}'".
format(
884 print(
"At least one section '[dataset:<name>]' is required.")
891 if __name__ ==
"__main__":
894 except KeyboardInterrupt: