1   
   2   
   3   
   4   
   5   
   6   
   7   
   8   
   9   
  10   
  11   
  12   
  13   
  14   
  15   
  16   
  17   
  18   
  19   
  20   
  21   
  22   
  23  """Module for the manipulation of pseudo-contact shift data.""" 
  24   
  25   
  26  from copy import deepcopy 
  27  from math import pi, sqrt 
  28  from numpy import array, float64, int32, ones, std, zeros 
  29  from numpy.linalg import norm 
  30  from random import gauss 
  31  import sys 
  32  from warnings import warn 
  33   
  34   
  35  from lib.alignment.pcs import ave_pcs_tensor, pcs_tensor 
  36  from lib.errors import RelaxError, RelaxNoAlignError, RelaxNoPdbError, RelaxNoPCSError, RelaxNoSequenceError 
  37  from lib.geometry.vectors import random_unit_vector 
  38  from lib.io import open_write_file 
  39  from lib.physical_constants import g1H, pcs_constant 
  40  from lib.sequence import read_spin_data, write_spin_data 
  41  from lib.warnings import RelaxWarning, RelaxNoSpinWarning 
  42  from pipe_control import grace, pipes 
  43  from pipe_control.align_tensor import get_tensor_index, get_tensor_object, opt_uses_align_data, opt_uses_tensor 
  44  from pipe_control.mol_res_spin import exists_mol_res_spin_data, generate_spin_id_unique, is_pseudoatom, return_spin, spin_index_loop, spin_loop 
  45   
  46   
  48      """Back calculate the PCS from the alignment tensor. 
  49   
  50      @keyword align_id:      The alignment tensor ID string. 
  51      @type align_id:         str 
  52      """ 
  53   
  54       
  55      check_pipe_setup(pcs_id=align_id, sequence=True, N=True, tensors=True, paramag_centre=True) 
  56   
  57       
  58      if align_id: 
  59          align_ids = [align_id] 
  60      else: 
  61          align_ids = cdp.align_ids 
  62   
  63       
  64      for align_id in align_ids: 
  65           
  66          if not hasattr(cdp, 'pcs_ids'): 
  67              cdp.pcs_ids = [] 
  68   
  69           
  70          if align_id not in cdp.pcs_ids: 
  71              cdp.pcs_ids.append(align_id) 
  72   
  73       
  74      weights = ones(cdp.N, float64) / cdp.N 
  75   
  76       
  77      unit_vect = zeros((cdp.N, 3), float64) 
  78   
  79       
  80      count = 0 
  81      for spin in spin_loop(): 
  82           
  83          if not hasattr(spin, 'pos'): 
  84              continue 
  85   
  86           
  87          pos = spin.pos 
  88          if type(pos[0]) in [float, float64]: 
  89              pos = [pos] * cdp.N 
  90   
  91           
  92          for id in align_ids: 
  93               
  94              vect = zeros((cdp.N, 3), float64) 
  95              r = zeros(cdp.N, float64) 
  96              dj = zeros(cdp.N, float64) 
  97              for c in range(cdp.N): 
  98                   
  99                  vect[c] = pos[c] - cdp.paramagnetic_centre 
 100   
 101                   
 102                  r[c] = norm(vect[c]) 
 103   
 104                   
 105                  if r[c]: 
 106                      vect[c] = vect[c] / r[c] 
 107   
 108                   
 109                  dj[c] = pcs_constant(cdp.temperature[id], cdp.spectrometer_frq[id] * 2.0 * pi / g1H, r[c]/1e10) 
 110   
 111               
 112              if not hasattr(spin, 'pcs_bc'): 
 113                  spin.pcs_bc = {} 
 114   
 115               
 116              spin.pcs_bc[id] = ave_pcs_tensor(dj, vect, cdp.N, cdp.align_tensors[get_tensor_index(align_id=id)].A, weights=weights) * 1e6 
 117   
 118           
 119          count += 1 
 120   
 121       
 122      if not count: 
 123          warn(RelaxWarning("No PCSs have been back calculated, probably due to missing spin position information.")) 
  124   
 125   
 126 -def centre(pos=None, atom_id=None, pipe=None, verbosity=1, ave_pos=False, force=False): 
  127      """Specify the atom in the loaded structure corresponding to the paramagnetic centre. 
 128   
 129      @keyword pos:       The atomic position.  If set, the atom_id string will be ignored. 
 130      @type pos:          list of float 
 131      @keyword atom_id:   The atom identification string. 
 132      @type atom_id:      str 
 133      @keyword pipe:      An alternative data pipe to extract the paramagnetic centre from. 
 134      @type pipe:         None or str 
 135      @keyword verbosity: The amount of information to print out.  The bigger the number, the more information. 
 136      @type verbosity:    int 
 137      @keyword ave_pos:   A flag which if True causes the atomic positions from multiple models to be averaged. 
 138      @type ave_pos:      bool 
 139      @keyword force:     A flag which if True will cause the current PCS centre to be overwritten. 
 140      """ 
 141   
 142       
 143      if pipe == None: 
 144          pipe = pipes.cdp_name() 
 145   
 146       
 147      check_pipe_setup(pipe=pipe) 
 148   
 149       
 150      source_dp = pipes.get_pipe(pipe) 
 151   
 152       
 153      if not hasattr(source_dp, 'structure'): 
 154          raise RelaxNoPdbError 
 155   
 156       
 157      if not force and hasattr(cdp, 'paramagnetic_centre'): 
 158          raise RelaxError("The paramagnetic centre has already been set to the coordinates " + repr(cdp.paramagnetic_centre) + ".") 
 159   
 160       
 161      if pos != None: 
 162          centre = array(pos) 
 163          num_pos = 1 
 164          full_pos_list = [] 
 165   
 166       
 167      else: 
 168           
 169          centre = zeros(3, float64) 
 170          full_pos_list = [] 
 171          num_pos = 0 
 172          for spin, spin_id in spin_loop(atom_id, pipe=pipe, return_id=True): 
 173               
 174              if not hasattr(spin, 'pos'): 
 175                  continue 
 176       
 177               
 178              if isinstance(spin.pos[0], float) or isinstance(spin.pos[0], float64): 
 179                  pos_list = [spin.pos] 
 180              else: 
 181                  pos_list = spin.pos 
 182       
 183               
 184              for pos in pos_list: 
 185                  full_pos_list.append(pos) 
 186                  centre = centre + array(pos) 
 187                  num_pos = num_pos + 1 
 188       
 189           
 190          if not num_pos: 
 191              raise RelaxError("No positional information could be found for the spin '%s'." % atom_id) 
 192   
 193       
 194      centre = centre / float(num_pos) 
 195   
 196       
 197      if verbosity: 
 198          print("Paramagnetic centres located at:") 
 199          for pos in full_pos_list: 
 200              print("    [%8.3f, %8.3f, %8.3f]" % (pos[0], pos[1], pos[2])) 
 201          print("\nAverage paramagnetic centre located at:") 
 202          print("    [%8.3f, %8.3f, %8.3f]" % (centre[0], centre[1], centre[2])) 
 203   
 204       
 205      if ave_pos: 
 206          if verbosity: 
 207              print("\nUsing the average paramagnetic position.") 
 208          cdp.paramagnetic_centre = centre 
 209      else: 
 210          if verbosity: 
 211              print("\nUsing all paramagnetic positions.") 
 212          cdp.paramagnetic_centre = full_pos_list 
  213   
 214   
 215 -def check_pipe_setup(pipe=None, pcs_id=None, sequence=False, N=False, tensors=False, pcs=False, paramag_centre=False): 
  216      """Check that the current data pipe has been setup sufficiently. 
 217   
 218      @keyword pipe:              The data pipe to check, defaulting to the current pipe. 
 219      @type pipe:                 None or str 
 220      @keyword pcs_id:            The PCS ID string to check for in cdp.pcs_ids. 
 221      @type pcs_id:               None or str 
 222      @keyword sequence:          A flag which when True will invoke the sequence data check. 
 223      @type sequence:             bool 
 224      @keyword N:                 A flag which if True will check that cdp.N is set. 
 225      @type N:                    bool 
 226      @keyword tensors:           A flag which if True will check that alignment tensors exist. 
 227      @type tensors:              bool 
 228      @keyword pcs:               A flag which if True will check that PCSs exist. 
 229      @type pcs:                  bool 
 230      @keyword paramag_centre:    A flag which if True will check that the paramagnetic centre has been set. 
 231      @type paramag_centre:       bool 
 232      """ 
 233   
 234       
 235      if pipe == None: 
 236          pipe = pipes.cdp_name() 
 237   
 238       
 239      dp = pipes.get_pipe(pipe) 
 240   
 241       
 242      pipes.test(pipe) 
 243   
 244       
 245      if sequence and not exists_mol_res_spin_data(pipe): 
 246          raise RelaxNoSequenceError(pipe) 
 247   
 248       
 249      if N and not hasattr(dp, 'N'): 
 250          raise RelaxError("The number of states N has not been set.") 
 251   
 252       
 253      if tensors and (not hasattr(dp, 'align_tensors') or len(dp.align_tensors) == 0): 
 254          raise RelaxNoTensorError('alignment') 
 255   
 256       
 257      if pcs_id and (not hasattr(dp, 'align_ids') or pcs_id not in dp.align_ids): 
 258          raise RelaxNoAlignError(pcs_id, pipe) 
 259   
 260       
 261      if pcs and not hasattr(dp, 'align_ids'): 
 262          raise RelaxNoAlignError() 
 263      if pcs and not hasattr(dp, 'pcs_ids'): 
 264          raise RelaxNoPCSError() 
 265      elif pcs and pcs_id and pcs_id not in dp.pcs_ids: 
 266          raise RelaxNoPCSError(pcs_id) 
 267   
 268       
 269      if paramag_centre and not hasattr(cdp, 'paramagnetic_centre'): 
 270          raise RelaxError("The paramagnetic centre has not been defined.") 
  271   
 272   
 273 -def copy(pipe_from=None, pipe_to=None, align_id=None): 
  274      """Copy the PCS data from one data pipe to another. 
 275   
 276      @keyword pipe_from: The data pipe to copy the PCS data from.  This defaults to the current data pipe. 
 277      @type pipe_from:    str 
 278      @keyword pipe_to:   The data pipe to copy the PCS data to.  This defaults to the current data pipe. 
 279      @type pipe_to:      str 
 280      @param align_id:    The alignment ID string. 
 281      @type align_id:     str 
 282      """ 
 283   
 284       
 285      if pipe_from == None and pipe_to == None: 
 286          raise RelaxError("The pipe_from and pipe_to arguments cannot both be set to None.") 
 287      elif pipe_from == None: 
 288          pipe_from = pipes.cdp_name() 
 289      elif pipe_to == None: 
 290          pipe_to = pipes.cdp_name() 
 291   
 292       
 293      check_pipe_setup(pipe=pipe_from, pcs_id=align_id, sequence=True, pcs=True) 
 294      check_pipe_setup(pipe=pipe_to, sequence=True) 
 295   
 296       
 297      dp_from = pipes.get_pipe(pipe_from) 
 298      dp_to = pipes.get_pipe(pipe_to) 
 299   
 300       
 301      if align_id == None: 
 302          align_ids = dp_from.align_ids 
 303      else: 
 304          align_ids = [align_id] 
 305   
 306       
 307      if not hasattr(dp_to, 'align_ids'): 
 308          dp_to.align_ids = [] 
 309      if not hasattr(dp_to, 'pcs_ids'): 
 310          dp_to.pcs_ids = [] 
 311   
 312       
 313      for align_id in align_ids: 
 314           
 315          if align_id not in dp_to.align_ids and align_id not in dp_to.align_ids: 
 316              dp_to.align_ids.append(align_id) 
 317          if align_id in dp_from.pcs_ids and align_id not in dp_to.pcs_ids: 
 318              dp_to.pcs_ids.append(align_id) 
 319   
 320           
 321          for mol_index, res_index, spin_index in spin_index_loop(): 
 322               
 323              spin_from = dp_from.mol[mol_index].res[res_index].spin[spin_index] 
 324              spin_to = dp_to.mol[mol_index].res[res_index].spin[spin_index] 
 325   
 326               
 327              if (not hasattr(spin_from, 'pcs') or not align_id in spin_from.pcs) and (not hasattr(spin_from, 'pcs_err') or not align_id in spin_from.pcs_err): 
 328                  continue 
 329   
 330               
 331              if hasattr(spin_from, 'pcs') and not hasattr(spin_to, 'pcs'): 
 332                  spin_to.pcs = {} 
 333              if hasattr(spin_from, 'pcs_err') and not hasattr(spin_to, 'pcs_err'): 
 334                  spin_to.pcs_err = {} 
 335   
 336               
 337              if hasattr(spin_from, 'pcs'): 
 338                  spin_to.pcs[align_id] = spin_from.pcs[align_id] 
 339              if hasattr(spin_from, 'pcs_err'): 
 340                  spin_to.pcs_err[align_id] = spin_from.pcs_err[align_id] 
  341   
 342   
 343 -def corr_plot(format=None, file=None, dir=None, force=False): 
  344      """Generate a correlation plot of the measured vs. back-calculated PCSs. 
 345   
 346      @keyword format:    The format for the plot file.  The following values are accepted: 'grace', a Grace plot; None, a plain text file. 
 347      @type format:       str or None 
 348      @keyword file:      The file name or object to write to. 
 349      @type file:         str or file object 
 350      @keyword dir:       The name of the directory to place the file into (defaults to the current directory). 
 351      @type dir:          str 
 352      @keyword force:     A flag which if True will cause any pre-existing file to be overwritten. 
 353      @type force:        bool 
 354      """ 
 355   
 356       
 357      check_pipe_setup(sequence=True) 
 358   
 359       
 360      if not hasattr(cdp, 'pcs_ids') or not cdp.pcs_ids: 
 361          warn(RelaxWarning("No PCS data exists, skipping file creation.")) 
 362          return 
 363   
 364       
 365      file = open_write_file(file, dir, force) 
 366   
 367       
 368      data = [] 
 369   
 370       
 371      data.append([[-100, -100, 0], [100, 100, 0]]) 
 372   
 373       
 374      types = [] 
 375      for spin in spin_loop(): 
 376          if spin.element not in types: 
 377              types.append(spin.element) 
 378   
 379       
 380      for align_id in cdp.pcs_ids: 
 381           
 382          for i in range(len(types)): 
 383               
 384              data.append([]) 
 385   
 386               
 387              err_flag = False 
 388              for spin in spin_loop(): 
 389                   
 390                  if not spin.select: 
 391                      continue 
 392   
 393                   
 394                  if hasattr(spin, 'pcs_err') and align_id in spin.pcs_err.keys(): 
 395                      err_flag = True 
 396                      break 
 397   
 398               
 399              for spin, spin_id in spin_loop(return_id=True): 
 400                   
 401                  if not spin.select: 
 402                      continue 
 403   
 404                   
 405                  if spin.element != types[i]: 
 406                      continue 
 407   
 408                   
 409                  if not hasattr(spin, 'pcs') or not hasattr(spin, 'pcs_bc') or not align_id in spin.pcs.keys() or not align_id in spin.pcs_bc.keys(): 
 410                      continue 
 411   
 412                   
 413                  data[-1].append([spin.pcs_bc[align_id], spin.pcs[align_id]]) 
 414   
 415                   
 416                  if err_flag: 
 417                      if hasattr(spin, 'pcs_err') and align_id in spin.pcs_err.keys(): 
 418                          data[-1][-1].append(spin.pcs_err[align_id]) 
 419                      else: 
 420                          data[-1][-1].append(None) 
 421   
 422                   
 423                  data[-1][-1].append(spin_id) 
 424   
 425       
 426      size = len(data) 
 427   
 428       
 429      data = [data] 
 430   
 431       
 432      if err_flag: 
 433          graph_type = 'xydy' 
 434      else: 
 435          graph_type = 'xy' 
 436   
 437       
 438      if format == 'grace': 
 439           
 440          set_names = [None] 
 441          for i in range(len(cdp.pcs_ids)): 
 442              for j in range(len(types)): 
 443                  set_names.append("%s (%s)" % (cdp.pcs_ids[i], types[j])) 
 444   
 445           
 446          grace.write_xy_header(file=file, title="PCS correlation plot", data_type=['pcs_bc', 'pcs'], sets=[size], set_names=[set_names], linestyle=[[2]+[0]*size], legend_pos=[[1, 0.5]]) 
 447   
 448           
 449          grace.write_xy_data(data=data, file=file, graph_type=graph_type) 
  450   
 451   
 453      """Delete the PCS data corresponding to the alignment ID. 
 454   
 455      @keyword align_id:  The alignment tensor ID string.  If not specified, all data will be deleted. 
 456      @type align_id:     str or None 
 457      """ 
 458   
 459       
 460      check_pipe_setup(sequence=True, pcs_id=align_id, pcs=True) 
 461   
 462       
 463      if not align_id: 
 464          ids = deepcopy(cdp.pcs_ids) 
 465      else: 
 466          ids = [align_id] 
 467   
 468       
 469      for id in ids: 
 470           
 471          cdp.pcs_ids.pop(cdp.pcs_ids.index(id)) 
 472   
 473           
 474          if hasattr(cdp, 'pcs_data_types') and id in cdp.pcs_data_types: 
 475              cdp.pcs_data_types.pop(id) 
 476   
 477           
 478          for spin in spin_loop(): 
 479               
 480              if hasattr(spin, 'pcs') and id in spin.pcs: 
 481                  spin.pcs.pop(id) 
 482   
 483               
 484              if hasattr(spin, 'pcs_err') and id in spin.pcs_err: 
 485                  spin.pcs_err.pop(id) 
 486   
 487           
 488          if not hasattr(cdp, 'rdc_ids') or id not in cdp.rdc_ids: 
 489              cdp.align_ids.pop(cdp.align_ids.index(id)) 
  490   
 491   
 492 -def display(align_id=None, bc=False): 
  493      """Display the PCS data corresponding to the alignment ID. 
 494   
 495      @keyword align_id:  The alignment tensor ID string. 
 496      @type align_id:     str 
 497      @keyword bc:        The back-calculation flag which if True will cause the back-calculated rather than measured data to be displayed. 
 498      @type bc:           bool 
 499      """ 
 500   
 501       
 502      check_pipe_setup(sequence=True, pcs_id=align_id, pcs=True) 
 503   
 504       
 505      write(align_id=align_id, file=sys.stdout, bc=bc) 
  506   
 507   
 509      """Determine if the PCS data for the given alignment ID is needed for optimisation. 
 510   
 511      @param align_id:    The alignment ID string. 
 512      @type align_id:     str 
 513      @return:            True if the PCS data is to be used for optimisation, False otherwise. 
 514      @rtype:             bool 
 515      """ 
 516   
 517       
 518      if not hasattr(cdp, 'pcs_ids'): 
 519          return False 
 520   
 521       
 522      if align_id not in cdp.pcs_ids: 
 523          return False 
 524   
 525       
 526      tensor_flag = opt_uses_tensor(get_tensor_object(align_id)) 
 527   
 528       
 529      pos_flag = False 
 530      if hasattr(cdp, 'paramag_centre_fixed') and not cdp.paramag_centre_fixed: 
 531          pos_flag = True 
 532   
 533       
 534      prob_flag = False 
 535      if cdp.model == 'population': 
 536          prob_flag = True 
 537   
 538       
 539      if not tensor_flag and not pos_flag and not prob_flag: 
 540          return False 
 541   
 542       
 543      return True 
  544   
 545   
 547      """Calculate the Q factors for the PCS data. 
 548   
 549      @keyword spin_id:   The spin ID string used to restrict the Q factor calculation to a subset of all spins. 
 550      @type spin_id:      None or str 
 551      """ 
 552   
 553       
 554      check_pipe_setup(sequence=True) 
 555   
 556       
 557      if not hasattr(cdp, 'pcs_ids') or not len(cdp.pcs_ids): 
 558          warn(RelaxWarning("No PCS data exists, Q factors cannot be calculated.")) 
 559          return 
 560   
 561       
 562      cdp.q_factors_pcs = {} 
 563   
 564       
 565      for align_id in cdp.pcs_ids: 
 566           
 567          pcs2_sum = 0.0 
 568          sse = 0.0 
 569   
 570           
 571          spin_count = 0 
 572          pcs_data = False 
 573          pcs_bc_data = False 
 574          for spin in spin_loop(spin_id): 
 575               
 576              if not spin.select: 
 577                  continue 
 578   
 579               
 580              spin_count += 1 
 581   
 582               
 583              if hasattr(spin, 'pcs') and align_id in spin.pcs: 
 584                  pcs_data = True 
 585              if hasattr(spin, 'pcs_bc') and align_id in spin.pcs_bc: 
 586                  pcs_bc_data = True 
 587   
 588               
 589              if not hasattr(spin, 'pcs') or not hasattr(spin, 'pcs_bc') or not align_id in spin.pcs or spin.pcs[align_id] == None or not align_id in spin.pcs_bc or spin.pcs_bc[align_id] == None: 
 590                  continue 
 591   
 592               
 593              sse = sse + (spin.pcs[align_id] - spin.pcs_bc[align_id])**2 
 594   
 595               
 596              pcs2_sum = pcs2_sum + spin.pcs[align_id]**2 
 597   
 598           
 599          if pcs2_sum: 
 600              Q = sqrt(sse / pcs2_sum) 
 601              cdp.q_factors_pcs[align_id] = Q 
 602   
 603           
 604          if not spin_count: 
 605              warn(RelaxWarning("No spins have been used in the calculation, skipping the PCS Q factor calculation.")) 
 606              return 
 607          if not pcs_data: 
 608              warn(RelaxWarning("No PCS data can be found for the alignment ID '%s', skipping the PCS Q factor calculation for this alignment." % align_id)) 
 609              continue 
 610          if not pcs_bc_data: 
 611              warn(RelaxWarning("No back-calculated PCS data can be found for the alignment ID '%s', skipping the PCS Q factor calculation for this alignment." % align_id)) 
 612              continue 
 613   
 614       
 615      cdp.q_pcs = 0.0 
 616      for id in cdp.q_factors_pcs: 
 617          cdp.q_pcs = cdp.q_pcs + cdp.q_factors_pcs[id]**2 
 618      cdp.q_pcs = cdp.q_pcs / len(cdp.q_factors_pcs) 
 619      cdp.q_pcs = sqrt(cdp.q_pcs) 
  620   
 621   
 622 -def read(align_id=None, 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): 
  623      """Read the PCS data from file. 
 624   
 625      @param align_id:        The alignment tensor ID string. 
 626      @type align_id:         str 
 627      @param file:            The name of the file to open. 
 628      @type file:             str 
 629      @param dir:             The directory containing the file (defaults to the current directory if None). 
 630      @type dir:              str or None 
 631      @param file_data:       An alternative 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. 
 632      @type file_data:        list of lists 
 633      @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. 
 634      @type spin_id_col:      int or None 
 635      @keyword mol_name_col:  The column containing the molecule name information.  If supplied, spin_id_col must be None. 
 636      @type mol_name_col:     int or None 
 637      @keyword res_name_col:  The column containing the residue name information.  If supplied, spin_id_col must be None. 
 638      @type res_name_col:     int or None 
 639      @keyword res_num_col:   The column containing the residue number information.  If supplied, spin_id_col must be None. 
 640      @type res_num_col:      int or None 
 641      @keyword spin_name_col: The column containing the spin name information.  If supplied, spin_id_col must be None. 
 642      @type spin_name_col:    int or None 
 643      @keyword spin_num_col:  The column containing the spin number information.  If supplied, spin_id_col must be None. 
 644      @type spin_num_col:     int or None 
 645      @keyword data_col:      The column containing the PCS data in Hz. 
 646      @type data_col:         int or None 
 647      @keyword error_col:     The column containing the PCS errors. 
 648      @type error_col:        int or None 
 649      @keyword sep:           The column separator which, if None, defaults to whitespace. 
 650      @type sep:              str or None 
 651      @keyword spin_id:       The spin ID string used to restrict data loading to a subset of all spins. 
 652      @type spin_id:          None or str 
 653      """ 
 654   
 655       
 656      check_pipe_setup(sequence=True) 
 657   
 658       
 659      if not exists_mol_res_spin_data(): 
 660          raise RelaxNoSequenceError 
 661   
 662       
 663      if data_col == None and error_col == None: 
 664          raise RelaxError("One of either the data or error column must be supplied.") 
 665   
 666   
 667       
 668       
 669   
 670       
 671      mol_names = [] 
 672      res_nums = [] 
 673      res_names = [] 
 674      spin_nums = [] 
 675      spin_names = [] 
 676      values = [] 
 677      errors = [] 
 678      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): 
 679           
 680          if data_col and error_col: 
 681              mol_name, res_num, res_name, spin_num, spin_name, value, error = data 
 682          elif data_col: 
 683              mol_name, res_num, res_name, spin_num, spin_name, value = data 
 684              error = None 
 685          else: 
 686              mol_name, res_num, res_name, spin_num, spin_name, error = data 
 687              value = None 
 688   
 689           
 690          if error == 0.0: 
 691              raise RelaxError("An invalid error value of zero has been encountered.") 
 692   
 693           
 694          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) 
 695          spin = return_spin(id) 
 696          if spin == None and spin_id[0] == '@':     
 697              spin = return_spin(id+spin_id) 
 698          if spin == None: 
 699              warn(RelaxNoSpinWarning(id)) 
 700              continue 
 701   
 702           
 703          if data_col: 
 704               
 705              if not hasattr(spin, 'pcs'): 
 706                  spin.pcs = {} 
 707   
 708               
 709              spin.pcs[align_id] = value 
 710   
 711           
 712          if error_col: 
 713               
 714              if not hasattr(spin, 'pcs_err'): 
 715                  spin.pcs_err = {} 
 716   
 717               
 718              spin.pcs_err[align_id] = error 
 719   
 720           
 721          mol_names.append(mol_name) 
 722          res_nums.append(res_num) 
 723          res_names.append(res_name) 
 724          spin_nums.append(spin_num) 
 725          spin_names.append(spin_name) 
 726          values.append(value) 
 727          errors.append(error) 
 728   
 729       
 730      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='PCSs', error=errors, error_name='PCS_error') 
 731   
 732   
 733       
 734       
 735   
 736       
 737      if not len(values): 
 738          return 
 739   
 740       
 741      if not hasattr(cdp, 'align_ids'): 
 742          cdp.align_ids = [] 
 743      if not hasattr(cdp, 'pcs_ids'): 
 744          cdp.pcs_ids = [] 
 745   
 746       
 747      if align_id not in cdp.align_ids: 
 748          cdp.align_ids.append(align_id) 
 749      if align_id not in cdp.pcs_ids: 
 750          cdp.pcs_ids.append(align_id) 
  751   
 752   
 754      """Set up the data structures for optimisation using PCSs as base data sets. 
 755   
 756      @keyword sim_index: The index of the simulation to optimise.  This should be None if normal optimisation is desired. 
 757      @type sim_index:    None or int 
 758      @return:            The assembled data structures for using PCSs as the base data for optimisation.  These include: 
 759                              - the PCS values. 
 760                              - the unit vectors connecting the paramagnetic centre (the electron spin) to the spin. 
 761                              - the PCS weight. 
 762                              - the experimental temperatures. 
 763                              - the spectrometer frequencies. 
 764                              - pseudo_flags, the list of flags indicating if the interatomic data contains a pseudo-atom (as 1's and 0's). 
 765      @rtype:             tuple of (numpy rank-2 float64 array, numpy rank-2 float64 array, numpy rank-2 float64 array, list of float, list of float, numpy rank-1 int32 array) 
 766      """ 
 767   
 768       
 769      if not hasattr(cdp, 'paramagnetic_centre') and (hasattr(cdp, 'paramag_centre_fixed') and cdp.paramag_centre_fixed): 
 770          raise RelaxError("The paramagnetic centre has not yet been specified.") 
 771      if not hasattr(cdp, 'temperature'): 
 772          raise RelaxError("The experimental temperatures have not been set.") 
 773      if not hasattr(cdp, 'spectrometer_frq'): 
 774          raise RelaxError("The spectrometer frequencies of the experiments have not been set.") 
 775   
 776       
 777      setup_pseudoatom_pcs() 
 778   
 779       
 780      pcs = [] 
 781      pcs_err = [] 
 782      pcs_weight = [] 
 783      temp = [] 
 784      frq = [] 
 785      pseudo_flags = [] 
 786   
 787       
 788      for i in range(len(cdp.align_ids)): 
 789           
 790          align_id = cdp.align_ids[i] 
 791   
 792           
 793          if not opt_uses_align_data(align_id): 
 794              continue 
 795   
 796           
 797          pcs.append([]) 
 798          pcs_err.append([]) 
 799          pcs_weight.append([]) 
 800   
 801           
 802          if align_id in cdp.temperature: 
 803              temp.append(cdp.temperature[align_id]) 
 804   
 805           
 806          else: 
 807              raise RelaxError("The experimental temperature for the alignment ID '%s' has not been set." % align_id) 
 808   
 809           
 810          if align_id in cdp.spectrometer_frq: 
 811              frq.append(cdp.spectrometer_frq[align_id] * 2.0 * pi / g1H) 
 812   
 813           
 814          else: 
 815              raise RelaxError("The spectrometer frequency for the alignment ID '%s' has not been set." % align_id) 
 816   
 817           
 818          j = 0 
 819          for spin in spin_loop(): 
 820               
 821              if not spin.select: 
 822                  continue 
 823   
 824               
 825              if not hasattr(spin, 'pcs'): 
 826                  continue 
 827   
 828               
 829              if align_id in spin.pcs.keys(): 
 830                  if sim_index != None: 
 831                      pcs[-1].append(spin.pcs_sim[align_id][sim_index]) 
 832                  else: 
 833                      pcs[-1].append(spin.pcs[align_id]) 
 834              else: 
 835                  pcs[-1].append(None) 
 836   
 837               
 838              if hasattr(spin, 'pcs_err') and align_id in spin.pcs_err.keys(): 
 839                  pcs_err[-1].append(spin.pcs_err[align_id]) 
 840              else: 
 841                  pcs_err[-1].append(None) 
 842   
 843               
 844              if hasattr(spin, 'pcs_weight') and align_id in spin.pcs_weight.keys(): 
 845                  pcs_weight[-1].append(spin.pcs_weight[align_id]) 
 846              else: 
 847                  pcs_weight[-1].append(1.0) 
 848   
 849               
 850              j = j + 1 
 851   
 852       
 853      for spin in spin_loop(): 
 854          if is_pseudoatom(spin): 
 855              pseudo_flags.append(1) 
 856          else: 
 857              pseudo_flags.append(0) 
 858   
 859       
 860      pcs = array(pcs, float64) 
 861      pcs_err = array(pcs_err, float64) 
 862      pcs_weight = array(pcs_weight, float64) 
 863      pseudo_flags = array(pseudo_flags, int32) 
 864   
 865       
 866      pcs = pcs * 1e-6 
 867      pcs_err = pcs_err * 1e-6 
 868   
 869       
 870      return pcs, pcs_err, pcs_weight, temp, frq, pseudo_flags 
  871   
 872   
 873 -def set_errors(align_id=None, spin_id=None, sd=None): 
  874      """Set the PCS errors if not already present. 
 875   
 876      @keyword align_id:  The optional alignment tensor ID string. 
 877      @type align_id:     str 
 878      @keyword spin_id:   The optional spin ID string. 
 879      @type spin_id:      None or str 
 880      @keyword sd:        The PCS standard deviation in ppm. 
 881      @type sd:           float or int. 
 882      """ 
 883   
 884       
 885      check_pipe_setup(sequence=True, pcs_id=align_id, pcs=True) 
 886   
 887       
 888      if align_id: 
 889          align_ids = [align_id] 
 890      else: 
 891          align_ids = cdp.pcs_ids 
 892   
 893       
 894      for spin in spin_loop(spin_id): 
 895           
 896          if not spin.select: 
 897              continue 
 898   
 899           
 900          if not hasattr(spin, 'pcs') or (align_id and not align_id in spin.pcs): 
 901              continue 
 902   
 903           
 904          if not hasattr(spin, 'pcs_err'): 
 905              spin.pcs_err = {} 
 906   
 907           
 908          for id in align_ids: 
 909              spin.pcs_err[id] = sd 
  910   
 911   
 913      """Make sure that the spin systems are properly set up for pseudo-atoms and PCSs. 
 914   
 915      All spin data containers which are a member of a pseudo-atom will be deselected. 
 916      """ 
 917   
 918       
 919      for pseudospin, pseudospin_id in spin_loop(return_id=True): 
 920           
 921          if not is_pseudoatom(pseudospin): 
 922              return 
 923   
 924           
 925          for spin, spin_id in pseudoatom_loop(pseudospin, return_id=True): 
 926               
 927              if spin.select: 
 928                  warn(RelaxWarning("Deselecting the '%s' spin as it is a member of the '%s' pseudo-atom system." % (spin_id, pseudospin_id))) 
 929                  spin.select = False 
  930   
 931   
 932 -def structural_noise(align_id=None, rmsd=0.2, sim_num=1000, file=None, dir=None, force=False): 
  933      """Determine the PCS error due to structural noise via simulation. 
 934   
 935      For the simulation the following must already be set up in the current data pipe: 
 936   
 937          - The position of the paramagnetic centre. 
 938          - The alignment and magnetic susceptibility tensor. 
 939   
 940      The protocol for the simulation is as follows: 
 941   
 942          - The lanthanide or paramagnetic centre position will be fixed.  Its motion is assumed to be on the femto- to pico- and nanosecond timescales.  Hence the motion is averaged over the evolution of the PCS and can be ignored. 
 943          - The positions of the nuclear spins will be randomised N times.  For each simulation a random unit vector will be generated.  Then a random distance along the unit vector will be generated by sampling from a Gaussian distribution centered at zero, the original spin position, with a standard deviation set to the given RMSD.  Both positive and negative displacements will be used. 
 944          - The PCS for the randomised position will be back calculated. 
 945          - The PCS standard deviation will be calculated from the N randomised PCS values. 
 946   
 947      The standard deviation will both be stored in the spin container data structure in the relax data store as well as being added to the already present PCS error (using variance addition).  This will then be used in any optimisations involving the PCS. 
 948   
 949      If the alignment ID string is not supplied, the procedure will be applied to the PCS data from all alignments. 
 950   
 951   
 952      @keyword align_id:  The alignment tensor ID string. 
 953      @type align_id:     str 
 954      @keyword rmsd:      The atomic position RMSD, in Angstrom, to randomise the spin positions with for the simulations. 
 955      @type rmsd:         float 
 956      @keyword sim_num:   The number of simulations, N, to perform to determine the structural noise component of the PCS errors. 
 957      @type sim_num:      int 
 958      @keyword file:      The optional name of the Grace file to plot the structural errors verses the paramagnetic centre to spin distances. 
 959      @type file:         None or str 
 960      @keyword dir:       The directory name to place the Grace file into. 
 961      @type dir:          None or str 
 962      @keyword force:     A flag which if True will cause any pre-existing file to be overwritten. 
 963      @type force:        bool 
 964      """ 
 965   
 966       
 967      check_pipe_setup(sequence=True, pcs_id=align_id, pcs=True, paramag_centre=True) 
 968   
 969       
 970      if align_id: 
 971          align_ids = [align_id] 
 972      else: 
 973          align_ids = cdp.align_ids 
 974   
 975       
 976      grace_data = [] 
 977      unit_vect = zeros(3, float64) 
 978      pcs = {} 
 979      for id in align_ids: 
 980          grace_data.append([]) 
 981          pcs[id] = zeros(sim_num, float64) 
 982   
 983       
 984      print("Executing %i simulations for each spin system." % sim_num) 
 985   
 986       
 987      for spin, spin_id in spin_loop(return_id=True): 
 988           
 989          if not spin.select: 
 990              continue 
 991   
 992           
 993          if not hasattr(spin, 'pcs'): 
 994              continue 
 995          if not hasattr(spin, 'pos'): 
 996              continue 
 997   
 998           
 999          print(spin_id) 
