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