Package lib :: Package plotting :: Module grace
[hide private]
[frames] | no frames]

Source Code for Module lib.plotting.grace

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2003-2015 Edward d'Auvergne                                   # 
  4  # Copyright (C) 2013-2014 Troels E. Linnet                                    # 
  5  #                                                                             # 
  6  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  7  #                                                                             # 
  8  # This program is free software: you can redistribute it and/or modify        # 
  9  # it under the terms of the GNU General Public License as published by        # 
 10  # the Free Software Foundation, either version 3 of the License, or           # 
 11  # (at your option) any later version.                                         # 
 12  #                                                                             # 
 13  # This program is distributed in the hope that it will be useful,             # 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 16  # GNU General Public License for more details.                                # 
 17  #                                                                             # 
 18  # You should have received a copy of the GNU General Public License           # 
 19  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 20  #                                                                             # 
 21  ############################################################################### 
 22   
 23  # Module docstring. 
 24  """Module for interfacing with Grace (also known as Xmgrace, Xmgr, and ace).""" 
 25   
 26  # Python module imports. 
 27  from math import ceil, sqrt 
 28   
 29  # relax module imports. 
 30  from lib.errors import RelaxError 
 31   
 32   
 33  # This script is used to batch convert the Grace *.agr files into graphics files using the Grace 
 34  # program itself. 
 35   
