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