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