1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Definitions of all basic objects with extra features to treat loop
17 diagrams"""
18
19 import copy
20 import itertools
21 import logging
22 import numbers
23 import os
24 import re
25 import madgraph.core.color_algebra as color
26 import madgraph.core.diagram_generation as diagram_generation
27 import madgraph.core.base_objects as base_objects
28 from madgraph import MadGraph5Error, MG5DIR
29
30 logger = logging.getLogger('madgraph.loop_base_objects')
36 """LoopDiagram: Contains an additional tag to uniquely identify the diagram
37 if it contains a loop. Also has many additional functions useful only
38 for loop computations.
39 """
40
41
42
43
44
45
46
47 cutting_method = 'optimal'
48
50 """Default values for all properties"""
51
52 super(LoopDiagram,self).default_setup()
53
54
55
56
57
58 self['tag'] = []
59
60
61
62
63
64
65 self['canonical_tag'] = []
66
67
68
69
70 self['type'] = 0
71
72
73 self['CT_vertices'] = base_objects.VertexList()
74
75 - def filter(self, name, value):
76 """Filter for valid diagram property values."""
77
78 if name == 'tag':
79 if not isinstance(value, list):
80 raise self.PhysicsObjectError, \
81 "%s is not a valid tag" % str(value)
82 else:
83 for item in value:
84 if (len(item)!=3 or \
85 not isinstance(item[0],base_objects.Leg) or \
86 not isinstance(item[1],list)) or \
87 not isinstance(item[2],base_objects.Vertex):
88 raise self.PhysicsObjectError, \
89 "%s is not a valid tag" % str(value)
90
91 if name == 'canonical_tag':
92 if not isinstance(value, list):
93 raise self.PhysicsObjectError, \
94 "%s is not a valid tag" % str(value)
95 else:
96 for item in value:
97 if (len(item)!=3 or not isinstance(item[0],int) or \
98 not isinstance(item[1],list)) or \
99 not isinstance(item[2],int):
100 raise self.PhysicsObjectError, \
101 "%s is not a valid canonical_tag" % str(value)
102
103 if name == 'CT_vertices':
104 if not isinstance(value, base_objects.VertexList):
105 raise self.PhysicsObjectError, \
106 "%s is not a valid VertexList object" % str(value)
107
108 if name == 'type':
109 if not isinstance(value, int):
110 raise self.PhysicsObjectError, \
111 "%s is not a valid integer" % str(value)
112
113 else:
114 super(LoopDiagram, self).filter(name, value)
115
116 return True
117
119 """Return particle property names as a nicely sorted list."""
120
121 return ['vertices', 'CT_vertices', 'orders', 'type', 'tag']
122
124 """Returns a nicely formatted string of the diagram content."""
125
126
127 if self['type']==0:
128 return super(LoopDiagram,self).nice_string()
129
130 mystr=''
131 if not self['vertices']:
132 return '()'
133 if self['canonical_tag']:
134 mystr = mystr+'canonical tag: '+str(self['canonical_tag'])+'\n'
135 if self['CT_vertices']:
136 mystr = mystr+'CT vertex ids:'
137 for ctvx in self['CT_vertices']:
138 mystr = mystr +' '+str(ctvx.get('id'))
139 mystr = mystr+'\n'
140 if self['vertices']:
141 mystr = mystr+'Loop vertices: ('
142 for vert in self['vertices']:
143 mystr = mystr + '('
144 for leg in vert['legs'][:-1]:
145 if leg['loop_line']:
146 mystr = mystr + str(leg['number']) + \
147 '(%s*)' % str(leg['id']) + ','
148 else:
149 mystr = mystr + str(leg['number']) + \
150 '(%s)' % str(leg['id']) + ','
151
152 if self['vertices'].index(vert) < len(self['vertices']) - 1:
153
154 mystr = mystr[:-1] + '>'
155 if vert['legs'][-1]['loop_line']:
156 mystr = mystr + str(vert['legs'][-1]['number']) + \
157 '(%s*)' % str(vert['legs'][-1]['id']) + ','
158 else:
159 mystr = mystr + str(vert['legs'][-1]['number']) + \
160 '(%s)' % str(vert['legs'][-1]['id']) + ','
161 mystr = mystr + 'id:' + str(vert['id']) + '),'
162 mystr = mystr[:-1] + ')'
163 mystr += " (%s)" % ",".join(["%s=%d" % (key, self['orders'][key]) \
164 for key in self['orders'].keys()])+"\n"
165 if struct_list and self['tag']:
166 for i, tag_elem in enumerate(self['tag']):
167 for j, struct in enumerate(tag_elem[1]):
168 if len(tag_elem[1])>1:
169 mystr += 'Struct. #'+str(j+1)+\
170 ' on loop vx #'+str(i+1)+": "+\
171 struct_list[struct].nice_string_vertices()+"\n"
172 else:
173 mystr += 'Struct. on loop vx #'+str(i+1)+": "+\
174 struct_list[struct].nice_string_vertices()+"\n"
175
176 mystr=mystr[:-1]
177
178 return mystr
179
180 - def get_CT(self,model,string=None):
181 """ Returns the CounterTerms of the type passed in argument. If None
182 it returns all of them. """
183 if string:
184 return base_objects.VertexList([vert for vert in \
185 self['CT_vertices'] if string in \
186 model['interaction_dict'][vert['id']]['type']])
187 else:
188 return self['CT_vertices']
189
191 """ Return none if there is no loop or if a tag has not yet been set and
192 returns True if this graph contains a purely fermionic loop and False if
193 not. """
194
195 if(self['tag']):
196 for part in self['tag']:
197 if not model.get('particle_dict')[part[0].get('id')].is_fermion():
198 return False
199 return True
200 else:
201 return False
202
204 """ Return None if there is no loop or if a tag has not yet been set and
205 returns True if this graph contains a tadpole loop and False if not. """
206
207 if(self['tag']):
208 if(len(self['tag'])==1):
209 return True
210 else:
211 return False
212 else:
213 return None
214
216 """Return None if there is no loop or if a tag has not yet been set and
217 returns True if this graph contains a vanishing tadpole loop and False
218 if not. """
219
220 if not self.is_tadpole():
221 return False
222
223
224 if(len(self['tag'][0][1])<=1):
225 return True
226
227 return any([part['mass'].lower()=='zero' for pdg,part in \
228 model.get('particle_dict').items() if \
229 pdg==abs(self['tag'][0][0]['id'])])
230
232 """ Return None if there is no loop or if a tag has not yet been set and
233 returns True if this graph contains a wave-function correction and False
234 if not. """
235
236 if self['tag'] :
237
238 if len(self['tag'])==2 and len(self['tag'][0][1])==1 \
239 and len(self['tag'][1][1])==1:
240
241 if struct_rep[self['tag'][0][1][0]].is_external() or \
242 struct_rep[self['tag'][1][1][0]].is_external():
243
244 inLegID=struct_rep[self['tag'][0][1][0]]['binding_leg']['id']
245 outLegID=struct_rep[self['tag'][1][1][0]]['binding_leg']['id']
246 return True
247
248
249 if len(self['tag'])==1 and len(self['tag'][0][1])==2 and \
250 (struct_rep[self['tag'][0][1][0]].is_external() or
251 struct_rep[self['tag'][0][1][1]].is_external()):
252 return True
253
254 return False
255 else:
256 return None
257
259 """Return the number of loop lines. """
260 if self['tag']:
261 return len(self['tag'])
262 else:
263 return None
264
265 @classmethod
267 """ Computes the weighting function S for this structure 'i' such that
268 S(i)>0 for each any i, S(i)!=S(j) if i['external_legs']!=j['external_legs']
269 and S(i+j)>max(S(i),S(j)). """
270
271 external_numbers=[leg['number'] for id in FD_ids_list for leg in \
272 struct_rep.get_struct(id).get('external_legs')]
273 external_numbers.sort()
274 weight=0
275 for i, number in enumerate(external_numbers):
276 weight=i*number_legs+number
277 return weight
278
279 @classmethod
281 """ This function chooses the place where to cut the loop in order to
282 maximize the loop wavefunction recycling in the open loops method.
283 This amounts to cut just before the combined structure with smallest
284 weight and then chose the direction to go towards the one with smallest
285 weight."""
286
287 tag=copy.deepcopy(intag)
288 model=process['model']
289 number_legs=len(process.get('legs'))
290
291
292 weights=[cls.compute_weight(t[1],struct_rep,number_legs) for t in tag]
293 imin = weights.index(min(weights))
294 tag=tag[imin:]+tag[:imin]
295 weights=weights[imin:]+weights[:imin]
296
297
298 rev_tag=cls.mirrored_tag(tag, model)
299
300 rev_tag=rev_tag[-1:]+rev_tag[:-1]
301 rev_weights=[cls.compute_weight(t[1],struct_rep,number_legs) for t in rev_tag]
302
303
304 if len(tag)==1:
305 return tag
306 elif len(tag)==2:
307 if abs(tag[0][0]['id'])>abs(tag[1][0]['id']):
308 return rev_tag
309 else:
310 return tag
311 else:
312 if rev_weights[1]<weights[1]:
313 return rev_tag
314 else:
315 return tag
316
317 @classmethod
319 """ This function chooses where to cut the loop. It returns the
320 canonical tag corresponding to this unambiguous choice."""
321
322
323
324
325
326
327
328 canonical_tag=copy.deepcopy(tag)
329 canonical_tag=cls.make_canonical_cyclic(canonical_tag)
330 canonical_mirrored_tag=copy.deepcopy(canonical_tag)
331 canonical_mirrored_tag=cls.mirrored_tag(canonical_mirrored_tag,model)
332
333 canonical_mirrored_tag=canonical_mirrored_tag[-1:]+\
334 canonical_mirrored_tag[:-1]
335
336
337
338
339
340
341
342
343
344
345
346
347 if (len(tag)==2 and abs(canonical_mirrored_tag[0][0]['id'])>\
348 abs(canonical_tag[0][0]['id'])) or (len(tag)>2 and \
349 canonical_mirrored_tag[1][1]<canonical_tag[1][1]):
350 canonical_tag=canonical_mirrored_tag
351
352 return canonical_tag
353
354 - def tag(self, struct_rep, start_in, end_in, process):
355 """ Construct the tag of the diagram providing the loop structure
356 of it. """
357
358
359
360 loopVertexList=base_objects.VertexList()
361
362
363
364 if isinstance(start_in,int) and isinstance(end_in,int):
365 start=start_in
366 end=end_in
367 elif isinstance(start_in,base_objects.Leg) and \
368 isinstance(end_in,base_objects.Leg):
369 start=start_in.get('number')
370 end=end_in.get('number')
371 else:
372 raise MadGraph5Error, "In the diagram tag function, 'start' and "+\
373 " 'end' must be either integers or Leg objects."
374
375 if(self.process_next_loop_leg(struct_rep,-1,-1,start,end,\
376 loopVertexList,process)):
377
378
379
380
381
382
383
384
385 if self.cutting_method=='default':
386
387 canonical_tag=self.choose_default_lcut(self['tag'],process['model'])
388 elif self.cutting_method=='optimal':
389
390
391 canonical_tag=self.choose_optimal_lcut(self['tag'],struct_rep,process)
392 else:
393 raise MadGraph5Error, 'The cutting method %s is not implemented.'\
394 %self.cutting_method
395
396 self['tag']=canonical_tag
397
398
399
400 self.synchronize_loop_vertices_with_tag(process['model'],
401 struct_rep,start,end)
402
403
404
405
406 self['canonical_tag']=[[t[0]['id'],t[1],t[2]] for t in canonical_tag]
407 return True
408 else:
409 raise self.PhysicsObjectError, \
410 "Loop diagram tagging failed."
411 return False
412
413
414 @classmethod
416 """ Generate a loop vertex from incoming legs myleglist and the
417 interaction with id vertID of the model given in argument """
418
419 ref_dict_to1 = model.get('ref_dict_to1')
420
421
422 key=tuple(sorted([leg.get('id') for leg in myleglist]))
423 if ref_dict_to1.has_key(key):
424 for interaction in ref_dict_to1[key]:
425
426 if interaction[1]==vertID:
427
428
429
430 legid = interaction[0]
431
432
433 number = min([leg.get('number') for leg in\
434 myleglist])
435
436
437
438 if len(myleglist)>1 and len(filter(lambda leg: \
439 leg.get('state') == False, myleglist)) == 1:
440 state = False
441 else:
442 state = True
443 myleglist.append(base_objects.Leg(\
444 {'number': number,\
445 'id': legid,\
446 'state': state,
447 'loop_line': True}))
448
449 return base_objects.Vertex({'legs':myleglist,'id':vertID})
450 else:
451 raise cls.PhysicsObjectError, \
452 "An interaction from the original L-cut diagram could"+\
453 " not be found when reconstructing the loop vertices."
454
455 - def process_next_loop_leg(self, structRep, fromVert, fromPos, currLeg, \
456 endLeg, loopVertexList, process):
457 """ Finds a loop leg and what is the next one. Also identify and tag the
458 FD structure attached in between these two loop legs. It adds the
459 corresponding tuple to the diagram tag and calls iself again to treat
460 the next loop leg. Return True when tag successfully computed."""
461
462 nextLoopLeg=None
463 legPos=-2
464 vertPos=-2
465 FDStructureIDList=[]
466 vertFoundID=-1
467
468
469 def process_loop_interaction(i,j,k,pos):
470 """For vertex position 'i' and loop leg position 'j'. Find the
471 structure attached to leg k of this loop interaction, tag it and
472 update the loop tag."""
473 FDStruct=FDStructure()
474
475
476
477 canonical = self.construct_FDStructure(i,pos,\
478 self['vertices'][i].get('legs')[k],FDStruct)
479 if not canonical:
480 raise self.PhysicsObjectError, \
481 "Failed to reconstruct a FDStructure."
482
483
484
485 if isinstance(canonical,int):
486 FDStruct.set('canonical',(((canonical,),0),))
487 elif isinstance(canonical,tuple):
488 FDStruct.set('canonical',canonical)
489 else:
490 raise self.PhysicsObjectError, \
491 "Non-proper behavior of the construct_FDStructure function"
492
493
494
495 myStructID=-1
496 myFDStruct=structRep.get_struct(FDStruct.get('canonical'))
497 if not myFDStruct:
498
499
500 myStructID=len(structRep)
501
502
503 FDStruct.set('id',myStructID)
504
505
506
507 FDStruct.generate_vertices(process)
508 structRep.append(FDStruct)
509 else:
510
511
512
513
514
515
516
517 myStructID=myFDStruct.get('id')
518
519 FDStructureIDList.append(myStructID)
520
521
522
523
524 vertRange=range(len(self['vertices']))
525
526
527 if not fromVert == -1:
528 if fromPos == -1:
529
530
531
532 vertRange=vertRange[fromVert+1:]
533 else:
534
535
536
537
538 vertRange=vertRange[:fromVert]
539 vertRange.reverse()
540
541
542 for i in vertRange:
543
544
545
546
547 legRange=range(len(self['vertices'][i].get('legs')))
548 if fromPos == -1:
549
550 if not i==len(self['vertices'])-1:
551 legRange=legRange[:-1]
552 else:
553
554
555 if i==len(self['vertices'])-1:
556 continue
557 else:
558 legRange=legRange[-1:]
559 for j in legRange:
560 if self['vertices'][i].get('legs')[j].same(currLeg):
561 vertPos=i
562 vertFoundID=self['vertices'][i]['id']
563
564
565
566 if isinstance(currLeg,int):
567 currLeg=self['vertices'][i].get('legs')[j]
568
569 for k in filter(lambda ind: not ind==j, \
570 range(len(self['vertices'][i].get('legs')))):
571
572
573
574
575
576
577
578 if not i==len(self['vertices'])-1 \
579 and k==len(self['vertices'][i].get('legs'))-1:
580 pos=-1
581 else:
582 pos=k
583
584 if self['vertices'][i].get('legs')[k].get('loop_line'):
585 if not nextLoopLeg:
586 nextLoopLeg=self['vertices'][i].get('legs')[k]
587 legPos=pos
588 else:
589 raise self.PhysicsObjectError, \
590 " An interaction has more than two loop legs."
591 else:
592 process_loop_interaction(i,j,k,pos)
593
594
595 break
596 if nextLoopLeg:
597 break
598
599
600 if not nextLoopLeg:
601
602
603 return False
604
605
606
607
608 if FDStructureIDList and vertFoundID not in [0,-1]:
609
610
611
612
613
614 myleglist=base_objects.LegList([copy.copy(\
615 structRep[FDindex]['binding_leg']) for FDindex in \
616 FDStructureIDList])
617
618
619
620
621
622
623 if loopVertexList:
624 self['tag'].append([copy.copy(\
625 loopVertexList[-1]['legs'][-1]),\
626 sorted(FDStructureIDList),vertFoundID])
627 myleglist.append(loopVertexList[-1]['legs'][-1])
628 else:
629 self['tag'].append([copy.copy(currLeg),\
630 sorted(FDStructureIDList),vertFoundID])
631 myleglist.append(copy.copy(currLeg))
632
633
634
635
636
637
638
639
640
641
642
643
644 loopVertexList.append(\
645 self.generate_loop_vertex(myleglist,process['model'],vertFoundID))
646 if nextLoopLeg.same(endLeg):
647
648
649
650 if vertFoundID not in [0,-1]:
651 starting_Leg=copy.copy(myleglist[-1])
652 legid=process['model'].get_particle(myleglist[-1]['id']).\
653 get_anti_pdg_code()
654 state=myleglist[-1].get('state')
655 else:
656 starting_Leg=copy.copy(currLeg)
657 legid=process['model'].get_particle(currLeg['id']).\
658 get_anti_pdg_code()
659 state=currLeg.get('state')
660
661 loopVertexList.append(base_objects.Vertex(\
662 {'legs':base_objects.LegList([starting_Leg,\
663 base_objects.Leg({'number': endLeg,
664 'id': legid,
665 'state': state,
666 'loop_line': True})]),
667 'id':-1}))
668
669
670
671 return True
672 else:
673
674
675 return self.process_next_loop_leg(structRep, vertPos, legPos, \
676 nextLoopLeg, endLeg, loopVertexList, process)
677
680 """ Construct the loop vertices from the tag of the loop diagram."""
681
682 if not self['tag']:
683 return
684
685 ref_dict_to1 = model.get('ref_dict_to1')
686
687
688 loopVertexList=base_objects.VertexList()
689 for i, t in enumerate(self['tag']):
690
691
692 myleglist=base_objects.LegList([copy.copy(\
693 struct_rep[FDindex]['binding_leg']) for FDindex in t[1]])
694 if i==0:
695 starting_leg=copy.copy(t[0])
696
697
698
699
700
701
702
703
704
705
706
707
708
709 if model.get_particle(starting_leg['id']).get('is_part'):
710 starting_leg['number']=lcut_part_number
711 end_number=lcut_antipart_number
712 else:
713 starting_leg['number']=lcut_antipart_number
714 end_number=lcut_part_number
715 starting_leg['state']=True
716 else:
717 starting_leg=loopVertexList[-1].get('legs')[-1]
718 self['tag'][i][0]=starting_leg
719 myleglist.append(starting_leg)
720 loopVertexList.append(self.generate_loop_vertex(myleglist,model,t[2]))
721
722
723 first_leg=copy.copy(loopVertexList[-1].get('legs')[-1])
724 sec_leg_id=model.get_particle(first_leg['id']).get_anti_pdg_code()
725 second_leg=base_objects.Leg({'number': end_number,
726 'id': sec_leg_id,
727 'state': first_leg.get('state'),
728 'loop_line': True})
729 loopVertexList.append(base_objects.Vertex(\
730 {'legs':base_objects.LegList([first_leg,second_leg]),
731 'id':-1}))
732
733 self['type'] = abs(first_leg['id'])
734 self['vertices'] = loopVertexList
735
737 """ Construct iteratively a Feynman Diagram structure attached to a Loop,
738 given at each step a vertex and the position of the leg this function is
739 called from. At the same time, it constructs a canonical representation
740 of the structure which is a tuple with each element corresponding to
741 a 2-tuple ((external_parent_legs),vertex_ID). The external parent legs
742 tuple is ordered as growing and the construction of the canonical
743 representation is such that the 2-tuples appear in a fixed order.
744 This functions returns a tuple of 2-tuple like above for the vertex
745 where currLeg was found or false if fails.
746
747 To illustrate this algorithm, we take a concrete example,
748 the following structure:
749
750 4 5 6 7
751 1 3 \/2 \/ <- Vertex ID, left=73 and right=99
752 \ / | \ / <- Vertex ID, left=34 and right=42
753 | |4 |
754 1\ | /2
755 \|/ <- Vertex ID=72
756 |
757 |1
758
759 For this structure with external legs (1,2,3,5,6,7) and current created
760 1, the canonical tag will be
761
762 (((1,2,3,4,5,6,7),72),((1,3),34),((2,6,7),42),((6,7),99),((4,5),73))
763 """
764 nextLeg = None
765 legPos=-2
766 vertPos=-2
767
768 vertRange=range(len(self['vertices']))
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789 vertBuffer=[]
790
791
792
793
794 parentBuffer=[[],0]
795
796
797
798
799
800 if fromPos == -1:
801
802
803
804 vertRange=vertRange[fromVert+1:]
805 else:
806
807
808
809
810
811 vertRange=vertRange[:fromVert]
812 vertRange.reverse()
813
814
815
816
817
818 pos=-2
819
820
821 def process_leg(vertID, legID):
822 """ Treats the leg equal to currLeg found in the place located by
823 self['vertices'][vertID].get('legs')[legID]"""
824
825
826
827 parentBuffer[1]=self['vertices'][vertID].get('id')
828
829
830
831
832
833
834
835 legPos=-2
836 for k in [ind for ind in \
837 range(len(self['vertices'][vertID].get('legs'))) if ind!=legID]:
838
839
840
841 if not self['vertices'][vertID].get('id'):
842 return self.construct_FDStructure(vertID, k,\
843 self['vertices'][vertID].get('legs')[k], FDStruct)
844
845 if k==len(self['vertices'][vertID].get('legs'))-1 \
846 and not vertID==len(self['vertices'])-1:
847 legPos=-1
848 else:
849 legPos=k
850
851 branch=self.construct_FDStructure(i, legPos, \
852 self['vertices'][vertID].get('legs')[k], FDStruct)
853 if not branch:
854 raise self.PhysicsObjectError, \
855 "Failed to reconstruct a FDStructure."
856
857 if isinstance(branch,int):
858 parentBuffer[0].append(branch)
859
860
861 elif isinstance(branch,tuple):
862 parentBuffer[0]+=list(branch[0][0])
863 vertBuffer.append(branch)
864 else:
865 raise self.PhysicsObjectError, \
866 "Non-proper behavior of the construct_FDStructure function"
867 return legPos
868
869
870
871
872 for i in vertRange:
873
874
875
876
877
878 legRange=range(len(self['vertices'][i].get('legs')))
879 if fromPos == -1:
880
881 if not i==len(self['vertices'])-1:
882 legRange=legRange[:-1]
883 else:
884
885
886 if i==len(self['vertices'])-1:
887 continue
888 else:
889 legRange=legRange[-1:]
890
891
892
893 findVert=False
894
895 for j in legRange:
896 if self['vertices'][i].get('legs')[j].same(currLeg):
897
898 pos=process_leg(i,j)
899
900
901 findVert=True
902 break;
903 if findVert:
904 break;
905
906 if(pos == -2):
907 if(not fromPos == -1):
908
909 FDStruct.get('external_legs').append(copy.copy(currLeg))
910 return currLeg.get('number')
911 else:
912 raise self.PhysicsObjectError, \
913 " A structure is malformed."
914 else:
915
916
917
918
919
920
921
922 vertBuffer.sort()
923
924
925
926
927
928
929 vertBufferFlat=[]
930 for t in vertBuffer:
931 for u in t:
932 vertBufferFlat.append(u)
933
934
935 parentBuffer[0].sort()
936
937 vertBufferFlat.insert(0,(tuple(parentBuffer[0]),parentBuffer[1]))
938 return tuple(vertBufferFlat)
939
940
941
943 """ Return the starting loop line of this diagram, i.e. lcut leg one."""
944 for v in self['vertices']:
945 for l in v['legs']:
946 if l['loop_line']:
947 return l
948
950 """ Return the finishing line of this diagram, i.e. lcut leg two.
951 Notice that this function is only available when the loop diagram is
952 constructed with the special two-point vertex with id -1. """
953
954 assert self['vertices'][-1].get('id')==-1, "Loop diagrams must finish "+\
955 " with vertex with id '-1' for get_finishing_loop_line to be called"
956
957 return max(self['vertices'][-1].get('legs'), key=lambda l: l['number'])
958
960 """ Return a set with one occurence of each different PDG code of the
961 particles running in the loop. By convention, the PDF of the particle,
962 not the antiparticle, is stored in this list. Using the tag would be
963 quicker, but we want this function to be available before tagging as
964 well"""
965 return set([abs(l['id']) for v in self['vertices'] for l in v['legs'] \
966 if l['loop_line']])
967
969 """ Return a dictionary with one entry per type of order appearing in
970 the interactions building the loop flow. The corresponding keys are the
971 number of type this order appear in the diagram. """
972
973 loop_orders = {}
974 for vertex in self['vertices']:
975
976 if vertex['id'] not in [0,-1] and len([1 for leg in vertex['legs'] if \
977 leg['loop_line']])==2:
978 vertex_orders = model.get_interaction(vertex['id'])['orders']
979 for order in vertex_orders.keys():
980 if order in loop_orders.keys():
981 loop_orders[order]+=vertex_orders[order]
982 else:
983 loop_orders[order]=vertex_orders[order]
984 return loop_orders
985
986
987 @classmethod
989 """ Perform cyclic permutations on the tag given in parameter such that
990 the structure with the lowest ID appears first."""
991
992 if not atag:
993 return []
994
995 imin=-2
996 minStructID=-2
997 for i, part in enumerate(atag):
998 if minStructID==-2 or min(part[1])<minStructID:
999 minStructID=min(part[1])
1000 imin=i
1001
1002 atag=atag[imin:]+atag[:imin]
1003
1004 return atag
1005
1006 @classmethod
1008 """ Performs a mirror operation on A COPY of the tag and returns it. """
1009
1010 if not atag:
1011 return []
1012
1013
1014 revTag=[(copy.deepcopy(elem[0]), copy.copy(elem[1]), \
1015 copy.copy(elem[2])) for elem in atag]
1016
1017
1018 revTag.reverse()
1019
1020 shiftBuff=revTag[-1]
1021 for i in range(len(revTag)-1):
1022 revTag[-(i+1)]=[revTag[-(i+2)][0],revTag[-(i+1)][1],revTag[-(i+1)][2]]
1023 revTag[0]=[shiftBuff[0],revTag[0][1],revTag[0][2]]
1024
1025
1026
1027 nonselfantipartlegs = [ elem[0] for elem in revTag if not \
1028 model.get('particle_dict')[elem[0].get('id')]['self_antipart'] ]
1029 for leg in nonselfantipartlegs:
1030 leg.set('id',\
1031 model.get('particle_dict')[leg.get('id')].get_anti_pdg_code())
1032
1033 return revTag
1034
1035
1036
1037
1038
1040 """ Returns the pdgs of the lines running in the loop while not
1041 differentiating the particles from the anti-particles """
1042
1043 return [abs(tag_elem[0].get('id')) for tag_elem in self['tag']]
1044
1046 """ Returns the pdgs of the lines directly branching off the loop."""
1047
1048 return [structs.get_struct(struct_ID).get('binding_leg').get('id') \
1049 for tag_elem in self['tag'] for struct_ID in tag_elem[1]]
1050
1056 """ A special kind of LoopDiagram which does not contain a loop but only
1057 specifies all UV counter-term which factorize the the same given born
1058 and bringing in the same orders. UV mass renormalization does not belong to
1059 this class of counter-term for example, and it is added along with the R2
1060 interactions."""
1061
1063 """Default values for all properties"""
1064
1065 super(LoopUVCTDiagram,self).default_setup()
1066
1067
1068 self['type']='UV'
1069 self['UVCT_orders']={}
1070 self['UVCT_couplings']=[]
1071
1072 - def filter(self, name, value):
1073 """Filter for valid diagram property values."""
1074
1075 if name == 'UVCT_couplings':
1076 if not isinstance(value, list):
1077 raise self.PhysicsObjectError, \
1078 "%s is not a valid list" % str(value)
1079 else:
1080 for elem in value:
1081 if not isinstance(elem, str) and not isinstance(elem, int):
1082 raise self.PhysicsObjectError, \
1083 "%s is not a valid string" % str(value)
1084
1085 if name == 'UVCT_orders':
1086 if not isinstance(value, dict):
1087 raise self.PhysicsObjectError, \
1088 "%s is not a valid dictionary" % str(value)
1089
1090 if name == 'type':
1091 if not isinstance(value, str):
1092 raise self.PhysicsObjectError, \
1093 "%s is not a valid string" % str(value)
1094
1095 else:
1096 super(LoopUVCTDiagram, self).filter(name, value)
1097
1098 return True
1099
1101 """Return particle property names as a nicely sorted list."""
1102
1103 return ['vertices', 'UVCT_couplings', 'UVCT_orders', 'type', 'orders']
1104
1106 """ Finds the UV counter-term interaction present in this UVCTDiagram """
1107
1108 for vert in self['vertices']:
1109 if vert.get('id') != 0:
1110 if model.get_interaction(vert.get('id')).is_UV():
1111 return model.get_interaction(vert.get('id'))
1112
1113 return None
1114
1116 """Calculate the actual coupling orders of this diagram. Note
1117 that the special order WEIGTHED corresponds to the sum of
1118 hierarchies for the couplings."""
1119
1120 coupling_orders = dict([(c, 0) for c in model.get('coupling_orders')])
1121 weight = 0
1122 for couplings in [model.get('interaction_dict')[vertex.get('id')].\
1123 get('orders') for vertex in self['vertices'] if \
1124 vertex.get('id') != 0]+[self['UVCT_orders']]:
1125 for coupling in couplings:
1126 coupling_orders[coupling] += couplings[coupling]
1127 weight += sum([model.get('order_hierarchy')[c]*n for \
1128 (c,n) in couplings.items()])
1129 coupling_orders['WEIGHTED'] = weight
1130 self.set('orders', coupling_orders)
1131
1133 """Returns a nicely formatted string of the diagram content."""
1134 res=''
1135 if self['vertices']:
1136 res=res+super(LoopUVCTDiagram,self).nice_string()
1137 if self['UVCT_couplings']:
1138 res=res+'UV renorm. vertices: '
1139 res=res+','.join(str(vert) for vert in self['UVCT_couplings'])+'\n'
1140 if self['UVCT_orders']:
1141 res=res+'UVCT orders: '
1142 res=res+','.join(order for order in self['UVCT_orders'].keys())+'\n'
1143 if self['type']:
1144 res=res+'UVCT type: '+self['type']
1145
1146 return res
1147
1148
1149
1150
1151 -class LoopModel(base_objects.Model):
1152 """A class to store all the model information with advanced feature
1153 to compute loop process."""
1154
1155
1157 super(LoopModel,self).default_setup()
1158 self['perturbation_couplings'] = []
1159
1160
1161
1162
1163
1164
1165
1166
1167 self['coupling_orders_counterterms']={}
1168
1169 - def filter(self, name, value):
1170 """Filter for model property values"""
1171
1172 if name == 'perturbation_couplings':
1173 if not isinstance(value, list):
1174 raise self.PhysicsObjectError, \
1175 "Object of type %s is not a list" % \
1176 type(value)
1177 for order in value:
1178 if not isinstance(order, str):
1179 raise self.PhysicsObjectError, \
1180 "Object of type %s is not a string" % \
1181 type(order)
1182 else:
1183 super(LoopModel,self).filter(name,value)
1184
1185 return True
1186
1188 """This function actualizes the dictionaries"""
1189
1190 if useUVCT:
1191 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1192 self['interactions'].generate_ref_dict(useR2UV=False,useUVCT=True)
1193 else:
1194 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1195 self['interactions'].generate_ref_dict()
1196 self['ref_dict_to0'].update(
1197 self['particles'].generate_ref_dict())
1198
1200 """Return process property names as a nicely sorted list."""
1201
1202 return ['name', 'particles', 'parameters', 'interactions', 'couplings',
1203 'lorentz','perturbation_couplings','conserved_charge']
1204
1205
1206
1207
1208 -class DGLoopLeg(base_objects.Leg):
1209 """A class only used during the loop diagram generation. Exactly like leg
1210 except for a few other parameters only useful during the loop diagram
1211 generation."""
1212
1224
1228
1229 - def filter(self, name, value):
1230 """Filter for model property values"""
1231
1232 if name == 'depth':
1233 if not isinstance(value, int):
1234 raise self.PhysicsObjectError, \
1235 "Object of type %s is not a int" % \
1236 type(value)
1237 else:
1238 super(DGLoopLeg,self).filter(name,value)
1239
1240 return True
1241
1243 """Return process property names as a nicely sorted list."""
1244
1245 return ['id', 'number', 'state', 'from_group','loop_line','depth']
1246
1248 """ Converts a DGLoopLeg back to a Leg. Basically removes the extra
1249 attributes """
1250
1251 aleg=base_objects.Leg()
1252 for key in aleg.get_sorted_keys():
1253 aleg.set(key,self[key])
1254
1255 return aleg
1256
1257
1258
1259
1260 -class FDStructure(base_objects.PhysicsObject):
1261 """FDStructure:
1262 list of vertices (ordered). This is part of a diagram.
1263 """
1264
1273
1275 """Returns wether the structure is simply made of an external particle
1276 only"""
1277 if (len(self['canonical'])==1 and self['canonical'][0][1]==0):
1278 return True
1279 else:
1280 return False
1281
1282 - def filter(self, name, value):
1283 """Filter for valid FDStructure property values."""
1284
1285 if name == 'vertices':
1286 if not isinstance(value, base_objects.VertexList):
1287 raise self.PhysicsObjectError, \
1288 "%s is not a valid VertexList object" % str(value)
1289
1290 if name == 'id':
1291 if not isinstance(value, int):
1292 raise self.PhysicsObjectError, \
1293 "id %s is not an integer" % repr(value)
1294
1295 if name == 'weight':
1296 if not isinstance(value, int):
1297 raise self.PhysicsObjectError, \
1298 "weight %s is not an integer" % repr(value)
1299
1300 if name == 'external_legs':
1301 if not isinstance(value, base_objects.LegList):
1302 raise self.PhysicsObjectError, \
1303 "external_legs %s is not a valid Leg List" % str(value)
1304
1305 if name == 'binding_leg':
1306 if not isinstance(value, base_objects.Leg):
1307 raise self.PhysicsObjectError, \
1308 "binding_leg %s is not a valid Leg" % str(value)
1309
1310 if name == 'canonical':
1311 if not isinstance(value, tuple):
1312 raise self.PhysicsObjectError, \
1313 "canonical %s is not a valid tuple" % str(value)
1314
1315 return True
1316
1318 """Return particle property names as a nicely sorted list."""
1319
1320 return ['id','external_legs','binding_leg','canonical','vertices']
1321
1323 """Returns a nicely formatted string of the structure content."""
1324
1325 mystr=''
1326
1327 if not self['id']==-1:
1328 mystr=mystr+'id: '+str(self['id'])+',\n'
1329 else:
1330 return '()'
1331
1332 if self['canonical']:
1333 mystr=mystr+'canonical_repr.: '+str(self['canonical'])+',\n'
1334
1335 if self['external_legs']:
1336 mystr=mystr+'external_legs: { '
1337 for leg in self['external_legs'][:-1]:
1338 mystr = mystr + str(leg['number']) + '(%s)' % str(leg['id']) \
1339 + ', '
1340 mystr = mystr + str(self['external_legs'][-1]['number']) + \
1341 '(%s)' % str(self['external_legs'][-1]['id']) + ' },\n'
1342 mystr = mystr+'binding_leg: '+str(self['binding_leg']['number']) +\
1343 '(%s)' % str(self['binding_leg']['id'])
1344 return mystr
1345
1347 """Returns a nicely formatted string of the structure vertices."""
1348 mystr=''
1349 if self['vertices']:
1350 mystr = mystr+'('
1351 for vert in self['vertices']:
1352 mystr = mystr + '('
1353 for leg in vert['legs'][:-1]:
1354 mystr = mystr + str(leg['number']) + \
1355 '(%s)' % str(leg['id']) + ','
1356 mystr = mystr[:-1] + '>'
1357 mystr = mystr + str(vert['legs'][-1]['number']) +\
1358 '(%s)' % str(vert['legs'][-1]['id']) + ','
1359 mystr = mystr + 'id:' + str(vert['id']) + '),'
1360 mystr = mystr[:-1] + ')'
1361 return mystr
1362 elif len(self['external_legs'])==1:
1363 return '('+str(self['external_legs'][0]['number'])+\
1364 '('+str(self['external_legs'][0]['id'])+'))'
1365 else:
1366 return '()'
1367
1368
1370 """ This functions generate the vertices building this structure,
1371 starting from the outter legs going towards the binding leg.
1372 It uses the interactions dictionaries from the model. """
1373
1374
1375 self.set('vertices',base_objects.VertexList())
1376
1377 tag=copy.copy(self['canonical'])
1378
1379
1380 model = process['model']
1381 ref_dict_to1 = model.get('ref_dict_to1')
1382
1383 if not tag:
1384 raise self.PhysicsObjectError, \
1385 "The canonical tag of the FD structure is not set yet, so that the "+\
1386 "reconstruction of the vertices cannot be performed."
1387
1388
1389 leglist = copy.deepcopy(process.get('legs'))
1390
1391 for leg in leglist:
1392
1393
1394 if leg.get('state') == False:
1395 part = model.get('particle_dict')[leg.get('id')]
1396 leg.set('id', part.get_anti_pdg_code())
1397
1398
1399 legDict={}
1400 for leg in leglist:
1401 legDict[leg['number']]=leg
1402
1403
1404
1405 if len(tag)==1 and len(tag[0][0])==1:
1406
1407 self['binding_leg']=copy.deepcopy(legDict[tag[0][0][0]])
1408 return
1409
1410
1411 tag=list(tag)
1412 tag.reverse()
1413
1414
1415
1416 for i, tagelem in enumerate(tag):
1417 tag[i]=list(tagelem)
1418 tag[i][0]=base_objects.LegList([legDict[myleg] for myleg in \
1419 tagelem[0]])
1420
1421
1422
1423
1424
1425 while tag:
1426
1427
1428 legs=tag[0][0]
1429
1430
1431 key=tuple(sorted([leg.get('id') for leg in legs]))
1432 if ref_dict_to1.has_key(key):
1433 for interaction in ref_dict_to1[key]:
1434
1435 if interaction[1]==tag[0][1]:
1436
1437
1438 legid = interaction[0]
1439
1440
1441 number = min([leg.get('number') for leg in legs])
1442
1443
1444 if len(filter(lambda leg: leg.get('state') == False,
1445 legs)) == 1:
1446 state = False
1447 else:
1448 state = True
1449 legs.append(base_objects.Leg({'number': number,\
1450 'id': legid,\
1451 'state': state,
1452 'loop_line': False}))
1453
1454 self.get('vertices').append(base_objects.Vertex(\
1455 {'legs':legs,'id':interaction[1]}))
1456 break
1457
1458
1459
1460 for i, tagelement in enumerate(tag[1:]):
1461 Found=False
1462 for leg in legs[:-1]:
1463 try:
1464 tag[i+1][0].remove(leg)
1465 Found=True
1466 except Exception:
1467 pass
1468 if Found:
1469 tag[i+1][0].append(legs[-1])
1470
1471
1472
1473 if len(tag)==1:
1474 self['binding_leg']=copy.deepcopy(legs[-1])
1475
1476
1477
1478 tag.pop(0)
1479
1480 else:
1481 raise self.PhysicsObjectError, \
1482 "The canonical tag of the FD structure is corrupted because one "+\
1483 "interaction does not exist."
1484
1489 """List of FDStructure objects
1490 """
1491
1493 """Test if object obj is a valid Diagram for the list."""
1494
1495 return isinstance(obj, FDStructure)
1496
1498 """Return the FDStructure of the list with the corresponding canonical
1499 tag if ID is a tuple or the corresponding ID if ID is an integer.
1500 It returns the structure if it founds it, or None if it was not found"""
1501 if isinstance(ID, int):
1502 for FDStruct in self:
1503 if FDStruct.get('id')==ID:
1504 return FDStruct
1505 return None
1506 elif isinstance(ID, tuple):
1507 for FDStruct in self:
1508 if FDStruct.get('canonical')==ID:
1509 return FDStruct
1510 return None
1511 else:
1512 raise self.PhysicsObjectListError, \
1513 "The ID %s specified for get_struct is not an integer or tuple"%\
1514 repr(object)
1515
1517 """Returns a nicely formatted string"""
1518 mystr = str(len(self)) + ' FD Structures:\n'
1519 for struct in self:
1520 mystr = mystr + " " + struct.nice_string() + '\n'
1521 return mystr[:-1]
1522