@@ -80,28 +80,16 @@ def get_residue_axes(
8080
8181 - If bonded to only one other residue:
8282 * Translational axes are principal axes of data_container.
83- * Find edge heavy atom (i.e. heavy atoms bonded to neighbour residue). Find
84- all heavy atoms bonded to edge heavy atom and compute their average
85- position. Find all other heavy atoms in residue and compute their average
86- position.The three points are now used to obtain determine residue
87- rotational axes. (see get_residue_custom_axes) If there are
88- only two heavy atoms in the residue/all heavy atoms are bonded to edge
89- atom, x-axis is set along the vector between the edge atom and average
90- position of bonded atoms, y-axis is arbitrary and z-axis is paralel
91- to the two. This is the same as case 2 in get_bonded_axes. Compute a
83+ * Find edge heavy atom (i.e. heavy atoms bonded to neighbour residue).
84+ Compute rotation centre and axes as in get_terminal_axes.
9285 custom MOI, using heavy atom positions and heavy atom + hydrogen masses.
9386
9487 - If bonded to at least two other residues:
9588 * Translational axes are principal axes of data_container.
96- * Find edge heavy atoms (i.e. heavy atoms bonded to neighbour residues)
97- and find the shortest chain between them: the backbone. Edge atoms
98- + backbone COM are used to determine residue rotational axes.
99- (see get_residue_custom_axes). If the two edge heavy atoms
100- are bonded to each other (i.e. there is no backbone), x-axis is set
101- along the vector between the edge atom and average position of bonded
102- atoms, y-axis is arbitrary and z-axis is paralel to the two. This is the
103- same as case 2 in get_bonded_axes. Compute a custom MOI, using heavy
104- atom positions and heavy atom + hydrogen masses.
89+ * Find edge heavy atoms (i.e. heavy atoms bonded to neighbour residues).
90+ Compute rotation centre and axes as in get_non_terminal_axes.
91+ Compute a custom MOI, using heavy atom positions and
92+ heavy atom + hydrogen masses.
10593
10694 Args:
10795 data_container (MDAnalysis.Universe or AtomGroup):
@@ -161,54 +149,13 @@ def get_residue_axes(
161149 else :
162150 make_whole (data_container .atoms )
163151 trans_axes = data_container .atoms .principal_axes ()
164-
165152 if len (edge_atom_set ) == 1 :
166153 edge_atom = edge_atom_set [0 ]
167- bonded_atoms = residue .select_atoms (
168- f"(mass 2 to 999) and bonded index { edge_atom .index } "
169- )
170- # find the average position of heavy atoms bonded to edge atom
171- if len (bonded_atoms ) > 0 :
172- average_bonded_atom = np .zeros (3 )
173- for bonded_atom in bonded_atoms :
174- average_bonded_atom += bonded_atom .position
175- average_bonded_atom /= len (bonded_atoms )
176- # find the average position of all other heavy atoms in residue
177- other_atoms = []
178- for atom in uas :
179- if atom != edge_atom and atom not in bonded_atoms :
180- other_atoms .append (atom )
181- if len (other_atoms ) > 0 :
182- average_other_atoms = np .zeros (3 )
183- for atom in other_atoms :
184- average_other_atoms += atom .position
185- average_other_atoms /= len (other_atoms )
186- rot_center , rot_axes = self .get_residue_custom_axes (
187- [edge_atom .position , average_other_atoms ], average_bonded_atom
188- )
189- else :
190- rot_center = edge_atom .position
191- rot_axes = self .get_custom_axes (
192- a = edge_atom .position , b = [average_bonded_atom ], c = np .zeros (3 )
193- )
194-
154+ rot_center , rot_axes = self .get_terminal_axes (residue , edge_atom )
195155 else :
196- edges = [edge_atom_set [0 ].position , edge_atom_set [1 ].position ]
197- backbone = self .get_chain (residue , edge_atom_set [0 ], edge_atom_set [1 ])
198- backbone_center = np .zeros (3 )
199- if len (backbone ) > 0 :
200- for heavy_atom in backbone :
201- backbone_center += heavy_atom .position
202- backbone_center /= len (backbone )
203- rot_center , rot_axes = self .get_residue_custom_axes (
204- edges , backbone_center
205- )
206- else :
207- rot_center = (edges [0 ] + edges [1 ]) / 2
208- rot_axes = self .get_custom_axes (
209- a = rot_center , b = [edges [0 ]], c = np .zeros (3 )
210- )
211-
156+ rot_center , rot_axes = self .get_non_terminal_axes (
157+ residue , edge_atom_set
158+ )
212159 moment_of_inertia = self .get_custom_residue_moment_of_inertia (
213160 center_of_mass = rot_center ,
214161 positions = uas .positions ,
@@ -289,26 +236,8 @@ def get_UA_axes(self, data_container, index: int, res_position):
289236 - If there are *no* bonds to other residues, use a custom principal axes
290237 from a moment-of-inertia (MOI) tensor that uses positions of heavy atoms
291238 only, but includes masses of heavy atom + bonded hydrogens.
292- - If bonded to only one other residue and there are only two heavy atoms
293- in the residue/all heavy atoms are bonded to edge atom,
294- x-axis is set along the vector between the edge atom and average position
295- of bonded atoms, y-axis is arbitrary and z-axis is paralel to the two.
296- This is the same as case 2 in get_bonded_axes.
297- - If bonded to only one other residue, find edge heavy atom
298- (i.e. heavy atom bonded to neighbour residue). Find all heavy atoms
299- bonded to edge heavy atom and compute their average position.
300- Find all other heavy atoms in residue and compute their average position.
301- The three points are now used to obtain determine residue rotational axes.
302- (see get_residue_custom_axes)
303- - If bonded to at least two other residues, find edge heavy atoms
304- (i.e. heavy atoms bonded to neighbour residues) and find the shortest
305- chain between them: the backbone. Edge atoms + backbone COM are used
306- to determine residue rotational axes. (see get_residue_custom_axes).
307- - If bonded to at least two other residues and the two edge heavy atoms
308- are bonded to each other (i.e. there is no backbone), x-axis is set along
309- the vector between the edge atom and average position of bonded atoms,
310- y-axis is arbitrary and z-axis is paralel to the two. This is the same
311- as case 2 in get_bonded_axes.
239+ - If bonded to only one other residue, see get_terminal_axes.
240+ - If bonded to at least two other residues, see get_non_terminal_axes.
312241
313242 - Rotational axes:
314243 Identify heavy atoms in the residue/molecule of interest and choose
@@ -346,7 +275,6 @@ def get_UA_axes(self, data_container, index: int, res_position):
346275 residue = data_container
347276 trans_center = data_container .atoms .center_of_mass (unwrap = True )
348277 trans_axes = data_container .atoms .principal_axes ()
349- residue_heavy_atoms = heavy_atoms
350278 else :
351279 # residue of interest has at least one neighbour
352280 if res_position == - 1 or res_position == 1 :
@@ -368,64 +296,26 @@ def get_UA_axes(self, data_container, index: int, res_position):
368296 f"resindex { resindex } and bonded resindex { resindex_prev } "
369297 )
370298 edge_atom = edge_atom_set [0 ]
371- residue_heavy_atoms = residue .atoms .select_atoms ("mass 2 to 999" )
372- bonded_atoms = residue .atoms .select_atoms (
373- f"(mass 2 to 999) and bonded index { edge_atom .index } "
299+ trans_center , trans_axes = self .get_terminal_axes (
300+ residue , edge_atom
374301 )
375- # find the average position of heavy atoms bonded to edge atom
376- if len (bonded_atoms ) > 0 :
377- average_bonded_atom = np .zeros (3 )
378- for atom in bonded_atoms :
379- average_bonded_atom += atom .position
380- average_bonded_atom /= len (bonded_atoms )
381- # find the average position of all other heavy atoms in residue
382- other_atoms = []
383- for atom in residue_heavy_atoms :
384- if atom != edge_atom and atom not in bonded_atoms :
385- other_atoms .append (atom )
386- average_other_atoms = np .zeros (3 )
387- if len (other_atoms ) > 0 :
388- for atom in other_atoms :
389- average_other_atoms += atom .position
390- average_other_atoms /= len (other_atoms )
391- trans_center , trans_axes = self .get_residue_custom_axes (
392- [edge_atom .position , average_other_atoms ],
393- average_bonded_atom ,
394- )
395- else :
396- trans_center = edge_atom .position
397- trans_axes = self .get_custom_axes (
398- a = edge_atom .position , b = [average_bonded_atom ], c = np .zeros (3 )
399- )
400302 else :
401303 # between 2 residues
402304 residue = data_container .residues [1 ]
403305 resindex = residue .resindex
404306 resindex_next = resindex + 1
405307 resindex_prev = resindex - 1
406308 residue_heavy_atoms = residue .atoms .select_atoms ("mass 2 to 999" )
407- edge_set = data_container .select_atoms (
309+ edge_atom_set = data_container .select_atoms (
408310 f"resindex { resindex } and "
409311 f"(bonded resindex { resindex_prev } or "
410312 f"resindex { resindex_next } )"
411313 )
412- edges = edge_set .positions
413- backbone = self .get_chain (residue , edge_set [0 ], edge_set [1 ])
414- if len (backbone ) > 0 :
415- backbone_center = np .zeros (3 )
416- for heavy_atom in backbone :
417- backbone_center += heavy_atom .position
418- backbone_center /= len (backbone )
419- trans_center , trans_axes = self .get_residue_custom_axes (
420- edges , backbone_center
421- )
422- else :
423- trans_center = (edges [0 ] + edges [1 ]) / 2
424- trans_axes = self .get_custom_axes (
425- a = trans_center , b = [edges [0 ]], c = np .zeros (3 )
426- )
427-
314+ trans_center , trans_axes = self .get_non_terminal_axes (
315+ residue , edge_atom_set
316+ )
428317 # look for heavy atoms in residue of interest
318+ residue_heavy_atoms = residue .atoms .select_atoms ("mass 2 to 999" )
429319 heavy_atom_indices = []
430320 for atom in residue_heavy_atoms :
431321 heavy_atom_indices .append (atom .index )
@@ -672,6 +562,98 @@ def get_residue_custom_axes(self, edges, center):
672562 rot_center = first_edge_origin_vector + edges [0 ]
673563 return rot_center , rot_axes
674564
565+ def get_terminal_axes (self , residue , edge ):
566+ """
567+ Compute rotation axes at the residue level/translation axes at the UA level
568+ for the terminal residues in a polymer, given the edge atom
569+ (i.e. atom bonded to neighbour residue) and residue of interest.
570+ Find all heavy atoms bonded to edge heavy atom and compute
571+ their average position. Find all other heavy atoms in residue
572+ and compute their average position. The three points are now used to
573+ obtain determine residue rotational axes. (see get_residue_custom_axes)
574+ If there are only two heavy atoms in the residue/all heavy atoms are bonded
575+ to edge atom, x-axis is set along the vector between the
576+ edge atom and average position of bonded atoms, y-axis is arbitrary
577+ and z-axis is paralel to the two. This is the same as case 2 in get_bonded_axes.
578+ If there no heavy atoms bonded to the edge atom (i.e. the edge atom is the only
579+ heavy atom in the residue), centre is set on edge atom and axes are principal
580+ axes.
581+
582+ Args:
583+ residue: MDAnalysis AtomGroup
584+ edge: MDAnalysis atom
585+
586+ Returns:
587+ rot_center: (3,) rotation centre,
588+ rot_axes: (3,3) rotation axes of residue
589+ """
590+ heavy_atoms = residue .atoms .select_atoms ("mass 2 to 999" )
591+ bonded_atoms = residue .atoms .select_atoms (
592+ f"(mass 2 to 999) and bonded index { edge .index } "
593+ )
594+ if len (bonded_atoms ) == 0 :
595+ # there is only one heavy atom in the residue
596+ rot_center = edge .position
597+ rot_axes = residue .atoms .principal_axes ()
598+ else :
599+ average_bonded = np .zeros (3 )
600+ for bonded_atom in bonded_atoms :
601+ average_bonded += bonded_atom .position
602+ average_bonded /= len (bonded_atoms )
603+ # find the average position of all other heavy atoms in residue
604+ other_atoms = []
605+ for atom in heavy_atoms :
606+ if atom != edge and atom not in bonded_atoms :
607+ other_atoms .append (atom )
608+ if len (other_atoms ) > 0 :
609+ average_other_atoms = np .zeros (3 )
610+ for atom in other_atoms :
611+ average_other_atoms += atom .position
612+ average_other_atoms /= len (other_atoms )
613+ rot_center , rot_axes = self .get_residue_custom_axes (
614+ [edge .position , average_other_atoms ], average_bonded
615+ )
616+ else :
617+ rot_center = edge .position
618+ rot_axes = self .get_custom_axes (
619+ a = edge .position , b = [average_bonded ], c = np .zeros (3 )
620+ )
621+ return rot_center , rot_axes
622+
623+ def get_non_terminal_axes (self , residue , edges ):
624+ """
625+ Compute rotation axes at the residue level/ translation axes at
626+ the UA level for the non-terminal residues in a linear polymer, given the
627+ edge atoms (i.e. heavy atoms bonded to neighbour residues) and
628+ residue of interest. Find the shortest chain between edge atoms: the backbone.
629+ Edges + backbone average position determine the residue rotational axes.
630+ (see get_residue_custom_axes). If the two edge heavy atoms
631+ are bonded to each other (i.e. there is no backbone), x-axis is set
632+ along the vector between the edge atom and average position of bonded
633+ atoms, y-axis is arbitrary and z-axis is paralel to the two. This is the
634+ same as case 2 in get_bonded_axes.
635+ Args:
636+ residue: MDAnalysis AtomGroup
637+ edges: MDAnalysis AtomGroup
638+
639+ Returns:
640+ rot_center: (3,) rotation centre,
641+ rot_axes: (3,3) rotation axes of residue
642+ """
643+ backbone = self .get_chain (residue , edges [0 ], edges [1 ])
644+ backbone_center = np .zeros (3 )
645+ if len (backbone ) > 0 :
646+ for heavy_atom in backbone :
647+ backbone_center += heavy_atom .position
648+ backbone_center /= len (backbone )
649+ rot_center , rot_axes = self .get_residue_custom_axes (
650+ edges .positions , backbone_center
651+ )
652+ else :
653+ rot_center = (edges [0 ].position + edges [1 ].position ) / 2
654+ rot_axes = self .get_custom_axes (a = rot_center , b = [edges [0 ]], c = np .zeros (3 ))
655+ return rot_center , rot_axes
656+
675657 def get_bonded_axes (self , system , atom , dimensions : np .ndarray ):
676658 r"""Compute UA rotational axes from bonded topology around a heavy atom.
677659
0 commit comments