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