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