Skip to content

Commit fd1e443

Browse files
committed
[TkAl][PixBary] Add "extract" job (->text files) from the output of a "single"
It runs extractBarycentre.py: * input: ROOT files created by a "single" job * output: text files that can be used to update the TWiki (csv, markdown) and a TeX table * configurable styles (defaults are used for partition, quality etc.) Updates in extractBarycentre.py: * arg parsing outside main() * support for writing directly to a file (instead of stdout) [SQUASHME] iterate over all the single jobs
1 parent 358310c commit fd1e443

File tree

2 files changed

+91
-27
lines changed

2 files changed

+91
-27
lines changed

Alignment/OfflineValidation/python/TkAlAllInOneTool/PixBary.py

Lines changed: 33 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))
@@ -31,12 +35,9 @@ def PixBary(config, validationDir, verbose=False):
3135
for alignment in jobConfig["alignments"]:
3236
alignmentConfig = config["alignments"][alignment]
3337

34-
##Work directory for each IOV
35-
workDir = os.path.join(validationDir, _validationName, jobType, jobName, alignment, IOV)
36-
3738
##Write local config
3839
local = {}
39-
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)
4041
local["alignment"] = copy.deepcopy(alignmentConfig)
4142
local["alignment"]["label"] = alignment
4243
local["validation"] = copy.deepcopy(jobConfig)
@@ -49,8 +50,9 @@ def PixBary(config, validationDir, verbose=False):
4950

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

6163
jobs.append(job)
6264

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+
6391
return jobs
6492

6593
def get_IOVs(jobConfig):

Alignment/OfflineValidation/scripts/extractBaryCentre.py

Lines changed: 58 additions & 22 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):
@@ -85,7 +75,22 @@ def main():
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))

0 commit comments

Comments
 (0)