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