1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """Module for the manipulation of the interatomic data structures in the relax data store."""
24
25
26 from copy import deepcopy
27 from numpy import float64, zeros
28 from numpy.linalg import norm
29 from re import search
30 import sys
31 from warnings import warn
32
33
34 from lib.arg_check import is_float
35 from lib.errors import RelaxError, RelaxInteratomInconsistentError, RelaxNoInteratomError, RelaxNoSpinError
36 from lib.io import extract_data, strip, write_data
37 from lib.warnings import RelaxWarning, RelaxZeroVectorWarning
38 from pipe_control import pipes
39 from pipe_control.mol_res_spin import Selection, count_spins, exists_mol_res_spin_data, generate_spin_id_unique, return_spin, spin_loop
40 from pipe_control.pipes import check_pipe
41
42
43 -def copy(pipe_from=None, pipe_to=None, spin_id1=None, spin_id2=None, verbose=True):
44 """Copy the interatomic data from one data pipe to another.
45
46 @keyword pipe_from: The data pipe to copy the interatomic data from. This defaults to the current data pipe.
47 @type pipe_from: str
48 @keyword pipe_to: The data pipe to copy the interatomic data to. This defaults to the current data pipe.
49 @type pipe_to: str
50 @keyword spin_id1: The spin ID string of the first atom.
51 @type spin_id1: str
52 @keyword spin_id2: The spin ID string of the second atom.
53 @type spin_id2: str
54 @keyword verbose: A flag which if True will cause info about each spin pair to be printed out.
55 @type verbose: bool
56 """
57
58
59 if pipe_from == None and pipe_to == None:
60 raise RelaxError("The pipe_from and pipe_to arguments cannot both be set to None.")
61 elif pipe_from == None:
62 pipe_from = pipes.cdp_name()
63 elif pipe_to == None:
64 pipe_to = pipes.cdp_name()
65
66
67 check_pipe(pipe_from)
68 check_pipe(pipe_to)
69
70
71 if spin_id1:
72 if count_spins(selection=spin_id1, pipe=pipe_from, skip_desel=False) == 0:
73 raise RelaxNoSpinError(spin_id1, pipe_from)
74 if count_spins(selection=spin_id1, pipe=pipe_to, skip_desel=False) == 0:
75 raise RelaxNoSpinError(spin_id1, pipe_to)
76 if spin_id2:
77 if count_spins(selection=spin_id2, pipe=pipe_from, skip_desel=False) == 0:
78 raise RelaxNoSpinError(spin_id2, pipe_from)
79 if count_spins(selection=spin_id2, pipe=pipe_to, skip_desel=False) == 0:
80 raise RelaxNoSpinError(spin_id2, pipe_to)
81
82
83 if not spin_id1 and not spin_id2:
84 for spin, spin_id in spin_loop(pipe=pipe_from, return_id=True):
85 if not return_spin(spin_id=spin_id, pipe=pipe_to):
86 raise RelaxNoSpinError(spin_id, pipe_to)
87
88
89 if not exists_data(pipe_from):
90 return
91
92
93 ids = []
94 for interatom in interatomic_loop(selection1=spin_id1, selection2=spin_id2, pipe=pipe_from):
95
96 new_interatom = create_interatom(spin_id1=interatom.spin_id1, spin_id2=interatom.spin_id2, pipe=pipe_to)
97
98
99 for name in dir(interatom):
100
101 if search('^_', name):
102 continue
103
104
105 if name in ['spin_id1', 'spin_id2']:
106 continue
107
108
109 if name in interatom.__class__.__dict__:
110 continue
111
112
113 obj = deepcopy(getattr(interatom, name))
114 setattr(new_interatom, name, obj)
115
116
117 ids.append([repr(interatom.spin_id1), repr(interatom.spin_id2)])
118
119
120 hash_update(interatom=new_interatom, pipe=pipe_to)
121
122
123 if verbose:
124 write_data(out=sys.stdout, headings=["Spin_ID_1", "Spin_ID_2"], data=ids)
125
126
156
157
158 -def create_interatom(spin_id1=None, spin_id2=None, spin1=None, spin2=None, pipe=None, verbose=False):
159 """Create and return the interatomic data container for the two spins.
160
161 @keyword spin_id1: The spin ID string of the first atom.
162 @type spin_id1: str
163 @keyword spin_id2: The spin ID string of the second atom.
164 @type spin_id2: str
165 @keyword spin1: The optional spin container for the first atom. This is for speeding up the interatomic data container creation, if the spin containers are already available in the calling function.
166 @type spin1: str
167 @keyword spin2: The optional spin container for the second atom. This is for speeding up the interatomic data container creation, if the spin containers are already available in the calling function.
168 @type spin2: str
169 @keyword pipe: The data pipe to create the interatomic data container for. This defaults to the current data pipe if not supplied.
170 @type pipe: str or None
171 @keyword verbose: A flag which if True will result printouts.
172 @type verbose: bool
173 @return: The newly created interatomic data container.
174 @rtype: data.interatomic.InteratomContainer instance
175 """
176
177
178 if verbose:
179 print("Creating an interatomic data container between the spins '%s' and '%s'." % (spin_id1, spin_id2))
180
181
182 if pipe == None:
183 pipe = pipes.cdp_name()
184
185
186 dp = pipes.get_pipe(pipe)
187
188
189 if spin1 == None:
190 spin1 = return_spin(spin_id=spin_id1, pipe=pipe)
191 if spin1 == None:
192 raise RelaxNoSpinError(spin_id1)
193 if spin2 == None:
194 spin2 = return_spin(spin_id=spin_id2, pipe=pipe)
195 if spin2 == None:
196 raise RelaxNoSpinError(spin_id2)
197
198
199 for i in range(len(dp.interatomic)):
200 if spin1._hash != spin2._hash and spin1._hash in [dp.interatomic[i]._spin_hash1, dp.interatomic[i]._spin_hash2] and spin2._hash in [dp.interatomic[i]._spin_hash1, dp.interatomic[i]._spin_hash2]:
201 raise RelaxError("The spin pair %s and %s have already been added." % (spin_id1, spin_id2))
202
203
204 interatom = dp.interatomic.add_item(spin_id1=spin_id1, spin_id2=spin_id2, spin_hash1=spin1._hash, spin_hash2=spin2._hash)
205
206
207 spin1._interatomic_hashes.append(interatom._hash)
208 spin2._interatomic_hashes.append(interatom._hash)
209
210
211 return interatom
212
213
214 -def define_dipole_pair(spin_id1=None, spin_id2=None, spin1=None, spin2=None, pipe=None, direct_bond=False, spin_selection=False, verbose=True):
215 """Set up the magnetic dipole-dipole interaction.
216
217 @keyword spin_id1: The spin identifier string of the first spin of the pair.
218 @type spin_id1: str
219 @keyword spin_id2: The spin identifier string of the second spin of the pair.
220 @type spin_id2: str
221 @keyword spin1: An optional single spin container for the first atom. This is for speeding up the interatomic data container creation, if the spin containers are already available in the calling function.
222 @type spin1: str
223 @keyword spin2: An optional single spin container for the second atom. This is for speeding up the interatomic data container creation, if the spin containers are already available in the calling function.
224 @type spin2: str
225 @param pipe: The data pipe to operate on. Defaults to the current data pipe.
226 @type pipe: str
227 @keyword direct_bond: A flag specifying if the two spins are directly bonded.
228 @type direct_bond: bool
229 @keyword spin_selection: Define the interatomic data container selection based on the spin selection. If either spin is deselected, the interatomic container will also be deselected. Otherwise the container will be selected.
230 @type spin_selection: bool
231 @keyword verbose: A flag which if True will result in printouts of the created interatomoic data containers.
232 @type verbose: bool
233 """
234
235
236 if pipe == None:
237 pipe = pipes.cdp_name()
238
239
240 dp = pipes.get_pipe(pipe)
241
242
243 ids = []
244 spins = []
245 spin_selections = []
246
247
248 if spin1 and spin2:
249
250 ids.append([spin_id1, spin_id2])
251
252
253 spins.append([spin1, spin2])
254 spin_selections.append([spin1.select, spin2.select])
255
256
257 elif hasattr(dp, 'structure'):
258
259 selection1 = cdp.structure.selection(atom_id=spin_id1)
260 selection2 = cdp.structure.selection(atom_id=spin_id2)
261
262
263 for mol_name1, res_num1, res_name1, atom_num1, atom_name1, mol_index1, atom_index1 in dp.structure.atom_loop(selection=selection1, model_num=1, mol_name_flag=True, res_num_flag=True, res_name_flag=True, atom_num_flag=True, atom_name_flag=True, mol_index_flag=True, index_flag=True):
264
265 id1 = generate_spin_id_unique(pipe_cont=dp, mol_name=mol_name1, res_num=res_num1, res_name=res_name1, spin_num=atom_num1, spin_name=atom_name1)
266
267
268 spin1 = return_spin(spin_id=id1)
269 if not spin1:
270 continue
271
272
273 for mol_name2, res_num2, res_name2, atom_num2, atom_name2, mol_index2, atom_index2 in dp.structure.atom_loop(selection=selection2, model_num=1, mol_name_flag=True, res_num_flag=True, res_name_flag=True, atom_num_flag=True, atom_name_flag=True, mol_index_flag=True, index_flag=True):
274
275 if direct_bond:
276
277 if mol_name1 != mol_name2:
278 continue
279
280
281 if not dp.structure.are_bonded_index(mol_index1=mol_index1, atom_index1=atom_index1, mol_index2=mol_index2, atom_index2=atom_index2):
282 continue
283
284
285 id2 = generate_spin_id_unique(pipe_cont=dp, mol_name=mol_name2, res_num=res_num2, res_name=res_name2, spin_num=atom_num2, spin_name=atom_name2)
286
287
288 spin2 = return_spin(spin_id=id2)
289 if not spin2:
290 continue
291
292
293 ids.append([id1, id2])
294
295
296 spins.append([spin1, spin2])
297 spin_selections.append([spin1.select, spin2.select])
298
299
300 if ids == []:
301 for spin1, mol_name1, res_num1, res_name1, id1 in spin_loop(spin_id1, pipe=pipe, full_info=True, return_id=True):
302 for spin2, mol_name2, res_num2, res_name2, id2 in spin_loop(spin_id2, pipe=pipe, full_info=True, return_id=True):
303
304 if direct_bond:
305
306 if mol_name1 != mol_name2:
307 continue
308
309
310 if not hasattr(spin1, 'element'):
311 raise RelaxError("The spin '%s' does not have the element type set." % id1)
312 if not hasattr(spin2, 'element'):
313 raise RelaxError("The spin '%s' does not have the element type set." % id2)
314
315
316 pair = False
317 if (spin1.element == 'N' and spin2.element == 'H') or (spin2.element == 'N' and spin1.element == 'H'):
318 pair = True
319 elif (spin1.element == 'C' and spin2.element == 'H') or (spin2.element == 'C' and spin1.element == 'H'):
320 pair = True
321
322
323 if pair and res_num1 != None and res_num1 != res_num2:
324 continue
325 elif pair and res_num1 == None and res_name1 != res_name2:
326 continue
327
328
329 ids.append([id1, id2])
330
331
332 spins.append([spin1, spin2])
333 spin_selections.append([spin1.select, spin2.select])
334
335
336 if not len(ids):
337
338 count1 = 0
339 count2 = 0
340 for spin in spin_loop(spin_id1):
341 count1 += 1
342 for spin in spin_loop(spin_id2):
343 count2 += 1
344
345
346 if count1 == 0 and count2 == 0:
347 raise RelaxError("Neither spin IDs '%s' and '%s' match any spins." % (spin_id1, spin_id2))
348 elif count1 == 0:
349 raise RelaxError("The spin ID '%s' matches no spins." % spin_id1)
350 elif count2 == 0:
351 raise RelaxError("The spin ID '%s' matches no spins." % spin_id2)
352 else:
353 raise RelaxError("Unknown error.")
354
355
356 for i in range(len(ids)):
357
358 id1, id2 = ids[i]
359 spin1, spin2 = spins[i]
360
361
362 interatom = return_interatom(spin_hash1=spin1._hash, spin_hash2=spin2._hash, pipe=pipe)
363
364
365 if interatom == None:
366 interatom = create_interatom(spin_id1=id1, spin_id2=id2, spin1=spins[i][0], spin2=spins[i][1], pipe=pipe)
367
368
369 if interatom.dipole_pair:
370 raise RelaxError("The magnetic dipole-dipole interaction already exists between the spins '%s' and '%s'." % (id1, id2))
371
372
373 interatom.dipole_pair = True
374
375
376 if spin_selection:
377 interatom.select = False
378 if spin_selections[i][0] and spin_selections[i][1]:
379 interatom.select = True
380
381
382 if verbose:
383
384 for i in range(len(ids)):
385 ids[i][0] = repr(ids[i][0])
386 ids[i][1] = repr(ids[i][1])
387
388
389 print("Interatomic interactions are now defined for the following spins:\n")
390 write_data(out=sys.stdout, headings=["Spin_ID_1", "Spin_ID_2"], data=ids)
391
392
394 """Determine if any interatomic data exists.
395
396 @keyword pipe: The data pipe in which the interatomic data will be checked for.
397 @type pipe: str
398 @return: The answer to the question about the existence of data.
399 @rtype: bool
400 """
401
402
403 if pipe == None:
404 pipe = pipes.cdp_name()
405
406
407 check_pipe(pipe)
408
409
410 dp = pipes.get_pipe(pipe)
411
412
413 if dp.interatomic.is_empty():
414 return False
415
416
417 return True
418
419
421 """Recreate the spin hashes for the interatomic data container.
422
423 @keyword interatom: The interatomic data container.
424 @type interatom: InteratomContainer instance
425 @keyword pipe: The data pipe containing the interatomic data container. Defaults to the current data pipe.
426 @type pipe: str or None
427 """
428
429
430 spin1 = return_spin(spin_hash=interatom._spin_hash1, pipe=pipe)
431 spin2 = return_spin(spin_hash=interatom._spin_hash2, pipe=pipe)
432
433
434 interatom._spin_hash1 = spin1._hash
435 interatom._spin_hash2 = spin2._hash
436
437
438 -def interatomic_loop(selection1=None, selection2=None, pipe=None, skip_desel=True):
439 """Generator function for looping over all the interatomic data containers.
440
441 @keyword selection1: The optional spin ID selection of the first atom.
442 @type selection1: str
443 @keyword selection2: The optional spin ID selection of the second atom.
444 @type selection2: str
445 @keyword pipe: The data pipe containing the spin. Defaults to the current data pipe.
446 @type pipe: str
447 @keyword skip_desel: A flag which if True will cause only selected interatomic data containers to be returned.
448 @type skip_desel: bool
449 """
450
451
452 if pipe == None:
453 pipe = pipes.cdp_name()
454
455
456 dp = pipes.get_pipe(pipe)
457
458
459 select_obj = None
460 select_obj1 = None
461 select_obj2 = None
462 if selection1 and selection2:
463 select_obj1 = Selection(selection1)
464 select_obj2 = Selection(selection2)
465 elif selection1:
466 select_obj = Selection(selection1)
467 elif selection2:
468 select_obj = Selection(selection2)
469
470
471 for i in range(len(dp.interatomic)):
472
473 if skip_desel and not dp.interatomic[i].select:
474 continue
475
476
477 interatom = dp.interatomic[i]
478 mol_index1, res_index1, spin_index1 = dp.mol._spin_hash_lookup[interatom._spin_hash1]
479 mol_index2, res_index2, spin_index2 = dp.mol._spin_hash_lookup[interatom._spin_hash2]
480 mol1 = dp.mol[mol_index1]
481 res1 = dp.mol[mol_index1].res[res_index1]
482 spin1 = dp.mol[mol_index1].res[res_index1].spin[spin_index1]
483 mol2 = dp.mol[mol_index2]
484 res2 = dp.mol[mol_index2].res[res_index2]
485 spin2 = dp.mol[mol_index2].res[res_index2].spin[spin_index2]
486
487
488 if select_obj:
489 sel1 = select_obj.contains_spin(spin_name=spin1.name, spin_num=spin1.num, res_name=res1.name, res_num=res1.num, mol=mol1.name)
490 sel2 = select_obj.contains_spin(spin_name=spin2.name, spin_num=spin2.num, res_name=res2.name, res_num=res2.num, mol=mol2.name)
491 if select_obj1:
492 sel11 = select_obj1.contains_spin(spin_name=spin1.name, spin_num=spin1.num, res_name=res1.name, res_num=res1.num, mol=mol1.name)
493 sel12 = select_obj1.contains_spin(spin_name=spin2.name, spin_num=spin2.num, res_name=res2.name, res_num=res2.num, mol=mol2.name)
494 if select_obj2:
495 sel21 = select_obj2.contains_spin(spin_name=spin1.name, spin_num=spin1.num, res_name=res1.name, res_num=res1.num, mol=mol1.name)
496 sel22 = select_obj2.contains_spin(spin_name=spin2.name, spin_num=spin2.num, res_name=res2.name, res_num=res2.num, mol=mol2.name)
497
498
499 if select_obj:
500 if not sel1 and not sel2:
501 continue
502 if select_obj1:
503 if not (sel11 or sel12):
504 continue
505 if select_obj2:
506 if not (sel21 or sel22):
507 continue
508
509
510 yield interatom
511
512
552
553
554 -def read_dist(file=None, dir=None, unit='meter', spin_id1_col=None, spin_id2_col=None, data_col=None, sep=None):
555 """Set up the magnetic dipole-dipole interaction.
556
557 @keyword file: The name of the file to open.
558 @type file: str
559 @keyword dir: The directory containing the file (defaults to the current directory if None).
560 @type dir: str or None
561 @keyword unit: The measurement unit. This can be either 'meter' or 'Angstrom'.
562 @type unit: str
563 @keyword spin_id1_col: The column containing the spin ID strings of the first spin.
564 @type spin_id1_col: int
565 @keyword spin_id2_col: The column containing the spin ID strings of the second spin.
566 @type spin_id2_col: int
567 @keyword data_col: The column containing the averaged distances in meters.
568 @type data_col: int or None
569 @keyword sep: The column separator which, if None, defaults to whitespace.
570 @type sep: str or None
571 """
572
573
574 if unit not in ['meter', 'Angstrom']:
575 raise RelaxError("The measurement unit of '%s' must be one of 'meter' or 'Angstrom'." % unit)
576
577
578 check_pipe()
579
580
581 if not exists_mol_res_spin_data():
582 raise RelaxNoSequenceError
583
584
585 file_data = extract_data(file, dir, sep=sep)
586 file_data = strip(file_data, comments=True)
587
588
589 data = []
590 for line in file_data:
591
592 if spin_id1_col > len(line):
593 warn(RelaxWarning("The data %s is invalid, no first spin ID column can be found." % line))
594 continue
595 if spin_id2_col > len(line):
596 warn(RelaxWarning("The data %s is invalid, no second spin ID column can be found." % line))
597 continue
598 if data_col and data_col > len(line):
599 warn(RelaxWarning("The data %s is invalid, no data column can be found." % line))
600 continue
601
602
603 spin_id1 = line[spin_id1_col-1]
604 spin_id2 = line[spin_id2_col-1]
605 ave_dist = None
606 if data_col:
607 ave_dist = line[data_col-1]
608
609
610 if ave_dist != None:
611 try:
612 ave_dist = float(ave_dist)
613 except ValueError:
614 warn(RelaxWarning("The averaged distance of '%s' from the line %s is invalid." % (ave_dist, line)))
615 continue
616
617
618 if unit == 'Angstrom':
619 ave_dist = ave_dist * 1e-10
620
621
622 spin1 = return_spin(spin_id=spin_id1)
623 spin2 = return_spin(spin_id=spin_id2)
624 interatom = return_interatom(spin_hash1=spin1._hash, spin_hash2=spin2._hash)
625
626
627 if interatom == None:
628 interatom = create_interatom(spin_id1=spin_id1, spin_id2=spin_id2, verbose=True)
629
630
631 interatom.r = ave_dist
632
633
634 data.append([repr(interatom.spin_id1), repr(interatom.spin_id2), repr(ave_dist)])
635
636
637 if not len(data):
638 raise RelaxError("No data could be extracted from the file.")
639
640
641 print("The following averaged distances have been read:\n")
642 write_data(out=sys.stdout, headings=["Spin_ID_1", "Spin_ID_2", "Ave_distance(meters)"], data=data)
643
644
646 """Return the list of interatomic data containers for the two spins.
647
648 @keyword spin_hash1: The unique spin hash for the first atom.
649 @type spin_hash1: str
650 @keyword spin_hash2: The unique spin hash for the second atom.
651 @type spin_hash2: str
652 @keyword pipe: The data pipe holding the container. Defaults to the current data pipe.
653 @type pipe: str or None
654 @return: The matching interatomic data container, if it exists.
655 @rtype: data.interatomic.InteratomContainer instance or None
656 """
657
658
659 if spin_hash1 == None:
660 raise RelaxError("The first spin hash must be supplied.")
661 if spin_hash2 == None:
662 raise RelaxError("The second spin hash must be supplied.")
663
664
665 if pipe == None:
666 pipe = pipes.cdp_name()
667
668
669 dp = pipes.get_pipe(pipe)
670
671
672 for i in range(len(dp.interatomic)):
673 if spin_hash1 != spin_hash2 and spin_hash1 in [dp.interatomic[i]._spin_hash1, dp.interatomic[i]._spin_hash2] and spin_hash2 in [dp.interatomic[i]._spin_hash1, dp.interatomic[i]._spin_hash2]:
674 return dp.interatomic[i]
675
676
677 return None
678
679
681 """Return the list of interatomic data containers for the given spin.
682
683 @keyword spin_hash: The unique spin hash.
684 @type spin_hash: str
685 @keyword pipe: The data pipe holding the container. This defaults to the current data pipe.
686 @type pipe: str or None
687 @return: The list of matching interatomic data containers, if any exist.
688 @rtype: list of data.interatomic.InteratomContainer instances
689 """
690
691
692 if spin_hash == None:
693 raise RelaxError("The unique spin hash must be supplied.")
694
695
696 if pipe == None:
697 pipe = pipes.cdp_name()
698
699
700 dp = pipes.get_pipe(pipe)
701
702
703 interatoms = []
704
705
706 for i in range(len(dp.interatomic)):
707 if spin_hash in [dp.interatomic[i]._spin_hash1, dp.interatomic[i]._spin_hash2]:
708 interatoms.append(dp.interatomic[i])
709
710
711 return interatoms
712
713
714 -def set_dist(spin_id1=None, spin_id2=None, ave_dist=None, unit='meter'):
715 """Set up the magnetic dipole-dipole interaction.
716
717 @keyword spin_id1: The spin identifier string of the first spin of the pair.
718 @type spin_id1: str
719 @keyword spin_id2: The spin identifier string of the second spin of the pair.
720 @type spin_id2: str
721 @keyword ave_dist: The r^-3 averaged interatomic distance.
722 @type ave_dist: float
723 @keyword unit: The measurement unit. This can be either 'meter' or 'Angstrom'.
724 @type unit: str
725 """
726
727
728 if unit not in ['meter', 'Angstrom']:
729 raise RelaxError("The measurement unit of '%s' must be one of 'meter' or 'Angstrom'." % unit)
730
731
732 if unit == 'Angstrom':
733 ave_dist = ave_dist * 1e-10
734
735
736 sel_obj1 = Selection(spin_id1)
737 sel_obj2 = Selection(spin_id2)
738
739
740 data = []
741 for interatom in interatomic_loop():
742
743 mol_name1, res_num1, res_name1, spin1 = return_spin(spin_hash=interatom._spin_hash1, full_info=True)
744 mol_name2, res_num2, res_name2, spin2 = return_spin(spin_hash=interatom._spin_hash2, full_info=True)
745
746
747 if not (sel_obj1.contains_spin(spin_num=spin1.num, spin_name=spin1.name, res_num=res_num1, res_name=res_name1, mol=mol_name1) and sel_obj2.contains_spin(spin_num=spin2.num, spin_name=spin2.name, res_num=res_num2, res_name=res_name2, mol=mol_name2)) and not (sel_obj2.contains_spin(spin_num=spin1.num, spin_name=spin1.name, res_num=res_num1, res_name=res_name1, mol=mol_name1) and sel_obj1.contains_spin(spin_num=spin2.num, spin_name=spin2.name, res_num=res_num2, res_name=res_name2, mol=mol_name2)):
748 continue
749
750
751 interatom.r = ave_dist
752
753
754 data.append([repr(interatom.spin_id1), repr(interatom.spin_id2), repr(ave_dist)])
755
756
757 if not len(data):
758 raise RelaxError("No data could be set.")
759
760
761 print("The following averaged distances have been set:\n")
762 write_data(out=sys.stdout, headings=["Spin_ID_1", "Spin_ID_2", "Ave_distance(meters)"], data=data)
763
764
766 """Extract the bond vectors from the loaded structures and store them in the spin container.
767
768 @keyword ave: A flag which if True will cause the average of all vectors to be calculated.
769 @type ave: bool
770 """
771
772
773 check_pipe()
774
775
776 if not exists_data():
777 raise RelaxNoInteratomError
778
779
780 if ave:
781 print("Averaging all vectors.")
782 else:
783 print("No averaging of the vectors.")
784
785
786 no_vectors = True
787 pos_info = False
788 for interatom in interatomic_loop(skip_desel=False):
789
790 spin1 = return_spin(spin_hash=interatom._spin_hash1)
791 spin2 = return_spin(spin_hash=interatom._spin_hash2)
792
793
794 if not hasattr(spin1, 'pos'):
795 continue
796 if not hasattr(spin2, 'pos'):
797 continue
798
799
800 pos_info = True
801
802
803 if is_float(spin1.pos[0], raise_error=False) and is_float(spin2.pos[0], raise_error=False):
804
805 vector_list = [spin2.pos - spin1.pos]
806
807
808 elif is_float(spin1.pos[0], raise_error=False) or is_float(spin2.pos[0], raise_error=False):
809
810 if is_float(spin2.pos[0], raise_error=False):
811 vector_list = []
812 for i in range(len(spin1.pos)):
813 vector_list.append(spin2.pos - spin1.pos[i])
814
815
816 else:
817 vector_list = []
818 for i in range(len(spin2.pos)):
819 vector_list.append(spin2.pos[i] - spin1.pos)
820
821
822 else:
823
824 if len(spin1.pos) != len(spin2.pos):
825 raise RelaxError("The spin '%s' consists of %s positions whereas the spin '%s' consists of %s - these numbers must match." % (interatom.spin_id1, len(spin1.pos), interatom.spin_id1, len(spin1.pos)))
826
827
828 vector_list = []
829 for i in range(len(spin1.pos)):
830
831 if spin1.pos[i] is None or spin2.pos[i] is None:
832 warn(RelaxWarning("No structural information for state %i can be found between spins '%s' and '%s'." % (i, interatom.spin_id1, interatom.spin_id2)))
833 vector_list.append(None)
834
835
836 else:
837 vector_list.append(spin2.pos[i] - spin1.pos[i])
838
839
840 for i in range(len(vector_list)):
841
842 if vector_list[i] is None:
843 continue
844
845
846 norm_factor = norm(vector_list[i])
847
848
849 if norm_factor == 0.0:
850 warn(RelaxZeroVectorWarning(spin_id1=interatom.spin_id1, spin_id2=interatom.spin_id2))
851
852
853 else:
854 vector_list[i] = vector_list[i] / norm_factor
855
856
857 if ave:
858 ave_vector = zeros(3, float64)
859 count = 0
860 for i in range(len(vector_list)):
861 if vector_list[i] is not None:
862 ave_vector = ave_vector + vector_list[i]
863 count += 1
864 vector_list = [ave_vector / count]
865
866
867 if len(vector_list) == 1:
868 vector_list = vector_list[0]
869
870
871 setattr(interatom, 'vector', vector_list)
872
873
874 no_vectors = False
875
876
877 num = 1
878 if not is_float(vector_list[0], raise_error=False):
879 num = len(vector_list)
880 plural = 's'
881 if num == 1:
882 plural = ''
883 if spin1.name:
884 spin1_str = spin1.name
885 else:
886 spin1_str = spin1.num
887 if spin2.name:
888 spin2_str = spin2.name
889 else:
890 spin2_str = spin2.num
891 print("Calculated %s %s-%s unit vector%s between the spins '%s' and '%s'." % (num, spin1_str, spin2_str, plural, interatom.spin_id1, interatom.spin_id2))
892
893
894 if not pos_info:
895 raise RelaxError("Positional information could not be found for any spins.")
896
897
898 if no_vectors:
899 raise RelaxError("No vectors could be extracted.")
900