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
27 """Main routine. Not called, if this module is loaded via `import`.
30 - `argv`: Command line arguments passed to the script.
37 setup_alignment.setup()
42 """Class encapsulating the alignment campaign setup procedure."""
48 - `argv`: command line arguments
79 """Setup the alignment campaign."""
88 msg = (
"Overriding global tag with single-IOV tags extracted from "
98 """Create ConfigParser object from command line arguments."""
100 helpEpilog =
"""Builds the config-templates from a universal
101 config-template for each dataset specified in .ini-file that is passed
102 to this script. Then calls mps_setup.py for all datasets."""
103 parser = argparse.ArgumentParser(
104 description = (
"Setup the alignment as configured in the "
105 "alignment_config file."),
107 parser.add_argument(
"-v",
"--verbose", action=
"store_true",
108 help=
"display detailed output of mps_setup")
109 parser.add_argument(
"-w",
"--weight", action=
"store_true",
110 help=(
"creates additional merge job(s) with "
111 "(possibly new) weights from .ini-config"))
112 parser.add_argument(
"alignmentConfig",
113 help=(
"name of the .ini config file that specifies "
114 "the datasets to be used"))
117 self.
_config = ConfigParser.ConfigParser()
125 """Determine directory paths and create the ones that are needed."""
127 mpsTemplates = os.path.join(
"src",
"Alignment",
128 "MillePedeAlignmentAlgorithm",
"templates")
130 mpsTemplates = os.path.join(os.environ[
"CMSSW_BASE"], mpsTemplates)
132 mpsTemplates = os.path.join(os.environ[
"CMSSW_RELEASE_BASE"], mpsTemplates)
133 self.
_mille_script = os.path.join(mpsTemplates,
"mps_runMille_template.sh")
134 self.
_pede_script = os.path.join(mpsTemplates,
"mps_runPede_rfcp_template.sh")
137 currentDir = os.getcwd()
138 match = re.search(re.compile(
'mpproduction\/mp(.+?)$', re.M|re.I),currentDir)
142 print(
"Current location does not seem to be a MillePede campaign directory:", end=
' ')
148 """Create and fill `general_options` dictionary."""
158 """Fetch information about external datasets."""
160 if self.
_config.has_option(
"general",
"externalDatasets"):
161 datasets =
map(
lambda x: x.strip(),
163 "externalDatasets").
split(
","))
164 datasets = [x
for x
in datasets
if len(x.strip()) > 0]
165 for item
in datasets:
166 splitted = item.split(
"|")
167 dataset = splitted[0].
strip()
168 dataset = os.path.expandvars(dataset)
170 weight = splitted[1]
if len(splitted) > 1
else None
171 config = ConfigParser.ConfigParser()
172 config.optionxform = str
174 config.config_path = dataset
182 Create MPS mass storage directory where, e.g., mille binaries are
188 "/eos/cms/store/group/alca_millepede/")
192 cmd = [
"mkdir",
"-p", self.
_mss_dir]
197 with open(os.devnull,
"w")
as dump:
198 subprocess.check_call(cmd, stdout = dump, stderr = dump)
199 except subprocess.CalledProcessError:
200 print(
"Failed to create mass storage directory:", self.
_mss_dir)
205 """Extract different weight configurations from `self._config`."""
207 weights_list = [[(name, weight)
for weight
in self.
_weight_dict[name]]
210 common_weights_list = [[(name, weight)
214 common_weights_dicts = []
215 for item
in itertools.product(*common_weights_list):
217 for name,weight
in item:
219 common_weights_dicts.append(d)
222 for weight_conf
in itertools.product(*weights_list):
223 number_of_configs = len(weight_configs)
224 for common_weight
in common_weights_dicts:
226 = tuple([(dataset[0],
227 reduce(
lambda x,y: mps_tools.replace_factors(x, y, common_weight[y]),
228 common_weight, dataset[1]))
229 for dataset
in weight_conf])
230 if replaced_config
not in weight_configs:
231 weight_configs.append(replaced_config)
234 if len(weight_configs) == number_of_configs:
235 weight_configs.append(weight_conf)
237 for weight_config
in weight_configs:
238 resolved_weight_config \
239 = [(dataset[0], mps_tools.compute_product_string(dataset[1]))
240 for dataset
in weight_config]
245 """Fetch 'pedesettings' from general section in `self._config`."""
247 self._pede_settings \
249 for x
in self.
_config.get(
"general",
"pedesettings").
split(
",")]
250 if self.
_config.has_option(
"general",
"pedesettings")
else [
None])
254 """Create the mille jobs based on the [dataset:<name>] sections."""
256 gt_regex = re.compile(
'setupGlobaltag\s*\=\s*[\"\'](.*?)[\"\']')
257 sg_regex = re.compile(
"setupRunStartGeometry\s*\=\s*.*$", re.M)
258 collection_regex = re.compile(
'setupCollection\s*\=\s*[\"\'](.*?)[\"\']')
259 czt_regex = re.compile(
'setupCosmicsZeroTesla\s*\=\s*.*$', re.M)
260 cdm_regex = re.compile(
'setupCosmicsDecoMode\s*\=\s*.*$', re.M)
261 pw_regex = re.compile(
'setupPrimaryWidth\s*\=\s*.*$', re.M)
262 json_regex = re.compile(
'setupJson\s*\=\s*.*$', re.M)
265 for name, dataset
in six.iteritems(self.
_datasets):
269 with open(dataset[
"configTemplate"],
"r")
as f:
272 print(
"The config-template called", end=
' ')
273 print(dataset[
"configTemplate"],
"cannot be found.")
276 tmpFile = re.sub(gt_regex,
277 'setupGlobaltag = \"'+dataset[
"globaltag"]+
'\"',
279 tmpFile = re.sub(sg_regex,
280 "setupRunStartGeometry = "+
282 tmpFile = re.sub(collection_regex,
283 'setupCollection = \"'+dataset[
"collection"]+
'\"',
285 if "ALCARECOTkAlCosmics" in dataset[
"collection"]:
286 if dataset[
'cosmicsZeroTesla']:
287 tmpFile = re.sub(czt_regex,
288 'setupCosmicsZeroTesla = True',
291 tmpFile = re.sub(czt_regex,
292 'setupCosmicsZeroTesla = False',
295 if dataset[
'cosmicsDecoMode']:
296 tmpFile = re.sub(cdm_regex,
297 'setupCosmicsDecoMode = True',
300 tmpFile = re.sub(cdm_regex,
301 'setupCosmicsDecoMode = False',
304 if dataset[
'primaryWidth'] > 0.0:
305 tmpFile = re.sub(pw_regex,
306 'setupPrimaryWidth = '+
str(dataset[
"primaryWidth"]),
308 if dataset[
'json'] !=
'':
309 tmpFile = re.sub(json_regex,
310 'setupJson = \"'+dataset[
"json"]+
'\"',
313 thisCfgTemplate =
"tmp.py"
314 with open(thisCfgTemplate,
"w")
as f:
322 first_dataset =
False
324 self.
_cms_process = mps_tools.get_process_object(thisCfgTemplate)
327 with open(thisCfgTemplate,
"a")
as f: f.write(self.
_override_gt)
331 command = [
"mps_setup.py",
338 dataset[
"inputFileList"],
339 str(dataset[
"njobs"]),
344 if dataset[
"numberOfEvents"] > 0:
345 command.extend([
"--max-events",
str(dataset[
"numberOfEvents"])])
346 command = [x
for x
in command
if len(x.strip()) > 0]
349 print(
"Creating jobs for dataset:", name)
351 print(
"Baseconfig: ", dataset[
"configTemplate"])
352 print(
"Collection: ", dataset[
"collection"])
353 if "ALCARECOTkAlCosmics" in dataset[
"collection"]:
354 print(
"cosmicsDecoMode: ", dataset[
"cosmicsDecoMode"])
355 print(
"cosmicsZeroTesla: ", dataset[
"cosmicsZeroTesla"])
356 print(
"Globaltag: ", dataset[
"globaltag"])
357 print(
"Number of jobs: ", dataset[
"njobs"])
358 print(
"Inputfilelist: ", dataset[
"inputFileList"])
359 if dataset[
"json"] !=
"":
360 print(
"Jsonfile: ", dataset[
"json"])
361 if self.
_args.verbose:
362 print(
"Pass to mps_setup: ",
" ".
join(command))
372 """Create pede jobs from the given input."""
374 for setting
in self._pede_settings:
379 "s" if len(self._pede_settings)*len(self.
_weight_configs) > 1
else ""))
382 print(
"Creating pede jobs using settings from '{0}'.".
format(setting))
387 thisCfgTemplate =
"tmp.py"
390 self.
_cms_process = mps_tools.get_process_object(thisCfgTemplate)
392 with open(thisCfgTemplate,
"a")
as f: f.write(self.
_override_gt)
394 for name,weight
in weight_conf:
402 lib = mpslib.jobdatabase()
406 jobm_path = os.path.join(
"jobData", lib.JOBDIR[-1])
409 command = [
"rm",
"-f", os.path.join(jobm_path,
"alignment_merge.py")]
415 "-w", thisCfgTemplate,
416 os.path.join(jobm_path,
"alignment_merge.py"),
420 if setting
is not None: command.extend([
"-a", setting])
427 os.path.abspath(os.path.join(jobm_path,
428 ".TrackerTree.root")))
432 with open(os.path.join(jobm_path,
".weights.pkl"),
"wb")
as f:
433 cPickle.dump(weight_conf, f, 2)
442 Create pede jobs in addition to already existing ones. Return GT
447 if not os.path.isdir(
"jobData"):
448 print(
"No jobData-folder found.", end=
' ')
449 print(
"Properly set up the alignment before using the -w option.")
451 if not os.path.exists(
"mps.db"):
452 print(
"No mps.db found.", end=
' ')
453 print(
"Properly set up the alignment before using the -w option.")
457 config_template = firstDataset[
"configTemplate"]
458 collection = firstDataset[
"collection"]
461 with open(config_template,
"r")
as f:
464 print(
"The config-template '"+config_template+
"' cannot be found.")
467 tmpFile = re.sub(
'setupGlobaltag\s*\=\s*[\"\'](.*?)[\"\']',
470 tmpFile = re.sub(
'setupCollection\s*\=\s*[\"\'](.*?)[\"\']',
471 'setupCollection = \"'+collection+
'\"',
473 tmpFile = re.sub(re.compile(
"setupRunStartGeometry\s*\=\s*.*$", re.M),
485 Wrapper around subprocess calls which treats output depending on verbosity
489 - `command`: list of command items
490 - `verbose`: flag to turn on verbosity
493 call_method = subprocess.check_call
if verbose
else subprocess.check_output
495 call_method(command, stderr=subprocess.STDOUT)
496 except subprocess.CalledProcessError
as e:
497 print(
"" if verbose
else e.output)
498 print(
"Failed to execute command:",
" ".
join(command))
504 Create sqlite file with single-IOV tags and use it to override the
505 GT. If the GT is already customized by the user, the customization has
506 higher priority. Creates a snippet to be appended to the configuration
511 if not run_number > 0:
512 print(
"'FirstRunForStartGeometry' must be positive, but is", run_number)
515 input_db_name = os.path.abspath(
"alignment_input.db")
517 run_number, input_db_name)
520 for record,tag
in six.iteritems(tags):
524 "Alignment.MillePedeAlignmentAlgorithm.alignmentsetup."
525 "SetCondition as tagwriter\n")
526 self.
_override_gt += (
"\ntagwriter.setCondition(process,\n"
527 " connect = \""+tag[
"connect"]+
"\",\n"
528 " record = \""+record+
"\",\n"
529 " tag = \""+tag[
"tag"]+
"\")\n")
534 Check consistency of input alignment payloads and IOV definition.
535 Returns a dictionary with the information needed to override possibly
536 problematic input taken from the global tag.
539 print(
"Checking consistency of IOV definition...")
540 iovs = mps_tools.make_unique_runranges(self.
_cms_process.AlignmentProducer)
543 "TrackerAlignmentRcd":
None,
544 "TrackerSurfaceDeformationRcd":
None,
545 "TrackerAlignmentErrorExtendedRcd":
None,
548 for condition
in self.
_cms_process.GlobalTag.toGet.value():
549 if condition.record.value()
in inputs:
550 inputs[condition.record.value()] = {
551 "tag": condition.tag.value(),
553 if not condition.hasParameter(
"connect")
554 else condition.connect.value())
557 inputs_from_gt = [record
for record
in inputs
if inputs[record]
is None]
559 mps_tools.get_tags(self.
_cms_process.GlobalTag.globaltag.value(),
563 if iovs[0] == 1
and len(iovs) == 1:
564 print(
"Single IOV output detected in configuration and", end=
' ')
565 print(
"'FirstRunForStartGeometry' is not 1.")
566 print(
"Creating single IOV output from input conditions in run", end=
' ')
568 for inp
in inputs: inputs[inp][
"problematic"] =
True
570 print(
"Value of 'FirstRunForStartGeometry' has to match first", end=
' ')
571 print(
"defined output IOV:", end=
' ')
575 for inp
in six.itervalues(inputs):
576 inp[
"iovs"] = mps_tools.get_iovs(inp[
"connect"], inp[
"tag"])
579 problematic_gt_inputs = {}
580 input_indices = {key: len(value[
"iovs"]) -1
581 for key,value
in six.iteritems(inputs)}
582 for iov
in reversed(iovs):
584 if inputs[inp].pop(
"problematic",
False):
585 problematic_gt_inputs[inp] = inputs[inp]
586 if inp
in problematic_gt_inputs:
continue
587 if input_indices[inp] < 0:
588 print(
"First output IOV boundary at run", iov, end=
' ')
589 print(
"is before the first input IOV boundary at", end=
' ')
590 print(inputs[inp][
"iovs"][0],
"for '"+inp+
"'.")
591 print(
"Please check your run range selection.")
593 input_iov = inputs[inp][
"iovs"][input_indices[inp]]
595 if inp
in inputs_from_gt:
596 problematic_gt_inputs[inp] = inputs[inp]
597 print(
"Found problematic input taken from global tag.")
598 print(
"Input IOV boundary at run",input_iov, end=
' ')
599 print(
"for '"+inp+
"' is within output IOV starting", end=
' ')
601 print(
"Deriving an alignment with coarse IOV", end=
' ')
602 print(
"granularity starting from finer granularity", end=
' ')
603 print(
"leads to wrong results.")
604 print(
"A single IOV input using the IOV of", end=
' ')
606 print(
"is automatically created and used.")
608 print(
"Found input IOV boundary at run",input_iov, end=
' ')
609 print(
"for '"+inp+
"' which is within output IOV starting", end=
' ')
611 print(
"Deriving an alignment with coarse IOV granularity", end=
' ')
612 print(
"starting from finer granularity leads to wrong", end=
' ')
614 print(
"Please check your run range selection.")
616 elif iov == input_iov:
617 input_indices[inp] -= 1
620 input_indices = {key: len(value[
"iovs"]) -1
621 for key,value
in six.iteritems(inputs)
622 if (key !=
"TrackerAlignmentRcd")
623 and (inp
not in problematic_gt_inputs)}
624 for iov
in reversed(inputs[
"TrackerAlignmentRcd"][
"iovs"]):
625 for inp
in input_indices:
626 input_iov = inputs[inp][
"iovs"][input_indices[inp]]
628 print(
"Found input IOV boundary at run",input_iov, end=
' ')
629 print(
"for '"+inp+
"' which is within 'TrackerAlignmentRcd'", end=
' ')
630 print(
"IOV starting with run",
str(iov)+
".")
631 print(
"Deriving an alignment with inconsistent IOV boundaries", end=
' ')
632 print(
"leads to wrong results.")
633 print(
"Please check your input IOVs.")
635 elif iov == input_iov:
636 input_indices[inp] -= 1
638 print(
" -> IOV consistency check successful.")
641 return problematic_gt_inputs
645 """Method to create hidden 'TrackerTree.root'."""
648 print(
"Trying to create the tracker tree before setting the global", end=
' ')
649 print(
"tag or the run to determine the geometry IOV.")
652 config = mpsv_iniparser.ConfigData()
653 config.jobDataPath =
"."
660 """Fetch general options from config file."""
662 for var
in (
"classInf",
"pedeMem",
"jobname",
"FirstRunForStartGeometry"):
665 except ConfigParser.NoOptionError:
666 print(
"No", var,
"found in [general] section.", end=
' ')
667 print(
"Please check ini-file.")
673 """Fetch default general options from config file."""
675 for var
in (
"globaltag",
"configTemplate",
"json",
"massStorageDir",
679 except ConfigParser.NoOptionError:
680 if var ==
"testMode":
continue
681 print(
"No '" + var +
"' given in [general] section.")
684 dataset[
"general"] = {}
685 for var
in (
"globaltag",
"configTemplate",
"json"):
687 dataset[
"general"][var] = dataset[
"config"].get(
"general", var)
688 except (ConfigParser.NoSectionError,ConfigParser.NoOptionError):
694 Fetch 'datasetDir' variable from general section and add it to the
695 'os.environ' dictionary.
698 if self.
_config.has_option(
"general",
"datasetdir"):
699 dataset_directory = self.
_config.get(
"general",
"datasetdir")
701 os.environ[
"datasetdir"] = dataset_directory
704 print(
"No datasetdir given in [general] section.", end=
' ')
705 print(
"Be sure to give a full path in inputFileList.")
710 """Fetch internal and external dataset configurations."""
712 all_configs = collections.OrderedDict()
713 all_configs[
"main"] = {
"config": self.
_config,
718 for config
in six.itervalues(all_configs):
719 global_weight =
"1" if config[
"weight"]
is None else config[
"weight"]
726 global_weight = (global_weight,)
729 for section
in config[
"config"].sections():
730 cache_datasetdir = os.environ[
"datasetdir"]
731 if "general" in section:
732 if config[
"config"].has_option(
"general",
"datasetdir"):
733 os.environ[
"datasetdir"] = config[
"config"].get(
"general",
"datasetdir")
734 elif section ==
"weights":
735 for option
in config[
"config"].
options(section):
736 common_weights[option] \
737 = [x.strip()
for x
in
738 config[
"config"].get(section, option).
split(
",")]
739 elif section.startswith(
"dataset:"):
744 print(
"WARNING: Duplicate definition of dataset '{}'".
format(name))
745 print(
" -> Using defintion in '{}':\n".
format(config[
"config"].config_path))
747 for k,v
in config[
"config"].
items(section):
748 print(
" ", k,
"=", v)
753 if config[
"config"].has_option(section,
"weight"):
755 = [x.strip()
for x
in
756 config[
"config"].get(section,
"weight").
split(
",")]
761 for global_w
in global_weight]
765 for var
in (
"inputFileList",
"collection"):
767 self.
_datasets[name][var] = config[
"config"].get(section, var)
768 except ConfigParser.NoOptionError:
769 print(
"No", var,
"found in", section+
". Please check ini-file.")
774 for var
in (
"configTemplate",
"globaltag"):
776 self.
_datasets[name][var] = config[
"config"].get(section, var)
777 except (ConfigParser.NoSectionError,ConfigParser.NoOptionError):
779 self.
_datasets[name][var] = config[
"general"][var]
783 = all_configs[
"main"][
"general"][var]
785 print(
"No",var,
"found in ["+section+
"]", end=
' ')
786 print(
"and no default in [general] section.")
790 if "ALCARECOTkAlCosmics" in self.
_datasets[name][
"collection"]:
792 self.
_datasets[name][
"cosmicsZeroTesla"] \
793 = config[
"config"].getboolean(section,
"cosmicsZeroTesla")
794 except ConfigParser.NoOptionError:
795 print(
"No option cosmicsZeroTesla found in", section,
"even though it is required for dataset type", self.
_datasets[name][
"collection"],
". Please check ini-file.")
798 self.
_datasets[name][
"cosmicsDecoMode"] \
799 = config[
"config"].getboolean(section,
"cosmicsDecoMode")
800 except ConfigParser.NoOptionError:
801 print(
"No option cosmicsDecoMode found in", section,
"even though it is required for dataset type", self.
_datasets[name][
"collection"],
".Please check ini-file.")
804 self.
_datasets[name][
"primaryWidth"] = -1.0
805 if config[
"config"].has_option(section,
"primaryWidth"):
807 = config[
"config"].getfloat(section,
"primaryWidth")
809 self.
_datasets[name][
"numberOfEvents"] = -1
810 if config[
"config"].has_option(section,
"numberOfEvents"):
812 = config[
"config"].getint(section,
"numberOfEvents")
816 self.
_datasets[name][
"json"] = config[
"config"].get(section,
"json")
817 except ConfigParser.NoOptionError:
819 self.
_datasets[name][
"json"] = config[
"general"][
"json"]
823 = all_configs[
"main"][
"general"][
"json"]
825 print(
"No json given in either [general] or", end=
' ')
826 print(
"["+section+
"] sections.")
827 print(
" -> Proceeding without json-file.")
831 for var
in (
"inputFileList",
"json",
"configTemplate"):
833 = os.path.expandvars(self.
_datasets[name][var])
839 with open(self.
_datasets[name][
"inputFileList"],
"r")
as filelist:
840 for line
in filelist:
841 if "CastorPool" in line:
844 if not line.strip()==
"":
847 print(
"Inputfilelist", self.
_datasets[name][
"inputFileList"], end=
' ')
848 print(
"does not exist.")
851 print(
"Number of jobs is 0. There may be a problem with the inputfilelist:")
856 if config[
"config"].has_option(section,
"njobs"):
857 if config[
"config"].getint(section,
"njobs") <= self.
_datasets[name][
"njobs"]:
858 self.
_datasets[name][
"njobs"] = config[
"config"].getint(section,
"njobs")
860 print(
"'njobs' is bigger than the number of files for this", end=
' ')
862 print(
"Using default.")
864 print(
"No number of jobs specified. Using number of files in", end=
' ')
865 print(
"inputfilelist as the number of jobs.")
868 for weight_name, weight_values
in six.iteritems(common_weights):
869 for key, weight
in six.iteritems(weight_dict):
870 if any([weight_name
in w
for w
in weight]):
871 self.
_common_weights[weight_name+config[
"config"].config_path] = weight_values
874 weight_name+config[
"config"].config_path)
880 os.environ[
"datasetdir"] = cache_datasetdir
883 print(
"No dataset section defined in '{0}'".
format(
885 print(
"At least one section '[dataset:<name>]' is required.")
892 if __name__ ==
"__main__":
895 except KeyboardInterrupt: