1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """All the routines to choose the position to each vertex and the
16 direction for particles. All those class are not related to any output class.
17
18 This file contains 4 class:
19 * FeynmanLine which extend the Leg with positioning information
20 * VertexPoint which extend the vertex with position and line information.
21 Coordinates belongs to [0,1] interval
22 * FeynmanDiagram which
23 1) Extends a diagram to have position information - load_diagram
24 2) Is able to structure the vertex in level - define_level
25 level are the number of s_channel line-initial particles
26 separating the vertex from the initial particles starting point.
27 3) Attributes position to each vertex - find_initial_vertex_position
28 * FeynmanDiagramHorizontal
29 is a child of FeynmanDiagram which assign position in a different way.
30
31 The x-coordinate will proportional to the level, both in FeynmanDiagram and
32 in FeynmanDiagramHorizontal
33
34 In FeynmanDiagram, the y-coordinate of external particles are put (if
35 possible and if option authorizes) to 0,1. all other y-coordinate are
36 assign such that the distance between two neighbor of the same level
37 are always the same.
38
39 In FeynmanDiagramHorizontal, an additional rules apply: if only one
40 S-channel is going from level to the next, then this S-channel should be
41 horizontal."""
42
43 from __future__ import division
44
45 import math
46
47 import madgraph.core.base_objects as base_objects
48
49
50
51
52
54 """All the information about a line in a Feynman diagram
55 i.e. begin-end/type/tag."""
56
58 """Exception raised if an error occurs in the definition
59 or the execution of a Feynam_line."""
60
62 """Initialize the FeynmanLine content."""
63
64 for key, value in init_dict.items():
65 setattr(self, key, value)
66 self.pid = pid
67 self.start = 0
68 self.end = 0
69
70
71
72
73
74
75
76
77
79 """
80 make a link between the present object and the associate model
81 """
82
83 assert isinstance(model, base_objects.Model), ' try to assign a non model obect'
84
85 self.model = model
86
88 """-Re-Define the starting point of the line."""
89
90 assert isinstance(vertex, VertexPoint), 'The begin point should be a ' + \
91 'Vertex_Point object'
92
93 self.start = vertex
94 vertex.add_line(self)
95 return
96
98 """-Re-Define the starting point of the line. with check"""
99
100 assert isinstance(vertex, VertexPoint), 'The end point should be a ' + \
101 'Vertex_Point object'
102
103 self.end = vertex
104 vertex.add_line(self)
105 return
106
108 """Associate the vertex to the line at the correct position.
109 line.start should be closer of the lower right corner than line.end.
110
111 This is achieved in the following way:
112 * We don't care about external particles.Those one will be perform
113 easily in a second step. In the mean time we apply this method anyway.
114 Internal particles are created from a combination of particles.
115 * S-channel either are create from number [X,Y,Z are strictly bigger
116 than two and A,B,C are strictly bigger than one).
117 (1 A [X Y]> 1) =>forward
118 (X Y [Z]> X) => backward
119 * T-channel are also produce either by
120 (1 X> 1) =>forward
121 (2 X >2) => backward
122 So the common rule is to check if the number is one or not.
123 """
124
125 if self.start:
126 self.def_end_point(vertex)
127 elif self.end:
128 self.def_begin_point(vertex)
129
130 else:
131 number = self.number
132 if number == 1:
133 self.def_begin_point(vertex)
134 else:
135 self.def_end_point(vertex)
136
138 """Define the line orientation. Use the following rules:
139 Particles move timelike when anti-particles move anti-timelike.
140 """
141
142 if (self.pid < 0):
143 self.inverse_begin_end()
144
146 """Change the particle in his anti-particle if this type is
147 equal to 'inversetype'."""
148
149 drawtype = self.get_info('line')
150 if drawtype == inversetype:
151 self.inverse_part_antipart()
152
154 """Pass particle into an anti-particle. This is needed for initial state
155 particles (usually wrongly defined) and for some fermion flow resolution
156 problem."""
157
158 self.pid = -1 * self.pid
159
161 """Invert the orientation of the line. This is needed to have correct
162 fermion flow."""
163
164 self.start, self.end = self.end, self.start
165
167 """Return the model information 'name' associated to the line."""
168
169 pid = abs(self.pid)
170 return self.model.get_particle(pid).get(name)
171
172
174 """Return the name associate to the particle."""
175
176 pid = self.pid
177 model_info = self.model.get_particle(pid)
178 if model_info is None:
179 model_info = self.model.get_particle(-pid)
180 return model_info.get_name()
181
183 """ return the length of the line """
184
185 return math.sqrt((self.end.pos_x - self.start.pos_x) ** 2 + \
186 (self.end.pos_y - self.start.pos_y) ** 2)
187
188
190 """Returns True if the particle is a fermion."""
191
192 model_info = self.model.get_particle(abs(self.pid))
193 if model_info.get('line') == 'straight':
194 return True
195
200
202 """Define that two line are equal when they have the same pointer"""
203
204 return self is other
205
207 """Define that two line are different when they have different
208 pointer."""
209
210 return self is not other
211
212
213
214
216 """Check if the two line intersects and returns status. A common vertex
217 is not consider as an intersection.
218 This routine first check input validity.
219
220 At current status this is use for test/debugging only."""
221
222 assert self.check_position_exist()
223 assert line.check_position_exist()
224
225 return self._has_intersection(line)
226
228 """Check if the two line intersects and returns status. A common vertex
229 is not consider as an intersection.
230
231 At current status this is only use for test/debugging only."""
232
233
234 sys_error = 1e-7
235
236
237 min, max = self._domain_intersection(line)
238
239
240 if min == None:
241 return False
242
243
244 if min == max :
245
246 if abs(self.start.pos_x - self.end.pos_x) > sys_error:
247
248 if abs(line.start.pos_x - line.end.pos_x) > sys_error:
249
250 return False
251
252 return self._intersection_with_vertical_line(line)
253
254
255 elif (abs(line.start.pos_x - line.end.pos_x) > sys_error):
256
257 return line._intersection_with_vertical_line(self)
258
259
260 else:
261
262 min, max = self._domain_intersection(line, 'y')
263 if min == None or min == max:
264 return False
265 else:
266 return True
267
268
269 xS0 = self.start.pos_x
270 yS0 = self.start.pos_y
271 xS1 = self.end.pos_x
272 yS1 = self.end.pos_y
273
274 xL0 = line.start.pos_x
275 yL0 = line.start.pos_y
276 xL1 = line.end.pos_x
277 yL1 = line.end.pos_y
278
279 coef1 = (yS1 - yS0) / (xS1 - xS0)
280 coef2 = (yL1 - yL0) / (xL1 - xL0)
281
282
283 if abs(coef1 - coef2) < sys_error:
284
285 if abs(line._has_ordinate(min) - self._has_ordinate(min)) < \
286 sys_error:
287 return True
288 else:
289 return False
290
291
292 commonX = (yS0 - yL0 - coef1 * xS0 + coef2 * xL0) / (coef2 - coef1)
293
294
295 if (commonX >= min) == (commonX >= max):
296 return False
297
298 commonY = self._has_ordinate(commonX)
299
300
301 if self.is_end_point(commonX, commonY):
302 if line.is_end_point(commonX, commonY):
303 return False
304 else:
305 return True
306 else:
307 return True
308
310 """Check if 'x','y' are one of the end point coordinates of the line.
311
312 At current status this is use for test/debugging only."""
313
314
315 gap = 1e-9
316
317 if abs(x - self.start.pos_x) < gap and abs(y - self.start.pos_y) < gap:
318 return True
319 elif abs(x - self.end.pos_x) < gap and abs(y - self.end.pos_y) < gap:
320 return True
321 else:
322 return False
323
324 - def domain_intersection(self, line, axis='x'):
325 """Returns x1,x2 where both line and self are defined.
326 Returns None, None if this domain is empty.
327 This routine contains self consistency check
328
329 At current status this is use for test/debugging only."""
330
331 assert isinstance(line, FeynmanLine), ' domain intersection are between ' + \
332 'Feynman_line object only and not {0} object'.format(type(line))
333
334
335 self.check_position_exist()
336 line.check_position_exist()
337
338
339 return self._domain_intersection(line, axis)
340
341 - def _domain_intersection(self, line, axis='x'):
342 """Returns x1,x2 where both line and self are defined.
343 Returns None, None if this domain is empty.
344 This routine doesn't contain self consistency check.
345
346 At current status this is use for debugging only."""
347
348
349 min_self, max_self = self.border_on_axis(axis)
350 min_line, max_line = line.border_on_axis(axis)
351
352
353 start = max(min_self, min_line)
354 end = min(max_self, max_line)
355 if start <= end:
356 return start, end
357 else:
358 return None, None
359
361 """ Returns the two value of the domain interval for the given axis.
362
363 At current status this is use for test/debugging only."""
364
365 data = [getattr(self.start, 'pos_' + axis), \
366 getattr(self.end, 'pos_' + axis)]
367 data.sort()
368 return data
369
371 """Checks if line intersect self. Line SHOULD be a vertical line and
372 self COULDN'T. No test are done to check those conditions.
373
374 At current status this is use for test/debugging only."""
375
376
377 y_self = self._has_ordinate(line.start.pos_x)
378
379
380
381 ymin, ymax = line.border_on_axis('y')
382
383
384 if (ymin == y_self or ymax == y_self):
385 if self.is_end_point(line.start.pos_x, y_self):
386 return False
387 else:
388 return True
389 elif (y_self > ymin) and (y_self < ymax):
390 return True
391 else:
392 return False
393
395 """Check that the begin-end position are defined.
396
397 At current status this is use for debugging only."""
398
399 try:
400 self.start.pos_x
401 self.end.pos_y
402 except:
403 raise self.FeynmanLineError, 'No vertex in begin-end position ' + \
404 ' or no position attach at one of those vertex '
405 return True
406
408 """Returns the y associate to the x value in the line
409 Raises FeynmanLineError if point outside interval or result not unique.
410 This routines contains check consistency.
411
412 At current status this is use for debugging only."""
413
414 if __debug__:
415 self.check_position_exist()
416 min = self.start.pos_x
417 max = self.end.pos_x
418 if max < min:
419 min, max = max, min
420
421 if min == max:
422 raise self.FeynmanLineError, 'Vertical line: no unique solution'
423 if(not(min <= x <= max)):
424 raise self.FeynmanLineError, 'point outside interval invalid ' + \
425 'invalid order {0:3}<={1:3}<={2:3}'.format(min, x, max)
426
427 return self._has_ordinate(x)
428
430 """Returns the y associate to the x value in the line
431 This routines doesn't contain check consistency.
432
433 At current status this is use for debugging only."""
434
435
436 x_0 = self.start.pos_x
437 y_0 = self.start.pos_y
438 x_1 = self.end.pos_x
439 y_1 = self.end.pos_y
440
441 alpha = (y_1 - y_0) / (x_1 - x_0)
442
443
444 ordinate_fct = lambda X: y_0 + alpha * (X - x_0)
445 return ordinate_fct(x)
446
447
448
449
450
451
453 """Extension of the class Vertex in order to store the information
454 linked to the display of a FeynmanDiagram, as position
455 """
456
458 """Exception raised if an error occurs in the definition
459 or the execution of a VertexPoint."""
460
461
463 """Update a vertex to a VertexPoint with additional information about
464 positioning and link with other vertex/line of the diagram."""
465
466
467 assert(isinstance(vertex, base_objects.Vertex))
468
469
470 for key, value in vertex.items():
471 setattr(self, key, value)
472 self.lines = []
473 self.level = -1
474 self.pos_x = 0
475 self.pos_y = 0
476
478 """-Re-Define the position of the vertex in a square [0, 1]^2"""
479
480
481 assert 0 <= x <= 1 and 0 <= y <= 1 , 'vertex coordinate should be' + \
482 ' in 0,1 interval introduce value ({0},{1})'.format(x, y)
483
484 self.pos_x = x
485 self.pos_y = y
486 return
487
489 """Import the line of the second vertex in the first one
490 this means
491 A) change the 'line' of this vertex
492 B) change the start-end position of line to point on this vertex
493 C) remove common_line (if defined)."""
494
495 for line in vertex.lines:
496
497 if line is common_line:
498 self.lines.remove(line)
499 continue
500
501
502
503 if line.start is vertex:
504 line.def_begin_point(self)
505 else:
506 line.def_end_point(self)
507 return
508
509
511 """Add the line in the list keeping line connected to this vertex :
512 self.lines. This routine avoid duplication of entry."""
513
514 assert isinstance(line, FeynmanLine), \
515 'Trying to add in a Vertex a non FeynmanLine Object'
516
517 for oldline in self.lines:
518 if oldline is line:
519 return
520
521 self.lines.append(line)
522
524 """Remove the line from the lineList. Didn't touch to vertex associate
525 to begin/end point. This happens only if we fuse two vertex together.
526 (Then the line will be completely drop out, such that we dont't care
527 about those vertex point."""
528
529 assert isinstance(line_to_del, FeynmanLine), \
530 'trying to remove in a Vertex_Point a non FeynmanLine Object'
531
532
533
534 for i, line in enumerate(self.lines):
535 if line is line_to_del:
536 del self.lines[i]
537 return
538
539 raise self.VertexPointError, 'trying to remove in a ' + \
540 'Vertex_Point a non present Feynman_Line'
541
542
544 """Define the Vertex level at 'level'. The level represents the
545 distance between the initial vertex and the current vertex. This
546 distance is define has the number of non T-channel particles needed to
547 connect this particle to initial states starting point."""
548
549 assert isinstance(level, int), 'Trying to attribute non integer level'
550
551 self.level = level
552
554 """Check if this vertex is external , i.e is related to a single
555 (external) particles."""
556
557
558 if len(self.lines) == 1:
559 return True
560 else:
561 return False
562
564 """Check if the line associate to the two vertex are equivalent.
565 This means that they have the same number of particles with the same pid
566 and that the external particles have the same number.
567
568 This is a backup function, this is not use for the moment."""
569
570
571 if len(self.lines) != len(other.lines):
572 return False
573
574
575 other_line_pid = [line.pid for line in other.lines]
576
577 other_line_number = [line.number for line in other.lines if \
578 line.is_external()]
579
580
581
582
583 for s_line in self.lines:
584 try:
585 other_line_pid.remove(s_line.pid)
586 except ValueError:
587 return False
588 if s_line.is_external():
589 try:
590 other_line_number.remove(s_line.number)
591 except ValueError:
592 return False
593
594
595 return True
596
598 """Provide a unique id for the vertex"""
599
600 tag = 0
601 for i, line in enumerate(self.lines):
602 tag += line.number / 10 ** (-i)
603 tag = tag * 10 ** (len(self.lines) + 1)
604 return tag
605
607 """Define equality with pointeur equality."""
608
609 return self is other
610
611
612
613
615 """Object to compute the position of the different Vertex and Line associate
616 to a diagram object.
617
618 This is the standard way to doing it [main]
619 1) Creates the new structure needed for the diagram generation [load_diagram]
620 This defines self.vertexList and self.lineList which are the list of
621 respectively all the vertex and all the line include in the diagram.
622 Each line is associated to two vertex, so we have added new vertex
623 compare to the diagram object (base_objects.Diagram). The two vertex are
624 named begin/end and represent the line direction. at this stage all line
625 are going timelike. T-channel are going from particle 1 to particle 2
626 2) Associate to each vertex a level. [define_level]
627 The level represents the distance between the initial vertex and the
628 current vertex. This distance is define has the number of non T-channel
629 particles needed to connect this particles to a initial state starting
630 point.
631 3) Compute the position of each vertex [find_initial_vertex_position]
632 The x-coordinate will proportional to the level. The vertex at level=0.
633 will have x=0 coordinate (vertex associate with initial state particle)
634 The vertex with the highest level value should be at x=1.
635
636 If an external particles cann't be place at the border at the current
637 level. we will try to place it one level later, potentially up to last
638 level. A option can force to place all external particles at x=1.
639
640 the y-coordinate are chosen such that
641 - external particles try to have (y=0 or y=1) coordinates
642 (if not move those vertex to next level)
643 - other particles maximizes distances between themselves.
644 4) Solve Fermion-flow and (anti)particle type [self.solve_line_direction]
645 the way to solve the fermion-flow is basic and fail in general for
646 majorana fermion. The basic idea is "particles are going timelike".
647 This is sufficient in all cases but T-channel particles which are solve
648 separately."""
649
651 """Class for internal error."""
652
653 - def __init__(self, diagram, model, amplitude=False, opt=None):
654 """Store the information concerning this diagram. This routines didn't
655 perform any action at all.
656 diagram: The diagram object to draw
657 model: The model associate to the diagram
658 amplitude: tell if the diagram has already fixed the I/O state of the fermion
659 opt: A DrawingOpt instance with all options for drawing the diagram."""
660
661
662 assert isinstance(diagram, base_objects.Diagram), \
663 'first argument should derivate from Diagram object'
664 assert isinstance(model, base_objects.Model), \
665 'second argument should derivate from Model object'
666
667
668 self.diagram = diagram
669 self.model = model
670 self.amplitude = amplitude
671
672 if opt is None:
673 self.opt = DrawOption()
674 else:
675 assert isinstance(opt, DrawOption), 'third argument should derivates' + \
676 ' from DrawOption object'
677 self.opt = opt
678
679
680 self.vertexList = []
681 self.initial_vertex = []
682 self.lineList = []
683 self.max_level = 0
684
685
686 self._treated_legs = []
687 self._available_legs = {}
688 self._ext_distance_up = self.opt.external
689 self._ext_distance_down = self.opt.external
690
691
693 """This routine will compute all the vertex position and line
694 orientation needed to draw the diagram."""
695
696
697
698 self.load_diagram(contract=self.opt.contract_non_propagating)
699
700 self.define_level()
701
702 self.find_initial_vertex_position()
703
704 self.adjust_position()
705
706 self.solve_line_direction()
707
708
709 fake_vertex = base_objects.Vertex({'id':0, 'legs':base_objects.LegList([])})
710
712 """Define all the object for the Feynman Diagram Drawing (Vertex and
713 Line) following the data include in 'self.diagram'
714 'contract' defines if we contract to one point the non propagating line.
715 """
716
717 for vertex in self.diagram.get('vertices'):
718 self.load_vertex(vertex)
719
720
721 last_vertex = self.vertexList[-1]
722
723 for line in last_vertex.lines:
724 self.deal_last_line(line)
725
726 if contract:
727
728 self._fuse_non_propa_particule()
729
730
731
732
733
734 for line in self.lineList:
735 if line.end == 0 or line.start == 0:
736
737 vertex_point = VertexPoint(self.fake_vertex)
738 self.vertexList.append(vertex_point)
739
740 if line.state== False:
741 if line.start:
742 line.inverse_begin_end()
743 line.def_begin_point(vertex_point)
744 vertex_point.def_level(0)
745 self.initial_vertex.append(vertex_point)
746 else:
747 if line.end:
748 line.inverse_begin_end()
749 line.def_end_point(vertex_point)
750
751 if len(self.initial_vertex) == 2:
752 if self.initial_vertex[0].lines[0].number == 2:
753 self.initial_vertex.reverse()
754 else:
755
756 self.remove_t_channel()
757
758 return
759
761 """Find the position of leg in self._treated_legs
762
763 if equal=0 returns the last position of number in the list
764 otherwise check that leg is the item in self._treated_legs
765
766 the two methods provides the same result if they provide a result.
767 But some times equal=0 mode provides result when equal=1 doesn't.
768 To my understanding equal=1 is suppose to be sufficient in all cases
769 but gg> 7x( g ) fails with using equal=1 only.
770
771 'end' removes the last 'end' element of the list, before looking at
772 the id in the list. (the list is not modify)"""
773
774 if equal:
775 return self.find_leg_id2(leg, end=end)
776
777 for i in range(len(self.lineList) - 1 - end, -1, -1):
778 if leg.get('number') == self.lineList[i].number:
779 return i
780
781 return None
782
784 """Find the position of leg in self._treated_legs. Use object equality
785 to find the position."""
786
787 for i in range(len(self.lineList) - 1 - end, -1, -1):
788 if (self._treated_legs[i] is leg):
789 return i
790
792 """Find the position of leg in self._treated_legs but only if this b
793 belongs to an available particles"""
794
795 try:
796 return self._available_legs[gen_id]
797 except:
798 return None
799
801 """1) Extend the vertex to a VertexPoint.
802 2) Add this vertex in vertexList of the diagram
803 3) Update vertex.lines list. (first update the leg into line if needed)
804 4) assign line.start[end] to this vertex. (in end if start is already
805 assigned to another vertex). the start-end will be flip later
806 if needed.
807 5) if the fermion flow is correctly set by the diagram (amplitude=True)
808 Then change the particles/anti-particles states accordingly.
809 """
810
811
812 vertex_point = VertexPoint(vertex)
813
814
815 self.vertexList.append(vertex_point)
816
817
818 for i, leg in enumerate(vertex.get('legs')):
819
820 gen_id = leg.get('number')
821
822
823
824 mg_id = self.find_leg_id3(gen_id)
825
826
827
828
829
830
831
832
833
834
835
836
837 if mg_id:
838 del self._available_legs[gen_id]
839 line = self.lineList[mg_id]
840 else:
841 line = self.load_leg(leg)
842 if i + 1 == len(vertex.get('legs')):
843 self._available_legs[gen_id] = len(self._treated_legs) - 1
844
845
846 line.add_vertex(vertex_point)
847
848
849
850
851
852
853 if line.number == 1 == vertex.get('legs')[0].get('number'):
854 line.inverse_part_antipart()
855 elif self.amplitude and line.number == 1:
856 nb = [l.get('number') for l in vertex.get('legs')]
857 if nb.count(1) == 2:
858 line.inverse_part_antipart()
859
861 """Extend the leg to Feynman line. Associate the line to the diagram.
862 """
863
864
865 line = FeynmanLine(leg.get('id'), leg)
866 line.def_model(self.model)
867
868
869
870 self._treated_legs.append(leg)
871 self.lineList.append(line)
872
873 return line
874
876 """The line of the last vertex breaks the rules that line before
877 '>' exist previously and the one after don't. The last one can also
878 already exist and for the one before the '>' sometimes they arrive
879 with a second object which is equivalent to another one but not
880 the same object. discover those case and treat this properly."""
881
882
883 if last_line.end == 0 or last_line.start == 0:
884
885 id1 = self.find_leg_id(self._treated_legs[-1])
886
887 id2 = self.find_leg_id(self._treated_legs[-1], end=len(self._treated_legs) - id1)
888 if id2 is not None:
889
890 line = self.lineList[id2]
891
892
893
894
895
896 if last_line.start == 0:
897 if line.end == 0 :
898 line.def_end_point(last_line.end)
899 else:
900 line.def_begin_point(last_line.end)
901
902 last_line.end.remove_line(last_line)
903 else:
904 if line.end == 0 :
905 line.def_end_point(last_line.start)
906 else:
907 line.def_begin_point(last_line.start)
908
909 last_line.start.remove_line(last_line)
910
911
912 self.lineList.remove(last_line)
913 else:
914 return
915
917 """Fuse all the non propagating line
918 step:
919 1) find those line
920 2) fuse the vertex
921 3) remove one vertex from self.vertexList
922 4) remove the line/leg from self.lineList/self._treated_leg
923 """
924
925
926
927 for i in range(len(self.lineList)).__reversed__():
928 if self.lineList[i].get_info('propagating'):
929 continue
930 else:
931 line = self.lineList[i]
932 line.start.fuse_vertex(line.end, common_line=line)
933 self.vertexList.remove(line.end)
934 del self._treated_legs[i]
935 del self.lineList[i]
936
938 """Assign to each vertex a level:
939 the level correspond to the number of visible particles and S-channel
940 needed in order to reach the initial particles vertex.
941
942 This is computing by search level by level starting at level 0.
943 """
944
945 for vertex in self.initial_vertex:
946 self.def_next_level_from(vertex)
947
948
950 """Define level for adjacent vertex.
951 If those vertex is already defined do nothing
952 Otherwise define as level+1 (at level 1 if T-channel)
953
954 This routine defines also self.max_level.
955
956 This routine is foreseen for an auto-recursive mode. So as soon as a
957 vertex have his level defined. We launch this routine for this vertex.
958 """
959
960 level = vertex.level
961 for line in vertex.lines:
962 if line.end.level != -1:
963 continue
964
965
966 if line.state == False:
967
968 line.end.def_level(1)
969 else:
970
971 line.end.def_level(level + 1)
972
973 self.max_level = max(self.max_level, level + 1)
974
975 self.def_next_level_from(line.end)
976
977
979 """Returns the vertex (T-vertex authorize) associate to level 1.
980 We start with the vertex associate to first entry of previous_level
981 and then following the T-line."""
982
983 vertex_at_level = []
984 try:
985 t_vertex = self.initial_vertex[-2]
986 except:
987 return []
988
989 while 1:
990
991 t_vertex = self.find_next_t_channel_vertex(t_vertex)
992
993
994 if t_vertex:
995 vertex_at_level.append(t_vertex)
996 else:
997 return vertex_at_level
998
1000 """Returns the next t_vertex. i.e. the vertex following t_vertex. t_line
1001 indicates the 'wrong' T-direction. This routines returns also the 'good'
1002 evolution direction (which will be the wrong one at the next step)."""
1003
1004 for line in t_vertex.lines:
1005 if line.state == False and line.start is t_vertex:
1006 return line.end
1007
1009 """Returns a list of vertex such that all those vertex are one level
1010 after the level of vertexlist and sorted in such way that the list
1011 start with vertex connected with the first vertex of 'vertexlist' then
1012 those connected to the second and so on."""
1013
1014 vertex_at_level = []
1015 for vertex in previous_level:
1016 if vertex.is_external() and vertex.pos_y not in [0, 1]:
1017
1018
1019 vertex.def_level(vertex.level + 1)
1020 vertex_at_level.append(vertex)
1021 continue
1022
1023 for line in vertex.lines:
1024 if line.start is vertex and line.state == True:
1025 vertex_at_level.append(line.end)
1026
1027 return vertex_at_level
1028
1058
1059
1061 """Finds the vertex position for level one, T channel are authorize"""
1062
1063
1064 t_vertex = self.find_t_channel_vertex()
1065
1066 self.assign_pos(t_vertex, 1)
1067 return t_vertex
1068
1069
1071 """Finds the vertex position for the particle at 'level' given the
1072 ordering at previous level given by the vertexlist.
1073 if auto equals True then pass in auto-recursive mode."""
1074
1075 if level > self.max_level:
1076 return
1077
1078
1079
1080
1081
1082 vertex_at_level = self.find_vertex_at_level(vertexlist)
1083
1084 if not vertex_at_level:
1085 return
1086
1087
1088
1089 self.assign_pos(vertex_at_level, level)
1090
1091
1092 if auto and vertex_at_level:
1093 self.find_vertex_position_at_level(vertex_at_level, level + 1)
1094
1095
1096 - def assign_pos(self, vertex_at_level, level, min=0, max=1):
1097 """Assign the position to each vertex of vertex_at_level.
1098
1099 The x-coordinate will the ratio of the current level with the maximum
1100 level of the diagram.
1101
1102 If the first_vertex of vertex_at_level is an outgoing particle. Put it
1103 at y=0 if possible (this could be prevented by min>0 or by drawing
1104 option). if you put it at y=0 delete the vertex of the list to avoid
1105 duplications.
1106
1107 Do the symmetric case for the last entry of vertex_at_level.
1108
1109 The y-value for the other point is computed such that the distance
1110 between two vertex of the list are the same. the distance between min
1111 (resp. max) and the first vertex is also equal but if min=0 (resp.
1112 max=1) then this distance counts half.
1113
1114 the option self.opt.external is used
1115 if equals 0, the external lines are authorizes to end only
1116 at the end of the diagram (in x=1 axis) so this will forbid
1117 to put any vertex at y=0-1 (except if x=1)
1118 if bigger than 0, minimal distance in before putting a external line
1119 on the border of the diagram.
1120
1121
1122 The computation of y is done in this way
1123 first compute the distance [dist] between two vertex and assign the point.
1124 begin_gap and end_gap are the ratio of the compute distance to put
1125 between min and first vertex.
1126 """
1127
1128 if not vertex_at_level:
1129 return []
1130
1131 if level > self.max_level:
1132 raise self.FeynamDiagramError, 'self.max_level not correctly ' + \
1133 'assigned level %s is bigger than max_level %s' % \
1134 (level, self.max_level)
1135
1136
1137
1138 if level == self.max_level:
1139 ext_dist_up = 1
1140 ext_dist_down = 1
1141
1142 if len(vertex_at_level) == 1 and min ==0 and max == 1:
1143 vertex_at_level[0].def_position(1, 0.5)
1144 return []
1145 else:
1146
1147 ext_dist_up = self._ext_distance_up
1148 ext_dist_down = self._ext_distance_down
1149
1150 begin_gap, end_gap = 1, 1
1151
1152 if min == 0:
1153 if ext_dist_down and vertex_at_level[0].is_external():
1154 line = vertex_at_level[0].lines[0]
1155 if line.end.level - line.start.level >= ext_dist_down:
1156
1157 self.define_vertex_at_border(vertex_at_level[0], level, 0)
1158
1159 del vertex_at_level[0]
1160
1161 if not vertex_at_level:
1162 return []
1163 else:
1164 begin_gap = 0.5
1165 else:
1166 begin_gap = 0.5
1167
1168
1169 if max == 1:
1170 if ext_dist_up and vertex_at_level[-1].is_external():
1171 line = vertex_at_level[-1].lines[0]
1172 if line.end.level - line.start.level >= ext_dist_up:
1173
1174 self.define_vertex_at_border(vertex_at_level[-1], level, 1)
1175
1176 del vertex_at_level[-1]
1177 if not vertex_at_level:
1178 return []
1179 else:
1180 end_gap = 0.5
1181 else:
1182 end_gap = 0.5
1183
1184
1185 dist = (max - min) / (begin_gap + end_gap + len(vertex_at_level) - 1)
1186
1187
1188 for i, vertex in enumerate(vertex_at_level):
1189 vertex.def_position(level / self.max_level, min + dist * \
1190 (begin_gap + i))
1191
1192 return vertex_at_level
1193
1195 """Define the position of the vertex considering the distance required
1196 in the Drawing Options. Update the option if needed."""
1197
1198
1199 if pos_y == 1:
1200 dist = self._ext_distance_up
1201 self._ext_distance_up += self.opt.add_gap
1202 else:
1203 dist = self._ext_distance_down
1204 self._ext_distance_down += self.opt.add_gap
1205
1206
1207 if dist % 1:
1208
1209 if level < self.max_level:
1210 pos_x = (level - 1 + (dist % 1)) / self.max_level
1211 elif (1 - vertex.lines[0].start.pos_x) * self.max_level > dist:
1212 pos_x = (level - 1 + (dist % 1)) / self.max_level
1213 else:
1214 pos_x = 1
1215 else:
1216 pos_x = level / self.max_level
1217
1218 vertex.def_position(pos_x, pos_y)
1219
1220
1222 """Removes all T-channel in a diagram and convert those in S-channel.
1223 This occur for 1>X diagram where T-channel are wrongly define."""
1224
1225 for line in self.lineList:
1226 if line.state == False:
1227 line.state = True
1228
1229
1231 """Computes the directions of the lines of the diagrams.
1232 first use simple rules as particles move in time directions (to right).
1233 - define_line_orientation -. Then flip T-channel particles to
1234 correct fermion flow in T-channel. Majorana case not deal correctly
1235 at this stage."""
1236
1237
1238
1239
1240
1241 for line in self.lineList:
1242 if line.state == True:
1243 line.define_line_orientation()
1244
1245
1246
1247
1248
1249
1250 try:
1251 t_vertex = self.initial_vertex[-2]
1252 except:
1253 return
1254
1255 t_vertex = self.find_next_t_channel_vertex(t_vertex)
1256 self.initial_vertex[0].lines[0].define_line_orientation()
1257
1258 t_old = self.initial_vertex[0].lines[0]
1259 while 1:
1260
1261 ver_flow = 0
1262 t_next = None
1263 for line in t_vertex.lines:
1264
1265
1266 if line.state == False and t_old is not line and \
1267 line.start is t_vertex:
1268 t_next = line
1269
1270
1271
1272
1273
1274 if not line.is_fermion():
1275 continue
1276
1277
1278 if (line.start is t_vertex):
1279 ver_flow += 1
1280 elif line.end is t_vertex:
1281 ver_flow -= 1
1282
1283
1284 if t_next:
1285 t_old = t_next
1286 t_vertex = t_next.end
1287
1288 if ver_flow:
1289 t_next.inverse_begin_end()
1290 else:
1291 if ver_flow:
1292 self.initial_vertex[1].lines[0].inverse_begin_end()
1293 return
1294
1295
1297 """Modify the position of some particles in order to improve the final
1298 diagram look. This routines use one option
1299 1) max_size which forbids external particles to be longer than max_size.
1300 This is in level unit. If a line is too long we contract it to
1301 max_size preserving the orientation.
1302 2) external indicating the minimal x-gap for an external line. This
1303 constraints is already take into account in previous stage. But that
1304 stage cann't do non integer gap. So this routines correct this."""
1305
1306 finalsize = self.opt.max_size
1307
1308
1309 if not finalsize:
1310 return
1311
1312
1313 for line in self.lineList:
1314 if line.is_external():
1315
1316
1317 if line.state == False or not line.is_external():
1318 continue
1319 size = line.get_length() * self.max_level
1320 if size > finalsize:
1321 ratio = finalsize / size
1322 new_x = line.start.pos_x + ratio * (line.end.pos_x -
1323 line.start.pos_x)
1324 new_y = line.start.pos_y + ratio * (line.end.pos_y -
1325 line.start.pos_y)
1326 line.end.def_position(new_x, new_y)
1327
1329 """Return a string to check to conversion of format for the diagram.
1330
1331 This is a debug function."""
1332
1333 text = 'line content :\n'
1334 for i in range(0, len(self.lineList)):
1335 line = self.lineList[i]
1336 try:
1337 begin = self.vertexList.index(line.start)
1338 except:
1339 begin = -1
1340 try:
1341 end = self.vertexList.index(line.end)
1342 except:
1343 end = -1
1344 try:
1345 external = line.is_external()
1346 except:
1347 external = '?'
1348 text += 'pos, %s ,id: %s, number: %s, external: %s, \
1349 begin at %s, end at %s \n' % (i, line.pid, \
1350 line.number, external, begin, end)
1351 text += 'vertex content : \n'
1352 for i in range(0, len(self.vertexList)):
1353 vertex = self.vertexList[i]
1354 text += 'pos, %s, id: %s, external: %s, uid: %s ' % \
1355 (i, vertex.id, vertex.is_external(), \
1356 vertex.get_uid())
1357 text += 'line: ' + ','.join([str(self.lineList.index(line)) \
1358 for line in vertex.lines]) + '\n'
1359 return text
1360
1361
1363 """Returns a string to check the level of each vertex.
1364
1365 This is a debug function."""
1366
1367 for line in self.lineList:
1368 if line.start.level > line.end.level:
1369 if text == 0:
1370 raise self.FeynamDiagramError('invalid level order')
1371
1372
1373 text = ''
1374 for vertex in self.vertexList:
1375 text += 'line : '
1376 text += ','.join([str(line['id']) for line in vertex.legs])
1377 text += ' level : ' + str(vertex.level)
1378 text += '\n'
1379 if text:
1380 return text
1381
1383 """Returns a string to check the position of each vertex.
1384
1385 This is a debug function."""
1386
1387 text = ''
1388 for vertex in self.vertexList:
1389 text += 'line : '
1390 text += ','.join([str(line.id) for line in vertex.lines])
1391 text += ' level : ' + str(vertex.level)
1392 text += ' pos : ' + str((vertex.pos_x, vertex.pos_y))
1393 text += '\n'
1394 return text
1395
1397 """Returns if some line cross are crossing each other.
1398
1399 This is a debug Function and is used for the test routine."""
1400
1401
1402 for i, line in enumerate(self.lineList):
1403 for j in range(i + 1, len(self.lineList)):
1404 line2 = self.lineList[j]
1405
1406 if line.has_intersection(line2):
1407 print 'intersection for %s %s' % (i, j)
1408 print 'line', i, '(', line.start.pos_x, line.start.pos_y, \
1409 ') (', line.end.pos_x, line.end.pos_y, ')'
1410 print 'line', j, '(', line2.start.pos_x, line2.start.pos_y, \
1411 ') (', line2.end.pos_x, line2.end.pos_y, ')'
1412 return True
1413 return False
1414
1416 """Check if two diagrams are equivalent. (same structure-same particle)
1417
1418 This function is not used for the moment. The initial purpose was the
1419 avoid duplication of identical diagram in the output (these could happen
1420 if we contract non propagating line). But the number of such comparaison
1421 rise as the number of diagram square such that the total time needed for
1422 this feature was consider as too (time-)expansive."""
1423
1424
1425 if self.max_level != other.max_level:
1426 return False
1427 elif len(self.lineList) != len(other.lineList):
1428 return False
1429
1430
1431
1432
1433 other_pos = [(vertex.pos_x, vertex.pos_y) for vertex in other.vertexList]
1434 for vertex_self in self.vertexList:
1435 try:
1436 i = other_pos.index((vertex_self.pos_x, vertex_self.pos_y))
1437 except:
1438
1439 return False
1440 else:
1441 vertex_other = other.vertexList[i]
1442
1443
1444
1445
1446 if not vertex_self.has_the_same_line_content(vertex_other):
1447 return False
1448
1449
1450
1451 return True
1452
1453
1454
1455
1456
1458 """Object to compute the position of the different Vertex and Line associate
1459 to a diagram object. This routines is quite similar to FeynmanDiagram.
1460 The only differences concerns the rules for the y-coordinate of each vertex.
1461
1462 In case of vertex with one and only one S-channel going to the next level.
1463 Then force this line to be horizontal. This creates sub-interval where other
1464 vertex can be place following the same rule as before (equal distance
1465 between vertex) but this time sub-interval by sub-interval."""
1466
1468 """Finds the vertex position for the particle at 'level' given the
1469 ordering at previous level given by the vertexlist.
1470 if auto=True pass in autorecursive mode.
1471
1472 Compare to the function of FeynmanDiagram, this check the number of
1473 S-channel particles going out of each vertex. If the result is one:
1474 1) Fix the associate vertex at the same y as the original vertex
1475 -> horizontal line
1476 2) Assign non fix vertex below the fix one in the current interval.
1477 3) Continue to the next vertex."""
1478
1479
1480 if level == 1 or level == self.max_level :
1481 FeynmanDiagram.find_vertex_position_at_level(self, vertexlist, \
1482 level, auto)
1483 return
1484 elif level > self.max_level:
1485 return
1486
1487
1488
1489
1490
1491
1492 vertex_at_level = self.find_vertex_at_level(vertexlist)
1493 vertex_at_level2 = []
1494
1495
1496
1497 min_pos = 0
1498 list_unforce_vertex = []
1499
1500
1501
1502 for vertex in vertexlist:
1503
1504 s_vertex = []
1505 ext_vertex = []
1506 v_pos = vertex.pos_y
1507
1508
1509
1510 for line in vertex.lines:
1511
1512
1513 if line.end in vertex_at_level:
1514 new_vertex = line.end
1515 elif line.start in vertex_at_level:
1516 new_vertex = line.start
1517 else:
1518
1519 continue
1520
1521
1522 if line.is_external():
1523 ext_vertex.append(new_vertex)
1524 else:
1525 s_vertex.append(new_vertex)
1526
1527
1528 if len(s_vertex) != 1:
1529
1530
1531 if len(ext_vertex) <= 1:
1532 if vertex.pos_y >= 0.5:
1533 list_unforce_vertex += (s_vertex + ext_vertex)
1534 else:
1535 list_unforce_vertex += (ext_vertex + s_vertex)
1536 else:
1537 list_unforce_vertex += ext_vertex[:-1] + s_vertex + \
1538 ext_vertex[-1:]
1539 continue
1540
1541
1542 force_vertex = s_vertex[0]
1543 force_vertex.def_position(level / self.max_level, v_pos)
1544
1545 list_unforce_vertex += ext_vertex
1546
1547
1548 if (len(ext_vertex) == 1 and v_pos >= 0.5) or len(ext_vertex) > 1:
1549 vertex_at_level2 += self.assign_pos(list_unforce_vertex[:-1], \
1550 level, min_pos, v_pos)
1551
1552 list_unforce_vertex = [list_unforce_vertex[-1]]
1553 else:
1554 vertex_at_level2 += self.assign_pos(list_unforce_vertex, level, \
1555 min_pos, v_pos)
1556 list_unforce_vertex = []
1557
1558
1559 min_pos = v_pos
1560 vertex_at_level2.append(force_vertex)
1561
1562
1563 if list_unforce_vertex:
1564 vertex_at_level2 += self.assign_pos(list_unforce_vertex, level, \
1565 min_pos, 1)
1566
1567 if auto and vertex_at_level2:
1568 self.find_vertex_position_at_level(vertex_at_level2, level + 1)
1569
1570
1571
1572
1574 """In principle ALL routines representing diagram in ANY format SHOULD
1575 derive from this class.
1576
1577 This is a (nearly empty) frameworks to draw a diagram in any type format
1578
1579 This frameworks defines in particular
1580 - function to convert the input diagram (create by the generation step)
1581 in the correct object. [convert_diagram]
1582 - main loop to draw a diagram in a line-by-line method
1583 [draw - draw_diagram - draw_line]
1584 - name of routine (routine are empty) in order to fit with the framework
1585 [ associate_name - associate_number - draw_straight ]
1586 - some basic definition of routines
1587 [conclude - initialize]
1588
1589 This framework is base on the idea that we can create the diagram line after
1590 line. Indeed all line object (FeynmanLine) contains the full information
1591 needed to be drawed independently of the rest of the diagram.
1592
1593 In order to create a class with this framework you should start to write the
1594 draw_straight, draw_curly, ... method which are called by the framework.
1595
1596 If you want to write a file, you can store his content in self.text variable
1597 the routine conclude will then automatically write the file.
1598
1599 The main routine to draw a diagram is 'draw' which call
1600 1) initialize: setup things for the diagram (usually open a file).
1601 2) convert_diagram : Update the diagram in the correct format if needed.
1602 3) draw_diagram : Build the diagram line after line.
1603 4) conclude : finish the operation.
1604 """
1605
1607 """Standard error for error occuring to create output of a Diagram."""
1608
1609 - def __init__(self, diagram=None, filename=None, model=None, amplitude=None, \
1610 opt=None):
1611 """Define basic variables and store some global information.
1612 All argument are optional:
1613 diagram : is the object to 'diagram' should inherit from either
1614 base_objects.Diagram or drawing_lib.FeynmanDiagram.
1615 filename: file's name of the file to write.
1616 model: model associate to the diagram. In principle use only if diagram
1617 inherit from base_objects.Diagram (for conversion).
1618 amplitude: amplitude associates to the diagram. NOT USE for the moment.
1619 In future you could pass the amplitude associate to the object in
1620 order to adjust fermion flow in case of Majorana fermion.
1621 opt: should be a valid DrawOption object."""
1622
1623
1624
1625 try:
1626 assert(not model or isinstance(model, base_objects.Model))
1627 assert(not filename or isinstance(filename, basestring))
1628 except AssertionError:
1629 raise self.DrawDiagramError('No valid model provide to convert ' + \
1630 'diagram in appropriate format')
1631
1632 assert opt is None or isinstance(opt, DrawOption) , \
1633 'The Option to draw the diagram are in a invalid format'
1634
1635
1636
1637
1638
1639 self.diagram = diagram
1640 self.filename = filename
1641 self.model = model
1642 self.amplitude = amplitude
1643 self.opt = opt
1644
1645
1646 self.text = ''
1647
1648 if file:
1649 self.file = True
1650
1651 else:
1652 self.file = False
1653
1654 - def draw(self, opt=None):
1655 """Main routine to draw a single diagram.
1656 opt is DrawOption object use for the conversion of the
1657 base_objects.Diagram in one of the Diagram object."""
1658
1659
1660 self.convert_diagram(amplitude=self.amplitude, opt=opt)
1661
1662
1663
1664 self.initialize()
1665
1666 self.draw_diagram(self.diagram)
1667
1668
1669 self.conclude()
1670
1671
1672 - def convert_diagram(self, diagram=None, model=None, amplitude=None, \
1673 opt=None):
1674 """If diagram is a basic diagram (inherit from base_objects.Diagram)
1675 convert him to a FeynmanDiagram one. 'opt' keeps track of possible
1676 option of drawing. 'amplitude' is not use for the moment. But, later,
1677 if defined will authorize to adjust the fermion-flow of Majorana
1678 particles. opt is a DrawOption object containing all option on the way
1679 to draw the diagram (see this class for more details)
1680
1681
1682 This is the list of recognize options:
1683 external [True] : authorizes external particles to finish on
1684 horizontal limit of the square
1685 horizontal [True]: if on true use FeynmanDiagramHorizontal to
1686 convert the diagram. otherwise use FeynmanDiagram (Horizontal
1687 forces S-channel to be horizontal)
1688 non_propagating [True] : removes the non propagating particles
1689 present in the diagram."""
1690
1691 if diagram is None:
1692 diagram = self.diagram
1693
1694
1695 if isinstance(diagram, FeynmanDiagram):
1696 return
1697
1698
1699 if model is None:
1700 model = self.model
1701 elif not isinstance(model, base_objects.Model):
1702 raise self.DrawDiagramError('No valid model provide to convert ' + \
1703 'diagram in appropriate format')
1704
1705
1706
1707 if opt is None:
1708 if self.opt:
1709 opt = self.opt
1710 else:
1711 opt = DrawOption()
1712 elif not isinstance(opt, DrawOption):
1713 raise self.DrawDiagramError('The Option to draw the diagram are' + \
1714 ' in a invalid format')
1715
1716
1717
1718 if opt.horizontal:
1719 diagram = FeynmanDiagramHorizontal(diagram, model, \
1720 amplitude=amplitude, opt=opt)
1721 else:
1722 diagram = FeynmanDiagram(diagram, model, \
1723 amplitude=amplitude, opt=opt)
1724
1725
1726 diagram.main()
1727
1728
1729 self.diagram = diagram
1730 return diagram
1731
1733 """Initialization of object-file before starting in order to draw the
1734 diagram correctly. By default, we just check if we are in writing mode.
1735 And open the output file if we are."""
1736
1737
1738
1739 if self.file:
1740 self.file = open(self.filename, 'w')
1741
1742
1744 """Building the diagram Line after Line.
1745 This is the key routine of 'draw'."""
1746
1747 for vertex in self.diagram.vertexList:
1748 self.draw_vertex(vertex)
1749
1750 for line in self.diagram.lineList:
1751 self.draw_line(line)
1752
1753
1754
1755 self.put_diagram_number(number)
1756
1757
1758 if self.file:
1759 self.file.writelines(self.text)
1760 self.text = ""
1761
1763 """Final operation of the draw method. By default, this end to write the
1764
1765 file (if this one exist)
1766 """
1767
1768
1769
1770 if self.file:
1771 self.file.writelines(self.text)
1772 self.file.close()
1773 return
1774
1776 """Draw the line information.
1777 First, call the method associate the line type [draw_XXXXXX]
1778 Then finalize line representation by adding his name and, if it's an
1779 external particle, the MadGraph number associate to it."""
1780
1781
1782 line_type = line.get_info('line')
1783
1784 if hasattr(self, 'draw_' + line_type):
1785 getattr(self, 'draw_' + line_type)(line)
1786 else:
1787 self.draw_straight(line)
1788
1789
1790 name = line.get_name()
1791 self.associate_name(line, name)
1792
1793 if line.is_external():
1794 number = line.number
1795 self.associate_number(line, number)
1796
1798 """default vertex style"""
1799 pass
1800
1801
1803 """Example of routine for drawing the line 'line' in a specific format.
1804 straight is an example and can be replace by other type of line as
1805 dashed, wavy, curly, ..."""
1806
1807 raise self.DrawDiagramError, 'DrawDiagram.draw_straight should be ' + \
1808 'overwritten by Inherited Class'
1809
1811 """Method to associate a name to a the given line.
1812 The default action of this framework doesn't do anything"""
1813 pass
1814
1815
1817 """Method to associate a number to 'line'. By default this method is
1818 call only for external particles and the number is the MadGraph number
1819 associate to the particle. The default routine doesn't do anything"""
1820 pass
1821
1823 """Dealing with the different option of the drawing method.
1824 This is the list of recognize attributes:
1825 horizontal [False]: force S-channel to be horizontal
1826 external [0]: authorizes external particles to end
1827 at top or bottom of diagram. If bigger than zero
1828 this tune the length of those line.
1829 add_gap [0]: make external rising after each positioning.
1830 max_size [0]: this forbids external line bigger than
1831 max_size.
1832 non_propagating [True]:contracts non propagating lines"""
1833
1835 """Error raising if an invalid entry is set in a option."""
1836
1838 """Fullfill option with standard value."""
1839
1840
1841 self.external = 0
1842 self.add_gap = 0
1843 self.horizontal = False
1844 self.max_size = 1.5
1845 self.contract_non_propagating = True
1846
1847 if isinstance(opt, dict):
1848 for key, value in opt.items():
1849 self.set(key, value)
1850 else:
1851 for value in ['external','add_gap','horizontal','max_size',
1852 'contract_non_propagating']:
1853 if hasattr(opt, value):
1854 self.set(value, getattr(opt, value))
1855
1856 - def set(self, key, value):
1857 """Check and attribute the given value."""
1858
1859 if key in ['horizontal', 'contract_non_propagating']:
1860 value = self.pass_to_logical(value)
1861 setattr(self, key, value)
1862 elif(key in ['external', 'max_size', 'add_gap']):
1863 try:
1864 value = self.pass_to_number(value)
1865 except:
1866 raise self.DrawingOptionError('%s is not a numerical when %s \
1867 requires one' % (value, key))
1868 setattr(self, key, value)
1869
1870 else:
1871 raise self.DrawingOptionError('%s is not a valid property for \
1872 drawing object' % key)
1873
1875 """convert the value in a logical"""
1876
1877 if value in [0, False, '0', 'False', 'false']:
1878 return False
1879 else:
1880 return True
1881
1883 """Convert the value in a number"""
1884
1885 return float(value)
1886