1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 from minfx.generic import generic_minimise
24 from numpy import array, float64, zeros
25 from numpy.linalg import norm
26 from os import F_OK, access, getcwd
27 from re import search
28 import sys
29 from warnings import warn
30
31
32 from lib.errors import RelaxError, RelaxFileError, RelaxNoPdbError, RelaxNoSequenceError
33 from lib.io import get_file_path, open_write_file, write_data
34 from lib.sequence import write_spin_data
35 from lib.structure.internal.displacements import Displacements
36 from lib.structure.internal.object import Internal
37 from lib.structure.represent.diffusion_tensor import diffusion_tensor
38 from lib.structure.statistics import atomic_rmsd
39 from lib.structure.superimpose import fit_to_first, fit_to_mean
40 from lib.warnings import RelaxWarning, RelaxNoPDBFileWarning, RelaxZeroVectorWarning
41 from pipe_control import molmol, pipes
42 from pipe_control.interatomic import interatomic_loop
43 from pipe_control.mol_res_spin import create_spin, exists_mol_res_spin_data, generate_spin_id_unique, linear_ave, return_spin, spin_loop
44 from pipe_control.structure.mass import pipe_centre_of_mass
45 from status import Status; status = Status()
46 from target_functions.ens_pivot_finder import Pivot_finder
47
48
49 -def add_atom(mol_name=None, atom_name=None, res_name=None, res_num=None, pos=[None, None, None], element=None, atom_num=None, chain_id=None, segment_id=None, pdb_record=None):
50 """Add a new atom to the structural data object.
51
52 @keyword mol_name: The name of the molecule.
53 @type mol_name: str
54 @keyword atom_name: The atom name, e.g. 'H1'.
55 @type atom_name: str or None
56 @keyword res_name: The residue name.
57 @type res_name: str or None
58 @keyword res_num: The residue number.
59 @type res_num: int or None
60 @keyword pos: The position vector of coordinates. If a rank-2 array is supplied, the length of the first dimension must match the number of models.
61 @type pos: rank-1 or rank-2 array or list of float
62 @keyword element: The element symbol.
63 @type element: str or None
64 @keyword atom_num: The atom number.
65 @type atom_num: int or None
66 @keyword chain_id: The chain identifier.
67 @type chain_id: str or None
68 @keyword segment_id: The segment identifier.
69 @type segment_id: str or None
70 @keyword pdb_record: The optional PDB record name, e.g. 'ATOM' or 'HETATM'.
71 @type pdb_record: str or None
72 """
73
74
75 pipes.test()
76
77
78 if not hasattr(cdp, 'structure'):
79 cdp.structure = Internal()
80
81
82 cdp.structure.add_atom(mol_name=mol_name, atom_name=atom_name, res_name=res_name, res_num=res_num, pos=pos, element=element, atom_num=atom_num, chain_id=chain_id, segment_id=segment_id, pdb_record=pdb_record)
83
84
86 """Add a new model to the empty structural data object."""
87
88
89 pipes.test()
90
91
92 if not hasattr(cdp, 'structure'):
93 cdp.structure = Internal()
94
95
96 if cdp.structure.num_molecules() != 0:
97 raise RelaxError("The internal structural object is not empty.")
98
99
100 cdp.structure.structural_data.add_item(model_num=model_num)
101 print("Created the empty model number %s." % model_num)
102
103
105 """Connect two atoms.
106
107 @keyword index1: The global index of the first atom.
108 @type index1: str
109 @keyword index2: The global index of the first atom.
110 @type index2: str
111 """
112
113
114 pipes.test()
115
116
117 if not hasattr(cdp, 'structure'):
118 cdp.structure = Internal()
119
120
121 cdp.structure.connect_atom(index1=index1, index2=index2)
122
123
136
137
139 """Create the PDB representation of the diffusion tensor.
140
141 @keyword scale: The scaling factor for the diffusion tensor.
142 @type scale: float
143 @keyword file: The name of the PDB file to create.
144 @type file: str
145 @keyword dir: The name of the directory to place the PDB file into.
146 @type dir: str
147 @keyword force: Flag which if set to True will overwrite any pre-existing file.
148 @type force: bool
149 """
150
151
152 pipes.test()
153
154
155 com = pipe_centre_of_mass()
156
157
158 structure = Internal()
159
160
161 if cdp.pipe_type == 'hybrid':
162 pipe_list = cdp.hybrid_pipes
163 else:
164 pipe_list = [pipes.cdp_name()]
165
166
167 if cdp.pipe_type == 'hybrid':
168 mol_names = []
169 for pipe in pipe_list:
170 mol_names.append('diff_tensor_' % pipe)
171 else:
172 mol_names = ['diff_tensor']
173
174
175 for pipe_index in range(len(pipe_list)):
176
177 pipe = pipes.get_pipe(pipe_list[pipe_index])
178
179
180 if not hasattr(pipe, 'diff_tensor'):
181 raise RelaxNoTensorError('diffusion')
182
183
184 if not hasattr(cdp, 'structure'):
185 raise RelaxNoPdbError
186
187
188 structure.add_molecule(name=mol_names[pipe_index])
189
190
191 mol = structure.get_molecule(mol_names[pipe_index])
192
193
194 diff_type = pipe.diff_tensor.type
195 if diff_type == 'spheroid':
196 diff_type = pipe.diff_tensor.spheroid_type
197
198
199 sim_num = None
200 if hasattr(pipe.diff_tensor, 'tm_sim'):
201
202 sim_num = len(pipe.diff_tensor.tm_sim)
203
204
205 axes = []
206 sim_axes = []
207 if diff_type in ['oblate', 'prolate']:
208 axes.append(pipe.diff_tensor.Dpar * pipe.diff_tensor.Dpar_unit)
209 sim_axes.append([])
210 if sim_num != None:
211 for i in range(sim_num):
212 sim_axes[0].append(pipe.diff_tensor.Dpar_sim[i] * pipe.diff_tensor.Dpar_unit_sim[i])
213
214 if diff_type == 'ellipsoid':
215 axes.append(pipe.diff_tensor.Dx * pipe.diff_tensor.Dx_unit)
216 axes.append(pipe.diff_tensor.Dy * pipe.diff_tensor.Dy_unit)
217 axes.append(pipe.diff_tensor.Dz * pipe.diff_tensor.Dz_unit)
218 sim_axes.append([])
219 sim_axes.append([])
220 sim_axes.append([])
221 if sim_num != None:
222 for i in range(sim_num):
223 sim_axes[0].append(pipe.diff_tensor.Dx_sim[i] * pipe.diff_tensor.Dx_unit_sim[i])
224 sim_axes[1].append(pipe.diff_tensor.Dy_sim[i] * pipe.diff_tensor.Dy_unit_sim[i])
225 sim_axes[2].append(pipe.diff_tensor.Dz_sim[i] * pipe.diff_tensor.Dz_unit_sim[i])
226
227
228 diffusion_tensor(mol=mol, tensor=pipe.diff_tensor.tensor, tensor_diag=pipe.diff_tensor.tensor_diag, diff_type=diff_type, rotation=pipe.diff_tensor.rotation, axes=axes, sim_axes=sim_axes, com=com, scale=scale)
229
230
231
232
233
234
235 print("\nGenerating the PDB file.")
236
237
238 tensor_pdb_file = open_write_file(file, dir, force=force)
239 structure.write_pdb(tensor_pdb_file)
240 tensor_pdb_file.close()
241
242
243 if not hasattr(cdp, 'result_files'):
244 cdp.result_files = []
245 if dir == None:
246 dir = getcwd()
247 cdp.result_files.append(['diff_tensor_pdb', 'Diffusion tensor PDB', get_file_path(file, dir)])
248 status.observers.result_file.notify()
249
250
251 -def delete(atom_id=None, verbosity=1, spin_info=True):
252 """Delete structural data.
253
254 @keyword atom_id: The molecule, residue, and atom identifier string. This matches the spin ID string format. If not given, then all structural data will be deleted.
255 @type atom_id: str or None
256 @keyword verbosity: The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1.
257 @type verbosity: int
258 @keyword spin_info: A flag which if True will cause all structural information in the spin containers and interatomic data containers to be deleted as well. If False, then only the 3D structural data will be deleted.
259 @type spin_info: bool
260 """
261
262
263 pipes.test()
264
265
266 if hasattr(cdp, 'structure'):
267 if verbosity:
268 print("Deleting structural data from the current pipe.")
269 cdp.structure.delete(atom_id=atom_id, verbosity=verbosity)
270 elif verbosity:
271 print("No structures are present.")
272
273
274 if not spin_info:
275 return
276
277
278 if verbosity:
279 print("Deleting all spin specific structural info.")
280 for spin in spin_loop(selection=atom_id):
281
282 if hasattr(spin, 'pos'):
283 del spin.pos
284
285
286 if verbosity:
287 print("Deleting all interatomic vectors.")
288 for interatom in interatomic_loop(selection1=atom_id):
289
290 if hasattr(interatom, 'vector'):
291 del interatom.vector
292
293
294 -def displacement(model_from=None, model_to=None, atom_id=None, centroid=None):
295 """Calculate the rotational and translational displacement between two structural models.
296
297 @keyword model_from: The optional model number for the starting position of the displacement.
298 @type model_from: int or None
299 @keyword model_to: The optional model number for the ending position of the displacement.
300 @type model_to: int or None
301 @keyword atom_id: The molecule, residue, and atom identifier string. This matches the spin ID string format.
302 @type atom_id: str or None
303 @keyword centroid: An alternative position of the centroid, used for studying pivoted systems.
304 @type centroid: list of float or numpy rank-1, 3D array
305 """
306
307
308 pipes.test()
309
310
311 if model_from != None:
312 model_from = [model_from]
313 if model_to != None:
314 model_to = [model_to]
315
316
317 models = []
318 for model in cdp.structure.model_loop():
319 models.append(model.num)
320
321
322 if model_from == None:
323 model_from = models
324 if model_to == None:
325 model_to = models
326
327
328 if not hasattr(cdp.structure, 'displacements'):
329 cdp.structure.displacements = Displacements()
330
331
332 for i in range(len(model_from)):
333
334 coord_from = []
335 for pos in cdp.structure.atom_loop(atom_id=atom_id, model_num=model_from[i], pos_flag=True):
336 coord_from.append(pos[0])
337
338
339 for j in range(len(model_to)):
340
341 coord_to = []
342 for pos in cdp.structure.atom_loop(atom_id=atom_id, model_num=model_to[j], pos_flag=True):
343 coord_to.append(pos[0])
344
345
346 cdp.structure.displacements._calculate(model_from=model_from[i], model_to=model_to[j], coord_from=array(coord_from), coord_to=array(coord_to), centroid=centroid)
347
348
349 -def find_pivot(models=None, atom_id=None, init_pos=None, func_tol=1e-5, box_limit=200):
350 """Superimpose a set of structural models.
351
352 @keyword models: The list of models to use. If set to None, then all models will be used.
353 @type models: list of int or None
354 @keyword atom_id: The molecule, residue, and atom identifier string. This matches the spin ID string format.
355 @type atom_id: str or None
356 @keyword init_pos: The starting pivot position for the pivot point optimisation.
357 @type init_pos: list of float or numpy rank-1, 3D array
358 @keyword func_tol: The function tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
359 @type func_tol: None or float
360 @keyword box_limit: The simplex optimisation used in this function is constrained withing a box of +/- x Angstrom containing the pivot point using the logarithmic barrier function. This argument is the value of x.
361 @type box_limit: int
362 """
363
364
365 pipes.test()
366
367
368 if init_pos == None:
369 init_pos = zeros(3, float64)
370 init_pos = array(init_pos)
371
372
373 cdp.structure.validate_models()
374
375
376 if models == None:
377 models = []
378 for model in cdp.structure.model_loop():
379 models.append(model.num)
380
381
382 coord = []
383 for model in models:
384 coord.append([])
385 for pos in cdp.structure.atom_loop(atom_id=atom_id, model_num=model, pos_flag=True):
386 coord[-1].append(pos[0])
387 coord[-1] = array(coord[-1])
388 coord = array(coord)
389
390
391 A = zeros((6, 3), float64)
392 b = zeros(6, float64)
393 for i in range(3):
394 A[2*i, i] = 1
395 A[2*i+1, i] = -1
396 b[2*i] = -box_limit
397 b[2*i+1] = -box_limit
398
399
400 finder = Pivot_finder(models, coord)
401 results = generic_minimise(func=finder.func, x0=init_pos, min_algor='Log barrier', min_options=('simplex',), A=A, b=b, func_tol=func_tol, print_flag=1)
402
403
404 if results == None:
405 return
406
407
408 cdp.structure.pivot = results
409
410
411 print("Motional pivot found at: %s" % results)
412
413
414 -def get_pos(spin_id=None, str_id=None, ave_pos=False):
415 """Load the spins from the structural object into the relax data store.
416
417 @keyword spin_id: The molecule, residue, and spin identifier string.
418 @type spin_id: str
419 @keyword str_id: The structure identifier. This can be the file name, model number, or structure number.
420 @type str_id: int or str
421 @keyword ave_pos: A flag specifying if the average atom position or the atom position from all loaded structures is loaded into the SpinContainer.
422 @type ave_pos: bool
423 """
424
425
426 pipes.test()
427
428
429 if not hasattr(cdp, 'structure') or not cdp.structure.num_models() or not cdp.structure.num_molecules():
430 raise RelaxNoPdbError
431
432
433 data = []
434 for mol_name, res_num, res_name, atom_num, atom_name, element, pos in cdp.structure.atom_loop(atom_id=spin_id, str_id=str_id, mol_name_flag=True, res_num_flag=True, res_name_flag=True, atom_num_flag=True, atom_name_flag=True, element_flag=True, pos_flag=True, ave=ave_pos):
435
436 if mol_name and search('\+', mol_name):
437 mol_name = mol_name.replace('+', '')
438 if res_name and search('\+', res_name):
439 res_name = res_name.replace('+', '')
440 if atom_name and search('\+', atom_name):
441 atom_name = atom_name.replace('+', '')
442
443
444 id = generate_spin_id_unique(res_num=res_num, res_name=None, spin_num=atom_num, spin_name=atom_name)
445
446
447 spin_cont = return_spin(id)
448
449
450 if spin_cont == None:
451 continue
452
453
454 spin_cont.pos = pos
455
456
457 data.append([id, repr(pos)])
458
459
460 if not len(data):
461 raise RelaxError("No positional information matching the spin ID '%s' could be found." % spin_id)
462
463
464 for spin in spin_loop():
465 if hasattr(spin, 'members'):
466
467 positions = []
468 for atom in spin.members:
469
470 subspin = return_spin(atom)
471
472
473 if subspin == None:
474 raise RelaxNoSpinError(atom)
475
476
477 if not hasattr(subspin, 'pos') or subspin.pos == None or not len(subspin.pos):
478 raise RelaxError("Positional information is not available for the atom '%s'." % atom)
479
480
481 pos = subspin.pos
482
483
484 multi_model = True
485 if type(pos[0]) in [float, float64]:
486 multi_model = False
487 pos = [pos]
488
489
490 positions.append([])
491 for i in range(len(pos)):
492 positions[-1].append(pos[i].tolist())
493
494
495 if spin.averaging == 'linear':
496
497 ave = linear_ave(positions)
498
499
500 if multi_model:
501 spin.pos = ave
502 else:
503 spin.pos = ave[0]
504
505
506 write_data(out=sys.stdout, headings=["Spin_ID", "Position"], data=data)
507
508
509 -def load_spins(spin_id=None, str_id=None, mol_name_target=None, ave_pos=False):
510 """Load the spins from the structural object into the relax data store.
511
512 @keyword spin_id: The molecule, residue, and spin identifier string.
513 @type spin_id: str
514 @keyword str_id: The structure identifier. This can be the file name, model number, or structure number.
515 @type str_id: int or str
516 @keyword mol_name_target: The name of target molecule container, overriding the name of the loaded structures
517 @type mol_name_target: str or None
518 @keyword ave_pos: A flag specifying if the average atom position or the atom position from all loaded structures is loaded into the SpinContainer.
519 @type ave_pos: bool
520 """
521
522
523 pipes.test()
524
525
526 if not hasattr(cdp, 'structure') or not cdp.structure.num_models() or not cdp.structure.num_molecules():
527 raise RelaxNoPdbError
528
529
530 print("Adding the following spins to the relax data store.\n")
531
532
533 mol_names = []
534 res_nums = []
535 res_names = []
536 spin_nums = []
537 spin_names = []
538
539
540 for mol_name, res_num, res_name, atom_num, atom_name, element, pos in cdp.structure.atom_loop(atom_id=spin_id, str_id=str_id, mol_name_flag=True, res_num_flag=True, res_name_flag=True, atom_num_flag=True, atom_name_flag=True, element_flag=True, pos_flag=True, ave=ave_pos):
541
542 if mol_name_target:
543 mol_name = mol_name_target
544
545
546 if mol_name and search('\+', mol_name):
547 mol_name = mol_name.replace('+', '')
548 if res_name and search('\+', res_name):
549 res_name = res_name.replace('+', '')
550 if atom_name and search('\+', atom_name):
551 atom_name = atom_name.replace('+', '')
552
553
554 id = generate_spin_id_unique(mol_name=mol_name, res_num=res_num, res_name=res_name, spin_num=atom_num, spin_name=atom_name)
555
556
557 try:
558 spin_cont = create_spin(mol_name=mol_name, res_num=res_num, res_name=res_name, spin_num=atom_num, spin_name=atom_name)
559
560
561 except RelaxError:
562 spin_cont = return_spin(id)
563
564
565 if mol_name_target:
566 mol_names.append(mol_name_target)
567 else:
568 mol_names.append(mol_name)
569 res_nums.append(res_num)
570 res_names.append(res_name)
571 spin_nums.append(atom_num)
572 spin_names.append(atom_name)
573
574
575 spin_cont.pos = pos
576
577
578 spin_cont.element = element
579
580
581 if len(mol_names) == 0:
582 warn(RelaxWarning("No spins matching the '%s' ID string could be found." % spin_id))
583 return
584
585
586 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)
587
588
589 -def read_gaussian(file=None, dir=None, set_mol_name=None, set_model_num=None, verbosity=1, fail=True):
590 """Read structures from a Gaussian log file.
591
592
593 @keyword file: The name of the Gaussian log file to read.
594 @type file: str
595 @keyword dir: The directory where the Gaussian log file is located. If set to None, then the file will be searched for in the current directory.
596 @type dir: str or None
597 @keyword set_mol_name: Set the names of the molecules which are loaded. If set to None, then the molecules will be automatically labelled based on the file name or other information.
598 @type set_mol_name: None, str, or list of str
599 @keyword set_model_num: Set the model number of the loaded molecule.
600 @type set_model_num: None, int, or list of int
601 @keyword fail: A flag which, if True, will cause a RelaxError to be raised if the Gaussian log file does not exist. If False, then a RelaxWarning will be trown instead.
602 @type fail: bool
603 @keyword verbosity: The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1.
604 @type verbosity: int
605 @raise RelaxFileError: If the fail flag is set, then a RelaxError is raised if the Gaussian log file does not exist.
606 """
607
608
609 pipes.test()
610
611
612 file_path = get_file_path(file, dir)
613
614
615 if not access(file_path, F_OK):
616 file_path_orig = file_path
617 file_path = file_path + '.log'
618
619
620 if not access(file_path, F_OK):
621 if fail:
622 raise RelaxFileError('Gaussian log', file_path_orig)
623 else:
624 warn(RelaxNoPDBFileWarning(file_path))
625 return
626
627
628 if not hasattr(cdp, 'structure'):
629 cdp.structure = Internal()
630
631
632 cdp.structure.load_gaussian(file_path, set_mol_name=set_mol_name, set_model_num=set_model_num, verbosity=verbosity)
633
634
635 -def read_pdb(file=None, dir=None, read_mol=None, set_mol_name=None, read_model=None, set_model_num=None, alt_loc=None, verbosity=1, merge=False, fail=True):
636 """The PDB loading function.
637
638 @keyword file: The name of the PDB file to read.
639 @type file: str
640 @keyword dir: The directory where the PDB file is located. If set to None, then the file will be searched for in the current directory.
641 @type dir: str or None
642 @keyword read_mol: The molecule(s) to read from the file, independent of model. The molecules are numbered consecutively from 1. If set to None, then all molecules will be loaded.
643 @type read_mol: None, int, or list of int
644 @keyword set_mol_name: Set the names of the molecules which are loaded. If set to None, then the molecules will be automatically labelled based on the file name or other information.
645 @type set_mol_name: None, str, or list of str
646 @keyword read_model: The PDB model to extract from the file. If set to None, then all models will be loaded.
647 @type read_model: None, int, or list of int
648 @keyword set_model_num: Set the model number of the loaded molecule. If set to None, then the PDB model numbers will be preserved, if they exist.
649 @type set_model_num: None, int, or list of int
650 @keyword alt_loc: The PDB ATOM record 'Alternate location indicator' field value to select which coordinates to use.
651 @type alt_loc: str or None
652 @keyword verbosity: The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1.
653 @type verbosity: int
654 @keyword merge: A flag which if set to True will try to merge the PDB structure into the currently loaded structures.
655 @type merge: bool
656 @keyword fail: A flag which, if True, will cause a RelaxError to be raised if the PDB file does not exist. If False, then a RelaxWarning will be trown instead.
657 @type fail: bool
658 @raise RelaxFileError: If the fail flag is set, then a RelaxError is raised if the PDB file does not exist.
659 """
660
661
662 pipes.test()
663
664
665 file_path = get_file_path(file, dir)
666
667
668 file_path_orig = file_path
669 if not access(file_path, F_OK):
670
671 for ext in ['.pdb', '.gz', '.pdb.gz', '.bz2', '.pdb.bz2']:
672
673 if access(file_path+ext, F_OK):
674 file_path = file_path + ext
675
676
677 if not access(file_path, F_OK):
678 if fail:
679 raise RelaxFileError('PDB', file_path_orig)
680 else:
681 warn(RelaxNoPDBFileWarning(file_path))
682 return
683
684
685 if not hasattr(cdp, 'structure'):
686 cdp.structure = Internal()
687
688
689 cdp.structure.load_pdb(file_path, read_mol=read_mol, set_mol_name=set_mol_name, read_model=read_model, set_model_num=set_model_num, alt_loc=alt_loc, verbosity=verbosity, merge=merge)
690
691
692 molmol.molmol_obj.open_pdb()
693
694
695 -def read_xyz(file=None, dir=None, read_mol=None, set_mol_name=None, read_model=None, set_model_num=None, verbosity=1, fail=True):
696 """The XYZ loading function.
697
698
699 @keyword file: The name of the XYZ file to read.
700 @type file: str
701 @keyword dir: The directory where the XYZ file is located. If set to None, then the
702 file will be searched for in the current directory.
703 @type dir: str or None
704 @keyword read_mol: The molecule(s) to read from the file, independent of model.
705 If set to None, then all molecules will be loaded.
706 @type read_mol: None, int, or list of int
707 @keyword set_mol_name: Set the names of the molecules which are loaded. If set to None, then
708 the molecules will be automatically labelled based on the file name or
709 other information.
710 @type set_mol_name: None, str, or list of str
711 @keyword read_model: The XYZ model to extract from the file. If set to None, then all models
712 will be loaded.
713 @type read_model: None, int, or list of int
714 @keyword set_model_num: Set the model number of the loaded molecule. If set to None, then the
715 XYZ model numbers will be preserved, if they exist.
716 @type set_model_num: None, int, or list of int
717 @keyword fail: A flag which, if True, will cause a RelaxError to be raised if the XYZ
718 file does not exist. If False, then a RelaxWarning will be trown
719 instead.
720 @type fail: bool
721 @keyword verbosity: The amount of information to print to screen. Zero corresponds to
722 minimal output while higher values increase the amount of output. The
723 default value is 1.
724 @type verbosity: int
725 @raise RelaxFileError: If the fail flag is set, then a RelaxError is raised if the XYZ file
726 does not exist.
727 """
728
729
730 pipes.test()
731
732
733 file_path = get_file_path(file, dir)
734
735
736 if not access(file_path, F_OK):
737 file_path_orig = file_path
738 file_path = file_path + '.xyz'
739
740
741 if not access(file_path, F_OK):
742 if fail:
743 raise RelaxFileError('XYZ', file_path_orig)
744 else:
745 warn(RelaxNoPDBFileWarning(file_path))
746 return
747
748
749 if not hasattr(cdp, 'structure'):
750 cdp.structure = Internal()
751
752
753 cdp.structure.load_xyz(file_path, read_mol=read_mol, set_mol_name=set_mol_name, read_model=read_model, set_model_num=set_model_num, verbosity=verbosity)
754
755
756 -def rmsd(atom_id=None, models=None):
757 """Calculate the RMSD between the loaded models.
758
759 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be used.
760 @type atom_id: str or None
761 @keyword models: The list of models to calculate the RMDS of. If set to None, then all models will be used.
762 @type models: list of int or None
763 @return: The RMSD value.
764 @rtype: float
765 """
766
767
768 pipes.test()
769
770
771 if models == None:
772 models = []
773 for model in cdp.structure.model_loop():
774 models.append(model.num)
775
776
777 coord = []
778 for model in models:
779 coord.append([])
780 for pos in cdp.structure.atom_loop(atom_id=atom_id, model_num=model, pos_flag=True):
781 coord[-1].append(pos[0])
782 coord[-1] = array(coord[-1])
783
784
785 cdp.structure.rmsd = atomic_rmsd(coord, verbosity=1)
786
787
788 return cdp.structure.rmsd
789
790
791 -def rotate(R=None, origin=None, model=None, atom_id=None):
792 """Rotate the structural data about the origin by the specified forwards rotation.
793
794 @keyword R: The forwards rotation matrix.
795 @type R: numpy 3D, rank-2 array or a 3x3 list of floats
796 @keyword origin: The origin of the rotation. If not supplied, the origin will be set to [0, 0, 0].
797 @type origin: numpy 3D, rank-1 array or list of len 3 or None
798 @keyword model: The model to rotate. If None, all models will be rotated.
799 @type model: int
800 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be used.
801 @type atom_id: str or None
802 """
803
804
805 pipes.test()
806
807
808 if not hasattr(cdp, 'structure') or not cdp.structure.num_models() or not cdp.structure.num_molecules():
809 raise RelaxNoPdbError
810
811
812 if origin == None:
813 origin = [0, 0, 0]
814
815
816 R = array(R, float64)
817 origin = array(origin, float64)
818
819
820 cdp.structure.rotate(R=R, origin=origin, model=model, atom_id=atom_id)
821
822
824 """Place the XH unit vector into the spin container object.
825
826 @keyword spin: The spin container object.
827 @type spin: SpinContainer instance
828 @keyword xh_vect: The unit vector parallel to the XH bond.
829 @type xh_vect: array of len 3
830 """
831
832
833 spin.xh_vect = xh_vect
834
835
836 -def superimpose(models=None, method='fit to mean', atom_id=None, centre_type="centroid", centroid=None):
837 """Superimpose a set of structural models.
838
839 @keyword models: The list of models to superimpose. If set to None, then all models will be used.
840 @type models: list of int or None
841 @keyword method: The superimposition method. It must be one of 'fit to mean' or 'fit to first'.
842 @type method: str
843 @keyword atom_id: The molecule, residue, and atom identifier string. This matches the spin ID string format.
844 @type atom_id: str or None
845 @keyword centre_type: The type of centre to superimpose over. This can either be the standard centroid superimposition or the CoM could be used instead.
846 @type centre_type: str
847 @keyword centroid: An alternative position of the centroid to allow for different superpositions, for example of pivot point motions.
848 @type centroid: list of float or numpy rank-1, 3D array
849 """
850
851
852 allowed = ['fit to mean', 'fit to first']
853 if method not in allowed:
854 raise RelaxError("The superimposition method '%s' is unknown. It must be one of %s." % (method, allowed))
855
856
857 allowed = ['centroid', 'CoM']
858 if centre_type not in allowed:
859 raise RelaxError("The superimposition centre type '%s' is unknown. It must be one of %s." % (centre_type, allowed))
860
861
862 pipes.test()
863
864
865 cdp.structure.validate_models()
866
867
868 if models == None:
869 models = []
870 for model in cdp.structure.model_loop():
871 models.append(model.num)
872
873
874 coord = []
875 for model in models:
876 coord.append([])
877 for pos in cdp.structure.atom_loop(atom_id=atom_id, model_num=model, pos_flag=True):
878 coord[-1].append(pos[0])
879 coord[-1] = array(coord[-1])
880
881
882 elements = []
883 for elem in cdp.structure.atom_loop(atom_id=atom_id, model_num=model, element_flag=True):
884 elements.append(elem)
885
886
887 if method == 'fit to mean':
888 T, R, pivot = fit_to_mean(models=models, coord=coord, centre_type=centre_type, elements=elements, centroid=centroid)
889 elif method == 'fit to first':
890 T, R, pivot = fit_to_first(models=models, coord=coord, centre_type=centre_type, elements=elements, centroid=centroid)
891
892
893
894 for i in range(len(models)):
895
896 translate(T=T[i], model=models[i])
897
898
899 rotate(R=R[i], origin=pivot[i], model=models[i])
900
901
902 -def translate(T=None, model=None, atom_id=None):
903 """Shift the structural data by the specified translation vector.
904
905 @keyword T: The translation vector.
906 @type T: numpy rank-1, 3D array or list of float
907 @keyword model: The model to translate. If None, all models will be rotated.
908 @type model: int or None
909 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be used.
910 @type atom_id: str or None
911 """
912
913
914 pipes.test()
915
916
917 if not hasattr(cdp, 'structure') or not cdp.structure.num_models() or not cdp.structure.num_molecules():
918 raise RelaxNoPdbError
919
920
921 T = array(T, float64)
922
923
924 cdp.structure.translate(T=T, model=model, atom_id=atom_id)
925
926
927 -def vectors(spin_id1=None, spin_id2=None, model=None, verbosity=1, ave=True, unit=True):
928 """Extract the bond vectors from the loaded structures and store them in the spin container.
929
930 @keyword spin_id1: The spin identifier string of the first spin of the pair.
931 @type spin_id1: str
932 @keyword spin_id2: The spin identifier string of the second spin of the pair.
933 @type spin_id2: str
934 @keyword model: The model to extract the vector from. If None, all vectors will be extracted.
935 @type model: str
936 @keyword verbosity: The higher the value, the more information is printed to screen.
937 @type verbosity: int
938 @keyword ave: A flag which if True will cause the average of all vectors to be extracted.
939 @type ave: bool
940 @keyword unit: A flag which if True will cause the function to calculate the unit vectors.
941 @type unit: bool
942 """
943
944
945 pipes.test()
946
947
948 if not hasattr(cdp, 'structure'):
949 raise RelaxNoPdbError
950
951
952 if not exists_mol_res_spin_data():
953 raise RelaxNoSequenceError
954
955
956 if verbosity:
957
958 num_models = cdp.structure.num_models()
959
960
961 if num_models > 1:
962 if model:
963 print("Extracting vectors for model '%s'." % model)
964 else:
965 print("Extracting vectors for all %s models." % num_models)
966 if ave:
967 print("Averaging all vectors.")
968
969
970 else:
971 print("Extracting vectors from the single model.")
972
973
974 if unit:
975 print("Calculating the unit vectors.")
976
977
978 no_vectors = True
979 for spin, mol_name, res_num, res_name in spin_loop(selection=spin_id, full_info=True):
980
981 if not spin.select:
982 continue
983
984
985 id = generate_spin_id_unique(res_num=res_num, res_name=None, spin_name=spin.name, spin_num=spin.num)
986
987
988 if spin.num == None and spin.name == None:
989 warn(RelaxWarning("Either the spin number or name must be set for the spin " + repr(id) + " to identify the corresponding atom in the molecule."))
990 continue
991
992
993 if hasattr(spin, 'vector'):
994 obj = getattr(spin, 'vector')
995 if obj != None:
996 warn(RelaxWarning("The bond vector for the spin " + repr(id) + " already exists."))
997 continue
998
999
1000 bond_vectors, attached_name, warnings = cdp.structure.bond_vectors(attached_atom=attached, model_num=model, res_num=res_num, spin_name=spin.name, spin_num=spin.num, return_name=True, return_warnings=True)
1001 id2 = generate_spin_id_unique(res_num=res_num, res_name=None, spin_name=spin.name)
1002
1003
1004 if not bond_vectors:
1005
1006 if warnings:
1007 warn(RelaxWarning(warnings + " (atom ID " + repr(id) + ")."))
1008
1009
1010 continue
1011
1012
1013 if not hasattr(spin, 'attached_atom'):
1014 spin.attached_atom = attached_name
1015 elif spin.attached_atom != attached_name:
1016 raise RelaxError("The " + repr(spin.attached_atom) + " atom already attached to the spin does not match the attached atom " + repr(attached_name) + ".")
1017
1018
1019 if ave:
1020 ave_vector = zeros(3, float64)
1021
1022
1023 for i in range(len(bond_vectors)):
1024
1025 if unit:
1026
1027 norm_factor = norm(bond_vectors[i])
1028
1029
1030 if norm_factor == 0.0:
1031 warn(RelaxZeroVectorWarning(spin_id1=id, spin_id2=id2))
1032
1033
1034 else:
1035 bond_vectors[i] = bond_vectors[i] / norm_factor
1036
1037
1038 if ave:
1039 ave_vector = ave_vector + bond_vectors[i]
1040
1041
1042 if ave:
1043 vector = ave_vector / float(len(bond_vectors))
1044 else:
1045 vector = bond_vectors
1046
1047
1048 if len(vector) == 1:
1049 vector = vector[0]
1050
1051
1052 setattr(spin, 'vector', vector)
1053
1054
1055 no_vectors = False
1056
1057
1058 if verbosity:
1059
1060 num = len(bond_vectors)
1061 plural = 's'
1062 if num == 1:
1063 plural = ''
1064
1065 if spin.name:
1066 print("Extracted %s %s-%s vector%s for the spin '%s'." % (num, spin.name, attached_name, plural, id))
1067 else:
1068 print("Extracted %s %s-%s vector%s for the spin '%s'." % (num, spin.num, attached_name, plural, id))
1069
1070
1071 if no_vectors:
1072 raise RelaxError("No vectors could be extracted.")
1073
1074
1075 -def web_of_motion(file=None, dir=None, models=None, force=False):
1076 """Create a PDB representation of the motion between a set of models.
1077
1078 This will create a PDB file containing the atoms of all models, with identical atoms links using CONECT records. This function only supports the internal structural object.
1079
1080 @keyword file: The name of the PDB file to write.
1081 @type file: str
1082 @keyword dir: The directory where the PDB file will be placed. If set to None, then the file will be placed in the current directory.
1083 @type dir: str or None
1084 @keyword models: The optional list of models to restrict this to.
1085 @type models: list of int or None
1086 @keyword force: The force flag which if True will cause the file to be overwritten.
1087 @type force: bool
1088 """
1089
1090
1091 pipes.test()
1092
1093
1094 if not hasattr(cdp, 'structure') or not cdp.structure.num_models() or not cdp.structure.num_molecules():
1095 raise RelaxNoPdbError
1096
1097
1098 cdp.structure.validate_models()
1099
1100
1101 web = Internal()
1102
1103
1104 if models == None:
1105 models = []
1106 for k in range(len(cdp.structure.structural_data)):
1107 models.append(cdp.structure.structural_data[k].num)
1108
1109
1110 for i in range(len(cdp.structure.structural_data[0].mol)):
1111
1112 mol1 = cdp.structure.structural_data[0].mol[i]
1113
1114
1115 for j in range(len(mol1.atom_name)):
1116
1117 for k in range(len(cdp.structure.structural_data)):
1118
1119 if cdp.structure.structural_data[k].num not in models:
1120 continue
1121
1122
1123 mol = cdp.structure.structural_data[k].mol[i]
1124
1125
1126 web.add_atom(mol_name=mol1.mol_name, atom_name=mol.atom_name[j], res_name=mol.res_name[j], res_num=mol.res_num[j], pos=[mol.x[j], mol.y[j], mol.z[j]], element=mol.element[j], chain_id=mol.chain_id[j], segment_id=mol.seg_id[j], pdb_record=mol.pdb_record[j])
1127
1128
1129 for k in range(len(models)):
1130 for l in range(len(models)):
1131
1132 if k == l:
1133 continue
1134
1135
1136 index1 = j*len(models) + k
1137 index2 = j*len(models) + l
1138
1139
1140 web.connect_atom(mol_name=mol1.mol_name, index1=index1, index2=index2)
1141
1142
1143 if isinstance(file, str):
1144
1145 file = get_file_path(file, dir)
1146
1147
1148 if not search(".pdb$", file):
1149 file += '.pdb'
1150
1151
1152 file = open_write_file(file, force=force)
1153
1154
1155 web.write_pdb(file)
1156
1157
1158 -def write_pdb(file=None, dir=None, model_num=None, compress_type=0, force=False):
1159 """The PDB writing function.
1160
1161 @keyword file: The name of the PDB file to write. This can also be a file instance.
1162 @type file: str or file instance
1163 @keyword dir: The directory where the PDB file will be placed. If set to None, then the file will be placed in the current directory.
1164 @type dir: str or None
1165 @keyword model_num: The model to place into the PDB file. If not supplied, then all models will be placed into the file.
1166 @type model_num: None or int
1167 @keyword compress_type: The compression type. The integer values correspond to the compression type: 0, no compression; 1, Bzip2 compression; 2, Gzip compression.
1168 @type compress_type: int
1169 @keyword force: The force flag which if True will cause the file to be overwritten.
1170 @type force: bool
1171 """
1172
1173
1174 pipes.test()
1175
1176
1177 if not hasattr(cdp, 'structure'):
1178 raise RelaxError("No structural data is present in the current data pipe.")
1179
1180
1181 if isinstance(file, str):
1182
1183 file = get_file_path(file, dir)
1184
1185
1186 if not search(".pdb$", file):
1187 file = file + '.pdb'
1188
1189
1190 file = open_write_file(file, compress_type=compress_type, force=force)
1191
1192
1193 cdp.structure.write_pdb(file, model_num=model_num)
1194