Skip to content

Commit 64e0829

Browse files
committed
introduced get_terminal_axes and get_non_terminal_axes functions called from get_residue_axes and get_UA_axes
1 parent 3b1faf6 commit 64e0829

1 file changed

Lines changed: 111 additions & 129 deletions

File tree

‎CodeEntropy/levels/axes.py‎

Lines changed: 111 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)