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