1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 from math import sqrt
24 from minfx.generic import generic_minimise
25 from numpy import array, dot, float64, ndarray, zeros
26 from numpy.linalg import norm
27 from os import F_OK, access
28 from re import search
29 import sys
30 from warnings import warn
31
32
33 from generic_fns import molmol, relax_re
34 from generic_fns.mol_res_spin import create_spin, exists_mol_res_spin_data, generate_spin_id, linear_ave, return_molecule, return_residue, return_spin, spin_loop
35 from generic_fns import pipes
36 from generic_fns.structure.api_base import Displacements
37 from generic_fns.structure.internal import Internal
38 from generic_fns.structure.scientific import Scientific_data
39 from generic_fns.structure.superimpose import fit_to_first, fit_to_mean, Pivot_finder
40 from relax_errors import RelaxError, RelaxFileError, RelaxNoPdbError, RelaxNoSequenceError
41 from relax_io import get_file_path, open_write_file, write_data, write_spin_data
42 from relax_warnings import RelaxWarning, RelaxNoPDBFileWarning, RelaxZeroVectorWarning
43
44
45 -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):
46 """Add a new atom to the structural data object.
47
48 @keyword mol_name: The name of the molecule.
49 @type mol_name: str
50 @keyword atom_name: The atom name, e.g. 'H1'.
51 @type atom_name: str or None
52 @keyword res_name: The residue name.
53 @type res_name: str or None
54 @keyword res_num: The residue number.
55 @type res_num: int or None
56 @keyword pos: The position vector of coordinates.
57 @type pos: list (length = 3)
58 @keyword element: The element symbol.
59 @type element: str or None
60 @keyword atom_num: The atom number.
61 @type atom_num: int or None
62 @keyword chain_id: The chain identifier.
63 @type chain_id: str or None
64 @keyword segment_id: The segment identifier.
65 @type segment_id: str or None
66 @keyword pdb_record: The optional PDB record name, e.g. 'ATOM' or 'HETATM'.
67 @type pdb_record: str or None
68 """
69
70
71 pipes.test()
72
73
74 if not hasattr(cdp, 'structure'):
75 cdp.structure = Internal()
76
77
78 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)
79
80
82 """Connect two atoms.
83
84 @keyword index1: The global index of the first atom.
85 @type index1: str
86 @keyword index2: The global index of the first atom.
87 @type index2: str
88 """
89
90
91 pipes.test()
92
93
94 if not hasattr(cdp, 'structure'):
95 cdp.structure = Internal()
96
97
98 cdp.structure.connect_atom(index1=index1, index2=index2)
99
100
102 """Simple function for deleting all structural data."""
103
104
105 pipes.test()
106
107
108 cdp.structure.delete()
109
110
111 for spin in spin_loop():
112
113 if hasattr(spin, 'pos'):
114 del spin.pos
115
116
117 if hasattr(spin, 'bond_vect'):
118 del spin.bond_vect
119 if hasattr(spin, 'xh_vect'):
120 del spin.xh_vect
121
122
123 -def displacement(model_from=None, model_to=None, atom_id=None, centroid=None):
124 """Calculate the rotational and translational displacement between two structural models.
125
126 @keyword model_from: The optional model number for the starting position of the displacement.
127 @type model_from: int or None
128 @keyword model_to: The optional model number for the ending position of the displacement.
129 @type model_to: int or None
130 @keyword atom_id: The molecule, residue, and atom identifier string. This matches the spin ID string format.
131 @type atom_id: str or None
132 @keyword centroid: An alternative position of the centroid, used for studying pivoted systems.
133 @type centroid: list of float or numpy rank-1, 3D array
134 """
135
136
137 pipes.test()
138
139
140 if model_from != None:
141 model_from = [model_from]
142 if model_to != None:
143 model_to = [model_to]
144
145
146 models = []
147 for model in cdp.structure.model_loop():
148 models.append(model.num)
149
150
151 if model_from == None:
152 model_from = models
153 if model_to == None:
154 model_to = models
155
156
157 if not hasattr(cdp.structure, 'displacements'):
158 cdp.structure.displacements = Displacements()
159
160
161 for i in range(len(model_from)):
162
163 coord_from = []
164 for pos in cdp.structure.atom_loop(atom_id=atom_id, model_num=model_from[i], pos_flag=True):
165 coord_from.append(pos[0])
166
167
168 for j in range(len(model_to)):
169
170 coord_to = []
171 for pos in cdp.structure.atom_loop(atom_id=atom_id, model_num=model_to[j], pos_flag=True):
172 coord_to.append(pos[0])
173
174
175 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)
176
177
178 -def find_pivot(models=None, atom_id=None, init_pos=None):
179 """Superimpose a set of structural models.
180
181 @keyword models: The list of models to use. If set to None, then all models will be used.
182 @type models: list of int or None
183 @keyword atom_id: The molecule, residue, and atom identifier string. This matches the spin ID string format.
184 @type atom_id: str or None
185 @keyword init_pos: The starting pivot position for the pivot point optimisation.
186 @type init_pos: list of float or numpy rank-1, 3D array
187 """
188
189
190 pipes.test()
191
192
193 if init_pos == None:
194 init_pos = zeros(3, float64)
195 init_pos = array(init_pos)
196
197
198 cdp.structure.validate_models()
199
200
201 if models == None:
202 models = []
203 for model in cdp.structure.model_loop():
204 models.append(model.num)
205
206
207 coord = []
208 for model in models:
209 coord.append([])
210 for pos in cdp.structure.atom_loop(atom_id=atom_id, model_num=model, pos_flag=True):
211 coord[-1].append(pos[0])
212 coord[-1] = array(coord[-1])
213 coord = array(coord)
214
215
216 finder = Pivot_finder(models, coord)
217 results = generic_minimise(func=finder.func, x0=init_pos, min_algor='simplex', print_flag=1)
218
219
220 if results == None:
221 return
222
223
224 cdp.structure.pivot = results
225
226
227 print("Motional pivot found at: %s" % results)
228
229
230 -def get_pos(spin_id=None, str_id=None, ave_pos=False):
231 """Load the spins from the structural object into the relax data store.
232
233 @keyword spin_id: The molecule, residue, and spin identifier string.
234 @type spin_id: str
235 @keyword str_id: The structure identifier. This can be the file name, model number, or structure number.
236 @type str_id: int or str
237 @keyword ave_pos: A flag specifying if the average atom position or the atom position from all loaded structures is loaded into the SpinContainer.
238 @type ave_pos: bool
239 """
240
241
242 pipes.test()
243
244
245 if not hasattr(cdp, 'structure') or not cdp.structure.num_models() or not cdp.structure.num_molecules():
246 raise RelaxNoPdbError
247
248
249 model_index = -1
250 last_model = None
251 data = []
252 for model_num, 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, model_num_flag=True, 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):
253
254 if last_model != model_num:
255 model_index = model_index + 1
256 last_model = model_num
257
258
259 if mol_name and search('\+', mol_name):
260 mol_name = mol_name.replace('+', '')
261 if res_name and search('\+', res_name):
262 res_name = res_name.replace('+', '')
263 if atom_name and search('\+', atom_name):
264 atom_name = atom_name.replace('+', '')
265
266
267 id = generate_spin_id(res_num=res_num, res_name=None, spin_name=atom_name)
268
269
270 spin_cont = return_spin(id)
271
272
273 if spin_cont == None:
274 continue
275
276
277 if ave_pos:
278 spin_cont.pos = pos
279 else:
280 if not hasattr(spin_cont, 'pos'):
281 spin_cont.pos = []
282 spin_cont.pos.append(pos)
283
284
285 data.append([id, repr(pos)])
286
287
288 if not len(data):
289 raise RelaxError("No positional information matching the spin ID '%s' could be found." % spin_id)
290
291
292 for spin in spin_loop():
293 if hasattr(spin, 'members'):
294
295 positions = []
296 for atom in spin.members:
297
298 subspin = return_spin(atom)
299
300
301 if subspin == None:
302 raise RelaxNoSpinError(atom)
303
304
305 if not hasattr(subspin, 'pos') or not subspin.pos:
306 raise RelaxError("Positional information is not available for the atom '%s'." % atom)
307
308
309 pos = subspin.pos
310
311
312 multi_model = True
313 if type(pos[0]) in [float, float64]:
314 multi_model = False
315 pos = [pos]
316
317
318 positions.append([])
319 for i in range(len(pos)):
320 positions[-1].append(pos[i].tolist())
321
322
323 if spin.averaging == 'linear':
324
325 ave = linear_ave(positions)
326
327
328 if multi_model:
329 spin.pos = ave
330 else:
331 spin.pos = ave[0]
332
333
334 write_data(out=sys.stdout, headings=["Spin_ID", "Position"], data=data)
335
336
337 -def load_spins(spin_id=None, str_id=None, ave_pos=False):
338 """Load the spins from the structural object into the relax data store.
339
340 @keyword spin_id: The molecule, residue, and spin identifier string.
341 @type spin_id: str
342 @keyword str_id: The structure identifier. This can be the file name, model number, or structure number.
343 @type str_id: int or str
344 @keyword ave_pos: A flag specifying if the average atom position or the atom position from all loaded structures is loaded into the SpinContainer.
345 @type ave_pos: bool
346 """
347
348
349 pipes.test()
350
351
352 if not hasattr(cdp, 'structure') or not cdp.structure.num_models() or not cdp.structure.num_molecules():
353 raise RelaxNoPdbError
354
355
356 print("Adding the following spins to the relax data store.\n")
357
358
359 mol_names = []
360 res_nums = []
361 res_names = []
362 spin_nums = []
363 spin_names = []
364
365
366 model_index = -1
367 last_model = 1000000
368
369
370 for model_num, 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, model_num_flag=True, 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):
371
372 if last_model != model_num:
373 model_index = model_index + 1
374 last_model = model_num
375
376
377 if mol_name and search('\+', mol_name):
378 mol_name = mol_name.replace('+', '')
379 if res_name and search('\+', res_name):
380 res_name = res_name.replace('+', '')
381 if atom_name and search('\+', atom_name):
382 atom_name = atom_name.replace('+', '')
383
384
385 id = generate_spin_id(mol_name=mol_name, res_num=res_num, res_name=res_name, spin_num=atom_num, spin_name=atom_name)
386
387
388 try:
389 spin_cont = create_spin(mol_name=mol_name, res_num=res_num, res_name=res_name, spin_num=atom_num, spin_name=atom_name)
390 except RelaxError:
391
392 if not model_index:
393 warn(RelaxWarning("The spin '%s' already exists." % id))
394 continue
395
396
397 spin_cont = return_spin(id)
398
399
400 if model_index == 0:
401 mol_names.append(mol_name)
402 res_nums.append(res_num)
403 res_names.append(res_name)
404 spin_nums.append(atom_num)
405 spin_names.append(atom_name)
406
407
408 pos = array(pos, float64)
409
410
411 if ave_pos:
412 spin_cont.pos = pos
413
414
415 else:
416
417 if not hasattr(spin_cont, 'pos'):
418 spin_cont.pos = []
419
420
421 spin_cont.pos.append(pos)
422
423
424 if not model_index:
425 spin_cont.element = element
426
427
428 if len(mol_names) == 0:
429 warn(RelaxWarning("No spins matching the '%s' ID string could be found." % spin_id))
430 return
431
432
433 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)
434
435
436 -def read_pdb(file=None, dir=None, read_mol=None, set_mol_name=None, read_model=None, set_model_num=None, parser='internal', verbosity=1, fail=True):
437 """The PDB loading function.
438
439 Parsers
440 =======
441
442 A number of parsers are available for reading PDB files. These include:
443
444 - 'scientific', the Scientific Python PDB parser.
445 - 'internal', a low quality yet fast PDB parser built into relax.
446
447
448 @keyword file: The name of the PDB file to read.
449 @type file: str
450 @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.
451 @type dir: str or None
452 @keyword read_mol: The molecule(s) to read from the file, independent of model. The molecules are determined differently by the different parsers, but are numbered consecutively from 1. If set to None, then all molecules will be loaded.
453 @type read_mol: None, int, or list of int
454 @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.
455 @type set_mol_name: None, str, or list of str
456 @keyword read_model: The PDB model to extract from the file. If set to None, then all models will be loaded.
457 @type read_model: None, int, or list of int
458 @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.
459 @type set_model_num: None, int, or list of int
460 @keyword parser: The parser to be used to read the PDB file.
461 @type parser: str
462 @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.
463 @type fail: bool
464 @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.
465 @type verbosity: int
466 @raise RelaxFileError: If the fail flag is set, then a RelaxError is raised if the PDB file does not exist.
467 """
468
469
470 pipes.test()
471
472
473 file_path = get_file_path(file, dir)
474
475
476 file_path_orig = file_path
477 if not access(file_path, F_OK):
478
479 for ext in ['.pdb', '.gz', '.pdb.gz', '.bz2', '.pdb.bz2']:
480
481 if access(file_path+ext, F_OK):
482 file_path = file_path + ext
483
484
485 if not access(file_path, F_OK):
486 if fail:
487 raise RelaxFileError('PDB', file_path_orig)
488 else:
489 warn(RelaxNoPDBFileWarning(file_path))
490 return
491
492
493 if hasattr(cdp, 'structure') and cdp.structure.id != parser:
494 raise RelaxError("The " + repr(parser) + " parser does not match the " + repr(cdp.structure.id) + " parser of the PDB loaded into the current pipe.")
495
496
497 if not hasattr(cdp, 'structure'):
498 if parser == 'scientific':
499 cdp.structure = Scientific_data()
500 elif parser == 'internal':
501 cdp.structure = Internal()
502
503
504 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, verbosity=verbosity)
505
506
507 molmol.molmol_obj.open_pdb()
508
509
510 -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):
511 """The XYZ loading function.
512
513
514 @keyword file: The name of the XYZ file to read.
515 @type file: str
516 @keyword dir: The directory where the XYZ file is located. If set to None, then the
517 file will be searched for in the current directory.
518 @type dir: str or None
519 @keyword read_mol: The molecule(s) to read from the file, independent of model.
520 If set to None, then all molecules will be loaded.
521 @type read_mol: None, int, or list of int
522 @keyword set_mol_name: Set the names of the molecules which are loaded. If set to None, then
523 the molecules will be automatically labelled based on the file name or
524 other information.
525 @type set_mol_name: None, str, or list of str
526 @keyword read_model: The XYZ model to extract from the file. If set to None, then all models
527 will be loaded.
528 @type read_model: None, int, or list of int
529 @keyword set_model_num: Set the model number of the loaded molecule. If set to None, then the
530 XYZ model numbers will be preserved, if they exist.
531 @type set_model_num: None, int, or list of int
532 @keyword fail: A flag which, if True, will cause a RelaxError to be raised if the XYZ
533 file does not exist. If False, then a RelaxWarning will be trown
534 instead.
535 @type fail: bool
536 @keyword verbosity: The amount of information to print to screen. Zero corresponds to
537 minimal output while higher values increase the amount of output. The
538 default value is 1.
539 @type verbosity: int
540 @raise RelaxFileError: If the fail flag is set, then a RelaxError is raised if the XYZ file
541 does not exist.
542 """
543
544
545 pipes.test()
546
547
548 file_path = get_file_path(file, dir)
549
550
551 if not access(file_path, F_OK):
552 file_path_orig = file_path
553 file_path = file_path + '.xyz'
554
555
556 if not access(file_path, F_OK):
557 if fail:
558 raise RelaxFileError('XYZ', file_path_orig)
559 else:
560 warn(RelaxNoPDBFileWarning(file_path))
561 return
562
563
564 if not hasattr(cdp, 'structure'):
565 cdp.structure = Internal()
566
567
568 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)
569
570
571 -def rotate(R=None, origin=None, model=None, atom_id=None):
572 """Rotate the structural data about the origin by the specified forwards rotation.
573
574 @keyword R: The forwards rotation matrix.
575 @type R: numpy 3D, rank-2 array or a 3x3 list of floats
576 @keyword origin: The origin of the rotation. If not supplied, the origin will be set to [0, 0, 0].
577 @type origin: numpy 3D, rank-1 array or list of len 3 or None
578 @keyword model: The model to rotate. If None, all models will be rotated.
579 @type model: int
580 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be used.
581 @type atom_id: str or None
582 """
583
584
585 pipes.test()
586
587
588 if not hasattr(cdp, 'structure') or not cdp.structure.num_models() or not cdp.structure.num_molecules():
589 raise RelaxNoPdbError
590
591
592 if origin == None:
593 origin = [0, 0, 0]
594
595
596 R = array(R, float64)
597 origin = array(origin, float64)
598
599
600 cdp.structure.rotate(R=R, origin=origin, model=model, atom_id=atom_id)
601
602
604 """Place the XH unit vector into the spin container object.
605
606 @keyword spin: The spin container object.
607 @type spin: SpinContainer instance
608 @keyword xh_vect: The unit vector parallel to the XH bond.
609 @type xh_vect: array of len 3
610 """
611
612
613 spin.xh_vect = xh_vect
614
615
616 -def superimpose(models=None, method='fit to mean', atom_id=None, centroid=None):
617 """Superimpose a set of structural models.
618
619 @keyword models: The list of models to superimpose. If set to None, then all models will be used.
620 @type models: list of int or None
621 @keyword method: The superimposition method. It must be one of 'fit to mean' or 'fit to first'.
622 @type method: str
623 @keyword atom_id: The molecule, residue, and atom identifier string. This matches the spin ID string format.
624 @type atom_id: str or None
625 @keyword centroid: An alternative position of the centroid to allow for different superpositions, for example of pivot point motions.
626 @type centroid: list of float or numpy rank-1, 3D array
627 """
628
629
630 allowed = ['fit to mean', 'fit to first']
631 if method not in allowed:
632 raise RelaxError("The superimposition method '%s' is unknown. It must be one of %s." % (method, allowed))
633
634
635 pipes.test()
636
637
638 cdp.structure.validate_models()
639
640
641 if models == None:
642 models = []
643 for model in cdp.structure.model_loop():
644 models.append(model.num)
645
646
647 coord = []
648 for model in models:
649 coord.append([])
650 for pos in cdp.structure.atom_loop(atom_id=atom_id, model_num=model, pos_flag=True):
651 coord[-1].append(pos[0])
652 coord[-1] = array(coord[-1])
653
654
655 if method == 'fit to mean':
656 T, R, pivot = fit_to_mean(models=models, coord=coord, centroid=centroid)
657 elif method == 'fit to first':
658 T, R, pivot = fit_to_first(models=models, coord=coord, centroid=centroid)
659
660
661
662 for i in range(len(models)):
663
664 translate(T=T[i], model=models[i])
665
666
667 rotate(R=R[i], origin=pivot[i], model=models[i])
668
669
670 -def translate(T=None, model=None, atom_id=None):
671 """Shift the structural data by the specified translation vector.
672
673 @keyword T: The translation vector.
674 @type T: numpy rank-1, 3D array or list of float
675 @keyword model: The model to translate. If None, all models will be rotated.
676 @type model: int or None
677 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be used.
678 @type atom_id: str or None
679 """
680
681
682 pipes.test()
683
684
685 if not hasattr(cdp, 'structure') or not cdp.structure.num_models() or not cdp.structure.num_molecules():
686 raise RelaxNoPdbError
687
688
689 T = array(T, float64)
690
691
692 cdp.structure.translate(T=T, model=model, atom_id=atom_id)
693
694
695 -def vectors(spin_id1=None, spin_id2=None, model=None, verbosity=1, ave=True, unit=True):
696 """Extract the bond vectors from the loaded structures and store them in the spin container.
697
698 @keyword spin_id1: The spin identifier string of the first spin of the pair.
699 @type spin_id1: str
700 @keyword spin_id2: The spin identifier string of the second spin of the pair.
701 @type spin_id2: str
702 @keyword model: The model to extract the vector from. If None, all vectors will be extracted.
703 @type model: str
704 @keyword verbosity: The higher the value, the more information is printed to screen.
705 @type verbosity: int
706 @keyword ave: A flag which if True will cause the average of all vectors to be extracted.
707 @type ave: bool
708 @keyword unit: A flag which if True will cause the function to calculate the unit vectors.
709 @type unit: bool
710 """
711
712
713 pipes.test()
714
715
716 if not hasattr(cdp, 'structure'):
717 raise RelaxNoPdbError
718
719
720 if not exists_mol_res_spin_data():
721 raise RelaxNoSequenceError
722
723
724 if verbosity:
725
726 num_models = cdp.structure.num_models()
727
728
729 if num_models > 1:
730 if model:
731 print("Extracting vectors for model '%s'." % model)
732 else:
733 print("Extracting vectors for all %s models." % num_models)
734 if ave:
735 print("Averaging all vectors.")
736
737
738 else:
739 print("Extracting vectors from the single model.")
740
741
742 if unit:
743 print("Calculating the unit vectors.")
744
745
746 no_vectors = True
747 for spin, mol_name, res_num, res_name in spin_loop(selection=spin_id, full_info=True):
748
749 if not spin.select:
750 continue
751
752
753 id = generate_spin_id(res_num=res_num, res_name=None, spin_name=spin.name, spin_num=spin.num)
754
755
756 if spin.num == None and spin.name == None:
757 warn(RelaxWarning("Either the spin number or name must be set for the spin " + repr(id) + " to identify the corresponding atom in the molecule."))
758 continue
759
760
761 if hasattr(spin, 'vector'):
762 obj = getattr(spin, 'vector')
763 if obj != None:
764 warn(RelaxWarning("The bond vector for the spin " + repr(id) + " already exists."))
765 continue
766
767
768 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)
769
770
771 if not bond_vectors:
772
773 if warnings:
774 warn(RelaxWarning(warnings + " (atom ID " + repr(id) + ")."))
775
776
777 continue
778
779
780 if not hasattr(spin, 'attached_atom'):
781 spin.attached_atom = attached_name
782 elif spin.attached_atom != attached_name:
783 raise RelaxError("The " + repr(spin.attached_atom) + " atom already attached to the spin does not match the attached atom " + repr(attached_name) + ".")
784
785
786 if ave:
787 ave_vector = zeros(3, float64)
788
789
790 for i in range(len(bond_vectors)):
791
792 if unit:
793
794 norm_factor = norm(bond_vectors[i])
795
796
797 if norm_factor == 0.0:
798 warn(RelaxZeroVectorWarning(id))
799
800
801 else:
802 bond_vectors[i] = bond_vectors[i] / norm_factor
803
804
805 if ave:
806 ave_vector = ave_vector + bond_vectors[i]
807
808
809 if ave:
810 vector = ave_vector / float(len(bond_vectors))
811 else:
812 vector = bond_vectors
813
814
815 if len(vector) == 1:
816 vector = vector[0]
817
818
819 setattr(spin, 'vector', vector)
820
821
822 no_vectors = False
823
824
825 if verbosity:
826
827 num = len(bond_vectors)
828 plural = 's'
829 if num == 1:
830 plural = ''
831
832 if spin.name:
833 print("Extracted %s %s-%s vector%s for the spin '%s'." % (num, spin.name, attached_name, plural, id))
834 else:
835 print("Extracted %s %s-%s vector%s for the spin '%s'." % (num, spin.num, attached_name, plural, id))
836
837
838 if no_vectors:
839 raise RelaxError("No vectors could be extracted.")
840
841
842 -def write_pdb(file=None, dir=None, model_num=None, compress_type=0, force=False):
843 """The PDB writing function.
844
845 @keyword file: The name of the PDB file to write.
846 @type file: str
847 @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.
848 @type dir: str or None
849 @keyword model_num: The model to place into the PDB file. If not supplied, then all models will be placed into the file.
850 @type model_num: None or int
851 @keyword compress_type: The compression type. The integer values correspond to the compression type: 0, no compression; 1, Bzip2 compression; 2, Gzip compression.
852 @type compress_type: int
853 @keyword force: The force flag which if True will cause the file to be overwritten.
854 @type force: bool
855 """
856
857
858 pipes.test()
859
860
861 if not hasattr(cdp, 'structure'):
862 raise RelaxError("No structural data is present in the current data pipe.")
863
864
865 if cdp.structure.id in ['scientific']:
866 raise RelaxError("The structures from the " + cdp.structure.id + " parser are not writable.")
867
868
869 file_path = get_file_path(file, dir)
870
871
872 if not search(".pdb$", file_path):
873 file_path = file_path + '.pdb'
874
875
876 file = open_write_file(file_path, compress_type=compress_type, force=force)
877
878
879 cdp.structure.write_pdb(file, model_num=model_num)
880