Skip to content

Commit ea6908c

Browse files
authored
Merge pull request #48512 from amecca/add-extractPixBary
[TkAl] Add "extract" jobs for the PixelBarycentre validation in the All-in-One framework
2 parents 5d1c399 + de63a20 commit ea6908c

File tree

8 files changed

+139
-34
lines changed

8 files changed

+139
-34
lines changed

Alignment/OfflineValidation/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ For details read [`README_JetHT.md`](https://github.com/cms-sw/cmssw/blob/master
118118
For details read [`README_MTS.md`](https://github.com/cms-sw/cmssw/blob/master/Alignment/OfflineValidation/README_MTS.md)
119119

120120
## Pixel BaryCenter
121-
For details read [`README_MTS.md`](https://github.com/cms-sw/cmssw/blob/master/Alignment/OfflineValidation/README_PixBary.md)
121+
For details read [`README_PixBary.md`](https://github.com/cms-sw/cmssw/blob/master/Alignment/OfflineValidation/README_PixBary.md)
122122

123123
## Generic validation (dataset validation)
124124
For details read [`README_Generic.md`](https://github.com/cms-sw/cmssw/blob/master/Alignment/OfflineValidation/README_Generic.md)

Alignment/OfflineValidation/README_PixBary.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ The following parameters are expected/accepted in the json/yaml configuration fi
77
```
88
validations:
99
PixBary:
10-
single:
10+
<step_type>:
1111
<job_name>:
1212
<options>
1313
```
1414

15+
The following steps are supported:
16+
- single (run the analyzer to compute the barycentre)
17+
- extract (retrieve the barycentre from the `single` output)
18+
19+
### Single PixBary jobs
20+
Runs PixelBaryCentreAnalyzer_cfg.py.
1521
The following options are understood:
1622

1723
Variable | Default value | Explanation/Options
@@ -20,3 +26,11 @@ firstRun | 290550 | The first run to process (inclusive)
2026
lastRun | 325175 | The last run to process (inclusive)
2127
lumisPerRun | 1 | The number of LumiSections tested for a change in the TrackerAlignmentRcd in each run
2228
alignments | None | List of alignments for which this validation is run
29+
30+
### Extract PixBary jobs
31+
Runs extractBarycentre.py.
32+
The following options are understood:
33+
34+
Variable | Default value | Explanation/Options
35+
-------- | ------------- | --------------------
36+
styles | [csv, twiki] | List of styles to be used; see `extractBarycentre.py -h`

Alignment/OfflineValidation/python/TkAlAllInOneTool/PixBary.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ def PixBary(config, validationDir, verbose=False):
1313
##Start with single jobs
1414
jobType = "single"
1515

16+
##Common paths
17+
dir_base = os.path.join(validationDir, _validationName) # Base path for work directories (one for for each job, alignment and IOV)
18+
out_base = os.path.join(config["LFS"], config["name"], _validationName)
19+
1620
##Check that a job is defined
1721
if not jobType in config["validations"][_validationName]:
1822
raise LookupError("No '%s' key word in config for %s" %(jobType, _validationName))
@@ -28,13 +32,12 @@ def PixBary(config, validationDir, verbose=False):
2832
for runRange in IOV_list:
2933
IOV = '-'.join(str(i) for i in runRange)
3034

31-
for alignment, alignmentConfig in config["alignments"].items():
32-
##Work directory for each IOV
33-
workDir = os.path.join(validationDir, _validationName, jobType, jobName, alignment, IOV)
35+
for alignment in jobConfig["alignments"]:
36+
alignmentConfig = config["alignments"][alignment]
3437

3538
##Write local config
3639
local = {}
37-
local["output"] = os.path.join(config["LFS"], config["name"], _validationName, jobType, alignment, jobName, IOV)
40+
local["output"] = os.path.join(out_base, jobType, alignment, jobName, IOV)
3841
local["alignment"] = copy.deepcopy(alignmentConfig)
3942
local["alignment"]["label"] = alignment
4043
local["validation"] = copy.deepcopy(jobConfig)
@@ -47,8 +50,9 @@ def PixBary(config, validationDir, verbose=False):
4750

4851
##Write job info
4952
job = {
53+
"label": jobName, # the name used in the config, so that it can be referenced lated
5054
"name": "{}_{}_{}_{}_{}".format(_validationName, alignment, jobType, jobName, IOV),
51-
"dir": workDir,
55+
"dir": os.path.join(dir_base, jobType, jobName, alignment, IOV),
5256
"exe": "cmsRun",
5357
"cms-config": "{}/src/Alignment/OfflineValidation/python/TkAlAllInOneTool/PixelBaryCentreAnalyzer_cfg.py".format(os.environ["CMSSW_BASE"]),
5458
"run-mode": "Condor",
@@ -58,6 +62,32 @@ def PixBary(config, validationDir, verbose=False):
5862

5963
jobs.append(job)
6064

65+
# Extract text from the ROOT files
66+
jobType = "extract"
67+
68+
for jobName, jobConfig in config["validations"][_validationName][jobType].items():
69+
for singleName in jobConfig.get("singles"):
70+
# Search for the "single" job referenced by name
71+
matchingSingleConfigs = [j for j in jobs if j.get("label", "") == singleName]
72+
73+
for singleConfig in matchingSingleConfigs:
74+
IOV = singleConfig["config"]["validation"]["IOV"] # <str>
75+
alignment = singleConfig["config"]["alignment"]["label"] # <str>
76+
77+
job = {
78+
"name": "_".join([_validationName, jobType, jobName, singleName, alignment, IOV]),
79+
"dir": os.path.join(dir_base, jobType, jobName, singleName, alignment, IOV),
80+
"dependencies": [singleConfig["name"]],
81+
"exe": "extractBaryCentre.py",
82+
"config": {
83+
"input": os.path.join(singleConfig["config"]["output"], "PixelBaryCentre.root"),
84+
"output": os.path.join(out_base, jobType, jobName),
85+
"styles": jobConfig.get("styles", ["csv", "twiki"])
86+
},
87+
"flavour": "espresso" # So fast that anything else would not make sense
88+
}
89+
jobs.append(job)
90+
6191
return jobs
6292

6393
def get_IOVs(jobConfig):

Alignment/OfflineValidation/python/TkAlAllInOneTool/PixelBaryCentreAnalyzer_cfg.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
alignments = configuration.get('alignments', None) # NOTE: aligments is plural
127127
if alignments is None:
128128
align = configuration['alignment'] # NOTE: alignment is singular
129-
label = configuration['alignment'].get('label', align['title'].split()[0])
129+
label = configuration['alignment'].get('label', 'alignment')
130130
alignments = {label: align}
131131

132132
for label, align in alignments.items():

Alignment/OfflineValidation/scripts/extractBaryCentre.py

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
from argparse import ArgumentParser
44
import logging
5+
import json
6+
import sys
7+
import os
58
import ROOT
69

710
class TFileContext(object):
@@ -12,7 +15,7 @@ def __enter__(self):
1215
def __exit__(self, type, value, traceback):
1316
self.tfile.Close()
1417

15-
def display_results(t_data, style='twiki'):
18+
def display_results(t_data, style='twiki', out=sys.stdout):
1619
before = after = None
1720
if (style == 'twiki'):
1821
header_fmt = ' | '.join( ['{:^6s}'] + ['{:^9s}' ] * (len(t_data.keys())-1) )
@@ -30,31 +33,18 @@ def display_results(t_data, style='twiki'):
3033
else:
3134
raise RuntimeError('Unknown style "%s" for table'%(style))
3235

33-
if(before is not None): print(before)
34-
print( header_fmt.format(*t_data.keys()) )
36+
if(before is not None): out.write(before+'\n')
37+
out.write( header_fmt.format(*t_data.keys())+'\n' )
3538
for i, run in enumerate(t_data['run']):
36-
print(row_fmt.format(run, *[t_data[k][i] for k in t_data.keys() if not k == 'run']))
37-
if(after is not None): print(after)
39+
out.write(row_fmt.format(run, *[t_data[k][i] for k in t_data.keys() if not k == 'run'])+'\n')
40+
if(after is not None): out.write(after+'\n')
3841

42+
return out
3943

40-
def main():
41-
parser = ArgumentParser()
42-
parser.add_argument('fname' , metavar='FILE')
43-
parser.add_argument('-p', '--partition', default='BPIX', help='Tracker partition (e.g. BPIX, FPIX, BPIXLYR1, etc.). Default: %(default)s')
44-
parser.add_argument('-l', '--list-content' , action='store_true', dest='list_content', help='List the contents of file and exit')
45-
parser.add_argument( '--list-branches', action='store_true', help='List the branches of the tree and exit')
46-
parser.add_argument('-t', '--type' , default='barycentre', choices=('barycentre', 'beamspot'), type=str.lower, help='Default: %(default)s')
47-
parser.add_argument( '--label' , default=None, help='Additional label that is appended to the folder name (i.e. PixelBaryCentreAnalyzer by default)')
48-
parser.add_argument( '--quality' , action='store_true', help='Read results with the WithPixelQuality flag (default: %(default)s)')
49-
parser.add_argument('-s', '--style' , default='twiki', choices=('twiki', 'latex', 'csv'), type=str.lower, help='Table style for the output (default: %(default)s)')
50-
parser.add_argument( '--loglevel' , metavar='LEVEL', default='WARNING', help='Level for the python logging module. Can be either a mnemonic string like DEBUG, INFO or WARNING or an integer (lower means more verbose).')
5144

52-
args = parser.parse_args()
53-
loglevel = args.loglevel.upper() if not args.loglevel.isdigit() else int(args.loglevel)
54-
logging.basicConfig(format='%(levelname)s:%(module)s:%(funcName)s: %(message)s', level=loglevel)
45+
def main(args):
5546
logging.debug('args: %s', args)
5647

57-
5848
folder = 'PixelBaryCentreAnalyzer' if not args.quality else 'PixelBaryCentreAnalyzerWithPixelQuality'
5949
tree_name = 'PixelBarycentre' if args.type == 'barycentre' else 'BeamSpot'
6050
if(args.label is not None):
@@ -78,14 +68,29 @@ def main():
7868
raise KeyError(tree_name)
7969

8070
if(args.list_branches):
81-
list_branches(tree, folder=folder)
71+
list_branches(tree, folder_name=folder)
8272
return 0
8373

8474
rdf = ROOT.RDataFrame(tree)
8575
logging.info('Reading "%s"', tree_name)
8676
results = rdf.AsNumpy(columns)
8777

88-
display_results(results, style=args.style)
78+
if(args.config is None):
79+
display_results(results, style=args.style)
80+
else:
81+
# When this script is called from the validation framework, the output is
82+
# written to a list of files, one for each style, with appropriate extensions
83+
fname_base = '_'.join([args.type, args.partition] + (['quality'] if args.quality else []))
84+
fname_path = os.path.join(args.config['output'], fname_base) # without the ext
85+
for style in args.config["styles"]:
86+
if (style == 'twiki'): ext = 'txt'
87+
elif(style == 'latex'): ext = 'tex'
88+
else: ext = style
89+
fname = '.'.join([fname_path, ext])
90+
logging.info('output in "%s"', fname)
91+
92+
with open(fname, 'w') as fout:
93+
display_results(results, style=style, out=fout)
8994

9095
return 0
9196

@@ -103,5 +108,36 @@ def list_branches(tree, folder_name=''):
103108
print('\n'.join(branches))
104109

105110

111+
def parse_args():
112+
parser = ArgumentParser()
113+
parser.add_argument('fname' , metavar='FILE')
114+
parser.add_argument('-p', '--partition', default='BPIX', help='Tracker partition (e.g. BPIX, FPIX, BPIXLYR1, etc.). Default: %(default)s')
115+
parser.add_argument('-l', '--list-content' , action='store_true', dest='list_content', help='List the contents of file and exit')
116+
parser.add_argument( '--list-branches', action='store_true', help='List the branches of the tree and exit')
117+
parser.add_argument('-t', '--type' , default='barycentre', choices=('barycentre', 'beamspot'), type=str.lower, help='Default: %(default)s')
118+
parser.add_argument( '--label' , default=None, help='Additional label that is appended to the folder name (i.e. PixelBaryCentreAnalyzer by default)')
119+
parser.add_argument( '--quality' , action='store_true', help='Read results with the WithPixelQuality flag (default: %(default)s)')
120+
parser.add_argument('-s', '--style' , default='twiki', choices=('twiki', 'latex', 'csv'), type=str.lower, help='Table style for the output (default: %(default)s)')
121+
parser.add_argument( '--loglevel' , metavar='LEVEL', default='WARNING', help='Level for the python logging module. Can be either a mnemonic string like DEBUG, INFO or WARNING or an integer (lower means more verbose).')
122+
123+
args = parser.parse_args()
124+
125+
# If the script is called from the validation framework, the first argument
126+
# will be a JSON file with the configuration, instead of a ROOT file
127+
try:
128+
with open(args.fname) as f:
129+
config = json.load(f)
130+
args.fname = config['input']
131+
args.config = config
132+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
133+
args.config = None
134+
135+
return args
136+
137+
106138
if __name__ == '__main__':
107-
exit(main())
139+
args = parse_args()
140+
loglevel = args.loglevel.upper() if not args.loglevel.isdigit() else int(args.loglevel)
141+
logging.basicConfig(format='%(levelname)s:%(module)s:%(funcName)s: %(message)s', level=loglevel)
142+
143+
exit(main(args))

Alignment/OfflineValidation/test/testingScripts/test_unitPixBary.sh

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,26 @@ function die { echo $1: status $2 ; exit $2; }
44

55
runrange=376370-379254
66

7-
thistest="Alignment/PixelBarycentre single yaml configuration unitTest (test 1/2)"
7+
thistest="Alignment/PixelBarycentre single yaml configuration unitTest (test 1/4)"
88
echo "TESTING $thistest..."
99
pushd test_yaml/PixBary/single/testSinglePixBary/unitTest/$runrange
1010
./cmsRun validation_cfg.py config=validation.json || die "Failure running $thistest" $?
1111
popd
1212

13-
thistest="Alignment/PixelBarycentre single yaml configuration mp3619 (test 2/2)"
13+
thistest="Alignment/PixelBarycentre single yaml configuration mp3619 (test 2/4)"
1414
echo "TESTING $thistest..."
1515
pushd test_yaml/PixBary/single/testSinglePixBary/mp3619/$runrange
1616
./cmsRun validation_cfg.py config=validation.json || die "Failure running $thistest" $?
1717
popd
18+
19+
thistest="Alignment/PixelBarycentre extract yaml configuration unitTest (test 3/4)"
20+
echo "TESTING $thistest..."
21+
pushd test_yaml/PixBary/extract/testExtractPixBary/testSinglePixBary/unitTest/$runrange
22+
./run.sh || die "Failure running $thistest" $?
23+
popd
24+
25+
thistest="Alignment/PixelBarycentre extract yaml configuration mp3619 (test 4/4)"
26+
echo "TESTING $thistest..."
27+
pushd test_yaml/PixBary/extract/testExtractPixBary/testSinglePixBary/mp3619/$runrange
28+
./run.sh || die "Failure running $thistest" $?
29+
popd

Alignment/OfflineValidation/test/unit_test.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,13 @@
331331
"mp3619"
332332
]
333333
}
334-
}
334+
},
335+
"extract": {
336+
"testExtractPixBary": {
337+
"singles": ["testSinglePixBary"],
338+
"styles": ["twiki", "csv"]
339+
}
340+
}
335341
}
336342
},
337343
"style":{

Alignment/OfflineValidation/test/unit_test.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,13 @@ validations:
330330
alignments:
331331
- unitTest
332332
- mp3619
333+
extract:
334+
testExtractPixBary:
335+
singles:
336+
- testSinglePixBary
337+
styles:
338+
- twiki
339+
- csv
333340
style:
334341
DMR:
335342
averaged:

0 commit comments

Comments
 (0)