1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """This files contains class for creating files or object representing a
17 diagram or a set of diagrams.
18
19 class structure:
20
21 DrawDiagram:
22 In principle ALL routines representing a diagram in Any format SHOULD derive
23 from this class. This is a (nearly empty) frameworks to draw a diagram
24 in any type format.
25
26 This frameworks defines in particular
27 - function to convert the input diagram in the correct object.
28 [convert_diagram]
29 - main loop to draw a diagram in a line-by-line method
30 [draw - draw_diagram]
31
32 DrawDiagramEPS:
33 This contains all the routines to represent one diagram in Encapsuled
34 PostScript (EPS)
35
36 DrawDiagramsEPS:
37 This contains all the routines to represent a set of diagrams in Encapsuled
38 PostScript (EPS)."""
39
40 from __future__ import division
41
42 from __future__ import absolute_import
43 import os
44 import math
45 import madgraph.core.drawing as draw
46 import madgraph.core.base_objects as base_objects
47 import madgraph.loop.loop_base_objects as loop_objects
48 import madgraph.various.misc as misc
49 import logging
50
51 logger = logging.getLogger('madgraph.drawing_eps')
52
53 _file_path = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] + '/'
54
55
56
57
59 """Class to write a EPS file containing the asked diagram
60 This class follows the DrawDiagram Frameworks.
61
62 The main routine to draw a diagram is 'draw' which call
63 1) initialize: setup things for the diagram (usually open a file)
64 2) convert_diagram : Update the diagram in the correct format if needed
65 3) draw_diagram : Perform diagram dependent operation
66 4) conclude : finish the operation.
67 """
68
69
70 width = 450
71 height = 450
72 npage = 1
73
74
75
76 x_min = 150
77 y_min = 450
78 x_max = 450
79 y_max = 750
80
81 blob_size = 1.5
82 april_fool = False
83
85 """Operation done before starting to create diagram specific EPS content
86 First open the file in write mode then write in it the header and the
87 library of particle type."""
88
89
90
91 super(EpsDiagramDrawer, self).initialize()
92
93
94 text = "%!PS-Adobe-2.0\n"
95 text += "%%" + "BoundingBox: -20 -20 %s %s \n" % \
96 (self.width, self.height)
97 text += "%%DocumentFonts: Helvetica\n"
98 text += "%%" + "Pages: %s \n" % self.npage
99 self.file.writelines(text)
100
101
102 self.file.writelines(open(os.path.join(_file_path, \
103 'iolibs/template_files/drawing_eps_header.inc')).read())
104
105
107 """Operation to perform when all code related to a specific diagram are
108 finish. Operation :
109 - Add the 'end of page' code
110 - write unwritten text and close the file. [DrawDiagram.conclude]"""
111
112
113 self.text = 'showpage\n'
114 self.text += '%%trailer\n'
115
116
117 super(EpsDiagramDrawer, self).conclude()
118
119
121 """All coordinates belongs to [0,1]. So that in order to have a visible
122 graph we need to re-scale the graph. This method distort the square in
123 a oblong. Deformation are linear."""
124
125
126
127 x = self.x_min + (self.x_max - self.x_min) * x
128 y = self.y_min + (self.y_max - self.y_min) * y
129
130 return x, y
131
132
152 - def draw_vertex(self, vertex, bypass = ['QED','QCD'] ):
153 """Add blob in case on non QED-QCD information"""
154
155 interaction = self.model.get_interaction(vertex.id)
156 if interaction:
157 order = interaction.get('orders')
158 order = [key for key in order.keys() if order[key] and \
159 key not in bypass]
160
161 if order:
162 x1, y1 = self.rescale(vertex.pos_x, vertex.pos_y)
163 self.text += " %s %s %s 1.0 Fblob \n" % (x1, y1, self.blob_size)
164
165
166
168 """ADD the EPS code for this fermion line."""
169
170
171 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
172 line.end.pos_x, line.end.pos_y, 'Ffermion')
173
175 """ADD the EPS code for this fermion line."""
176
177 if not cercle:
178 curvature = 0.4
179 else:
180 curvature = 1
181
182 if (line.begin.pos_x, line.begin.pos_y) == self.curved_part_start:
183 curvature *= -1
184
185
186 x1, y1 = self.rescale(line.begin.pos_x, line.begin.pos_y)
187 self.text += ' %s %s moveto \n' % (x1, y1)
188 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
189 line.end.pos_x, line.end.pos_y, '%s Fhiggsl' %\
190 curvature)
191
193 """ADD the EPS code for this fermion line."""
194
195 if not cercle:
196 curvature = 0.4
197 else:
198 curvature = 1
199
200 if (line.begin.pos_x, line.begin.pos_y) == self.curved_part_start:
201 curvature *= -1
202
203
204 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
205 line.end.pos_x, line.end.pos_y, '%s Ffermionl' %\
206 curvature)
207
209 """ADD the EPS code for this fermion line."""
210
211 if not cercle:
212 curvature = 4
213 else:
214 curvature = 5
215
216 is_tadpole = line.begin.pos_x==line.end.pos_x and \
217 line.begin.pos_y==line.end.pos_y
218
219 if is_tadpole:
220
221 direction = None
222 for l in line.begin.lines:
223 new_direction = (l.end.pos_x-l.begin.pos_x, l.end.pos_y-l.begin.pos_y)
224 if new_direction==(0.0,0.0):
225 continue
226 norm = math.sqrt(new_direction[0]**2+new_direction[1]**2)
227 new_direction = (new_direction[0]/norm, new_direction[1]/norm)
228
229 if not direction:
230 direction = new_direction
231 else:
232 if direction not in \
233 [new_direction, (-new_direction[0],-new_direction[1])]:
234 pass
235
236
237 continue
238
239
240 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
241 line.end.pos_x+0.01*direction[0], line.end.pos_y+0.01*direction[1],
242 '%s Ffermionl' % (curvature*7))
243 else:
244
245 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
246 line.end.pos_x+0.01, line.end.pos_y+0.01, '%s Ffermionl' %\
247 curvature)
249 """ADD the EPS code for this Higgs line."""
250
251
252 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
253 line.end.pos_x, line.end.pos_y, 'Fhiggs')
254
255
257 """ADD the EPS code for this Higgs line."""
258 if not cercle:
259 curvature = 4
260 else:
261 curvature = 5
262
263 is_tadpole = line.begin.pos_x==line.end.pos_x and \
264 line.begin.pos_y==line.end.pos_y
265
266 if is_tadpole:
267
268 direction = None
269 for l in line.begin.lines:
270 new_direction = (l.end.pos_x-l.begin.pos_x, l.end.pos_y-l.begin.pos_y)
271 if new_direction==(0.0,0.0):
272 continue
273 norm = math.sqrt(new_direction[0]**2+new_direction[1]**2)
274 new_direction = (new_direction[0]/norm, new_direction[1]/norm)
275
276 if not direction:
277 direction = new_direction
278 else:
279 if direction not in \
280 [new_direction, (-new_direction[0],-new_direction[1])]:
281
282
283 pass
284
285
286 x, y = self.rescale(line.begin.pos_x, line.begin.pos_y)
287 self.text += '%s %s moveto'%(x, y)
288 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
289 line.end.pos_x+0.01*direction[0], line.end.pos_y+0.01*direction[1],
290 '%s Fhiggsl' % (curvature*7))
291 else:
292
293 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
294 line.end.pos_x+0.01, line.end.pos_y+0.01, '%s Fhiggsl'% curvature)
295
297 """ADD the EPS code for the ghost line."""
298
299
300 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,\
301 line.end.pos_x, line.end.pos_y, 'Fghost')
302
304 """ADD the EPS code for the ghost line."""
305 if not cercle:
306 curvature = 0.4
307 else:
308 curvature = 1
309
310 if (line.begin.pos_x, line.begin.pos_y) == self.curved_part_start:
311 curvature *= -1
312
313 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,\
314 line.end.pos_x, line.end.pos_y, '%s Fghostl'% curvature)
315
317 """ADD the EPS code for the ghost line."""
318 if not cercle:
319 curvature = 4
320 else:
321 curvature = 5
322
323 is_tadpole = line.begin.pos_x==line.end.pos_x and \
324 line.begin.pos_y==line.end.pos_y
325
326 if is_tadpole:
327
328 direction = None
329 for l in line.begin.lines:
330 new_direction = (l.end.pos_x-l.begin.pos_x, l.end.pos_y-l.begin.pos_y)
331 if new_direction==(0.0,0.0):
332 continue
333 norm = math.sqrt(new_direction[0]**2+new_direction[1]**2)
334 new_direction = (new_direction[0]/norm, new_direction[1]/norm)
335
336 if not direction:
337 direction = new_direction
338 else:
339 if direction not in \
340 [new_direction, (-new_direction[0],-new_direction[1])]:
341
342
343 pass
344
345
346 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
347 line.end.pos_x+0.01*direction[0], line.end.pos_y+0.01*direction[1],
348 '%s Fghostl' % (curvature*7))
349 else:
350
351 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,\
352 line.end.pos_x+0.01, line.end.pos_y+0.01, '%s Fghostl'% curvature)
353
355 """ADD the EPS code for this photon line."""
356
357
358 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
359 line.end.pos_x, line.end.pos_y, '%d Fphoton%s' % (opt,type))
360
362 """ADD the EPS code for this photon line."""
363 if not cercle:
364 curvature = 0.4
365 else:
366 curvature = 1
367 if (line.begin.pos_x, line.begin.pos_y) == self.curved_part_start:
368 curvature *= -1
369
370 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
371 line.end.pos_x, line.end.pos_y, '%d %s Fphotonl%s' % (opt,curvature,type))
372
374 """ADD the EPS code for this photon line."""
375
376 if not cercle:
377 curvature = 4
378 else:
379 curvature = 5
380
381 is_tadpole = line.begin.pos_x==line.end.pos_x and \
382 line.begin.pos_y==line.end.pos_y
383
384 if is_tadpole:
385
386 direction = None
387 for l in line.begin.lines:
388 new_direction = (l.end.pos_x-l.begin.pos_x, l.end.pos_y-l.begin.pos_y)
389 if new_direction==(0.0,0.0):
390 continue
391 norm = math.sqrt(new_direction[0]**2+new_direction[1]**2)
392 new_direction = (new_direction[0]/norm, new_direction[1]/norm)
393
394 if not direction:
395 direction = new_direction
396 else:
397 if direction not in \
398 [new_direction, (-new_direction[0],-new_direction[1])]:
399
400
401 pass
402
403
404 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
405 line.end.pos_x+0.01*direction[0], line.end.pos_y+0.01*direction[1],
406 '%d %s Fphotonl%s' % (opt,curvature*7,type))
407 else:
408
409 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
410 line.end.pos_x+0.01, line.end.pos_y+0.01,
411 '%d %s Fphotonl%s' % (opt,curvature,type))
412
414 """ADD the EPS code for this gluon line."""
415
416
417
418
419 if (line.begin.pos_x < line.end.pos_x) or \
420 (line.begin.pos_x == line.end.pos_x and \
421 line.begin.pos_y > line.end.pos_y):
422 self.text += self.line_format(line.begin.pos_x,
423 line.begin.pos_y, line.end.pos_x,
424 line.end.pos_y, '0 Fgluon%s' % type)
425 else:
426 self.text += self.line_format(line.end.pos_x,
427 line.end.pos_y, line.begin.pos_x,
428 line.begin.pos_y, '0 Fgluon%s' % type)
429
434
436 """ADD the EPS code for this gluon line."""
437
438 dist = math.sqrt((line.begin.pos_x-line.end.pos_x)**2 + \
439 (line.begin.pos_y-line.end.pos_y)**2)
440 if not cercle or dist > 0.3:
441 curvature = 0.4
442 else:
443 curvature = 1
444
445
446
447
448
449
450 if (line.begin.pos_x, line.begin.pos_y) == self.curved_part_start:
451 curvature *= -1
452
453 self.text += self.line_format(line.end.pos_x,
454 line.end.pos_y, line.begin.pos_x,
455 line.begin.pos_y, '0 %s Fgluonl%s' % (-1*curvature, type))
456
457
458
464
469
471 """ADD the EPS code for this neutralino line."""
472
473
474 length = math.sqrt((line.end.pos_y - line.begin.pos_y)**2 + (line.end.pos_x - line.begin.pos_x) **2)
475 c1 = (line.end.pos_x - line.begin.pos_x)/length
476 c2 = (line.end.pos_y - line.begin.pos_y)/length
477
478 gap = 0.013
479 start2_x = line.begin.pos_x + gap * c1
480 start2_y = line.begin.pos_y + gap * c2
481 stop1_x = line.end.pos_x - gap * c1
482 stop1_y = line.end.pos_y - gap * c2
483
484
485 self.text += self.line_format(line.begin.pos_x, line.begin.pos_y,
486 stop1_x, stop1_y, '0 Fphoton%s' % (type))
487
488 self.text += self.line_format(start2_x, start2_y,
489 line.end.pos_x, line.end.pos_y, '0 Fphoton%s' % (type))
490
491
493 """ADD the comment 'diagram [number]' just below the diagram."""
494
495
496 x = 0.2
497 y = -0.17
498
499 x, y = self.rescale(x, y)
500
501 self.text += ' %s %s moveto \n' % (x, y)
502
503 if hasattr(self, 'diagram_type'):
504 self.text += '(%s diagram %s ) show\n' % (self.diagram_type, number + 1)
505
506
507 else:
508 self.text += '(diagram %s ) show\n' % (number + 1)
509
510
511
512 mystr = " (%s)" % ", ".join(["%s=%d" % (key, self.diagram.diagram['orders'][key]) \
513 for key in sorted(self.diagram.diagram['orders'].keys()) \
514 if key != 'WEIGHTED'])
515
516 x = 0.6
517 y = -0.17
518 x, y = self.rescale(x, y)
519
520 self.text += ' %s %s moveto \n' % (x, y)
521 self.text += '%s show\n' % (mystr)
522
523
524
526 """Write in the EPS figure the MadGraph5_aMC@NLO number associate to the line.
527 Note that this routine is called only for external particle."""
528
529
530 if line.begin.is_external():
531 vertex = line.begin
532 else:
533 vertex = line.end
534
535
536 x = vertex.pos_x
537 y = vertex.pos_y
538
539
540 if x == 0:
541 x = -0.04
542 else:
543 x += 0.04
544 y = line._has_ordinate(x)
545
546
547 x, y = self.rescale(x, y)
548
549 self.text += ' %s %s moveto \n' % (x, y)
550 self.text += '(%s) show\n' % (number)
551
553 """ADD the EPS code associate to the name of the particle. Place it near
554 to the center of the line.
555 """
556
557 is_tadpole = line.begin.pos_x==line.end.pos_x and \
558 line.begin.pos_y==line.end.pos_y
559
560 if is_tadpole:
561
562 direction = None
563 for l in line.begin.lines:
564 new_direction = (l.end.pos_x-l.begin.pos_x, l.end.pos_y-l.begin.pos_y)
565 if new_direction==(0.0,0.0):
566 continue
567 norm = math.sqrt(new_direction[0]**2+new_direction[1]**2)
568 new_direction = (new_direction[0]/norm, new_direction[1]/norm)
569 if not direction:
570 direction = new_direction
571 else:
572 if direction not in \
573 [new_direction, (-new_direction[0],-new_direction[1])]:
574
575
576 pass
577
578 orthogonal = (-direction[1],direction[0])
579
580
581 x1, y1 = line.begin.pos_x, line.begin.pos_y
582 x2, y2 = line.end.pos_x, line.end.pos_y
583
584 d = line.get_length()
585 if is_tadpole:
586 scale = 0.08
587 dx, dy = scale*orthogonal[0], scale*orthogonal[1]
588
589 elif abs(x1 - x2) < 1e-3:
590 dx = 0.015
591 dy = -0.01
592 elif abs(y1 - y2) < 1e-3:
593 dx = -0.01
594 dy = 0.025
595 elif ((x1 < x2) == (y1 < y2)):
596 dx = -0.03 * len(name)
597 dy = 0.02 * len(name)
598 else:
599 dx = 0.01
600 dy = 0.02
601 if loop:
602 dx, dy = 1.5* dx, dy
603 if x1 == x2:
604 if y1 < y2:
605 dx, dy = -dx, -dy
606 elif y1 == y2:
607 if x1 >x2:
608 dx, dy = -dx, -dy
609 elif x1 < x2:
610 dx, dy = -dx, -dy
611 if reverse:
612 dx, dy = -dx, -dy
613
614
615
616
617 x_pos = (x1 + x2) / 2 + dx
618 y_pos = (y1 + y2) / 2 + dy
619
620
621 x_pos, y_pos = self.rescale(x_pos, y_pos)
622
623 self.text += ' %s %s moveto \n' % (x_pos, y_pos)
624 self.text += '(' + name + ') show\n'
625
626
627
628
629
631 """Class to write a EPS file containing the asked set of diagram
632 This class follows the DrawDiagram Frameworks.
633
634 The main routine to draw a diagram is 'draw' which call
635 1) initialize: setup things for the diagram (usually open a file)
636 2) convert_diagram : Update the diagram in the correct format if needed
637 3) draw_diagram : Perform diagram dependent operation
638 4) conclude : finish the operation.
639 """
640
641
642
643
644
645 x_min = 75
646 x_size = 200
647 y_min = 560
648 y_size = 150
649
650 x_gap = 75
651 y_gap = 70
652
653
654 font=9
655
656
657 nb_line = 3
658 nb_col = 2
659
660 blob_size = 1.5
661
662 lower_scale = 5
663 second_scale ={'x_min': 40, 'x_size':150,'y_min':620,'y_size':100,
664 'x_gap':42,'y_gap':30,'font':6,'nb_line':5,'nb_col':3,
665 'blob_size':0.9}
666
667 - def __init__(self, diagramlist=None, filename='diagram.eps', \
668 model=None, amplitude=None, legend='',diagram_type=''):
669 """Define basic variable and store some global information
670 all argument are optional
671 diagramlist : are the list of object to draw. item should inherit
672 from either base_objects.Diagram or drawing_lib.FeynmanDiagram
673 filename: filename of the file to write
674 model: model associate to the diagram. In principle use only if diagram
675 inherit from base_objects.Diagram
676 amplitude: amplitude associate to the diagram. NOT USE for the moment.
677 In future you could pass the amplitude associate to the object in
678 order to adjust fermion flow in case of Majorana fermion."""
679
680
681 super(MultiEpsDiagramDrawer, self).__init__(None, filename , model, \
682 amplitude)
683 self.legend = legend
684
685 self.block_nb = 0
686 self.curr_page = 0
687 self.block_in_page = 0
688
689 self.npage = 1
690 self.diagram_type = diagram_type
691
692 diagramlist = [d for d in diagramlist if not (isinstance(d, loop_objects.LoopUVCTDiagram) or \
693 (isinstance(d, loop_objects.LoopDiagram) and d.get('type') < 0))]
694 diagramlist = base_objects.DiagramList(diagramlist)
695
696 limit = self.lower_scale * self.nb_col * self.nb_line
697 if len(diagramlist) < limit:
698 self.npage += (len(diagramlist)-1) // (self.nb_col * self.nb_line)
699 else:
700 add = (len(diagramlist) - limit -1) // \
701 (self.second_scale['nb_col'] * self.second_scale['nb_line'])
702 self.npage += self.lower_scale + add
703
704 if diagramlist:
705
706 assert(isinstance(diagramlist, base_objects.DiagramList))
707 self.diagramlist = diagramlist
708 else:
709 self.diagramlist = None
710
712 """All coordinates belongs to [0,1]. So that in order to have a visible
713 graph we need to re-scale the graph. This method distort the square in
714 a oblong. Deformation are linear."""
715
716
717 block_pos = self.block_in_page
718 line_pos = block_pos // self.nb_col
719 col_pos = block_pos % self.nb_col
720
721
722
723 x_min = self.x_min + (self.x_size + self.x_gap) * col_pos
724 x_max = self.x_min + self.x_gap * (col_pos) + self.x_size * \
725 (col_pos + 1)
726 y_min = self.y_min - (self.y_size + self.y_gap) * line_pos
727 y_max = self.y_min - self.y_gap * (line_pos) - self.y_size * \
728 (line_pos - 1)
729
730
731 x = x_min + (x_max - x_min) * x
732 y = y_min + (y_max - y_min) * y
733
734 return x, y
735
737 """Creates the representation in EPS format associate to a specific
738 diagram."""
739
740
741 super(MultiEpsDiagramDrawer, self).draw_diagram(diagram, self.block_nb)
742
743
744 self.block_nb += 1
745 self.block_in_page +=1
746
747
748 - def draw(self, diagramlist='', opt=None):
749 """Creates the representation in EPS format associate to a specific
750 diagram. 'opt' keeps track of possible option of drawing. Those option
751 are used if we need to convert diagram to Drawing Object.
752 opt is an DrawOption object containing all the possible option on how
753 draw a diagram."""
754
755 if not opt and EpsDiagramDrawer.april_fool:
756 opt = draw.DrawOption({'external':True,
757 'horizontal':True,
758 'max_size':0.4,
759 'add_gap': 2.5})
760
761 if diagramlist == '':
762 diagramlist = self.diagramlist
763
764
765
766 self.initialize()
767 self.text += '/Helvetica findfont %s scalefont setfont\n' % self.font
768 self.text += ' 50 770 moveto\n'
769 self.text += ' (%s) show\n' % self.legend
770 self.text += ' 525 770 moveto\n'
771 self.text += ' (page %s/%s) show\n' % (self.curr_page + 1, self.npage)
772 self.text += ' 260 50 moveto\n'
773 self.text += ' (Diagrams made by MadGraph5_aMC@NLO) show\n'
774
775 for i,diagram in enumerate(diagramlist):
776
777 diagram = self.convert_diagram(diagram, self.model, self.amplitude, opt)
778 if diagram==None:
779 continue
780
781 self.draw_diagram(diagram)
782
783
784 if self.block_in_page % (self.nb_col * self.nb_line) == 0:
785
786 self.pass_to_next_page()
787
788
789 self.conclude()
790
792 """Insert text in order to pass to next EPS page."""
793
794 self.curr_page += 1
795 self.block_in_page = 0
796 if self.curr_page == self.lower_scale:
797 for key, value in self.second_scale.items():
798 setattr(self, key, value)
799
800
801 self.text += 'showpage\n'
802 self.text += '%%' + 'Page: %s %s \n' % (self.curr_page+1, self.curr_page+1)
803 self.text += '%%PageBoundingBox:-20 -20 600 800\n'
804 self.text += '%%PageFonts: Helvetica\n'
805 self.text += '/Helvetica findfont %s scalefont setfont\n' % self.font
806 self.text += ' 50 770 moveto\n'
807 self.text += ' (%s) show\n' % self.legend
808 self.text += ' 525 770 moveto\n'
809 self.text += ' (page %s/%s) show\n' % (self.curr_page + 1, self.npage)
810 self.text += ' 260 40 moveto\n'
811 self.text += ' (Diagrams made by MadGraph5_aMC@NLO) show\n'
812