Package madgraph :: Package iolibs :: Module drawing_eps
[hide private]
[frames] | no frames]

Source Code for Module madgraph.iolibs.drawing_eps

  1  ################################################################################ 
  2  # 
  3  # Copyright (c) 2009 The MadGraph Development team and Contributors 
  4  # 
  5  # This file is a part of the MadGraph 5 project, an application which  
  6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
  7  # high-energy processes in the Standard Model and beyond. 
  8  # 
  9  # It is subject to the MadGraph license which should accompany this  
 10  # distribution. 
 11  # 
 12  # For more information, please visit: http://madgraph.phys.ucl.ac.be 
 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   
 47  _file_path = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] + '/' 
 48   
 49  #=============================================================================== 
 50  # DrawDiagramEps 
 51  #=============================================================================== 
52 -class EpsDiagramDrawer(draw.DiagramDrawer):
53 """Class to write a EPS file containing the asked diagram 54 This class follows the DrawDiagram Frameworks. 55 56 The main routine to draw a diagram is 'draw' which call 57 1) initialize: setup things for the diagram (usually open a file) 58 2) convert_diagram : Update the diagram in the correct format if needed 59 3) draw_diagram : Perform diagram dependent operation 60 4) conclude : finish the operation. 61 """ 62 63 #some page information 64 width = 450 65 height = 450 66 npage = 1 67 68 # Define where to put the diagram in the page. This is the coordinate of 69 #the two opposites point of the drawing area 70 x_min = 150 71 y_min = 450 72 x_max = 450 73 y_max = 750 74 75 blob_size = 1.5 76
77 - def initialize(self):
78 """Operation done before starting to create diagram specific EPS content 79 First open the file in write mode then write in it the header and the 80 library of particle type.""" 81 82 # Open file 83 super(EpsDiagramDrawer, self).initialize() 84 85 # File Header 86 text = "%!PS-Adobe-2.0\n" 87 text += "%%" + "BoundingBox: -20 -20 %s %s \n" % \ 88 (self.width, self.height) 89 text += "%%DocumentFonts: Helvetica\n" 90 text += "%%" + "Pages: %s \n" % self.npage 91 self.file.writelines(text) 92 93 # Import the definition of the different way to represent a line 94 self.file.writelines(open(os.path.join(_file_path, \ 95 'iolibs/template_files/drawing_eps_header.inc')).read())
96 97
98 - def conclude(self):
99 """Operation to perform when all code related to a specific diagram are 100 finish. Operation : 101 - Add the 'end of page' code 102 - write unwritten text and close the file. [DrawDiagram.conclude]""" 103 104 # Add an 'end of page statement' 105 self.text = 'showpage\n' 106 self.text += '%%trailer\n' 107 108 #write the diagram file 109 super(EpsDiagramDrawer, self).conclude()
110 111
112 - def rescale(self, x, y):
113 """All coordinates belongs to [0,1]. So that in order to have a visible 114 graph we need to re-scale the graph. This method distort the square in 115 a oblong. Deformation are linear.""" 116 117 # Use the information for the graph position. 'self.x_???,self.y_???? 118 #are the coordinate of the two opposites point of the drawing area. 119 x = self.x_min + (self.x_max - self.x_min) * x 120 y = self.y_min + (self.y_max - self.y_min) * y 121 122 return x, y
123 124
125 - def line_format(self, x1, y1, x2, y2, name):
126 """Specify the text format of a specific Particles. 127 EPS format for Particle is either [X Y X Y NAME] or [X Y X Y NUM NAME]. 128 In this routine we will consider only the first format. The second can 129 be matched by redefining name in [NUM NAME].""" 130 131 # Compute real position for starting/ending point 132 x1, y1 = self.rescale(x1, y1) 133 x2, y2 = self.rescale(x2, y2) 134 135 #return the line in correct format 136 return " %s %s %s %s %s \n" % (x1, y1, x2, y2, name)
137
138 - def draw_vertex(self, vertex, bypass = ['QED','QCD'] ):
139 """Add blob in case on non QED-QCD information""" 140 141 interaction = self.model.get_interaction(vertex.id) 142 if interaction: 143 order = interaction.get('orders') 144 order = [key for key in order.keys() if order[key] and \ 145 key not in bypass] 146 147 if order: 148 x1, y1 = self.rescale(vertex.pos_x, vertex.pos_y) 149 self.text += " %s %s %s 1.0 Fblob \n" % (x1, y1, self.blob_size)
150 151 152
153 - def draw_straight(self, line):
154 """ADD the EPS code for this fermion line.""" 155 156 #add the code in the correct format 157 self.text += self.line_format(line.start.pos_x, line.start.pos_y, 158 line.end.pos_x, line.end.pos_y, 'Ffermion')
159 160
161 - def draw_dashed(self, line):
162 """ADD the EPS code for this Higgs line.""" 163 164 #add the code in the correct format 165 self.text += self.line_format(line.start.pos_x, line.start.pos_y, 166 line.end.pos_x, line.end.pos_y, 'Fhiggs')
167 168
169 - def draw_wavy(self, line, opt=0, type='d'):
170 """ADD the EPS code for this photon line.""" 171 172 #add the code in the correct format 173 self.text += self.line_format(line.start.pos_x, line.start.pos_y, 174 line.end.pos_x, line.end.pos_y, '%d Fphoton%s' % (opt,type))
175 176
177 - def draw_curly(self, line, type=''):
178 """ADD the EPS code for this gluon line.""" 179 180 # Due to the asymmetry in the way to draw the gluon (everything is draw 181 #upper or below the line joining the points). We have to put conditions 182 #in order to have nice diagram. 183 if (line.start.pos_x < line.end.pos_x) or \ 184 (line.start.pos_x == line.end.pos_x and \ 185 line.start.pos_y > line.end.pos_y): 186 self.text += self.line_format(line.start.pos_x, 187 line.start.pos_y, line.end.pos_x, 188 line.end.pos_y, '0 Fgluon%s' % type) 189 else: 190 self.text += self.line_format(line.end.pos_x, 191 line.end.pos_y, line.start.pos_x, 192 line.start.pos_y, '0 Fgluon%s' % type)
193
194 - def draw_scurly(self, line):
195 """ADD the EPS code for this gluino line.""" 196 self.draw_curly(line, type='r' 197 ) 198 self.draw_straight(line)
199
200 - def draw_swavy(self, line):
201 """ADD the EPS code for this neutralino line.""" 202 self.draw_wavy(line, type='r') 203 self.draw_straight(line)
204
205 - def draw_double(self, line, type='r'):
206 """ADD the EPS code for this neutralino line.""" 207 208 209 length = math.sqrt((line.end.pos_y - line.start.pos_y)**2 + (line.end.pos_x - line.start.pos_x) **2) 210 c1 = (line.end.pos_x - line.start.pos_x)/length 211 c2 = (line.end.pos_y - line.start.pos_y)/length 212 213 gap = 0.013 214 start2_x = line.start.pos_x + gap * c1 215 start2_y = line.start.pos_y + gap * c2 216 stop1_x = line.end.pos_x - gap * c1 217 stop1_y = line.end.pos_y - gap * c2 218 219 220 self.text += self.line_format(line.start.pos_x, line.start.pos_y, 221 stop1_x, stop1_y, '0 Fphoton%s' % (type)) 222 #add the code in the correct format 223 self.text += self.line_format(start2_x, start2_y, 224 line.end.pos_x, line.end.pos_y, '0 Fphoton%s' % (type))
225 226
227 - def put_diagram_number(self, number=0):
228 """ADD the comment 'diagram [number]' just below the diagram.""" 229 230 # Position of the text in [0,1] square 231 x = 0.2 232 y = -0.17 233 # Compute the EPS coordinate 234 x, y = self.rescale(x, y) 235 #write the text 236 self.text += ' %s %s moveto \n' % (x, y) 237 self.text += '( diagram %s ) show\n' % (number + 1) # +1 python 238 #starts to count at 239 #zero. 240 241 mystr = " (%s)" % ", ".join(["%s=%d" % (key, self.diagram.diagram['orders'][key]) \ 242 for key in sorted(self.diagram.diagram['orders'].keys()) \ 243 if key != 'WEIGHTED']) 244 245 x = 0.6 246 y = -0.17 247 x, y = self.rescale(x, y) 248 #write the text 249 self.text += ' %s %s moveto \n' % (x, y) 250 self.text += '%s show\n' % (mystr)
251 252 253
254 - def associate_number(self, line, number):
255 """Write in the EPS figure the MadGraph number associate to the line. 256 Note that this routine is called only for external particle.""" 257 258 # find the external vertex associate to the line 259 if line.start.is_external(): 260 vertex = line.start 261 else: 262 vertex = line.end 263 264 # find the position of this vertex 265 x = vertex.pos_x 266 y = vertex.pos_y 267 268 # Move slightly the position to avoid overlapping 269 if x == 0: 270 x = -0.04 271 else: 272 x += 0.04 273 y = line._has_ordinate(x) 274 275 # Re-scale x,y in order to pass in EPS coordinate 276 x, y = self.rescale(x, y) 277 # Write the EPS text associate 278 self.text += ' %s %s moveto \n' % (x, y) 279 self.text += '(%s) show\n' % (number)
280
281 - def associate_name(self, line, name):
282 """ADD the EPS code associate to the name of the particle. Place it near 283 to the center of the line. 284 """ 285 286 # Put alias for vertex positions 287 x1, y1 = line.start.pos_x, line.start.pos_y 288 x2, y2 = line.end.pos_x, line.end.pos_y 289 290 d = line.get_length() 291 if d == 0: 292 raise self.DrawDiagramError('Line can not have 0 length') 293 294 295 # compute gap from middle point 296 if abs(x1 - x2) < 1e-3: 297 dx = 0.015 298 dy = -0.01 299 elif abs(y1 - y2) < 1e-3: 300 dx = -0.01 301 dy = 0.025 302 elif ((x1 < x2) == (y1 < y2)): 303 dx = -0.03 * len(name) 304 dy = 0.02 * len(name) #d * 0.12 305 else: 306 dx = 0.01 #0.05 307 dy = 0.02 #d * 0.12 308 309 # Assign position 310 x_pos = (x1 + x2) / 2 + dx 311 y_pos = (y1 + y2) / 2 + dy 312 313 # Pass in EPS coordinate 314 x_pos, y_pos = self.rescale(x_pos, y_pos) 315 #write EPS code 316 self.text += ' %s %s moveto \n' % (x_pos, y_pos) 317 self.text += '(' + name + ') show\n'
318 319 320 #=============================================================================== 321 # DrawDiagramsEps 322 #===============================================================================
323 -class MultiEpsDiagramDrawer(EpsDiagramDrawer):
324 """Class to write a EPS file containing the asked set of diagram 325 This class follows the DrawDiagram Frameworks. 326 327 The main routine to draw a diagram is 'draw' which call 328 1) initialize: setup things for the diagram (usually open a file) 329 2) convert_diagram : Update the diagram in the correct format if needed 330 3) draw_diagram : Perform diagram dependent operation 331 4) conclude : finish the operation. 332 """ 333 334 # Define where to put the diagrams in the page. This is the coordinate of 335 #the lower left corner of the drawing area of the first graph. and the 336 #dimension associate to this drawing area. 337 338 x_min = 75 339 x_size = 200 340 y_min = 560 341 y_size = 150 342 # Define distances between two drawing area 343 x_gap = 75 344 y_gap = 70 345 346 #define font 347 font=9 348 349 #Defines the number of line-column in a EPS page 350 nb_line = 3 351 nb_col = 2 352 353 blob_size = 1.5 354 355 lower_scale = 5 356 second_scale ={'x_min': 40, 'x_size':150,'y_min':620,'y_size':100, 357 'x_gap':42,'y_gap':30,'font':6,'nb_line':5,'nb_col':3, 358 'blob_size':0.9} 359
360 - def __init__(self, diagramlist=None, filename='diagram.eps', \ 361 model=None, amplitude=None, legend=''):
362 """Define basic variable and store some global information 363 all argument are optional 364 diagramlist : are the list of object to draw. item should inherit 365 from either base_objects.Diagram or drawing_lib.FeynmanDiagram 366 filename: filename of the file to write 367 model: model associate to the diagram. In principle use only if diagram 368 inherit from base_objects.Diagram 369 amplitude: amplitude associate to the diagram. NOT USE for the moment. 370 In future you could pass the amplitude associate to the object in 371 order to adjust fermion flow in case of Majorana fermion.""" 372 373 #use standard initialization but without any diagram 374 super(MultiEpsDiagramDrawer, self).__init__(None, filename , model, \ 375 amplitude) 376 self.legend = legend 377 #additional information 378 self.block_nb = 0 # keep track of the number of diagram already written 379 self.curr_page = 0 # keep track of the page position 380 self.block_in_page = 0 #ckeep track of the block in a page 381 #compute the number of pages 382 self.npage = 1 383 384 limit = self.lower_scale * self.nb_col * self.nb_line 385 if len(diagramlist) < limit: 386 self.npage += len(diagramlist) // (self.nb_col * self.nb_line) 387 else: 388 add = (len(diagramlist) - self.lower_scale) // \ 389 (self.second_scale['nb_col'] * self.second_scale['nb_line']) 390 self.npage += self.lower_scale + add -2 391 if diagramlist: 392 # diagramlist Argument should be a DiagramList object 393 assert(isinstance(diagramlist, base_objects.DiagramList)) 394 self.diagramlist = diagramlist 395 else: 396 self.diagramlist = None
397 398
399 - def rescale(self, x, y):
400 """All coordinates belongs to [0,1]. So that in order to have a visible 401 graph we need to re-scale the graph. This method distort the square in 402 a oblong. Deformation are linear.""" 403 404 # Compute the current line and column 405 block_pos = self.block_in_page 406 line_pos = block_pos // self.nb_col 407 col_pos = block_pos % self.nb_col 408 409 # Compute the coordinate of the drawing area associate to this line 410 #and column. 411 x_min = self.x_min + (self.x_size + self.x_gap) * col_pos 412 x_max = self.x_min + self.x_gap * (col_pos) + self.x_size * \ 413 (col_pos + 1) 414 y_min = self.y_min - (self.y_size + self.y_gap) * line_pos 415 y_max = self.y_min - self.y_gap * (line_pos) - self.y_size * \ 416 (line_pos - 1) 417 418 # Re-scale the coordinate in that box 419 x = x_min + (x_max - x_min) * x 420 y = y_min + (y_max - y_min) * y 421 422 return x, y
423
424 - def draw_diagram(self, diagram):
425 """Creates the representation in EPS format associate to a specific 426 diagram.""" 427 428 # Standard method 429 super(MultiEpsDiagramDrawer, self).draw_diagram(diagram, self.block_nb) 430 # But keep track how many diagrams are already drawn 431 432 self.block_nb += 1 433 self.block_in_page +=1
434 435
436 - def draw(self, diagramlist='', opt=None):
437 """Creates the representation in EPS format associate to a specific 438 diagram. 'opt' keeps track of possible option of drawing. Those option 439 are used if we need to convert diagram to Drawing Object. 440 opt is an DrawOption object containing all the possible option on how 441 draw a diagram.""" 442 443 if diagramlist == '': 444 diagramlist = self.diagramlist 445 446 # Initialize some variable before starting to draw the diagram 447 # This creates the header-library of the output file 448 self.initialize() 449 self.text += '/Helvetica findfont %s scalefont setfont\n' % self.font 450 self.text += ' 50 770 moveto\n' 451 self.text += ' (%s) show\n' % self.legend 452 self.text += ' 525 770 moveto\n' 453 self.text += ' (page %s/%s) show\n' % (self.curr_page + 1, self.npage) 454 self.text += ' 260 50 moveto\n' 455 self.text += ' (Diagrams made by MadGraph5) show\n' 456 # Loop on all diagram 457 for diagram in diagramlist: 458 # Check if they need to be convert in correct format 459 diagram = self.convert_diagram(diagram, self.model, self.amplitude, opt) 460 # Write the code associate to this diagram 461 self.draw_diagram(diagram) 462 463 # Check if the current page is full or not 464 if self.block_in_page % (self.nb_col * self.nb_line) == 0: 465 #if full initialize a new page 466 self.pass_to_next_page() 467 468 #finish operation 469 self.conclude()
470
471 - def pass_to_next_page(self):
472 """Insert text in order to pass to next EPS page.""" 473 474 self.curr_page += 1 475 self.block_in_page = 0 476 if self.curr_page == self.lower_scale: 477 for key, value in self.second_scale.items(): 478 setattr(self, key, value) 479 480 481 self.text += 'showpage\n' 482 self.text += '%%' + 'Page: %s %s \n' % (self.curr_page, self.curr_page) 483 self.text += '%%PageBoundingBox:-20 -20 600 800\n' 484 self.text += '%%PageFonts: Helvetica\n' 485 self.text += ' 50 770 moveto\n' 486 self.text += ' (%s) show\n' % self.legend 487 self.text += ' 525 770 moveto\n' 488 self.text += ' (page %s/%s) show\n' % (self.curr_page + 1, self.npage) 489 self.text += ' 260 40 moveto\n' 490 self.text += ' (Diagrams made by MadGraph5) show\n'
491