1000   
1001           
1002          if type(spin.pos[0]) in [float, float64]: 
1003              pos = spin.pos 
1004          else: 
1005              pos = zeros(3, float64) 
1006              for i in range(len(spin.pos)): 
1007                  pos += spin.pos[i] 
1008              pos = pos / len(spin.pos) 
1009   
1010           
1011          orig_vect = pos - cdp.paramagnetic_centre 
1012          orig_r = norm(orig_vect) 
1013   
1014           
1015          for i in range(sim_num): 
1016               
1017              random_unit_vector(unit_vect) 
1018   
1019               
1020              disp = gauss(0, rmsd) 
1021   
1022               
1023              new_pos = pos + disp*unit_vect 
1024   
1025               
1026              vect = new_pos - cdp.paramagnetic_centre 
1027              r = norm(vect) 
1028              vect = vect / r 
1029   
1030               
1031              for id in align_ids: 
1032                   
1033                  if id not in spin.pcs: 
1034                      continue 
1035   
1036                   
1037                  dj = pcs_constant(cdp.temperature[id], cdp.spectrometer_frq[id] * 2.0 * pi / g1H, r/1e10) 
1038   
1039                   
1040                  pcs[id][i] = pcs_tensor(dj, vect, cdp.align_tensors[get_tensor_index(id)].A) * 1e6 
1041   
1042           
1043          if not hasattr(spin, 'pcs_struct_err'): 
1044              spin.pcs_struct_err = {} 
1045   
1046           
1047          align_index = 0 
1048          for id in align_ids: 
1049               
1050              if id not in spin.pcs: 
1051                  align_index += 1 
1052                  continue 
1053   
1054               
1055              sd = std(pcs[id]) 
1056   
1057               
1058              if id in spin.pcs_struct_err: 
1059                  warn(RelaxWarning("Removing the previous structural error value from the PCS error of the spin '%s' for the alignment ID '%s'." % (spin_id, id))) 
1060                  spin.pcs_err[id] = sqrt(spin.pcs_err[id]**2 - spin.pcs_struct_err[id]**2) 
1061   
1062               
1063              spin.pcs_struct_err[id] = sd 
1064   
1065               
1066              spin.pcs_err[id] = sqrt(spin.pcs_err[id]**2 + sd**2) 
1067   
1068               
1069              grace_data[align_index].append([orig_r, sd]) 
1070   
1071               
1072              align_index += 1 
1073   
1074       
1075      if file: 
1076           
1077          file = open_write_file(file, dir, force) 
1078   
1079           
1080          grace.write_xy_header(file=file, title="PCS structural noise", subtitle="%s Angstrom structural noise"%rmsd, data_type=['pcs_bc', 'pcs'], sets=[len(align_ids)], set_names=[align_ids], symbol_sizes=[[0.5]*len(align_ids)], linetype=[[0]*len(align_ids)], axis_labels=[["Ln\\S3+\\N to spin distance (Angstrom)", "PCS standard deviation (ppm)"]]) 
1081   
1082           
1083          grace.write_xy_data(data=[grace_data], file=file, graph_type='xy') 
 1084   
