From bf0c18a0f238c0d80fca98d6fd954a68f02e41d8 Mon Sep 17 00:00:00 2001
From: sjplimp <sjplimp@f3b2605a-c512-4ea7-a41b-209d697bcdaa>
Date: Tue, 6 Sep 2016 23:19:15 +0000
Subject: [PATCH] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@15557
 f3b2605a-c512-4ea7-a41b-209d697bcdaa

---
 src/group.cpp | 109 ++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 78 insertions(+), 31 deletions(-)

diff --git a/src/group.cpp b/src/group.cpp
index d94d67ae7c..1322afcebf 100644
--- a/src/group.cpp
+++ b/src/group.cpp
@@ -1671,39 +1671,86 @@ void Group::inertia(int igroup, double *cm, double itensor[3][3], int iregion)
 void Group::omega(double *angmom, double inertia[3][3], double *w)
 {
   double idiag[3],ex[3],ey[3],ez[3],cross[3];
-  double evectors[3][3];
-  
-  int ierror = MathExtra::jacobi(inertia,idiag,evectors);
-  if (ierror) error->all(FLERR,
-                         "Insufficient Jacobi rotations for group::omega");
-
-  ex[0] = evectors[0][0];
-  ex[1] = evectors[1][0];
-  ex[2] = evectors[2][0];
-  ey[0] = evectors[0][1];
-  ey[1] = evectors[1][1];
-  ey[2] = evectors[2][1];
-  ez[0] = evectors[0][2];
-  ez[1] = evectors[1][2];
-  ez[2] = evectors[2][2];
-  
-  // enforce 3 evectors as a right-handed coordinate system
-  // flip 3rd vector if needed
-  
-  MathExtra::cross3(ex,ey,cross);
-  if (MathExtra::dot3(cross,ez) < 0.0) MathExtra::negate3(ez);
-  
-  // if any principal moment < scaled EPSILON, set to 0.0
+  double evectors[3][3],inverse[3][3];
+
+  // determinant = triple product of rows of inertia matrix
+
+  double determinant = inertia[0][0]*inertia[1][1]*inertia[2][2] +
+    inertia[0][1]*inertia[1][2]*inertia[2][0] +
+    inertia[0][2]*inertia[1][0]*inertia[2][1] -
+    inertia[0][0]*inertia[1][2]*inertia[2][1] -
+    inertia[0][1]*inertia[1][0]*inertia[2][2] -
+    inertia[2][0]*inertia[1][1]*inertia[0][2];
+
+  // non-singular I matrix
+  // use L = Iw, inverting I to solve for w
+
+  if (determinant > EPSILON) {
+
+    inverse[0][0] = inertia[1][1]*inertia[2][2] - inertia[1][2]*inertia[2][1];
+    inverse[0][1] = -(inertia[0][1]*inertia[2][2] - 
+                      inertia[0][2]*inertia[2][1]);
+    inverse[0][2] = inertia[0][1]*inertia[1][2] - inertia[0][2]*inertia[1][1];
+
+    inverse[1][0] = -(inertia[1][0]*inertia[2][2] - 
+                      inertia[1][2]*inertia[2][0]);
+    inverse[1][1] = inertia[0][0]*inertia[2][2] - inertia[0][2]*inertia[2][0];
+    inverse[1][2] = -(inertia[0][0]*inertia[1][2] - 
+                      inertia[0][2]*inertia[1][0]);
+
+    inverse[2][0] = inertia[1][0]*inertia[2][1] - inertia[1][1]*inertia[2][0];
+    inverse[2][1] = -(inertia[0][0]*inertia[2][1] - 
+                      inertia[0][1]*inertia[2][0]);
+    inverse[2][2] = inertia[0][0]*inertia[1][1] - inertia[0][1]*inertia[1][0];
+
+    for (int i = 0; i < 3; i++)
+      for (int j = 0; j < 3; j++)
+        inverse[i][j] /= determinant;
+
+    w[0] = inverse[0][0]*angmom[0] + inverse[0][1]*angmom[1] +
+      inverse[0][2]*angmom[2];
+    w[1] = inverse[1][0]*angmom[0] + inverse[1][1]*angmom[1] +
+      inverse[1][2]*angmom[2];
+    w[2] = inverse[2][0]*angmom[0] + inverse[2][1]*angmom[1] +
+      inverse[2][2]*angmom[2];
+
+  // handle (nearly) singular I matrix
+  // due to 2-atom group or linear molecule
+  // use jacobi() and angmom_to_omega() to calculate valid omega
+
+  } else {
+    int ierror = MathExtra::jacobi(inertia,idiag,evectors);
+    if (ierror) error->all(FLERR,
+                           "Insufficient Jacobi rotations for group::omega");
+
+    ex[0] = evectors[0][0];
+    ex[1] = evectors[1][0];
+    ex[2] = evectors[2][0];
+    ey[0] = evectors[0][1];
+    ey[1] = evectors[1][1];
+    ey[2] = evectors[2][1];
+    ez[0] = evectors[0][2];
+    ez[1] = evectors[1][2];
+    ez[2] = evectors[2][2];
   
-  double max;
-  max = MAX(idiag[0],idiag[1]);
-  max = MAX(max,idiag[2]);
+    // enforce 3 evectors as a right-handed coordinate system
+    // flip 3rd vector if needed
   
-  if (idiag[0] < EPSILON*max) idiag[0] = 0.0;
-  if (idiag[1] < EPSILON*max) idiag[1] = 0.0;
-  if (idiag[2] < EPSILON*max) idiag[2] = 0.0;
+    MathExtra::cross3(ex,ey,cross);
+    if (MathExtra::dot3(cross,ez) < 0.0) MathExtra::negate3(ez);
   
-  // calculate omega using diagonalized inertia matrix
+    // if any principal moment < scaled EPSILON, set to 0.0
   
-  MathExtra::angmom_to_omega(angmom,ex,ey,ez,idiag,w);
+    double max;
+    max = MAX(idiag[0],idiag[1]);
+    max = MAX(max,idiag[2]);
+    
+    if (idiag[0] < EPSILON*max) idiag[0] = 0.0;
+    if (idiag[1] < EPSILON*max) idiag[1] = 0.0;
+    if (idiag[2] < EPSILON*max) idiag[2] = 0.0;
+    
+    // calculate omega using diagonalized inertia matrix
+    
+    MathExtra::angmom_to_omega(angmom,ex,ey,ez,idiag,w);
+  }
 }
-- 
GitLab