1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Definitions of objects inheriting from the classes defined in
17 helas_objects.py and which have special attributes and function
18 devoted to the treatment of Loop processes"""
19
20 import array
21 import copy
22 import logging
23 import itertools
24 import math
25 import os
26
27 import aloha
28 import aloha.create_aloha as create_aloha
29
30 from madgraph import MadGraph5Error
31 import madgraph.core.base_objects as base_objects
32 import madgraph.loop.loop_base_objects as loop_base_objects
33 import madgraph.core.diagram_generation as diagram_generation
34 import madgraph.loop.loop_diagram_generation as loop_diagram_generation
35 import madgraph.core.color_amp as color_amp
36 import madgraph.loop.loop_color_amp as loop_color_amp
37 import madgraph.core.color_algebra as color
38 import madgraph.core.helas_objects as helas_objects
39 import madgraph.various.misc as misc
40
41
42
43
44
45 logger = logging.getLogger('madgraph.helas_objects')
51 """LoopHelasUVCTAmplitude object, behaving exactly as an amplitude except that
52 it also contains additional vertices with coupling constants corresponding
53 to the 'UVCTVertices' defined in the 'UVCTVertices ' of the
54 loop_base_objects.LoopUVCTDiagram of the LoopAmplitude. These are stored
55 in the additional attribute 'UVCT_interaction_ids' of this class.
56 """
57
58
67
76
77 - def filter(self, name, value):
78 """Filter for valid LoopHelasAmplitude property values."""
79
80 if name=='UVCT_couplings':
81 if not isinstance(value, list):
82 raise self.PhysicsObjectError, \
83 "%s is not a valid list for UVCT_couplings" % str(value)
84 for id in value:
85 if not isinstance(id, str) and not isinstance(id, int):
86 raise self.PhysicsObjectError, \
87 "%s is not a valid string or integer for UVCT_couplings" % str(value)
88
89 if name == 'UVCT_orders':
90 if not isinstance(value, dict):
91 raise self.PhysicsObjectError, \
92 "%s is not a valid dictionary" % str(value)
93
94 if name == 'type':
95 if not isinstance(value, str):
96 raise self.PhysicsObjectError, \
97 "%s is not a valid string" % str(value)
98
99 else:
100 return super(LoopHelasUVCTAmplitude,self).filter(name, value)
101
103 """Return LoopHelasAmplitude property names as a nicely sorted list."""
104
105 return super(LoopHelasUVCTAmplitude,self).get_sorted_keys()+\
106 ['UVCT_couplings','UVCT_orders','type']
107
108 return True
109
111 """ Exactly as a regular HelasAmplitude except that here we must add
112 an entry to mutliply the final result by the coupling constants of the
113 interaction in UVCT_couplings if there are any"""
114 original_call_key = super(LoopHelasUVCTAmplitude,self).get_call_key()
115
116 if self.get_UVCT_couplings()=='1.0d0':
117 return original_call_key
118 else:
119 return (original_call_key[0],original_call_key[1],'UVCT')
120
122 """ Returns a list of the string UVCT_couplings defined for this
123 amplitudes. """
124 return [coupl for coupl in self['UVCT_couplings'] if \
125 isinstance(coupl,str)]
126
128 """ Returns the string corresponding to the overall UVCT coupling which
129 factorize this amplitude """
130 if self['UVCT_couplings']==[]:
131 return '1.0d0'
132
133 answer=[]
134 integer_sum=0
135 for coupl in list(set(self['UVCT_couplings'])):
136 if isinstance(coupl,int):
137 integer_sum+=coupl
138 else:
139 answer.append(str(len([1 for c in self['UVCT_couplings'] if \
140 c==coupl]))+'.0d0*'+coupl)
141 if integer_sum!=0:
142 answer.append(str(integer_sum)+'.0d0')
143 if answer==[] and (integer_sum==0 or integer_sum==1):
144 return '1.0d0'
145 else:
146 return '+'.join(answer)
147
149 """Return the loop_base_objects.LoopUVCTDiagram which corresponds to this
150 amplitude, using a recursive method for the wavefunctions."""
151
152 vertices = super(LoopHelasUVCTAmplitude,self).get_base_diagram(\
153 wf_dict, vx_list, optimization)['vertices']
154
155 return loop_base_objects.LoopUVCTDiagram({'vertices': vertices, \
156 'UVCT_couplings': self['UVCT_couplings'], \
157 'UVCT_orders': self['UVCT_orders'], \
158 'type': self['type']})
159
171
176 """LoopHelasAmplitude object, behaving exactly as an amplitude except that
177 it also contains loop wave-functions closed on themselves, building an
178 amplitude corresponding to the closed loop.
179 """
180
181
190
192 """Comparison between different LoopHelasAmplitude in order to recognize
193 which ones are equivalent at the level of the file output.
194 I decided not to overload the operator __eq__ to be sure not to interfere
195 with other functionalities of the code."""
196
197 if(len(self.get('wavefunctions'))!=len(other.get('wavefunctions')) or
198 len(self.get('amplitudes'))!=len(other.get('amplitudes')) or
199 [len(wf.get('coupling')) for wf in self.get('wavefunctions')]!=
200 [len(wf.get('coupling')) for wf in other.get('wavefunctions')] or
201 [len(amp.get('coupling')) for amp in self.get('amplitudes')]!=
202 [len(amp.get('coupling')) for amp in other.get('amplitudes')]):
203 return False
204
205 wfArgsToCheck = ['fermionflow','lorentz','state','onshell','spin',\
206 'is_part','self_antipart','color']
207 for arg in wfArgsToCheck:
208 if [wf.get(arg) for wf in self.get('wavefunctions')]!=\
209 [wf.get(arg) for wf in other.get('wavefunctions')]:
210 return False
211
212 if [wf.find_outgoing_number() for wf in self.get('wavefunctions')]!=\
213 [wf.find_outgoing_number() for wf in other.get('wavefunctions')]:
214 return False
215
216 ampArgsToCheck = ['lorentz',]
217 for arg in ampArgsToCheck:
218 if [amp.get(arg) for amp in self.get('amplitudes')]!=\
219 [amp.get(arg) for amp in other.get('amplitudes')]:
220 return False
221
222
223
224
225
226
227 if [[m.get('is_loop') for m in lwf.get('mothers')] for lwf in self.get('wavefunctions')]!=\
228 [[m.get('is_loop') for m in lwf.get('mothers')] for lwf in other.get('wavefunctions')]:
229 return False
230 if [[m.get('is_loop') for m in lwf.get('mothers')] for lwf in self.get('amplitudes')]!=\
231 [[m.get('is_loop') for m in lwf.get('mothers')] for lwf in other.get('amplitudes')]:
232 return False
233
234 return True
235
267
268
269 - def get(self, name):
276
277 - def filter(self, name, value):
278 """Filter for valid LoopHelasAmplitude property values."""
279
280 if name=='wavefunctions':
281 if not isinstance(value, helas_objects.HelasWavefunctionList):
282 raise self.PhysicsObjectError, \
283 "%s is not a valid list of HelasWaveFunctions" % str(value)
284 for wf in value:
285 if not wf['is_loop']:
286 raise self.PhysicsObjectError, \
287 "Wavefunctions from a LoopHelasAmplitude must be from a loop."
288
289 elif name=='amplitudes':
290 if not isinstance(value, helas_objects.HelasAmplitudeList):
291 raise self.PhysicsObjectError, \
292 "%s is not a valid list of HelasAmplitudes" % str(value)
293
294 elif name in ['type','loop_group_id','multiplier','loopsymmetryfactor']:
295 if not isinstance(value, int):
296 raise self.PhysicsObjectError, \
297 "%s is not a valid integer for the attribute '%s'" %(str(value),name)
298
299 else:
300 return super(LoopHelasAmplitude,self).filter(name, value)
301
302 return True
303
305 """Return LoopHelasAmplitude property names as a nicely sorted list."""
306
307 return super(LoopHelasAmplitude,self).get_sorted_keys()+\
308 ['wavefunctions', 'amplitudes','loop_group_id']
309
316
318 """ Return the starting external loop mother of this loop helas amplitude.
319 It is the loop wavefunction of the l-cut leg one."""
320
321 loop_wf=self.get_final_loop_wavefunction()
322 loop_wf_mother=loop_wf.get_loop_mother()
323 while loop_wf_mother:
324 loop_wf=loop_wf_mother
325 loop_wf_mother=loop_wf.get_loop_mother()
326 return loop_wf
327
329 """Return the non-external loop mother of the helas amplitude building
330 this loop amplitude"""
331
332 final_lwf=[lwf for lwf in self.get('amplitudes')[0].get('mothers') if \
333 lwf.get('mothers')]
334 if len(final_lwf)!=1:
335 raise MadGraph5Error, 'The helas amplitude building the helas loop'+\
336 ' amplitude should be made of exactly one loop wavefunctions'+\
337 ' with mothers.'
338 return final_lwf[0]
339
341 """Return the loop_base_objects.LoopDiagram which corresponds to this
342 amplitude, using a recursive method for the wavefunctions.
343 Remember that this diagram is not tagged and structures are not
344 recognized."""
345
346 vertices = self['amplitudes'][0].get_base_diagram(\
347 wf_dict, vx_list, optimization)['vertices']
348
349 out = loop_base_objects.LoopDiagram({'vertices': vertices,\
350 'type':self['type']})
351
352
353
354
355
356
357
358
359 starting_loop_line = out.get_starting_loop_line()
360 finishing_loop_line = out.get_finishing_loop_line()
361 if starting_loop_line['number'] == finishing_loop_line['number']:
362
363
364
365
366 nb_external = len(out.get_external_legs()) +1
367 if nb_external == starting_loop_line['number']:
368 starting_loop_line.set('number', nb_external -1)
369 else:
370 starting_loop_line.set('number', nb_external)
371
372
373 return out
374
376 """ Sets the mothers of this amplitude in the same order as they will
377 be used in the arguments of the helas calls building this loop"""
378
379 if len(self.get('amplitudes'))!=1:
380 self.PhysicsObjectError, \
381 "HelasLoopAmplitude is for now designed to contain only one \
382 HelasAmplitude"
383
384 self.set('mothers',helas_objects.HelasWavefunctionList())
385 for lwf in [wf for wf in self.get('wavefunctions') if wf.get('mothers')]:
386 mothersList=[wf for wf in lwf.get('mothers') if not wf['is_loop']]
387 self['mothers'].extend(mothersList)
388 self['pairing'].append(len(mothersList))
389
393 """Get a list of the number of legs in vertices in this diagram"""
394
395 if max_n_loop == 0:
396 max_n_loop = base_objects.Vertex.max_n_loop_for_multichanneling
397
398
399
400
401 vertex_leg_numbers = [len(self.get('mothers'))] if \
402 (self.get('interaction_id') not in veto_inter_id) or \
403 len(self.get('mothers'))>max_n_loop else []
404 for mother in self.get('mothers'):
405 vertex_leg_numbers.extend(mother.get_vertex_leg_numbers(
406 veto_inter_id=veto_inter_id, max_n_loop=max_n_loop))
407
408 return vertex_leg_numbers
409
411 """ Returns the denominator structure as a tuple (tupleA, tupleB) whose
412 elements are of this form ((external_part_ids),mass) where
413 external_part_ids are all the leg id building the momentum flowing in
414 the loop, i.e:
415 D_i=(q+Sum(p_j,j))^2 - m^2
416 """
417
418 denoms=[]
419 last_loop_wf=self.get_final_loop_wavefunction()
420 last_loop_wf_mother=last_loop_wf.get_loop_mother()
421 while last_loop_wf_mother:
422 denoms.append((tuple(last_loop_wf.get_struct_external_leg_ids()),
423 last_loop_wf.get('mass')))
424 last_loop_wf=last_loop_wf_mother
425 last_loop_wf_mother=last_loop_wf.get_loop_mother()
426 denoms.reverse()
427
428 return tuple(denoms)
429
431 """ Returns the list of the masses of the loop particles as they should
432 appear for cuttools (L-cut particles specified last) """
433
434 masses=[]
435 if not aloha.complex_mass:
436 for lwf in [wf for wf in self.get('wavefunctions') if wf.get('mothers')]:
437 masses.append(lwf.get('mass'))
438 else:
439 for lwf in [wf for wf in self.get('wavefunctions') if wf.get('mothers')]:
440 if (lwf.get('width') == 'ZERO' or lwf.get('mass') == 'ZERO'):
441 masses.append(lwf.get('mass'))
442 else:
443 masses.append('CMASS_%s' % lwf.get('mass'))
444 return masses
445
447 """ Returns the list of the couplings of the different helas objects
448 building this HelasLoopAmplitude. They are ordered as they will appear
449 in the helas calls."""
450
451 return (sum([wf.get('coupling') for wf in self.get('wavefunctions') \
452 if wf.get('coupling')!=['none']],[])\
453 +sum([amp.get('coupling') for amp in self.get('amplitudes') if \
454 amp.get('coupling')!=['none']],[]))
455
457 """ return a dictionary to be used for formatting
458 HELAS call. """
459 output = {}
460 output['numLoopLines']='_%d'%(len(self.get('wavefunctions'))-2)
461
462 output['loop_group_id']=self.get('loop_group_id')+1
463 output['ampNumber']=self.get('amplitudes')[0].get('number')
464 if len(self.get('mothers'))!=len(self.get('pairing')):
465 output['numMotherWfs']='_%d'%len(self.get('mothers'))
466 else:
467 output['numMotherWfs']=''
468 for i, pairing in enumerate(self.get('pairing')):
469 output["Pairing%d"%i]=pairing
470 output['numCouplings']='_%d'%len(self.get('coupling'))
471 output['numeratorNumber']=self.get('number')
472 output["LoopRank"]=self.get_analytic_info('wavefunction_rank')
473 if OptimizedOutput:
474 if self.get('loop_group_id')==-1:
475 output['loopNumber']=self.get('number')
476 else:
477 output['loopNumber']=self.get('loop_group_id')+1
478 else:
479 output['loopNumber']=self.get('amplitudes')[0].get('number')
480 for i , wf in enumerate(self.get('mothers')):
481 output["MotherID%d"%(i+1)]=wf.get('number')
482 for i , mass in enumerate(self.get_masses()):
483 output["LoopMass%d"%(i+1)]=mass
484 for i , coupling in enumerate(self.get('coupling')):
485 output["LoopCoupling%d"%(i+1)]=coupling
486 output["LoopSymmetryFactor"] = self.get('loopsymmetryfactor')
487 output["LoopMultiplier"] = self.get('multiplier')
488 output.update(opt)
489
490 return output
491
493 """ The helas call to a loop is simple and only depends on the number
494 of loop lines and mothers. This how it is reflected in the call key. """
495
496 return ("LOOP",len(self.get('wavefunctions'))-2,\
497 len(self.get('mothers')),len(self.get('coupling')))
498
500 """ Compute the orders building this loop amplitude only (not from the
501 struct wavefunctions. Uses the cached result if available."""
502
503 if self.get('orders') != {}:
504 return self.get('orders')
505 else:
506 coupling_orders = {}
507 last_wf = self.get_final_loop_wavefunction()
508 while last_wf.get_loop_mother()!=None:
509 for order in last_wf.get('orders').keys():
510 try:
511 coupling_orders[order] += last_wf.get('orders')[order]
512 except Exception:
513 coupling_orders[order] = last_wf.get('orders')[order]
514 last_wf = last_wf.get_loop_mother()
515 return coupling_orders
516
518 """ Returns an analytic information of the loop numerator, for example
519 the 'wavefunction_rank' i.e. the maximum power to which the loop momentum
520 is elevated in the loop numerator. All analytic pieces of information
521 are for now identical to the one retrieved from the final_loop_wavefunction."""
522
523 return self.get_final_loop_wavefunction().\
524 get_analytic_info(info, alohaModel)
525
535
537 """ The fermion factor is not implemented for this object but in the
538 subamplitude"""
539 self['fermion_factor']=0
540 for amp in self.get('amplitudes'):
541 amp.get('fermionfactor')
542
544 """ Calculate the loop symmetry factor. For one-loop matrix elements,
545 it is always 2 for bubble with identical particles and tadpoles with self-conjugated particles
546 and 1 otherwise."""
547
548
549
550
551 self['loopsymmetryfactor']=1
552
553 physical_wfs = [wf for wf in self.get('wavefunctions') if wf.get('interaction_id')!=0]
554 if len(physical_wfs)==1:
555 if physical_wfs[0].get('self_antipart'):
556 self['loopsymmetryfactor']=2
557 elif len(physical_wfs)==2:
558 if physical_wfs[0].get('particle')==physical_wfs[1].get('antiparticle'):
559 self['loopsymmetryfactor']=2
560
565 """LoopHelasDiagram object, behaving exactly as a Diagram except that
566 it has a couple of additional functions which can reconstruct and
567 handle loop amplitudes.
568 """
569
571 """ Quick access to ALL non-loop amplitudes, including those which are
572 inside the LoopAmplitudes defined in this diagram."""
573
574 ampList=helas_objects.HelasAmplitudeList()
575 for loopAmp in self.get_loop_amplitudes():
576 ampList.extend(loopAmp['amplitudes'])
577 ampList.extend(self.get_ct_amplitudes())
578 return ampList
579
581 """ Quick access to the regular amplitudes defined directly in this
582 diagram (not in the LoopAmplitudes). Usually they correspond to the
583 counter-terms. """
584
585 return helas_objects.HelasAmplitudeList([amp for amp in \
586 self['amplitudes'] if not isinstance(amp, LoopHelasAmplitude)])
587
593
599
604 """LoopHelasMatrixElement: list of processes with identical Helas
605 calls, and the list of LoopHelasDiagrams associated with the processes.
606 It works as for the HelasMatrixElement except for the loop-related features
607 which are defined here. """
608
625
626 - def filter(self, name, value):
627 """Filter for valid diagram property values."""
628
629 if name=='born_color_basis' or name=='loop_color_basis':
630 if not isinstance(value,color_amp.ColorBasis):
631 raise self.PhysicsObjectError, \
632 "%s is not a valid color basis" % str(value)
633 elif name=='loop_groups':
634 if not isinstance(value,list):
635 raise self.PhysicsObjectError, \
636 "%s is not a valid list"%str(value)
637 for (dkey, dvalue) in value:
638 if not isinstance(dvalue,helas_objects.HelasAmplitudeList):
639 raise self.PhysicsObjectError, \
640 "%s is not a valid HelasAmplitudeList."%str(dvalue)
641 if not isinstance(dkey,tuple):
642 raise self.PhysicsObjectError, \
643 "%s is not a valid tuple."%str(dkey)
644 else:
645 return super(LoopHelasMatrixElement,self).filter(name, value)
646
647 return True
648
649 - def get(self,name):
650 """Overload in order to return the loop_color_basis when simply asked
651 for color_basis. The setter is not updated to avoid side effects."""
652
653 if name=='color_basis':
654 return self['loop_color_basis']
655 elif name=='loop_groups':
656 if not self['loop_groups']:
657 self.identify_loop_groups()
658 return self['loop_groups']
659 else:
660 return super(LoopHelasMatrixElement,self).get(name)
661
663 """ Identify what are the loops sharing the same denominators and put
664 them together in the 'loop_groups' attribute of this object. """
665
666 identified_denom_structures=[]
667 for lamp in [lamp for ldiag in self.get_loop_diagrams() for lamp in \
668 ldiag.get_loop_amplitudes()]:
669 denom_structure=lamp.get_denominators()
670 try:
671 denom_index=identified_denom_structures.index(denom_structure)
672 self['loop_groups'][denom_index][1].append(lamp)
673 except ValueError:
674 denom_index=len(self['loop_groups'])
675 self['loop_groups'].append((denom_structure,
676 helas_objects.HelasAmplitudeList([lamp,])))
677 identified_denom_structures.append(denom_structure)
678 lamp.set('loop_group_id',denom_index)
679
680
681
682 self['loop_groups']=[(group[0],helas_objects.HelasAmplitudeList(
683 sorted(group[1],key=lambda lamp: \
684 lamp.get_analytic_info('wavefunction_rank'),reverse=True)))
685 for group in self['loop_groups']]
686
687
688 self['loop_groups']=sorted(self['loop_groups'],key=lambda group: \
689 group[1][0].get('number'))
690 self.update_loop_group_ids()
691
693 """ Make sure never to use this optimization in the loop context."""
694
695 for diag in helas_diagrams:
696 for wf in diag['wavefunctions']:
697 wf.set('me_id',wf.get('number'))
698
699 return helas_diagrams
700
702 """ Make sure that the attribute 'loop_group_id' of all loop amplitudes
703 in the 'loop_groups' list is correct given the order of 'loop_groups'"""
704
705 for i, group in enumerate(self['loop_groups']):
706 for lamp in group[1]:
707 lamp.set('loop_group_id',i)
708
710 """ Perform the simple color processing from a single matrix element
711 (without optimization then). This is called from the initialization
712 and overloaded here in order to have the correct treatment """
713
714
715
716 self.relabel_helas_objects()
717 self.get('loop_color_basis').build_loop(self.get('base_amplitude'))
718 if self.get('base_amplitude')['process']['has_born']:
719 self.get('born_color_basis').build_born(self.get('base_amplitude'))
720 self.set('color_matrix',\
721 color_amp.ColorMatrix(self.get('loop_color_basis'),\
722 self.get('born_color_basis')))
723 else:
724 self.set('color_matrix',\
725 color_amp.ColorMatrix(self.get('loop_color_basis')))
726
728 """Return particle property names as a nicely sorted list."""
729
730 return ['processes', 'identical_particle_factor',
731 'diagrams', 'born_color_basis','loop_color_basis',
732 'color_matrix','base_amplitude', 'has_mirror_process',
733 'loop_groups']
734
735
736 - def __init__(self, amplitude=None, optimization=1,
737 decay_ids=[], gen_color=True, optimized_output=False):
738 """Constructor for the LoopHelasMatrixElement. For now, it works exactly
739 as for the HelasMatrixElement one."""
740 self.optimized_output=optimized_output
741 super(LoopHelasMatrixElement, self).__init__(amplitude, optimization,\
742 decay_ids, gen_color)
743
744
745
746
747
749 """Comparison between different loop matrix elements, to allow check for
750 identical processes.
751 """
752
753 if not isinstance(other, LoopHelasMatrixElement):
754 return False
755
756
757 if not self['processes'] and not other['processes']:
758 return True
759
760
761 if not self['processes'] or not self['processes']:
762 return False
763
764
765 if self['has_mirror_process'] != other['has_mirror_process'] or \
766 self['processes'][0]['id'] != other['processes'][0]['id'] or \
767 self['identical_particle_factor'] != \
768 other['identical_particle_factor']:
769 return False
770
771
772 if self['diagrams'] != other['diagrams']:
773 return False
774
775 return True
776
777
778
780 """Overloading the nonequality operator, to make comparison easy"""
781 return not self.__eq__(other)
782
785 """Starting from a list of LoopDiagrams from the diagram
786 generation, generate the corresponding LoopHelasDiagrams, i.e.,
787 the wave functions and amplitudes (for the loops and their R2 and UV
788 counterterms). Choose between default optimization (= 1, maximum
789 recycling of wavefunctions) or no optimization (= 0, no recycling of
790 wavefunctions, useful for GPU calculations with very restricted memory).
791
792 Note that we need special treatment for decay chains, since
793 the end product then is a wavefunction, not an amplitude.
794 """
795
796 assert isinstance(amplitude, loop_diagram_generation.LoopAmplitude), \
797 "Bad arguments for generate_helas_diagrams in LoopHelasMatrixElement"
798 assert isinstance(optimization, int), \
799 "Bad arguments for generate_helas_diagrams in LoopHelasMatrixElement"
800
801 structures = amplitude.get('structure_repository')
802
803 process = amplitude.get('process')
804 has_born = amplitude.get('has_born')
805
806 model = process.get('model')
807
808
809
810 self.sort_split_orders(self.get('processes')[0].get('split_orders'))
811
812
813
814
815
816
817
818 amplitude.order_diagrams_according_to_split_orders(\
819 self.get('processes')[0].get('split_orders'))
820
821
822 wavefunctions = []
823
824
825
826
827
828
829
830
831 structID_to_infos = {}
832
833
834
835 wf_mother_arrays = []
836
837 wf_number = 0
838
839
840 external_wavefunctions = dict([(leg.get('number'),
841 helas_objects.HelasWavefunction(\
842 leg, 0, model, decay_ids)) \
843 for leg in process.get('legs')])
844
845
846
847 external_loop_wfs_dict={}
848
849
850
851 for key in external_wavefunctions.keys():
852 wf = external_wavefunctions[key]
853 if wf.is_boson() and wf.get('state') == 'initial' and \
854 not wf.get('self_antipart'):
855 wf.set('is_part', not wf.get('is_part'))
856
857
858
859 for key in external_wavefunctions.keys():
860 wf = external_wavefunctions[key]
861 if wf.get('leg_state') == False and \
862 not wf.get('self_antipart'):
863 wf.flip_part_antipart()
864
865
866 wf_number = len(process.get('legs'))
867
868
869
870 helas_diagrams = helas_objects.HelasDiagramList()
871
872
873 amplitude_number = 0
874 diagram_number = 0
875
876 def process_born_diagram(diagram, wfNumber, amplitudeNumber, UVCTdiag=False):
877 """ Helper function to process a born diagrams exactly as it is done in
878 HelasMatrixElement for tree-level diagrams. This routine can also
879 process LoopUVCTDiagrams, and if so the argument UVCTdiag must be set
880 to true"""
881
882
883
884
885 number_to_wavefunctions = [{}]
886
887
888 color_lists = [[]]
889
890
891 diagram_wavefunctions = helas_objects.HelasWavefunctionList()
892
893 vertices = copy.copy(diagram.get('vertices'))
894
895
896 lastvx = vertices.pop()
897
898
899
900 for vertex in vertices:
901
902
903
904
905
906
907
908
909
910
911 new_number_to_wavefunctions = []
912 new_color_lists = []
913 for number_wf_dict, color_list in zip(number_to_wavefunctions,
914 color_lists):
915 legs = copy.copy(vertex.get('legs'))
916 last_leg = legs.pop()
917
918 mothers = self.getmothers(legs, number_wf_dict,
919 external_wavefunctions,
920 wavefunctions,
921 diagram_wavefunctions)
922 inter = model.get('interaction_dict')[vertex.get('id')]
923
924
925
926
927 done_color = {}
928 for coupl_key in sorted(inter.get('couplings').keys()):
929 color = coupl_key[0]
930 if color in done_color:
931 wf = done_color[color]
932 wf.get('coupling').append(inter.get('couplings')[coupl_key])
933 wf.get('lorentz').append(inter.get('lorentz')[coupl_key[1]])
934 continue
935 wf = helas_objects.HelasWavefunction(last_leg, \
936 vertex.get('id'), model)
937 wf.set('coupling', [inter.get('couplings')[coupl_key]])
938 if inter.get('color'):
939 wf.set('inter_color', inter.get('color')[coupl_key[0]])
940 done_color[color] = wf
941 wf.set('lorentz', [inter.get('lorentz')[coupl_key[1]]])
942 wf.set('color_key', color)
943 wf.set('mothers',mothers)
944
945
946
947 wf.set_state_and_particle(model)
948
949
950
951
952 wf, wfNumber = wf.check_and_fix_fermion_flow(\
953 wavefunctions,
954 diagram_wavefunctions,
955 external_wavefunctions,
956 wfNumber)
957
958 new_number_wf_dict = copy.copy(number_wf_dict)
959
960 try:
961 wf = diagram_wavefunctions[\
962 diagram_wavefunctions.index(wf)]
963 except ValueError:
964
965 wfNumber = wfNumber + 1
966 wf.set('number', wfNumber)
967 try:
968
969
970 wf = wavefunctions[wf_mother_arrays.index(\
971 wf.to_array())]
972
973
974 wfNumber = wfNumber - 1
975 except ValueError:
976 diagram_wavefunctions.append(wf)
977
978 new_number_wf_dict[last_leg.get('number')] = wf
979
980
981 new_number_to_wavefunctions.append(\
982 new_number_wf_dict)
983
984 new_color_list = copy.copy(color_list)
985 new_color_list.append(coupl_key[0])
986 new_color_lists.append(new_color_list)
987
988 number_to_wavefunctions = new_number_to_wavefunctions
989 color_lists = new_color_lists
990
991
992
993 if not UVCTdiag:
994 helas_diagram = helas_objects.HelasDiagram()
995 else:
996 helas_diagram = LoopHelasDiagram()
997
998 for number_wf_dict, color_list in zip(number_to_wavefunctions,
999 color_lists):
1000
1001
1002 if lastvx.get('id'):
1003 inter = model.get_interaction(lastvx.get('id'))
1004 keys = sorted(inter.get('couplings').keys())
1005 pdg_codes = [p.get_pdg_code() for p in \
1006 inter.get('particles')]
1007 else:
1008
1009
1010 inter = None
1011 keys = [(0, 0)]
1012 pdg_codes = None
1013
1014
1015 legs = lastvx.get('legs')
1016 mothers = self.getmothers(legs, number_wf_dict,
1017 external_wavefunctions,
1018 wavefunctions,
1019 diagram_wavefunctions).\
1020 sort_by_pdg_codes(pdg_codes, 0)[0]
1021
1022
1023 wfNumber = mothers.check_and_fix_fermion_flow(wavefunctions,
1024 diagram_wavefunctions,
1025 external_wavefunctions,
1026 None,
1027 wfNumber,
1028 False,
1029 number_to_wavefunctions)
1030 done_color = {}
1031 for i, coupl_key in enumerate(keys):
1032 color = coupl_key[0]
1033 if inter and color in done_color.keys():
1034 amp = done_color[color]
1035 amp.get('coupling').append(inter.get('couplings')[coupl_key])
1036 amp.get('lorentz').append(inter.get('lorentz')[coupl_key[1]])
1037 continue
1038 if not UVCTdiag:
1039 amp = helas_objects.HelasAmplitude(lastvx, model)
1040 else:
1041 amp = LoopHelasUVCTAmplitude(lastvx, model)
1042 amp.set('UVCT_orders',diagram.get('UVCT_orders'))
1043 amp.set('UVCT_couplings',diagram.get('UVCT_couplings'))
1044 amp.set('type',diagram.get('type'))
1045 if inter:
1046 amp.set('coupling', [inter.get('couplings')[coupl_key]])
1047 amp.set('lorentz', [inter.get('lorentz')[coupl_key[1]]])
1048 if inter.get('color'):
1049 amp.set('inter_color', inter.get('color')[color])
1050 amp.set('color_key', color)
1051 done_color[color] = amp
1052 amp.set('mothers', mothers)
1053 amplitudeNumber = amplitudeNumber + 1
1054 amp.set('number', amplitudeNumber)
1055
1056 new_color_list = copy.copy(color_list)
1057 if inter:
1058 new_color_list.append(color)
1059
1060 amp.set('color_indices', new_color_list)
1061
1062
1063 helas_diagram.get('amplitudes').append(amp)
1064
1065
1066
1067 helas_diagram.set('wavefunctions', diagram_wavefunctions)
1068
1069
1070 diagram_wavefunctions.sort(lambda wf1, wf2: \
1071 wf1.get('number') - wf2.get('number'))
1072
1073 if optimization:
1074 wavefunctions.extend(diagram_wavefunctions)
1075 wf_mother_arrays.extend([wf.to_array() for wf \
1076 in diagram_wavefunctions])
1077 else:
1078 wfNumber = len(process.get('legs'))
1079 if self.optimized_output:
1080
1081
1082 wfNumber = wfNumber+1
1083
1084
1085 return helas_diagram, wfNumber, amplitudeNumber
1086
1087 def process_struct(sID, diag_wfs, wfNumber):
1088 """ Scan a structure, create the necessary wavefunctions, add them
1089 to the diagram wavefunctions list, and return a list of bridge
1090 wavefunctions (i.e. those attached to the loop) with a list, ordered
1091 in the same way, of color lists. Each element of these lists
1092 correspond to one choice of color-lorentz structure of this
1093 tree-structure #sID. """
1094
1095
1096
1097
1098 number_to_wavefunctions = [{}]
1099
1100
1101 color_lists = [[]]
1102
1103
1104 bridge_wfs = helas_objects.HelasWavefunctionList()
1105
1106 vertices = copy.copy(structures[sID].get('vertices'))
1107
1108
1109
1110 if len(vertices)==0:
1111 binding_leg=copy.copy(structures[sID]['binding_leg'])
1112 binding_wf = self.getmothers(base_objects.LegList([binding_leg,]),
1113 {},
1114 external_wavefunctions,
1115 wavefunctions,
1116 diag_wfs)
1117
1118
1119 return [(binding_wf[0],[])] ,wfNumber
1120
1121
1122
1123 for i, vertex in enumerate(vertices):
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134 new_number_to_wavefunctions = []
1135 new_color_lists = []
1136 for number_wf_dict, color_list in zip(number_to_wavefunctions,
1137 color_lists):
1138 legs = copy.copy(vertex.get('legs'))
1139 last_leg = legs.pop()
1140
1141 mothers = self.getmothers(legs, number_wf_dict,
1142 external_wavefunctions,
1143 wavefunctions,
1144 diag_wfs)
1145 inter = model.get('interaction_dict')[vertex.get('id')]
1146
1147
1148
1149
1150
1151 grouped_interaction_keys = {}
1152 colors_order = []
1153 for coupl_key in sorted(inter.get('couplings').keys()):
1154 color = coupl_key[0]
1155 if color not in colors_order:
1156 colors_order.append(color)
1157 grouped_interaction_keys[color] = \
1158 (coupl_key, [inter.get('couplings')[coupl_key]], [inter.get('lorentz')[coupl_key[1]]])
1159 else:
1160 grouped_interaction_keys[color][1].append(inter.get('couplings')[coupl_key])
1161 grouped_interaction_keys[color][2].append(inter.get('lorentz')[coupl_key[1]])
1162
1163 for coupl_key, all_couplings, all_lorentz in [grouped_interaction_keys[color] for color in colors_order]:
1164 color = coupl_key[0]
1165 wf = helas_objects.HelasWavefunction(last_leg, vertex.get('id'), model)
1166 wf.set('coupling', all_couplings)
1167 if inter.get('color'):
1168 wf.set('inter_color', inter.get('color')[coupl_key[0]])
1169 wf.set('lorentz', all_lorentz)
1170 wf.set('color_key', color)
1171 wf.set('mothers',mothers)
1172
1173
1174
1175
1176
1177
1178
1179
1180 wf.set_state_and_particle(model)
1181
1182
1183
1184 wf, wfNumber = wf.check_and_fix_fermion_flow(\
1185 wavefunctions,
1186 diag_wfs,
1187 external_wavefunctions,
1188 wfNumber)
1189
1190 new_number_wf_dict = copy.copy(number_wf_dict)
1191
1192
1193 try:
1194 wf = diag_wfs[\
1195 diag_wfs.index(wf)]
1196 except ValueError:
1197
1198 wfNumber = wfNumber + 1
1199 wf.set('number', wfNumber)
1200 try:
1201
1202
1203 wf = wavefunctions[wf_mother_arrays.index(wf.to_array())]
1204
1205
1206 wfNumber = wfNumber - 1
1207 except ValueError:
1208 diag_wfs.append(wf)
1209
1210 new_number_wf_dict[last_leg.get('number')] = wf
1211 if i==(len(vertices)-1):
1212
1213
1214 bridge_wfs.append(wf)
1215
1216 new_number_to_wavefunctions.append(\
1217 new_number_wf_dict)
1218
1219 new_color_list = copy.copy(color_list)
1220 new_color_list.append(coupl_key[0])
1221 new_color_lists.append(new_color_list)
1222
1223
1224 number_to_wavefunctions = new_number_to_wavefunctions
1225 color_lists = new_color_lists
1226
1227
1228
1229
1230
1231 return zip(bridge_wfs, color_lists), wfNumber
1232
1233 def getloopmothers(loopWfsIn, structIDs, color_list, diag_wfs, wfNumber):
1234 """From the incoming loop leg(s) and the list of structures IDs
1235 connected to the loop at this point, it generates the list of
1236 mothers, a list of colorlist and a number_to_wavefunctions
1237 dictionary list for which each element correspond to one
1238 lorentz-color structure of the tree-structure attached to the loop.
1239 It will launch the reconstruction procedure of the structures
1240 which have not been encountered yet."""
1241
1242
1243
1244
1245
1246 mothers_list = [loopWfsIn,]
1247 color_lists = [color_list,]
1248
1249
1250
1251 for sID in structIDs:
1252 try:
1253 struct_infos = structID_to_infos[sID]
1254 except KeyError:
1255
1256
1257 struct_infos, wfNumber = \
1258 process_struct(sID, diag_wfs, wfNumber)
1259
1260
1261
1262
1263
1264 if optimization and False:
1265
1266
1267
1268
1269 structID_to_infos[sID]=copy.copy(struct_infos)
1270
1271
1272 new_mothers_list = []
1273 new_color_lists = []
1274 for mothers, orig_color_list in zip(mothers_list, color_lists):
1275 for struct_wf, struct_color_list in struct_infos:
1276 new_color_list = copy.copy(orig_color_list)+\
1277 copy.copy(struct_color_list)
1278 new_mothers = copy.copy(mothers)
1279 new_mothers.append(struct_wf)
1280 new_color_lists.append(new_color_list)
1281 new_mothers_list.append(new_mothers)
1282 mothers_list = new_mothers_list
1283 color_lists = new_color_lists
1284
1285
1286
1287
1288
1289
1290 return (mothers_list, color_lists), wfNumber
1291
1292 def process_loop_diagram(diagram, wavefunctionNumber, amplitudeNumber):
1293 """ Helper function to process a the loop diagrams which features
1294 several different aspects compared to the tree born diagrams."""
1295
1296
1297 helas_diagram = LoopHelasDiagram()
1298
1299
1300
1301
1302
1303
1304
1305 last_loop_wfs = helas_objects.HelasWavefunctionList()
1306
1307
1308 color_lists = [[]]
1309
1310
1311 diagram_wavefunctions = helas_objects.HelasWavefunctionList()
1312
1313
1314
1315
1316 tag = copy.deepcopy(diagram.get('tag'))
1317 loop_vertices = copy.deepcopy(diagram.get('vertices'))
1318 for i in range(len(tag)):
1319 tag[i][2]=loop_vertices[i]
1320
1321
1322 ct_vertices = copy.copy(diagram.get('CT_vertices'))
1323
1324
1325 external_loop_wf=helas_objects.HelasWavefunction(\
1326 tag[0][0], 0, model, decay_ids)
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336 if not self.optimized_output:
1337 wavefunctionNumber=wavefunctionNumber+1
1338 external_loop_wf.set('number',wavefunctionNumber)
1339 diagram_wavefunctions.append(external_loop_wf)
1340 else:
1341 try:
1342 external_loop_wf=\
1343 external_loop_wfs_dict[external_loop_wf.get('pdg_code')]
1344 except KeyError:
1345 wavefunctionNumber=wavefunctionNumber+1
1346 external_loop_wf.set('number',wavefunctionNumber)
1347 external_loop_wfs_dict[external_loop_wf.get('pdg_code')]=\
1348 external_loop_wf
1349 diagram_wavefunctions.append(external_loop_wf)
1350
1351
1352 last_loop_wfs.append(external_loop_wf)
1353
1354 def process_tag_elem(tagElem, wfNumber, lastloopwfs, colorlists):
1355 """Treat one tag element of the loop diagram (not the last one
1356 which provides an amplitude)"""
1357
1358
1359
1360
1361
1362 new_color_lists = []
1363 new_last_loop_wfs = helas_objects.HelasWavefunctionList()
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374 vertex=tagElem[2]
1375 structIDs=tagElem[1]
1376 for last_loop_wf, color_list in zip(lastloopwfs,
1377 colorlists):
1378 loopLegOut = copy.copy(vertex.get('legs')[-1])
1379
1380
1381
1382
1383
1384
1385 (motherslist, colorlists), wfNumber = \
1386 getloopmothers(\
1387 helas_objects.HelasWavefunctionList([last_loop_wf,]),
1388 structIDs,\
1389 color_list, diagram_wavefunctions, wfNumber)
1390 inter = model.get('interaction_dict')[vertex.get('id')]
1391
1392
1393
1394 for mothers, structcolorlist in zip(motherslist, colorlists):
1395
1396 done_color = {}
1397 for coupl_key in sorted(inter.get('couplings').keys()):
1398 color = coupl_key[0]
1399 if color in done_color:
1400 wf = done_color[color]
1401 wf.get('coupling').append(inter.get('couplings')[coupl_key])
1402 wf.get('lorentz').append(inter.get('lorentz')[coupl_key[1]])
1403 continue
1404 wf = helas_objects.HelasWavefunction(loopLegOut, \
1405 vertex.get('id'), model)
1406 wf.set('coupling', [inter.get('couplings')[coupl_key]])
1407 if inter.get('color'):
1408 wf.set('inter_color', inter.get('color')[coupl_key[0]])
1409 done_color[color] = wf
1410 wf.set('lorentz', [inter.get('lorentz')[coupl_key[1]]])
1411 wf.set('color_key', color)
1412 wf.set('mothers',mothers)
1413
1414
1415
1416 wf.set_state_and_particle(model)
1417
1418
1419
1420 wf, wfNumber = wf.check_and_fix_fermion_flow(\
1421 wavefunctions,
1422 diagram_wavefunctions,
1423 external_wavefunctions,
1424 wfNumber)
1425
1426
1427 try:
1428 wf = diagram_wavefunctions[\
1429 diagram_wavefunctions.index(wf)]
1430 except ValueError:
1431
1432 wfNumber = wfNumber + 1
1433 wf.set('number', wfNumber)
1434
1435
1436
1437 try:
1438 if not self.optimized_output:
1439 raise ValueError
1440
1441
1442 wf = wavefunctions[wf_mother_arrays.index(\
1443 wf.to_array())]
1444
1445
1446 wfNumber = wfNumber - 1
1447
1448
1449 self.lwf_reused += 1
1450 except ValueError:
1451 diagram_wavefunctions.append(wf)
1452
1453
1454
1455 new_last_loop_wfs.append(wf)
1456
1457 new_color_list = copy.copy(structcolorlist)
1458 new_color_list.append(coupl_key[0])
1459 new_color_lists.append(new_color_list)
1460
1461
1462
1463
1464 return wfNumber, new_last_loop_wfs, new_color_lists
1465
1466
1467
1468
1469
1470 def create_amplitudes(lastvx, wfNumber, amplitudeNumber):
1471 """Treat the last tag element of the loop diagram (which
1472 provides an amplitude)"""
1473
1474
1475
1476
1477
1478
1479 other_external_loop_wf=helas_objects.HelasWavefunction()
1480
1481 for leg in [leg for leg in lastvx['legs'] if leg['loop_line']]:
1482 if last_loop_wfs[0]['number_external']!=leg['number']:
1483 other_external_loop_wf=\
1484 helas_objects.HelasWavefunction(leg, 0, model, decay_ids)
1485
1486 break
1487
1488
1489 for last_loop_wf, color_list in zip(last_loop_wfs,color_lists):
1490
1491 if lastvx.get('id')!=-1:
1492 raise self.PhysicsObjectError, \
1493 "The amplitude vertex of a loop diagram must be a "+\
1494 "two point vertex with id=-1"
1495
1496
1497 if other_external_loop_wf.is_majorana():
1498 fix_lcut_majorana_fermion_flow(last_loop_wf,\
1499 other_external_loop_wf)
1500
1501 mothers=helas_objects.HelasWavefunctionList(\
1502 [last_loop_wf,other_external_loop_wf])
1503 wfNumber = mothers.check_and_fix_fermion_flow(wavefunctions,
1504 diagram_wavefunctions,
1505 external_wavefunctions,
1506 None,
1507 wfNumber,
1508 False,
1509 [])
1510 amp = helas_objects.HelasAmplitude(lastvx, model)
1511 amp.set('interaction_id',-1)
1512 amp.set('mothers',mothers)
1513
1514
1515 amp.set('pdg_codes',[last_loop_wf.get_pdg_code(),
1516 other_external_loop_wf.get_pdg_code()])
1517
1518
1519
1520
1521
1522 amp.set('color_indices', copy.copy(color_list))
1523
1524
1525 amplitudeNumber = amplitudeNumber + 1
1526 amp.set('number', amplitudeNumber)
1527 amp.set('type','loop')
1528 loop_amp = LoopHelasAmplitude()
1529 loop_amp.set('amplitudes',\
1530 helas_objects.HelasAmplitudeList([amp,]))
1531
1532
1533
1534
1535 loop_amp_wfs=helas_objects.HelasWavefunctionList(\
1536 [last_loop_wf,])
1537 while loop_amp_wfs[-1].get('mothers'):
1538 loop_amp_wfs.append([lwf for lwf in \
1539 loop_amp_wfs[-1].get('mothers') if lwf['is_loop']][0])
1540
1541
1542
1543
1544
1545 loop_amp_wfs.append(other_external_loop_wf)
1546
1547
1548 loop_amp_wfs.reverse()
1549 loop_amp.set('wavefunctions',loop_amp_wfs)
1550 loop_amp.set('type',diagram.get('type'))
1551 loop_amp.set('multiplier',diagram.get('multiplier'))
1552
1553 loop_amp.set('number',min([amp.get('number') for amp
1554 in loop_amp.get('amplitudes')]))
1555 loop_amp.set('coupling',loop_amp.get_couplings())
1556 loop_amp.set('orders',loop_amp.get_orders())
1557 helas_diagram.get('amplitudes').append(loop_amp)
1558
1559
1560 check_lcut_fermion_flow_consistency(\
1561 loop_amp_wfs[0],loop_amp_wfs[1])
1562 return wfNumber, amplitudeNumber
1563
1564 def check_lcut_fermion_flow_consistency(lcut_wf1, lcut_wf2):
1565 """Checks that the two L-cut loop helas wavefunctions have
1566 a consistent fermion flow."""
1567 if lcut_wf1.is_boson():
1568 if lcut_wf1.get('state')!='final' or\
1569 lcut_wf2.get('state')!='final':
1570 raise MadGraph5Error,\
1571 "Inconsistent flow in L-cut bosons."
1572 elif not lcut_wf1.is_majorana():
1573 for lcut_wf in [lcut_wf1,lcut_wf2]:
1574 if not ((lcut_wf.get('is_part') and \
1575 lcut_wf.get('state')=='outgoing') or\
1576 (not lcut_wf.get('is_part') and\
1577 lcut_wf.get('state')=='incoming')):
1578 raise MadGraph5Error,\
1579 "Inconsistent flow in L-cut Dirac fermions."
1580 elif lcut_wf1.is_majorana():
1581 if (lcut_wf1.get('state'), lcut_wf2.get('state')) not in \
1582 [('incoming','outgoing'),('outgoing','incoming')]:
1583 raise MadGraph5Error,\
1584 "Inconsistent flow in L-cut Majorana fermions."
1585
1586 def fix_lcut_majorana_fermion_flow(last_loop_wf,\
1587 other_external_loop_wf):
1588 """Fix the fermion flow of the last external Majorana loop
1589 wavefunction through the fermion flow of the first external
1590 Majorana loop wavefunction."""
1591
1592
1593 loop_amp_wfs=helas_objects.HelasWavefunctionList(\
1594 [last_loop_wf,])
1595 while loop_amp_wfs[-1].get('mothers'):
1596 loop_amp_wfs.append([lwf for lwf in \
1597 loop_amp_wfs[-1].get('mothers') if lwf['is_loop']][0])
1598 loop_amp_wfs.append(other_external_loop_wf)
1599 loop_amp_wfs.reverse()
1600
1601
1602 rep={'incoming':'outgoing','outgoing':'incoming'}
1603
1604 other_external_loop_wf['state']=rep[loop_amp_wfs[1]['state']]
1605 return
1606
1607 def process_counterterms(ct_vertices, wfNumber, amplitudeNumber):
1608 """Process the counterterms vertices defined in this loop
1609 diagram."""
1610
1611 structIDs=[]
1612 for tagElem in tag:
1613 structIDs += tagElem[1]
1614
1615
1616
1617
1618 (motherslist, colorlists), wfNumber = getloopmothers(\
1619 helas_objects.HelasWavefunctionList(), structIDs, \
1620 [], diagram_wavefunctions, wfNumber)
1621 for mothers, structcolorlist in zip(motherslist, colorlists):
1622 for ct_vertex in ct_vertices:
1623
1624 inter = model.get_interaction(ct_vertex.get('id'))
1625 keys = inter.get_canonical_couplings_keys_order()
1626 pdg_codes = [p.get_pdg_code() for p in \
1627 inter.get('particles')]
1628 mothers = mothers.sort_by_pdg_codes(pdg_codes, 0)[0]
1629
1630
1631 wfNumber = mothers.check_and_fix_fermion_flow(wavefunctions,
1632 diagram_wavefunctions,
1633 external_wavefunctions,
1634 None,
1635 wfNumber,
1636 False,
1637 [])
1638 done_color = {}
1639 for i, coupl_key in enumerate(keys):
1640 color = coupl_key[0]
1641 if color in done_color.keys():
1642 amp = done_color[color]
1643 amp.get('coupling').append(inter.get('couplings')[coupl_key])
1644 amp.get('lorentz').append(inter.get('lorentz')[coupl_key[1]])
1645 continue
1646 amp = helas_objects.HelasAmplitude(ct_vertex, model)
1647 amp.set('coupling', [inter.get('couplings')[coupl_key]])
1648 amp.set('lorentz', [inter.get('lorentz')[coupl_key[1]]])
1649 if inter.get('color'):
1650 amp.set('inter_color', inter.get('color')[color])
1651 amp.set('color_key', color)
1652 done_color[color] = amp
1653 amp.set('mothers', mothers)
1654 amplitudeNumber = amplitudeNumber + 1
1655 amp.set('number', amplitudeNumber)
1656
1657 amp_color_list = copy.copy(structcolorlist)
1658 amp_color_list.append(color)
1659 amp.set('color_indices', amp_color_list)
1660 amp.set('type',inter.get('type'))
1661
1662
1663 helas_diagram.get('amplitudes').append(amp)
1664 return wfNumber, amplitudeNumber
1665
1666 for tagElem in tag:
1667 wavefunctionNumber, last_loop_wfs, color_lists = \
1668 process_tag_elem(tagElem, wavefunctionNumber, \
1669 last_loop_wfs, color_lists)
1670
1671
1672
1673 wavefunctionNumber, amplitudeNumber = create_amplitudes(
1674 loop_vertices[-1], wavefunctionNumber, amplitudeNumber)
1675
1676
1677 if ct_vertices:
1678 wavefunctionNumber, amplitudeNumber = process_counterterms(\
1679 ct_vertices, wavefunctionNumber, amplitudeNumber)
1680
1681
1682
1683 struct_wfs=helas_objects.HelasWavefunctionList(\
1684 [wf for wf in diagram_wavefunctions if not wf['is_loop']])
1685 loop_wfs=helas_objects.HelasWavefunctionList(\
1686 [wf for wf in diagram_wavefunctions if wf['is_loop']])
1687
1688
1689 struct_wfs.sort(lambda wf1, wf2: \
1690 wf1.get('number') - wf2.get('number'))
1691
1692
1693
1694 helas_diagram.set('wavefunctions', struct_wfs)
1695
1696
1697
1698
1699 if optimization:
1700 wavefunctions.extend(struct_wfs)
1701 wf_mother_arrays.extend([wf.to_array() for wf in struct_wfs])
1702 if self.optimized_output:
1703 wavefunctions.extend(loop_wfs)
1704 wf_mother_arrays.extend([wf.to_array() for wf in loop_wfs])
1705 else:
1706 wavefunctionNumber = len(process.get('legs'))
1707 if self.optimized_output:
1708
1709
1710 wavefunctionNumber = wavefunctionNumber+1
1711
1712
1713
1714
1715
1716
1717 if self.optimized_output:
1718 loop_wfs = helas_objects.HelasWavefunctionList(
1719 [lwf for lwf in loop_wfs if len(lwf.get('mothers'))>0])
1720 helas_diagram.set('loop_wavefunctions',loop_wfs)
1721
1722
1723 return helas_diagram, wavefunctionNumber, amplitudeNumber
1724
1725
1726 if has_born:
1727 for diagram in amplitude.get('born_diagrams'):
1728 helBornDiag, wf_number, amplitude_number=\
1729 process_born_diagram(diagram, wf_number, amplitude_number)
1730 diagram_number = diagram_number + 1
1731 helBornDiag.set('number', diagram_number)
1732 helas_diagrams.append(helBornDiag)
1733
1734
1735 self.lwf_reused=0
1736 for diagram in amplitude.get('loop_diagrams'):
1737 loopHelDiag, wf_number, amplitude_number=\
1738 process_loop_diagram(diagram, wf_number, amplitude_number)
1739 diagram_number = diagram_number + 1
1740 loopHelDiag.set('number', diagram_number)
1741 helas_diagrams.append(loopHelDiag)
1742
1743
1744 for diagram in amplitude.get('loop_UVCT_diagrams'):
1745 loopHelDiag, wf_number, amplitude_number=\
1746 process_born_diagram(diagram, wf_number, amplitude_number, \
1747 UVCTdiag=True)
1748 diagram_number = diagram_number + 1
1749 loopHelDiag.set('number', diagram_number)
1750
1751
1752 for lamp in loopHelDiag.get_loop_UVCTamplitudes():
1753 new_orders = copy.copy(lamp.get('orders'))
1754 for order, value in lamp.get('UVCT_orders').items():
1755 try:
1756 new_orders[order] = new_orders[order] + value
1757 except KeyError:
1758 new_orders[order] = value
1759 lamp.set('orders', new_orders)
1760 helas_diagrams.append(loopHelDiag)
1761
1762 self.set('diagrams', helas_diagrams)
1763
1764 if __debug__:
1765 for diag in self.get('diagrams'):
1766
1767
1768
1769
1770 diag.get('wavefunctions').check_wavefunction_numbers_order()
1771
1772
1773 if self.optimized_output:
1774 logger.debug('%d loop wavefunctions have been reused'%self.lwf_reused+
1775 ', for a total of %d ones'%sum([len(ldiag.get('loop_wavefunctions'))
1776 for ldiag in self.get_loop_diagrams()]))
1777
1778
1779 for wf in self.get_all_wavefunctions():
1780 wf.set('mothers', helas_objects.HelasMatrixElement.sorted_mothers(wf))
1781
1782 for amp in self.get_all_amplitudes():
1783 amp.set('mothers', helas_objects.HelasMatrixElement.sorted_mothers(amp))
1784
1785
1786
1787 gen_colors = amp.get('color_indices')
1788 amp.set('color_indices', amp.get_color_indices())
1789 if isinstance(amp,LoopHelasAmplitude):
1790 assert (amp.get('color_indices')==gen_colors), \
1791 "Error in the treatment of color in the loop helas diagram "+\
1792 "generation. It could be harmless, but report this bug to be sure."+\
1793 " The different keys are %s vs %s."%(str(gen_colors),\
1794 str(amp.get('color_indices')))
1795 for loopdiag in self.get_loop_diagrams():
1796 for loopamp in loopdiag.get_loop_amplitudes():
1797 loopamp.set_mothers_and_pairing()
1798
1799
1800
1801
1802
1803
1804
1805
1806
1808 """This function returns a list and a dictionary:
1809 squared_orders, amps_orders
1810 ===
1811 The squared_orders lists all contributing squared_orders as tuple whose
1812 elements are the power at which are elevated the couplings orderered as
1813 in the 'split_orders'.
1814
1815 squared_orders : All possible contributing squared orders among those
1816 specified in the process['split_orders'] argument. The elements of
1817 the list are tuples of the format
1818 ((OrderValue1,OrderValue2,...),
1819 (max_contrib_ct_amp_number,
1820 max_contrib_uvct_amp_number,
1821 max_contrib_loop_amp_number,
1822 max_contrib_group_id))
1823 with OrderValue<i> correspond to the value of the <i>th order in
1824 process['split_orders'] (the others are summed over and therefore
1825 left unspecified).
1826 Ex for dijet with process['split_orders']=['QCD','QED']:
1827 => [((4,0),(8,2,3)),((2,2),(10,3,3)),((0,4),(20,5,4))]
1828
1829 'max_contrib_loop_amp_number': For optimization purposes, it is good to
1830 know what is the maximum loop amplitude number contributing to any given
1831 squared order. The fortran output is structured so that if the user
1832 is interested in a given squared order contribution only, then
1833 all the open loop coefficients for the amplitudes with a number above
1834 this value can be skipped.
1835
1836 'max_contrib_(uv)ct_amp_number': Same as above but for the
1837 (uv)ctamplitude number.
1838
1839 'max_contrib_group_id': The same as above, except this time
1840 it is for the loop group id used for the loop reduction.
1841 ===
1842 The amps_orders is a *dictionary* with keys
1843 'born_amp_orders',
1844 'loop_amp_orders'
1845 with values being the tuples described below.
1846
1847 If process['split_orders'] is empty, all these tuples are set empty.
1848
1849 'born_amp_orders' : Exactly as for squared order except that this list specifies
1850 the contributing order values for the amplitude (i.e. not 'squared').
1851 Also, the tuple describing the amplitude order is nested with a
1852 second one listing all amplitude numbers contributing to this order.
1853 Ex for dijet with process['split_orders']=['QCD','QED']:
1854 => [((2, 0), (2,)), ((0, 2), (1, 3, 4))]
1855 The function returns () if the process has no borns.
1856
1857 'loop_amp_orders' : The same as for born_amp_orders but for the loop
1858 type of amplitudes only.
1859
1860 Keep in mind that the orders of the elements of the outter most list is
1861 important as it dictates the order for the corresponding "order indices"
1862 in the fortran code output by the exporters.
1863 """
1864
1865 split_orders=self.get('processes')[0].get('split_orders')
1866
1867 amps_orders = {'born_amp_orders':[],
1868 'loop_amp_orders':[]}
1869 if len(split_orders)==0:
1870 self.squared_orders = []
1871 return [],amps_orders
1872
1873
1874
1875 self.sort_split_orders(split_orders)
1876
1877 process = self.get('processes')[0]
1878
1879
1880 self.sort_split_orders(split_orders)
1881 loop_amp_orders = self.get_split_orders_mapping_for_diagram_list(\
1882 self.get_loop_diagrams(), split_orders,
1883 get_amplitudes_function = lambda diag: diag.get_loop_amplitudes(),
1884
1885
1886
1887 get_amp_number_function = lambda amp:
1888 (amp.get('amplitudes')[0].get('number'),amp.get('loop_group_id')))
1889 ct_amp_orders = self.get_split_orders_mapping_for_diagram_list(\
1890 self.get_loop_diagrams(), split_orders,
1891 get_amplitudes_function = lambda diag: diag.get_ct_amplitudes())
1892 uvct_amp_orders = self.get_split_orders_mapping_for_diagram_list(\
1893 self.get_loop_UVCT_diagrams(), split_orders)
1894
1895
1896
1897
1898 amps_orders['loop_amp_orders'] = dict([(lao[0],
1899 [el[0] for el in lao[1]]) for lao in loop_amp_orders])
1900
1901 for ct_amp_order in ct_amp_orders+uvct_amp_orders:
1902 try:
1903 amps_orders['loop_amp_orders'][ct_amp_order[0]].extend(\
1904 list(ct_amp_order[1]))
1905 except KeyError:
1906 amps_orders['loop_amp_orders'][ct_amp_order[0]] = \
1907 list(ct_amp_order[1])
1908
1909 amps_orders['loop_amp_orders'] = [
1910 (key, tuple(sorted(amps_orders['loop_amp_orders'][key])))
1911 for key in amps_orders['loop_amp_orders'].keys()]
1912
1913 order_hierarchy = self.get('processes')[0]\
1914 .get('model').get('order_hierarchy')
1915 if set(order_hierarchy.keys()).union(set(split_orders))==\
1916 set(order_hierarchy.keys()):
1917 amps_orders['loop_amp_orders'].sort(key= lambda so:
1918 sum([order_hierarchy[split_orders[i]]*order_power for \
1919 i, order_power in enumerate(so[0])]))
1920
1921
1922 if process.get('has_born'):
1923 born_amp_orders = self.get_split_orders_mapping_for_diagram_list(\
1924 self.get_born_diagrams(),split_orders)
1925
1926 amps_orders['born_amp_orders'] = born_amp_orders
1927
1928
1929
1930
1931
1932 loop_orders = [(lso[0],tuple(zip(*list(lso[1])))) for lso in loop_amp_orders]
1933
1934
1935
1936 if process.get('has_born'):
1937 ref_orders = [bao[0] for bao in born_amp_orders]
1938 else:
1939 ref_orders = [lao[0] for lao in loop_orders+ct_amp_orders]
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951 def smax(AmpNumList):
1952 return -1 if len(AmpNumList)==0 else max(AmpNumList)
1953
1954 squared_orders = {}
1955 for ref_order in ref_orders:
1956 for uvct_order in uvct_amp_orders:
1957 key = tuple([ord1 + ord2 for ord1,ord2 in zip(uvct_order[0],
1958 ref_order)])
1959 try:
1960
1961 squared_orders[key][0] = smax([squared_orders[key][0]]+
1962 list(uvct_order[1]))
1963 except KeyError:
1964 squared_orders[key] = [smax(list(uvct_order[1])),-1,-1,-1]
1965
1966 for ct_order in ct_amp_orders:
1967 key = tuple([ord1 + ord2 for ord1,ord2 in zip(ct_order[0],
1968 ref_order)])
1969 try:
1970
1971 squared_orders[key][1] = smax([squared_orders[key][1]]+
1972 list(ct_order[1]))
1973 except KeyError:
1974 squared_orders[key] = [-1,smax(list(ct_order[1])),-1,-1]
1975
1976 for loop_order in loop_orders:
1977 key = tuple([ord1 + ord2 for ord1,ord2 in zip(loop_order[0],
1978 ref_order)])
1979 try:
1980
1981 squared_orders[key][2] = smax([squared_orders[key][2]]+
1982 list(loop_order[1][0]))
1983
1984 squared_orders[key][3] = smax([squared_orders[key][3]]+
1985 list(loop_order[1][1]))
1986 except KeyError:
1987 squared_orders[key] = [-1,-1,smax(list(loop_order[1][0])),
1988 smax(list(loop_order[1][1]))]
1989
1990
1991
1992
1993
1994
1995 squared_orders = [(sqso[0],tuple(sqso[1])) for sqso in \
1996 squared_orders.items()]
1997
1998 order_hierarchy = self.get('processes')[0].get('model').get('order_hierarchy')
1999 if set(order_hierarchy.keys()).union(set(split_orders))==\
2000 set(order_hierarchy.keys()):
2001 squared_orders.sort(key= lambda so:
2002 sum([order_hierarchy[split_orders[i]]*order_power for \
2003 i, order_power in enumerate(so[0])]))
2004
2005
2006 self.squared_orders = squared_orders
2007
2008 return squared_orders, amps_orders
2009
2011 """Return the squared_order contributions as returned by the function
2012 get_split_orders_mapping. It uses the cached value self.squared_orders
2013 if it was already defined during a previous call to get_split_orders_mapping.
2014 """
2015
2016 if not hasattr(self, "squared_orders"):
2017 self.get_split_orders_mapping()
2018
2019 return self.squared_orders
2020
2022 """ Find the maximum number of loop couplings appearing in any of the
2023 LoopHelasAmplitude in this LoopHelasMatrixElement"""
2024 if len(self.get_loop_diagrams())==0:
2025 return 0
2026 return max([len(amp.get('coupling')) for amp in \
2027 sum([d.get_loop_amplitudes() for d in self.get_loop_diagrams()],[])])
2028
2030 """ Returns the maximum power of loop momentum brought by a loop
2031 interaction. For renormalizable theories, it should be no more than one.
2032 """
2033 return max([lwf.get_analytic_info('interaction_rank') for lwf in \
2034 self.get_all_loop_wavefunctions()])
2035
2037 """ Returns the rank of the contributing loop with maximum rank """
2038 r_list = [lamp.get_analytic_info('wavefunction_rank') for ldiag in \
2039 self.get_loop_diagrams() for lamp in ldiag.get_loop_amplitudes()]
2040 if len(r_list)==0:
2041 return 0
2042 else:
2043 return max(r_list)
2044
2046 """Returns the maximum spin that any particle either connected to a loop
2047 or running in it has, among all the loops contributing to this ME"""
2048
2049
2050
2051
2052
2053 return max(
2054 max(l.get('spin') for l in lamp.get('mothers')+
2055 lamp.get('wavefunctions')+d.get('loop_wavefunctions'))
2056 for d in self['diagrams'] if isinstance(d,LoopHelasDiagram)
2057 for lamp in d.get_loop_amplitudes()
2058 )
2059
2061 """ Returns the spin of the loop particle with maximum spin among all
2062 the loop contributing to this ME"""
2063 return max([lwf.get('spin') for lwf in \
2064 self.get_all_loop_wavefunctions()])
2065
2067 """Give a unique number to each non-equivalent (at the level of the output)
2068 LoopHelasAmplitude """
2069
2070 LoopHelasAmplitudeRecognized=[]
2071 for lamp in \
2072 sum([d.get_loop_amplitudes() for d in self.get_loop_diagrams()],[]):
2073 lamp.set('number',-1)
2074 for lamp2 in LoopHelasAmplitudeRecognized:
2075 if lamp.is_equivalent(lamp2):
2076
2077
2078 lamp.set('number',lamp2.get('number'))
2079 break;
2080 if lamp.get('number')==-1:
2081 lamp.set('number',(len(LoopHelasAmplitudeRecognized)+1))
2082 LoopHelasAmplitudeRecognized.append(lamp)
2083
2085 """Give a unique number to each LoopHelasAmplitude. These will be the
2086 number used for the LOOPCOEF array in the optimized output and the
2087 grouping is done in a further stage by adding all the LOOPCOEF sharing
2088 the same denominator to a given one using the 'loop_group_id' attribute
2089 of the LoopHelasAmplitudes. """
2090
2091 lamp_number=1
2092 for lamp in \
2093 sum([d.get_loop_amplitudes() for d in self.get_loop_diagrams()],[]):
2094 lamp.set('number',lamp_number)
2095 lamp_number += 1
2096
2098 """ Give the correct number for the default output to the wavefunctions
2099 and amplitudes building the loops """
2100
2101
2102 CT_ampnumber=1
2103 loop_ampnumber=self.get_number_of_CT_amplitudes()+1
2104 loopwfnumber=1
2105
2106 for loopdiag in self.get_loop_diagrams():
2107 for wf in loopdiag.get('wavefunctions'):
2108 wf.set('number',wfnumber)
2109 wfnumber=wfnumber+1
2110 for loopamp in loopdiag.get_loop_amplitudes():
2111 loopwfnumber=1
2112 for loopwf in loopamp['wavefunctions']:
2113 loopwf.set('number',loopwfnumber)
2114 loopwfnumber=loopwfnumber+1
2115 for amp in loopamp['amplitudes']:
2116 amp.set('number',loop_ampnumber)
2117 loop_ampnumber=loop_ampnumber+1
2118 for ctamp in loopdiag.get_ct_amplitudes():
2119 ctamp.set('number',CT_ampnumber)
2120 CT_ampnumber=CT_ampnumber+1
2121
2122 for loopUVCTdiag in self.get_loop_UVCT_diagrams():
2123 for wf in loopUVCTdiag.get('wavefunctions'):
2124 wf.set('number',wfnumber)
2125 wfnumber=wfnumber+1
2126 for amp in loopUVCTdiag.get('amplitudes'):
2127 amp.set('number',CT_ampnumber)
2128 CT_ampnumber=CT_ampnumber+1
2129
2131 """ Give the correct number for the optimized output to the wavefunctions
2132 and amplitudes building the loops """
2133 CT_ampnumber=1
2134 loop_ampnumber=self.get_number_of_CT_amplitudes()+1
2135 loopwfnumber=1
2136
2137 for loopdiag in self.get_loop_diagrams():
2138 for wf in loopdiag.get('wavefunctions'):
2139 wf.set('number',wfnumber)
2140 wfnumber=wfnumber+1
2141 for lwf in loopdiag.get('loop_wavefunctions'):
2142 lwf.set('number',loopwfnumber)
2143 loopwfnumber=loopwfnumber+1
2144 for loopamp in loopdiag.get_loop_amplitudes():
2145
2146
2147 start_loop_wf = loopamp.get_starting_loop_wavefunction()
2148 if start_loop_wf.get('fermionflow')==1:
2149 start_loop_wf.set('number',0)
2150 else:
2151
2152 start_loop_wf.set('number',-1)
2153 for amp in loopamp['amplitudes']:
2154 amp.set('number',loop_ampnumber)
2155 loop_ampnumber=loop_ampnumber+1
2156 for ctamp in loopdiag.get_ct_amplitudes():
2157 ctamp.set('number',CT_ampnumber)
2158 CT_ampnumber=CT_ampnumber+1
2159
2160 for loopUVCTdiag in self.get_loop_UVCT_diagrams():
2161 for wf in loopUVCTdiag.get('wavefunctions'):
2162 wf.set('number',wfnumber)
2163 wfnumber=wfnumber+1
2164 for amp in loopUVCTdiag.get('amplitudes'):
2165 amp.set('number',CT_ampnumber)
2166 CT_ampnumber=CT_ampnumber+1
2167
2169 """After the generation of the helas objects, we can give up on having
2170 a unique number identifying the helas wavefunction and amplitudes and
2171 instead use a labeling which is optimal for the output of the loop process.
2172 Also we tag all the LoopHelasAmplitude which are identical with the same
2173 'number' attribute."""
2174
2175
2176 if self.optimized_output:
2177 self.relabel_loop_amplitudes_optimized()
2178 else:
2179 self.relabel_loop_amplitudes()
2180
2181
2182 wfnumber=1
2183 ampnumber=1
2184 for borndiag in self.get_born_diagrams():
2185 for wf in borndiag.get('wavefunctions'):
2186 wf.set('number',wfnumber)
2187 wfnumber=wfnumber+1
2188 for amp in borndiag.get('amplitudes'):
2189 amp.set('number',ampnumber)
2190 ampnumber=ampnumber+1
2191
2192
2193
2194 if self.optimized_output:
2195 self.relabel_loop_wfs_and_amps_optimized(wfnumber)
2196 for lwf in [lwf for loopdiag in self.get_loop_diagrams() for \
2197 lwf in loopdiag.get('loop_wavefunctions')]:
2198 lwf.set('me_id',lwf.get('number'))
2199 else:
2200 self.relabel_loop_wfs_and_amps(wfnumber)
2201
2202
2203
2204 for wf in self.get_all_wavefunctions():
2205 wf.set('me_id',wf.get('number'))
2206
2207
2209 """Gives the total number of wavefunctions for this ME, including the
2210 loop ones"""
2211
2212 return len(self.get_all_wavefunctions())
2213
2215 """ Gives the total number of loop wavefunctions for this ME."""
2216 return sum([len(ldiag.get('loop_wavefunctions')) for ldiag in \
2217 self.get_loop_diagrams()])
2218
2220 """Gives the total number of wavefunctions for this ME, excluding the
2221 loop ones."""
2222
2223 return sum([ len(d.get('wavefunctions')) for d in self.get('diagrams')])
2224
2226 """Gives a list of all wavefunctions for this ME"""
2227
2228 allwfs=sum([d.get('wavefunctions') for d in self.get('diagrams')], [])
2229 for d in self['diagrams']:
2230 if isinstance(d,LoopHelasDiagram):
2231 for l in d.get_loop_amplitudes():
2232 allwfs += l.get('wavefunctions')
2233
2234 return allwfs
2235
2249
2251 """Gives (number or external particles, number of
2252 incoming particles)"""
2253
2254 external_wfs = filter(lambda wf:
2255 not wf.get('mothers') and not wf.get('is_loop'),
2256 self.get_all_wavefunctions())
2257
2258 return (len(set([wf.get('number_external') for wf in \
2259 external_wfs])),
2260 len(set([wf.get('number_external') for wf in \
2261 filter(lambda wf: wf.get('leg_state') == False,
2262 external_wfs)])))
2263
2265 """Gives the total number of amplitudes for this ME, including the loop
2266 ones."""
2267
2268 return len(self.get_all_amplitudes())
2269
2276
2278 """Gives the total number of amplitudes for this ME, excluding those
2279 inside the loop amplitudes. (So only one is counted per loop amplitude.)
2280 """
2281
2282 return sum([ len(d.get('amplitudes')) for d in \
2283 self.get('diagrams')])
2284
2286 """Gives the total number of helas amplitudes for the loop diagrams of this ME,
2287 excluding those inside the loop amplitudes, but including the CT-terms.
2288 (So only one amplitude is counted per loop amplitude.)
2289 """
2290
2291 return sum([len(d.get('amplitudes')) for d in (self.get_loop_diagrams()+
2292 self.get_loop_UVCT_diagrams())])
2293
2295 """Gives the total number of amplitudes for the born diagrams of this ME
2296 """
2297
2298 return sum([len(d.get('amplitudes')) for d in self.get_born_diagrams()])
2299
2310
2316
2323
2330
2358
2374
2376 """ Returns the list of the helas loop amplitude of type
2377 CALL LOOP_I_J(_K)(...) used for this matrix element """
2378
2379
2380
2381 if self.optimized_output:
2382 last_relevant_index=3
2383 else:
2384 last_relevant_index=4
2385
2386 return list(set([lamp.get_call_key()[1:last_relevant_index] \
2387 for ldiag in self.get_loop_diagrams() for lamp in \
2388 ldiag.get_loop_amplitudes()]))
2389
2399
2409
2411 """ Just to forbid the usage of this generic function in a
2412 LoopHelasMatrixElement"""
2413
2414 raise self.PhysicsObjectError, \
2415 "Usage of get_color_amplitudes is not allowed in a LoopHelasMatrixElement"
2416
2418 """Return a list of (coefficient, amplitude number) lists,
2419 corresponding to the JAMPs for this born color basis and the born
2420 diagrams of this LoopMatrixElement. The coefficients are given in the
2421 format (fermion factor, color coeff (frac), imaginary, Nc power)."""
2422
2423 return super(LoopHelasMatrixElement,self).generate_color_amplitudes(\
2424 self['born_color_basis'],self.get_born_diagrams())
2425
2427 """Return a list of (coefficient, amplitude number) lists,
2428 corresponding to the JAMPs for this loop color basis and the loop
2429 diagrams of this LoopMatrixElement. The coefficients are given in the
2430 format (fermion factor, color coeff (frac), imaginary, Nc power)."""
2431
2432 diagrams=self.get_loop_diagrams()
2433 color_basis=self['loop_color_basis']
2434
2435 if not color_basis:
2436
2437
2438 col_amp = []
2439 for diagram in diagrams:
2440 for amplitude in diagram.get('amplitudes'):
2441 col_amp.append(((amplitude.get('fermionfactor'),
2442 1, False, 0),
2443 amplitude.get('number')))
2444 return [col_amp]
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455 LoopDiagramsHelasAmplitudeList=self.get_helas_amplitudes_loop_diagrams()
2456
2457
2458 for i, helas_amp_list in enumerate(LoopDiagramsHelasAmplitudeList):
2459 new_helas_amp_list=helas_objects.HelasAmplitudeList()
2460 for helas_amp in helas_amp_list:
2461 if isinstance(helas_amp,LoopHelasAmplitude):
2462 new_helas_amp_list.extend(helas_amp['amplitudes'])
2463 else:
2464 new_helas_amp_list.append(helas_amp)
2465 LoopDiagramsHelasAmplitudeList[i]=new_helas_amp_list
2466
2467
2468
2469
2470
2471 col_amp_list = []
2472 for i, col_basis_elem in \
2473 enumerate(sorted(color_basis.keys())):
2474
2475 col_amp = []
2476
2477 for diag_tuple in color_basis[col_basis_elem]:
2478 res_amps = filter(lambda amp: \
2479 tuple(amp.get('color_indices')) == diag_tuple[1],
2480 LoopDiagramsHelasAmplitudeList[diag_tuple[0]])
2481 if not res_amps:
2482 raise self.PhysicsObjectError, \
2483 """No amplitude found for color structure
2484 %s and color index chain (%s) (diagram %i)""" % \
2485 (col_basis_elem,
2486 str(diag_tuple[1]),
2487 diag_tuple[0])
2488
2489 for res_amp in res_amps:
2490 col_amp.append(((res_amp.get('fermionfactor'),
2491 diag_tuple[2],
2492 diag_tuple[3],
2493 diag_tuple[4]),
2494 res_amp.get('number')))
2495
2496 col_amp_list.append(col_amp)
2497
2498 return col_amp_list
2499
2501 """ When creating the base_objects.Diagram in get_base_amplitudes(),
2502 each LoopHelasDiagram will lead to one loop_base_objects.LoopDiagram
2503 for its LoopHelasAmplitude and one other for each of its counter-term
2504 (with different interaction id). This function return a list for which
2505 each element is a HelasAmplitudeList corresponding to the HelasAmplitudes
2506 related to a given loop_base_objects.LoopDiagram generated """
2507
2508 amplitudes_loop_diagrams=[]
2509
2510 for diag in self.get_loop_diagrams():
2511
2512 amplitudes_loop_diagrams.append(diag.get_loop_amplitudes())
2513
2514
2515
2516
2517
2518
2519
2520
2521 ctIDs={}
2522 for ctamp in diag.get_ct_amplitudes():
2523 try:
2524 ctIDs[ctamp.get('interaction_id')].append(ctamp)
2525 except KeyError:
2526 ctIDs[ctamp.get('interaction_id')]=\
2527 helas_objects.HelasAmplitudeList([ctamp])
2528
2529
2530 keys=ctIDs.keys()
2531 keys.sort()
2532 for key in keys:
2533 amplitudes_loop_diagrams.append(ctIDs[key])
2534
2535 for diag in self.get_loop_UVCT_diagrams():
2536 amplitudes_loop_diagrams.append(diag.get_loop_UVCTamplitudes())
2537
2538 return amplitudes_loop_diagrams
2539
2541 """Generate a loop_diagram_generation.LoopAmplitude from a
2542 LoopHelasMatrixElement. This is used to generate both color
2543 amplitudes and diagram drawing."""
2544
2545
2546
2547
2548 optimization = 1
2549 if len(filter(lambda wf: wf.get('number') == 1,
2550 self.get_all_wavefunctions())) > 1:
2551 optimization = 0
2552
2553 model = self.get('processes')[0].get('model')
2554
2555 wf_dict = {}
2556 vx_list = []
2557 diagrams = base_objects.DiagramList()
2558
2559
2560 for diag in self.get_born_diagrams():
2561 newdiag=diag.get('amplitudes')[0].get_base_diagram(\
2562 wf_dict, vx_list, optimization)
2563 diagrams.append(loop_base_objects.LoopDiagram({
2564 'vertices':newdiag['vertices'],'type':0}))
2565
2566
2567
2568
2569 dtype=1
2570 for HelasAmpList in self.get_helas_amplitudes_loop_diagrams():
2571
2572
2573 if isinstance(HelasAmpList[0],LoopHelasAmplitude):
2574 diagrams.append(HelasAmpList[0].get_base_diagram(\
2575 wf_dict, vx_list, optimization))
2576 dtype=diagrams[-1]['type']
2577 elif isinstance(HelasAmpList[0],LoopHelasUVCTAmplitude):
2578 diagrams.append(HelasAmpList[0].\
2579 get_base_diagram(wf_dict, vx_list, optimization))
2580 else:
2581 newdiag=HelasAmpList[0].get_base_diagram(wf_dict, vx_list, optimization)
2582 diagrams.append(loop_base_objects.LoopDiagram({
2583 'vertices':newdiag['vertices'],'type':-dtype}))
2584
2585
2586 for diag in diagrams:
2587 diag.calculate_orders(self.get('processes')[0].get('model'))
2588
2589 return loop_diagram_generation.LoopAmplitude({\
2590 'process': self.get('processes')[0],
2591 'diagrams': diagrams})
2592
2597 """LoopHelasProcess: Analogous of HelasMultiProcess except that it is suited
2598 for LoopAmplitude and with the peculiarity that it is always treating only
2599 one loop amplitude. So this LoopHelasProcess correspond to only one single
2600 subprocess without multiparticle labels (contrary to HelasMultiProcess)."""
2601
2602
2603 matrix_element_class = LoopHelasMatrixElement
2604
2605 - def __init__(self, argument=None, combine_matrix_elements=True,
2606 optimized_output = True, compute_loop_nc = False, matrix_element_opts={}):
2607 """ Allow for the initialization of the HelasMultiProcess with the
2608 right argument 'optimized_output' for the helas_matrix_element options.
2609 """
2610
2611 matrix_element_opts = dict(matrix_element_opts)
2612 matrix_element_opts.update({'optimized_output' : optimized_output})
2613
2614 super(LoopHelasProcess, self).__init__(argument, combine_matrix_elements,
2615 compute_loop_nc = compute_loop_nc,
2616 matrix_element_opts = matrix_element_opts)
2617
2618 @classmethod
2619 - def process_color(cls,matrix_element,color_information,compute_loop_nc=False):
2620 """ Process the color information for a given matrix
2621 element made of a loop diagrams. It will create a different
2622 color matrix depending on wether the process has a born or not.
2623 The compute_loop_nc sets wheter independent tracking of Nc power coming
2624 from the color loop trace is necessary or not (it is time consuming).
2625 """
2626 if matrix_element.get('processes')[0]['has_born']:
2627 logger.debug('Computing the loop and Born color basis')
2628 else:
2629 logger.debug('Computing the loop color basis')
2630
2631
2632 for key in color_information:
2633 exec("%s=color_information['%s']"%(key,key))
2634
2635
2636
2637
2638 matrix_element.relabel_helas_objects()
2639
2640
2641
2642
2643 new_amp = matrix_element.get_base_amplitude()
2644 matrix_element.set('base_amplitude', new_amp)
2645
2646 loop_col_basis = loop_color_amp.LoopColorBasis(
2647 compute_loop_nc = compute_loop_nc)
2648 loop_colorize_obj = loop_col_basis.create_loop_color_dict_list(\
2649 matrix_element.get('base_amplitude'),
2650 )
2651 try:
2652
2653
2654
2655 loop_col_basis_index = list_colorize.index(loop_colorize_obj)
2656 loop_col_basis = list_color_basis[loop_col_basis_index]
2657 except ValueError:
2658
2659 list_colorize.append(loop_colorize_obj)
2660 loop_col_basis.build()
2661 loop_col_basis_index = len(list_color_basis)
2662 list_color_basis.append(loop_col_basis)
2663 logger.info(\
2664 "Processing color information for %s" % \
2665 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2666 replace('Process', 'loop process'))
2667 else:
2668 logger.info(\
2669 "Reusing existing color information for %s" % \
2670 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2671 replace('Process', 'loop process'))
2672
2673 if new_amp['process']['has_born']:
2674 born_col_basis = loop_color_amp.LoopColorBasis()
2675 born_colorize_obj = born_col_basis.create_born_color_dict_list(\
2676 matrix_element.get('base_amplitude'))
2677 try:
2678
2679
2680
2681 born_col_basis_index = list_colorize.index(born_colorize_obj)
2682 born_col_basis = list_color_basis[born_col_basis_index]
2683 except ValueError:
2684
2685 list_colorize.append(born_colorize_obj)
2686 born_col_basis.build()
2687 born_col_basis_index = len(list_color_basis)
2688 list_color_basis.append(born_col_basis)
2689 logger.info(\
2690 "Processing color information for %s" % \
2691 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2692 replace('Process', 'born process'))
2693 else:
2694 logger.info(\
2695 "Reusing existing color information for %s" % \
2696 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2697 replace('Process', 'born process'))
2698 loopborn_matrices_key=(loop_col_basis_index,born_col_basis_index)
2699 else:
2700 loopborn_matrices_key=(loop_col_basis_index,loop_col_basis_index)
2701
2702
2703
2704 try:
2705
2706
2707
2708 col_matrix = dict_loopborn_matrices[loopborn_matrices_key]
2709 except KeyError:
2710
2711 col_matrix = color_amp.ColorMatrix(\
2712 list_color_basis[loopborn_matrices_key[0]],
2713 list_color_basis[loopborn_matrices_key[1]])
2714 dict_loopborn_matrices[loopborn_matrices_key]=col_matrix
2715 logger.info(\
2716 "Creating color matrix %s" % \
2717 matrix_element.get('processes')[0].nice_string().\
2718 replace('Process', 'loop process'))
2719 else:
2720 logger.info(\
2721 "Reusing existing color matrix for %s" % \
2722 matrix_element.get('processes')[0].nice_string().\
2723 replace('Process', 'loop process'))
2724
2725 matrix_element.set('loop_color_basis',loop_col_basis)
2726 if new_amp['process']['has_born']:
2727 matrix_element.set('born_color_basis',born_col_basis)
2728 matrix_element.set('color_matrix',col_matrix)
2729