Belle II Software development
whizard.py
1
8
9'''
10Helper functions for producing events with the WHIZARD generator.
11'''
12
13import hashlib
14import os
15import shutil
16import subprocess
17
18import b2test_utils
19import basf2
20import beamparameters
21
22
23
24_default_iterations = '25:10000:"gw", 10:10000:""'
25
26
27_refined_iterations = '25:10000:"gw", 20:10000:""'
28
29
30_processes_dict = {
31 'eeee': {
32 'final_state': '"e-", "e+", "e-", "e+"',
33 'cuts': 'cuts = all M > 500 MeV ["e-" + "e+"]',
34 'iterations': _refined_iterations,
35 },
36 'eemumu': {
37 'final_state': '"e-", "e+", "mu-", "mu+"',
38 'cuts': 'cuts = all M > 500 MeV ["mu+" + "mu-"]',
39 'iterations': _default_iterations
40 },
41 'eetautau': {
42 'final_state': '"e-", "e+", "tau-", "tau+"',
43 'cuts': '',
44 'iterations': _default_iterations
45 },
46 'mumumumu': {
47 'final_state': '"mu-", "mu+", "mu-", "mu+"',
48 'cuts': '',
49 'iterations': _default_iterations
50 },
51 'mumutautau': {
52 'final_state': '"mu-", "mu+", "tau-", "tau+"',
53 'cuts': '',
54 'iterations': _default_iterations
55 },
56 'tautautautau': {
57 'final_state': '"tau-", "tau+", "tau-", "tau+"',
58 'cuts': '',
59 'iterations': _default_iterations
60 }
61}
62
63
64_sindarin_template = '''
65process {process} = "e-", "e+" => {final_state}
66
67! ISR configuration
68beams = "e-", "e+" => isr
69?isr_handler = true
70$isr_handler_mode = "recoil"
71?keep_beams = true
72?keep_remnants = true
73isr_mass = me
74
75seed = {random_seed}
76sqrts = {cm_energy} GeV
77n_events = {events}
78sample_format = lhef
79{cuts}
80integrate ({process}) {{
81 iterations = {iterations}
82 relative_error_goal = 0.05
83}}
84simulate ({process})
85'''
86
87
88def get_sindarin(process, events, cm_energy, random_seed=114):
89 '''
90 Return a properly formatted SINDARIN file as a string.
91 '''
92 if process not in _processes_dict.keys():
93 basf2.B2FATAL(f'The process {process} is currently not supported, you need to write a SINDARIN file yourself.')
94 sindarin = _sindarin_template.format(
95 process=process,
96 final_state=_processes_dict[process]['final_state'],
97 cuts=_processes_dict[process]['cuts'],
98 iterations=_processes_dict[process]['iterations'],
99 events=events,
100 cm_energy=cm_energy,
101 random_seed=random_seed
102 )
103 return sindarin
104
105
106def run_whizard(process, experiment, run, events, print_sindarin=False):
107 '''
108 This function takes care of running WHIZARD.
109 '''
110 if process not in _processes_dict.keys():
111 basf2.B2FATAL(f'The process {process} is currently not supported, you need to write a SINDARIN file yourself.')
112
113 cm_energy = beamparameters.get_collisions_invariant_mass(experiment, run)
114
115 basf2_seed = basf2.get_random_seed().encode('utf-8')
116 whizard_seed = int(hashlib.sha256(basf2_seed).hexdigest(), 16) % 10**8
117
118 sindarin = get_sindarin(process, events, cm_energy, whizard_seed)
119 if print_sindarin:
120 basf2.B2INFO(f'The SINDARIN file is:\n\n{sindarin}')
121
122 cwd = os.getcwd()
123 path = os.path.join(cwd, f'{process}_{whizard_seed}')
124 whizard = 'whizard'
125 sin = f'{process}.sin'
126 lhe = f'{process}.lhe'
127 log = f'{process}.log'
128
129 if not os.path.exists(path):
130 os.mkdir(path)
131 else:
132 shutil.rmtree(path, ignore_errors=True)
133 os.mkdir(path)
134
135 # Hacky solution for keeping track if WHIZARD failed or not
136 # since we want to ensure that the working directory is actually cleaned
137 failed = False
138
139 # Run WHIZARD dumping the output files in a temporary directory
141
142 with open(sin, 'w') as sindarin_file:
143 sindarin_file.write(sindarin)
144
145 tmp = os.getcwd()
146
147 try:
148 subprocess.check_call([whizard, '-L', log, sin])
149 moved_lhe, moved_log = os.path.join(path, lhe), os.path.join(path, log)
150 shutil.move(os.path.join(tmp, lhe), moved_lhe)
151 shutil.move(os.path.join(tmp, log), moved_log)
152 except subprocess.CalledProcessError:
153 failed = True
154
155 if failed:
156 basf2.B2FATAL(f'WHIZARD failed while generating the {process} process')
157
158 return path, moved_lhe, moved_log
159
160
161if __name__ == "__main__":
162 pass
def get_collisions_invariant_mass(experiment, run, verbose=False)
def clean_working_directory()
Definition: __init__.py:189