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

Source Code for Module lib.plotting.grace

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2003-2016 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 # Catch missing data sets. 171 if len(data[gi][si]) == 0: 172 file.write("&\n") 173 continue 174 175 # Normalisation (to the first data point y value!). 176 norm_fact = 1.0 177 if norm[gi]: 178 if norm_type == 'first': 179 norm_fact = data[gi][si][0][1] 180 elif norm_type == 'last': 181 norm_fact = data[gi][si][-1][1] 182 else: 183 raise RelaxError("The normalisation type '%s' must be one of ['first', 'last']." % norm_fact) 184 185 # Loop over the data points. 186 for point in data[gi][si]: 187 # Bad data. 188 if point[0] == None or point[1] == None: 189 continue 190 191 # X and Y data. 192 file.write("%30.15f %30.15f" % (point[0], point[1]/norm_fact)) 193 194 # The dx and dy errors. 195 if graph_type in ['xydx', 'xydy', 'xydxdy']: 196 # Catch if graph is a mix with and without error. 197 if len(point) < 3: 198 error = None 199 else: 200 error = point[2] 201 # Catch x or y-axis errors of None. 202 if error == None: 203 error = 0.0 204 205 # Write the error. 206 file.write(" %30.15f" % (error/norm_fact)) 207 208 # The dy errors of xydxdy. 209 if graph_type == 'xydxdy': 210 # Catch y-axis errors of None. 211 error = point[3] 212 if error == None: 213 error = 0.0 214 215 # Write the error. 216 file.write(" %30.15f" % (error/norm_fact)) 217 218 # The comment if given. 219 try: 220 file.write(" \"%s\"" % point[comment_col]) 221 except IndexError: 222 pass 223 224 # End the point. 225 file.write("\n") 226 227 # End of the data set i. 228 file.write("&\n") 229 230 # Autoscaling of all graphs to avoid user confusion. 231 if autoscale: 232 for i in range(graph_num): 233 file.write("@with g%i\n" % i) 234 file.write("@autoscale\n") 235 236 # Auto-arrange the graphs if multiple are present. 237 if len(data) > 1: 238 row_num = int(round(sqrt(len(data)))) 239 col_num = int(ceil(sqrt(len(data)))) 240 file.write("@arrange(%i, %i, .1, .1, .1, OFF, OFF, OFF)\n" % (row_num, col_num))
241 242
243 -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):
244 """Write the grace header for xy-scatter plots. 245 246 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). 247 248 249 @keyword file: The file object to write the data to. 250 @type file: file object 251 @keyword paper_size: The paper size, i.e. 'A4'. If set to None, this will default to letter size. 252 @type paper_size: str 253 @keyword title: The title of the graph. 254 @type title: None or str 255 @keyword subtitle: The sub-title of the graph. 256 @type subtitle: None or str 257 @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. 258 @type world: Nor or list of list of numbers 259 @keyword view: List of 4 coordinates defining the graph view port. 260 @type view: None or list of float 261 @keyword graph_num: The total number of graphs. 262 @type graph_num: int 263 @keyword sets: The number of data sets in each graph. 264 @type sets: list of int 265 @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. 266 @type set_names: None or list of list of str 267 @keyword set_colours: The colours for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 268 @type set_colours: None or list of list of int 269 @keyword x_axis_type_zero: The flags specifying if the X-axis should be placed at zero. 270 @type x_axis_type_zero: None or list of lists of bool 271 @keyword y_axis_type_zero: The flags specifying if the Y-axis should be placed at zero. 272 @type y_axis_type_zero: None or list of lists of bool 273 @keyword symbols: The symbol style for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 274 @type symbols: None or list of list of int 275 @keyword symbol_sizes: The symbol size for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 276 @type symbol_sizes: None or list of list of int 277 @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. 278 @type symbol_fill: None or list of list of int 279 @keyword linestyle: The line style for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 280 @type linestyle: None or list of list of int 281 @keyword linetype: The line type for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 282 @type linetype: None or list of list of int 283 @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. 284 @type linewidth: None or list of float 285 @keyword data_type: The axis data category (in the [X, Y] list format). 286 @type data_type: None or list of list of str 287 @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'. 288 @type seq_type: None or list of list of str 289 @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. 290 @type tick_major_spacing: None or list of list of numbers 291 @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. 292 @type tick_minor_count: None or list of list of int 293 @keyword axis_labels: The labels for the axes (in the [X, Y] list format). The first dimension is the graph. 294 @type axis_labels: None or list of list of str 295 @keyword legend: If True, the legend will be visible. The first dimension is the graph. 296 @type legend: list of bool 297 @keyword legend_pos: The position of the legend, e.g. [0.3, 0.8]. The first dimension is the graph. 298 @type legend_pos: None or list of list of float 299 @keyword legend_box_fill_pattern: The legend box fill. If set to 0, it will become transparent. 300 @type legend_box_fill_pattern: int 301 @keyword legend_char_size: The size of the legend box text. 302 @type legend_char_size: float 303 @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. 304 @type norm: list of bool 305 """ 306 307 # Defaults. 308 if sets == None: 309 sets = [] 310 for gi in range(graph_num): 311 sets.append(1) 312 if x_axis_type_zero == None: 313 x_axis_type_zero = [] 314 for gi in range(graph_num): 315 x_axis_type_zero.append(False) 316 if y_axis_type_zero == None: 317 y_axis_type_zero = [] 318 for gi in range(graph_num): 319 y_axis_type_zero.append(False) 320 if linewidth == None: 321 linewidth = [] 322 for gi in range(graph_num): 323 linewidth.append(0.5) 324 if norm == None: 325 norm = [] 326 for gi in range(graph_num): 327 norm.append(False) 328 if legend == None: 329 legend = [] 330 for gi in range(graph_num): 331 legend.append(True) 332 if not legend_box_fill_pattern: 333 legend_box_fill_pattern = [] 334 for gi in range(graph_num): 335 legend_box_fill_pattern.append(1) 336 if not legend_char_size: 337 legend_char_size = [] 338 for gi in range(graph_num): 339 legend_char_size.append(1.0) 340 341 # Set the None args to lists as needed. 342 if not data_type: 343 data_type = [None, None] 344 if not seq_type: 345 seq_type = [None, None] 346 if not axis_labels: 347 axis_labels = [] 348 for gi in range(graph_num): 349 axis_labels.append([None, None]) 350 351 # Set the Grace version number of the header's formatting for compatibility. 352 file.write("@version 50121\n") 353 354 # The paper size. 355 if paper_size == 'A4': 356 file.write("@page size 842, 595\n") 357 358 # Loop over each graph. 359 for gi in range(graph_num): 360 # Graph Gi. 361 file.write("@with g%i\n" % gi) 362 363 # The world view. 364 if world: 365 file.write("@ world %s, %s, %s, %s\n" % (world[gi][0], world[gi][1], world[gi][2], world[gi][3])) 366 367 # The view port. 368 if not view: 369 view = [0.15, 0.15, 1.28, 0.85] 370 file.write("@ view %s, %s, %s, %s\n" % (view[0], view[1], view[2], view[3])) 371 372 # The title and subtitle. 373 if title: 374 file.write("@ title \"%s\"\n" % title) 375 if subtitle: 376 file.write("@ subtitle \"%s\"\n" % subtitle) 377 378 # Axis at zero. 379 if x_axis_type_zero[gi]: 380 file.write("@ xaxis type zero true\n") 381 if y_axis_type_zero[gi]: 382 file.write("@ yaxis type zero true\n") 383 384 # Axis specific settings. 385 axes = ['x', 'y'] 386 for i in range(2): 387 # Some axis default values for spin data. 388 if data_type[i] == 'spin': 389 # Residue only data. 390 if seq_type[i] == 'res': 391 # X-axis label. 392 if not axis_labels[gi][i]: 393 axis_labels[gi][i] = "Residue number" 394 395 # Spin only data. 396 if seq_type[i] == 'spin': 397 # X-axis label. 398 if not axis_labels[gi][i]: 399 axis_labels[gi][i] = "Spin number" 400 401 # Mixed data. 402 if seq_type[i] == 'mixed': 403 # X-axis label. 404 if not axis_labels[gi][i]: 405 axis_labels[gi][i] = "Spin ID string" 406 407 # Write out the data. 408 if axis_labels[gi][i]: 409 file.write("@ %saxis label \"%s\"\n" % (axes[i], axis_labels[gi][i])) 410 file.write("@ %saxis label char size 1.00\n" % axes[i]) 411 if tick_major_spacing != None: 412 file.write("@ %saxis tick major %s\n" % (axes[i], tick_major_spacing[gi][i])) 413 file.write("@ %saxis tick major size 0.50\n" % axes[i]) 414 file.write("@ %saxis tick major linewidth %s\n" % (axes[i], linewidth[gi])) 415 if tick_minor_count != None: 416 file.write("@ %saxis tick minor ticks %s\n" % (axes[i], tick_minor_count[gi][i])) 417 file.write("@ %saxis tick minor linewidth %s\n" % (axes[i], linewidth[gi])) 418 file.write("@ %saxis tick minor size 0.25\n" % axes[i]) 419 file.write("@ %saxis ticklabel char size 0.70\n" % axes[i]) 420 421 # Legend box. 422 if legend != None and legend[gi]: 423 file.write("@ legend on\n") 424 else: 425 file.write("@ legend off\n") 426 if legend_pos != None: 427 file.write("@ legend %s, %s\n" % (legend_pos[gi][0], legend_pos[gi][1])) 428 file.write("@ legend box fill pattern %s\n" % legend_box_fill_pattern[gi]) 429 file.write("@ legend char size %s\n" % legend_char_size[gi]) 430 431 # Frame. 432 file.write("@ frame linewidth %s\n" % linewidth[gi]) 433 434 # Loop over each graph set. 435 for i in range(sets[gi]): 436 # Symbol style (default to all different symbols). 437 if symbols: 438 file.write("@ s%i symbol %i\n" % (i, symbols[gi][i])) 439 else: 440 # The symbol number (cycle between 1 and 10). 441 num = i % 10 + 1 442 443 # Write out. 444 file.write("@ s%i symbol %i\n" % (i, num)) 445 446 # Symbol sizes (default to a small size). 447 if symbol_sizes: 448 file.write("@ s%i symbol size %s\n" % (i, symbol_sizes[gi][i])) 449 else: 450 file.write("@ s%i symbol size 0.45\n" % i) 451 452 # The symbol fill. 453 if symbol_fill: 454 file.write("@ s%i symbol fill pattern %i\n" % (i, symbol_fill[gi][i])) 455 456 # The symbol line width. 457 file.write("@ s%i symbol linewidth %s\n" % (i, linewidth[gi])) 458 459 # Symbol colour (default to nothing). 460 if set_colours: 461 file.write("@ s%i symbol color %s\n" % (i, set_colours[gi][i])) 462 file.write("@ s%i symbol fill color %s\n" % (i, set_colours[gi][i])) 463 464 # Error bars. 465 file.write("@ s%i errorbar size 0.5\n" % i) 466 file.write("@ s%i errorbar linewidth %s\n" % (i, linewidth[gi])) 467 file.write("@ s%i errorbar riser linewidth %s\n" % (i, linewidth[gi])) 468 469 # Line linestyle (default to nothing). 470 if linestyle: 471 file.write("@ s%i line linestyle %s\n" % (i, linestyle[gi][i])) 472 473 # Line linetype (default to nothing). 474 if linetype: 475 file.write("@ s%i line type %s\n" % (i, linetype[gi][i])) 476 477 # Line and all other colours (default to nothing). 478 if set_colours: 479 file.write("@ s%i line color %s\n" % (i, set_colours[gi][i])) 480 file.write("@ s%i fill color %s\n" % (i, set_colours[gi][i])) 481 file.write("@ s%i avalue color %s\n" % (i, set_colours[gi][i])) 482 file.write("@ s%i errorbar color %s\n" % (i, set_colours[gi][i])) 483 484 # Legend. 485 if set_names and len(set_names) and len(set_names[gi]) and set_names[gi][i]: 486 file.write("@ s%i legend \"%s\"\n" % (i, set_names[gi][i]))
487