36 -def script_grace2images(file=None):
37 """Write a python "grace to PNG/EPS/SVG..." conversion script.. 38 39 The makes a conversion script to image types as PNG/EPS/SVG. The conversion is looping over a directory list of *.agr files, and making function calls to xmgrace. Successful conversion of images depends on the compilation of xmgrace. The input is a list of image types which is wanted, f.ex: PNG EPS SVG. PNG is default. 40 41 @keyword file: The file object to write the data to. 42 @type file: file object 43 """ 44 45 # Write to file 46 file.write("#!/usr/bin/env python\n") 47 file.write("#\n") 48 file.write("# This script is used to batch convert the Grace *.agr files into graphics bitmap files using the\n") 49 file.write("# Grace program itself. Therefore you will need to install on your system xmgrace,\n") 50 file.write("# (http://plasma-gate.weizmann.ac.il/Grace/), qtgrace (http://sourceforge.net/projects/qtgrace/)\n") 51 file.write("# or gracegtk (http://sourceforge.net/projects/gracegtk/).\n") 52 file.write("\n") 53 file.write("import glob, os, sys\n") 54 file.write("import shlex, subprocess\n") 55 file.write("import optparse\n") 56 file.write("\n") 57 file.write("# Define a callback function, for a multiple input of PNG, EPS, SVG\n") 58 file.write("def foo_callback(option, opt, value, parser):\n") 59 file.write(" setattr(parser.values, option.dest, value.split(','))\n") 60 file.write("\n") 61 file.write("# Add functioning for argument parsing\n") 62 file.write("parser = optparse.OptionParser(description='Process grace files to images')\n") 63 file.write("# Add argument type. Destination instance is set to types.\n") 64 file.write("parser.add_option('-g', action='store_true', dest='relax_gui', default=False, help='Make it possible to run script through relax GUI. Run by using User-functions -> script')\n") 65 file.write("parser.add_option('-l', action='callback', callback=foo_callback, dest='l', type=\"string\", default=False, help='Make in possible to run scriptif relax has logfile turned on. Run by using User-functions -> script')\n") 66 file.write("parser.add_option('-t', action='callback', callback=foo_callback, dest='types', type=\"string\", default=[], help='List image types for conversion. Execute script with: python %s -t PNG,EPS ...'%(sys.argv[0]))\n") 67 file.write("\n") 68 file.write("# Parse the arguments to a Class instance object\n") 69 file.write("args = parser.parse_args()\n") 70 file.write("\n") 71 file.write("# Lets print help if no arguments are passed\n") 72 file.write("if len(sys.argv) == 1 or len(args[0].types) == 0:\n") 73 file.write(" print('system argument is:', sys.argv)\n") 74 file.write(" parser.print_help()\n") 75 file.write(" print('Performing a default PNG conversion')\n") 76 file.write(" # If no input arguments, we make a default PNG option\n") 77 file.write(" args[0].types = ['PNG']\n") 78 file.write("\n") 79 file.write("# If we run through the GUI we cannot pass input arguments so we make a default PNG option\n") 80 file.write("if args[0].relax_gui:\n") 81 file.write(" args[0].types = ['PNG']\n") 82 file.write("\n") 83 file.write("types = list(args[0].types)\n") 84 file.write("\n") 85 file.write("# A easy search for files with *.agr, is to use glob, which is pathnames matching a specified pattern according to the rules used by the Unix shell, not opening a shell\n") 86 file.write("gracefiles = glob.glob(\"*.agr\")\n") 87 file.write("\n") 88 file.write("# For png conversion, several parameters can be passed to xmgrace. These can be altered later afterwards and the script rerun. \n") 89 file.write("# The option for transparent is good for poster or insertion in color backgrounds. The ability for this still depends on xmgrace compilation\n") 90 file.write("if \"PNG\" in types:\n") 91 file.write(" pngpar = \"png.par\"\n") 92 file.write(" if not os.path.isfile(pngpar):\n") 93 file.write(" wpngpar = open(pngpar, \"w\")\n") 94 file.write(" wpngpar.write(\"DEVICE \\\"PNG\\\" FONT ANTIALIASING on\\n\")\n") 95 file.write(" wpngpar.write(\"DEVICE \\\"PNG\\\" OP \\\"transparent:off\\\"\\n\")\n") 96 file.write(" wpngpar.write(\"DEVICE \\\"PNG\\\" OP \\\"compression:9\\\"\\n\")\n") 97 file.write(" wpngpar.close()\n") 98 file.write("\n") 99 file.write("# Now loop over the grace files\n") 100 file.write("for grace in gracefiles:\n") 101 file.write(" # Get the filename without extension\n") 102 file.write(" fname = grace.split(\".agr\")[0]\n") 103 file.write(" if (\"PNG\" in types or \".PNG\" in types or \"png\" in types or \".png\" in types):\n") 104 file.write(" # Produce the argument string\n") 105 file.write(" im_args = r\"xmgrace -hdevice PNG -hardcopy -param %s -printfile %s.png %s\" % (pngpar, fname, grace)\n") 106 file.write(" # Split the arguments the right way to call xmgrace\n") 107 file.write(" im_args = shlex.split(im_args)\n") 108 file.write(" return_code = subprocess.call(im_args)\n") 109 file.write(" if (\"EPS\" in types or \".EPS\" in types or \"eps\" in types or \".eps\" in types):\n") 110 file.write(" im_args = r\"xmgrace -hdevice EPS -hardcopy -printfile %s.eps %s\" % (fname, grace)\n") 111 file.write(" im_args = shlex.split(im_args)\n") 112 file.write(" return_code = subprocess.call(im_args)\n") 113 file.write(" if (\"JPG\" in types or \".JPG\" in types or \"jpg\" in types or \".jpg\" in types):\n") 114 file.write(" im_args = r\"xmgrace -hdevice JPEG -hardcopy -printfile %s.jpg %s\" % (fname, grace)\n") 115 file.write(" im_args = shlex.split(im_args)\n") 116 file.write(" if (\"JPEG\" in types or \".JPEG\" in types or \"jpeg\" in types or \".jpeg\" in types):\n") 117 file.write(" im_args = r\"xmgrace -hdevice JPEG -hardcopy -printfile %s.jpg %s\" % (fname, grace)\n") 118 file.write(" im_args = shlex.split(im_args)\n") 119 file.write(" return_code = subprocess.call(im_args)\n") 120 file.write(" if (\"SVG\" in types or \".SVG\" in types or \"svg\" in types or \".svg\" in types):\n") 121 file.write(" im_args = r\"xmgrace -hdevice SVG -hardcopy -printfile %s.svg %s\" % (fname, grace)\n") 122 file.write(" im_args = shlex.split(im_args)\n") 123 file.write(" return_code = subprocess.call(im_args)\n")
124 125
126 -def write_xy_data(data, file=None, graph_type=None, norm_type='first', norm=None, autoscale=True):
127 """Write the data into the Grace xy-scatter plot. 128 129 The numerical data should be supplied as a 4 dimensional list or array object. The first dimension corresponds to the graphs, Gx. The second corresponds the sets of each graph, Sx. The third corresponds to the data series (i.e. each data point). The forth is a list of the information about each point, it is a list where the first element is the x value, the second is the y value, the third is the optional dx or dy error (either dx or dy dependent upon the graph_type arg), and the forth is the optional dy error when graph_type is xydxdy (the third position is then dx). 130 131 132 @param data: The 4D structure of numerical data to graph (see docstring). 133 @type data: list of lists of lists of float 134 @keyword file: The file object to write the data to. 135 @type file: file object 136 @keyword graph_type: The graph type which can be one of xy, xydy, xydx, or xydxdy. 137 @type graph_type: str 138 @keyword norm_type: The point to normalise to 1. This can be 'first' or 'last'. 139 @type norm_type: str 140 @keyword norm: The normalisation flag which if set to True will cause all graphs to be normalised to 1. The first dimension is the graph. 141 @type norm: None or list of bool 142 @keyword autoscale: A flag which if True will cause the world view of each graph to be autoscaled (by placing the Grace command "@autoscale" at the end of the file). If you have supplied a world view for the header or the tick spacing, this argument should be set to False to prevent that world view from being overwritten. 143 @type autoscale: bool 144 """ 145 146 # Graph number. 147 graph_num = len(data) 148 149 # Defaults. 150 if not norm: 151 norm = [] 152 for gi in range(graph_num): 153 norm.append(False) 154 155 # Comment columns. 156 comment_col = 2 157 if graph_type in ['xydx', 'xydy']: 158 comment_col = 3 159 elif graph_type == 'xydxdy': 160 comment_col = 4 161 162 # Loop over the graphs. 163 for gi in range(graph_num): 164 # Loop over the data sets of the graph. 165 for si in range(len(data[gi])): 166 # The target and type. 167 file.write("@target G%s.S%s\n" % (gi, si)) 168 file.write("@type %s\n" % graph_type) 169 170 # Normalisation (to the first data point y value!). 171 norm_fact = 1.0 172 if norm[gi]: 173 if norm_type == 'first': 174 norm_fact = data[gi][si][0][1] 175 elif norm_type == 'last': 176 norm_fact = data[gi][si][-1][1] 177 else: 178 raise RelaxError("The normalisation type '%s' must be one of ['first', 'last']." % norm_fact) 179 180 # Loop over the data points. 181 for point in data[gi][si]: 182 # Bad data. 183 if point[0] == None or point[1] == None: 184 continue 185 186 # X and Y data. 187 file.write("%30.15f %30.15f" % (point[0], point[1]/norm_fact)) 188 189 # The dx and dy errors. 190 if graph_type in ['xydx', 'xydy', 'xydxdy']: 191 # Catch if graph is a mix with and without error. 192 if len(point) < 3: 193 error = None 194 else: 195 error = point[2] 196 # Catch x or y-axis errors of None. 197 if error == None: 198 error = 0.0 199 200 # Write the error. 201 file.write(" %30.15f" % (error/norm_fact)) 202 203 # The dy errors of xydxdy. 204 if graph_type == 'xydxdy': 205 # Catch y-axis errors of None. 206 error = point[3] 207 if error == None: 208 error = 0.0 209 210 # Write the error. 211 file.write(" %30.15f" % (error/norm_fact)) 212 213 # The comment if given. 214 try: 215 file.write(" \"%s\"" % point[comment_col]) 216 except IndexError: 217 pass 218 219 # End the point. 220 file.write("\n") 221 222 # End of the data set i. 223 file.write("&\n") 224 225 # Autoscaling of all graphs to avoid user confusion. 226 if autoscale: 227 for i in range(graph_num): 228 file.write("@with g%i\n" % i) 229 file.write("@autoscale\n") 230 231 # Auto-arrange the graphs if multiple are present. 232 if len(data) > 1: 233 row_num = int(round(sqrt(len(data)))) 234 col_num = int(ceil(sqrt(len(data)))) 235 file.write("@arrange(%i, %i, .1, .1, .1, OFF, OFF, OFF)\n" % (row_num, col_num))
236 237
238 -def write_xy_header(file=None, paper_size='A4', title=None, subtitle=None, world=None, view=None, graph_num=1, sets=None, set_names=None, set_colours=None, x_axis_type_zero=None, y_axis_type_zero=None, symbols=None, symbol_sizes=None, symbol_fill=None, linestyle=None, linetype=None, linewidth=None, data_type=None, seq_type=None, axis_labels=None, tick_major_spacing=None, tick_minor_count=None, legend=None, legend_pos=None, legend_box_fill_pattern=None, legend_char_size=None, norm=None):
239 """Write the grace header for xy-scatter plots. 240 241 Many of these keyword arguments should be supplied in a [X, Y] list format, where the first element corresponds to the X data, and the second the Y data. Defaults will be used for any non-supplied args (or lists with elements set to None). 242 243 244 @keyword file: The file object to write the data to. 245 @type file: file object 246 @keyword paper_size: The paper size, i.e. 'A4'. If set to None, this will default to letter size. 247 @type paper_size: str 248 @keyword title: The title of the graph. 249 @type title: None or str 250 @keyword subtitle: The sub-title of the graph. 251 @type subtitle: None or str 252 @keyword world: The Grace plot default zoom. This consists of a list of the X-axis minimum, Y-axis minimum, X-axis maximum, and Y-axis maximum values. Each graph should supply its own world view. 253 @type world: Nor or list of list of numbers 254 @keyword view: List of 4 coordinates defining the graph view port. 255 @type view: None or list of float 256 @keyword graph_num: The total number of graphs. 257 @type graph_num: int 258 @keyword sets: The number of data sets in each graph. 259 @type sets: list of int 260 @keyword set_names: The names associated with each graph data set Gx.Sy. For example this can be a list of spin identification strings. The first dimension is the graph, the second is the set. 261 @type set_names: None or list of list of str 262 @keyword set_colours: The colours for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 263 @type set_colours: None or list of list of int 264 @keyword x_axis_type_zero: The flags specifying if the X-axis should be placed at zero. 265 @type x_axis_type_zero: None or list of lists of bool 266 @keyword y_axis_type_zero: The flags specifying if the Y-axis should be placed at zero. 267 @type y_axis_type_zero: None or list of lists of bool 268 @keyword symbols: The symbol style for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 269 @type symbols: None or list of list of int 270 @keyword symbol_sizes: The symbol size for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 271 @type symbol_sizes: None or list of list of int 272 @keyword symbol_fill: The symbol file style for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 273 @type symbol_fill: None or list of list of int 274 @keyword linestyle: The line style for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 275 @type linestyle: None or list of list of int 276 @keyword linetype: The line type for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 277 @type linetype: None or list of list of int 278 @keyword linewidth: The line width for all elements of each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 279 @type linewidth: None or list of float 280 @keyword data_type: The axis data category (in the [X, Y] list format). 281 @type data_type: None or list of list of str 282 @keyword seq_type: The sequence data type (in the [X, Y] list format). This is for molecular sequence specific data and can be one of 'res', 'spin', or 'mixed'. 283 @type seq_type: None or list of list of str 284 @keyword tick_major_spacing: The spacing between major ticks. This is in the [X, Y] list format whereby the first dimension corresponds to the graph number. 285 @type tick_major_spacing: None or list of list of numbers 286 @keyword tick_minor_count: The number of minor ticks between the major ticks. This is in the [X, Y] list format whereby the first dimension corresponds to the graph number. 287 @type tick_minor_count: None or list of list of int 288 @keyword axis_labels: The labels for the axes (in the [X, Y] list format). The first dimension is the graph. 289 @type axis_labels: None or list of list of str 290 @keyword legend: If True, the legend will be visible. The first dimension is the graph. 291 @type legend: list of bool 292 @keyword legend_pos: The position of the legend, e.g. [0.3, 0.8]. The first dimension is the graph. 293 @type legend_pos: None or list of list of float 294 @keyword legend_box_fill_pattern: The legend box fill. If set to 0, it will become transparent. 295 @type legend_box_fill_pattern: int 296 @keyword legend_char_size: The size of the legend box text. 297 @type legend_char_size: float 298 @keyword norm: The normalisation flag which if set to True will cause all graphs to be normalised to 1. The first dimension is the graph. 299 @type norm: list of bool 300 """ 301 302 # Defaults. 303 if sets == None: 304 sets = [] 305 for gi in range(graph_num): 306 sets.append(1) 307 if x_axis_type_zero == None: 308 x_axis_type_zero = [] 309 for gi in range(graph_num): 310 x_axis_type_zero.append(False) 311 if y_axis_type_zero == None: 312 y_axis_type_zero = [] 313 for gi in range(graph_num): 314 y_axis_type_zero.append(False) 315 if linewidth == None: 316 linewidth = [] 317 for gi in range(graph_num): 318 linewidth.append(0.5) 319 if norm == None: 320 norm = [] 321 for gi in range(graph_num): 322 norm.append(False) 323 if legend == None: 324 legend = [] 325 for gi in range(graph_num): 326 legend.append(True) 327 if not legend_box_fill_pattern: 328 legend_box_fill_pattern = [] 329 for gi in range(graph_num): 330 legend_box_fill_pattern.append(1) 331 if not legend_char_size: 332 legend_char_size = [] 333 for gi in range(graph_num): 334 legend_char_size.append(1.0) 335 336 # Set the None args to lists as needed. 337 if not data_type: 338 data_type = [None, None] 339 if not seq_type: 340 seq_type = [None, None] 341 if not axis_labels: 342 axis_labels = [] 343 for gi in range(graph_num): 344 axis_labels.append([None, None]) 345 346 # Set the Grace version number of the header's formatting for compatibility. 347 file.write("@version 50121\n") 348 349 # The paper size. 350 if paper_size == 'A4': 351 file.write("@page size 842, 595\n") 352 353 # Loop over each graph. 354 for gi in range(graph_num): 355 # Graph Gi. 356 file.write("@with g%i\n" % gi) 357 358 # The world view. 359 if world: 360 file.write("@ world %s, %s, %s, %s\n" % (world[gi][0], world[gi][1], world[gi][2], world[gi][3])) 361 362 # The view port. 363 if not view: 364 view = [0.15, 0.15, 1.28, 0.85] 365 file.write("@ view %s, %s, %s, %s\n" % (view[0], view[1], view[2], view[3])) 366 367 # The title and subtitle. 368 if title: 369 file.write("@ title \"%s\"\n" % title) 370 if subtitle: 371 file.write("@ subtitle \"%s\"\n" % subtitle) 372 373 # Axis at zero. 374 if x_axis_type_zero[gi]: 375 file.write("@ xaxis type zero true\n") 376 if y_axis_type_zero[gi]: 377 file.write("@ yaxis type zero true\n") 378 379 # Axis specific settings. 380 axes = ['x', 'y'] 381 for i in range(2): 382 # Some axis default values for spin data. 383 if data_type[i] == 'spin': 384 # Residue only data. 385 if seq_type[i] == 'res': 386 # X-axis label. 387 if not axis_labels[gi][i]: 388 axis_labels[gi][i] = "Residue number" 389 390 # Spin only data. 391 if seq_type[i] == 'spin': 392 # X-axis label. 393 if not axis_labels[gi][i]: 394 axis_labels[gi][i] = "Spin number" 395 396 # Mixed data. 397 if seq_type[i] == 'mixed': 398 # X-axis label. 399 if not axis_labels[gi][i]: 400 axis_labels[gi][i] = "Spin ID string" 401 402 # Write out the data. 403 if axis_labels[gi][i]: 404 file.write("@ %saxis label \"%s\"\n" % (axes[i], axis_labels[gi][i])) 405 file.write("@ %saxis label char size 1.00\n" % axes[i]) 406 if tick_major_spacing != None: 407 file.write("@ %saxis tick major %s\n" % (axes[i], tick_major_spacing[gi][i])) 408 file.write("@ %saxis tick major size 0.50\n" % axes[i]) 409 file.write("@ %saxis tick major linewidth %s\n" % (axes[i], linewidth[gi])) 410 if tick_minor_count != None: 411 file.write("@ %saxis tick minor ticks %s\n" % (axes[i], tick_minor_count[gi][i])) 412 file.write("@ %saxis tick minor linewidth %s\n" % (axes[i], linewidth[gi])) 413 file.write("@ %saxis tick minor size 0.25\n" % axes[i]) 414 file.write("@ %saxis ticklabel char size 0.70\n" % axes[i]) 415 416 # Legend box. 417 if legend != None and legend[gi]: 418 file.write("@ legend on\n") 419 else: 420 file.write("@ legend off\n") 421 if legend_pos != None: 422 file.write("@ legend %s, %s\n" % (legend_pos[gi][0], legend_pos[gi][1])) 423 file.write("@ legend box fill pattern %s\n" % legend_box_fill_pattern[gi]) 424 file.write("@ legend char size %s\n" % legend_char_size[gi]) 425 426 # Frame. 427 file.write("@ frame linewidth %s\n" % linewidth[gi]) 428 429 # Loop over each graph set. 430 for i in range(sets[gi]): 431 # Symbol style (default to all different symbols). 432 if symbols: 433 file.write("@ s%i symbol %i\n" % (i, symbols[gi][i])) 434 else: 435 # The symbol number (cycle between 1 and 10). 436 num = i % 10 + 1 437 438 # Write out. 439 file.write("@ s%i symbol %i\n" % (i, num)) 440 441 # Symbol sizes (default to a small size). 442 if symbol_sizes: 443 file.write("@ s%i symbol size %s\n" % (i, symbol_sizes[gi][i])) 444 else: 445 file.write("@ s%i symbol size 0.45\n" % i) 446 447 # The symbol fill. 448 if symbol_fill: 449 file.write("@ s%i symbol fill pattern %i\n" % (i, symbol_fill[gi][i])) 450 451 # The symbol line width. 452 file.write("@ s%i symbol linewidth %s\n" % (i, linewidth[gi])) 453 454 # Symbol colour (default to nothing). 455 if set_colours: 456 file.write("@ s%i symbol color %s\n" % (i, set_colours[gi][i])) 457 file.write("@ s%i symbol fill color %s\n" % (i, set_colours[gi][i])) 458 459 # Error bars. 460 file.write("@ s%i errorbar size 0.5\n" % i) 461 file.write("@ s%i errorbar linewidth %s\n" % (i, linewidth[gi])) 462 file.write("@ s%i errorbar riser linewidth %s\n" % (i, linewidth[gi])) 463 464 # Line linestyle (default to nothing). 465 if linestyle: 466 file.write("@ s%i line linestyle %s\n" % (i, linestyle[gi][i])) 467 468 # Line linetype (default to nothing). 469 if linetype: 470 file.write("@ s%i line type %s\n" % (i, linetype[gi][i])) 471 472 # Line and all other colours (default to nothing). 473 if set_colours: 474 file.write("@ s%i line color %s\n" % (i, set_colours[gi][i])) 475 file.write("@ s%i fill color %s\n" % (i, set_colours[gi][i])) 476 file.write("@ s%i avalue color %s\n" % (i, set_colours[gi][i])) 477 file.write("@ s%i errorbar color %s\n" % (i, set_colours[gi][i])) 478 479 # Legend. 480 if set_names and len(set_names) and len(set_names[gi]) and set_names[gi][i]: 481 file.write("@ s%i legend \"%s\"\n" % (i, set_names[gi][i]))
482