From 948ff2957902d958b0130102b9e00bbca6eb0133 Mon Sep 17 00:00:00 2001 From: Chris Maffeo <cmaffeo2@illinois.edu> Date: Thu, 7 Mar 2019 16:01:58 -0600 Subject: [PATCH] Added coords.py from mrdna --- arbdmodel/coords.py | 204 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 arbdmodel/coords.py diff --git a/arbdmodel/coords.py b/arbdmodel/coords.py new file mode 100644 index 0000000..41628e8 --- /dev/null +++ b/arbdmodel/coords.py @@ -0,0 +1,204 @@ +import numpy as np +from scipy.optimize import newton + +def minimizeRmsd(coordsB, coordsA, weights=None, maxIter=100): + ## Going through many iterations wasn't really needed + tol = 1 + count = 0 + + R = np.eye(3) + comB = np.zeros([3,]) + cNext = coordsB + + while tol > 1e-6: + q,cB,comA = _minimizeRmsd(cNext,coordsA, weights) + R = R.dot(quaternion_to_matrix(q)) + assert( np.all(np.isreal( R )) ) + + comB += cB + cLast = cNext + cNext = (coordsB-comB).dot(R) + + tol = np.sum(((cNext-cLast)**2)[:]) / np.max(np.shape(coordsB)) + if count > maxIter: + Exception("Exceeded maxIter (%d)" % maxIter) + count += 1 + + print("%d iterations",count) + return R, comB, comA + + +def minimizeRmsd(coordsB, coordsA, weights=None): + q,comA,comB = _minimizeRmsd(coordsB, coordsA, weights) + assert( np.all(np.isreal( q )) ) + return quaternion_to_matrix(q),comA,comB + + +## http://onlinelibrary.wiley.com/doi/10.1002/jcc.21439/full +def _minimizeRmsd(coordsB, coordsA, weights=None): + A = coordsA + B = coordsB + + shapeA,shapeB = [np.shape(X) for X in (A,B)] + for s in (shapeA,shapeB): assert( len(s) == 2 ) + + A,B = [X.T if s[1] > s[0] else X for X,s in zip([A,B],(shapeA,shapeB))] # TODO: print warning + + shapeA,shapeB = [np.shape(X) for X in (A,B)] + assert( shapeA == shapeB ) + for X,s in zip((A,B),(shapeA,shapeB)): + assert( s[1] == 3 and s[0] >= s[1] ) + + # if weights is None: weights = np.ones(len(A)) + if weights is None: + comA,comB = [np.mean( X, axis=0 ) for X in (A,B)] + else: + assert( len(weights[:]) == len(B) ) + W = np.diag(weights) + comA,comB = [np.sum( W.dot(X), axis=0 ) / np.sum(W) for X in (A,B)] + + A = np.array( A-comA ) + B = np.array( B-comB ) + + if weights is None: + s = A.T.dot(B) + else: + s = A.T.dot(W.dot(B)) + + sxx,sxy,sxz = s[0,:] + syx,syy,syz = s[1,:] + szx,szy,szz = s[2,:] + + K = [[ sxx+syy+szz, syz-szy, szx-sxz, sxy-syx], + [syz-szy, sxx-syy-szz, sxy+syx, sxz+szx], + [szx-sxz, sxy+syx, -sxx+syy-szz, syz+szy], + [sxy-syx, sxz+szx, syz+szy, -sxx-syy+szz]] + K = np.array(K) + + # GA = np.trace( A.T.dot(W.dot(A)) ) + # GB = np.trace( B.T.dot(W.dot(B)) ) + + ## Finding GA/GB can be done more quickly + # I = np.eye(4) + # x0 = (GA+GB)*0.5 + # vals = newtoon(lambda x: np.det(K-x*I), x0 = x0) + + vals, vecs = np.linalg.eig(K) + i = np.argmax(vals) + q = vecs[:,i] + + # RMSD = np.sqrt( (GA+GB-2*vals[i]) / len(A) ) + # print("CHECK:", K.dot(q)-vals[i]*q ) + return q, comB, comA + +def quaternion_to_matrix(q): + assert(len(q) == 4) + + ## It looks like the wikipedia article I used employed a less common convention for q (see below + ## http://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions#Rotation_matrix_.E2.86.94_quaternion + # q1,q2,q3,q4 = q + # R = [[1-2*(q2*q2 + q3*q3), 2*(q1*q2 - q3*q4), 2*(q1*q3 + q2*q4)], + # [ 2*(q1*q2 + q3*q4), 1-2*(q1*q1 + q3*q3), 2*(q2*q3 - q1*q4)], + # [ 2*(q1*q3 - q2*q4), 2*(q1*q4 + q2*q3), 1-2*(q2*q2 + q1*q1)]] + + q = q / np.linalg.norm(q) + q0,q1,q2,q3 = q + R = [[1-2*(q2*q2 + q3*q3), 2*(q1*q2 - q3*q0), 2*(q1*q3 + q2*q0)], + [ 2*(q1*q2 + q3*q0), 1-2*(q1*q1 + q3*q3), 2*(q2*q3 - q1*q0)], + [ 2*(q1*q3 - q2*q0), 2*(q1*q0 + q2*q3), 1-2*(q2*q2 + q1*q1)]] + + return np.array(R) + +def quaternion_from_matrix( R ): + e1 = R[0] + e2 = R[1] + e3 = R[2] + + # d1 = 0.5 * np.sqrt( 1+R[0,0]+R[1,1]+R[2,2] ) + # d2 = 0.5 * np.sqrt( 1+R[0,0]-R[1,1]-R[2,2] ) + # d2 = 0.5 * np.sqrt( 1+R[0,0]-R[1,1]-R[2,2] ) + # d2 = 0.5 * np.sqrt( 1+R[0,0]-R[1,1]-R[2,2] ) + + d1 = 1+R[0,0]+R[1,1]+R[2,2] + d2 = 1+R[0,0]-R[1,1]-R[2,2] + d3 = 1-R[0,0]+R[1,1]-R[2,2] + d4 = 1-R[0,0]-R[1,1]+R[2,2] + + maxD = max((d1,d2,d3,d4)) + d = 0.5 / np.sqrt(maxD) + + if d1 == maxD: + return np.array(( 1.0/(4*d), + d * (R[2,1]-R[1,2]), + d * (R[0,2]-R[2,0]), + d * (R[1,0]-R[0,1]) )) + elif d2 == maxD: + return np.array(( d * (R[2,1]-R[1,2]), + 1.0/(4*d), + d * (R[0,1]+R[1,0]), + d * (R[0,2]+R[2,0]) )) + elif d3 == maxD: + return np.array(( d * (R[0,2]-R[2,0]), + d * (R[0,1]+R[1,0]), + 1.0/(4*d), + d * (R[1,2]+R[2,1]) )) + elif d4 == maxD: + return np.array(( d * (R[1,0]-R[0,1]), + d * (R[0,2]+R[2,0]), + d * (R[1,2]+R[2,1]), + 1.0/(4*d) )) + +def rotationAboutAxis(axis,angle, normalizeAxis=True): + if normalizeAxis: axis = axis / np.linalg.norm(axis) + angle = angle * 0.5 * np.pi/180 + cos = np.cos( angle ) + sin = np.sin( angle ) + q = [cos] + [sin*x for x in axis] + return quaternion_to_matrix(q) + +def readArbdCoords(fname): + coords = [] + with open(fname) as fh: + for line in fh: + coords.append([float(x) for x in line.split()[1:]]) + return np.array(coords) + +def readAvgArbdCoords(psf,pdb,dcd,rmsdThreshold=3.5): + import MDAnalysis as mda + + usel = mda.Universe(psf, dcd) + sel = usel.select_atoms("name D*") + + # r0 = ref.xyz[0,ids,:] + ts = usel.trajectory[-1] + r0 = sel.positions + pos = [] + for t in range(ts.frame-1,-1,-1): + usel.trajectory[t] + R,comA,comB = minimizeRmsd(sel.positions,r0) + r = np.array( [(r-comA).dot(R)+comB for r in sel.positions] ) + rmsd = np.mean( (r-r0)**2 ) + r = np.array( [(r-comA).dot(R)+comB for r in usel.atoms.positions] ) + pos.append( r ) + if rmsd > rmsdThreshold**2: + break + t0=t+1 + print( "Averaging coordinates in %s after frame %d" % (dcd, t0) ) + + pos = np.mean(pos, axis=0) + return pos + +def unit_quat_conversions(): + for axis in [[0,0,1],[1,1,1],[1,0,0],[-1,-2,0]]: + for angle in np.linspace(-180,180,10): + R = rotationAboutAxis(axis, angle) + R2 = quaternion_to_matrix( quaternion_from_matrix( R ) ) + if not np.all( np.abs(R-R2) < 0.01 ): + import pdb + pdb.set_trace() + quaternion_to_matrix( quaternion_from_matrix( R ) ) + + +if __name__ == "__main__": + unit_quat_conversions() + -- GitLab