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 recogeom_regex = re.compile(
'setupRecoGeometry\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)
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(recogeom_regex,
280 'setupRecoGeometry = \"'+dataset[
"recogeometry"]+
'\"',
282 tmpFile = re.sub(sg_regex,
283 "setupRunStartGeometry = "+
285 tmpFile = re.sub(collection_regex,
286 'setupCollection = \"'+dataset[
"collection"]+
'\"',
288 if "ALCARECOTkAlCosmics" in dataset[
"collection"]:
289 if dataset[
'cosmicsZeroTesla']:
290 tmpFile = re.sub(czt_regex,
291 'setupCosmicsZeroTesla = True',
294 tmpFile = re.sub(czt_regex,
295 'setupCosmicsZeroTesla = False',
298 if dataset[
'cosmicsDecoMode']:
299 tmpFile = re.sub(cdm_regex,
300 'setupCosmicsDecoMode = True',
303 tmpFile = re.sub(cdm_regex,
304 'setupCosmicsDecoMode = False',
307 if dataset[
'primaryWidth'] > 0.0:
308 tmpFile = re.sub(pw_regex,
309 'setupPrimaryWidth = '+
str(dataset[
"primaryWidth"]),
311 if dataset[
'json'] !=
'':
312 tmpFile = re.sub(json_regex,
313 'setupJson = \"'+dataset[
"json"]+
'\"',
316 thisCfgTemplate =
"tmp.py" 317 with open(thisCfgTemplate,
"w")
as f:
325 first_dataset =
False 327 self.
_cms_process = mps_tools.get_process_object(thisCfgTemplate)
330 with open(thisCfgTemplate,
"a")
as f: f.write(self.
_override_gt)
334 command = [
"mps_setup.py",
341 dataset[
"inputFileList"],
342 str(dataset[
"njobs"]),
347 if dataset[
"numberOfEvents"] > 0:
348 command.extend([
"--max-events",
str(dataset[
"numberOfEvents"])])
349 command = [x
for x
in command
if len(x.strip()) > 0]
352 print(
"Creating jobs for dataset:", name)
354 print(
"Baseconfig: ", dataset[
"configTemplate"])
355 print(
"Collection: ", dataset[
"collection"])
356 if "ALCARECOTkAlCosmics" in dataset[
"collection"]:
357 print(
"cosmicsDecoMode: ", dataset[
"cosmicsDecoMode"])
358 print(
"cosmicsZeroTesla: ", dataset[
"cosmicsZeroTesla"])
359 print(
"Globaltag: ", dataset[
"globaltag"])
360 print(
"RecoGeometry: ", dataset[
"recogeometry"])
361 print(
"Number of jobs: ", dataset[
"njobs"])
362 print(
"Inputfilelist: ", dataset[
"inputFileList"])
363 if dataset[
"json"] !=
"":
364 print(
"Jsonfile: ", dataset[
"json"])
365 if self.
_args.verbose:
366 print(
"Pass to mps_setup: ",
" ".
join(command))
376 """Create pede jobs from the given input.""" 378 for setting
in self._pede_settings:
383 "s" if len(self._pede_settings)*len(self.
_weight_configs) > 1
else ""))
386 print(
"Creating pede jobs using settings from '{0}'.".
format(setting))
391 thisCfgTemplate =
"tmp.py" 394 self.
_cms_process = mps_tools.get_process_object(thisCfgTemplate)
396 with open(thisCfgTemplate,
"a")
as f: f.write(self.
_override_gt)
398 for name,weight
in weight_conf:
406 lib = mpslib.jobdatabase()
410 jobm_path = os.path.join(
"jobData", lib.JOBDIR[-1])
413 command = [
"rm",
"-f", os.path.join(jobm_path,
"alignment_merge.py")]
419 "-w", thisCfgTemplate,
420 os.path.join(jobm_path,
"alignment_merge.py"),
424 if setting
is not None: command.extend([
"-a", setting])
431 os.path.abspath(os.path.join(jobm_path,
432 ".TrackerTree.root")))
436 with open(os.path.join(jobm_path,
".weights.pkl"),
"wb")
as f:
437 cPickle.dump(weight_conf, f, 2)
446 Create pede jobs in addition to already existing ones. Return GT 451 if not os.path.isdir(
"jobData"):
452 print(
"No jobData-folder found.", end=
' ')
453 print(
"Properly set up the alignment before using the -w option.")
455 if not os.path.exists(
"mps.db"):
456 print(
"No mps.db found.", end=
' ')
457 print(
"Properly set up the alignment before using the -w option.")
461 config_template = firstDataset[
"configTemplate"]
462 collection = firstDataset[
"collection"]
465 with open(config_template,
"r") as f: 468 print(
"The config-template '"+config_template+
"' cannot be found.")
471 tmpFile = re.sub(
'setupGlobaltag\s*\=\s*[\"\'](.*?)[\"\']',
474 tmpFile = re.sub(
'setupRecoGeometry\s*\=\s*[\"\'](.*?)[\"\']',
477 tmpFile = re.sub(
'setupCollection\s*\=\s*[\"\'](.*?)[\"\']',
478 'setupCollection = \"'+collection+
'\"',
480 tmpFile = re.sub(re.compile(
"setupRunStartGeometry\s*\=\s*.*$", re.M),
492 Wrapper around subprocess calls which treats output depending on verbosity 496 - `command`: list of command items 497 - `verbose`: flag to turn on verbosity 500 call_method = subprocess.check_call
if verbose
else subprocess.check_output
502 call_method(command, stderr=subprocess.STDOUT)
503 except subprocess.CalledProcessError
as e:
504 print(
"" if verbose
else e.output)
505 print(
"Failed to execute command:",
" ".
join(command))
511 Create sqlite file with single-IOV tags and use it to override the 512 GT. If the GT is already customized by the user, the customization has 513 higher priority. Creates a snippet to be appended to the configuration 518 if not run_number > 0:
519 print(
"'FirstRunForStartGeometry' must be positive, but is", run_number)
522 input_db_name = os.path.abspath(
"alignment_input.db")
524 run_number, input_db_name)
527 for record,tag
in tags.items():
531 "Alignment.MillePedeAlignmentAlgorithm.alignmentsetup." 532 "SetCondition as tagwriter\n")
533 self.
_override_gt += (
"\ntagwriter.setCondition(process,\n" 534 " connect = \""+tag[
"connect"]+
"\",\n" 535 " record = \""+record+
"\",\n" 536 " tag = \""+tag[
"tag"]+
"\")\n")
541 Check consistency of input alignment payloads and IOV definition. 542 Returns a dictionary with the information needed to override possibly 543 problematic input taken from the global tag. 546 print(
"Checking consistency of IOV definition...")
547 iovs = mps_tools.make_unique_runranges(self.
_cms_process.AlignmentProducer)
550 "TrackerAlignmentRcd":
None,
551 "TrackerSurfaceDeformationRcd":
None,
552 "TrackerAlignmentErrorExtendedRcd":
None,
555 for condition
in self.
_cms_process.GlobalTag.toGet.value():
556 if condition.record.value()
in inputs:
557 inputs[condition.record.value()] = {
558 "tag": condition.tag.value(),
560 if not condition.hasParameter(
"connect")
561 else condition.connect.value())
564 inputs_from_gt = [record
for record
in inputs
if inputs[record]
is None]
566 mps_tools.get_tags(self.
_cms_process.GlobalTag.globaltag.value(),
570 if iovs[0] == 1
and len(iovs) == 1:
571 print(
"Single IOV output detected in configuration and", end=
' ')
572 print(
"'FirstRunForStartGeometry' is not 1.")
573 print(
"Creating single IOV output from input conditions in run", end=
' ')
575 for inp
in inputs: inputs[inp][
"problematic"] =
True 577 print(
"Value of 'FirstRunForStartGeometry' has to match first", end=
' ')
578 print(
"defined output IOV:", end=
' ')
582 for inp
in inputs.values():
583 inp[
"iovs"] = mps_tools.get_iovs(inp[
"connect"], inp[
"tag"])
586 problematic_gt_inputs = {}
587 input_indices = {key: len(value[
"iovs"]) -1
588 for key,value
in inputs.items()}
589 for iov
in reversed(iovs):
591 if inputs[inp].pop(
"problematic",
False):
592 problematic_gt_inputs[inp] = inputs[inp]
593 if inp
in problematic_gt_inputs:
continue 594 if input_indices[inp] < 0:
595 print(
"First output IOV boundary at run", iov, end=
' ')
596 print(
"is before the first input IOV boundary at", end=
' ')
597 print(inputs[inp][
"iovs"][0],
"for '"+inp+
"'.")
598 print(
"Please check your run range selection.")
600 input_iov = inputs[inp][
"iovs"][input_indices[inp]]
602 if inp
in inputs_from_gt:
603 problematic_gt_inputs[inp] = inputs[inp]
604 print(
"Found problematic input taken from global tag.")
605 print(
"Input IOV boundary at run",input_iov, end=
' ')
606 print(
"for '"+inp+
"' is within output IOV starting", end=
' ')
608 print(
"Deriving an alignment with coarse IOV", end=
' ')
609 print(
"granularity starting from finer granularity", end=
' ')
610 print(
"leads to wrong results.")
611 print(
"A single IOV input using the IOV of", end=
' ')
613 print(
"is automatically created and used.")
615 print(
"Found input IOV boundary at run",input_iov, end=
' ')
616 print(
"for '"+inp+
"' which is within output IOV starting", end=
' ')
618 print(
"Deriving an alignment with coarse IOV granularity", end=
' ')
619 print(
"starting from finer granularity leads to wrong", end=
' ')
621 print(
"Please check your run range selection.")
623 elif iov == input_iov:
624 input_indices[inp] -= 1
627 input_indices = {key: len(value[
"iovs"]) -1
628 for key,value
in inputs.items()
629 if (key !=
"TrackerAlignmentRcd")
630 and (inp
not in problematic_gt_inputs)}
631 for iov
in reversed(inputs[
"TrackerAlignmentRcd"][
"iovs"]):
632 for inp
in input_indices:
633 input_iov = inputs[inp][
"iovs"][input_indices[inp]]
635 print(
"Found input IOV boundary at run",input_iov, end=
' ')
636 print(
"for '"+inp+
"' which is within 'TrackerAlignmentRcd'", end=
' ')
637 print(
"IOV starting with run",
str(iov)+
".")
638 print(
"Deriving an alignment with inconsistent IOV boundaries", end=
' ')
639 print(
"leads to wrong results.")
640 print(
"Please check your input IOVs.")
642 elif iov == input_iov:
643 input_indices[inp] -= 1
645 print(
" -> IOV consistency check successful.")
648 return problematic_gt_inputs
652 """Method to create hidden 'TrackerTree.root'.""" 655 print(
"Trying to create the tracker tree before setting the global", end=
' ')
656 print(
"tag or the run to determine the geometry IOV.")
659 config = mpsv_iniparser.ConfigData()
660 config.jobDataPath =
"." 668 """Fetch general options from config file.""" 670 for var
in (
"classInf",
"pedeMem",
"jobname",
"FirstRunForStartGeometry"):
673 except ConfigParser.NoOptionError:
674 print(
"No", var,
"found in [general] section.", end=
' ')
675 print(
"Please check ini-file.")
681 """Fetch default general options from config file.""" 683 for var
in (
"globaltag",
"recogeometry",
"configTemplate",
"json",
"massStorageDir",
687 except ConfigParser.NoOptionError:
688 if var ==
"testMode":
continue 689 print(
"No '" + var +
"' given in [general] section.")
692 dataset[
"general"] = {}
693 for var
in (
"globaltag",
"recogeometry",
"configTemplate",
"json"):
695 dataset[
"general"][var] = dataset[
"config"].get(
"general", var)
696 except (ConfigParser.NoSectionError,ConfigParser.NoOptionError):
702 Fetch 'datasetDir' variable from general section and add it to the 703 'os.environ' dictionary. 706 if self.
_config.has_option(
"general",
"datasetdir"):
707 dataset_directory = self.
_config.get(
"general",
"datasetdir")
709 os.environ[
"datasetdir"] = dataset_directory
712 print(
"No datasetdir given in [general] section.", end=
' ')
713 print(
"Be sure to give a full path in inputFileList.")
718 """Fetch internal and external dataset configurations.""" 720 all_configs = collections.OrderedDict()
721 all_configs[
"main"] = {
"config": self.
_config,
726 for config
in all_configs.values():
727 global_weight =
"1" if config[
"weight"]
is None else config[
"weight"]
734 global_weight = (global_weight,)
737 for section
in config[
"config"].sections():
738 cache_datasetdir = os.environ[
"datasetdir"]
739 if "general" in section:
740 if config[
"config"].has_option(
"general",
"datasetdir"):
741 os.environ[
"datasetdir"] = config[
"config"].get(
"general",
"datasetdir")
742 elif section ==
"weights":
743 for option
in config[
"config"].
options(section):
744 common_weights[option] \
745 = [x.strip()
for x
in 746 config[
"config"].get(section, option).
split(
",")]
747 elif section.startswith(
"dataset:"):
752 print(
"WARNING: Duplicate definition of dataset '{}'".
format(name))
753 print(
" -> Using defintion in '{}':\n".
format(config[
"config"].config_path))
755 for k,v
in config[
"config"].
items(section):
756 print(
" ", k,
"=", v)
761 if config[
"config"].has_option(section,
"weight"):
763 = [x.strip()
for x
in 764 config[
"config"].get(section,
"weight").
split(
",")]
769 for global_w
in global_weight]
773 for var
in (
"inputFileList",
"collection"):
775 self.
_datasets[name][var] = config[
"config"].get(section, var)
776 except ConfigParser.NoOptionError:
777 print(
"No", var,
"found in", section+
". Please check ini-file.")
782 for var
in (
"configTemplate",
"globaltag"):
784 self.
_datasets[name][var] = config[
"config"].get(section, var)
785 except (ConfigParser.NoSectionError,ConfigParser.NoOptionError):
787 self.
_datasets[name][var] = config[
"general"][var]
791 = all_configs[
"main"][
"general"][var]
793 print(
"No",var,
"found in ["+section+
"]", end=
' ')
794 print(
"and no default in [general] section.")
799 for var
in (
"configTemplate",
"recogeometry"):
801 self.
_datasets[name][var] = config[
"config"].get(section, var)
802 except (ConfigParser.NoSectionError,ConfigParser.NoOptionError):
804 self.
_datasets[name][var] = config[
"general"][var]
808 = all_configs[
"main"][
"general"][var]
810 print(
"No",var,
"found in ["+section+
"]", end=
' ')
811 print(
"and no default in [general] section.")
815 if "ALCARECOTkAlCosmics" in self.
_datasets[name][
"collection"]:
817 self.
_datasets[name][
"cosmicsZeroTesla"] \
818 = config[
"config"].getboolean(section,
"cosmicsZeroTesla")
819 except ConfigParser.NoOptionError:
820 print(
"No option cosmicsZeroTesla found in", section,
"even though it is required for dataset type", self.
_datasets[name][
"collection"],
". Please check ini-file.")
823 self.
_datasets[name][
"cosmicsDecoMode"] \
824 = config[
"config"].getboolean(section,
"cosmicsDecoMode")
825 except ConfigParser.NoOptionError:
826 print(
"No option cosmicsDecoMode found in", section,
"even though it is required for dataset type", self.
_datasets[name][
"collection"],
".Please check ini-file.")
829 self.
_datasets[name][
"primaryWidth"] = -1.0
830 if config[
"config"].has_option(section,
"primaryWidth"):
832 = config[
"config"].getfloat(section,
"primaryWidth")
834 self.
_datasets[name][
"numberOfEvents"] = -1
835 if config[
"config"].has_option(section,
"numberOfEvents"):
837 = config[
"config"].getint(section,
"numberOfEvents")
841 self.
_datasets[name][
"json"] = config[
"config"].get(section,
"json")
842 except ConfigParser.NoOptionError:
844 self.
_datasets[name][
"json"] = config[
"general"][
"json"]
848 = all_configs[
"main"][
"general"][
"json"]
850 print(
"No json given in either [general] or", end=
' ')
851 print(
"["+section+
"] sections.")
852 print(
" -> Proceeding without json-file.")
856 for var
in (
"inputFileList",
"json",
"configTemplate"):
858 = os.path.expandvars(self.
_datasets[name][var])
864 with open(self.
_datasets[name][
"inputFileList"],
"r") as filelist: 865 for line
in filelist:
866 if "CastorPool" in line:
869 if not line.strip()==
"":
872 print(
"Inputfilelist", self.
_datasets[name][
"inputFileList"], end=
' ')
873 print(
"does not exist.")
876 print(
"Number of jobs is 0. There may be a problem with the inputfilelist:")
881 if config[
"config"].has_option(section,
"njobs"):
882 if config[
"config"].getint(section,
"njobs") <= self.
_datasets[name][
"njobs"]:
883 self.
_datasets[name][
"njobs"] = config[
"config"].getint(section,
"njobs")
885 print(
"'njobs' is bigger than the number of files for this", end=
' ')
887 print(
"Using default.")
889 print(
"No number of jobs specified. Using number of files in", end=
' ')
890 print(
"inputfilelist as the number of jobs.")
893 for weight_name, weight_values
in common_weights.items():
894 for key, weight
in weight_dict.items():
895 if any([weight_name
in w
for w
in weight]):
896 self.
_common_weights[weight_name+config[
"config"].config_path] = weight_values
899 weight_name+config[
"config"].config_path)
905 os.environ[
"datasetdir"] = cache_datasetdir
908 print(
"No dataset section defined in '{0}'".
format(
910 print(
"At least one section '[dataset:<name>]' is required.")
917 if __name__ ==
"__main__":
920 except KeyboardInterrupt:
def _check_iov_definition(self)
def _create_mass_storage_directory(self)
def _create_input_db(self)
def _fetch_essentials(self)
bool any(const std::vector< T > &v, const T &what)
def _fetch_external_datasets(self)
def _fetch_pede_settings(self)
def _fetch_dataset_directory(self)
def _create_weight_configs(self)
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
def _fetch_defaults(self)
def _handle_process_call(self, command, verbose=False)
def _create_additional_pede_jobs(self)
def split(sequence, size)
def _create_mille_jobs(self)
static std::string join(char **cmd)
def _fill_general_options(self)
def _construct_paths(self)
def _create_tracker_tree(self)
def _fetch_datasets(self)
def _create_pede_jobs(self)