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