2"""principalAxisCalculator
4Opens an XYZ file and computes the moments of inertia and principal
5axes for the structure in the XYZ file. Optionally rotates the
6structure so that the long axis (that with the smallest eigenvalue) is
7pointing along the z-axis.
9Usage: principalAxisCalculator
12 -h, --help show this help
13 -x, --xyz=... use specified XYZ (.xyz) file for the structure
14 -o, --out=... rotate the structure so that the smallest eigenvalue
15 of the rotation matrix points along the z-axis.
18 principalAxisCalculator -x junk.xyz -o rot.xyz
22__author__ = "Dan Gezelter (gezelter@nd.edu)"
23__copyright__ = "Copyright (c) 2004-present The University of Notre Dame. All Rights Reserved."
32from functools import reduce
77 'O_TIP4P-Ew': 15.9994,
108 return reduce(add, seq)
110def readFile(XYZFileName):
111 print("reading XYZ file")
113 XYZFile = open(XYZFileName, 'r')
114 # Find number of atoms first
115 line = XYZFile.readline()
119 line = XYZFile.readline()
120 for i in range(nAtoms):
121 line = XYZFile.readline()
124 indices.append(myIndex)
126 atypes.append(atomType)
130 positions.append([x, y, z])
141 for i in range(0, len(indices)):
142 myMass = mass_table[atypes[i]]
144 Xcom = Xcom + myMass * positions[i][0]
145 Ycom = Ycom + myMass * positions[i][1]
146 Zcom = Zcom + myMass * positions[i][2]
147 totalMass = totalMass + myMass
149 Xcom = Xcom / totalMass
150 Ycom = Ycom / totalMass
151 Zcom = Zcom / totalMass
153 COM = [Xcom, Ycom, Zcom]
161 #find inertia tensor matrix elements
163 I = numpy.zeros((3, 3), numpy.float)
165 for i in range(0, len(indices)):
166 myMass = mass_table[atypes[i]]
168 dx = positions[i][0] - COM[0]
169 dy = positions[i][1] - COM[1]
170 dz = positions[i][2] - COM[2]
172 I[0, 0] = I[0, 0] + myMass * ( dy * dy + dz * dz )
173 I[1, 1] = I[1, 1] + myMass * ( dx * dx + dz * dz )
174 I[2, 2] = I[2, 2] + myMass * ( dx * dx + dy * dy )
176 I[0, 1] = I[0, 1] - myMass * ( dx * dy )
177 I[0, 2] = I[0, 2] - myMass * ( dx * dz )
179 I[1, 2] = I[1, 2] - myMass * ( dy * dz )
185 print("Inertia Tensor:")
189 (evals, evects) = numpy.linalg.eig(I)
197 return (COM, evals, evects)
199def writeFile(OutFileName):
201 (COM, evals, evects) = findMoments()
203 # we need to re-order the axes so that the smallest moment of inertia
204 # (which corresponds to the long axis of the molecule) is along the z-axis
205 # we'll just reverse the order of the three axes:
207 axOrder = numpy.argsort(evals)
208 RotMat = numpy.zeros((3, 3), numpy.float)
209 RotMat[0] = evects[axOrder[2]]
210 RotMat[1] = evects[axOrder[1]]
211 RotMat[2] = evects[axOrder[0]]
213 q = [0.0, 0.0, 0.0, 0.0]
214 myEuler = [0.0, 0.0, 0.0]
216 # RotMat to Quat code is out of OpenMD's SquareMatrix3.hpp code:
218 t = RotMat[0][0] + RotMat[1][1] + RotMat[2][2] + 1.0
221 s = 0.5 / math.sqrt( t )
223 q[1] = (RotMat[1][2] - RotMat[2][1]) * s
224 q[2] = (RotMat[2][0] - RotMat[0][2]) * s
225 q[3] = (RotMat[0][1] - RotMat[1][0]) * s
231 if( ad1 >= ad2 and ad1 >= ad3 ):
232 s = 0.5 / math.sqrt( 1.0 + RotMat[0][0] - RotMat[1][1] - RotMat[2][2] )
233 q[0] = (RotMat[1][2] - RotMat[2][1]) * s
235 q[2] = (RotMat[0][1] + RotMat[1][0]) * s
236 q[3] = (RotMat[0][2] + RotMat[2][0]) * s
237 elif ( ad2 >= ad1 and ad2 >= ad3 ):
238 s = 0.5 / math.sqrt( 1.0 + RotMat[1][1] - RotMat[0][0] - RotMat[2][2] )
239 q[0] = (RotMat[2][0] - RotMat[0][2] ) * s
240 q[1] = (RotMat[0][1] + RotMat[1][0]) * s
242 q[3] = (RotMat[1][2] + RotMat[2][1]) * s
244 s = 0.5 / math.sqrt( 1.0 + RotMat[2][2] - RotMat[0][0] - RotMat[1][1] )
245 q[0] = (RotMat[0][1] - RotMat[1][0]) * s
246 q[1] = (RotMat[0][2] + RotMat[2][0]) * s
247 q[2] = (RotMat[1][2] + RotMat[2][1]) * s
249 print("Quaternions:")
252 theta = math.acos(RotMat[2][2])
253 ctheta = RotMat[2][2]
254 stheta = math.sqrt(1.0 - ctheta * ctheta)
256 if (math.fabs(stheta) < 1e-6):
258 phi = math.atan2(-RotMat[1][0], RotMat[0][0])
260 phi = math.atan2(RotMat[2][0], -RotMat[2][1])
261 psi = math.atan2(RotMat[0][2], RotMat[1][2])
264 phi = phi + 2.0 * math.pi;
267 psi = psi + 2.0 * math.pi;
269 myEuler[0] = phi * 180.0 / math.pi;
270 myEuler[1] = theta * 180.0 / math.pi;
271 myEuler[2] = psi * 180.0 / math.pi;
273 print("Euler Angles:")
276 nAtoms = len(indices)
278 print("writing output XYZ file")
280 OutFile = open(OutFileName, 'w')
282 OutFile.write('%10d\n' % (nAtoms))
285 for i in range(nAtoms):
287 dx = positions[i][0] - COM[0]
288 dy = positions[i][1] - COM[1]
289 dz = positions[i][2] - COM[2]
291 r = numpy.array([dx, dy, dz])
292 rnew = numpy.dot(RotMat, r)
294 OutFile.write('%s\t%f\t%f\t%f\t%d\n' % (atypes[i], rnew[0], rnew[1], rnew[2], i))
299 opts, args = getopt.getopt(argv, "hx:o:", ["help", "xyz=", "out="])
300 except getopt.GetoptError:
303 for opt, arg in opts:
304 if opt in ("-h", "--help"):
307 elif opt in ("-x", "--xyz"):
309 global _haveXYZFileName
311 elif opt in ("-o", "--out"):
313 global _haveOutFileName
317 if (_haveXYZFileName != 1):
319 print("No xyz file was specified")
322 readFile(XYZFileName)
324 if (_haveOutFileName == 1):
325 writeFile(OutFileName)
329if __name__ == "__main__":
330 if len(sys.argv) == 1: