Belle II Software  release-08-01-10
whizard.py
1 
8 
9 '''
10 Helper functions for producing events with the WHIZARD generator.
11 '''
12 
13 import hashlib
14 import os
15 import shutil
16 import subprocess
17 
18 import b2test_utils
19 import basf2
20 import 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 = '''
65 process {process} = "e-", "e+" => {final_state}
66 
67 ! ISR configuration
68 beams = "e-", "e+" => isr
69 ?isr_handler = true
70 $isr_handler_mode = "recoil"
71 ?keep_beams = true
72 ?keep_remnants = true
73 isr_mass = me
74 
75 seed = {random_seed}
76 sqrts = {cm_energy} GeV
77 n_events = {events}
78 sample_format = lhef
79 {cuts}
80 integrate ({process}) {{
81  iterations = {iterations}
82  relative_error_goal = 0.05
83 }}
84 simulate ({process})
85 '''
86 
87 
88 def 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 
106 def 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 
161 if __name__ == "__main__":
162  pass
def get_collisions_invariant_mass(experiment, run, verbose=False)
def clean_working_directory()
Definition: __init__.py:189