diff --git a/man/man1/fccanalysis-plots.1 b/man/man1/fccanalysis-plots.1 new file mode 100644 index 0000000000..b8602d8d2d --- /dev/null +++ b/man/man1/fccanalysis-plots.1 @@ -0,0 +1,73 @@ +.\" Manpage for fccanalysis-plots +.\" Contact FCC-PED-SoftwareAndComputing-Analysis@cern.ch to correct errors or typos. +.TH FCCANALYSIS\-PLOTS 1 "28 Jun 2024" "0.9.0" "fccanalysis-plots man page" +.SH NAME +\fBfccanalysis\-plots\fR \(en generate analysis plots +.SH SYNOPSIS +.B fccanalysis plots +[\fB\-h\fR | \fB\-\-help\fR] +[\fB\-\-legend-text-size\fR \fILEGEND_TEXT_SIZE\fR] +[\fB\-\-legend-x-min\fR \fILEGEND_X_MIN\fR] +[\fB\-\-legend-x-max\fR \fILEGEND_X_MAX\fR] +[\fB\-\-legend-y-min\fR \fILEGEND_Y_MIN\fR] +[\fB\-\-legend-y-max\fR \fILEGEND_Y_MAX\fR] +.I plots-script +.SH DESCRIPTION +.B fccanalysis\-plots +is FCCAnalyses sub-command to generate plots out of histograms created in the +previous stages of the analysis\&. +.SH OPTIONS +.TP +.BR \-h ", " \-\-help +Prints short help message and exits\&. +.TP +.BR \-c ", " \-\-clean\-plots +Completely clean plots directory before compilation\&. +.TP +\fB\-\-legend\-text\-size\fR \fILEGEND_TEXT_SIZE\fR +Specify text size for the legend elements\&. The default value is: 0.035\&. +.TP +\fB\-\-legend\-x\-min\fR \fILEGEND_X_MIN\fR +Specify minimal x position of the legend\&. The default value: automatic\&. +.TP +\fB--legend-x-max\fR \fILEGEND_X_MAX\fR +Specify maximal x position of the legend\&. The default value: automatic\&. +.TP +\fB--legend-y-min\fR \fILEGEND_Y_MIN\fR +Specify minimal y position of the legend\&. The default value: automatic\&. +.TP +\fB--legend-y-max\fR \fILEGEND_Y_MAX\fR +Specify maximal y position of the legend\&. The default value: automatic\&. +.SH SEE ALSO +fccanalysis(1), fccanalysis\-run(1), fccanalysis\-final(1) +.SH BUGS +Many +.SH AUTHORS +There are many contributors to the FCCAnalyses framework, but the principal +authors are: +.in +4 +Clement Helsens +.br +Valentin Volk +.br +Gerardo Ganis +.SH FCCANALYSES +Part of the FCCAnalyses framework\&. +.SH LINKS +.PP +.UR https://hep-fcc\&.github\&.io/FCCAnalyses/ +FCCAnalyses webpage +.UE +.PP +.UR https://github\&.com/HEP\-FCC/FCCAnalyses/ +FCCAnalysises GitHub repository +.UE +.PP +.UR https://fccsw\-forum\&.web\&.cern\&.ch/ +FCCSW Forum +.UE +.SH CONTACT +.pp +.MT FCC-PED-SoftwareAndComputing-Analysis@cern.ch +FCC-PED-SoftwareAndComputing-Analysis +.ME diff --git a/man/man1/fccanalysis-run.1 b/man/man1/fccanalysis-run.1 index 4c5925ebce..4a20f611da 100644 --- a/man/man1/fccanalysis-run.1 +++ b/man/man1/fccanalysis-run.1 @@ -16,7 +16,7 @@ [\fB\-\-graph\-path\fR \fIGRAPH_PATH\fR] .I analysis-script .SH DESCRIPTION -.B fccanalysis-run +.B fccanalysis\-run will run analysis provided in the analysis file\&. The analysis itself can be divided into several stages if desired\&. For all those stages \fIfccanalysis-run\fR is used\&. diff --git a/python/do_plots.py b/python/do_plots.py index 927d2280c0..63f5275e04 100644 --- a/python/do_plots.py +++ b/python/do_plots.py @@ -188,54 +188,79 @@ def mapHistosFromHistmaker(hName, param, plotCfg): # _____________________________________________________________________________ -def runPlots(var, sel, param, hsignal, hbackgrounds, extralab, splitLeg, - plotStatUnc): +def runPlots(config: dict, + args, + var, + sel, + script_module, + hsignal, + hbackgrounds, + extralab): # Below are settings for separate signal and background legends - if splitLeg: - legsize = 0.04*(len(hsignal)) - legsize2 = 0.04*(len(hbackgrounds)) - legCoord = [0.15, 0.60 - legsize, 0.50, 0.62] + if config['split_leg']: + legsize = 0.04 * (len(hsignal)) + legsize2 = 0.04 * (len(hbackgrounds)) + leg = ROOT.TLegend(0.15, 0.60 - legsize, 0.50, 0.62) leg2 = ROOT.TLegend(0.60, 0.60 - legsize2, 0.88, 0.62) + + if config['leg_position'][0] is not None and \ + config['leg_position'][2] is not None: + leg.SetX1(config['leg_position'][0]) + leg.SetX2((config['leg_position'][0] + + config['leg_position'][2]) / 2) + leg2.SetX2((config['leg_position'][0] + + config['leg_position'][2]) / 2) + leg2.SetX2(config['leg_position'][0]) + if config['leg_position'][1] is not None: + leg.SetY1(config['leg_position'][1]) + leg2.SetY1(config['leg_position'][1]) + if config['leg_position'][3] is not None: + leg.SetY2(config['leg_position'][3]) + leg2.SetY2(config['leg_position'][3]) + leg2.SetFillColor(0) leg2.SetFillStyle(0) leg2.SetLineColor(0) leg2.SetShadowColor(10) - leg2.SetTextSize(0.035) + leg2.SetTextSize(config['legend_text_size']) leg2.SetTextFont(42) else: - legsize = 0.04*(len(hbackgrounds)+len(hsignal)) - legCoord = [0.68, 0.86-legsize, 0.96, 0.88] - try: - legCoord = param.legendCoord - except AttributeError: - LOGGER.debug('No legCoord, using default one...') - legCoord = [0.68, 0.86-legsize, 0.96, 0.88] + legsize = 0.04 * (len(hbackgrounds) + len(hsignal)) + leg = ROOT.TLegend(0.68, 0.86 - legsize, 0.96, 0.88) leg2 = None - leg = ROOT.TLegend(legCoord[0], legCoord[1], legCoord[2], legCoord[3]) + if config['leg_position'][0] is not None: + leg.SetX1(config['leg_position'][0]) + if config['leg_position'][1] is not None: + leg.SetY1(config['leg_position'][1]) + if config['leg_position'][2] is not None: + leg.SetX2(config['leg_position'][2]) + if config['leg_position'][3] is not None: + leg.SetY2(config['leg_position'][3]) + leg.SetFillColor(0) leg.SetFillStyle(0) leg.SetLineColor(0) leg.SetShadowColor(10) - leg.SetTextSize(0.035) + leg.SetTextSize(config['legend_text_size']) leg.SetTextFont(42) for b in hbackgrounds: - if splitLeg: - leg2.AddEntry(hbackgrounds[b][0], param.legend[b], "f") + if config['split_leg']: + leg2.AddEntry(hbackgrounds[b][0], script_module.legend[b], "f") else: - leg.AddEntry(hbackgrounds[b][0], param.legend[b], "f") + leg.AddEntry(hbackgrounds[b][0], script_module.legend[b], "f") for s in hsignal: - leg.AddEntry(hsignal[s][0], param.legend[s], "l") + leg.AddEntry(hsignal[s][0], script_module.legend[s], "l") yields = {} for s in hsignal: - yields[s] = [param.legend[s], + yields[s] = [script_module.legend[s], hsignal[s][0].Integral(0, -1), hsignal[s][0].GetEntries()] for b in hbackgrounds: - yields[b] = [param.legend[b], + yields[b] = [script_module.legend[b], hbackgrounds[b][0].Integral(0, -1), hbackgrounds[b][0].GetEntries()] @@ -245,82 +270,91 @@ def runPlots(var, sel, param, hsignal, hbackgrounds, extralab, splitLeg, nsig = len(hsignal) nbkg = len(hbackgrounds) - for s in hsignal: - histos.append(hsignal[s][0]) - colors.append(param.colors[s]) + for sig in hsignal: + histos.append(hsignal[sig][0]) + colors.append(script_module.colors[sig]) - for b in hbackgrounds: - histos.append(hbackgrounds[b][0]) - colors.append(param.colors[b]) + for bkg in hbackgrounds: + histos.append(hbackgrounds[bkg][0]) + colors.append(script_module.colors[bkg]) - intLumiab = param.intLumi/1e+06 + intLumiab = script_module.intLumi/1e+06 intLumi = f'L = {intLumiab:.0f} ab^{{-1}}' - if hasattr(param, "intLumiLabel"): - intLumi = getattr(param, "intLumiLabel") + if hasattr(script_module, "intLumiLabel"): + intLumi = getattr(script_module, "intLumiLabel") lt = 'FCCAnalyses: FCC-hh Simulation (Delphes)' - rt = f'#sqrt{{s}} = {param.energy:.1f} TeV, L = {intLumi}' + rt = f'#sqrt{{s}} = {script_module.energy:.1f} TeV, L = {intLumi}' - if 'ee' in param.collider: + if 'ee' in script_module.collider: lt = 'FCCAnalyses: FCC-ee Simulation (Delphes)' - rt = f'#sqrt{{s}} = {param.energy:.1f} GeV, {intLumi}' + rt = f'#sqrt{{s}} = {script_module.energy:.1f} GeV, {intLumi}' customLabel = "" try: - customLabel = param.customLabel + customLabel = script_module.customLabel except AttributeError: - LOGGER.debug('No customLable, using nothing...') + LOGGER.debug('No custom label, using nothing...') scaleSig = 1. try: - scaleSig = param.scaleSig + scaleSig = script_module.scaleSig except AttributeError: LOGGER.debug('No scale signal, using 1.') - param.scaleSig = scaleSig + script_module.scaleSig = scaleSig if 'AAAyields' in var: - drawStack(var, 'events', leg, lt, rt, param.formats, - param.outdir+"/"+sel, False, True, histos, colors, - param.ana_tex, extralab, scaleSig, customLabel, nsig, - nbkg, leg2, yields, plotStatUnc) + drawStack(var, 'events', leg, lt, rt, script_module.formats, + script_module.outdir + "/" + sel, False, True, histos, + colors, script_module.ana_tex, extralab, scaleSig, + customLabel, nsig, nbkg, leg2, yields, + config['plot_stat_unc']) return - if 'stack' in param.stacksig: - if 'lin' in param.yaxis: - drawStack(var+"_stack_lin", 'events', leg, lt, rt, param.formats, - param.outdir+"/"+sel, False, True, histos, colors, - param.ana_tex, extralab, scaleSig, customLabel, nsig, - nbkg, leg2, yields, plotStatUnc) - if 'log' in param.yaxis: - drawStack(var+"_stack_log", 'events', leg, lt, rt, param.formats, - param.outdir+"/"+sel, True, True, histos, colors, - param.ana_tex, extralab, scaleSig, customLabel, nsig, - nbkg, leg2, yields, plotStatUnc) - if 'lin' not in param.yaxis and 'log' not in param.yaxis: - LOGGER.info('Unrecognised option in formats, should be ' + if 'stack' in script_module.stacksig: + if 'lin' in script_module.yaxis: + drawStack(var + "_stack_lin", 'events', leg, lt, rt, + script_module.formats, script_module.outdir + "/" + sel, + False, True, histos, colors, script_module.ana_tex, + extralab, scaleSig, customLabel, nsig, nbkg, leg2, + yields, config['plot_stat_unc']) + if 'log' in script_module.yaxis: + drawStack(var + "_stack_log", 'events', leg, lt, rt, + script_module.formats, script_module.outdir + "/" + sel, + True, True, histos, colors, script_module.ana_tex, + extralab, scaleSig, customLabel, nsig, nbkg, leg2, + yields, config['plot_stat_unc']) + if 'lin' not in script_module.yaxis and \ + 'log' not in script_module.yaxis: + LOGGER.info('Unrecognized option in formats, should be ' '[\'lin\',\'log\']') - if 'nostack' in param.stacksig: - if 'lin' in param.yaxis: - drawStack(var+"_nostack_lin", 'events', leg, lt, rt, param.formats, - param.outdir+"/"+sel, False, False, histos, colors, - param.ana_tex, extralab, scaleSig, customLabel, nsig, - nbkg, leg2, yields, plotStatUnc) - if 'log' in param.yaxis: - drawStack(var+"_nostack_log", 'events', leg, lt, rt, param.formats, - param.outdir+"/"+sel, True, False, histos, colors, - param.ana_tex, extralab, scaleSig, customLabel, nsig, - nbkg, leg2, yields, plotStatUnc) - if 'lin' not in param.yaxis and 'log' not in param.yaxis: + if 'nostack' in script_module.stacksig: + if 'lin' in script_module.yaxis: + drawStack(var + "_nostack_lin", 'events', leg, lt, rt, + script_module.formats, + script_module.outdir + "/" + sel, False, False, histos, + colors, script_module.ana_tex, extralab, scaleSig, + customLabel, nsig, nbkg, leg2, yields, + config['plot_stat_unc']) + if 'log' in script_module.yaxis: + drawStack(var + "_nostack_log", 'events', leg, lt, rt, + script_module.formats, script_module.outdir + "/" + sel, + True, False, histos, colors, script_module.ana_tex, + extralab, scaleSig, customLabel, nsig, nbkg, leg2, + yields, config['plot_stat_unc']) + if 'lin' not in script_module.yaxis and \ + 'log' not in script_module.yaxis: LOGGER.info('Unrecognised option in formats, should be ' '[\'lin\',\'log\']') - if 'stack' not in param.stacksig and 'nostack' not in param.stacksig: - LOGGER.info('Unrecognised option in stacksig, should be ' + if 'stack' not in script_module.stacksig and \ + 'nostack' not in script_module.stacksig: + LOGGER.info('Unrecognized option in stacksig, should be ' '[\'stack\',\'nostack\']') # _____________________________________________________________________________ -def runPlotsHistmaker(hName, param, plotCfg): +def runPlotsHistmaker(args, hName, param, plotCfg): output = plotCfg['output'] hsignal, hbackgrounds = mapHistosFromHistmaker(hName, param, plotCfg) @@ -337,15 +371,15 @@ def runPlotsHistmaker(hName, param, plotCfg): # Below are settings for separate signal and background legends if splitLeg: - legsize = 0.04*(len(hsignal)) - legsize2 = 0.04*(len(hbackgrounds)) + legsize = 0.04 * (len(hsignal)) + legsize2 = 0.04 * (len(hbackgrounds)) legCoord = [0.15, 0.60 - legsize, 0.50, 0.62] leg2 = ROOT.TLegend(0.60, 0.60 - legsize2, 0.88, 0.62) leg2.SetFillColor(0) leg2.SetFillStyle(0) leg2.SetLineColor(0) leg2.SetShadowColor(10) - leg2.SetTextSize(0.035) + leg2.SetTextSize(args.legend_text_size) leg2.SetTextFont(42) else: legsize = 0.04*(len(hbackgrounds)+len(hsignal)) @@ -357,12 +391,16 @@ def runPlotsHistmaker(hName, param, plotCfg): legCoord = [0.68, 0.86-legsize, 0.96, 0.88] leg2 = None - leg = ROOT.TLegend(legCoord[0], legCoord[1], legCoord[2], legCoord[3]) + leg = ROOT.TLegend( + args.legend_x_min if args.legend_x_min > 0 else legCoord[0], + args.legend_y_min if args.legend_y_min > 0 else legCoord[1], + args.legend_x_max if args.legend_x_max > 0 else legCoord[2], + args.legend_y_max if args.legend_y_max > 0 else legCoord[3]) leg.SetFillColor(0) leg.SetFillStyle(0) leg.SetLineColor(0) leg.SetShadowColor(10) - leg.SetTextSize(0.035) + leg.SetTextSize(args.legend_text_size) leg.SetTextFont(42) for b in hbackgrounds: @@ -743,49 +781,87 @@ def print_canvas(canvas, name, formats, directory): # _____________________________________________________________________________ -def run(script_path): +def run(args): ''' Run over all the plots. ''' ROOT.gROOT.SetBatch(True) ROOT.gErrorIgnoreLevel = ROOT.kWarning - module_path = os.path.abspath(script_path) + module_path = os.path.abspath(args.script_path) module_dir = os.path.dirname(module_path) - base_name = os.path.splitext(ntpath.basename(script_path))[0] + base_name = os.path.splitext(ntpath.basename(args.script_path))[0] + # Load plot script as module sys.path.insert(0, module_dir) - param = importlib.import_module(base_name) - - split_leg = False - if hasattr(param, "splitLeg"): - split_leg = param.splitLeg - - plot_stat_unc = False - if hasattr(param, "plotStatUnc"): - plot_stat_unc = param.plotStatUnc - - if hasattr(param, "hists"): - for hist_name, plot_cfg in param.hists.items(): - runPlotsHistmaker(hist_name, param, plot_cfg) + script_module = importlib.import_module(base_name) + + # Merge script and command line arguments into one configuration object + config = {} + + config['split_leg'] = False + if hasattr(script_module, 'splitLeg'): + config['split_leg'] = script_module.splitLeg + + config['leg_position'] = [None, None, None, None] + if hasattr(script_module, 'legendCoord'): + config['leg_position'] = script_module.legendCoord + if args.legend_x_min is not None: + config['leg_position'][0] = args.legend_x_min + if args.legend_y_min is not None: + config['leg_position'][1] = args.legend_y_min + if args.legend_x_max is not None: + config['leg_position'][2] = args.legend_x_max + if args.legend_y_max is not None: + config['leg_position'][3] = args.legend_y_max + + config['plot_stat_unc'] = False + if hasattr(script_module, 'plotStatUnc'): + config['plot_stat_unc'] = script_module.plotStatUnc + + config['legend_text_size'] = 0.035 + if hasattr(script_module, 'legendTextSize'): + config['legend_text_size'] = script_module.legendTextSize + if args.legend_text_size is not None: + config['legend_text_size'] = args.legend_text_size + + # Handle plots for Histmaker analyses and exit + if hasattr(script_module, 'hists'): + for hist_name, plot_cfg in script_module.hists.items(): + runPlotsHistmaker(args, hist_name, script_module, plot_cfg) sys.exit() counter = 0 - for var_index, var in enumerate(param.variables): - for label, sels in param.selections.items(): + for var_index, var in enumerate(script_module.variables): + for label, sels in script_module.selections.items(): for sel in sels: rebin_tmp = 1 - if (hasattr(param, "rebin") and - len(param.rebin) == len(param.variables)): - rebin_tmp = param.rebin[var_index] - hsignal, hbackgrounds = mapHistos(var, label, sel, param, + if hasattr(script_module, "rebin"): + if len(script_module.rebin) == \ + len(script_module.variables): + rebin_tmp = script_module.rebin[var_index] + hsignal, hbackgrounds = mapHistos(var, + label, + sel, + script_module, rebin=rebin_tmp) - runPlots(var+"_"+label, sel, param, hsignal, hbackgrounds, - param.extralabel[sel], split_leg, plot_stat_unc) + runPlots(config, + args, + var + "_" + label, + sel, + script_module, + hsignal, + hbackgrounds, + script_module.extralabel[sel]) if counter == 0: - runPlots("AAAyields_"+label, sel, param, hsignal, - hbackgrounds, param.extralabel[sel], split_leg, - plot_stat_unc) + runPlots(config, + args, + "AAAyields_"+label, + sel, + script_module, + hsignal, + hbackgrounds, + script_module.extralabel[sel]) counter += 1 @@ -804,4 +880,4 @@ def do_plots(parser): args.script_path) sys.exit(3) - run(args.script_path) + run(args) diff --git a/python/parsers.py b/python/parsers.py index 2632f494ab..c3fde42ea2 100644 --- a/python/parsers.py +++ b/python/parsers.py @@ -143,6 +143,17 @@ def setup_run_parser_plots(parser): Define command line arguments for the plots sub-command. ''' parser.add_argument('script_path', help="path to the plots script") + parser.add_argument('--legend-text-size', type=float, default=None, + help='text size for the legend elements') + parser.add_argument('--legend-x-min', type=float, default=None, + help='minimal x position of the legend') + parser.add_argument('--legend-x-max', type=float, default=None, + help='maximal x position of the legend') + parser.add_argument('--legend-y-min', type=float, default=None, + help='minimal y position of the legend') + parser.add_argument('--legend-y-max', type=float, default=None, + help='maximal y position of the legend') + def setup_run_parser_combine(parser): ''' @@ -151,7 +162,6 @@ def setup_run_parser_combine(parser): parser.add_argument('script_path', help="path to the combine script") - # _____________________________________________________________________________ def setup_subparsers(subparsers): ''' @@ -192,4 +202,4 @@ def setup_subparsers(subparsers): setup_run_parser(parser_run) setup_run_parser_final(parser_run_final) setup_run_parser_plots(parser_run_plots) - setup_run_parser_combine(parser_run_combine) \ No newline at end of file + setup_run_parser_combine(parser_run_combine)