Package pipe_control :: Module value
[hide private]
[frames] | no frames]

Source Code for Module pipe_control.value

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2003-2014 Edward d'Auvergne                                   # 
  4  # Copyright (C) 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 the manipulation of parameter and constant values.""" 
 25   
 26  # Python module imports. 
 27  from numpy import ndarray 
 28  import sys 
 29   
 30  # relax module imports. 
 31  from lib.check_types import is_num 
 32  from lib.errors import RelaxError, RelaxNoSequenceError, RelaxParamSetError, RelaxValueError 
 33  from lib.io import get_file_path, open_write_file 
 34  from lib.sequence import read_spin_data, write_spin_data 
 35  from pipe_control import minimise, pipes 
 36  from pipe_control.mol_res_spin import exists_mol_res_spin_data, generate_spin_id_unique, spin_loop 
 37  from pipe_control.pipes import check_pipe 
 38  from pipe_control.result_files import add_result_file 
 39  from specific_analyses.api import return_api 
 40  from status import Status; status = Status() 
 41   
 42   
43 -def copy(pipe_from=None, pipe_to=None, param=None, force=False):
44 """Copy spin specific data values from pipe_from to pipe_to. 45 46 @param pipe_from: The data pipe to copy the value from. This defaults to the current data 47 pipe. 48 @type pipe_from: str 49 @param pipe_to: The data pipe to copy the value to. This defaults to the current data pipe. 50 @type pipe_to: str 51 @param param: The name of the parameter to copy the values of. 52 @type param: str 53 @keyword force: A flag forcing the overwriting of current values. 54 @type force: bool 55 """ 56 57 # The current data pipe. 58 if pipe_from == None: 59 pipe_from = pipes.cdp_name() 60 if pipe_to == None: 61 pipe_to = pipes.cdp_name() 62 pipe_orig = pipes.cdp_name() 63 64 # The second pipe does not exist. 65 check_pipe(pipe_to) 66 67 # Test if the sequence data for pipe_from is loaded. 68 if not exists_mol_res_spin_data(pipe_from): 69 raise RelaxNoSequenceError(pipe_from) 70 71 # Test if the sequence data for pipe_to is loaded. 72 if not exists_mol_res_spin_data(pipe_to): 73 raise RelaxNoSequenceError(pipe_to) 74 75 # The specific analysis API object. 76 api = return_api(pipe_name=pipe_from) 77 78 # Test if the data exists for pipe_to. 79 if force == False: 80 for spin in spin_loop(pipe=pipe_to): 81 # Get the value and error for pipe_to. 82 value, error = api.return_value(spin, param) 83 84 # Data exists. 85 if value != None or error != None: 86 raise RelaxValueError(param, pipe_to) 87 88 # Switch to the data pipe to copy values to. 89 pipes.switch(pipe_to) 90 91 # Copy the values. 92 for spin, spin_id in spin_loop(pipe=pipe_from, return_id=True): 93 # Get the value and error from pipe_from. 94 value, error = api.return_value(spin, param) 95 96 # Set the values of pipe_to. 97 if value != None: 98 set(spin_id=spin_id, val=value, param=param, pipe=pipe_to, force=force) 99 if error != None: 100 set(spin_id=spin_id, val=error, param=param, pipe=pipe_to, error=True, force=force) 101 102 # Reset all minimisation statistics. 103 minimise.reset_min_stats(pipe_to) 104 105 # Switch back to the original current data pipe. 106 pipes.switch(pipe_orig)
107 108
109 -def display(param=None, scaling=1.0):
110 """Display spin specific data values. 111 112 @keyword param: The name of the parameter to display. 113 @type param: str 114 @keyword scaling: The value to scale the parameter by. 115 @type scaling: float 116 """ 117 118 # Test if the current pipe exists. 119 check_pipe() 120 121 # Test if the sequence data is loaded. 122 if not exists_mol_res_spin_data(): 123 raise RelaxNoSequenceError 124 125 # Print the data. 126 write_data(param=param, file=sys.stdout, scaling=scaling)
127 128
129 -def get_parameters():
130 """Return a list of the parameters associated with the current data pipe. 131 132 @return: The list of parameters. 133 @rtype: list of str 134 """ 135 136 # No data pipes. 137 if cdp == None: 138 return [] 139 140 # The specific analysis API object. 141 api = return_api() 142 143 # Return an empty list if the required functions are absent. 144 if not hasattr(api, 'data_names') or not hasattr(api, 'return_data_desc'): 145 return [] 146 147 # Loop over the parameters. 148 params = [] 149 for name in (api.data_names(set='params') + api.data_names(set='generic') + api.data_names(set='min')): 150 # Get the description. 151 desc = api.return_data_desc(name) 152 153 # No description. 154 if not desc: 155 text = name 156 157 # The text. 158 else: 159 text = "'%s': %s" % (name, desc) 160 161 # Append the data as a list. 162 params.append((text, name)) 163 164 # Return the data. 165 return params
166 167
168 -def partition_params(val, param):
169 """Function for sorting and partitioning the parameters and their values. 170 171 The two major partitions are the tensor parameters and the spin specific parameters. 172 173 @param val: The parameter values. 174 @type val: None, number, or list of numbers 175 @param param: The parameter names. 176 @type param: None, str, or list of str 177 @return: A tuple, of length 4, of lists. The first and second elements are the lists of 178 spin specific parameters and values respectively. The third and forth elements 179 are the lists of all other parameters and their values. 180 @rtype: tuple of 4 lists 181 """ 182 183 # The specific analysis API object. 184 api = return_api() 185 186 # Initialise. 187 spin_params = [] 188 spin_values = [] 189 other_params = [] 190 other_values = [] 191 192 # Single parameter. 193 if isinstance(param, str): 194 # Spin specific parameter. 195 if api.is_spin_param(param): 196 params = spin_params 197 values = spin_values 198 199 # Other parameters. 200 else: 201 params = other_params 202 values = other_values 203 204 # List of values. 205 if isinstance(val, list) or isinstance(val, ndarray): 206 # Parameter name. 207 for i in range(len(val)): 208 params.append(param) 209 210 # Parameter value. 211 values = val 212 213 # Single value. 214 else: 215 # Parameter name. 216 params.append(param) 217 218 # Parameter value. 219 values.append(val) 220 221 # Multiple parameters. 222 elif isinstance(param, list): 223 # Loop over all parameters. 224 for i in range(len(param)): 225 # Spin specific parameter. 226 if api.is_spin_param(param[i]): 227 params = spin_params 228 values = spin_values 229 230 # Other parameters. 231 else: 232 params = other_params 233 values = other_values 234 235 # Parameter name. 236 params.append(param[i]) 237 238 # Parameter value. 239 if isinstance(val, list) or isinstance(val, ndarray): 240 values.append(val[i]) 241 else: 242 values.append(val) 243 244 245 # Return the partitioned parameters and values. 246 return spin_params, spin_values, other_params, other_values
247 248
249 -def read(param=None, scaling=1.0, file=None, dir=None, file_data=None, spin_id_col=None, mol_name_col=None, res_num_col=None, res_name_col=None, spin_num_col=None, spin_name_col=None, data_col=None, error_col=None, sep=None, spin_id=None):
250 """Read spin specific data values from a file. 251 252 @keyword param: The name of the parameter to read. 253 @type param: str 254 @keyword scaling: A scaling factor by which all read values are multiplied by. 255 @type scaling: float 256 @keyword file: The name of the file to open. 257 @type file: str 258 @keyword dir: The directory containing the file (defaults to the current directory if None). 259 @type dir: str or None 260 @keyword file_data: An alternative to opening a file, if the data already exists in the correct format. The format is a list of lists where the first index corresponds to the row and the second the column. 261 @type file_data: list of lists 262 @keyword spin_id_col: The column containing the spin ID strings. If supplied, the mol_name_col, res_name_col, res_num_col, spin_name_col, and spin_num_col arguments must be none. 263 @type spin_id_col: int or None 264 @keyword mol_name_col: The column containing the molecule name information. If supplied, spin_id_col must be None. 265 @type mol_name_col: int or None 266 @keyword res_name_col: The column containing the residue name information. If supplied, spin_id_col must be None. 267 @type res_name_col: int or None 268 @keyword res_num_col: The column containing the residue number information. If supplied, spin_id_col must be None. 269 @type res_num_col: int or None 270 @keyword spin_name_col: The column containing the spin name information. If supplied, spin_id_col must be None. 271 @type spin_name_col: int or None 272 @keyword spin_num_col: The column containing the spin number information. If supplied, spin_id_col must be None. 273 @type spin_num_col: int or None 274 @keyword data_col: The column containing the RDC data in Hz. 275 @type data_col: int or None 276 @keyword error_col: The column containing the RDC errors. 277 @type error_col: int or None 278 @keyword sep: The column separator which, if None, defaults to whitespace. 279 @type sep: str or None 280 @keyword spin_id: The spin ID string. 281 @type spin_id: None or str 282 """ 283 284 # Test if the current pipe exists. 285 check_pipe() 286 287 # Test if sequence data is loaded. 288 if not exists_mol_res_spin_data(): 289 raise RelaxNoSequenceError 290 291 # Minimisation statistic flag. 292 min_stat = False 293 294 # Alias specific analysis API object method. 295 api = return_api() 296 return_value = api.return_value 297 298 # Specific set function. 299 set_fn = set 300 301 # Test data corresponding to param already exists. 302 for spin in spin_loop(): 303 # Skip deselected spins. 304 if not spin.select: 305 continue 306 307 # Get the value and error. 308 value, error = return_value(spin, param) 309 310 # Data exists. 311 if value != None or error != None: 312 raise RelaxValueError(param) 313 314 # Loop over the data. 315 mol_names = [] 316 res_nums = [] 317 res_names = [] 318 spin_nums = [] 319 spin_names = [] 320 values = [] 321 errors = [] 322 for data in read_spin_data(file=file, dir=dir, file_data=file_data, spin_id_col=spin_id_col, mol_name_col=mol_name_col, res_num_col=res_num_col, res_name_col=res_name_col, spin_num_col=spin_num_col, spin_name_col=spin_name_col, data_col=data_col, error_col=error_col, sep=sep, spin_id=spin_id): 323 # Unpack. 324 if data_col and error_col: 325 mol_name, res_num, res_name, spin_num, spin_name, value, error = data 326 elif data_col: 327 mol_name, res_num, res_name, spin_num, spin_name, value = data 328 error = None 329 else: 330 mol_name, res_num, res_name, spin_num, spin_name, error = data 331 value = None 332 333 # Set the value. 334 id = generate_spin_id_unique(mol_name=mol_name, res_num=res_num, res_name=res_name, spin_num=spin_num, spin_name=spin_name) 335 set_fn(val=value, error=error, param=param, spin_id=id) 336 337 # Append the data for printout. 338 mol_names.append(mol_name) 339 res_nums.append(res_num) 340 res_names.append(res_name) 341 spin_nums.append(spin_num) 342 spin_names.append(spin_name) 343 values.append(value) 344 errors.append(error) 345 346 # Print out. 347 write_spin_data(file=sys.stdout, mol_names=mol_names, res_nums=res_nums, res_names=res_names, spin_nums=spin_nums, spin_names=spin_names, data=values, data_name=param, error=errors, error_name='%s_error'%param) 348 349 # Reset the minimisation statistics. 350 if api.set(param) == 'min': 351 minimise.reset_min_stats()
352 353
354 -def set(val=None, param=None, index=None, pipe=None, spin_id=None, verbosity=1, error=False, force=True, reset=True):
355 """Set global or spin specific data values. 356 357 @keyword val: The parameter values. 358 @type val: None or list 359 @keyword param: The parameter names. 360 @type param: None, str, or list of str 361 @keyword index: The index for parameters which are of the list-type. This is ignored for all other types. 362 @type index: None or int 363 @keyword pipe: The data pipe the values should be placed in. 364 @type pipe: None or str 365 @keyword spin_id: The spin identification string. 366 @type spin_id: str 367 @keyword verbosity: The amount of information to print. The higher the value, the greater the verbosity. 368 @type verbosity: int 369 @keyword error: A flag which if True will allow the parameter errors to be set instead of the values. 370 @type error: bool 371 @keyword force: A flag forcing the overwriting of current values. 372 @type force: bool 373 @keyword reset: A flag which if True will cause all minimisation statistics to be reset. 374 @type reset: bool 375 """ 376 377 # Switch to the data pipe, storing the original. 378 if pipe: 379 orig_pipe = pipes.cdp_name() 380 pipes.switch(pipe) 381 382 # Test if the current data pipe exists. 383 check_pipe() 384 385 # The specific analysis API object. 386 api = return_api() 387 388 # Convert numpy arrays to lists, if necessary. 389 if isinstance(val, ndarray): 390 val = val.tolist() 391 392 # Invalid combinations. 393 if (isinstance(val, float) or isinstance(val, int)) and param == None: 394 raise RelaxError("The combination of a single value '%s' without specifying the parameter name is invalid." % val) 395 if isinstance(val, list) and isinstance(param, str): 396 raise RelaxError("Invalid combination: When multiple values '%s' are specified, either no parameters or a list of parameters must by supplied rather than the single parameter '%s'." % (val, param)) 397 398 # Value array and parameter array of equal length. 399 if isinstance(val, list) and isinstance(param, list) and len(val) != len(param): 400 raise RelaxError("Both the value array and parameter array must be of equal length.") 401 402 # Get the parameter list if needed. 403 if param == None: 404 param = api.get_param_names() 405 406 # Convert the param to a list if needed. 407 if not isinstance(param, list): 408 param = [param] 409 410 # Convert the value to a list if needed. 411 if val != None and not isinstance(val, list): 412 val = [val] * len(param) 413 414 # Default values. 415 if val == None: 416 # Loop over the parameters, getting the default values. 417 val = [] 418 for i in range(len(param)): 419 val.append(api.default_value(param[i])) 420 421 # Check that there is a default. 422 if val[-1] == None: 423 raise RelaxParamSetError(param[i]) 424 425 # Set the parameter values. 426 api.set_param_values(param=param, value=val, index=index, spin_id=spin_id, error=error, force=force) 427 428 # Reset all minimisation statistics. 429 if reset: 430 minimise.reset_min_stats(verbosity=verbosity) 431 432 # Switch back. 433 if pipe: 434 pipes.switch(orig_pipe)
435 436
437 -def write(param=None, file=None, dir=None, scaling=1.0, return_value=None, return_data_desc=None, comment=None, bc=False, force=False):
438 """Write data to a file. 439 440 @keyword param: The name of the parameter to write to file. 441 @type param: str 442 @keyword file: The file to write the data to. 443 @type file: str 444 @keyword dir: The name of the directory to place the file into (defaults to the current directory). 445 @type dir: str 446 @keyword scaling: The value to scale the parameter by. 447 @type scaling: float 448 @keyword return_value: An optional function which if supplied will override the default value returning function. 449 @type return_value: None or func 450 @keyword return_data_desc: An optional function which if supplied will override the default parameter description returning function. 451 @type return_data_desc: None or func 452 @keyword comment: Text which will be added to the start of the file as comments. All lines will be prefixed by '# '. 453 @type comment: str 454 @keyword bc: A flag which if True will cause the back calculated values to be written. 455 @type bc: bool 456 @keyword force: A flag which if True will cause any pre-existing file to be overwritten. 457 @type force: bool 458 """ 459 460 # Test if the current pipe exists. 461 check_pipe() 462 463 # Test if the sequence data is loaded. 464 if not exists_mol_res_spin_data(): 465 raise RelaxNoSequenceError 466 467 # Open the file for writing. 468 file_path = get_file_path(file, dir) 469 file = open_write_file(file, dir, force) 470 471 # Write the data. 472 write_data(param=param, file=file, scaling=scaling, return_value=return_value, return_data_desc=return_data_desc, comment=comment, bc=bc) 473 474 # Close the file. 475 file.close() 476 477 # Add the file to the results file list. 478 add_result_file(type='text', label='Text', file=file_path)
479 480
481 -def write_data(param=None, file=None, scaling=1.0, bc=False, return_value=None, return_data_desc=None, comment=None):
482 """The function which actually writes the data. 483 484 @keyword param: The parameter to write. 485 @type param: str 486 @keyword file: The file to write the data to. 487 @type file: str 488 @keyword scaling: The value to scale the parameter by. 489 @type scaling: float 490 @keyword bc: A flag which if True will cause the back calculated values to be written. 491 @type bc: bool 492 @keyword return_value: An optional function which if supplied will override the default value returning function. 493 @type return_value: None or func 494 @keyword return_data_desc: An optional function which if supplied will override the default parameter description returning function. 495 @type return_data_desc: None or func 496 @keyword comment: Text which will be added to the start of the file as comments. All lines will be prefixed by '# '. 497 @type comment: str 498 """ 499 500 # The specific analysis API object. 501 api = return_api() 502 503 # Get the value and error returning function parameter description function if required. 504 if not return_value: 505 return_value = api.return_value 506 if not return_data_desc: 507 return_data_desc = api.return_data_desc 508 509 # Format string. 510 format = "%-30s%-30s" 511 512 # Init the data. 513 mol_names = [] 514 res_nums = [] 515 res_names = [] 516 spin_nums = [] 517 spin_names = [] 518 values = [] 519 errors = [] 520 521 # Get the parameter description and add it to the file. 522 desc = return_data_desc(param) 523 if desc: 524 file.write("# Parameter description: %s.\n" % desc) 525 file.write("#\n") 526 527 # The comments. 528 if comment: 529 # Split up the lines. 530 lines = comment.splitlines() 531 532 # Write out. 533 for line in lines: 534 file.write("# %s\n" % line) 535 file.write("#\n") 536 537 # Determine the data type, check the data, and set up the dictionary type data keys. 538 data_names = 'value' 539 error_names = 'error' 540 data_type = None 541 for spin, mol_name, res_num, res_name in spin_loop(full_info=True): 542 # Get the value and error. 543 value, error = return_value(spin, param, bc=bc) 544 545 # Dictionary type data. 546 if isinstance(value, dict): 547 # Sanity check. 548 if not data_type in [None, 'dict']: 549 raise RelaxError("Mixed data types.") 550 data_type = 'dict' 551 552 # Initialise the structures. 553 if not isinstance(data_names, list): 554 data_names = [] 555 error_names = [] 556 557 # Sort the keys. 558 keys = sorted(value.keys()) 559 560 # Loop over the keys. 561 for key in keys: 562 # Add the data and error names if new. 563 if key not in data_names: 564 data_names.append(key) 565 error_names.append('sd(%s)' % key) 566 567 # List type data. 568 elif isinstance(value, list): 569 # Sanity check. 570 if not data_type in [None, 'list']: 571 raise RelaxError("Mixed data types.") 572 data_type = 'list' 573 574 # Initialise the structures. 575 if not isinstance(data_names, list): 576 data_names = [] 577 error_names = [] 578 579 # Check the length. 580 elif len(data_names) != len(value): 581 raise RelaxError("The list type data has an inconsistent number of elements between different spin systems.") 582 583 # Loop over the data. 584 for i in range(len(value)): 585 # The data and error names. 586 data_names.append('value_%s' % i) 587 error_names.append('error_%s' % i) 588 589 # None. 590 elif value == None: 591 pass 592 593 # Simple values. 594 else: 595 # Sanity check. 596 if not data_type in [None, 'value']: 597 raise RelaxError("Mixed data types.") 598 data_type = 'value' 599 600 # Pack the data. 601 for spin, mol_name, res_num, res_name in spin_loop(full_info=True): 602 # Get the value and error. 603 value, error = return_value(spin, param, bc=bc) 604 605 # Append the spin data (scaled). 606 mol_names.append(mol_name) 607 res_nums.append(res_num) 608 res_names.append(res_name) 609 spin_nums.append(spin.num) 610 spin_names.append(spin.name) 611 612 # Dictionary type data. 613 if data_type == 'dict': 614 # Initialise the lists. 615 values.append([]) 616 errors.append([]) 617 618 # Loop over the keys. 619 for key in data_names: 620 # Append the scaled values and errors. 621 if value == None or key not in value: 622 values[-1].append(None) 623 else: 624 values[-1].append(scale(value[key], scaling)) 625 if error == None or key not in error: 626 errors[-1].append(None) 627 else: 628 errors[-1].append(scale(error[key], scaling)) 629 630 # List type data. 631 elif data_type == 'list': 632 # Initialise the lists. 633 values.append([]) 634 errors.append([]) 635 636 # Loop over the data. 637 for i in range(len(data_names)): 638 # Append the scaled values and errors. 639 if value == None: 640 values[-1].append(None) 641 else: 642 values[-1].append(scale(value[i], scaling)) 643 if error == None: 644 errors[-1].append(None) 645 else: 646 errors[-1].append(scale(error[i], scaling)) 647 648 # Simple values. 649 else: 650 # Append the scaled values and errors. 651 values.append(scale(value, scaling)) 652 errors.append(scale(error, scaling)) 653 654 # Replace all spaces " " with "_" in data_names list. 655 if isinstance(data_names, list): 656 for i, data_name in enumerate(data_names): 657 data_str = data_name.replace(" ", "_") 658 # Replace string. 659 data_names[i] = data_str 660 661 # Replace all spaces " " with "_" in error_names list. 662 if isinstance(error_names, list): 663 for i, error_name in enumerate(error_names): 664 error_str = error_name.replace(" ", "_") 665 # Replace string. 666 error_names[i] = error_str 667 668 # Write the data. 669 write_spin_data(file, mol_names=mol_names, res_nums=res_nums, res_names=res_names, spin_nums=spin_nums, spin_names=spin_names, data=values, data_name=data_names, error=errors, error_name=error_names)
670 671
672 -def scale(value, scaling):
673 """Scale the given value by the scaling factor, handling all input value types. 674 675 @param value: The value to scale. 676 @type value: anything 677 @param scaling: The scaling factor. 678 @type scaling: float 679 """ 680 681 # No a number, so return the value unmodified. 682 if not is_num(value): 683 return value 684 685 # Scale. 686 return value * scaling
687