How do I calculate a dihedral angle given Cartesian coordinates?

I have the $(x, y, z)$ coordinates for four points in space (atoms). How do I calculate the dihedral angle ($\phi$ in the below pictures from WP)?

I'm somewhat hazy on my math, and am trying to implement some function for this in Python, so a lower programmer's level would be appreciated.

enter image description here

enter image description here


Solution 1:

It seems a single complete answer has not yet been given. Here is what I think is the most straightforward and numerically stable solution, expressed in a way that is easy to implement. It overlaps a lot with Vhailor's and Jyrki's answers, so I've marked it community wiki.

  1. Given the coordinates of the four points, obtain the vectors $b_1$, $b_2$, and $b_3$ by vector subtraction.

  2. Let me use the nonstandard notation $\langle v \rangle$ to denote $v/\lVert v \rVert$, the unit vector in the direction of the vector $v$. Compute $n_1 = \langle b_1 \times b_2 \rangle$ and $n_2 = \langle b_2 \times b_3 \rangle$, the normal vectors to the planes containing $b_1$ and $b_2$, and $b_2$ and $b_3$ respectively. The angle we seek is the same as the angle between $n_1$ and $n_2$.

  3. The three vectors $n_1$, $\langle b_2 \rangle$, and $m_1 := n_1 \times \langle b_2 \rangle$ form an orthonormal frame. Compute the coordinates of $n_2$ in this frame: $x = n_1 \cdot n_2$ and $y = m_1 \cdot n_2$. (You don't need to compute $\langle b_2 \rangle \cdot n_2$ as it should always be zero.)

  4. The dihedral angle, with the correct sign, is $\operatorname{atan2}(y,x)$.

(The reason I recommend the two-argument $\operatorname{atan2}$ function to the traditional $\cos^{-1}$ in this case is both because it naturally produces an angle over a range of $2\pi$, and because $\cos^{-1}$ is poorly conditioned when the angle is close to $0$ or $\pm\pi$.)

Solution 2:

I think what you are looking for (if I understood the picture correctly) is the angle between the planes spanned by b1,b2 and b2,b3.

One way to do this is to calculate the vector angle between $b_1 \times b_2$ and $b_2 \times b_3$, where $\times$ is the cross product of vectors (you should be able to find the formula on wikipedia). You can calculate this angle with various methods, like the one mentionned by Ross. Depending on whether b1 and b3 lie on the plane of the second drawing, my answer and Ross's might give the same result.

Solution 3:

Please correct me, if I interpret your question and/or picture incorrectly. Unfortunately that is a live possibility. Judging from the bottom figure I take it that you want the following angle. First you orthogonally project vectors $-\vec{b}_1$ and $\vec{b}_3$ to the plane that is orthogonal to the vector $\vec{b}_2$. The angle $\phi$ is then the angle between these two projections. Note the minus sign. This is necessary, because you seem to want to have $\phi=\pi$, when the two projections point in the same direction.

If this is the correct interpretation, then you can proceed as follows. The projection mapping is given by the formula $$ p(\vec{x})=\vec{x}-\frac{\vec{x}\cdot\vec{b}_2}{\vec{b}_2\cdot\vec{b}_2}\vec{b}_2. $$ Then you get the cosine of the angle $\phi$ from the usual formula $$ -p(\vec{b}_1)\cdot p(\vec{b}_3)=|p(\vec{b}_1)|\, |p(\vec{b}_3)|\cos\phi. $$ This gives you $\cos\phi$ and leaves you with a choice of two angles - one in the interval $[0,\pi]$ the other in $[-\pi,0]$. We need to know the sign of $\sin\phi$ to decide between the two. I read your bottom picture in the way the vector $\vec{b}_2$ is pointing away from the camera. This determines the orientation. We take advantage of this by computing the cross product $$ \vec{u}=p(\vec{b}_1)\times p(\vec{b}_3). $$ Then the inner product $\vec{u}\cdot \vec{b}_2$ has the same sign as $\sin\phi$ --- unless I made a systematic error :-). Let's check. If the angle in your picture is in the interval $[0,\pi]$, then I can twist the thumb of my right hand along $p(-\vec{b}_1)$, the index finger along $p(\vec{b}_3)$, so the middle finger will point in the direction opposite to $\vec{b}_2$. Therefore I need to use $p(\vec{b}_1)$ as opposed to its negative.

Solution 4:

To clear up an ambiguity to the otherwise great response by Rahul, using the nonstandard notation for his unit vectors could give two possible answers.

For the orthogonal vector $n_2 =⟨b_2×b_3⟩$ this is not the the cross product normalized by it's components... $$n_2 = b_2×b_3$$ Such that... $$⟨n_2⟩=\frac{n_2}{∥n_2∥}$$ Rather, it is the cross product of the normalized vectors $b_2$ and $b_3$... $$n_2 = ⟨b_2⟩×⟨b_3⟩$$