1085   
1086 -def weight(align_id=None, spin_id=None, weight=1.0): 
 1087      """Set optimisation weights on the PCS data. 
1088   
1089      @keyword align_id:  The alignment tensor ID string. 
1090      @type align_id:     str 
1091      @keyword spin_id:   The spin ID string. 
1092      @type spin_id:      None or str 
1093      @keyword weight:    The optimisation weight.  The higher the value, the more importance the PCS will have. 
1094      @type weight:       float or int. 
1095      """ 
1096   
1097       
1098      check_pipe_setup(sequence=True, pcs_id=align_id, pcs=True) 
1099   
1100       
1101      for spin in spin_loop(spin_id): 
1102           
1103          if not hasattr(spin, 'pcs_weight'): 
1104              spin.pcs_weight = {} 
1105   
1106           
1107          spin.pcs_weight[align_id] = weight 
 1108   
1109   
1110 -def write(align_id=None, file=None, dir=None, bc=False, force=False): 
 1111      """Display the PCS data corresponding to the alignment ID. 
1112   
1113      @keyword align_id:  The alignment tensor ID string. 
1114      @type align_id:     str 
1115      @keyword file:      The file name or object to write to. 
1116      @type file:         str or file object 
1117      @keyword dir:       The name of the directory to place the file into (defaults to the current directory). 
1118      @type dir:          str 
1119      @keyword bc:        The back-calculation flag which if True will cause the back-calculated rather than measured data to be written. 
1120      @type bc:           bool 
1121      @keyword force:     A flag which if True will cause any pre-existing file to be overwritten. 
1122      @type force:        bool 
1123      """ 
1124   
1125       
1126      check_pipe_setup(sequence=True, pcs_id=align_id, pcs=True) 
1127   
1128       
1129      file = open_write_file(file, dir, force) 
1130   
1131       
1132      mol_names = [] 
1133      res_nums = [] 
1134      res_names = [] 
1135      spin_nums = [] 
1136      spin_names = [] 
1137      values = [] 
1138      errors = [] 
1139      for spin, mol_name, res_num, res_name in spin_loop(full_info=True): 
1140           
1141          if not spin.select: 
1142              continue 
1143   
1144           
1145          if not bc and (not hasattr(spin, 'pcs') or not align_id in spin.pcs.keys()): 
1146              continue 
1147          elif bc and (not hasattr(spin, 'pcs_bc') or align_id not in spin.pcs_bc.keys()): 
1148              continue 
1149   
1150           
1151          mol_names.append(mol_name) 
1152          res_nums.append(res_num) 
1153          res_names.append(res_name) 
1154          spin_nums.append(spin.num) 
1155          spin_names.append(spin.name) 
1156   
1157           
1158          if bc: 
1159              values.append(spin.pcs_bc[align_id]) 
1160          else: 
1161              values.append(spin.pcs[align_id]) 
1162   
1163           
1164          if hasattr(spin, 'pcs_err') and align_id in spin.pcs_err.keys(): 
1165              errors.append(spin.pcs_err[align_id]) 
1166          else: 
1167              errors.append(None) 
1168   
1169       
1170      write_spin_data(file=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='PCSs', error=errors, error_name='PCS_error') 
 1171