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.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 spin of the loop particle with maximum spin among all
2007 the loop contributing to this ME"""
2008 return max([lwf.get('spin') for lwf in \
2009 self.get_all_loop_wavefunctions()])
2010
2012 """Give a unique number to each non-equivalent (at the level of the output)
2013 LoopHelasAmplitude """
2014
2015 LoopHelasAmplitudeRecognized=[]
2016 for lamp in \
2017 sum([d.get_loop_amplitudes() for d in self.get_loop_diagrams()],[]):
2018 lamp.set('number',-1)
2019 for lamp2 in LoopHelasAmplitudeRecognized:
2020 if lamp.is_equivalent(lamp2):
2021
2022
2023 lamp.set('number',lamp2.get('number'))
2024 break;
2025 if lamp.get('number')==-1:
2026 lamp.set('number',(len(LoopHelasAmplitudeRecognized)+1))
2027 LoopHelasAmplitudeRecognized.append(lamp)
2028
2030 """Give a unique number to each LoopHelasAmplitude. These will be the
2031 number used for the LOOPCOEF array in the optimized output and the
2032 grouping is done in a further stage by adding all the LOOPCOEF sharing
2033 the same denominator to a given one using the 'loop_group_id' attribute
2034 of the LoopHelasAmplitudes. """
2035
2036 lamp_number=1
2037 for lamp in \
2038 sum([d.get_loop_amplitudes() for d in self.get_loop_diagrams()],[]):
2039 lamp.set('number',lamp_number)
2040 lamp_number += 1
2041
2043 """ Give the correct number for the default output to the wavefunctions
2044 and amplitudes building the loops """
2045
2046
2047 CT_ampnumber=1
2048 loop_ampnumber=self.get_number_of_CT_amplitudes()+1
2049 loopwfnumber=1
2050
2051 for loopdiag in self.get_loop_diagrams():
2052 for wf in loopdiag.get('wavefunctions'):
2053 wf.set('number',wfnumber)
2054 wfnumber=wfnumber+1
2055 for loopamp in loopdiag.get_loop_amplitudes():
2056 loopwfnumber=1
2057 for loopwf in loopamp['wavefunctions']:
2058 loopwf.set('number',loopwfnumber)
2059 loopwfnumber=loopwfnumber+1
2060 for amp in loopamp['amplitudes']:
2061 amp.set('number',loop_ampnumber)
2062 loop_ampnumber=loop_ampnumber+1
2063 for ctamp in loopdiag.get_ct_amplitudes():
2064 ctamp.set('number',CT_ampnumber)
2065 CT_ampnumber=CT_ampnumber+1
2066
2067 for loopUVCTdiag in self.get_loop_UVCT_diagrams():
2068 for wf in loopUVCTdiag.get('wavefunctions'):
2069 wf.set('number',wfnumber)
2070 wfnumber=wfnumber+1
2071 for amp in loopUVCTdiag.get('amplitudes'):
2072 amp.set('number',CT_ampnumber)
2073 CT_ampnumber=CT_ampnumber+1
2074
2076 """ Give the correct number for the optimized output to the wavefunctions
2077 and amplitudes building the loops """
2078 CT_ampnumber=1
2079 loop_ampnumber=self.get_number_of_CT_amplitudes()+1
2080 loopwfnumber=1
2081
2082 for loopdiag in self.get_loop_diagrams():
2083 for wf in loopdiag.get('wavefunctions'):
2084 wf.set('number',wfnumber)
2085 wfnumber=wfnumber+1
2086 for lwf in loopdiag.get('loop_wavefunctions'):
2087 lwf.set('number',loopwfnumber)
2088 loopwfnumber=loopwfnumber+1
2089 for loopamp in loopdiag.get_loop_amplitudes():
2090
2091
2092 loopamp.get_starting_loop_wavefunction().set('number',0)
2093 for amp in loopamp['amplitudes']:
2094 amp.set('number',loop_ampnumber)
2095 loop_ampnumber=loop_ampnumber+1
2096 for ctamp in loopdiag.get_ct_amplitudes():
2097 ctamp.set('number',CT_ampnumber)
2098 CT_ampnumber=CT_ampnumber+1
2099
2100 for loopUVCTdiag in self.get_loop_UVCT_diagrams():
2101 for wf in loopUVCTdiag.get('wavefunctions'):
2102 wf.set('number',wfnumber)
2103 wfnumber=wfnumber+1
2104 for amp in loopUVCTdiag.get('amplitudes'):
2105 amp.set('number',CT_ampnumber)
2106 CT_ampnumber=CT_ampnumber+1
2107
2109 """After the generation of the helas objects, we can give up on having
2110 a unique number identifying the helas wavefunction and amplitudes and
2111 instead use a labeling which is optimal for the output of the loop process.
2112 Also we tag all the LoopHelasAmplitude which are identical with the same
2113 'number' attribute."""
2114
2115
2116 if self.optimized_output:
2117 self.relabel_loop_amplitudes_optimized()
2118 else:
2119 self.relabel_loop_amplitudes()
2120
2121
2122 wfnumber=1
2123 ampnumber=1
2124 for borndiag in self.get_born_diagrams():
2125 for wf in borndiag.get('wavefunctions'):
2126 wf.set('number',wfnumber)
2127 wfnumber=wfnumber+1
2128 for amp in borndiag.get('amplitudes'):
2129 amp.set('number',ampnumber)
2130 ampnumber=ampnumber+1
2131
2132
2133
2134 if self.optimized_output:
2135 self.relabel_loop_wfs_and_amps_optimized(wfnumber)
2136 for lwf in [lwf for loopdiag in self.get_loop_diagrams() for \
2137 lwf in loopdiag.get('loop_wavefunctions')]:
2138 lwf.set('me_id',lwf.get('number'))
2139 else:
2140 self.relabel_loop_wfs_and_amps(wfnumber)
2141
2142
2143
2144 for wf in self.get_all_wavefunctions():
2145 wf.set('me_id',wf.get('number'))
2146
2147
2149 """Gives the total number of wavefunctions for this ME, including the
2150 loop ones"""
2151
2152 return len(self.get_all_wavefunctions())
2153
2155 """ Gives the total number of loop wavefunctions for this ME."""
2156 return sum([len(ldiag.get('loop_wavefunctions')) for ldiag in \
2157 self.get_loop_diagrams()])
2158
2160 """Gives the total number of wavefunctions for this ME, excluding the
2161 loop ones."""
2162
2163 return sum([ len(d.get('wavefunctions')) for d in self.get('diagrams')])
2164
2166 """Gives a list of all wavefunctions for this ME"""
2167
2168 allwfs=sum([d.get('wavefunctions') for d in self.get('diagrams')], [])
2169 for d in self['diagrams']:
2170 if isinstance(d,LoopHelasDiagram):
2171 for l in d.get_loop_amplitudes():
2172 allwfs += l.get('wavefunctions')
2173
2174 return allwfs
2175
2189
2191 """Gives (number or external particles, number of
2192 incoming particles)"""
2193
2194 external_wfs = filter(lambda wf:
2195 not wf.get('mothers') and not wf.get('is_loop'),
2196 self.get_all_wavefunctions())
2197
2198 return (len(set([wf.get('number_external') for wf in \
2199 external_wfs])),
2200 len(set([wf.get('number_external') for wf in \
2201 filter(lambda wf: wf.get('leg_state') == False,
2202 external_wfs)])))
2203
2205 """Gives the total number of amplitudes for this ME, including the loop
2206 ones."""
2207
2208 return len(self.get_all_amplitudes())
2209
2216
2218 """Gives the total number of amplitudes for this ME, excluding those
2219 inside the loop amplitudes. (So only one is counted per loop amplitude.)
2220 """
2221
2222 return sum([ len(d.get('amplitudes')) for d in \
2223 self.get('diagrams')])
2224
2226 """Gives the total number of helas amplitudes for the loop diagrams of this ME,
2227 excluding those inside the loop amplitudes, but including the CT-terms.
2228 (So only one amplitude is counted per loop amplitude.)
2229 """
2230
2231 return sum([len(d.get('amplitudes')) for d in (self.get_loop_diagrams()+
2232 self.get_loop_UVCT_diagrams())])
2233
2235 """Gives the total number of amplitudes for the born diagrams of this ME
2236 """
2237
2238 return sum([len(d.get('amplitudes')) for d in self.get_born_diagrams()])
2239
2250
2256
2263
2270
2298
2314
2316 """ Returns the list of the helas loop amplitude of type
2317 CALL LOOP_I_J(_K)(...) used for this matrix element """
2318
2319
2320
2321 if self.optimized_output:
2322 last_relevant_index=3
2323 else:
2324 last_relevant_index=4
2325
2326 return list(set([lamp.get_call_key()[1:last_relevant_index] \
2327 for ldiag in self.get_loop_diagrams() for lamp in \
2328 ldiag.get_loop_amplitudes()]))
2329
2339
2349
2351 """ Just to forbid the usage of this generic function in a
2352 LoopHelasMatrixElement"""
2353
2354 raise self.PhysicsObjectError, \
2355 "Usage of get_color_amplitudes is not allowed in a LoopHelasMatrixElement"
2356
2358 """Return a list of (coefficient, amplitude number) lists,
2359 corresponding to the JAMPs for this born color basis and the born
2360 diagrams of this LoopMatrixElement. The coefficients are given in the
2361 format (fermion factor, color coeff (frac), imaginary, Nc power)."""
2362
2363 return super(LoopHelasMatrixElement,self).generate_color_amplitudes(\
2364 self['born_color_basis'],self.get_born_diagrams())
2365
2367 """Return a list of (coefficient, amplitude number) lists,
2368 corresponding to the JAMPs for this loop color basis and the loop
2369 diagrams of this LoopMatrixElement. The coefficients are given in the
2370 format (fermion factor, color coeff (frac), imaginary, Nc power)."""
2371
2372 diagrams=self.get_loop_diagrams()
2373 color_basis=self['loop_color_basis']
2374
2375 if not color_basis:
2376
2377
2378 col_amp = []
2379 for diagram in diagrams:
2380 for amplitude in diagram.get('amplitudes'):
2381 col_amp.append(((amplitude.get('fermionfactor'),
2382 1, False, 0),
2383 amplitude.get('number')))
2384 return [col_amp]
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395 LoopDiagramsHelasAmplitudeList=self.get_helas_amplitudes_loop_diagrams()
2396
2397
2398 for i, helas_amp_list in enumerate(LoopDiagramsHelasAmplitudeList):
2399 new_helas_amp_list=helas_objects.HelasAmplitudeList()
2400 for helas_amp in helas_amp_list:
2401 if isinstance(helas_amp,LoopHelasAmplitude):
2402 new_helas_amp_list.extend(helas_amp['amplitudes'])
2403 else:
2404 new_helas_amp_list.append(helas_amp)
2405 LoopDiagramsHelasAmplitudeList[i]=new_helas_amp_list
2406
2407
2408
2409
2410
2411 col_amp_list = []
2412 for i, col_basis_elem in \
2413 enumerate(sorted(color_basis.keys())):
2414
2415 col_amp = []
2416
2417 for diag_tuple in color_basis[col_basis_elem]:
2418 res_amps = filter(lambda amp: \
2419 tuple(amp.get('color_indices')) == diag_tuple[1],
2420 LoopDiagramsHelasAmplitudeList[diag_tuple[0]])
2421 if not res_amps:
2422 raise self.PhysicsObjectError, \
2423 """No amplitude found for color structure
2424 %s and color index chain (%s) (diagram %i)""" % \
2425 (col_basis_elem,
2426 str(diag_tuple[1]),
2427 diag_tuple[0])
2428
2429 for res_amp in res_amps:
2430 col_amp.append(((res_amp.get('fermionfactor'),
2431 diag_tuple[2],
2432 diag_tuple[3],
2433 diag_tuple[4]),
2434 res_amp.get('number')))
2435
2436 col_amp_list.append(col_amp)
2437
2438 return col_amp_list
2439
2441 """ When creating the base_objects.Diagram in get_base_amplitudes(),
2442 each LoopHelasDiagram will lead to one loop_base_objects.LoopDiagram
2443 for its LoopHelasAmplitude and one other for each of its counter-term
2444 (with different interaction id). This function return a list for which
2445 each element is a HelasAmplitudeList corresponding to the HelasAmplitudes
2446 related to a given loop_base_objects.LoopDiagram generated """
2447
2448 amplitudes_loop_diagrams=[]
2449
2450 for diag in self.get_loop_diagrams():
2451
2452 amplitudes_loop_diagrams.append(diag.get_loop_amplitudes())
2453
2454
2455
2456
2457
2458
2459
2460
2461 ctIDs={}
2462 for ctamp in diag.get_ct_amplitudes():
2463 try:
2464 ctIDs[ctamp.get('interaction_id')].append(ctamp)
2465 except KeyError:
2466 ctIDs[ctamp.get('interaction_id')]=\
2467 helas_objects.HelasAmplitudeList([ctamp])
2468
2469
2470 keys=ctIDs.keys()
2471 keys.sort()
2472 for key in keys:
2473 amplitudes_loop_diagrams.append(ctIDs[key])
2474
2475 for diag in self.get_loop_UVCT_diagrams():
2476 amplitudes_loop_diagrams.append(diag.get_loop_UVCTamplitudes())
2477
2478 return amplitudes_loop_diagrams
2479
2481 """Generate a loop_diagram_generation.LoopAmplitude from a
2482 LoopHelasMatrixElement. This is used to generate both color
2483 amplitudes and diagram drawing."""
2484
2485
2486
2487
2488 optimization = 1
2489 if len(filter(lambda wf: wf.get('number') == 1,
2490 self.get_all_wavefunctions())) > 1:
2491 optimization = 0
2492
2493 model = self.get('processes')[0].get('model')
2494
2495 wf_dict = {}
2496 vx_list = []
2497 diagrams = base_objects.DiagramList()
2498
2499
2500 for diag in self.get_born_diagrams():
2501 newdiag=diag.get('amplitudes')[0].get_base_diagram(\
2502 wf_dict, vx_list, optimization)
2503 diagrams.append(loop_base_objects.LoopDiagram({
2504 'vertices':newdiag['vertices'],'type':0}))
2505
2506
2507
2508
2509 dtype=1
2510 for HelasAmpList in self.get_helas_amplitudes_loop_diagrams():
2511
2512
2513 if isinstance(HelasAmpList[0],LoopHelasAmplitude):
2514 diagrams.append(HelasAmpList[0].get_base_diagram(\
2515 wf_dict, vx_list, optimization))
2516 dtype=diagrams[-1]['type']
2517 elif isinstance(HelasAmpList[0],LoopHelasUVCTAmplitude):
2518 diagrams.append(HelasAmpList[0].\
2519 get_base_diagram(wf_dict, vx_list, optimization))
2520 else:
2521 newdiag=HelasAmpList[0].get_base_diagram(wf_dict, vx_list, optimization)
2522 diagrams.append(loop_base_objects.LoopDiagram({
2523 'vertices':newdiag['vertices'],'type':-dtype}))
2524
2525
2526 for diag in diagrams:
2527 diag.calculate_orders(self.get('processes')[0].get('model'))
2528
2529 return loop_diagram_generation.LoopAmplitude({\
2530 'process': self.get('processes')[0],
2531 'diagrams': diagrams})
2532
2537 """LoopHelasProcess: Analogous of HelasMultiProcess except that it is suited
2538 for LoopAmplitude and with the peculiarity that it is always treating only
2539 one loop amplitude. So this LoopHelasProcess correspond to only one single
2540 subprocess without multiparticle labels (contrary to HelasMultiProcess)."""
2541
2542
2543 matrix_element_class = LoopHelasMatrixElement
2544
2545 - def __init__(self, argument=None, combine_matrix_elements=True,
2546 optimized_output = True, compute_loop_nc = False, matrix_element_opts={}):
2547 """ Allow for the initialization of the HelasMultiProcess with the
2548 right argument 'optimized_output' for the helas_matrix_element options.
2549 """
2550
2551 matrix_element_opts = dict(matrix_element_opts)
2552 matrix_element_opts.update({'optimized_output' : optimized_output})
2553
2554 super(LoopHelasProcess, self).__init__(argument, combine_matrix_elements,
2555 compute_loop_nc = compute_loop_nc,
2556 matrix_element_opts = matrix_element_opts)
2557
2558 @classmethod
2559 - def process_color(cls,matrix_element,color_information,compute_loop_nc=False):
2560 """ Process the color information for a given matrix
2561 element made of a loop diagrams. It will create a different
2562 color matrix depending on wether the process has a born or not.
2563 The compute_loop_nc sets wheter independent tracking of Nc power coming
2564 from the color loop trace is necessary or not (it is time consuming).
2565 """
2566 if matrix_element.get('processes')[0]['has_born']:
2567 logger.debug('Computing the loop and Born color basis')
2568 else:
2569 logger.debug('Computing the loop color basis')
2570
2571
2572 for key in color_information:
2573 exec("%s=color_information['%s']"%(key,key))
2574
2575
2576
2577
2578 matrix_element.relabel_helas_objects()
2579
2580
2581
2582
2583 new_amp = matrix_element.get_base_amplitude()
2584 matrix_element.set('base_amplitude', new_amp)
2585
2586 loop_col_basis = loop_color_amp.LoopColorBasis(
2587 compute_loop_nc = compute_loop_nc)
2588 loop_colorize_obj = loop_col_basis.create_loop_color_dict_list(\
2589 matrix_element.get('base_amplitude'),
2590 )
2591 try:
2592
2593
2594
2595 loop_col_basis_index = list_colorize.index(loop_colorize_obj)
2596 loop_col_basis = list_color_basis[loop_col_basis_index]
2597 except ValueError:
2598
2599 list_colorize.append(loop_colorize_obj)
2600 loop_col_basis.build()
2601 loop_col_basis_index = len(list_color_basis)
2602 list_color_basis.append(loop_col_basis)
2603 logger.info(\
2604 "Processing color information for %s" % \
2605 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2606 replace('Process', 'loop process'))
2607 else:
2608 logger.info(\
2609 "Reusing existing color information for %s" % \
2610 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2611 replace('Process', 'loop process'))
2612
2613 if new_amp['process']['has_born']:
2614 born_col_basis = loop_color_amp.LoopColorBasis()
2615 born_colorize_obj = born_col_basis.create_born_color_dict_list(\
2616 matrix_element.get('base_amplitude'))
2617 try:
2618
2619
2620
2621 born_col_basis_index = list_colorize.index(born_colorize_obj)
2622 born_col_basis = list_color_basis[born_col_basis_index]
2623 except ValueError:
2624
2625 list_colorize.append(born_colorize_obj)
2626 born_col_basis.build()
2627 born_col_basis_index = len(list_color_basis)
2628 list_color_basis.append(born_col_basis)
2629 logger.info(\
2630 "Processing color information for %s" % \
2631 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2632 replace('Process', 'born process'))
2633 else:
2634 logger.info(\
2635 "Reusing existing color information for %s" % \
2636 matrix_element.get('processes')[0].nice_string(print_weighted=False).\
2637 replace('Process', 'born process'))
2638 loopborn_matrices_key=(loop_col_basis_index,born_col_basis_index)
2639 else:
2640 loopborn_matrices_key=(loop_col_basis_index,loop_col_basis_index)
2641
2642
2643
2644 try:
2645
2646
2647
2648 col_matrix = dict_loopborn_matrices[loopborn_matrices_key]
2649 except KeyError:
2650
2651 col_matrix = color_amp.ColorMatrix(\
2652 list_color_basis[loopborn_matrices_key[0]],
2653 list_color_basis[loopborn_matrices_key[1]])
2654 dict_loopborn_matrices[loopborn_matrices_key]=col_matrix
2655 logger.info(\
2656 "Creating color matrix %s" % \
2657 matrix_element.get('processes')[0].nice_string().\
2658 replace('Process', 'loop process'))
2659 else:
2660 logger.info(\
2661 "Reusing existing color matrix for %s" % \
2662 matrix_element.get('processes')[0].nice_string().\
2663 replace('Process', 'loop process'))
2664
2665 matrix_element.set('loop_color_basis',loop_col_basis)
2666 if new_amp['process']['has_born']:
2667 matrix_element.set('born_color_basis',born_col_basis)
2668 matrix_element.set('color_matrix',col_matrix)
2669