Package madgraph :: Package iolibs :: Module helas_call_writers
[hide private]
[frames] | no frames]

Source Code for Module madgraph.iolibs.helas_call_writers

   1  ################################################################################ 
   2  # 
   3  # Copyright (c) 2010 The MadGraph5_aMC@NLO Development team and Contributors 
   4  # 
   5  # This file is a part of the MadGraph5_aMC@NLO project, an application which  
   6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
   7  # high-energy processes in the Standard Model and beyond. 
   8  # 
   9  # It is subject to the MadGraph5_aMC@NLO license which should accompany this  
  10  # distribution. 
  11  # 
  12  # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch 
  13  # 
  14  ################################################################################ 
  15  """Classes for writing Helas calls. HelasCallWriter is the base class.""" 
  16   
  17  import madgraph.core.base_objects as base_objects 
  18  import madgraph.core.helas_objects as helas_objects 
  19  import madgraph.loop.loop_helas_objects as loop_helas_objects 
  20  import models.check_param_card as check_param_card 
  21  import aloha.aloha_writers as aloha_writers 
  22  import aloha 
  23  from madgraph import MadGraph5Error 
  24  import madgraph.various.misc as misc 
25 26 -class HelasWriterError(Exception):
27 """Class for the error of this module """ 28 pass
29
30 31 #=============================================================================== 32 # HelasCallWriter 33 #=============================================================================== 34 -class HelasCallWriter(base_objects.PhysicsObject):
35 """Language independent base class for writing Helas calls. The 36 calls are stored in two dictionaries, wavefunctions and 37 amplitudes, with entries being a mapping from a set of spin, 38 incoming/outgoing states and Lorentz structure to a function which 39 writes the corresponding wavefunction/amplitude call (taking a 40 HelasWavefunction/HelasAmplitude as argument).""" 41 42 # Dictionaries used for automatic generation of Helas calls 43 # Dictionaries from spin states to letters in Helas call 44 mother_dict = {1: 'S', 2: 'O', -2: 'I', 3: 'V', 5: 'T', 4:'OR', -4:'IR', 45 99:'P'} 46 47 @staticmethod
49 """ Place holder for PLUGIN/... 50 (used by madevent output for small width handling) 51 """ 52 return call, arg
53 54 @staticmethod
56 return call,arg
57 #customize_argument_for_all_other_helas_object = fct_customize_argument_for_all_other_helas_object 58 #default_customize_argument_for_all_other_helas_object = fct_customize_argument_for_all_other_helas_object 59
60 - def default_setup(self):
61 62 self['model'] = base_objects.Model() 63 self['wavefunctions'] = {} 64 self['amplitudes'] = {}
65
66 - def filter(self, name, value):
67 """Filter for model property values""" 68 69 if name == 'model': 70 if not isinstance(value, base_objects.Model): 71 raise self.PhysicsObjectError, \ 72 "Object of type %s is not a model" % type(value) 73 74 if name == 'wavefunctions': 75 # Should be a dictionary of functions returning strings, 76 # with keys (spins, flow state) 77 if not isinstance(value, dict): 78 raise self.PhysicsObjectError, \ 79 "%s is not a valid dictionary for wavefunction" % \ 80 str(value) 81 82 for key in value.keys(): 83 self.add_wavefunction(key, value[key]) 84 85 if name == 'amplitudes': 86 # Should be a dictionary of functions returning strings, 87 # with keys (spins, flow state) 88 if not isinstance(value, dict): 89 raise self.PhysicsObjectError, \ 90 "%s is not a valid dictionary for amplitude" % \ 91 str(value) 92 93 for key in value.keys(): 94 self.add_amplitude(key, value[key]) 95 96 return True
97
98 - def get_sorted_keys(self):
99 """Return process property names as a nicely sorted list.""" 100 101 return ['model', 'wavefunctions', 'amplitudes']
102 103 104 105 106 107
108 - def get_loop_amp_helas_calls(self, matrix_element):
109 """Return a list of strings, corresponding to the Helas calls 110 for building loop amplitudes (AMPL) only.""" 111 112 assert isinstance(matrix_element, loop_helas_objects.LoopHelasMatrixElement), \ 113 "%s not valid argument for get_loop_amp_helas_calls" % \ 114 repr(matrix_element) 115 116 res = [] 117 118 for diagram in matrix_element.get_loop_diagrams(): 119 res.append("# Loop amplitude for loop diagram with ID %d" % \ 120 diagram.get('number')) 121 for amplitude in diagram.get_loop_amplitudes(): 122 # Substitute the proc_prefix 123 res.append(self.get_amplitude_call(amplitude)) 124 125 return res
126
127 - def get_sqso_target_skip_code(self, number_checked, sqso_target_numbers, 128 continue_label, split_orders, squared_orders, comment):
129 """Treat the optimization for the squared order target so that 130 unnecessary computations can be skipped. This function returns the lines 131 of codes for doing so, based on the number_checked (typically amplitude number) 132 to be checked and the list of squared order contributions 133 sqso_target_numbers specifying for each of them the maximum contributing 134 number. The continue_label informs where the code must 'goto' if indeed 135 the rest of this part of the computation must be skipped. 136 The split_orders and squared_orders values lists, as well as the string 137 comment template is just for printing out a nice comment in the fortran 138 code explaining what is going on.""" 139 140 res = [] 141 for sqso_index, target in enumerate(sqso_target_numbers): 142 if target!=number_checked: 143 continue 144 sqso_name = ' '.join(['%s=%d'%(split_orders[i],val) for i, \ 145 val in enumerate(squared_orders[sqso_index][0])]) 146 sqso_identifier = "(%s), i.e. of split order ID=%d,"\ 147 %(sqso_name, sqso_index+1) 148 res.append(comment%sqso_identifier) 149 res.append("IF(FILTER_SO.AND.SQSO_TARGET."+\ 150 "EQ.%d) GOTO %d"%(sqso_index+1,continue_label)) 151 return res
152
153 - def get_born_ct_helas_calls(self, matrix_element, include_CT=True, 154 squared_orders=[], split_orders=[]):
155 """Return a two lists of strings, the first corresponding to the Helas 156 calls for building the non-loop wavefunctions, the born and CT (only if 157 include_CT=True). The second correspond to the Helas calls for the 158 UVCT amplitudes only. The squared_orders can provide the information of 159 what is the maximum contributing CT amp number.""" 160 161 assert isinstance(matrix_element, loop_helas_objects.LoopHelasMatrixElement), \ 162 "%s not valid argument for get_born_ct_helas_calls" % \ 163 repr(matrix_element) 164 165 res = [] 166 167 sqso_max_uvctamp = [sqso[1][0] for sqso in squared_orders] 168 sqso_max_ctamp = [sqso[1][1] for sqso in squared_orders] 169 170 for diagram in matrix_element.get_born_diagrams(): 171 res.extend([ self.get_wavefunction_call(wf) for \ 172 wf in diagram.get('wavefunctions') ]) 173 res.append("# Amplitude(s) for born diagram with ID %d" % \ 174 diagram.get('number')) 175 for amplitude in diagram.get('amplitudes'): 176 res.append(self.get_amplitude_call(amplitude)) 177 178 for diagram in matrix_element.get_loop_diagrams(): 179 res.extend([ self.get_wavefunction_call(wf) for \ 180 wf in diagram.get('wavefunctions') ]) 181 if diagram.get_ct_amplitudes() and include_CT: 182 res.append("# Counter-term amplitude(s) for loop diagram number %d" % \ 183 diagram.get('number')) 184 for ctamp in diagram.get_ct_amplitudes(): 185 res.append(self.get_amplitude_call(ctamp)) 186 res.extend(self.get_sqso_target_skip_code(ctamp.get('number'), 187 sqso_max_ctamp, 2000, split_orders, squared_orders, 188 "# At this point, all CT amps needed for %s are computed.")) 189 190 if not include_CT: 191 return res, [] 192 193 res_UVCT = [] 194 for diagram in matrix_element.get_loop_UVCT_diagrams(): 195 res_UVCT.extend([ self.get_wavefunction_call(wf) for \ 196 wf in diagram.get('wavefunctions') ]) 197 res_UVCT.append("# Amplitude(s) for UVCT diagram with ID %d" % \ 198 diagram.get('number')) 199 for ctamp in diagram.get('amplitudes'): 200 res_UVCT.append(self.get_amplitude_call(ctamp)) 201 res_UVCT.extend(self.get_sqso_target_skip_code(ctamp.get('number'), 202 sqso_max_uvctamp, 3000, split_orders, squared_orders, 203 "# At this point, all UVCT amps needed for %s are computed.")) 204 205 return res, res_UVCT
206
207 - def get_loop_matrix_element_calls(self, loop_matrix_element):
208 """Return a list of strings, corresponding to the Helas calls 209 for the loop matrix element""" 210 211 res_born_CT, res_UVCT = self.get_born_ct_helas_calls(loop_matrix_element) 212 res = res_born_CT + res_UVCT + \ 213 self.get_loop_amp_helas_calls(loop_matrix_element) 214 return res
215 216
217 - def get_matrix_element_calls(self, matrix_element):
218 """Return a list of strings, corresponding to the Helas calls 219 for the matrix element""" 220 221 assert isinstance(matrix_element, helas_objects.HelasMatrixElement), \ 222 "%s not valid argument for get_matrix_element_calls" % \ 223 type(matrix_element) 224 225 # Do not reuse the wavefunctions for loop matrix elements 226 if isinstance(matrix_element, loop_helas_objects.LoopHelasMatrixElement): 227 return self.get_loop_matrix_element_calls(matrix_element) 228 229 me = matrix_element.get('diagrams') 230 matrix_element.reuse_outdated_wavefunctions(me) 231 232 res = [] 233 for diagram in matrix_element.get('diagrams'): 234 res.extend([ self.get_wavefunction_call(wf) for \ 235 wf in diagram.get('wavefunctions') ]) 236 res.append("# Amplitude(s) for diagram number %d" % \ 237 diagram.get('number')) 238 for amplitude in diagram.get('amplitudes'): 239 res.append(self.get_amplitude_call(amplitude)) 240 241 return res
242
243 - def get_wavefunction_calls(self, wavefunctions):
244 """Return a list of strings, corresponding to the Helas calls 245 for the matrix element""" 246 247 assert isinstance(wavefunctions, helas_objects.HelasWavefunctionList), \ 248 "%s not valid argument for get_wavefunction_calls" % \ 249 repr(wavefunctions) 250 251 res = [self.get_wavefunction_call(wf) for wf in wavefunctions] 252 253 return res
254
255 - def get_amplitude_calls(self, matrix_element):
256 """Return a list of strings, corresponding to the Helas calls 257 for the matrix element""" 258 259 assert isinstance(matrix_element, helas_objects.HelasMatrixElement), \ 260 "%s not valid argument for get_matrix_element_calls" % \ 261 repr(matrix_element) 262 263 res = [] 264 for diagram in matrix_element.get('diagrams'): 265 res.append("# Amplitude(s) for diagram number %d" % \ 266 diagram.get('number')) 267 for amplitude in diagram.get('amplitudes'): 268 res.append(self.get_amplitude_call(amplitude)) 269 270 return res
271
272 - def get_wavefunction_call(self, wavefunction):
273 """Return the function for writing the wavefunction 274 corresponding to the key""" 275 276 try: 277 call = self["wavefunctions"][wavefunction.get_call_key()](\ 278 wavefunction) 279 return call 280 except KeyError: 281 return ""
282
283 - def get_amplitude_call(self, amplitude):
284 """Return the function for writing the amplitude 285 corresponding to the key""" 286 287 try: 288 call = self["amplitudes"][amplitude.get_call_key()] 289 except KeyError, error: 290 return "" 291 else: 292 return call(amplitude)
293
294 - def add_wavefunction(self, key, function):
295 """Set the function for writing the wavefunction 296 corresponding to the key""" 297 298 assert isinstance(key, tuple), \ 299 "%s is not a valid tuple for wavefunction key" % key 300 301 assert callable(function), \ 302 "%s is not a valid function for wavefunction string" % function 303 304 self.get('wavefunctions')[key] = function 305 return True
306
307 - def add_amplitude(self, key, function):
308 """Set the function for writing the amplitude 309 corresponding to the key""" 310 311 assert isinstance(key, tuple), \ 312 "%s is not a valid tuple for amplitude key" % str(key) 313 314 assert callable(function), \ 315 "%s is not a valid function for amplitude string" % str(function) 316 317 318 self.get('amplitudes')[key] = function 319 return True
320
321 - def get_model_name(self):
322 """Return the model name""" 323 return self['model'].get('name')
324 325 # Customized constructor 326
327 - def __init__(self, argument={}):
328 """Allow generating a HelasCallWriter from a Model 329 """ 330 331 if isinstance(argument, base_objects.Model): 332 super(HelasCallWriter, self).__init__() 333 self.set('model', argument) 334 else: 335 super(HelasCallWriter, self).__init__(argument)
336
337 #=============================================================================== 338 # FortranHelasCallWriter 339 #=============================================================================== 340 -class FortranHelasCallWriter(HelasCallWriter):
341 """The class for writing Helas calls in Fortran, starting from 342 HelasWavefunctions and HelasAmplitudes. 343 344 Includes the function generate_helas_call, which automatically 345 generates the Fortran Helas call based on the Lorentz structure of 346 the interaction.""" 347 348 # Dictionaries used for automatic generation of Helas calls 349 # Dictionaries from spin states to letters in Helas call 350 self_dict = {1: 'H', 2: 'F', -2: 'F', 3: 'J', 5: 'U'} 351 # Dictionaries used for sorting the letters in the Helas call 352 sort_wf = {'O': 0, 'I': 1, 'S': 2, 'T': 3, 'V': 4} 353 sort_amp = {'S': 0, 'V': 2, 'T': 1, 'O': 3, 'I': 4} 354
355 - def default_setup(self):
356 """Set up special Helas calls (wavefunctions and amplitudes) 357 that can not be done automatically by generate_helas_call""" 358 359 super(FortranHelasCallWriter, self).default_setup() 360 361 # Add special fortran Helas calls, which are not automatically 362 # generated 363 364 # Gluon 4-vertex division tensor calls ggT for the FR sm and mssm 365 366 key = ((3, 3, 5, 3), ('A',)) 367 call = lambda wf: \ 368 "CALL UVVAXX(W(1,%d),W(1,%d),%s,zero,zero,zero,W(1,%d))" % \ 369 (FortranHelasCallWriter.sorted_mothers(wf)[0].get('me_id'), 370 FortranHelasCallWriter.sorted_mothers(wf)[1].get('me_id'), 371 wf.get('coupling')[0], 372 wf.get('me_id')) 373 self.add_wavefunction(key, call) 374 375 key = ((3, 5, 3, 1), ('A',)) 376 call = lambda wf: \ 377 "CALL JVTAXX(W(1,%d),W(1,%d),%s,zero,zero,W(1,%d))" % \ 378 (FortranHelasCallWriter.sorted_mothers(wf)[0].get('me_id'), 379 FortranHelasCallWriter.sorted_mothers(wf)[1].get('me_id'), 380 wf.get('coupling')[0], 381 wf.get('me_id')) 382 self.add_wavefunction(key, call) 383 384 key = ((3, 3, 5), ('A',)) 385 call = lambda amp: \ 386 "CALL VVTAXX(W(1,%d),W(1,%d),W(1,%d),%s,zero,AMP(%d))" % \ 387 (FortranHelasCallWriter.sorted_mothers(amp)[0].get('me_id'), 388 FortranHelasCallWriter.sorted_mothers(amp)[1].get('me_id'), 389 FortranHelasCallWriter.sorted_mothers(amp)[2].get('me_id'), 390 amp.get('coupling')[0], 391 amp.get('number')) 392 self.add_amplitude(key, call) 393 394 # SM gluon 4-vertex components 395 396 key = ((3, 3, 3, 3, 1), ('gggg3',)) 397 call = lambda wf: \ 398 "CALL JGGGXX(W(1,%d),W(1,%d),W(1,%d),%s,W(1,%d))" % \ 399 (FortranHelasCallWriter.sorted_mothers(wf)[1].get('me_id'), 400 FortranHelasCallWriter.sorted_mothers(wf)[0].get('me_id'), 401 FortranHelasCallWriter.sorted_mothers(wf)[2].get('me_id'), 402 wf.get('coupling')[0], 403 wf.get('me_id')) 404 self.add_wavefunction(key, call) 405 key = ((3, 3, 3, 3), ('gggg1',)) 406 call = lambda amp: \ 407 "CALL GGGGXX(W(1,%d),W(1,%d),W(1,%d),W(1,%d),%s,AMP(%d))" % \ 408 (FortranHelasCallWriter.sorted_mothers(amp)[0].get('me_id'), 409 FortranHelasCallWriter.sorted_mothers(amp)[1].get('me_id'), 410 FortranHelasCallWriter.sorted_mothers(amp)[2].get('me_id'), 411 FortranHelasCallWriter.sorted_mothers(amp)[3].get('me_id'), 412 amp.get('coupling')[0], 413 amp.get('number')) 414 self.add_amplitude(key, call) 415 key = ((3, 3, 3, 3, 1), ('gggg2',)) 416 call = lambda wf: \ 417 "CALL JGGGXX(W(1,%d),W(1,%d),W(1,%d),%s,W(1,%d))" % \ 418 (FortranHelasCallWriter.sorted_mothers(wf)[0].get('me_id'), 419 FortranHelasCallWriter.sorted_mothers(wf)[2].get('me_id'), 420 FortranHelasCallWriter.sorted_mothers(wf)[1].get('me_id'), 421 wf.get('coupling')[0], 422 wf.get('me_id')) 423 self.add_wavefunction(key, call) 424 key = ((3, 3, 3, 3), ('gggg2',)) 425 call = lambda amp: \ 426 "CALL GGGGXX(W(1,%d),W(1,%d),W(1,%d),W(1,%d),%s,AMP(%d))" % \ 427 (FortranHelasCallWriter.sorted_mothers(amp)[2].get('me_id'), 428 FortranHelasCallWriter.sorted_mothers(amp)[0].get('me_id'), 429 FortranHelasCallWriter.sorted_mothers(amp)[1].get('me_id'), 430 FortranHelasCallWriter.sorted_mothers(amp)[3].get('me_id'), 431 amp.get('coupling')[0], 432 amp.get('number')) 433 self.add_amplitude(key, call) 434 key = ((3, 3, 3, 3, 1), ('gggg1',)) 435 call = lambda wf: \ 436 "CALL JGGGXX(W(1,%d),W(1,%d),W(1,%d),%s,W(1,%d))" % \ 437 (FortranHelasCallWriter.sorted_mothers(wf)[2].get('me_id'), 438 FortranHelasCallWriter.sorted_mothers(wf)[1].get('me_id'), 439 FortranHelasCallWriter.sorted_mothers(wf)[0].get('me_id'), 440 wf.get('coupling')[0], 441 wf.get('me_id')) 442 self.add_wavefunction(key, call) 443 key = ((3, 3, 3, 3), ('gggg3',)) 444 call = lambda amp: \ 445 "CALL GGGGXX(W(1,%d),W(1,%d),W(1,%d),W(1,%d),%s,AMP(%d))" % \ 446 (FortranHelasCallWriter.sorted_mothers(amp)[1].get('me_id'), 447 FortranHelasCallWriter.sorted_mothers(amp)[2].get('me_id'), 448 FortranHelasCallWriter.sorted_mothers(amp)[0].get('me_id'), 449 FortranHelasCallWriter.sorted_mothers(amp)[3].get('me_id'), 450 amp.get('coupling')[0], 451 amp.get('number')) 452 self.add_amplitude(key, call) 453 454 # HEFT VVVS calls 455 456 key = ((1, 3, 3, 3, 3), ('',)) 457 call = lambda wf: \ 458 "CALL JVVSXX(W(1,%d),W(1,%d),W(1,%d),DUM1,%s,%s,%s,W(1,%d))" % \ 459 (wf.get('mothers')[0].get('me_id'), 460 wf.get('mothers')[1].get('me_id'), 461 wf.get('mothers')[2].get('me_id'), 462 wf.get('coupling')[0], 463 wf.get('mass'), 464 wf.get('width'), 465 wf.get('me_id')) 466 self.add_wavefunction(key, call) 467 468 key = ((3, 3, 3, 1, 4), ('',)) 469 call = lambda wf: \ 470 "CALL HVVVXX(W(1,%d),W(1,%d),W(1,%d),DUM1,%s,%s,%s,W(1,%d))" % \ 471 (wf.get('mothers')[0].get('me_id'), 472 wf.get('mothers')[1].get('me_id'), 473 wf.get('mothers')[2].get('me_id'), 474 wf.get('coupling')[0], 475 wf.get('mass'), 476 wf.get('width'), 477 wf.get('me_id')) 478 self.add_wavefunction(key, call) 479 480 key = ((1, 3, 3, 3), ('',)) 481 call = lambda amp: \ 482 "CALL VVVSXX(W(1,%d),W(1,%d),W(1,%d),W(1,%d),DUM1,%s,AMP(%d))" % \ 483 (amp.get('mothers')[0].get('me_id'), 484 amp.get('mothers')[1].get('me_id'), 485 amp.get('mothers')[2].get('me_id'), 486 amp.get('mothers')[3].get('me_id'), 487 amp.get('coupling')[0], 488 amp.get('number')) 489 self.add_amplitude(key, call) 490 491 # HEFT VVVS calls 492 493 key = ((1, 3, 3, 3, 1), ('',)) 494 call = lambda wf: \ 495 "CALL JVVSXX(W(1,%d),W(1,%d),W(1,%d),DUM1,%s,%s,%s,W(1,%d))" % \ 496 (wf.get('mothers')[0].get('me_id'), 497 wf.get('mothers')[1].get('me_id'), 498 wf.get('mothers')[2].get('me_id'), 499 wf.get('coupling')[0], 500 wf.get('mass'), 501 wf.get('width'), 502 wf.get('me_id')) 503 self.add_wavefunction(key, call) 504 505 key = ((3, 3, 3, 1, 4), ('',)) 506 call = lambda wf: \ 507 "CALL HVVVXX(W(1,%d),W(1,%d),W(1,%d),DUM1,%s,%s,%s,W(1,%d))" % \ 508 (wf.get('mothers')[0].get('me_id'), 509 wf.get('mothers')[1].get('me_id'), 510 wf.get('mothers')[2].get('me_id'), 511 wf.get('coupling')[0], 512 wf.get('mass'), 513 wf.get('width'), 514 wf.get('me_id')) 515 self.add_wavefunction(key, call) 516 517 key = ((1, 3, 3, 3), ('',)) 518 call = lambda amp: \ 519 "CALL VVVSXX(W(1,%d),W(1,%d),W(1,%d),W(1,%d),DUM1,%s,AMP(%d))" % \ 520 (amp.get('mothers')[0].get('me_id'), 521 amp.get('mothers')[1].get('me_id'), 522 amp.get('mothers')[2].get('me_id'), 523 amp.get('mothers')[3].get('me_id'), 524 amp.get('coupling')[0], 525 amp.get('number')) 526 self.add_amplitude(key, call) 527 528 # Spin2 Helas Routine 529 key = ((-2, 2, 5), ('',)) 530 call = lambda amp: \ 531 "CALL IOTXXX(W(1,%d),W(1,%d),W(1,%d),%s,%s,AMP(%d))" % \ 532 (amp.get('mothers')[0].get('me_id'), 533 amp.get('mothers')[1].get('me_id'), 534 amp.get('mothers')[2].get('me_id'), 535 amp.get('coupling')[0], 536 amp.get('mothers')[0].get('mass'), 537 amp.get('number')) 538 self.add_amplitude(key, call) 539 540 key = ((-2, 2, 5, 3), ('',)) 541 call = lambda wf: \ 542 "CALL UIOXXX(W(1,%d),W(1,%d),%s,%s,%s,%s,W(1,%d))" % \ 543 (wf.get('mothers')[0].get('me_id'), 544 wf.get('mothers')[1].get('me_id'), 545 wf.get('coupling')[0], 546 wf.get('mothers')[0].get('mass'), 547 wf.get('mass'), 548 wf.get('width'), 549 wf.get('me_id')) 550 self.add_wavefunction(key, call) 551 552 key = ((3,3,3,5),('',)) 553 call = lambda amp: \ 554 "CALL VVVTXX(W(1,%d),W(1,%d),W(1,%d),W(1,%d),1d0,%s,AMP(%d))" % \ 555 (amp.get('mothers')[0].get('me_id'), 556 amp.get('mothers')[1].get('me_id'), 557 amp.get('mothers')[2].get('me_id'), 558 amp.get('mothers')[3].get('me_id'), 559 amp.get('coupling')[0], 560 amp.get('number')) 561 self.add_amplitude(key, call) 562 563 key = ((3,3,5),('',)) 564 call = lambda amp: \ 565 "CALL VVTXXX(W(1,%d),W(1,%d),W(1,%d),%s,%s,AMP(%d))" % \ 566 (amp.get('mothers')[0].get('me_id'), 567 amp.get('mothers')[1].get('me_id'), 568 amp.get('mothers')[2].get('me_id'), 569 amp.get('coupling')[0], 570 amp.get('mothers')[0].get('mass'), 571 amp.get('number')) 572 self.add_amplitude(key, call)
573 574
575 - def get_wavefunction_call(self, wavefunction):
576 """Return the function for writing the wavefunction 577 corresponding to the key. If the function doesn't exist, 578 generate_helas_call is called to automatically create the 579 function.""" 580 581 if wavefunction.get('spin') == 1 and \ 582 wavefunction.get('interaction_id') != 0: 583 # Special feature: For HVS vertices with the two 584 # scalars different, we need extra minus sign in front 585 # of coupling for one of the two scalars since the HVS 586 # is asymmetric in the two scalars 587 wavefunction.set_scalar_coupling_sign(self['model']) 588 589 val = super(FortranHelasCallWriter, self).get_wavefunction_call(wavefunction) 590 591 if val: 592 return val 593 594 # If function not already existing, try to generate it. 595 596 if len(wavefunction.get('mothers')) > 3: 597 raise self.PhysicsObjectError, \ 598 """Automatic generation of Fortran wavefunctions not 599 implemented for > 3 mothers""" 600 601 self.generate_helas_call(wavefunction) 602 return super(FortranHelasCallWriter, self).get_wavefunction_call(\ 603 wavefunction)
604
605 - def get_amplitude_call(self, amplitude):
606 """Return the function for writing the amplitude corresponding 607 to the key. If the function doesn't exist, generate_helas_call 608 is called to automatically create the function.""" 609 610 val = super(FortranHelasCallWriter, self).get_amplitude_call(amplitude) 611 612 if val: 613 return val 614 615 # If function not already existing, try to generate it. 616 617 if len(amplitude.get('mothers')) > 4: 618 raise self.PhysicsObjectError, \ 619 """Automatic generation of Fortran amplitudes not 620 implemented for > 4 mothers""" 621 622 self.generate_helas_call(amplitude) 623 return super(FortranHelasCallWriter, self).get_amplitude_call(amplitude)
624
625 - def generate_helas_call(self, argument):
626 """Routine for automatic generation of Fortran Helas calls 627 according to just the spin structure of the interaction. 628 629 First the call string is generated, using a dictionary to go 630 from the spin state of the calling wavefunction and its 631 mothers, or the mothers of the amplitude, to letters. 632 633 Then the call function is generated, as a lambda which fills 634 the call string with the information of the calling 635 wavefunction or amplitude. The call has different structure, 636 depending on the spin of the wavefunction and the number of 637 mothers (multiplicity of the vertex). The mother 638 wavefunctions, when entering the call, must be sorted in the 639 correct way - this is done by the sorted_mothers routine. 640 641 Finally the call function is stored in the relevant 642 dictionary, in order to be able to reuse the function the next 643 time a wavefunction with the same Lorentz structure is needed. 644 """ 645 646 if not isinstance(argument, helas_objects.HelasWavefunction) and \ 647 not isinstance(argument, helas_objects.HelasAmplitude): 648 raise self.PhysicsObjectError, \ 649 "get_helas_call must be called with wavefunction or amplitude" 650 651 call = "CALL " 652 653 call_function = None 654 655 if isinstance(argument, helas_objects.HelasAmplitude) and \ 656 argument.get('interaction_id') == 0: 657 call = "#" 658 call_function = lambda amp: call 659 660 self.add_amplitude(argument.get_call_key(), call_function) 661 return 662 663 if isinstance(argument, helas_objects.HelasWavefunction) and \ 664 not argument.get('mothers'): 665 # String is just IXXXXX, OXXXXX, VXXXXX or SXXXXX 666 call = call + HelasCallWriter.mother_dict[\ 667 argument.get_spin_state_number()] 668 # Fill out with X up to 6 positions 669 call = call + 'X' * (11 - len(call)) 670 call = call + "(P(0,%d)," 671 if argument.get('spin') != 1: 672 # For non-scalars, need mass and helicity 673 call = call + "%s,NHEL(%d)," 674 call = call + "%+d*IC(%d),W(1,%d))" 675 if argument.get('spin') == 1: 676 call_function = lambda wf: call % \ 677 (wf.get('number_external'), 678 # For boson, need initial/final here 679 (-1) ** (wf.get('state') == 'initial'), 680 wf.get('number_external'), 681 wf.get('me_id')) 682 elif argument.is_boson(): 683 call_function = lambda wf: call % \ 684 (wf.get('number_external'), 685 wf.get('mass'), 686 wf.get('number_external'), 687 # For boson, need initial/final here 688 (-1) ** (wf.get('state') == 'initial'), 689 wf.get('number_external'), 690 wf.get('me_id')) 691 else: 692 call_function = lambda wf: call % \ 693 (wf.get('number_external'), 694 wf.get('mass'), 695 wf.get('number_external'), 696 # For fermions, need particle/antiparticle 697 - (-1) ** wf.get_with_flow('is_part'), 698 wf.get('number_external'), 699 wf.get('me_id')) 700 else: 701 # String is FOVXXX, FIVXXX, JIOXXX etc. 702 if isinstance(argument, helas_objects.HelasWavefunction): 703 call = call + \ 704 FortranHelasCallWriter.self_dict[\ 705 argument.get_spin_state_number()] 706 707 mother_letters = FortranHelasCallWriter.sorted_letters(argument) 708 709 # If Lorentz structure is given, by default add this 710 # to call name 711 lor_name = argument.get('lorentz')[0] 712 713 # Take care of special case: WWWW or WWVV calls 714 if len(lor_name) > 3 and lor_name[:2] == "WW": 715 if lor_name[:4] == "WWWW": 716 mother_letters = "WWWW"[:len(mother_letters)] 717 if lor_name[:4] == "WWVV": 718 mother_letters = "W3W3"[:len(mother_letters)] 719 lor_name = lor_name[4:] 720 721 call = call + mother_letters 722 call = call + lor_name 723 724 # Check if we need to append a charge conjugation flag 725 if argument.needs_hermitian_conjugate(): 726 call = call + 'C' 727 728 assert len(call) < 12, "Call to Helas routine %s should be maximum 6 chars" \ 729 % call[5:] 730 731 # Fill out with X up to 6 positions 732 call = call + 'X' * (11 - len(call)) + '(' 733 # Wavefunctions 734 call = call + "W(1,%d)," * len(argument.get('mothers')) 735 # Couplings 736 call = call + "%s," 737 738 739 if isinstance(argument, helas_objects.HelasWavefunction): 740 # Extra dummy coupling for 4-vector vertices 741 if argument.get('lorentz') == ['WWVV']: 742 # SM W3W3 vertex 743 call = call + "1D0," 744 elif argument.get('lorentz') == ['WWWW']: 745 # SM WWWW vertex 746 call = call + "0D0," 747 elif argument.get('spin') == 3 and \ 748 [wf.get('spin') for wf in argument.get('mothers')] == \ 749 [3, 3, 3]: 750 # All other 4-vector vertices (FR) - note that gggg 751 # has already been defined 752 call = call + "DUM0," 753 # Mass and width 754 call = call + "%s,%s," 755 # New wavefunction 756 call = call + "W(1,%d))" 757 else: 758 # Extra dummy coupling for 4-particle vertices 759 # Need to replace later with the correct type 760 if argument.get('lorentz') == ['WWVV']: 761 # SM W3W3 vertex 762 call = call + "1D0," 763 elif argument.get('lorentz') == ['WWWW']: 764 # SM WWWW vertex 765 call = call + "0D0," 766 elif [wf.get('spin') for wf in argument.get('mothers')] == \ 767 [3, 3, 3, 3]: 768 # Other 4-vector vertices (FR) - note that gggg 769 # has already been defined 770 call = call + "DUM0," 771 # Amplitude 772 call = call + "AMP(%d))" 773 774 if isinstance(argument, helas_objects.HelasWavefunction): 775 # Create call for wavefunction 776 if len(argument.get('mothers')) == 2: 777 call_function = lambda wf: call % \ 778 (FortranHelasCallWriter.sorted_mothers(wf)[0].\ 779 get('me_id'), 780 FortranHelasCallWriter.sorted_mothers(wf)[1].\ 781 get('me_id'), 782 ','.join(wf.get_with_flow('coupling')), 783 wf.get('mass'), 784 wf.get('width'), 785 wf.get('me_id')) 786 else: 787 call_function = lambda wf: call % \ 788 (FortranHelasCallWriter.sorted_mothers(wf)[0].\ 789 get('me_id'), 790 FortranHelasCallWriter.sorted_mothers(wf)[1].\ 791 get('me_id'), 792 FortranHelasCallWriter.sorted_mothers(wf)[2].\ 793 get('me_id'), 794 ','.join(wf.get_with_flow('coupling')), 795 wf.get('mass'), 796 wf.get('width'), 797 wf.get('me_id')) 798 else: 799 # Create call for amplitude 800 if len(argument.get('mothers')) == 3: 801 call_function = lambda amp: call % \ 802 (FortranHelasCallWriter.sorted_mothers(amp)[0].\ 803 get('me_id'), 804 FortranHelasCallWriter.sorted_mothers(amp)[1].\ 805 get('me_id'), 806 FortranHelasCallWriter.sorted_mothers(amp)[2].\ 807 get('me_id'), 808 809 ','.join(amp.get('coupling')), 810 amp.get('number')) 811 else: 812 call_function = lambda amp: call % \ 813 (FortranHelasCallWriter.sorted_mothers(amp)[0].\ 814 get('me_id'), 815 FortranHelasCallWriter.sorted_mothers(amp)[1].\ 816 get('me_id'), 817 FortranHelasCallWriter.sorted_mothers(amp)[2].\ 818 get('me_id'), 819 FortranHelasCallWriter.sorted_mothers(amp)[3].\ 820 get('me_id'), 821 ','.join(amp.get('coupling')), 822 amp.get('number')) 823 824 # Add the constructed function to wavefunction or amplitude dictionary 825 if isinstance(argument, helas_objects.HelasWavefunction): 826 self.add_wavefunction(argument.get_call_key(), call_function) 827 else: 828 self.add_amplitude(argument.get_call_key(), call_function)
829 830 # Static helper functions 831 832 @staticmethod
833 - def sorted_letters(arg):
834 """Gives a list of letters sorted according to 835 the order of letters in the Fortran Helas calls""" 836 837 if isinstance(arg, helas_objects.HelasWavefunction): 838 return "".join(sorted([HelasCallWriter.mother_dict[\ 839 wf.get_spin_state_number()] for wf in arg.get('mothers')], 840 lambda l1, l2: \ 841 FortranHelasCallWriter.sort_wf[l2] - \ 842 FortranHelasCallWriter.sort_wf[l1])) 843 844 if isinstance(arg, helas_objects.HelasAmplitude): 845 return "".join(sorted([HelasCallWriter.mother_dict[\ 846 wf.get_spin_state_number()] for wf in arg.get('mothers')], 847 lambda l1, l2: \ 848 FortranHelasCallWriter.sort_amp[l2] - \ 849 FortranHelasCallWriter.sort_amp[l1]))
850 851 @staticmethod
852 - def sorted_mothers(arg):
853 """Gives a list of mother wavefunctions sorted according to 854 1. The order of the particles in the interaction 855 2. Cyclic reordering of particles in same spin group 856 3. Fermions ordered IOIOIO... according to the pairs in 857 the interaction.""" 858 859 assert isinstance(arg, (helas_objects.HelasWavefunction, helas_objects.HelasAmplitude)), \ 860 "%s is not a valid HelasWavefunction or HelasAmplitude" % repr(arg) 861 862 if not arg.get('interaction_id'): 863 return arg.get('mothers') 864 my_pdg_code = 0 865 my_spin = 0 866 if isinstance(arg, helas_objects.HelasWavefunction): 867 my_pdg_code = arg.get_anti_pdg_code() 868 my_spin = arg.get_spin_state_number() 869 870 sorted_mothers, my_index = arg.get('mothers').sort_by_pdg_codes(\ 871 arg.get('pdg_codes'), my_pdg_code) 872 873 # If fermion, partner is the corresponding fermion flow partner 874 partner = None 875 if isinstance(arg, helas_objects.HelasWavefunction) and arg.is_fermion(): 876 # Fermion case, just pick out the fermion flow partner 877 if my_index % 2 == 0: 878 # partner is after arg 879 partner_index = my_index 880 else: 881 # partner is before arg 882 partner_index = my_index - 1 883 partner = sorted_mothers.pop(partner_index) 884 # If partner is incoming, move to before arg 885 if partner.get_spin_state_number() > 0: 886 my_index = partner_index 887 else: 888 my_index = partner_index + 1 889 890 # Reorder fermions pairwise according to incoming/outgoing 891 for i in range(0, len(sorted_mothers), 2): 892 if sorted_mothers[i].is_fermion(): 893 # This is a fermion, order between this fermion and its brother 894 if sorted_mothers[i].get_spin_state_number() > 0 and \ 895 sorted_mothers[i + 1].get_spin_state_number() < 0: 896 # Switch places between outgoing and incoming 897 sorted_mothers = sorted_mothers[:i] + \ 898 [sorted_mothers[i+1], sorted_mothers[i]] + \ 899 sorted_mothers[i+2:] 900 elif sorted_mothers[i].get_spin_state_number() < 0 and \ 901 sorted_mothers[i + 1].get_spin_state_number() > 0: 902 # This is the right order 903 pass 904 else: 905 # No more fermions in sorted_mothers 906 break 907 908 # Put back partner into sorted_mothers 909 if partner: 910 sorted_mothers.insert(partner_index, partner) 911 912 same_spin_mothers = [] 913 if isinstance(arg, helas_objects.HelasWavefunction): 914 # Pick out mothers with same spin, for cyclic reordering 915 same_spin_index = -1 916 i=0 917 while i < len(sorted_mothers): 918 if abs(sorted_mothers[i].get_spin_state_number()) == \ 919 abs(my_spin): 920 if same_spin_index < 0: 921 # Remember starting index for same spin states 922 same_spin_index = i 923 same_spin_mothers.append(sorted_mothers.pop(i)) 924 else: 925 i += 1 926 927 # Make cyclic reordering of mothers with same spin as this wf 928 if same_spin_mothers: 929 same_spin_mothers = same_spin_mothers[my_index - same_spin_index:] \ 930 + same_spin_mothers[:my_index - same_spin_index] 931 932 # Insert same_spin_mothers in sorted_mothers 933 sorted_mothers = sorted_mothers[:same_spin_index] + \ 934 same_spin_mothers + sorted_mothers[same_spin_index:] 935 936 # Next sort according to spin_state_number 937 return helas_objects.HelasWavefunctionList(sorted_mothers)
938
939 940 #=============================================================================== 941 # UFOHelasCallWriter 942 #=============================================================================== 943 -class UFOHelasCallWriter(HelasCallWriter):
944 """The class for writing Helas calls in Fortran, starting from 945 HelasWavefunctions and HelasAmplitudes. 946 947 Includes the function generate_helas_call, which automatically 948 generates the Fortran Helas call based on the Lorentz structure of 949 the interaction.""" 950 951
952 - def get_wavefunction_call(self, wavefunction, **opt):
953 """Return the function for writing the wavefunction 954 corresponding to the key. If the function doesn't exist, 955 generate_helas_call is called to automatically create the 956 function. -UFO ROUTINE-""" 957 958 # Special feature: For octet Majorana fermions, need an extra 959 # minus sign in the FVI (and FSI?) wavefunction in UFO 960 # models. For MG4 models, this is taken care of by calling 961 # different routines (in import_v4.py) 962 wavefunction.set_octet_majorana_coupling_sign() 963 964 val = super(UFOHelasCallWriter, self).get_wavefunction_call(wavefunction) 965 if val: 966 return val 967 968 # If function not already existing, try to generate it. 969 self.generate_helas_call(wavefunction, **opt) 970 return super(UFOHelasCallWriter, self).get_wavefunction_call(\ 971 wavefunction)
972
973 - def get_amplitude_call(self, amplitude):
974 """Return the function for writing the amplitude corresponding 975 to the key. If the function doesn't exist, generate_helas_call 976 is called to automatically create the function.""" 977 978 val = super(UFOHelasCallWriter, self).get_amplitude_call(amplitude) 979 if val: 980 return val 981 982 # If function not already existing, try to generate it. 983 self.generate_helas_call(amplitude) 984 return super(UFOHelasCallWriter, self).get_amplitude_call(amplitude)
985 986 # Helper function
987 - def write_factor(self, factor):
988 """Create a suitable string for the factor of the form 989 (fraction, is_imaginary?).""" 990 imag_dict = {True: "IMAG1", False: "ONE"} 991 return str(factor[0]*factor[1]) + "*" + imag_dict[factor[2]]
992
993 #=============================================================================== 994 # FortranUFOHelasCallWriter 995 #=============================================================================== 996 -class FortranUFOHelasCallWriter(UFOHelasCallWriter):
997 """The class for writing Helas calls in Fortran, starting from 998 HelasWavefunctions and HelasAmplitudes. 999 1000 Includes the function generate_helas_call, which automatically 1001 generates the Fortran Helas call based on the Lorentz structure of 1002 the interaction.""" 1003 1004 mp_prefix = check_param_card.ParamCard.mp_prefix 1005
1006 - def __init__(self, argument={}, hel_sum = False):
1007 """Allow generating a HelasCallWriter from a Model.The hel_sum argument 1008 specifies if amplitude and wavefunctions must be stored specifying the 1009 helicity, i.e. W(1,i) vs W(1,i,H). 1010 """ 1011 self.hel_sum = hel_sum 1012 super(FortranUFOHelasCallWriter, self).__init__(argument)
1013
1014 - def format_helas_object(self, prefix, number):
1015 """ Returns the string for accessing the wavefunction with number in 1016 argument. Typical output is {prefix}(1,{number}) """ 1017 1018 if self.hel_sum: 1019 return '%s%s,H)'%(prefix, number) 1020 else: 1021 return '%s%s)'%(prefix, number)
1022
1023 - def get_amplitude_call(self, amplitude,**opts):
1024 """ We overwrite this function here because we must call 1025 set_octet_majorana_coupling_sign for all wavefunction taking part in 1026 this loopHelasAmplitude. This is not necessary in the optimized mode""" 1027 1028 # Special feature: For octet Majorana fermions, need an extra 1029 # minus sign in the FVI (and FSI?) wavefunction in UFO 1030 # models. 1031 if isinstance(amplitude,loop_helas_objects.LoopHelasAmplitude): 1032 for lwf in amplitude.get('wavefunctions'): 1033 lwf.set_octet_majorana_coupling_sign() 1034 amplitude.set('coupling',amplitude.get_couplings()) 1035 1036 return super(FortranUFOHelasCallWriter, self).get_amplitude_call( 1037 amplitude,**opts)
1038 1039 1040
1041 - def generate_loop_amplitude_call(self, loopamp):
1042 """ Routine for automatic generation of a call to CutTools for loop 1043 amplitudes.""" 1044 1045 call = "LOOP%(numLoopLines)s" 1046 if (len(loopamp.get('pairing')) != len(loopamp.get('mothers'))): 1047 call += "%(numMotherWfs)s%(numCouplings)s(%(numeratorNumber)d," 1048 for i in range(len(loopamp.get('pairing'))): 1049 call = call + "%(Pairing{0})d,".format(i) 1050 else: 1051 call += "%(numCouplings)s(%(numeratorNumber)d," 1052 for i in range(len(loopamp.get('mothers'))): 1053 call = call + "%(MotherID{0})d,".format(i+1) 1054 for i in range(len(loopamp.get('wavefunctions'))-2): 1055 call = call + \ 1056 "DCMPLX(%(LoopMass{0})s),CMPLX({1}%(LoopMass{0})s,KIND=16),"\ 1057 .format(i+1,self.mp_prefix) 1058 for i in range(len(loopamp.get('coupling'))): 1059 call = call + \ 1060 "%(LoopCoupling{0})s,%(MPLoopCoupling{0})s,".format(i+1) 1061 call = call + "%(LoopRank)d," 1062 call = call + "%(LoopSymmetryFactor)d,%(LoopMultiplier)d," 1063 call = call + "%(ampNumber)d,AMPL(1,%(ampNumber)d),S(%(ampNumber)d))" 1064 1065 def create_loop_amp(amplitude): 1066 helas_dict = amplitude.get_helas_call_dict() 1067 # Make sure the potential minus sign on coupling appears at the 1068 # right place when specifying the mp_coupling. It must be 1069 # -MP__GC10 and not MP__-GC10 1070 for i in range(len(loopamp.get('coupling'))): 1071 coupl = helas_dict['LoopCoupling%i'%(i+1)] 1072 helas_dict['MPLoopCoupling%i'%(i+1)]= \ 1073 '-%s%s'%(self.mp_prefix,coupl[1:]) if coupl.startswith('-') \ 1074 else '%s%s'%(self.mp_prefix,coupl) 1075 # We add here the placeholde for the proc_prefix 1076 return 'CALL %(proc_prefix)s'+call%helas_dict
1077 1078 self.add_amplitude(loopamp.get_call_key(), create_loop_amp) 1079 return
1080
1081 - def generate_helas_call(self, argument, startingExternalWFNumber=0):
1082 """Routine for automatic generation of Fortran Helas calls 1083 according to just the spin structure of the interaction. 1084 """ 1085 1086 if not isinstance(argument, helas_objects.HelasWavefunction) and \ 1087 not isinstance(argument, helas_objects.HelasAmplitude): 1088 raise self.PhysicsObjectError, \ 1089 "generate_helas_call must be called with wavefunction or amplitude" 1090 1091 call = "CALL " 1092 1093 call_function = None 1094 1095 if isinstance(argument, helas_objects.HelasAmplitude) and \ 1096 not isinstance(argument, loop_helas_objects.LoopHelasAmplitude) and \ 1097 argument.get('interaction_id') == 0: 1098 call = "#" 1099 call_function = lambda amp: call 1100 self.add_amplitude(argument.get_call_key(), call_function) 1101 return 1102 1103 if isinstance(argument, helas_objects.HelasWavefunction) and \ 1104 not argument.get('mothers'): 1105 self.generate_external_wavefunction(argument) 1106 return 1107 1108 if isinstance(argument,loop_helas_objects.LoopHelasAmplitude): 1109 self.generate_loop_amplitude_call(argument) 1110 return 1111 1112 self.generate_all_other_helas_objects(argument)
1113
1114 - def generate_external_wavefunction(self,argument):
1115 """ Generate an external wavefunction """ 1116 1117 call="CALL " 1118 call_function = None 1119 if argument.get('is_loop'): 1120 call=call+"LCUT_%(conjugate)s%(lcutspinletter)s(Q(0),I,WL(1,%(number)d))" 1121 else: 1122 # String is just IXXXXX, OXXXXX, VXXXXX or SXXXXX 1123 call = call + HelasCallWriter.mother_dict[\ 1124 argument.get_spin_state_number()] 1125 # Fill out with X up to 6 positions 1126 call = call + 'X' * (11 - len(call)) 1127 call = call + "(P(0,%(number_external)d)," 1128 if argument.get('spin') != 1: 1129 # For non-scalars, need mass and helicity 1130 call = call + "%(mass)s,NHEL(%(number_external)d)," 1131 call = call + "%(state_id)+d*IC(%(number_external)d),{0})".format(\ 1132 self.format_helas_object('W(1,','%(me_id)d')) 1133 1134 call_function = lambda wf: call % wf.get_external_helas_call_dict() 1135 self.add_wavefunction(argument.get_call_key(), call_function)
1136
1137 - def generate_all_other_helas_objects(self,argument):
1138 """ Generate all the helas objects for which no special handlers was 1139 placed in generate_helas_call """ 1140 1141 1142 if isinstance(argument, helas_objects.HelasWavefunction): 1143 outgoing = argument.find_outgoing_number() 1144 else: 1145 outgoing = 0 1146 1147 1148 # Check if we need to append a charge conjugation flag 1149 l = [str(l) for l in argument.get('lorentz')] 1150 flag = [] 1151 if argument.needs_hermitian_conjugate(): 1152 flag = ['C%d' % i for i in \ 1153 argument.get_conjugate_index()] 1154 if (isinstance(argument, helas_objects.HelasWavefunction) and \ 1155 argument.get('is_loop') or \ 1156 (isinstance(argument, helas_objects.HelasAmplitude) and \ 1157 argument.get('type')=='loop')): 1158 flag.insert(0,"L") 1159 1160 # Creating line formatting: 1161 call = 'CALL %(routine_name)s(%(wf)s%(coup)s%(mass)s%(out)s)' 1162 1163 arg = {'routine_name': aloha_writers.combine_name(\ 1164 '%s' % l[0], l[1:], outgoing, flag, True), 1165 'coup': ("%%(coup%d)s," * len(argument.get('coupling'))) % \ 1166 tuple(range(len(argument.get('coupling')))) 1167 } 1168 1169 # select how to write a single wf 1170 if (isinstance(argument,helas_objects.HelasWavefunction) \ 1171 and argument.get('is_loop')) or \ 1172 ((isinstance(argument,helas_objects.HelasAmplitude) \ 1173 and argument['type']=='loop')): 1174 base_wf = "W%(WF{0})s," 1175 else: 1176 base_wf = self.format_helas_object('W(1,','%({0})d')+',' 1177 1178 # compute the full list of wf 1179 wf = '' 1180 for i in range(len(argument.get('mothers'))): 1181 wf += base_wf.format(i) 1182 arg['wf'] = wf 1183 1184 1185 # Treat other argument 1186 # First WaveFunction 1187 if isinstance(argument, helas_objects.HelasWavefunction): 1188 if argument['is_loop']: 1189 arg['out'] = 'WL(1,%(out)d)' 1190 if aloha.complex_mass: 1191 arg['mass'] = "ML(%(out)d)," 1192 else: 1193 arg['mass'] = "ML(%(out)d),ZERO," 1194 else: 1195 arg['out']=self.format_helas_object('W(1,','%(out)d') 1196 if aloha.complex_mass: 1197 arg['mass'] = "DCMPLX(%(CM)s)," 1198 else: 1199 arg['mass'] = "%(M)s,%(W)s," 1200 # Standard Amplitude 1201 elif argument['type'] == 'base': 1202 arg['mass'] = '' 1203 arg['out'] = self.format_helas_object('AMP(','%(out)d') 1204 # Loop Amplitude 1205 elif argument['type'] == 'loop': 1206 arg['mass'] = '' 1207 arg['out'] = 'BUFF(I)' 1208 # UV Counterterm (and other) 1209 else: 1210 arg['mass'] = '' 1211 ampl = "AMPL({0},%(out)d)".format(argument.get_epsilon_order()+1) 1212 arg['out'] = '%s' % ampl 1213 if isinstance(argument,loop_helas_objects.LoopHelasUVCTAmplitude)\ 1214 and argument.get_UVCT_couplings()!='1.0d0': 1215 # add a second line to take into account the multiplicative factor 1216 call += "\n %(second_line)s " 1217 arg['second_line'] = ampl+"="+ampl+"*(%(uvct)s)" 1218 1219 # ALL ARGUMENT FORMATTED ############################################### 1220 call, arg = HelasCallWriter.customize_argument_for_all_other_helas_object(call, arg) 1221 # Store the result. 1222 call = call % arg 1223 # Now we have a line correctly formatted 1224 call_function = lambda wf: call % wf.get_helas_call_dict(\ 1225 OptimizedOutput=False, specifyHel=self.hel_sum) 1226 1227 # Add the constructed function to wavefunction or amplitude dictionary 1228 if isinstance(argument, helas_objects.HelasWavefunction): 1229 self.add_wavefunction(argument.get_call_key(), call_function) 1230 else: 1231 self.add_amplitude(argument.get_call_key(), call_function)
1232 1233 1234
1235 - def get_loop_amplitude_helas_calls(self, loop_matrix_element):
1236 """ Returns a list of strings corresponding to the Helas calls for each 1237 loop amplitude of this loop matrix element. This function is placed in 1238 this class and not in HelasWriter, because it contains fortran-specific 1239 code.""" 1240 1241 res = [] 1242 loopHelasAmpNumberTreated=[] 1243 for ldiag in loop_matrix_element.get_loop_diagrams(): 1244 for lamp in ldiag.get_loop_amplitudes(): 1245 if lamp.get('number') in loopHelasAmpNumberTreated: 1246 continue 1247 else: 1248 loopHelasAmpNumberTreated.append(lamp.get('number')) 1249 lcutpart=self['model'].get_particle(lamp['type']) 1250 res.append("ELSEIF (ID.EQ.%d) THEN"%lamp.get('number')) 1251 res.append("#Loop diagram number %d (might be others, just an example)"\ 1252 %ldiag.get('number')) 1253 if lcutpart.get('spin')==1: 1254 res.append("DO I=1,1") 1255 elif lcutpart.get('spin')==2 or lcutpart.get('spin')==3: 1256 res.append("DO I=1,4") 1257 else: 1258 raise self.PhysicsObjectError, \ 1259 "The L-cut particle type is not supported" 1260 # Temporarily relabel the 'me_id' attribute of the external wfs 1261 # in this wavefunction's mothers so to have them matching the 1262 # convention in the loop helas calls. 1263 # The same relabeling is performed for couplings. 1264 # We save the original values to reset them afterwards. 1265 externalWfNumber=1 1266 originalNumbers=[] 1267 couplingNumber=1 1268 originalCouplings=[] 1269 for lwf in lamp.get('wavefunctions'): 1270 if lwf.get('coupling')!=['none']: 1271 originalCouplings.append(lwf.get('coupling')) 1272 couplings=[] 1273 for coup in lwf.get('coupling'): 1274 couplings.append("LC(%d)"%couplingNumber) 1275 couplingNumber=couplingNumber+1 1276 lwf.set('coupling',couplings) 1277 for mother in lwf.get('mothers'): 1278 if not mother.get('is_loop'): 1279 originalNumbers.append(mother.get('number')) 1280 mother.set('me_id',externalWfNumber) 1281 externalWfNumber=externalWfNumber+1 1282 # Now we can generate the call for the starting loop wavefunction 1283 res.append(self.get_wavefunction_call(\ 1284 lamp.get_starting_loop_wavefunction())) 1285 # And now for all the other wavefunctions 1286 res.extend([ self.get_wavefunction_call(wf) for \ 1287 wf in lamp.get('wavefunctions') if wf.get('mothers')]) 1288 1289 # Get the last wf generated and the corresponding loop 1290 # wavefunction number 1291 for lwf in lamp.get('amplitudes')[0].get('mothers'): 1292 if lwf.get('mothers'): 1293 last_lwf_number=lwf.get('number') 1294 break 1295 res.append('BUFF(I)=WL(I+4,%d)'%last_lwf_number) 1296 # And re-establish the original numbering 1297 indexMothers=0 1298 indexWfs=0 1299 for lwf in lamp.get('wavefunctions'): 1300 if lwf.get('coupling')!=['none']: 1301 lwf.set('coupling',originalCouplings[indexWfs]) 1302 indexWfs=indexWfs+1 1303 for mother in lwf.get('mothers'): 1304 if not mother.get('is_loop'): 1305 mother.set('me_id',originalNumbers[indexMothers]) 1306 indexMothers=indexMothers+1 1307 res.append('ENDDO') 1308 if lcutpart.get('spin')==1: 1309 res.append("CALL CLOSE_1(BUFF(1),RES)") 1310 elif lcutpart.get('spin')==2 or lcutpart.get('spin')==3: 1311 res.append("CALL CLOSE_4(BUFF(1),RES)") 1312 # We must change the first 'ELSE IF' into an 'IF' 1313 res[0]=res[0][4:] 1314 # And add an ENDIF at the end 1315 res.append('ENDIF') 1316 1317 return res
1318
1319 #=============================================================================== 1320 # FortranUFOHelasCallWriterOptimized 1321 #=============================================================================== 1322 -class FortranUFOHelasCallWriterOptimized(FortranUFOHelasCallWriter):
1323 """ Version of FortranUFOHelasCallWriter meeting the needs of the optimized 1324 output for loop processes """ 1325
1326 - def get_amplitude_call(self, *args, **opts):
1327 """ We overwrite this function here because in the optimized mode one 1328 does not need to call the function set_octet_majorana_coupling_sign 1329 for the wavefunctions of the loop amplitudes. So we directly call 1330 the mother of the mother, namely UFOHelasCallWriter. """ 1331 1332 return super(FortranUFOHelasCallWriter, self).get_amplitude_call( 1333 *args,**opts)
1334
1335 - def format_helas_object(self, prefix, number):
1336 """ Returns the string for accessing the wavefunction with number in 1337 argument. Typical output is {prefix}(1,{number}) """ 1338 1339 if self.hel_sum: 1340 return '%s%s,H)'%(prefix, number) 1341 else: 1342 return '%s%s)'%(prefix, number)
1343
1344 - def get_coef_construction_calls(self, matrix_element, group_loops=True, 1345 squared_orders=[], split_orders=[]):
1346 """ Return the calls to the helas routines to construct the coefficients 1347 of the polynomial representation of the loop numerator (i.e. Pozzorini 1348 method). Group the coefficients of the loop with same denominator 1349 together if group_loops is set True. The squared_orders can provide the 1350 information of what is the maximum contributing loop amp number.""" 1351 1352 assert isinstance(matrix_element, loop_helas_objects.LoopHelasMatrixElement), \ 1353 "%s not valid argument for get_coef_construction_calls" % \ 1354 repr(matrix_element) 1355 loop_induced = (not matrix_element.get('processes')[0].get('has_born')) 1356 res = [] 1357 sqso_max_lamp = [sqso[1][2] for sqso in squared_orders] 1358 1359 i=0 1360 for ldiag in matrix_element.get_loop_diagrams(): 1361 res.append("# Coefficient construction for loop diagram with ID %d"\ 1362 %ldiag.get('number')) 1363 for lwf in ldiag.get('loop_wavefunctions'): 1364 res.append(self.get_wavefunction_call(lwf)) 1365 for lamp in ldiag.get_loop_amplitudes(): 1366 # If the loop grouping is not desired, then make sure it is 1367 # turned off here. It is of course not to be included for 1368 # loop-induced processes 1369 if (not group_loops) or loop_induced: 1370 lamp.set('loop_group_id',i) 1371 i=i+1 1372 create_coef=[ 1373 'CREATE_LOOP_COEFS(WL(1,0,1,%(number)d)', 1374 '%(loop_rank)d','%(lcut_size)d', 1375 '%(loop_number)d','%(LoopSymmetryFactor)d', 1376 '%(LoopMultiplier)d'] 1377 if not loop_induced: 1378 create_coef.append('%(amp_number)d,H)') 1379 else: 1380 create_coef.append('%(amp_number)d)') 1381 1382 res.append('CALL %(proc_prefix)s'+','.join(create_coef)%{\ 1383 'number':lamp.get_final_loop_wavefunction().get('number'), 1384 'loop_rank':lamp.get_analytic_info('wavefunction_rank'), 1385 'lcut_size':lamp.get_lcut_size(), 1386 # For the loop_number below, we used the id of the 'loop_group' this 1387 # amplitude belongs to. All amplitudes of such loop_group will therefore 1388 # be added into the same LOOPCOEF array component. 1389 'loop_number':(lamp.get('loop_group_id')+1), 1390 'amp_number':lamp.get('amplitudes')[0].get('number'), 1391 'LoopSymmetryFactor':lamp.get('loopsymmetryfactor'), 1392 'LoopMultiplier':lamp.get('multiplier')}) 1393 res.extend(self.get_sqso_target_skip_code( 1394 lamp.get('amplitudes')[0].get('number'), 1395 sqso_max_lamp, 4000, split_orders, squared_orders, 1396 "# At this point, all loop coefficients needed"+ 1397 " for %s are computed.")) 1398 1399 coef_merge=['C Grouping of loop diagrams now done directly when '+\ 1400 'creating the LOOPCOEFS.'] 1401 1402 return res, coef_merge
1403
1404 - def get_loop_CT_calls(self, matrix_element, group_loops=True, 1405 squared_orders=[], split_orders=[]):
1406 """ Return the calls to CutTools interface routines to launch the 1407 computation of the contribution of one loop group. The squared_orders 1408 can provide the information of the maximum reference loop group ID for 1409 each contributing squared loop orders.""" 1410 1411 assert isinstance(matrix_element, loop_helas_objects.LoopHelasMatrixElement), \ 1412 "%s not valid argument for get_loop_CT_calls" % \ 1413 repr(matrix_element) 1414 1415 res = [] 1416 1417 sqso_max_lgroup_refs = [sqso[1][3] for sqso in squared_orders] 1418 1419 # Either call CutTools for all loop diagrams or for only the reference 1420 # amplitude for each group (for which the coefficients are the sum of 1421 # all others) 1422 if group_loops and matrix_element.get('processes')[0].get('has_born'): 1423 # Reformat the loop group list in a convenient form 1424 loop_group_refs=[(lamps[1][0],lamps[1][1:]) for lamps in \ 1425 matrix_element.get('loop_groups')] 1426 for (lamp_ref, lamps) in loop_group_refs: 1427 res.append("# CutTools call for loop numbers %s"%\ 1428 ','.join(['%d'%lamp_ref.get('number'),]+\ 1429 ['%d'%lamp.get('number') for lamp in lamps])) 1430 res.append(self.get_amplitude_call(lamp_ref)) 1431 res.extend(self.get_sqso_target_skip_code( 1432 lamp_ref.get('loop_group_id'), sqso_max_lgroup_refs, 5000, 1433 split_orders, squared_orders, 1434 "# At this point, all reductions needed for %s are computed.")) 1435 else: 1436 for ldiag in matrix_element.get_loop_diagrams(): 1437 res.append("# CutTools call for loop # %d"%ldiag.get('number')) 1438 for lamp in ldiag.get_loop_amplitudes(): 1439 # Make sure the loop number is used instead of the loop 1440 # group number 1441 loop_group_id_tmp = lamp.get('loop_group_id') 1442 lamp.set('loop_group_id',lamp.get('number')-1) 1443 res.append(self.get_amplitude_call(lamp)) 1444 lamp.set('loop_group_id',loop_group_id_tmp) 1445 1446 return res
1447
1448 - def generate_external_wavefunction(self,argument):
1449 """ Generate an external wavefunction """ 1450 1451 call_function = None 1452 if argument.get('is_loop'): 1453 call="LCUT_OPT(PL(0,%(number)d),WL(1,1,1,%(number)d))" 1454 call_function = lambda wf: "CALL %(proc_prefix)s"+ \ 1455 call % {'number':wf.get('number')} 1456 self.add_wavefunction(argument.get_call_key(), call_function) 1457 else: 1458 # For the tree external wavefunction, just call the mother 1459 FortranUFOHelasCallWriter.generate_external_wavefunction(self, 1460 argument)
1461
1462 - def generate_loop_amplitude_call(self, loopamp):
1463 """ Routine for automatic generation of a call to CutTools for loop 1464 amplitudes for the optimized output.""" 1465 1466 call = "LOOP%(numLoopLines)s" 1467 if (len(loopamp.get('pairing')) != len(loopamp.get('mothers'))): 1468 call += "%(numMotherWfs)s(" 1469 for i in range(len(loopamp.get('pairing'))): 1470 call = call + "%(Pairing{0})d,".format(i) 1471 else: 1472 call += "(" 1473 for i in range(len(loopamp.get('mothers'))): 1474 call = call + "%(MotherID{0})d,".format(i+1) 1475 for i in range(len(loopamp.get('wavefunctions'))-2): 1476 call = call + \ 1477 "DCMPLX(%(LoopMass{0})s),".format(i+1) 1478 call = call + "%(LoopRank)d," 1479 call = call + "I_SO,%(loop_group_id)d)" 1480 1481 # We add here the placeholde for the proc_prefix 1482 call_function = lambda amp: 'CALL %(proc_prefix)s'+\ 1483 call % amp.get_helas_call_dict(OptimizedOutput=True) 1484 self.add_amplitude(loopamp.get_call_key(), call_function) 1485 return
1486
1487 - def generate_all_other_helas_objects(self,argument):
1488 """ Generate all the helas objects for which no special handlers was 1489 placed in generate_helas_call """ 1490 1491 if isinstance(argument, helas_objects.HelasWavefunction): 1492 outgoing = argument.find_outgoing_number() 1493 else: 1494 outgoing = 0 1495 1496 if isinstance(argument, helas_objects.HelasAmplitude) and \ 1497 argument.get('type')=='loop': 1498 raise MadGraph5Error, 'There should not be any helas call '+\ 1499 'associated with helas amplitudes of type loop.' 1500 1501 # Check if we need to append a charge conjugation flag 1502 l = [str(l) for l in argument.get('lorentz')] 1503 flag = [] 1504 if argument.needs_hermitian_conjugate(): 1505 flag = ['C%d' % i for i in argument.get_conjugate_index()] 1506 1507 if (isinstance(argument, helas_objects.HelasWavefunction) and \ 1508 argument.get('is_loop')): 1509 flag.insert(0,"L%d"%argument.get_loop_index()) 1510 1511 # Creating line formatting: 1512 call = 'CALL %(routine_name)s(%(wf)s%(coup)s%(mass)s%(out)s)' 1513 1514 arg = {'routine_name': aloha_writers.combine_name(\ 1515 '%s' % l[0], l[1:], outgoing, flag, True), 1516 'coup': ("%%(coup%d)s," * len(argument.get('coupling'))) % \ 1517 tuple(range(len(argument.get('coupling')))) 1518 } 1519 1520 # select how to write a single wf 1521 if (isinstance(argument,helas_objects.HelasWavefunction) \ 1522 and argument.get('is_loop')): 1523 base_wf = "%(WF{0})s," 1524 else: 1525 base_wf = self.format_helas_object('W(1,','%({0})d')+',' 1526 1527 # compute the full list of wf 1528 wf = '' 1529 for i in range(len(argument.get('mothers'))): 1530 wf += base_wf.format(i) 1531 arg['wf'] = wf 1532 1533 1534 # Treat other argument 1535 # First WaveFunction 1536 if isinstance(argument, helas_objects.HelasWavefunction): 1537 if argument['is_loop']: 1538 arg['out'] = 'PL(0,%(out)d),COEFS' 1539 else: 1540 arg['out']=self.format_helas_object('W(1,','%(out)d') 1541 if aloha.complex_mass: 1542 arg['mass'] = "DCMPLX(%(CM)s)," 1543 else: 1544 arg['mass'] = "%(M)s,%(W)s," 1545 # Standard Amplitude 1546 elif argument['type'] == 'base': 1547 arg['mass'] = '' 1548 arg['out'] = self.format_helas_object('AMP(','%(out)d') 1549 # UV Counterterm (and other) 1550 else: 1551 arg['mass'] = '' 1552 ampl = "AMPL({0},%(out)d)".format(argument.get_epsilon_order()+1) 1553 arg['out'] = '%s' % ampl 1554 if isinstance(argument,loop_helas_objects.LoopHelasUVCTAmplitude)\ 1555 and argument.get_UVCT_couplings()!='1.0d0': 1556 # add a second line to take into account the multiplicative factor 1557 call += "\n %(second_line)s " 1558 arg['second_line'] = ampl+"="+ampl+"*(%(uvct)s)" 1559 1560 # ALL ARGUMENT FORMATTED ############################################### 1561 # Store the result. 1562 call = call % arg 1563 if (isinstance(argument, helas_objects.HelasWavefunction) and \ 1564 argument.get('is_loop')): 1565 # We add here the call to the UPDATE_COEF subroutine 1566 call += "\nCALL {0}UPDATE_WL_%(loop_mother_rank)d_%(vertex_rank)d(" 1567 call += "WL(1,0,1,%(loop_mother_number)d),%(lcut_size)d,COEFS," 1568 call += "%(in_size)d,%(out_size)d,WL(1,0,1,%(out)d))" 1569 # Now we have a line correctly formatted, with the proc_prefix 1570 call_function = lambda wf:\ 1571 (call%wf.get_helas_call_dict(OptimizedOutput=True, 1572 specifyHel=self.hel_sum)).format('%(proc_prefix)s') 1573 1574 # Add the constructed function to wavefunction or amplitude dictionary 1575 if isinstance(argument, helas_objects.HelasWavefunction): 1576 self.add_wavefunction(argument.get_call_key(), call_function) 1577 else: 1578 self.add_amplitude(argument.get_call_key(), call_function)
1579
1580 #=============================================================================== 1581 # CPPUFOHelasCallWriter 1582 #=============================================================================== 1583 -class CPPUFOHelasCallWriter(UFOHelasCallWriter):
1584 """The class for writing Helas calls in C++, starting from 1585 HelasWavefunctions and HelasAmplitudes. 1586 1587 Includes the function generate_helas_call, which automatically 1588 generates the C++ Helas call based on the Lorentz structure of 1589 the interaction.""" 1590
1591 - def generate_helas_call(self, argument):
1592 """Routine for automatic generation of C++ Helas calls 1593 according to just the spin structure of the interaction. 1594 1595 First the call string is generated, using a dictionary to go 1596 from the spin state of the calling wavefunction and its 1597 mothers, or the mothers of the amplitude, to difenrentiate wich call is 1598 done. 1599 1600 Then the call function is generated, as a lambda which fills 1601 the call string with the information of the calling 1602 wavefunction or amplitude. The call has different structure, 1603 depending on the spin of the wavefunction and the number of 1604 mothers (multiplicity of the vertex). The mother 1605 wavefunctions, when entering the call, must be sorted in the 1606 correct way - this is done by the sorted_mothers routine. 1607 1608 Finally the call function is stored in the relevant 1609 dictionary, in order to be able to reuse the function the next 1610 time a wavefunction with the same Lorentz structure is needed. 1611 """ 1612 1613 if not isinstance(argument, helas_objects.HelasWavefunction) and \ 1614 not isinstance(argument, helas_objects.HelasAmplitude): 1615 raise self.PhysicsObjectError, \ 1616 "get_helas_call must be called with wavefunction or amplitude" 1617 1618 call = "" 1619 1620 call_function = None 1621 1622 if isinstance(argument, helas_objects.HelasAmplitude) and \ 1623 argument.get('interaction_id') == 0: 1624 call = "#" 1625 call_function = lambda amp: call 1626 self.add_amplitude(argument.get_call_key(), call_function) 1627 return 1628 1629 if isinstance(argument, helas_objects.HelasWavefunction) and \ 1630 not argument.get('mothers'): 1631 # String is just ixxxxx, oxxxxx, vxxxxx or sxxxxx 1632 call = call + HelasCallWriter.mother_dict[\ 1633 argument.get_spin_state_number()].lower() 1634 # Fill out with X up to 6 positions 1635 call = call + 'x' * (6 - len(call)) 1636 # Specify namespace for Helas calls 1637 call = call + "(p[perm[%d]]," 1638 if argument.get('spin') != 1: 1639 # For non-scalars, need mass and helicity 1640 call = call + "mME[%d],hel[%d]," 1641 call = call + "%+d,w[%d]);" 1642 if argument.get('spin') == 1: 1643 call_function = lambda wf: call % \ 1644 (wf.get('number_external')-1, 1645 # For boson, need initial/final here 1646 (-1) ** (wf.get('state') == 'initial'), 1647 wf.get('me_id')-1) 1648 elif argument.is_boson(): 1649 call_function = lambda wf: call % \ 1650 (wf.get('number_external')-1, 1651 wf.get('number_external')-1, 1652 wf.get('number_external')-1, 1653 # For boson, need initial/final here 1654 (-1) ** (wf.get('state') == 'initial'), 1655 wf.get('me_id')-1) 1656 else: 1657 call_function = lambda wf: call % \ 1658 (wf.get('number_external')-1, 1659 wf.get('number_external')-1, 1660 wf.get('number_external')-1, 1661 # For fermions, need particle/antiparticle 1662 - (-1) ** wf.get_with_flow('is_part'), 1663 wf.get('me_id')-1) 1664 else: 1665 if isinstance(argument, helas_objects.HelasWavefunction): 1666 outgoing = argument.find_outgoing_number() 1667 else: 1668 outgoing = 0 1669 1670 # Check if we need to append a charge conjugation flag 1671 l = [str(l) for l in argument.get('lorentz')] 1672 flag = [] 1673 if argument.needs_hermitian_conjugate(): 1674 flag = ['C%d' % i for i in argument.get_conjugate_index()] 1675 1676 1677 # Creating line formatting: 1678 call = '%(routine_name)s(%(wf)s%(coup)s%(mass)s%(out)s);' 1679 # compute wf 1680 arg = {'routine_name': aloha_writers.combine_name(\ 1681 '%s' % l[0], l[1:], outgoing, flag,True), 1682 'wf': ("w[%%(%d)d]," * len(argument.get('mothers'))) % \ 1683 tuple(range(len(argument.get('mothers')))), 1684 'coup': ("pars->%%(coup%d)s," * len(argument.get('coupling'))) % \ 1685 tuple(range(len(argument.get('coupling')))) 1686 } 1687 if isinstance(argument, helas_objects.HelasWavefunction): 1688 arg['out'] = 'w[%(out)d]' 1689 if aloha.complex_mass: 1690 arg['mass'] = "pars->%(CM)s," 1691 else: 1692 arg['mass'] = "pars->%(M)s,pars->%(W)s," 1693 else: 1694 arg['out'] = 'amp[%(out)d]' 1695 arg['mass'] = '' 1696 1697 call = call % arg 1698 # Now we have a line correctly formatted 1699 call_function = lambda wf: self.format_coupling( 1700 call % wf.get_helas_call_dict(index=0)) 1701 1702 # Add the constructed function to wavefunction or amplitude dictionary 1703 if isinstance(argument, helas_objects.HelasWavefunction): 1704 self.add_wavefunction(argument.get_call_key(), call_function) 1705 else: 1706 self.add_amplitude(argument.get_call_key(), call_function)
1707 1708 @staticmethod
1709 - def format_coupling(call):
1710 """Format the coupling so any minus signs are put in front""" 1711 1712 return call.replace('pars->-', '-pars->')
1713
1714 1715 #=============================================================================== 1716 # PythonUFOHelasCallWriter 1717 #=============================================================================== 1718 -class PythonUFOHelasCallWriter(UFOHelasCallWriter):
1719 """The class for writing Helas calls in Python, starting from 1720 HelasWavefunctions and HelasAmplitudes. 1721 1722 Includes the function generate_helas_call, which automatically 1723 generates the Python Helas call based on the Lorentz structure of 1724 the interaction.""" 1725
1726 - def get_matrix_element_calls(self, matrix_element, gauge_check=False):
1727 """Return a list of strings, corresponding to the Helas calls 1728 for the matrix element""" 1729 1730 assert isinstance(matrix_element, helas_objects.HelasMatrixElement), \ 1731 "%s not valid argument for get_matrix_element_calls" % \ 1732 repr(matrix_element) 1733 1734 me = matrix_element.get('diagrams') 1735 matrix_element.reuse_outdated_wavefunctions(me) 1736 1737 res = [] 1738 for diagram in matrix_element.get('diagrams'): 1739 wfs = diagram.get('wavefunctions') 1740 if gauge_check and diagram.get('number') == 1: 1741 gauge_check_wfs = [wf for wf in wfs if not wf.get('mothers') \ 1742 and wf.get('spin') == 3 \ 1743 and wf.get('mass').lower() == 'zero'] 1744 if not gauge_check_wfs: 1745 raise HelasWriterError, \ 1746 'no massless spin one particle for gauge check' 1747 gauge_check_wf = wfs.pop(wfs.index(gauge_check_wfs[0])) 1748 res.append(self.generate_helas_call(gauge_check_wf, True)(\ 1749 gauge_check_wf)) 1750 res.extend([ self.get_wavefunction_call(wf) for wf in wfs ]) 1751 res.append("# Amplitude(s) for diagram number %d" % \ 1752 diagram.get('number')) 1753 for amplitude in diagram.get('amplitudes'): 1754 res.append(self.get_amplitude_call(amplitude)) 1755 1756 return res
1757 1758 1759
1760 - def generate_helas_call(self, argument, gauge_check=False):
1761 """Routine for automatic generation of Python Helas calls 1762 according to just the spin structure of the interaction. 1763 """ 1764 1765 if not isinstance(argument, helas_objects.HelasWavefunction) and \ 1766 not isinstance(argument, helas_objects.HelasAmplitude): 1767 raise self.PhysicsObjectError, \ 1768 "get_helas_call must be called with wavefunction or amplitude" 1769 1770 call_function = None 1771 1772 if isinstance(argument, helas_objects.HelasAmplitude) and \ 1773 argument.get('interaction_id') == 0: 1774 call = "#" 1775 call_function = lambda amp: call 1776 self.add_amplitude(argument.get_call_key(), call_function) 1777 return 1778 1779 if isinstance(argument, helas_objects.HelasWavefunction) and \ 1780 not argument.get('mothers'): 1781 # String is just IXXXXX, OXXXXX, VXXXXX or SXXXXX 1782 call = "w[%d] = " 1783 1784 call = call + HelasCallWriter.mother_dict[\ 1785 argument.get_spin_state_number()].lower() 1786 # Fill out with X up to 6 positions 1787 call = call + 'x' * (14 - len(call)) 1788 call = call + "(p[%d]," 1789 if argument.get('spin') != 1: 1790 # For non-scalars, need mass and helicity 1791 if gauge_check and argument.get('spin') == 3 and \ 1792 argument.get('mass') == 'ZERO': 1793 call = call + "%s, 4," 1794 else: 1795 call = call + "%s,hel[%d]," 1796 call = call + "%+d)" 1797 if argument.get('spin') == 1: 1798 call_function = lambda wf: call % \ 1799 (wf.get('me_id')-1, 1800 wf.get('number_external')-1, 1801 # For boson, need initial/final here 1802 (-1)**(wf.get('state') == 'initial')) 1803 elif argument.is_boson(): 1804 if not gauge_check or argument.get('mass') != 'ZERO': 1805 call_function = lambda wf: call % \ 1806 (wf.get('me_id')-1, 1807 wf.get('number_external')-1, 1808 wf.get('mass'), 1809 wf.get('number_external')-1, 1810 # For boson, need initial/final here 1811 (-1)**(wf.get('state') == 'initial')) 1812 else: 1813 call_function = lambda wf: call % \ 1814 (wf.get('me_id')-1, 1815 wf.get('number_external')-1, 1816 'ZERO', 1817 # For boson, need initial/final here 1818 (-1)**(wf.get('state') == 'initial')) 1819 else: 1820 call_function = lambda wf: call % \ 1821 (wf.get('me_id')-1, 1822 wf.get('number_external')-1, 1823 wf.get('mass'), 1824 wf.get('number_external')-1, 1825 # For fermions, need particle/antiparticle 1826 -(-1)**wf.get_with_flow('is_part')) 1827 else: 1828 # String is LOR1_0, LOR1_2 etc. 1829 1830 if isinstance(argument, helas_objects.HelasWavefunction): 1831 outgoing = argument.find_outgoing_number() 1832 else: 1833 outgoing = 0 1834 1835 # Check if we need to append a charge conjugation flag 1836 l = [str(l) for l in argument.get('lorentz')] 1837 flag = [] 1838 if argument.needs_hermitian_conjugate(): 1839 flag = ['C%d' % i for i in argument.get_conjugate_index()] 1840 1841 1842 # Creating line formatting: 1843 call = '%(out)s= %(routine_name)s(%(wf)s%(coup)s%(mass)s)' 1844 # compute wf 1845 arg = {'routine_name': aloha_writers.combine_name(\ 1846 '%s' % l[0], l[1:], outgoing, flag, True), 1847 'wf': ("w[%%(%d)d]," * len(argument.get('mothers'))) % \ 1848 tuple(range(len(argument.get('mothers')))), 1849 'coup': ("%%(coup%d)s," * len(argument.get('coupling'))) % \ 1850 tuple(range(len(argument.get('coupling')))) 1851 } 1852 1853 if isinstance(argument, helas_objects.HelasWavefunction): 1854 arg['out'] = 'w[%(out)d]' 1855 if aloha.complex_mass: 1856 arg['mass'] = "%(CM)s" 1857 else: 1858 arg['mass'] = "%(M)s,%(W)s" 1859 else: 1860 arg['coup'] = arg['coup'][:-1] #removing the last coma 1861 arg['out'] = 'amp[%(out)d]' 1862 arg['mass'] = '' 1863 1864 call = call % arg 1865 # Now we have a line correctly formatted 1866 call_function = lambda wf: call % wf.get_helas_call_dict(index=0) 1867 1868 routine_name = aloha_writers.combine_name( 1869 '%s' % l[0], l[1:], outgoing, flag) 1870 1871 # Add the constructed function to wavefunction or amplitude dictionary 1872 if isinstance(argument, helas_objects.HelasWavefunction): 1873 if not gauge_check: 1874 self.add_wavefunction(argument.get_call_key(), call_function) 1875 else: 1876 self.add_amplitude(argument.get_call_key(), call_function) 1877 1878 return call_function
1879