Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
utils.py
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file utils.py
1 #!/usr/bin/env python3
2 import numpy as np
3 import subprocess
4 import argparse
5 import os
6 import shutil
7 
8 parser = argparse.ArgumentParser()
9 subparser = parser.add_subparsers(dest='command')
10 
11 create = subparser.add_parser('create', help='Create condor submission directory.')
12 status = subparser.add_parser('status', help='Check the status of the condor submission.')
13 hadd = subparser.add_parser('hadd', help='Merge completed condor jobs.')
14 
15 create.add_argument('-e', '--executable', type=str, default='scripts/genFun4All.sh', help='Job script to execute. Default: scripts/genFun4All.sh')
16 create.add_argument('-a', '--macros', type=str, default='current/macro', help='Directory of input macros. Directory containing Fun4All_G4_sPHENIX.C and G4Setup_sPHENIX.C. Default: current/macro')
17 create.add_argument('-b', '--bin-dir', type=str, default='current/bin', help='Directory containing Fun4All_G4_sPHENIX executable. Default: current/bin')
18 create.add_argument('-n', '--events', type=int, default=1, help='Number of events to generate. Default: 1.')
19 create.add_argument('-d', '--output', type=str, default='test', help='Output Directory. Default: Current Directory.')
20 create.add_argument('-m', '--jobs-per-submission', type=int, default=20000, help='Maximum number of jobs per condor submission. Default: 20000.')
21 create.add_argument('-j', '--events-per-job', type=int, default=100, help='Number of events to generate per job. Default: 100.')
22 create.add_argument('-s', '--memory', type=int, default=6, help='Memory (units of GB) to request per condor submission. Default: 6 GB.')
23 create.add_argument('-u', '--build-tag', type=str, default='unknown', help='Specify build tag. Ex: ana.xxx, Default: unknown')
24 
25 status.add_argument('-d','--condor-dir', type=str, help='Condor submission directory.', required=True)
26 
27 hadd.add_argument('-i','--job-dir-list', type=str, help='List of directories containing condor jobs to be merged.', required=True)
28 hadd.add_argument('-o','--output', type=str, default='test.root', help='Output root file. Default: test.root.')
29 hadd.add_argument('-n','--jobs-per-hadd', type=int, default=5000, help='Number of jobs to merge per hadd call. Default: 5000.')
30 hadd.add_argument('-j','--jobs-open', type=int, default=50, help='Number of jobs to load at once. Default: 50.')
31 hadd.add_argument('-m','--multiple-submit-dir', type=bool, default=False,help='If merging condor jobs over multiple directories. Default: False')
32 
33 args = parser.parse_args()
34 
36  events = args.events
37  jobs_per_submission = args.jobs_per_submission
38  output_dir = os.path.realpath(args.output)
39  bin_dir = os.path.realpath(args.bin_dir)
40  executable = os.path.realpath(args.executable)
41  events_per_job = min(args.events_per_job, events)
42  memory = args.memory
43  macros_dir = os.path.realpath(args.macros)
44  jobs = events//events_per_job
45  submissions = int(np.ceil(jobs/jobs_per_submission))
46  tag = args.build_tag
47 
48  print(f'Events: {events}')
49  print(f'Events per job: {events_per_job}')
50  print(f'Jobs: {jobs}')
51  print(f'Maximum jobs per condor submission: {jobs_per_submission}')
52  print(f'Submissions: {submissions}')
53  print(f'Requested memory per job: {memory}GB')
54  print(f'Output Directory: {output_dir}')
55  print(f'Macros Directory: {macros_dir}')
56  print(f'Bin Directory: {bin_dir}')
57  print(f'Executable: {executable}')
58  print(f'Build Tag: {tag}')
59 
60  os.makedirs(output_dir,exist_ok=True)
61  with open(f'{output_dir}/log.txt', mode='w') as file:
62  file.write(f'Events: {events}\n')
63  file.write(f'Events per job: {events_per_job}\n')
64  file.write(f'Jobs: {jobs}\n')
65  file.write(f'Maximum jobs per condor submission: {jobs_per_submission}\n')
66  file.write(f'Submissions: {submissions}\n')
67  file.write(f'Requested memory per job: {memory}GB\n')
68  file.write(f'Output Directory: {output_dir}\n')
69  file.write(f'Macros Directory: {macros_dir}\n')
70  file.write(f'Bin Directory: {bin_dir}\n')
71  file.write(f'Executable: {executable}\n')
72  file.write(f'Build Tag: {tag}\n')
73 
74  # Generate condor submission file
75  condor_file = f'{output_dir}/genFun4All.sub'
76  with open(condor_file, mode="w") as file:
77  file.write(f'executable = bin/{os.path.basename(executable)}\n')
78  file.write(f'arguments = $(myPid) $(initialSeed) {events_per_job}\n')
79  file.write('log = log/job-$(myPid).log\n')
80  file.write('output = stdout/job-$(myPid).out\n')
81  file.write('error = error/job-$(myPid).err\n')
82  file.write(f'request_memory = {memory}GB\n')
83  file.write('should_transfer_files = YES\n')
84  file.write('when_to_transfer_output = ON_EXIT\n')
85  # file.write('transfer_input_files = src/Fun4All_G4_sPHENIX.C, src/G4Setup_sPHENIX.C\n')
86  file.write('transfer_input_files = bin/Fun4All_G4_sPHENIX\n')
87  file.write('transfer_output_files = G4sPHENIX_g4cemc_eval-$(myPid).root\n')
88  file.write('transfer_output_remaps = "G4sPHENIX_g4cemc_eval-$(myPid).root = output/G4sPHENIX_g4cemc_eval-$(myPid).root"\n')
89  file.write('queue myPid,initialSeed from seed.txt')
90 
91  for i in range(submissions):
92  print(f'Submission: {i}')
93 
94  submit_dir = f'{output_dir}/submission-{i}'
95  print(f'Submission Directory: {submit_dir}')
96 
97  os.makedirs(f'{submit_dir}/stdout',exist_ok=True)
98  os.makedirs(f'{submit_dir}/error',exist_ok=True)
99  os.makedirs(f'{submit_dir}/log',exist_ok=True)
100  os.makedirs(f'{submit_dir}/output',exist_ok=True)
101  os.makedirs(f'{submit_dir}/bin',exist_ok=True)
102  os.makedirs(f'{submit_dir}/src',exist_ok=True)
103 
104  shutil.copy(condor_file, submit_dir)
105  shutil.copy(executable, f'{submit_dir}/bin')
106  shutil.copy(f'{bin_dir}/Fun4All_G4_sPHENIX', f'{submit_dir}/bin')
107  shutil.copy(f'{macros_dir}/Fun4All_G4_sPHENIX.C', f'{submit_dir}/src')
108  shutil.copy(f'{macros_dir}/G4Setup_sPHENIX.C', f'{submit_dir}/src')
109 
110  file_name = f'{submit_dir}/seed.txt'
111  with open(file_name, mode="w") as file:
112  for j in range(min(jobs,jobs_per_submission)):
113  file.write(f'{j} {i*jobs_per_submission+100}\n')
114 
115  jobs -= min(jobs,jobs_per_submission)
116  print(f'Written {file_name}')
117 
119  condor_dir = os.path.realpath(args.condor_dir)
120  submit_dirs = next(os.walk(condor_dir))[1]
121  print(f'Condor Directory: {condor_dir}')
122  jobs_done_total = 0
123  total = 0
124  for submit_dir in submit_dirs:
125  jobs_done = len(os.listdir(f'{condor_dir}/{submit_dir}/output'))
126  jobs_total = len(os.listdir(f'{condor_dir}/{submit_dir}/log'))
127  if(jobs_total != 0):
128  print(f'Condor submission dir: {condor_dir}/{submit_dir}, done: {jobs_done}, {jobs_done/jobs_total*100:.2f} %')
129  jobs_done_total += jobs_done
130  total += jobs_total
131 
132  if(total != 0):
133  print(f'Total jobs done: {jobs_done_total}, {jobs_done_total/total*100:.2f} %')
134 
135 def hadd(jobs_dir):
136  output = os.path.realpath(args.output)
137  jobs_per_hadd = args.jobs_per_hadd
138  jobs_open = args.jobs_open+1
139  print(f'jobs directory: {jobs_dir}')
140  print(f'output: {output}')
141  print(f'jobs per hadd: {jobs_per_hadd}')
142  print(f'jobs open at once: {jobs_open-1}')
143 
144  jobs = os.listdir(jobs_dir)
145  jobs = [f'{jobs_dir}/{job}' for job in jobs]
146 
147  total_jobs = len(jobs)
148  hadd_calls = int(np.ceil(total_jobs/jobs_per_hadd))
149 
150  print(f'total jobs: {total_jobs}')
151  print(f'hadd calls: {hadd_calls}')
152 
153  for i in range(hadd_calls):
154  subprocess.run(['echo', '#######################'])
155  subprocess.run(['echo', f'working on hadd: {i}'])
156  command = f'hadd -a -n {jobs_open} {output}'.split()
157  i_start = jobs_per_hadd*i
158  i_end = min(jobs_per_hadd*(i+1), total_jobs)
159  subprocess.run(['echo', f'i_start: {i_start}, i_end: {i_end}'])
160  command.extend(jobs[i_start:i_end])
161  subprocess.run(command)
162  subprocess.run(['echo', f'done with hadd: {i}'])
163  subprocess.run(['echo', '#######################'])
164 
165 if __name__ == '__main__':
166  if(args.command == 'create'):
167  create_jobs()
168  elif(args.command == 'status'):
169  get_status()
170  elif(args.command == 'hadd'):
171  if(args.multiple_submit_dir):
172  job_dir_list = os.path.realpath(args.job_dir_list)
173  with open(job_dir_list) as f:
174  for jobs_dir in f:
175  jobs_dir = jobs_dir.strip()
176  hadd(jobs_dir)
177  else:
178  job_dir = args.job_dir_list
179  hadd(jobs_dir)