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
748
750 """Comparison between different loop matrix elements. It works exactly as for
751 the HelasMatrixElement for now."""
752
753 return super(LoopHelasMatrixElement,self).__eq__(other)
754
756 """Overloading the nonequality operator, to make comparison easy"""
757 return not self.__eq__(other)
758
761 """Starting from a list of LoopDiagrams from the diagram
762 generation, generate the corresponding LoopHelasDiagrams, i.e.,
763 the wave functions and amplitudes (for the loops and their R2 and UV
764 counterterms). Choose between default optimization (= 1, maximum
765 recycling of wavefunctions) or no optimization (= 0, no recycling of
766 wavefunctions, useful for GPU calculations with very restricted memory).
767
768 Note that we need special treatment for decay chains, since
769 the end product then is a wavefunction, not an amplitude.
770 """
771
772 assert isinstance(amplitude, loop_diagram_generation.LoopAmplitude), \
773 "Bad arguments for generate_helas_diagrams in LoopHelasMatrixElement"
774 assert isinstance(optimization, int), \
775 "Bad arguments for generate_helas_diagrams in LoopHelasMatrixElement"
776
777 structures = amplitude.get('structure_repository')
778
779 process = amplitude.get('process')
780 has_born = amplitude.get('has_born')
781
782 model = process.get('model')
783
784
785
786 self.sort_split_orders(self.get('processes')[0].get('split_orders'))
787
788
789
790
791
792
793
794 amplitude.order_diagrams_according_to_split_orders(\
795 self.get('processes')[0].get('split_orders'))
796
797
798 wavefunctions = []
799
800
801
802
803
804
805
806
807 structID_to_infos = {}
808
809
810
811 wf_mother_arrays = []
812
813 wf_number = 0
814
815
816 external_wavefunctions = dict([(leg.get('number'),
817 helas_objects.HelasWavefunction(\
818 leg, 0, model, decay_ids)) \
819 for leg in process.get('legs')])
820
821
822
823 external_loop_wfs_dict={}
824
825
826
827 for key in external_wavefunctions.keys():
828 wf = external_wavefunctions[key]
829 if wf.is_boson() and wf.get('state') == 'initial' and \
830 not wf.get('self_antipart'):
831 wf.set('is_part', not wf.get('is_part'))
832
833
834
835 for key in external_wavefunctions.keys():
836 wf = external_wavefunctions[key]
837 if wf.get('leg_state') == False and \
838 not wf.get('self_antipart'):
839 wf.flip_part_antipart()
840
841
842 wf_number = len(process.get('legs'))
843
844
845
846 helas_diagrams = helas_objects.HelasDiagramList()
847
848
849 amplitude_number = 0
850 diagram_number = 0
851
852 def process_born_diagram(diagram, wfNumber, amplitudeNumber, UVCTdiag=False):
853 """ Helper function to process a born diagrams exactly as it is done in
854 HelasMatrixElement for tree-level diagrams. This routine can also
855 process LoopUVCTDiagrams, and if so the argument UVCTdiag must be set
856 to true"""
857
858
859
860
861 number_to_wavefunctions = [{}]
862
863
864 color_lists = [[]]
865
866
867 diagram_wavefunctions = helas_objects.HelasWavefunctionList()
868
869 vertices = copy.copy(diagram.get('vertices'))
870
871
872 lastvx = vertices.pop()
873
874
875
876 for vertex in vertices:
877
878
879
880
881
882
883
884
885
886
887 new_number_to_wavefunctions = []
888 new_color_lists = []
889 for number_wf_dict, color_list in zip(number_to_wavefunctions,
890 color_lists):
891 legs = copy.copy(vertex.get('legs'))
892 last_leg = legs.pop()
893
894 mothers = self.getmothers(legs, number_wf_dict,
895 external_wavefunctions,
896 wavefunctions,
897 diagram_wavefunctions)
898 inter = model.get('interaction_dict')[vertex.get('id')]
899
900
901
902
903 done_color = {}
904 for coupl_key in sorted(inter.get('couplings').keys()):
905 color = coupl_key[0]
906 if color in done_color:
907 wf = done_color[color]
908 wf.get('coupling').append(inter.get('couplings')[coupl_key])
909 wf.get('lorentz').append(inter.get('lorentz')[coupl_key[1]])
910 continue
911 wf = helas_objects.HelasWavefunction(last_leg, \
912 vertex.get('id'), model)
913 wf.set('coupling', [inter.get('couplings')[coupl_key]])
914 if inter.get('color'):
915 wf.set('inter_color', inter.get('color')[coupl_key[0]])
916 done_color[color] = wf
917 wf.set('lorentz', [inter.get('lorentz')[coupl_key[1]]])
918 wf.set('color_key', color)
919 wf.set('mothers',mothers)
920
921
922
923 wf.set_state_and_particle(model)
924
925
926
927
928 wf, wfNumber = wf.check_and_fix_fermion_flow(\
929 wavefunctions,
930 diagram_wavefunctions,
931 external_wavefunctions,
932 wfNumber)
933
934 new_number_wf_dict = copy.copy(number_wf_dict)
935
936 try:
937 wf = diagram_wavefunctions[\
938 diagram_wavefunctions.index(wf)]
939 except ValueError:
940
941 wfNumber = wfNumber + 1
942 wf.set('number', wfNumber)
943 try:
944
945
946 wf = wavefunctions[wf_mother_arrays.index(\
947 wf.to_array())]
948
949
950 wfNumber = wfNumber - 1
951 except ValueError:
952 diagram_wavefunctions.append(wf)
953
954 new_number_wf_dict[last_leg.get('number')] = wf
955
956
957 new_number_to_wavefunctions.append(\
958 new_number_wf_dict)
959
960 new_color_list = copy.copy(color_list)
961 new_color_list.append(coupl_key[0])
962 new_color_lists.append(new_color_list)
963
964 number_to_wavefunctions = new_number_to_wavefunctions
965 color_lists = new_color_lists
966
967
968
969 if not UVCTdiag:
970 helas_diagram = helas_objects.HelasDiagram()
971 else:
972 helas_diagram = LoopHelasDiagram()
973
974 for number_wf_dict, color_list in zip(number_to_wavefunctions,
975 color_lists):
976
977
978 if lastvx.get('id'):
979 inter = model.get_interaction(lastvx.get('id'))
980 keys = sorted(inter.get('couplings').keys())
981 pdg_codes = [p.get_pdg_code() for p in \
982 inter.get('particles')]
983 else:
984
985
986 inter = None
987 keys = [(0, 0)]
988 pdg_codes = None
989
990
991 legs = lastvx.get('legs')
992 mothers = self.getmothers(legs, number_wf_dict,
993 external_wavefunctions,
994 wavefunctions,
995 diagram_wavefunctions).\
996 sort_by_pdg_codes(pdg_codes, 0)[0]
997
998
999 wfNumber = mothers.check_and_fix_fermion_flow(wavefunctions,
1000 diagram_wavefunctions,
1001 external_wavefunctions,
1002 None,
1003 wfNumber,
1004 False,
1005 number_to_wavefunctions)
1006 done_color = {}
1007 for i, coupl_key in enumerate(keys):
1008 color = coupl_key[0]
1009 if inter and color in done_color.keys():
1010 amp = done_color[color]
1011 amp.get('coupling').append(inter.get('couplings')[coupl_key])
1012 amp.get('lorentz').append(inter.get('lorentz')[coupl_key[1]])
1013 continue
1014 if not UVCTdiag:
1015 amp = helas_objects.HelasAmplitude(lastvx, model)
1016 else:
1017 amp = LoopHelasUVCTAmplitude(lastvx, model)
1018 amp.set('UVCT_orders',diagram.get('UVCT_orders'))
1019 amp.set('UVCT_couplings',diagram.get('UVCT_couplings'))
1020 amp.set('type',diagram.get('type'))
1021 if inter:
1022 amp.set('coupling', [inter.get('couplings')[coupl_key]])
1023 amp.set('lorentz', [inter.get('lorentz')[coupl_key[1]]])
1024 if inter.get('color'):
1025 amp.set('inter_color', inter.get('color')[color])
1026 amp.set('color_key', color)
1027 done_color[color] = amp
1028 amp.set('mothers', mothers)
1029 amplitudeNumber = amplitudeNumber + 1
1030 amp.set('number', amplitudeNumber)
1031
1032 new_color_list = copy.copy(color_list)
1033 if inter:
1034 new_color_list.append(color)
1035
1036 amp.set('color_indices', new_color_list)
1037
1038
1039 helas_diagram.get('amplitudes').append(amp)
1040
1041
1042
1043 helas_diagram.set('wavefunctions', diagram_wavefunctions)
1044
1045
1046 diagram_wavefunctions.sort(lambda wf1, wf2: \
1047 wf1.get('number') - wf2.get('number'))
1048
1049 if optimization:
1050 wavefunctions.extend(diagram_wavefunctions)
1051 wf_mother_arrays.extend([wf.to_array() for wf \
1052 in diagram_wavefunctions])
1053 else:
1054 wfNumber = len(process.get('legs'))
1055 if self.optimized_output:
1056
1057
1058 wfNumber = wfNumber+1
1059
1060
1061 return helas_diagram, wfNumber, amplitudeNumber
1062
1063 def process_struct(sID, diag_wfs, wfNumber):
1064 """ Scan a structure, create the necessary wavefunctions, add them
1065 to the diagram wavefunctions list, and return a list of bridge
1066 wavefunctions (i.e. those attached to the loop) with a list, ordered
1067 in the same way, of color lists. Each element of these lists
1068 correspond to one choice of color-lorentz structure of this
1069 tree-structure #sID. """
1070
1071
1072
1073
1074 number_to_wavefunctions = [{}]
1075
1076
1077 color_lists = [[]]
1078
1079
1080 bridge_wfs = helas_objects.HelasWavefunctionList()
1081
1082 vertices = copy.copy(structures[sID].get('vertices'))
1083
1084
1085
1086 if len(vertices)==0:
1087 binding_leg=copy.copy(structures[sID]['binding_leg'])
1088 binding_wf = self.getmothers(base_objects.LegList([binding_leg,]),
1089 {},
1090 external_wavefunctions,
1091 wavefunctions,
1092 diag_wfs)
1093
1094
1095 return [(binding_wf[0],[])] ,wfNumber
1096
1097
1098
1099 for i, vertex in enumerate(vertices):
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110 new_number_to_wavefunctions = []
1111 new_color_lists = []
1112 for number_wf_dict, color_list in zip(number_to_wavefunctions,
1113 color_lists):
1114 legs = copy.copy(vertex.get('legs'))
1115 last_leg = legs.pop()
1116
1117 mothers = self.getmothers(legs, number_wf_dict,
1118 external_wavefunctions,
1119 wavefunctions,
1120 diag_wfs)
1121 inter = model.get('interaction_dict')[vertex.get('id')]
1122
1123
1124
1125
1126
1127 grouped_interaction_keys = {}
1128 colors_order = []
1129 for coupl_key in sorted(inter.get('couplings').keys()):
1130 color = coupl_key[0]
1131 if color not in colors_order:
1132 colors_order.append(color)
1133 grouped_interaction_keys[color] = \
1134 (coupl_key, [inter.get('couplings')[coupl_key]], [inter.get('lorentz')[coupl_key[1]]])
1135 else:
1136 grouped_interaction_keys[color][1].append(inter.get('couplings')[coupl_key])
1137 grouped_interaction_keys[color][2].append(inter.get('lorentz')[coupl_key[1]])
1138
1139 for coupl_key, all_couplings, all_lorentz in [grouped_interaction_keys[color] for color in colors_order]:
1140 color = coupl_key[0]
1141 wf = helas_objects.HelasWavefunction(last_leg, vertex.get('id'), model)
1142 wf.set('coupling', all_couplings)
1143 if inter.get('color'):
1144 wf.set('inter_color', inter.get('color')[coupl_key[0]])
1145 wf.set('lorentz', all_lorentz)
1146 wf.set('color_key', color)
1147 wf.set('mothers',mothers)
1148
1149
1150
1151
1152
1153
1154
1155
1156 wf.set_state_and_particle(model)
1157
1158
1159
1160 wf, wfNumber = wf.check_and_fix_fermion_flow(\
1161 wavefunctions,
1162 diag_wfs,
1163 external_wavefunctions,
1164 wfNumber)
1165
1166 new_number_wf_dict = copy.copy(number_wf_dict)
1167
1168
1169 try:
1170 wf = diag_wfs[\
1171 diag_wfs.index(wf)]
1172 except ValueError:
1173
1174 wfNumber = wfNumber + 1
1175 wf.set('number', wfNumber)
1176 try:
1177
1178
1179 wf = wavefunctions[wf_mother_arrays.index(wf.to_array())]
1180
1181
1182 wfNumber = wfNumber - 1
1183 except ValueError:
1184 diag_wfs.append(wf)
1185
1186 new_number_wf_dict[last_leg.get('number')] = wf
1187 if i==(len(vertices)-1):
1188
1189
1190 bridge_wfs.append(wf)
1191
1192 new_number_to_wavefunctions.append(\
1193 new_number_wf_dict)
1194
1195 new_color_list = copy.copy(color_list)
1196 new_color_list.append(coupl_key[0])
1197 new_color_lists.append(new_color_list)
1198
1199
1200 number_to_wavefunctions = new_number_to_wavefunctions
1201 color_lists = new_color_lists
1202
1203
1204
1205
1206
1207 return zip(bridge_wfs, color_lists), wfNumber
1208
1209 def getloopmothers(loopWfsIn, structIDs, color_list, diag_wfs, wfNumber):
1210 """From the incoming loop leg(s) and the list of structures IDs
1211 connected to the loop at this point, it generates the list of
1212 mothers, a list of colorlist and a number_to_wavefunctions
1213 dictionary list for which each element correspond to one
1214 lorentz-color structure of the tree-structure attached to the loop.
1215 It will launch the reconstruction procedure of the structures
1216 which have not been encountered yet."""
1217
1218
1219
1220
1221
1222 mothers_list = [loopWfsIn,]
1223 color_lists = [color_list,]
1224
1225
1226
1227 for sID in structIDs:
1228 try:
1229 struct_infos = structID_to_infos[sID]
1230 except KeyError:
1231
1232
1233 struct_infos, wfNumber = \
1234 process_struct(sID, diag_wfs, wfNumber)
1235
1236
1237
1238
1239
1240 if optimization and False:
1241
1242
1243
1244
1245 structID_to_infos[sID]=copy.copy(struct_infos)
1246
1247
1248 new_mothers_list = []
1249 new_color_lists = []
1250 for mothers, orig_color_list in zip(mothers_list, color_lists):
1251 for struct_wf, struct_color_list in struct_infos:
1252 new_color_list = copy.copy(orig_color_list)+\
1253 copy.copy(struct_color_list)
1254 new_mothers = copy.copy(mothers)
1255 new_mothers.append(struct_wf)
1256 new_color_lists.append(new_color_list)
1257 new_mothers_list.append(new_mothers)
1258 mothers_list = new_mothers_list
1259 color_lists = new_color_lists
1260
1261
1262
1263
1264
1265
1266 return (mothers_list, color_lists), wfNumber
1267
1268 def process_loop_diagram(diagram, wavefunctionNumber, amplitudeNumber):
1269 """ Helper function to process a the loop diagrams which features
1270 several different aspects compared to the tree born diagrams."""
1271
1272
1273 helas_diagram = LoopHelasDiagram()
1274
1275
1276
1277
1278
1279
1280
1281 last_loop_wfs = helas_objects.HelasWavefunctionList()
1282
1283
1284 color_lists = [[]]
1285
1286
1287 diagram_wavefunctions = helas_objects.HelasWavefunctionList()
1288
1289
1290
1291
1292 tag = copy.deepcopy(diagram.get('tag'))
1293 loop_vertices = copy.deepcopy(diagram.get('vertices'))
1294 for i in range(len(tag)):
1295 tag[i][2]=loop_vertices[i]
1296
1297
1298 ct_vertices = copy.copy(diagram.get('CT_vertices'))
1299
1300
1301 external_loop_wf=helas_objects.HelasWavefunction(\
1302 tag[0][0], 0, model, decay_ids)
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312 if not self.optimized_output:
1313 wavefunctionNumber=wavefunctionNumber+1
1314 external_loop_wf.set('number',wavefunctionNumber)
1315 diagram_wavefunctions.append(external_loop_wf)
1316 else:
1317 try:
1318 external_loop_wf=\
1319 external_loop_wfs_dict[external_loop_wf.get('pdg_code')]
1320 except KeyError:
1321 wavefunctionNumber=wavefunctionNumber+1
1322 external_loop_wf.set('number',wavefunctionNumber)
1323 external_loop_wfs_dict[external_loop_wf.get('pdg_code')]=\
1324 external_loop_wf
1325 diagram_wavefunctions.append(external_loop_wf)
1326
1327
1328 last_loop_wfs.append(external_loop_wf)
1329
1330 def process_tag_elem(tagElem, wfNumber, lastloopwfs, colorlists):
1331 """Treat one tag element of the loop diagram (not the last one
1332 which provides an amplitude)"""
1333
1334
1335
1336
1337
1338 new_color_lists = []
1339 new_last_loop_wfs = helas_objects.HelasWavefunctionList()
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350 vertex=tagElem[2]
1351 structIDs=tagElem[1]
1352 for last_loop_wf, color_list in zip(lastloopwfs,
1353 colorlists):
1354 loopLegOut = copy.copy(vertex.get('legs')[-1])
1355
1356
1357
1358
1359
1360
1361 (motherslist, colorlists), wfNumber = \
1362 getloopmothers(\
1363 helas_objects.HelasWavefunctionList([last_loop_wf,]),
1364 structIDs,\
1365 color_list, diagram_wavefunctions, wfNumber)
1366 inter = model.get('interaction_dict')[vertex.get('id')]
1367
1368
1369
1370 for mothers, structcolorlist in zip(motherslist, colorlists):
1371
1372 done_color = {}
1373 for coupl_key in sorted(inter.get('couplings').keys()):
1374 color = coupl_key[0]
1375 if color in done_color:
1376 wf = done_color[color]
1377 wf.get('coupling').append(inter.get('couplings')[coupl_key])
1378 wf.get('lorentz').append(inter.get('lorentz')[coupl_key[1]])
1379 continue
1380 wf = helas_objects.HelasWavefunction(loopLegOut, \
1381 vertex.get('id'), model)
1382 wf.set('coupling', [inter.get('couplings')[coupl_key]])
1383 if inter.get('color'):
1384 wf.set('inter_color', inter.get('color')[coupl_key[0]])
1385 done_color[color] = wf
1386 wf.set('lorentz', [inter.get('lorentz')[coupl_key[1]]])
1387 wf.set('color_key', color)
1388 wf.set('mothers',mothers)
1389
1390
1391
1392 wf.set_state_and_particle(model)
1393
1394
1395
1396 wf, wfNumber = wf.check_and_fix_fermion_flow(\
1397 wavefunctions,
1398 diagram_wavefunctions,
1399 external_wavefunctions,
1400 wfNumber)
1401
1402
1403 try:
1404 wf = diagram_wavefunctions[\
1405 diagram_wavefunctions.index(wf)]
1406 except ValueError:
1407
1408 wfNumber = wfNumber + 1
1409 wf.set('number', wfNumber)
1410
1411
1412
1413 try:
1414 if not self.optimized_output:
1415 raise ValueError
1416
1417
1418 wf = wavefunctions[wf_mother_arrays.index(\
1419 wf.to_array())]
1420
1421
1422 wfNumber = wfNumber - 1
1423
1424
1425 self.lwf_reused += 1
1426 except ValueError:
1427 diagram_wavefunctions.append(wf)
1428
1429
1430
1431 new_last_loop_wfs.append(wf)
1432
1433 new_color_list = copy.copy(structcolorlist)
1434 new_color_list.append(coupl_key[0])
1435 new_color_lists.append(new_color_list)
1436
1437
1438
1439
1440 return wfNumber, new_last_loop_wfs, new_color_lists
1441
1442
1443
1444
1445
1446 def create_amplitudes(lastvx, wfNumber, amplitudeNumber):
1447 """Treat the last tag element of the loop diagram (which
1448 provides an amplitude)"""
1449
1450
1451
1452
1453
1454
1455 other_external_loop_wf=helas_objects.HelasWavefunction()
1456
1457 for leg in [leg for leg in lastvx['legs'] if leg['loop_line']]:
1458 if last_loop_wfs[0]['number_external']!=leg['number']:
1459 other_external_loop_wf=\
1460 helas_objects.HelasWavefunction(leg, 0, model, decay_ids)
1461
1462 break
1463
1464
1465 for last_loop_wf, color_list in zip(last_loop_wfs,color_lists):
1466
1467 if lastvx.get('id')!=-1:
1468 raise self.PhysicsObjectError, \
1469 "The amplitude vertex of a loop diagram must be a "+\
1470 "two point vertex with id=-1"
1471
1472
1473 if other_external_loop_wf.is_majorana():
1474 fix_lcut_majorana_fermion_flow(last_loop_wf,\
1475 other_external_loop_wf)
1476
1477 mothers=helas_objects.HelasWavefunctionList(\
1478 [last_loop_wf,other_external_loop_wf])
1479 wfNumber = mothers.check_and_fix_fermion_flow(wavefunctions,
1480 diagram_wavefunctions,
1481 external_wavefunctions,
1482 None,
1483 wfNumber,
1484 False,
1485 [])
1486 amp = helas_objects.HelasAmplitude(lastvx, model)
1487 amp.set('interaction_id',-1)
1488 amp.set('mothers',mothers)
1489
1490
1491 amp.set('pdg_codes',[last_loop_wf.get_pdg_code(),
1492 other_external_loop_wf.get_pdg_code()])
1493
1494
1495
1496
1497
1498 amp.set('color_indices', copy.copy(color_list))
1499
1500
1501 amplitudeNumber = amplitudeNumber + 1
1502 amp.set('number', amplitudeNumber)
1503 amp.set('type','loop')
1504 loop_amp = LoopHelasAmplitude()
1505 loop_amp.set('amplitudes',\
1506 helas_objects.HelasAmplitudeList([amp,]))
1507
1508
1509
1510
1511 loop_amp_wfs=helas_objects.HelasWavefunctionList(\
1512 [last_loop_wf,])
1513 while loop_amp_wfs[-1].get('mothers'):
1514 loop_amp_wfs.append([lwf for lwf in \
1515 loop_amp_wfs[-1].get('mothers') if lwf['is_loop']][0])
1516
1517
1518
1519
1520
1521 loop_amp_wfs.append(other_external_loop_wf)
1522
1523
1524 loop_amp_wfs.reverse()
1525 loop_amp.set('wavefunctions',loop_amp_wfs)
1526 loop_amp.set('type',diagram.get('type'))
1527 loop_amp.set('multiplier',diagram.get('multiplier'))
1528
1529 loop_amp.set('number',min([amp.get('number') for amp
1530 in loop_amp.get('amplitudes')]))
1531 loop_amp.set('coupling',loop_amp.get_couplings())
1532 loop_amp.set('orders',loop_amp.get_orders())
1533 helas_diagram.get('amplitudes').append(loop_amp)
1534
1535
1536 check_lcut_fermion_flow_consistency(\
1537 loop_amp_wfs[0],loop_amp_wfs[1])
1538 return wfNumber, amplitudeNumber
1539
1540 def check_lcut_fermion_flow_consistency(lcut_wf1, lcut_wf2):
1541 """Checks that the two L-cut loop helas wavefunctions have
1542 a consistent fermion flow."""
1543 if lcut_wf1.is_boson():
1544 if lcut_wf1.get('state')!='final' or\
1545 lcut_wf2.get('state')!='final':
1546 raise MadGraph5Error,\
1547 "Inconsistent flow in L-cut bosons."
1548 elif not lcut_wf1.is_majorana():
1549 for lcut_wf in [lcut_wf1,lcut_wf2]:
1550 if not ((lcut_wf.get('is_part') and \
1551 lcut_wf.get('state')=='outgoing') or\
1552 (not lcut_wf.get('is_part') and\
1553 lcut_wf.get('state')=='incoming')):
1554 raise MadGraph5Error,\
1555 "Inconsistent flow in L-cut Dirac fermions."
1556 elif lcut_wf1.is_majorana():
1557 if (lcut_wf1.get('state'), lcut_wf2.get('state')) not in \
1558 [('incoming','outgoing'),('outgoing','incoming')]:
1559 raise MadGraph5Error,\
1560 "Inconsistent flow in L-cut Majorana fermions."
1561
1562 def fix_lcut_majorana_fermion_flow(last_loop_wf,\
1563 other_external_loop_wf):
1564 """Fix the fermion flow of the last external Majorana loop
1565 wavefunction through the fermion flow of the first external
1566 Majorana loop wavefunction."""
1567
1568
1569 loop_amp_wfs=helas_objects.HelasWavefunctionList(\
1570 [last_loop_wf,])
1571 while loop_amp_wfs[-1].get('mothers'):
1572 loop_amp_wfs.append([lwf for lwf in \
1573 loop_amp_wfs[-1].get('mothers') if lwf['is_loop']][0])
1574 loop_amp_wfs.append(other_external_loop_wf)
1575 loop_amp_wfs.reverse()
1576
1577
1578 rep={'incoming':'outgoing','outgoing':'incoming'}
1579
1580 other_external_loop_wf['state']=rep[loop_amp_wfs[1]['state']]
1581 return
1582
1583 def process_counterterms(ct_vertices, wfNumber, amplitudeNumber):
1584 """Process the counterterms vertices defined in this loop
1585 diagram."""
1586
1587 structIDs=[]
1588 for tagElem in tag:
1589 structIDs += tagElem[1]
1590
1591
1592
1593
1594 (motherslist, colorlists), wfNumber = getloopmothers(\
1595 helas_objects.HelasWavefunctionList(), structIDs, \
1596 [], diagram_wavefunctions, wfNumber)
1597
1598 for mothers, structcolorlist in zip(motherslist, colorlists):
1599 for ct_vertex in ct_vertices:
1600
1601 inter = model.get_interaction(ct_vertex.get('id'))
1602 keys = sorted(inter.get('couplings').keys())
1603 pdg_codes = [p.get_pdg_code() for p in \
1604 inter.get('particles')]
1605 mothers = mothers.sort_by_pdg_codes(pdg_codes, 0)[0]
1606
1607
1608 wfNumber = mothers.check_and_fix_fermion_flow(wavefunctions,
1609 diagram_wavefunctions,
1610 external_wavefunctions,
1611 None,
1612 wfNumber,
1613 False,
1614 [])
1615 done_color = {}
1616 for i, coupl_key in enumerate(keys):
1617 color = coupl_key[0]
1618 if color in done_color.keys():
1619 amp = done_color[color]
1620 amp.get('coupling').append(inter.get('couplings')[coupl_key])
1621 amp.get('lorentz').append(inter.get('lorentz')[coupl_key[1]])
1622 continue
1623 amp = helas_objects.HelasAmplitude(ct_vertex, model)
1624 amp.set('coupling', [inter.get('couplings')[coupl_key]])
1625 amp.set('lorentz', [inter.get('lorentz')[coupl_key[1]]])
1626 if inter.get('color'):
1627 amp.set('inter_color', inter.get('color')[color])
1628 amp.set('color_key', color)
1629 done_color[color] = amp
1630 amp.set('mothers', mothers)
1631 amplitudeNumber = amplitudeNumber + 1
1632 amp.set('number', amplitudeNumber)
1633
1634 amp_color_list = copy.copy(structcolorlist)
1635 amp_color_list.append(color)
1636 amp.set('color_indices', amp_color_list)
1637 amp.set('type',inter.get('type'))
1638
1639
1640 helas_diagram.get('amplitudes').append(amp)
1641 return wfNumber, amplitudeNumber
1642
1643 for tagElem in tag:
1644 wavefunctionNumber, last_loop_wfs, color_lists = \
1645 process_tag_elem(tagElem, wavefunctionNumber, \
1646 last_loop_wfs, color_lists)
1647
1648
1649
1650 wavefunctionNumber, amplitudeNumber = create_amplitudes(
1651 loop_vertices[-1], wavefunctionNumber, amplitudeNumber)
1652
1653
1654 if ct_vertices:
1655 wavefunctionNumber, amplitudeNumber = process_counterterms(\
1656 ct_vertices, wavefunctionNumber, amplitudeNumber)
1657
1658
1659
1660 struct_wfs=helas_objects.HelasWavefunctionList(\
1661 [wf for wf in diagram_wavefunctions if not wf['is_loop']])
1662 loop_wfs=helas_objects.HelasWavefunctionList(\
1663 [wf for wf in diagram_wavefunctions if wf['is_loop']])
1664
1665
1666 struct_wfs.sort(lambda wf1, wf2: \
1667 wf1.get('number') - wf2.get('number'))
1668
1669
1670
1671 helas_diagram.set('wavefunctions', struct_wfs)
1672
1673
1674
1675
1676 if optimization:
1677 wavefunctions.extend(struct_wfs)
1678 wf_mother_arrays.extend([wf.to_array() for wf in struct_wfs])
1679 if self.optimized_output:
1680 wavefunctions.extend(loop_wfs)
1681 wf_mother_arrays.extend([wf.to_array() for wf in loop_wfs])
1682 else:
1683 wavefunctionNumber = len(process.get('legs'))
1684 if self.optimized_output:
1685
1686
1687 wavefunctionNumber = wavefunctionNumber+1
1688
1689
1690
1691
1692
1693
1694 if self.optimized_output:
1695 loop_wfs = helas_objects.HelasWavefunctionList(
1696 [lwf for lwf in loop_wfs if len(lwf.get('mothers'))>0])
1697 helas_diagram.set('loop_wavefunctions',loop_wfs)
1698
1699
1700 return helas_diagram, wavefunctionNumber, amplitudeNumber
1701
1702
1703 if has_born:
1704 for diagram in amplitude.get('born_diagrams'):
1705 helBornDiag, wf_number, amplitude_number=\
1706 process_born_diagram(diagram, wf_number, amplitude_number)
1707 diagram_number = diagram_number + 1
1708 helBornDiag.set('number', diagram_number)
1709 helas_diagrams.append(helBornDiag)
1710
1711
1712 self.lwf_reused=0
1713 for diagram in amplitude.get('loop_diagrams'):
1714 loopHelDiag, wf_number, amplitude_number=\
1715 process_loop_diagram(diagram, wf_number, amplitude_number)
1716 diagram_number = diagram_number + 1
1717 loopHelDiag.set('number', diagram_number)
1718 helas_diagrams.append(loopHelDiag)
1719
1720
1721 for diagram in amplitude.get('loop_UVCT_diagrams'):
1722 loopHelDiag, wf_number, amplitude_number=\
1723 process_born_diagram(diagram, wf_number, amplitude_number, \
1724 UVCTdiag=True)
1725 diagram_number = diagram_number + 1
1726 loopHelDiag.set('number', diagram_number)
1727
1728
1729 for lamp in loopHelDiag.get_loop_UVCTamplitudes():
1730 new_orders = copy.copy(lamp.get('orders'))
1731 for order, value in lamp.get('UVCT_orders').items():
1732 try:
1733 new_orders[order] = new_orders[order] + value
1734 except KeyError:
1735 new_orders[order] = value
1736 lamp.set('orders', new_orders)
1737 helas_diagrams.append(loopHelDiag)
1738
1739 self.set('diagrams', helas_diagrams)
1740
1741 if __debug__:
1742 for diag in self.get('diagrams'):
1743
1744
1745
1746
1747 diag.get('wavefunctions').check_wavefunction_numbers_order()
1748
1749
1750 if self.optimized_output:
1751 logger.debug('%d loop wavefunctions have been reused'%self.lwf_reused+
1752 ', for a total of %d ones'%sum([len(ldiag.get('loop_wavefunctions'))
1753 for ldiag in self.get_loop_diagrams()]))
1754
1755
1756 for wf in self.get_all_wavefunctions():
1757 wf.set('mothers', helas_objects.HelasMatrixElement.sorted_mothers(wf))
1758
1759 for amp in self.get_all_amplitudes():
1760 amp.set('mothers', helas_objects.HelasMatrixElement.sorted_mothers(amp))
1761
1762
1763
1764 gen_colors = amp.get('color_indices')
1765 amp.set('color_indices', amp.get_color_indices())
1766 if isinstance(amp,LoopHelasAmplitude):
1767 assert (amp.get('color_indices')==gen_colors), \
1768 "Error in the treatment of color in the loop helas diagram "+\
1769 "generation. It could be harmless, but report this bug to be sure."+\
1770 " The different keys are %s vs %s."%(str(gen_colors),\
1771 str(amp.get('color_indices')))
1772 for loopdiag in self.get_loop_diagrams():
1773 for loopamp in loopdiag.get_loop_amplitudes():
1774 loopamp.set_mothers_and_pairing()
1775
1776
1777
1778
1779
1780
1781
1782
1783
1785 """This function returns a list and a dictionary:
1786 squared_orders, amps_orders
1787 ===
1788 The squared_orders lists all contributing squared_orders as tuple whose
1789 elements are the power at which are elevated the couplings orderered as
1790 in the 'split_orders'.
1791
1792 squared_orders : All possible contributing squared orders among those
1793 specified in the process['split_orders'] argument. The elements of
1794 the list are tuples of the format
1795 ((OrderValue1,OrderValue2,...),
1796 (max_contrib_ct_amp_number,
1797 max_contrib_uvct_amp_number,
1798 max_contrib_loop_amp_number,
1799 max_contrib_group_id))
1800 with OrderValue<i> correspond to the value of the <i>th order in
1801 process['split_orders'] (the others are summed over and therefore
1802 left unspecified).
1803 Ex for dijet with process['split_orders']=['QCD','QED']:
1804 => [((4,0),(8,2,3)),((2,2),(10,3,3)),((0,4),(20,5,4))]
1805
1806 'max_contrib_loop_amp_number': For optimization purposes, it is good to
1807 know what is the maximum loop amplitude number contributing to any given
1808 squared order. The fortran output is structured so that if the user
1809 is interested in a given squared order contribution only, then
1810 all the open loop coefficients for the amplitudes with a number above
1811 this value can be skipped.
1812
1813 'max_contrib_(uv)ct_amp_number': Same as above but for the
1814 (uv)ctamplitude number.
1815
1816 'max_contrib_group_id': The same as above, except this time
1817 it is for the loop group id used for the loop reduction.
1818 ===
1819 The amps_orders is a *dictionary* with keys
1820 'born_amp_orders',
1821 'loop_amp_orders'
1822 with values being the tuples described below.
1823
1824 If process['split_orders'] is empty, all these tuples are set empty.
1825
1826 'born_amp_orders' : Exactly as for squared order except that this list specifies
1827 the contributing order values for the amplitude (i.e. not 'squared').
1828 Also, the tuple describing the amplitude order is nested with a
1829 second one listing all amplitude numbers contributing to this order.
1830 Ex for dijet with process['split_orders']=['QCD','QED']:
1831 => [((2, 0), (2,)), ((0, 2), (1, 3, 4))]
1832 The function returns () if the process has no borns.
1833
1834 'loop_amp_orders' : The same as for born_amp_orders but for the loop
1835 type of amplitudes only.
1836
1837 Keep in mind that the orders of the elements of the outter most list is
1838 important as it dictates the order for the corresponding "order indices"
1839 in the fortran code output by the exporters.
1840 """
1841
1842 split_orders=self.get('processes')[0].get('split_orders')
1843
1844 amps_orders = {'born_amp_orders':[],
1845 'loop_amp_orders':[]}
1846 if len(split_orders)==0:
1847 self.squared_orders = []
1848 return [],amps_orders
1849
1850
1851
1852 self.sort_split_orders(split_orders)
1853
1854 process = self.get('processes')[0]
1855
1856
1857 self.sort_split_orders(split_orders)
1858 loop_amp_orders = self.get_split_orders_mapping_for_diagram_list(\
1859 self.get_loop_diagrams(), split_orders,
1860 get_amplitudes_function = lambda diag: diag.get_loop_amplitudes(),
1861
1862
1863
1864 get_amp_number_function = lambda amp:
1865 (amp.get('amplitudes')[0].get('number'),amp.get('loop_group_id')))
1866 ct_amp_orders = self.get_split_orders_mapping_for_diagram_list(\
1867 self.get_loop_diagrams(), split_orders,
1868 get_amplitudes_function = lambda diag: diag.get_ct_amplitudes())
1869 uvct_amp_orders = self.get_split_orders_mapping_for_diagram_list(\
1870 self.get_loop_UVCT_diagrams(), split_orders)
1871
1872
1873
1874
1875 amps_orders['loop_amp_orders'] = dict([(lao[0],
1876 [el[0] for el in lao[1]]) for lao in loop_amp_orders])
1877
1878 for ct_amp_order in ct_amp_orders+uvct_amp_orders:
1879 try:
1880 amps_orders['loop_amp_orders'][ct_amp_order[0]].extend(\
1881 list(ct_amp_order[1]))
1882 except KeyError:
1883 amps_orders['loop_amp_orders'][ct_amp_order[0]] = \
1884 list(ct_amp_order[1])
1885
1886 amps_orders['loop_amp_orders'] = [
1887 (key, tuple(sorted(amps_orders['loop_amp_orders'][key])))
1888 for key in amps_orders['loop_amp_orders'].keys()]
1889
1890 order_hierarchy = self.get('processes')[0]\
1891 .get('model').get('order_hierarchy')
1892 if set(order_hierarchy.keys()).union(set(split_orders))==\
1893 set(order_hierarchy.keys()):
1894 amps_orders['loop_amp_orders'].sort(key= lambda so:
1895 sum([order_hierarchy[split_orders[i]]*order_power for \
1896 i, order_power in enumerate(so[0])]))
1897
1898
1899 if process.get('has_born'):
1900 born_amp_orders = self.get_split_orders_mapping_for_diagram_list(\
1901 self.get_born_diagrams(),split_orders)
1902
1903 amps_orders['born_amp_orders'] = born_amp_orders
1904
1905
1906
1907
1908
1909 loop_orders = [(lso[0],tuple(zip(*list(lso[1])))) for lso in loop_amp_orders]
1910
1911
1912
1913 if process.get('has_born'):
1914 ref_orders = [bao[0] for bao in born_amp_orders]
1915 else:
1916 ref_orders = [lao[0] for lao in loop_orders+ct_amp_orders]
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928 def smax(AmpNumList):
1929 return -1 if len(AmpNumList)==0 else max(AmpNumList)
1930
1931 squared_orders = {}
1932 for ref_order in ref_orders:
1933 for uvct_order in uvct_amp_orders:
1934 key = tuple([ord1 + ord2 for ord1,ord2 in zip(uvct_order[0],
1935 ref_order)])
1936 try:
1937
1938 squared_orders[key][0] = smax([squared_orders[key][0]]+
1939 list(uvct_order[1]))
1940 except KeyError:
1941 squared_orders[key] = [smax(list(uvct_order[1])),-1,-1,-1]
1942
1943 for ct_order in ct_amp_orders:
1944 key = tuple([ord1 + ord2 for ord1,ord2 in zip(ct_order[0],
1945 ref_order)])
1946 try:
1947
1948 squared_orders[key][1] = smax([squared_orders[key][1]]+
1949 list(ct_order[1]))
1950 except KeyError:
1951 squared_orders[key] = [-1,smax(list(ct_order[1])),-1,-1]
1952
1953 for loop_order in loop_orders:
1954 key = tuple([ord1 + ord2 for ord1,ord2 in zip(loop_order[0],
1955 ref_order)])
1956 try:
1957
1958 squared_orders[key][2] = smax([squared_orders[key][2]]+
1959 list(loop_order[1][0]))
1960
1961 squared_orders[key][3] = smax([squared_orders[key][3]]+
1962 list(loop_order[1][1]))
1963 except KeyError:
1964 squared_orders[key] = [-1,-1,smax(list(loop_order[1][0])),
1965 smax(list(loop_order[1][1]))]
1966
1967
1968
1969
1970
1971
1972 squared_orders = [(sqso[0],tuple(sqso[1])) for sqso in \
1973 squared_orders.items()]
1974
1975 order_hierarchy = self.get('processes')[0].get('model').get('order_hierarchy')
1976 if set(order_hierarchy.keys()).union(set(split_orders))==\
1977 set(order_hierarchy.keys()):
1978 squared_orders.sort(key= lambda so:
1979 sum([order_hierarchy[split_orders[i]]*order_power for \
1980 i, order_power in enumerate(so[0])]))
1981
1982
1983 self.squared_orders = squared_orders
1984
1985 return squared_orders, amps_orders
1986
1988 """Return the squared_order contributions as returned by the function
1989 get_split_orders_mapping. It uses the cached value self.squared_orders
1990 if it was already defined during a previous call to get_split_orders_mapping.
1991 """
1992
1993 if not hasattr(self, "squared_orders"):
1994 self.get_split_orders_mapping()
1995
1996 return self.squared_orders
1997
1999 """ Find the maximum number of loop couplings appearing in any of the
2000 LoopHelasAmplitude in this LoopHelasMatrixElement"""
2001 if len(self.get_loop_diagrams())==0:
2002 return 0
2003 return max([len(amp.get('coupling')) for amp in \
2004 sum([d.get_loop_amplitudes() for d in self.get_loop_diagrams()],[])])
2005
2007 """ Returns the maximum power of loop momentum brought by a loop
2008 interaction. For renormalizable theories, it should be no more than one.
2009 """
2010 return max([lwf.get_analytic_info('interaction_rank') for lwf in \
2011 self.get_all_loop_wavefunctions()])
2012
2014 """ Returns the rank of the contributing loop with maximum rank """
2015 r_list = [lamp.get_analytic_info('wavefunction_rank') for ldiag in \
2016 self.get_loop_diagrams() for lamp in ldiag.get_loop_amplitudes()]
2017 if len(r_list)==0:
2018 return 0
2019 else:
2020 return max(r_list)
2021
2023 """Returns the maximum spin that any particle either connected to a loop
2024 or running in it has, among all the loops contributing to this ME"""
2025
2026
2027
2028
2029
2030 return max(
2031 max(l.get('spin') for l in lamp.get('mothers')+
2032 lamp.get('wavefunctions')+d.get('loop_wavefunctions'))
2033 for d in self['diagrams'] if isinstance(d,LoopHelasDiagram)
2034 for lamp in d.get_loop_amplitudes()
2035 )
2036
2038 """ Returns the spin of the loop particle with maximum spin among all
2039 the loop contributing to this ME"""
2040 return max([lwf.get('spin') for lwf in \
2041 self.get_all_loop_wavefunctions()])
2042
2044 """Give a unique number to each non-equivalent (at the level of the output)
2045 LoopHelasAmplitude """
2046
2047 LoopHelasAmplitudeRecognized=[]
2048 for lamp in \
2049 sum([d.get_loop_amplitudes() for d in self.get_loop_diagrams()],[]):
2050 lamp.set('number',-1)
2051 for lamp2 in LoopHelasAmplitudeRecognized:
2052 if lamp.is_equivalent(lamp2):
2053
2054
2055 lamp.set('number',lamp2.get('number'))
2056 break;
2057 if lamp.get('number')==-1:
2058 lamp.set('number',(len(LoopHelasAmplitudeRecognized)+1))
2059 LoopHelasAmplitudeRecognized.append(lamp)
2060
2062 """Give a unique number to each LoopHelasAmplitude. These will be the
2063 number used for the LOOPCOEF array in the optimized output and the
2064 grouping is done in a further stage by adding all the LOOPCOEF sharing
2065 the same denominator to a given one using the 'loop_group_id' attribute
2066 of the LoopHelasAmplitudes. """
2067
2068 lamp_number=1
2069 for lamp in \
2070 sum([d.get_loop_amplitudes() for d in self.get_loop_diagrams()],[]):
2071 lamp.set('number',lamp_number)
2072 lamp_number += 1
2073
2075 """ Give the correct number for the default output to the wavefunctions
2076 and amplitudes building the loops """
2077
2078
2079 CT_ampnumber=1
2080 loop_ampnumber=self.get_number_of_CT_amplitudes()+1
2081 loopwfnumber=1
2082
2083 for loopdiag in self.get_loop_diagrams():
2084 for wf in loopdiag.get('wavefunctions'):
2085 wf.set('number',wfnumber)
2086 wfnumber=wfnumber+1
2087 for loopamp in loopdiag.get_loop_amplitudes():
2088 loopwfnumber=1
2089 for loopwf in loopamp['wavefunctions']:
2090 loopwf.set('number',loopwfnumber)
2091 loopwfnumber=loopwfnumber+1
2092 for amp in loopamp['amplitudes']:
2093 amp.set('number',loop_ampnumber)
2094 loop_ampnumber=loop_ampnumber+1
2095 for ctamp in loopdiag.get_ct_amplitudes():
2096 ctamp.set('number',CT_ampnumber)
2097 CT_ampnumber=CT_ampnumber+1
2098
2099 for loopUVCTdiag in self.get_loop_UVCT_diagrams():
2100 for wf in loopUVCTdiag.get('wavefunctions'):
2101 wf.set('number',wfnumber)
2102 wfnumber=wfnumber+1
2103 for amp in loopUVCTdiag.get('amplitudes'):
2104 amp.set('number',CT_ampnumber)
2105 CT_ampnumber=CT_ampnumber+1
2106
2108 """ Give the correct number for the optimized output to the wavefunctions
2109 and amplitudes building the loops """
2110 CT_ampnumber=1
2111 loop_ampnumber=self.get_number_of_CT_amplitudes()+1
2112 loopwfnumber=1
2113
2114 for loopdiag in self.get_loop_diagrams():
2115 for wf in loopdiag.get('wavefunctions'):
2116 wf.set('number',wfnumber)
2117 wfnumber=wfnumber+1
2118 for lwf in loopdiag.get('loop_wavefunctions'):
2119 lwf.set('number',loopwfnumber)
2120 loopwfnumber=loopwfnumber+1
2121 for loopamp in loopdiag.get_loop_amplitudes():
2122
2123
2124 start_loop_wf = loopamp.get_starting_loop_wavefunction()
2125 if start_loop_wf.get('fermionflow')==1:
2126 start_loop_wf.set('number',0)
2127 else:
2128
2129 start_loop_wf.set('number',-1)
2130 for amp in loopamp['amplitudes']:
2131 amp.set('number',loop_ampnumber)
2132 loop_ampnumber=loop_ampnumber+1
2133 for ctamp in loopdiag.get_ct_amplitudes():
2134 ctamp.set('number',CT_ampnumber)
2135 CT_ampnumber=CT_ampnumber+1
2136
2137 for loopUVCTdiag in self.get_loop_UVCT_diagrams():
2138 for wf in loopUVCTdiag.get('wavefunctions'):
2139 wf.set('number',wfnumber)
2140 wfnumber=wfnumber+1
2141 for amp in loopUVCTdiag.get('amplitudes'):
2142 amp.set('number',CT_ampnumber)
2143 CT_ampnumber=CT_ampnumber+1
2144
2146 """After the generation of the helas objects, we can give up on having
2147 a unique number identifying the helas wavefunction and amplitudes and
2148 instead use a labeling which is optimal for the output of the loop process.
2149 Also we tag all the LoopHelasAmplitude which are identical with the same
2150 'number' attribute."""
2151
2152
2153 if self.optimized_output:
2154 self.relabel_loop_amplitudes_optimized()
2155 else:
2156 self.relabel_loop_amplitudes()
2157
2158
2159 wfnumber=1
2160 ampnumber=1
2161 for borndiag in self.get_born_diagrams():
2162 for wf in borndiag.get('wavefunctions'):
2163 wf.set('number',wfnumber)
2164 wfnumber=wfnumber+1
2165 for amp in borndiag.get('amplitudes'):
2166 amp.set('number',ampnumber)
2167 ampnumber=ampnumber+1
2168
2169
2170
2171 if self.optimized_output:
2172 self.relabel_loop_wfs_and_amps_optimized(wfnumber)
2173 for lwf in [lwf for loopdiag in self.get_loop_diagrams() for \
2174 lwf in loopdiag.get('loop_wavefunctions')]:
2175 lwf.set('me_id',lwf.get('number'))
2176 else:
2177 self.relabel_loop_wfs_and_amps(wfnumber)
2178
2179
2180
2181 for wf in self.get_all_wavefunctions():
2182 wf.set('me_id',wf.get('number'))
2183
2184
2186 """Gives the total number of wavefunctions for this ME, including the
2187 loop ones"""
2188
2189 return len(self.get_all_wavefunctions())
2190
2192 """ Gives the total number of loop wavefunctions for this ME."""
2193 return sum([len(ldiag.get('loop_wavefunctions')) for ldiag in \
2194 self.get_loop_diagrams()])
2195
2197 """Gives the total number of wavefunctions for this ME, excluding the
2198 loop ones."""
2199
2200 return sum([ len(d.get('wavefunctions')) for d in self.get('diagrams')])
2201
2203 """Gives a list of all wavefunctions for this ME"""
2204
2205 allwfs=sum([d.get('wavefunctions') for d in self.get('diagrams')], [])
2206 for d in self['diagrams']:
2207 if isinstance(d,LoopHelasDiagram):
2208 for l in d.get_loop_amplitudes():
2209 allwfs += l.get('wavefunctions')
2210
2211 return allwfs
2212
2226
2228 """Gives (number or external particles, number of
2229 incoming particles)"""
2230
2231 external_wfs = filter(lambda wf:
2232 not wf.get('mothers') and not wf.get('is_loop'),
2233 self.get_all_wavefunctions())
2234
2235 return (len(set([wf.get('number_external') for wf in \
2236 external_wfs])),
2237 len(set([wf.get('number_external') for wf in \
2238 filter(lambda wf: wf.get('leg_state') == False,
2239 external_wfs)])))
2240
2242 """Gives the total number of amplitudes for this ME, including the loop
2243 ones."""
2244
2245 return len(self.get_all_amplitudes())
2246
2253
2255 """Gives the total number of amplitudes for this ME, excluding those
2256 inside the loop amplitudes. (So only one is counted per loop amplitude.)
2257 """
2258
2259 return sum([ len(d.get('amplitudes')) for d in \
2260 self.get('diagrams')])
2261
2263 """Gives the total number of helas amplitudes for the loop diagrams of this ME,
2264 excluding those inside the loop amplitudes, but including the CT-terms.
2265 (So only one amplitude is counted per loop amplitude.)
2266 """
2267
2268 return sum([len(d.get('amplitudes')) for d in (self.get_loop_diagrams()+
2269 self.get_loop_UVCT_diagrams())])
2270
2272 """Gives the total number of amplitudes for the born diagrams of this ME
2273 """
2274
2275 return sum([len(d.get('amplitudes')) for d in self.get_born_diagrams()])
2276
2287
2293
2300
2307
2335
2351
2353 """ Returns the list of the helas loop amplitude of type
2354 CALL LOOP_I_J(_K)(...) used for this matrix element """
2355
2356
2357
2358 if self.optimized_output:
2359 last_relevant_index=3
2360 else:
2361 last_relevant_index=4
2362
2363 return list(set([lamp.get_call_key()[1:last_relevant_index] \
2364 for ldiag in self.get_loop_diagrams() for lamp in \
2365 ldiag.get_loop_amplitudes()]))
2366
2376
2386
2388 """ Just to forbid the usage of this generic function in a
2389 LoopHelasMatrixElement"""
2390
2391 raise self.PhysicsObjectError, \
2392 "Usage of get_color_amplitudes is not allowed in a LoopHelasMatrixElement"
2393
2395 """Return a list of (coefficient, amplitude number) lists,
2396 corresponding to the JAMPs for this born color basis and the born
2397 diagrams of this LoopMatrixElement. The coefficients are given in the
2398 format (fermion factor, color coeff (frac), imaginary, Nc power)."""
2399
2400 return super(LoopHelasMatrixElement,self).generate_color_amplitudes(\
2401 self['born_color_basis'],self.get_born_diagrams())
2402
2404 """Return a list of (coefficient, amplitude number) lists,
2405 corresponding to the JAMPs for this loop color basis and the loop
2406 diagrams of this LoopMatrixElement. The coefficients are given in the
2407 format (fermion factor, color coeff (frac), imaginary, Nc power)."""
2408
2409 diagrams=self.get_loop_diagrams()
2410 color_basis=self['loop_color_basis']
2411
2412 if not color_basis:
2413
2414
2415 col_amp = []
2416 for diagram in diagrams:
2417 for amplitude in diagram.get('amplitudes'):
2418 col_amp.append(((amplitude.get('fermionfactor'),
2419 1, False, 0),
2420 amplitude.get('number')))
2421 return [col_amp]
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432 LoopDiagramsHelasAmplitudeList=self.get_helas_amplitudes_loop_diagrams()
2433
2434
2435 for i, helas_amp_list in enumerate(LoopDiagramsHelasAmplitudeList):
2436 new_helas_amp_list=helas_objects.HelasAmplitudeList()
2437 for helas_amp in helas_amp_list:
2438 if isinstance(helas_amp,LoopHelasAmplitude):
2439 new_helas_amp_list.extend(helas_amp['amplitudes'])
2440 else:
2441 new_helas_amp_list.append(helas_amp)
2442 LoopDiagramsHelasAmplitudeList[i]=new_helas_amp_list
2443
2444
2445
2446
2447
2448 col_amp_list = []
2449 for i, col_basis_elem in \
2450 enumerate(sorted(color_basis.keys())):
2451
2452 col_amp = []
2453
2454 for diag_tuple in color_basis[col_basis_elem]:
2455 res_amps = filter(lambda amp: \
2456 tuple(amp.get('color_indices')) == diag_tuple[1],
2457 LoopDiagramsHelasAmplitudeList[diag_tuple[0]])
2458 if not res_amps:
2459 raise self.PhysicsObjectError, \
2460 """No amplitude found for color structure
2461 %s and color index chain (%s) (diagram %i)""" % \
2462 (col_basis_elem,
2463 str(diag_tuple[1]),
2464 diag_tuple[0])
2465
2466 for res_amp in res_amps:
2467 col_amp.append(((res_amp.get('fermionfactor'),
2468 diag_tuple[2],
2469 diag_tuple[3],
2470 diag_tuple[4]),
2471 res_amp.get('number')))
2472
2473 col_amp_list.append(col_amp)
2474
2475 return col_amp_list
2476
2478 """ When creating the base_objects.Diagram in get_base_amplitudes(),
2479 each LoopHelasDiagram will lead to one loop_base_objects.LoopDiagram
2480 for its LoopHelasAmplitude and one other for each of its counter-term
2481 (with different interaction id). This function return a list for which
2482 each element is a HelasAmplitudeList corresponding to the HelasAmplitudes
2483 related to a given loop_base_objects.LoopDiagram generated """
2484
2485 amplitudes_loop_diagrams=[]
2486
2487 for diag in self.get_loop_diagrams():
2488
2489 amplitudes_loop_diagrams.append(diag.get_loop_amplitudes())
2490
2491
2492
2493
2494
2495
2496
2497
2498 ctIDs={}
2499 for ctamp in diag.get_ct_amplitudes():
2500 try:
2501 ctIDs[ctamp.get('interaction_id')].append(ctamp)
2502 except KeyError:
2503 ctIDs[ctamp.get('interaction_id')]=\
2504 helas_objects.HelasAmplitudeList([ctamp])
2505
2506
2507 keys=ctIDs.keys()
2508 keys.sort()
2509 for key in keys:
2510 amplitudes_loop_diagrams.append(ctIDs[key])
2511
2512 for diag in self.get_loop_UVCT_diagrams():
2513 amplitudes_loop_diagrams.append(diag.get_loop_UVCTamplitudes())
2514
2515 return amplitudes_loop_diagrams
2516
2518 """Generate a loop_diagram_generation.LoopAmplitude from a
2519 LoopHelasMatrixElement. This is used to generate both color
2520 amplitudes and diagram drawing."""
2521
2522
2523
2524
2525 optimization = 1
2526 if len(filter(lambda wf: wf.get('number') == 1,
2527 self.get_all_wavefunctions())) > 1:
2528 optimization = 0
2529
2530 model = self.get('processes')[0].get('model')
2531
2532 wf_dict = {}
2533 vx_list = []
2534 diagrams = base_objects.DiagramList()
2535
2536
2537 for diag in self.get_born_diagrams():
2538 newdiag=diag.get('amplitudes')[0].get_base_diagram(\
2539 wf_dict, vx_list, optimization)
2540 diagrams.append(loop_base_objects.LoopDiagram({
2541 'vertices':newdiag['vertices'],'type':0}))
2542
2543
2544
2545
2546 dtype=1
2547 for HelasAmpList in self.get_helas_amplitudes_loop_diagrams():
2548
2549
2550 if isinstance(HelasAmpList[0],LoopHelasAmplitude):
2551 diagrams.append(HelasAmpList[0].get_base_diagram(\
2552 wf_dict, vx_list, optimization))
2553 dtype=diagrams[-1]['type']
2554 elif isinstance(HelasAmpList[0],LoopHelasUVCTAmplitude):
2555 diagrams.append(HelasAmpList[0].\
2556 get_base_diagram(wf_dict, vx_list, optimization))
2557 else:
2558 newdiag=HelasAmpList[0].get_base_diagram(wf_dict, vx_list, optimization)
2559 diagrams.append(loop_base_objects.LoopDiagram({
2560 'vertices':newdiag['vertices'],'type':-dtype}))
2561
2562
2563 for diag in diagrams:
2564 diag.calculate_orders(self.get('processes')[0].get('model'))
2565
2566 return loop_diagram_generation.LoopAmplitude({\
2567 'process': self.get('processes')[0],
2568 'diagrams': diagrams})
2569
2574 """LoopHelasProcess: Analogous of HelasMultiProcess except that it is suited
2575 for LoopAmplitude and with the peculiarity that it is always treating only
2576 one loop amplitude. So this LoopHelasProcess correspond to only one single
2577 subprocess without multiparticle labels (contrary to HelasMultiProcess)."""
2578
2579
2580 matrix_element_class = LoopHelasMatrixElement
2581
2582 - def __init__(self, argument=None, combine_matrix_elements=True,
2583 optimized_output = True, compute_loop_nc = False, matrix_element_opts={}):
2584 """ Allow for the initialization of the HelasMultiProcess with the
2585 right argument 'optimized_output' for the helas_matrix_element options.
2586 """
2587
2588 matrix_element_opts = dict(matrix_element_opts)
2589 matrix_element_opts.update({'optimized_output' : optimized_output})
2590
2591 super(LoopHelasProcess, self).__init__(argument, combine_matrix_elements,
2592 compute_loop_nc = compute_loop_nc,
2593 matrix_element_opts = matrix_element_opts)
2594
2595 @classmethod
2596 - def process_color(cls,matrix_element,color_information,compute_loop_nc=False):
2597 """ Process the color information for a given matrix
2598 element made of a loop diagrams. It will create a different
2599 color matrix depending on wether the process has a born or not.
2600 The compute_loop_nc sets wheter independent tracking of Nc power coming
2601 from the color loop trace is necessary or not (it is time consuming).
2602 """
2603 if matrix_element.get('processes')[0]['has_born']:
2604 logger.debug('Computing the loop and Born color basis')
2605 else:
2606 logger.debug('Computing the loop color basis')
2607
2608
2609 for key in color_information:
2610 exec("%s=color_information['%s']"%(key,key))
2611
2612
2613
2614
2615 matrix_element.relabel_helas_objects()
2616
2617
2618
2619
2620 new_amp = matrix_element.get_base_amplitude()
2621 matrix_element.set('base_amplitude', new_amp)
2622
2623 loop_col_basis = loop_color_amp.LoopColorBasis(
2624 compute_loop_nc = compute_loop_nc)
2625 loop_colorize_obj = loop_col_basis.create_loop_color_dict_list(\
2626 matrix_element.get('base_amplitude'),
2627 )
2628 try:
2629
2630
2631
2632 loop_col_basis_index = list_colorize.index(loop_colorize_obj)
2633 loop_col_basis = list_color_basis[loop_col_basis_index]
2634 except ValueError:
2635
2636 list_colorize.append(loop_colorize_obj)
2637 loop_col_basis.build()
2638 loop_col_basis_index = len(list_color_basis)
2639 list_color_basis.append(loop_col_basis)
2640 logger.info(\
2641 "Processing color information for %s" % \
2642 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2643 replace('Process', 'loop process'))
2644 else:
2645 logger.info(\
2646 "Reusing existing color information for %s" % \
2647 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2648 replace('Process', 'loop process'))
2649
2650 if new_amp['process']['has_born']:
2651 born_col_basis = loop_color_amp.LoopColorBasis()
2652 born_colorize_obj = born_col_basis.create_born_color_dict_list(\
2653 matrix_element.get('base_amplitude'))
2654 try:
2655
2656
2657
2658 born_col_basis_index = list_colorize.index(born_colorize_obj)
2659 born_col_basis = list_color_basis[born_col_basis_index]
2660 except ValueError:
2661
2662 list_colorize.append(born_colorize_obj)
2663 born_col_basis.build()
2664 born_col_basis_index = len(list_color_basis)
2665 list_color_basis.append(born_col_basis)
2666 logger.info(\
2667 "Processing color information for %s" % \
2668 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2669 replace('Process', 'born process'))
2670 else:
2671 logger.info(\
2672 "Reusing existing color information for %s" % \
2673 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2674 replace('Process', 'born process'))
2675 loopborn_matrices_key=(loop_col_basis_index,born_col_basis_index)
2676 else:
2677 loopborn_matrices_key=(loop_col_basis_index,loop_col_basis_index)
2678
2679
2680
2681 try:
2682
2683
2684
2685 col_matrix = dict_loopborn_matrices[loopborn_matrices_key]
2686 except KeyError:
2687
2688 col_matrix = color_amp.ColorMatrix(\
2689 list_color_basis[loopborn_matrices_key[0]],
2690 list_color_basis[loopborn_matrices_key[1]])
2691 dict_loopborn_matrices[loopborn_matrices_key]=col_matrix
2692 logger.info(\
2693 "Creating color matrix %s" % \
2694 matrix_element.get('processes')[0].nice_string().\
2695 replace('Process', 'loop process'))
2696 else:
2697 logger.info(\
2698 "Reusing existing color matrix for %s" % \
2699 matrix_element.get('processes')[0].nice_string().\
2700 replace('Process', 'loop process'))
2701
2702 matrix_element.set('loop_color_basis',loop_col_basis)
2703 if new_amp['process']['has_born']:
2704 matrix_element.set('born_color_basis',born_col_basis)
2705 matrix_element.set('color_matrix',col_matrix)
2706