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 import madgraph.various.misc as misc
29 from madgraph import MadGraph5Error, MG5DIR
30
31 logger = logging.getLogger('madgraph.loop_base_objects')
37 """LoopDiagram: Contains an additional tag to uniquely identify the diagram
38 if it contains a loop. Also has many additional functions useful only
39 for loop computations.
40 """
41
42
43
44
45
46
47
48 cutting_method = 'optimal'
49
51 """Default values for all properties"""
52
53 super(LoopDiagram,self).default_setup()
54
55
56
57
58
59 self['tag'] = []
60
61
62
63
64
65
66 self['canonical_tag'] = []
67
68
69
70
71 self['type'] = 0
72
73
74
75
76 self['multiplier'] = 1
77
78
79 self['CT_vertices'] = base_objects.VertexList()
80
81
82
83
84 self['contracted_diagram'] = None
85
86 - def filter(self, name, value):
87 """Filter for valid diagram property values."""
88
89 if name == 'tag':
90 if not isinstance(value, list):
91 raise self.PhysicsObjectError, \
92 "%s is not a valid tag" % str(value)
93 else:
94 for item in value:
95 if (len(item)!=3 or \
96 not isinstance(item[0],base_objects.Leg) or \
97 not isinstance(item[1],list)) or \
98 not isinstance(item[2],base_objects.Vertex):
99 raise self.PhysicsObjectError, \
100 "%s is not a valid tag" % str(value)
101
102 if name == 'canonical_tag':
103 if not isinstance(value, list):
104 raise self.PhysicsObjectError, \
105 "%s is not a valid tag" % str(value)
106 else:
107 for item in value:
108 if (len(item)!=3 or not isinstance(item[0],int) or \
109 not isinstance(item[1],list)) or \
110 not isinstance(item[2],int):
111 raise self.PhysicsObjectError, \
112 "%s is not a valid canonical_tag" % str(value)
113
114 if name == 'CT_vertices':
115 if not isinstance(value, base_objects.VertexList):
116 raise self.PhysicsObjectError, \
117 "%s is not a valid VertexList object" % str(value)
118
119 if name == 'type':
120 if not isinstance(value, int):
121 raise self.PhysicsObjectError, \
122 "%s is not a valid integer" % str(value)
123
124 if name == 'multiplier':
125 if not isinstance(value, int):
126 raise self.PhysicsObjectError, \
127 "%s is not a valid integer" % str(value)
128
129 if name == 'contracted_diagram':
130 if not isinstance(value, base_objects.Diagram):
131 raise self.PhysicsObjectError, \
132 "%s is not a valid Diagram." % str(value)
133
134 else:
135 super(LoopDiagram, self).filter(name, value)
136
137 return True
138
140 """Return particle property names as a nicely sorted list."""
141
142 return ['vertices', 'CT_vertices', 'orders', 'type', 'tag']
143
145 """Returns a nicely formatted string of the diagram content."""
146
147
148 if self['type']==0:
149 return super(LoopDiagram,self).nice_string()
150
151 mystr=''
152 if not self['vertices']:
153 return '()'
154 if self['canonical_tag']:
155 mystr = mystr+'canonical tag: '+str(self['canonical_tag'])+'\n'
156 if self['CT_vertices']:
157 mystr = mystr+'CT vertex ids:'
158 for ctvx in self['CT_vertices']:
159 mystr = mystr +' '+str(ctvx.get('id'))
160 mystr = mystr+'\n'
161 if self['vertices']:
162 mystr = mystr+'Loop vertices: ('
163 for vert in self['vertices']:
164 mystr = mystr + '('
165 for leg in vert['legs'][:-1]:
166 if leg['loop_line']:
167 mystr = mystr + str(leg['number']) + \
168 '(%s*)' % str(leg['id']) + ','
169 else:
170 mystr = mystr + str(leg['number']) + \
171 '(%s)' % str(leg['id']) + ','
172
173 if self['vertices'].index(vert) < len(self['vertices']) - 1:
174
175 mystr = mystr[:-1] + '>'
176 if vert['legs'][-1]['loop_line']:
177 mystr = mystr + str(vert['legs'][-1]['number']) + \
178 '(%s*)' % str(vert['legs'][-1]['id']) + ','
179 else:
180 mystr = mystr + str(vert['legs'][-1]['number']) + \
181 '(%s)' % str(vert['legs'][-1]['id']) + ','
182 mystr = mystr + 'id:' + str(vert['id']) + '),'
183 mystr = mystr[:-1] + ')'
184 mystr += " (%s)" % ",".join(["%s=%d" % (key, self['orders'][key]) \
185 for key in self['orders'].keys()])+"\n"
186 if struct_list and self['tag']:
187 for i, tag_elem in enumerate(self['tag']):
188 for j, struct in enumerate(tag_elem[1]):
189 if len(tag_elem[1])>1:
190 mystr += 'Struct. #'+str(j+1)+\
191 ' on loop vx #'+str(i+1)+": "+\
192 struct_list[struct].nice_string_vertices()+"\n"
193 else:
194 mystr += 'Struct. on loop vx #'+str(i+1)+": "+\
195 struct_list[struct].nice_string_vertices()+"\n"
196
197 mystr=mystr[:-1]
198
199 return mystr
200
202 """This is the old function used without tag which means that no
203 canonical loop information can be produced. It will be used for
204 unit test only and moved there when I'll implement them."""
205
206
207
208 if len(self.get('vertices'))==0:
209 raise MadGraph5Error, "Function get_contracted_loop_diagram()"+\
210 "called for the first time without specifying struct_rep "+\
211 "for a diagram already tagged."
212
213
214 contracted_vertex_last_loop_leg = None
215
216
217 vertices_after_contracted_vertex = []
218 vertices_before_contracted_vertex = []
219
220
221
222
223 contracted_vertex_leg_daughters_nb = []
224
225
226
227 for vertex in self.get('vertices')[:-1]:
228
229 if not any(l['loop_line'] for l in vertex.get('legs')):
230
231
232 if any((l.get('number') in contracted_vertex_leg_daughters_nb) \
233 for l in vertex.get('legs')[:-1]):
234 vertices_after_contracted_vertex.append(vertex)
235 contracted_vertex_leg_daughters_nb.append(vertex.get('legs')[-1])
236 else:
237 vertices_before_contracted_vertex.append(vertex)
238 else:
239
240 contracted_vertex.get('legs').extend(
241 [l for l in vertex.get('legs')[:-1] if not l['loop_line']])
242
243
244 contracted_vertex.get('PDGs').extend([l.get('id') for l in
245 vertex.get('legs') if not l['loop_line']])
246
247
248
249 if not vertex.get('legs')[-1]['loop_line']:
250
251 contracted_vertex_last_loop_leg = vertex.get('legs')[-1]
252
253
254 if any(l['loop_line'] for l in self.get('vertices')[-1].get('legs')):
255
256 contracted_vertex.get('legs').extend([l for l in
257 self.get('vertices')[-1].get('legs') if not l['loop_line']])
258
259 else:
260 vertices_after_contracted_vertex.append(self.get('vertices')[-1])
261
262
263 contracted_diagram_vertices.extend(vertices_before_contracted_vertex)
264 if not contracted_vertex_last_loop_leg is None:
265 contracted_vertex.get('legs').append(contracted_vertex_last_loop_leg)
266
267 if len(contracted_vertex.get('legs'))==1:
268 stop
269 contracted_diagram_vertices.append(contracted_vertex)
270 contracted_diagram_vertices.extend(vertices_after_contracted_vertex)
271
272 contracted_diagram = base_objects.Diagram(
273 {'vertices':contracted_diagram_vertices,'orders':self.get('orders')})
274
275 return contracted_diagram
276
279 """ This function returns what will be used as the 'loop_tag' attribute
280 of the ContractedVertex instance in the function 'get_contracted_loop_diagram'.
281 It is important since it is what is used by MG5_aMC to decide
282 if two processes have *exactly* the same matrix element and can be
283 identified.
284 There is no need to characterize the details of the FDStructures attached
285 to the loops because these are already compared using the rest of the
286 DiagramTag structure. All we need is to identify a structure by its
287 external leg numbers."""
288
289 canonical_tag = self['canonical_tag']
290
291
292
293
294
295
296
297 loop_parts_tagging = [[]]*len(canonical_tag)
298 for i, tag_elem in enumerate(canonical_tag):
299 loop_part = model.get_particle(tag_elem[0])
300 loop_parts_tagging[i] = (loop_part.get('spin'),
301 loop_part.get('color'),
302 loop_part.get('self_antipart'),
303 loop_part.get('mass'),
304 loop_part.get('width'),
305 loop_part.get('is_part'))
306
307
308
309 FDStructs_tagging = [[]]*len(canonical_tag)
310 for i, tag_elem in enumerate(canonical_tag):
311 for struct_ID in tag_elem[1]:
312 if not use_FDStructure_ID_for_tag:
313
314
315
316
317
318
319
320
321
322
323
324 pass
325
326
327 else:
328
329
330
331
332
333
334
335 FDStructs_tagging[i].append(struct_ID)
336
337 FDStructs_tagging[i].sort()
338 FDStructs_tagging[i] = tuple(FDStructs_tagging[i])
339
340
341
342
343
344
345
346 interactions_tagging = [[]]*len(canonical_tag)
347 for i, tag_elem in enumerate(canonical_tag):
348 inter = model.get_interaction(tag_elem[2])
349 coup_keys = sorted(inter.get('couplings').keys())
350 interactions_tagging[i]=(
351 tuple((key, inter.get('couplings')[key]) for key in coup_keys),
352 tuple(str(c) for c in inter.get('color')),
353 tuple(inter.get('lorentz')))
354
355 return tuple(
356
357 zip(
358
359 loop_parts_tagging,
360
361 FDStructs_tagging,
362
363 interactions_tagging,
364 )
365
366 + sorted(self.get_loop_orders(model).items())
367 )
368
370 """ Returns a base_objects.Diagram which correspond to this loop diagram
371 with the loop shrunk to a point. If struct_rep is no specified, then
372 the tagging will proceed assuming no FDStructure has been identified yet.
373 Otherwise, it will possible reuse them an update the repository."""
374
375 if self['type']<=0:
376 return copy.copy(self)
377
378 if self['contracted_diagram']:
379 return self['contracted_diagram']
380
381
382
383 if not self['canonical_tag'] or struct_rep is None:
384 n_external_legs = len(base_objects.LegList([l for l in
385 self.get_external_legs() if not l['loop_line']]))
386
387
388 start_in, end_in = n_external_legs +1, n_external_legs+2
389 for l in self['vertices'][0]['legs']:
390 if l.same(start_in):
391 break
392 elif l.same(end_in):
393 start_in, end_in = end_in, start_in
394 break
395
396 if struct_rep is None:
397 struct_rep = FDStructureList([])
398 self.tag(struct_rep, model, start_in=start_in, end_in=end_in,
399 synchronize=False)
400
401 contracted_diagram_vertices = base_objects.VertexList()
402
403
404
405
406 contracted_vertex = base_objects.ContractedVertex({
407 'id':-2,
408 'loop_orders':self.get_loop_orders(model),
409 'loop_tag': self.build_loop_tag_for_diagram_identification(model, struct_rep)
410 })
411
412
413
414
415
416 for tagelem in self['tag']:
417 contracted_vertex.get('legs').extend([struct_rep[
418 struct_ID].get('binding_leg') for struct_ID in tagelem[1]])
419
420
421 contracted_vertex.get('PDGs').extend([struct_rep[struct_ID].
422 get('binding_leg').get('id') for struct_ID in tagelem[1]])
423 contracted_diagram_vertices.extend(sum([struct_rep[
424 struct_ID].get('vertices') for struct_ID in tagelem[1]],[]))
425
426
427 contracted_diagram_vertices.append(contracted_vertex)
428
429 contracted_diagram = base_objects.Diagram(
430 {'vertices':contracted_diagram_vertices,'orders':self.get('orders')})
431
432 self['contracted_diagram'] = contracted_diagram
433
434 return contracted_diagram
435
436 - def get_CT(self,model,string=None):
437 """ Returns the CounterTerms of the type passed in argument. If None
438 it returns all of them. """
439 if string:
440 return base_objects.VertexList([vert for vert in \
441 self['CT_vertices'] if string in \
442 model['interaction_dict'][vert['id']]['type']])
443 else:
444 return self['CT_vertices']
445
447 """ Return none if there is no loop or if a tag has not yet been set and
448 returns True if this graph contains a purely fermionic loop and False if
449 not. """
450
451 if(self['tag']):
452 for part in self['tag']:
453 if not model.get('particle_dict')[part[0].get('id')].is_fermion():
454 return False
455 return True
456 else:
457 return False
458
460 """ Return None if there is no loop or if a tag has not yet been set and
461 returns True if this graph contains a tadpole loop and False if not. """
462
463 if(self['tag']):
464 if(len(self['tag'])==1):
465 return True
466 else:
467 return False
468 else:
469 return None
470
472 """Return None if there is no loop or if a tag has not yet been set and
473 returns True if this graph contains a vanishing tadpole loop and False
474 if not. """
475
476 if not self.is_tadpole():
477 return False
478
479
480 if(len(self['tag'][0][1])<=1):
481 return True
482
483 return any([part['mass'].lower()=='zero' for pdg,part in \
484 model.get('particle_dict').items() if \
485 pdg==abs(self['tag'][0][0]['id'])])
486
488 """ Return None if there is no loop or if a tag has not yet been set and
489 returns True if this graph contains a wave-function correction and False
490 if not. """
491
492 if self['tag'] :
493
494 if len(self['tag'])==2 and len(self['tag'][0][1])==1 \
495 and len(self['tag'][1][1])==1:
496
497 if struct_rep[self['tag'][0][1][0]].is_external() or \
498 struct_rep[self['tag'][1][1][0]].is_external():
499
500 inLegID=struct_rep[self['tag'][0][1][0]]['binding_leg']['id']
501 outLegID=struct_rep[self['tag'][1][1][0]]['binding_leg']['id']
502 return True
503
504
505 if len(self['tag'])==1 and len(self['tag'][0][1])==2 and \
506 (struct_rep[self['tag'][0][1][0]].is_external() or
507 struct_rep[self['tag'][0][1][1]].is_external()):
508 return True
509
510 return False
511 else:
512 return None
513
515 """Return the number of loop lines. """
516 if self['tag']:
517 return len(self['tag'])
518 else:
519 return None
520
521 @classmethod
523 """ Computes the weighting function S for this structure 'i' such that
524 S(i)>0 for each any i, S(i)!=S(j) if i['external_legs']!=j['external_legs']
525 and S(i+j)>max(S(i),S(j)). """
526
527 external_numbers=[leg['number'] for id in FD_ids_list for leg in \
528 struct_rep.get_struct(id).get('external_legs')]
529 external_numbers.sort()
530 weight=0
531 for i, number in enumerate(external_numbers):
532 weight=i*number_legs+number
533 return weight
534
535 @classmethod
537 """ This function chooses the place where to cut the loop in order to
538 maximize the loop wavefunction recycling in the open loops method.
539 This amounts to cut just before the combined structure with smallest
540 weight and then chose the direction to go towards the one with smallest
541 weight."""
542
543 tag=copy.deepcopy(intag)
544 number_legs=len(external_legs)
545
546
547 weights=[cls.compute_weight(t[1],struct_rep,number_legs) for t in tag]
548 imin = weights.index(min(weights))
549 tag=tag[imin:]+tag[:imin]
550 weights=weights[imin:]+weights[:imin]
551
552
553 rev_tag=cls.mirrored_tag(tag, model)
554
555 rev_tag=rev_tag[-1:]+rev_tag[:-1]
556 rev_weights=[cls.compute_weight(t[1],struct_rep,number_legs) for t in rev_tag]
557
558
559 if len(tag)==1:
560 return tag
561 elif len(tag)==2:
562 if abs(tag[0][0]['id'])>abs(tag[1][0]['id']):
563 return rev_tag
564 else:
565 return tag
566 else:
567 if rev_weights[1]<weights[1]:
568 return rev_tag
569 else:
570 return tag
571
572 @classmethod
574 """ This function chooses where to cut the loop. It returns the
575 canonical tag corresponding to this unambiguous choice."""
576
577
578
579
580
581
582
583 canonical_tag=copy.deepcopy(tag)
584 canonical_tag=cls.make_canonical_cyclic(canonical_tag)
585 canonical_mirrored_tag=copy.deepcopy(canonical_tag)
586 canonical_mirrored_tag=cls.mirrored_tag(canonical_mirrored_tag,model)
587
588 canonical_mirrored_tag=canonical_mirrored_tag[-1:]+\
589 canonical_mirrored_tag[:-1]
590
591
592
593
594
595
596
597
598
599
600
601
602 if (len(tag)==2 and abs(canonical_mirrored_tag[0][0]['id'])>\
603 abs(canonical_tag[0][0]['id'])) or (len(tag)>2 and \
604 canonical_mirrored_tag[1][1]<canonical_tag[1][1]):
605 canonical_tag=canonical_mirrored_tag
606
607 return canonical_tag
608
609 - def tag(self, struct_rep, model, start_in=None, end_in=None, synchronize=True):
610 """ Construct the tag of the diagram providing the loop structure
611 of it. """
612
613
614
615 loopVertexList=base_objects.VertexList()
616
617
618
619
620 external_legs = base_objects.LegList([l for l in
621 self.get_external_legs() if not l['loop_line']])
622 n_initial = len([1 for leg in external_legs if not leg['state']])
623
624 if start_in is None or end_in is None:
625 start_in = len(external_legs)+1
626 end_in = len(external_legs)+2
627
628
629
630 if isinstance(start_in,int) and isinstance(end_in,int):
631 start=start_in
632 end=end_in
633 elif isinstance(start_in,base_objects.Leg) and \
634 isinstance(end_in,base_objects.Leg):
635 start=start_in.get('number')
636 end=end_in.get('number')
637 else:
638 raise MadGraph5Error, "In the diagram tag function, 'start' and "+\
639 " 'end' must be either integers or Leg objects."
640
641 if self.process_next_loop_leg(struct_rep,-1,-1,start,end,\
642 loopVertexList, model, external_legs):
643
644
645
646
647
648
649
650
651 if self.cutting_method=='default':
652
653 canonical_tag=self.choose_default_lcut(self['tag'],model)
654 elif self.cutting_method=='optimal':
655
656
657 canonical_tag=self.choose_optimal_lcut(self['tag'],struct_rep,
658 model, external_legs)
659 else:
660 raise MadGraph5Error, 'The cutting method %s is not implemented.'\
661 %self.cutting_method
662
663 self['tag']=canonical_tag
664
665
666
667 if synchronize:
668 self.synchronize_loop_vertices_with_tag(model,n_initial,
669 struct_rep,start,end)
670
671
672
673
674 self['canonical_tag']=[[t[0]['id'],t[1],t[2]] for t in canonical_tag]
675 return True
676 else:
677 raise self.PhysicsObjectError, \
678 "Loop diagram tagging failed."
679 return False
680
681
682 @classmethod
684 """ Generate a loop vertex from incoming legs myleglist and the
685 interaction with id vertID of the model given in argument """
686
687 ref_dict_to1 = model.get('ref_dict_to1')
688
689
690 key=tuple(sorted([leg.get('id') for leg in myleglist]))
691 if ref_dict_to1.has_key(key):
692 for interaction in ref_dict_to1[key]:
693
694 if interaction[1]==vertID:
695
696
697
698 legid = interaction[0]
699
700
701 number = min([leg.get('number') for leg in\
702 myleglist])
703
704
705
706
707 if n_initial>1 and len(myleglist)>1 and len(filter( \
708 lambda leg: leg.get('state') == False, myleglist)) == 1:
709 state = False
710 else:
711 state = True
712 myleglist.append(base_objects.Leg(\
713 {'number': number,\
714 'id': legid,\
715 'state': state,
716 'loop_line': True}))
717
718 return base_objects.Vertex({'legs':myleglist,'id':vertID})
719 else:
720 raise cls.PhysicsObjectError, \
721 "An interaction from the original L-cut diagram could"+\
722 " not be found when reconstructing the loop vertices."
723
724 - def process_next_loop_leg(self, structRep, fromVert, fromPos, currLeg, \
725 endLeg, loopVertexList, model, external_legs):
726 """ Finds a loop leg and what is the next one. Also identify and tag the
727 FD structure attached in between these two loop legs. It adds the
728 corresponding tuple to the diagram tag and calls iself again to treat
729 the next loop leg. Return True when tag successfully computed."""
730
731 nextLoopLeg=None
732 legPos=-2
733 vertPos=-2
734 FDStructureIDList=[]
735 vertFoundID=-1
736 n_initial = len([1 for leg in external_legs if not leg['state']])
737
738
739 def process_loop_interaction(i,j,k,pos):
740 """For vertex position 'i' and loop leg position 'j'. Find the
741 structure attached to leg k of this loop interaction, tag it and
742 update the loop tag."""
743 FDStruct=FDStructure()
744
745
746
747 canonical = self.construct_FDStructure(i,pos,\
748 self['vertices'][i].get('legs')[k],FDStruct)
749
750 if not canonical:
751 raise self.PhysicsObjectError, \
752 "Failed to reconstruct a FDStructure."
753
754
755
756 if isinstance(canonical,int):
757 FDStruct.set('canonical',(((canonical,),0),))
758 elif isinstance(canonical,tuple):
759 FDStruct.set('canonical',canonical)
760 else:
761 raise self.PhysicsObjectError, \
762 "Non-proper behavior of the construct_FDStructure function"
763
764
765
766 myStructID=-1
767 myFDStruct=structRep.get_struct(FDStruct.get('canonical'))
768 if not myFDStruct:
769
770
771 myStructID=len(structRep)
772
773
774 FDStruct.set('id',myStructID)
775
776
777
778 FDStruct.generate_vertices(model, external_legs)
779 structRep.append(FDStruct)
780 else:
781
782
783
784
785
786
787
788 myStructID=myFDStruct.get('id')
789
790 FDStructureIDList.append(myStructID)
791
792
793
794
795 vertRange=range(len(self['vertices']))
796
797
798 if not fromVert == -1:
799 if fromPos == -1:
800
801
802
803 vertRange=vertRange[fromVert+1:]
804 else:
805
806
807
808
809 vertRange=vertRange[:fromVert]
810 vertRange.reverse()
811
812
813 for i in vertRange:
814
815
816
817
818 legRange=range(len(self['vertices'][i].get('legs')))
819 if fromPos == -1:
820
821 if not i==len(self['vertices'])-1:
822 legRange=legRange[:-1]
823 else:
824
825
826 if i==len(self['vertices'])-1:
827 continue
828 else:
829 legRange=legRange[-1:]
830 for j in legRange:
831 if self['vertices'][i].get('legs')[j].same(currLeg):
832 vertPos=i
833 vertFoundID=self['vertices'][i]['id']
834
835
836
837 if isinstance(currLeg,int):
838 currLeg=base_objects.Leg(self['vertices'][i].get('legs')[j])
839
840
841 for k in filter(lambda ind: not ind==j, \
842 range(len(self['vertices'][i].get('legs')))):
843
844
845
846
847
848
849
850 if not i==len(self['vertices'])-1 \
851 and k==len(self['vertices'][i].get('legs'))-1:
852 pos=-1
853 else:
854 pos=k
855
856 if self['vertices'][i].get('legs')[k].get('loop_line'):
857 if not nextLoopLeg:
858 nextLoopLeg=self['vertices'][i].get('legs')[k]
859 legPos=pos
860 else:
861 raise self.PhysicsObjectError, \
862 " An interaction has more than two loop legs."
863 else:
864 process_loop_interaction(i,j,k,pos)
865
866
867 break
868 if nextLoopLeg:
869 break
870
871
872 if not nextLoopLeg:
873
874
875 return False
876
877
878
879
880 if FDStructureIDList and vertFoundID not in [0,-1]:
881
882
883
884
885
886 myleglist=base_objects.LegList([copy.copy(\
887 structRep[FDindex]['binding_leg']) for FDindex in \
888 FDStructureIDList])
889
890
891
892
893
894
895
896 if loopVertexList:
897 self['tag'].append([copy.copy(\
898 loopVertexList[-1]['legs'][-1]),\
899 sorted(FDStructureIDList),vertFoundID])
900 myleglist.append(loopVertexList[-1]['legs'][-1])
901 else:
902 self['tag'].append([copy.copy(currLeg),\
903 sorted(FDStructureIDList),vertFoundID])
904 new_input_leg = copy.copy(currLeg)
905 if fromPos!=-1:
906
907
908
909
910
911
912 new_input_leg.set('id',model.get_particle(
913 new_input_leg.get('id')).get_anti_pdg_code())
914 myleglist.append(new_input_leg)
915
916
917
918
919
920
921
922
923
924
925
926
927 loopVertexList.append(\
928 self.generate_loop_vertex(myleglist,model,n_initial,vertFoundID))
929
930
931 if nextLoopLeg.same(endLeg):
932
933
934
935 if vertFoundID not in [0,-1]:
936 starting_Leg=copy.copy(myleglist[-1])
937 legid=model.get_particle(myleglist[-1]['id']).get_anti_pdg_code()
938 state=myleglist[-1].get('state')
939 else:
940 starting_Leg=copy.copy(currLeg)
941 legid=model.get_particle(currLeg['id']).get_anti_pdg_code()
942 state=currLeg.get('state')
943
944 loopVertexList.append(base_objects.Vertex(\
945 {'legs':base_objects.LegList([starting_Leg,\
946 base_objects.Leg({'number': endLeg,
947 'id': legid,
948 'state': state,
949 'loop_line': True})]),
950 'id':-1}))
951
952
953
954 return True
955 else:
956
957
958 return self.process_next_loop_leg(structRep, vertPos, legPos, \
959 nextLoopLeg, endLeg, loopVertexList, model, external_legs)
960
963 """ Construct the loop vertices from the tag of the loop diagram."""
964
965 if not self['tag']:
966 return
967
968 ref_dict_to1 = model.get('ref_dict_to1')
969
970
971 loopVertexList=base_objects.VertexList()
972 for i, t in enumerate(self['tag']):
973
974
975 myleglist=base_objects.LegList([copy.copy(\
976 struct_rep[FDindex]['binding_leg']) for FDindex in t[1]])
977 if i==0:
978 starting_leg=copy.copy(t[0])
979
980
981
982
983
984
985
986
987
988
989
990
991
992 if model.get_particle(starting_leg['id']).get('is_part'):
993 starting_leg['number']=lcut_part_number
994 end_number=lcut_antipart_number
995 else:
996 starting_leg['number']=lcut_antipart_number
997 end_number=lcut_part_number
998 starting_leg['state']=True
999 else:
1000 starting_leg=loopVertexList[-1].get('legs')[-1]
1001 self['tag'][i][0]=starting_leg
1002 myleglist.append(starting_leg)
1003 loopVertexList.append(self.generate_loop_vertex(myleglist,
1004 model,n_initial,t[2]))
1005
1006
1007 first_leg=copy.copy(loopVertexList[-1].get('legs')[-1])
1008 sec_leg_id=model.get_particle(first_leg['id']).get_anti_pdg_code()
1009 second_leg=base_objects.Leg({'number': end_number,
1010 'id': sec_leg_id,
1011 'state': first_leg.get('state'),
1012 'loop_line': True})
1013 loopVertexList.append(base_objects.Vertex(\
1014 {'legs':base_objects.LegList([first_leg,second_leg]),
1015 'id':-1}))
1016
1017 self['type'] = abs(first_leg['id'])
1018 self['vertices'] = loopVertexList
1019
1021 """ Construct iteratively a Feynman Diagram structure attached to a Loop,
1022 given at each step a vertex and the position of the leg this function is
1023 called from. At the same time, it constructs a canonical representation
1024 of the structure which is a tuple with each element corresponding to
1025 a 2-tuple ((external_parent_legs),vertex_ID). The external parent legs
1026 tuple is ordered as growing and the construction of the canonical
1027 representation is such that the 2-tuples appear in a fixed order.
1028 This functions returns a tuple of 2-tuple like above for the vertex
1029 where currLeg was found or false if fails.
1030
1031 To illustrate this algorithm, we take a concrete example,
1032 the following structure:
1033
1034 4 5 6 7
1035 1 3 \/2 \/ <- Vertex ID, left=73 and right=99
1036 \ / | \ / <- Vertex ID, left=34 and right=42
1037 | |4 |
1038 1\ | /2
1039 \|/ <- Vertex ID=72
1040 |
1041 |1
1042
1043 For this structure with external legs (1,2,3,5,6,7) and current created
1044 1, the canonical tag will be
1045
1046 (((1,2,3,4,5,6,7),72),((1,3),34),((2,6,7),42),((6,7),99),((4,5),73))
1047 """
1048 nextLeg = None
1049 legPos=-2
1050 vertPos=-2
1051
1052 vertRange=range(len(self['vertices']))
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073 vertBuffer=[]
1074
1075
1076
1077
1078 parentBuffer=[[],0]
1079
1080
1081
1082
1083
1084 if fromPos == -1:
1085
1086
1087
1088 vertRange=vertRange[fromVert+1:]
1089 else:
1090
1091
1092
1093
1094
1095 vertRange=vertRange[:fromVert]
1096 vertRange.reverse()
1097
1098
1099
1100
1101
1102 pos=-2
1103
1104
1105 def process_leg(vertID, legID):
1106 """ Treats the leg equal to currLeg found in the place located by
1107 self['vertices'][vertID].get('legs')[legID]"""
1108
1109
1110
1111 parentBuffer[1]=self['vertices'][vertID].get('id')
1112
1113
1114
1115
1116
1117
1118
1119 legPos=-2
1120 for k in [ind for ind in \
1121 range(len(self['vertices'][vertID].get('legs'))) if ind!=legID]:
1122
1123
1124
1125 if not self['vertices'][vertID].get('id'):
1126 return self.construct_FDStructure(vertID, k,\
1127 self['vertices'][vertID].get('legs')[k], FDStruct)
1128
1129 if k==len(self['vertices'][vertID].get('legs'))-1 \
1130 and not vertID==len(self['vertices'])-1:
1131 legPos=-1
1132 else:
1133 legPos=k
1134
1135 branch=self.construct_FDStructure(i, legPos, \
1136 self['vertices'][vertID].get('legs')[k], FDStruct)
1137 if not branch:
1138 raise self.PhysicsObjectError, \
1139 "Failed to reconstruct a FDStructure."
1140
1141 if isinstance(branch,int):
1142 parentBuffer[0].append(branch)
1143
1144
1145 elif isinstance(branch,tuple):
1146 parentBuffer[0]+=list(branch[0][0])
1147 vertBuffer.append(branch)
1148 else:
1149 raise self.PhysicsObjectError, \
1150 "Non-proper behavior of the construct_FDStructure function"
1151 return legPos
1152
1153
1154
1155
1156 for i in vertRange:
1157
1158
1159
1160
1161
1162 legRange=range(len(self['vertices'][i].get('legs')))
1163 if fromPos == -1:
1164
1165 if not i==len(self['vertices'])-1:
1166 legRange=legRange[:-1]
1167 else:
1168
1169
1170 if i==len(self['vertices'])-1:
1171 continue
1172 else:
1173 legRange=legRange[-1:]
1174
1175
1176
1177 findVert=False
1178
1179 for j in legRange:
1180 if self['vertices'][i].get('legs')[j].same(currLeg):
1181
1182 pos=process_leg(i,j)
1183
1184
1185 findVert=True
1186 break;
1187 if findVert:
1188 break;
1189
1190 if(pos == -2):
1191 if(not fromPos == -1):
1192
1193 FDStruct.get('external_legs').append(copy.copy(currLeg))
1194 return currLeg.get('number')
1195 else:
1196 raise self.PhysicsObjectError, \
1197 " A structure is malformed."
1198 else:
1199
1200
1201
1202
1203
1204
1205
1206 vertBuffer.sort()
1207
1208
1209
1210
1211
1212
1213 vertBufferFlat=[]
1214 for t in vertBuffer:
1215 for u in t:
1216 vertBufferFlat.append(u)
1217
1218
1219 parentBuffer[0].sort()
1220
1221 vertBufferFlat.insert(0,(tuple(parentBuffer[0]),parentBuffer[1]))
1222 return tuple(vertBufferFlat)
1223
1224
1225
1227 """ Return the starting loop line of this diagram, i.e. lcut leg one."""
1228 for v in self['vertices']:
1229 for l in v['legs']:
1230 if l['loop_line']:
1231 return l
1232
1234 """ Return the finishing line of this diagram, i.e. lcut leg two.
1235 Notice that this function is only available when the loop diagram is
1236 constructed with the special two-point vertex with id -1. """
1237
1238 assert self['vertices'][-1].get('id')==-1, "Loop diagrams must finish "+\
1239 " with vertex with id '-1' for get_finishing_loop_line to be called"
1240
1241 return max(self['vertices'][-1].get('legs'), key=lambda l: l['number'])
1242
1244 """ Return a set with one occurence of each different PDG code of the
1245 particles running in the loop. By convention, the PDF of the particle,
1246 not the antiparticle, is stored in this list. Using the tag would be
1247 quicker, but we want this function to be available before tagging as
1248 well"""
1249 return set([abs(l['id']) for v in self['vertices'] for l in v['legs'] \
1250 if l['loop_line']])
1251
1253 """ Return a dictionary with one entry per type of order appearing in
1254 the interactions building the loop flow. The corresponding keys are the
1255 number of type this order appear in the diagram. """
1256
1257 loop_orders = {}
1258 for vertex in self['vertices']:
1259
1260
1261
1262 if vertex['id'] not in [0,-1] and len([1 for leg \
1263 in vertex['legs'] if leg['loop_line']])==2:
1264 vertex_orders = model.get_interaction(vertex['id'])['orders']
1265 for order in vertex_orders.keys():
1266 if order in loop_orders.keys():
1267 loop_orders[order]+=vertex_orders[order]
1268 else:
1269 loop_orders[order]=vertex_orders[order]
1270 return loop_orders
1271
1272
1273 @classmethod
1275 """ Perform cyclic permutations on the tag given in parameter such that
1276 the structure with the lowest ID appears first."""
1277
1278 if not atag:
1279 return []
1280
1281 imin=-2
1282 minStructID=-2
1283 for i, part in enumerate(atag):
1284 if minStructID==-2 or min(part[1])<minStructID:
1285 minStructID=min(part[1])
1286 imin=i
1287
1288 atag=atag[imin:]+atag[:imin]
1289
1290 return atag
1291
1292 @classmethod
1294 """ Performs a mirror operation on A COPY of the tag and returns it. """
1295
1296 if not atag:
1297 return []
1298
1299
1300 revTag=[(copy.deepcopy(elem[0]), copy.copy(elem[1]), \
1301 copy.copy(elem[2])) for elem in atag]
1302
1303
1304 revTag.reverse()
1305
1306 shiftBuff=revTag[-1]
1307 for i in range(len(revTag)-1):
1308 revTag[-(i+1)]=[revTag[-(i+2)][0],revTag[-(i+1)][1],revTag[-(i+1)][2]]
1309 revTag[0]=[shiftBuff[0],revTag[0][1],revTag[0][2]]
1310
1311
1312
1313 nonselfantipartlegs = [ elem[0] for elem in revTag if not \
1314 model.get('particle_dict')[elem[0].get('id')]['self_antipart'] ]
1315 for leg in nonselfantipartlegs:
1316 leg.set('id',\
1317 model.get('particle_dict')[leg.get('id')].get_anti_pdg_code())
1318
1319 return revTag
1320
1321
1322
1323
1324
1326 """ Returns the pdgs of the lines running in the loop while not
1327 differentiating the particles from the anti-particles """
1328
1329 return [abs(tag_elem[0].get('id')) for tag_elem in self['tag']]
1330
1332 """ Returns the pdgs of the lines directly branching off the loop."""
1333
1334 return [structs.get_struct(struct_ID).get('binding_leg').get('id') \
1335 for tag_elem in self['tag'] for struct_ID in tag_elem[1]]
1336
1342 """ A special kind of LoopDiagram which does not contain a loop but only
1343 specifies all UV counter-term which factorize the the same given born
1344 and bringing in the same orders. UV mass renormalization does not belong to
1345 this class of counter-term for example, and it is added along with the R2
1346 interactions."""
1347
1349 """Default values for all properties"""
1350
1351 super(LoopUVCTDiagram,self).default_setup()
1352
1353
1354 self['type']='UV'
1355 self['UVCT_orders']={}
1356 self['UVCT_couplings']=[]
1357
1358 - def filter(self, name, value):
1359 """Filter for valid diagram property values."""
1360
1361 if name == 'UVCT_couplings':
1362 if not isinstance(value, list):
1363 raise self.PhysicsObjectError, \
1364 "%s is not a valid list" % str(value)
1365 else:
1366 for elem in value:
1367 if not isinstance(elem, str) and not isinstance(elem, int):
1368 raise self.PhysicsObjectError, \
1369 "%s is not a valid string" % str(value)
1370
1371 if name == 'UVCT_orders':
1372 if not isinstance(value, dict):
1373 raise self.PhysicsObjectError, \
1374 "%s is not a valid dictionary" % str(value)
1375
1376 if name == 'type':
1377 if not isinstance(value, str):
1378 raise self.PhysicsObjectError, \
1379 "%s is not a valid string" % str(value)
1380
1381 else:
1382 super(LoopUVCTDiagram, self).filter(name, value)
1383
1384 return True
1385
1387 """Return particle property names as a nicely sorted list."""
1388
1389 return ['vertices', 'UVCT_couplings', 'UVCT_orders', 'type', 'orders']
1390
1392 """ Finds the UV counter-term interaction present in this UVCTDiagram """
1393
1394 for vert in self['vertices']:
1395 if vert.get('id') != 0:
1396 if model.get_interaction(vert.get('id')).is_UV():
1397 return model.get_interaction(vert.get('id'))
1398
1399 return None
1400
1402 """Calculate the actual coupling orders of this diagram. Note
1403 that the special order WEIGTHED corresponds to the sum of
1404 hierarchies for the couplings."""
1405
1406 coupling_orders = dict([(c, 0) for c in model.get('coupling_orders')])
1407 weight = 0
1408 for couplings in [model.get('interaction_dict')[vertex.get('id')].\
1409 get('orders') for vertex in self['vertices'] if \
1410 vertex.get('id') != 0]+[self['UVCT_orders']]:
1411 for coupling in couplings:
1412 coupling_orders[coupling] += couplings[coupling]
1413 weight += sum([model.get('order_hierarchy')[c]*n for \
1414 (c,n) in couplings.items()])
1415 coupling_orders['WEIGHTED'] = weight
1416 self.set('orders', coupling_orders)
1417
1419 """Returns a nicely formatted string of the diagram content."""
1420 res=''
1421 if self['vertices']:
1422 res=res+super(LoopUVCTDiagram,self).nice_string()
1423 if self['UVCT_couplings']:
1424 res=res+'UV renorm. vertices: '
1425 res=res+','.join(str(vert) for vert in self['UVCT_couplings'])+'\n'
1426 if self['UVCT_orders']:
1427 res=res+'UVCT orders: '
1428 res=res+','.join(order for order in self['UVCT_orders'].keys())+'\n'
1429 if self['type']:
1430 res=res+'UVCT type: '+self['type']
1431
1432 return res
1433
1434
1435
1436
1437 -class LoopModel(base_objects.Model):
1438 """A class to store all the model information with advanced feature
1439 to compute loop process."""
1440
1442 """Make sure to copy over the attribute map_CTcoup_CTparam if the
1443 first instance used is a LoopModel"""
1444
1445 if len(args)>0 and isinstance(args[0],LoopModel):
1446 if hasattr(args[0],'map_CTcoup_CTparam'):
1447 self.map_CTcoup_CTparam = copy.copy(args[0].map_CTcoup_CTparam)
1448
1449 super(LoopModel,self).__init__(*args,**opts)
1450
1452 super(LoopModel,self).default_setup()
1453 self['perturbation_couplings'] = []
1454
1455
1456
1457
1458
1459
1460
1461
1462 self['coupling_orders_counterterms']={}
1463
1464
1465
1466
1467
1468 if not hasattr(self,'map_CTcoup_CTparam'):
1469 self.map_CTcoup_CTparam = {}
1470
1471
1472 - def filter(self, name, value):
1473 """Filter for model property values"""
1474
1475 if name == 'perturbation_couplings':
1476 if not isinstance(value, list):
1477 raise self.PhysicsObjectError, \
1478 "Object of type %s is not a list" % \
1479 type(value)
1480 for order in value:
1481 if not isinstance(order, str):
1482 raise self.PhysicsObjectError, \
1483 "Object of type %s is not a string" % \
1484 type(order)
1485 else:
1486 super(LoopModel,self).filter(name,value)
1487
1488 return True
1489
1491 """This function actualizes the dictionaries"""
1492
1493 if useUVCT:
1494 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1495 self['interactions'].generate_ref_dict(useR2UV=False,useUVCT=True)
1496 else:
1497 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1498 self['interactions'].generate_ref_dict()
1499 self['ref_dict_to0'].update(
1500 self['particles'].generate_ref_dict())
1501
1503 """Return process property names as a nicely sorted list."""
1504
1505 return ['name', 'particles', 'parameters', 'interactions', 'couplings',
1506 'lorentz','perturbation_couplings','conserved_charge']
1507
1508
1509
1510
1511 -class DGLoopLeg(base_objects.Leg):
1512 """A class only used during the loop diagram generation. Exactly like leg
1513 except for a few other parameters only useful during the loop diagram
1514 generation."""
1515
1527
1531
1532 - def filter(self, name, value):
1533 """Filter for model property values"""
1534
1535 if name == 'depth':
1536 if not isinstance(value, int):
1537 raise self.PhysicsObjectError, \
1538 "Object of type %s is not a int" % \
1539 type(value)
1540 else:
1541 super(DGLoopLeg,self).filter(name,value)
1542
1543 return True
1544
1546 """Return process property names as a nicely sorted list."""
1547
1548 return ['id', 'number', 'state', 'from_group','loop_line','depth']
1549
1551 """ Converts a DGLoopLeg back to a Leg. Basically removes the extra
1552 attributes """
1553
1554 aleg=base_objects.Leg()
1555 for key in aleg.get_sorted_keys():
1556 aleg.set(key,self[key])
1557
1558 return aleg
1559
1560
1561
1562
1563 -class FDStructure(base_objects.PhysicsObject):
1564 """FDStructure:
1565 list of vertices (ordered). This is part of a diagram.
1566 """
1567
1576
1578 """Returns wether the structure is simply made of an external particle
1579 only"""
1580 if (len(self['canonical'])==1 and self['canonical'][0][1]==0):
1581 return True
1582 else:
1583 return False
1584
1585 - def filter(self, name, value):
1586 """Filter for valid FDStructure property values."""
1587
1588 if name == 'vertices':
1589 if not isinstance(value, base_objects.VertexList):
1590 raise self.PhysicsObjectError, \
1591 "%s is not a valid VertexList object" % str(value)
1592
1593 if name == 'id':
1594 if not isinstance(value, int):
1595 raise self.PhysicsObjectError, \
1596 "id %s is not an integer" % repr(value)
1597
1598 if name == 'weight':
1599 if not isinstance(value, int):
1600 raise self.PhysicsObjectError, \
1601 "weight %s is not an integer" % repr(value)
1602
1603 if name == 'external_legs':
1604 if not isinstance(value, base_objects.LegList):
1605 raise self.PhysicsObjectError, \
1606 "external_legs %s is not a valid Leg List" % str(value)
1607
1608 if name == 'binding_leg':
1609 if not isinstance(value, base_objects.Leg):
1610 raise self.PhysicsObjectError, \
1611 "binding_leg %s is not a valid Leg" % str(value)
1612
1613 if name == 'canonical':
1614 if not isinstance(value, tuple):
1615 raise self.PhysicsObjectError, \
1616 "canonical %s is not a valid tuple" % str(value)
1617
1618 return True
1619
1621 """Return particle property names as a nicely sorted list."""
1622
1623 return ['id','external_legs','binding_leg','canonical','vertices']
1624
1626 """Returns a nicely formatted string of the structure content."""
1627
1628 mystr=''
1629
1630 if not self['id']==-1:
1631 mystr=mystr+'id: '+str(self['id'])+',\n'
1632 else:
1633 return '()'
1634
1635 if self['canonical']:
1636 mystr=mystr+'canonical_repr.: '+str(self['canonical'])+',\n'
1637
1638 if self['external_legs']:
1639 mystr=mystr+'external_legs: { '
1640 for leg in self['external_legs'][:-1]:
1641 mystr = mystr + str(leg['number']) + '(%s)' % str(leg['id']) \
1642 + ', '
1643 mystr = mystr + str(self['external_legs'][-1]['number']) + \
1644 '(%s)' % str(self['external_legs'][-1]['id']) + ' },\n'
1645 mystr = mystr+'binding_leg: '+str(self['binding_leg']['number']) +\
1646 '(%s)' % str(self['binding_leg']['id'])
1647 return mystr
1648
1650 """Returns a nicely formatted string of the structure vertices."""
1651 mystr=''
1652 if self['vertices']:
1653 mystr = mystr+'('
1654 for vert in self['vertices']:
1655 mystr = mystr + '('
1656 for leg in vert['legs'][:-1]:
1657 mystr = mystr + str(leg['number']) + \
1658 '(%s)' % str(leg['id']) + ','
1659 mystr = mystr[:-1] + '>'
1660 mystr = mystr + str(vert['legs'][-1]['number']) +\
1661 '(%s)' % str(vert['legs'][-1]['id']) + ','
1662 mystr = mystr + 'id:' + str(vert['id']) + '),'
1663 mystr = mystr[:-1] + ')'
1664 return mystr
1665 elif len(self['external_legs'])==1:
1666 return '('+str(self['external_legs'][0]['number'])+\
1667 '('+str(self['external_legs'][0]['id'])+'))'
1668 else:
1669 return '()'
1670
1671
1673 """ This functions generate the vertices building this structure,
1674 starting from the outter legs going towards the binding leg.
1675 It uses the interactions dictionaries from the model. """
1676
1677 if isinstance(model, base_objects.Process):
1678 assert external_legs is None
1679
1680 external_legs= model.get('legs')
1681 model = model['model']
1682 assert external_legs is not None
1683 assert isinstance(model, base_objects.Model)
1684
1685
1686
1687
1688 self.set('vertices',base_objects.VertexList())
1689
1690 tag=copy.copy(self['canonical'])
1691
1692
1693 ref_dict_to1 = model.get('ref_dict_to1')
1694
1695 if not tag:
1696 raise self.PhysicsObjectError, \
1697 "The canonical tag of the FD structure is not set yet, so that the "+\
1698 "reconstruction of the vertices cannot be performed."
1699
1700
1701 leglist = copy.deepcopy(external_legs)
1702
1703
1704 legDict={}
1705 for leg in leglist:
1706 legDict[leg['number']]=leg
1707
1708
1709
1710 if len(tag)==1 and len(tag[0][0])==1:
1711
1712 self['binding_leg']=copy.deepcopy(legDict[tag[0][0][0]])
1713 return
1714
1715
1716 tag=list(tag)
1717 tag.reverse()
1718
1719
1720
1721 for i, tagelem in enumerate(tag):
1722 tag[i]=list(tagelem)
1723 tag[i][0]=base_objects.LegList([legDict[myleg] for myleg in \
1724 tagelem[0]])
1725
1726
1727
1728
1729
1730 while tag:
1731
1732
1733 legs=tag[0][0]
1734
1735
1736 key=tuple(sorted([leg.get('id') for leg in legs]))
1737 if ref_dict_to1.has_key(key):
1738 for interaction in ref_dict_to1[key]:
1739
1740 if interaction[1]==tag[0][1]:
1741
1742
1743 legid = interaction[0]
1744
1745
1746 number = min([leg.get('number') for leg in legs])
1747
1748
1749 if len(filter(lambda leg: leg.get('state') == False,
1750 legs)) == 1:
1751 state = False
1752 else:
1753 state = True
1754 legs.append(base_objects.Leg({'number': number,\
1755 'id': legid,\
1756 'state': state,
1757 'loop_line': False}))
1758
1759 self.get('vertices').append(base_objects.Vertex(\
1760 {'legs':legs,'id':interaction[1]}))
1761 break
1762
1763
1764
1765 for i, tagelement in enumerate(tag[1:]):
1766 Found=False
1767 for leg in legs[:-1]:
1768 try:
1769 tag[i+1][0].remove(leg)
1770 Found=True
1771 except Exception:
1772 pass
1773 if Found:
1774 tag[i+1][0].append(legs[-1])
1775
1776
1777
1778 if len(tag)==1:
1779 self['binding_leg']=copy.deepcopy(legs[-1])
1780
1781
1782
1783 tag.pop(0)
1784
1785 else:
1786 raise self.PhysicsObjectError, \
1787 "The canonical tag of the FD structure is corrupted because one "+\
1788 "interaction does not exist."
1789
1794 """List of FDStructure objects
1795 """
1796
1798 """Test if object obj is a valid Diagram for the list."""
1799
1800 return isinstance(obj, FDStructure)
1801
1803 """Return the FDStructure of the list with the corresponding canonical
1804 tag if ID is a tuple or the corresponding ID if ID is an integer.
1805 It returns the structure if it founds it, or None if it was not found"""
1806 if isinstance(ID, int):
1807 for FDStruct in self:
1808 if FDStruct.get('id')==ID:
1809 return FDStruct
1810 return None
1811 elif isinstance(ID, tuple):
1812 for FDStruct in self:
1813 if FDStruct.get('canonical')==ID:
1814 return FDStruct
1815 return None
1816 else:
1817 raise self.PhysicsObjectListError, \
1818 "The ID %s specified for get_struct is not an integer or tuple"%\
1819 repr(object)
1820
1822 """Returns a nicely formatted string"""
1823 mystr = str(len(self)) + ' FD Structures:\n'
1824 for struct in self:
1825 mystr = mystr + " " + struct.nice_string() + '\n'
1826 return mystr[:-1]
1827