Skip to content

Commit f825780

Browse files
authored
Improve AOD creation and Analysis (#76)
- Make common header for python scripts - Add possibility to read hepmc files and convert to AODs - Add example inside the config file - Improve messages - Create intermediate AODs - Create temporary AODs to avoid running on the runs that are partially filled and killed mid-way
1 parent dbca05c commit f825780

File tree

5 files changed

+135
-139
lines changed

5 files changed

+135
-139
lines changed

examples/scripts/common.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#! /usr/bin/env python3
2+
3+
"""
4+
Common header for AOD python scripts
5+
"""
6+
7+
import argparse
8+
import multiprocessing
9+
import sys
10+
try:
11+
import tqdm
12+
except ImportError as e:
13+
print("Module tqdm is not imported. Progress bar will not be available (you can install tqdm for the progress bar)")
14+
15+
16+
# Global running flags
17+
verbose_mode = False
18+
19+
20+
def set_verbose_mode(parser):
21+
global verbose_mode
22+
verbose_mode = parser.verbose
23+
24+
25+
def get_default_parser(description):
26+
parser = argparse.ArgumentParser(description=description)
27+
parser.add_argument("--verbose", "-v",
28+
action="store_true", help="Verbose mode.")
29+
parser.add_argument("--njobs", "-j", type=int,
30+
default=10,
31+
help="Number of concurrent jobs, by default 10.")
32+
return parser
33+
34+
35+
class bcolors:
36+
# Colors for bash
37+
BOLD = "\033[1m"
38+
UNDERLINE = "\033[4m"
39+
HEADER = "\033[95m"
40+
OKBLUE = "\033[94m"
41+
BOKBLUE = BOLD + OKBLUE
42+
OKGREEN = "\033[92m"
43+
BOKGREEN = BOLD + OKGREEN
44+
WARNING = "\033[93m"
45+
BWARNING = BOLD + WARNING
46+
FAIL = "\033[91m"
47+
BFAIL = BOLD + FAIL
48+
ENDC = "\033[0m"
49+
50+
51+
def verbose_msg(*args, color=bcolors.OKBLUE):
52+
if verbose_mode:
53+
print("** ", color, *args, bcolors.ENDC)
54+
55+
56+
def msg(*args, color=bcolors.BOKBLUE):
57+
print(color, *args, bcolors.ENDC)
58+
59+
60+
def fatal_msg(*args, fatal_message="Fatal Error!"):
61+
msg("[FATAL]", *args, color=bcolors.BFAIL)
62+
raise RuntimeError(fatal_message)
63+
64+
65+
def warning_msg(*args):
66+
msg("[WARNING]", *args, color=bcolors.BWARNING)
67+
68+
69+
def run_in_parallel(processes, job_runner, job_arguments, job_message):
70+
"""
71+
In parallel processer of functions with a nice progress printing
72+
"""
73+
with multiprocessing.Pool(processes=processes) as pool:
74+
msg(job_message)
75+
if "tqdm" not in sys.modules:
76+
for i in enumerate(pool.imap(job_runner, job_arguments)):
77+
msg(f"Done: {i[0]+1},", len(job_arguments)-i[0]-1, "to go")
78+
else:
79+
r = list(tqdm.tqdm(pool.imap(job_runner, job_arguments),
80+
total=len(job_arguments),
81+
bar_format='{l_bar}{bar:10}{r_bar}{bar:-10b}'))

examples/scripts/createO2tables.py

Lines changed: 28 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,13 @@
44
Handler to run the DelphesO2 framework and to create O2 analysis tables
55
"""
66

7-
import argparse
87
import configparser
98
import os
109
import shutil
11-
import multiprocessing
1210
import time
1311
import random
1412
from datetime import datetime
15-
import sys
16-
try:
17-
import tqdm
18-
except ImportError as e:
19-
print("Module tqdm is not imported. Progress bar will not be available (you can install tqdm for the progress bar)")
20-
21-
# Global running flags
22-
verbose_mode = False
23-
metric_mode = False
24-
25-
26-
class bcolors:
27-
# Colors for bash
28-
BOLD = "\033[1m"
29-
UNDERLINE = "\033[4m"
30-
HEADER = "\033[95m"
31-
OKBLUE = "\033[94m"
32-
BOKBLUE = BOLD + OKBLUE
33-
OKGREEN = "\033[92m"
34-
BOKGREEN = BOLD + OKGREEN
35-
WARNING = "\033[93m"
36-
BWARNING = BOLD + WARNING
37-
FAIL = "\033[91m"
38-
BFAIL = BOLD + FAIL
39-
ENDC = "\033[0m"
40-
41-
42-
def verbose_msg(*args, color=bcolors.OKBLUE):
43-
if verbose_mode:
44-
print("** ", color, *args, bcolors.ENDC)
45-
46-
47-
def msg(*args, color=bcolors.BOKBLUE):
48-
print(color, *args, bcolors.ENDC)
49-
50-
51-
def fatal_msg(*args):
52-
msg("[FATAL]", *args, color=bcolors.BFAIL)
53-
raise ValueError("Fatal Error!")
13+
from common import bcolors, msg, fatal_msg, verbose_msg, run_in_parallel, set_verbose_mode, get_default_parser, warning_msg
5414

5515

5616
def run_cmd(cmd, comment="", check_status=True):
@@ -68,14 +28,10 @@ def run_cmd(cmd, comment="", check_status=True):
6828
for i in content.strip().split("\n"):
6929
verbose_msg("++", i)
7030
if "Encountered error" in content:
71-
msg("[WARNING] Error encountered runtime in",
72-
cmd, color=bcolors.BWARNING)
31+
warning_msg("Error encountered runtime in", cmd)
7332
if check_status:
7433
if "OK" not in content and "root" not in cmd:
75-
msg("Error:\n",
76-
content, color=bcolors.FAIL)
77-
raise RuntimeError(
78-
"Command", cmd, "does not have the OK tag", content)
34+
fatal_msg("Command", cmd, "does not have the OK tag", content)
7935
except:
8036
fatal_msg("Error while running", f"'{cmd}'")
8137

@@ -85,8 +41,8 @@ def process_run(run_number):
8541
verbose_msg("> starting run", run_number)
8642
run_cmd(f"bash runner{run_number}.sh")
8743
if not os.path.isfile(f"AODRun5.{run_number}.root"):
88-
msg("++ something went wrong for run", run_number, ", no output table found. Please check:",
89-
f"AODRun5.{run_number}.log", color=bcolors.FAIL)
44+
msg(f"++ something went wrong for run {run_number}, no output table found. Please check: 'AODRun5.{run_number}.log'",
45+
color=bcolors.FAIL)
9046
verbose_msg("< complete run", run_number)
9147
processing_time = time.time() - processing_time
9248
verbose_msg(f"-- took {processing_time} seconds --",
@@ -98,16 +54,13 @@ def main(configuration_file,
9854
njobs,
9955
nruns,
10056
nevents,
101-
verbose,
10257
qa,
10358
output_path,
10459
clean_delphes_files,
10560
create_luts,
10661
turn_off_vertexing,
10762
append_production):
108-
arguments = locals()
109-
global verbose_mode
110-
verbose_mode = verbose
63+
arguments = locals() # List of arguments to put into the log
11164
parser = configparser.RawConfigParser()
11265
parser.read(configuration_file)
11366

@@ -343,8 +296,18 @@ def copy_and_link(file_name):
343296
gen_log_file = f"gen.{run_number}.log"
344297
hepmc_file = f"hepmcfile.{run_number}.hepmc"
345298
custom_gen_option = f" --output {hepmc_file} --nevents {nevents} --seed {mc_seed}"
346-
write_to_runner(custom_gen + custom_gen_option,
347-
log_file=gen_log_file, check_status=True)
299+
if "INPUT_FILES" in custom_gen:
300+
input_hepmc_file = custom_gen.replace("INPUT_FILES",
301+
"").strip().split(" ")
302+
if run_number < len(input_hepmc_file):
303+
input_hepmc_file = input_hepmc_file[run_number]
304+
else:
305+
return
306+
write_to_runner(f"ln -s {input_hepmc_file}"
307+
f" {hepmc_file} \n")
308+
else:
309+
write_to_runner(custom_gen + custom_gen_option,
310+
log_file=gen_log_file, check_status=True)
348311
write_to_runner(f"DelphesHepMC propagate.tcl {delphes_file} {hepmc_file}",
349312
log_file=delphes_log_file, check_status=True)
350313
else: # Using DelphesPythia
@@ -370,9 +333,11 @@ def copy_and_link(file_name):
370333
check_status=True)
371334
aod_file = f"AODRun5.{run_number}.root"
372335
aod_log_file = aod_file.replace(".root", ".log")
373-
write_to_runner(f"root -l -b -q 'createO2tables.C+(\"{delphes_file}\", \"{aod_file}\", 0)'",
336+
write_to_runner(f"root -l -b -q 'createO2tables.C+(\"{delphes_file}\", \"tmp_{aod_file}\", 0)'",
374337
log_file=aod_log_file,
375338
check_status=True)
339+
write_to_runner(f"mv tmp_{aod_file} {aod_file}",
340+
check_status=True)
376341
if not clean_delphes_files:
377342
copy_and_link(delphes_file)
378343
if hepmc_file is not None:
@@ -383,6 +348,8 @@ def copy_and_link(file_name):
383348
if hepmc_file is not None:
384349
write_to_runner(f"rm {hepmc_file}")
385350
write_to_runner("exit 0\n")
351+
352+
# Configuring all the runs
386353
for i in run_list:
387354
configure_run(i)
388355

@@ -395,15 +362,8 @@ def copy_and_link(file_name):
395362
fatal_msg("'createO2tables.C' did not compile!")
396363
total_processing_time = time.time()
397364
msg(" --- start processing the runs ", color=bcolors.HEADER)
398-
with multiprocessing.Pool(processes=njobs) as pool:
399-
msg("Running production")
400-
if "tqdm" not in sys.modules:
401-
for i in enumerate(pool.imap(process_run, run_list)):
402-
msg(f"Done: {i[0]+1},", len(run_list)-i[0]-1, "to go")
403-
else:
404-
r = list(tqdm.tqdm(pool.imap(process_run, run_list),
405-
total=len(run_list),
406-
bar_format='{l_bar}{bar:10}{r_bar}{bar:-10b}'))
365+
run_in_parallel(processes=njobs, job_runner=process_run,
366+
job_arguments=run_list, job_message="Running production")
407367

408368
# merge runs when all done
409369
msg(" --- all runs are processed, so long", color=bcolors.HEADER)
@@ -471,7 +431,7 @@ def write_config(entry, prefix=""):
471431

472432

473433
if __name__ == "__main__":
474-
parser = argparse.ArgumentParser(description=__doc__)
434+
parser = get_default_parser(description=__doc__)
475435
parser.add_argument("configuration_file", type=str,
476436
help="Input configuration file e.g. you can use the provided default_configfile.ini or variations of it.")
477437
parser.add_argument("--entry", "-e", type=str,
@@ -480,9 +440,6 @@ def write_config(entry, prefix=""):
480440
parser.add_argument("--output-path", "--output_path", "-o", type=str,
481441
default=None,
482442
help="Output path, by default the current path is used as output.")
483-
parser.add_argument("--njobs", "-j", type=int,
484-
default=10,
485-
help="Number of concurrent jobs, by default 10.")
486443
parser.add_argument("--nevents", "--ev", type=int,
487444
default=1000,
488445
help="Number of simulated events, by default 1000.")
@@ -491,8 +448,6 @@ def write_config(entry, prefix=""):
491448
help="Number of runs, by default 10.")
492449
parser.add_argument("--qa", "-qa", action="store_true",
493450
help="QA mode: runs basic tasks at the end to assess QA.")
494-
parser.add_argument("--verbose", "-v",
495-
action="store_true", help="Verbose mode.")
496451
parser.add_argument("--clean-delphes", "-c",
497452
action="store_true",
498453
help="Option to clean the delphes files in output and keep only the AODs, by default everything is kept.")
@@ -506,17 +461,17 @@ def write_config(entry, prefix=""):
506461
action="store_true",
507462
help="Option to use preexisting LUTs instead of creating new ones, in this case LUTs with the requested tag are fetched from the LUT path. By default new LUTs are created at each run.")
508463
args = parser.parse_args()
464+
set_verbose_mode(args)
465+
509466
# Check arguments
510467
if args.append and args.output_path is None:
511468
fatal_msg(
512469
"Asked to append production but did not specify output path (option '-o')")
513-
514470
main(configuration_file=args.configuration_file,
515471
config_entry=args.entry,
516472
njobs=args.njobs,
517473
nevents=args.nevents,
518474
nruns=args.nruns,
519-
verbose=args.verbose,
520475
output_path=args.output_path,
521476
clean_delphes_files=args.clean_delphes,
522477
qa=args.qa,

examples/scripts/default_configfile.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ generators = $O2DPG_ROOT/MC/config/common/pythia8/generator/pythia8_KrKr.cfg
7070
[INEL_HI_XEXE] # (need O2DPG loaded)
7171
generators = $DELPHESO2_ROOT/examples/pythia8/pythia8_XeXe.cfg
7272

73+
[INPUT_HEPMC] # AOD creation from input HEPMC files
74+
custom_gen = INPUT_FILES /tmp/AnalysisResults_1.hepmc /tmp/AnalysisResults_2.hepmc
75+
7376
[GUN]
7477
custom_gen = rpythia8-gun --pdg 421 --px 1. --py 0. --pz 0. --xProd 0. --yProd 0. --zProd 0. --config $DELPHESO2_ROOT/examples/pythia8/decays/force_hadronic_D.cfg --decay
7578

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../common.py

0 commit comments

Comments
 (0)