PySSA aims to combine PyMOL and ColabFold to enable the prediction and analysis of 3D protein structures for the scientific end-user. v1.0 has been released on July 10, 2024.
This is a toolkit for rapidly inspecting multiple maps and models. The right & left arrow keys navigate sequentially through the amino acid chain, but alternating between two structures (could be NCS-related chains or models solved in different space groups). Each view is rendered in a consistent orientation (the default is centered on alpha carbon, with nitrogen right, carbonyl left & beta carbon up). The view can be customized. It is necessary to edit the script to define the behavior for the problem at hand.
INSTALLATION
Three components are needed:
A Python module, support.py, giving matrix algebra & Kabsch-style structure alignment (source code given below). This module must be in the current working directory, and PyMol started from the command line, e.g., on MacOSX:
Atomic coordinates must be loaded first, and if appropriate, maps and isomesh levels. An example .pml file is shown here, consisting of two structural models solved in different space groups:
A final pml is then loaded (@consistent.pml) to define the viewing sequence for the problem at hand. An example is shown here. (The python / python end construct will not work in PyMOL versions below 1.0.)
pythonfromsupportimportmatrix,residue,std_residue,kabsch_rotationfromsupportimportview_matrix,new_set_view# This section is the generic preset view--DO NOT EDIT ########################## Centered on CA, CB up, N left, C=O rightpreset_view=view_matrix((0.295077115,0.440619737,-0.847830832,-0.408159286,0.860427082,0.305112839,0.863915384,0.256014407,0.433732271,0.,0.,-30.,0.,0.,0.,26.,33.,0.))preset_view.difference_to_this_CA=matrix.col((0.,0.,0.,))preset_std_residue=std_residue()preset_std_residue.N=matrix.col((16.1469993591,33.0660018921,-20.0760002136))preset_std_residue.CA=matrix.col((15.4309997559,34.2599983215,-20.4640007019))preset_std_residue.C=matrix.col((15.2889995575,34.1189994812,-21.9699993134))preset_std_residue.CB=matrix.col((16.2469997406,35.516998291,-20.1380004883))# END GENERIC VIEW #############################################################defview_preset():cmd.current_std_view=preset_viewcmd.current_std_residue=preset_std_residueview_preset()# EDIT THIS SECTION TO REFLECT THE PROBLEM AT HAND ############################## In this example, there are two objects: structure mod02 & mod03# We will be looking at chain B in each structure, each having 262 residues,# for a total of 524 amino acids to view. # For each of 524 views, we select the appropriate map and isomeshcmd.no_of_structures=2cmd.global_ptr=1*cmd.no_of_structurescmd.last_residue=preset_std_residuedefseqwalk():chain_id="B"ifcmd.global_ptr<524:resid=cmd.global_ptr/2this_object=cmd.global_ptr%2ifthis_object==0:atoms=cmd.get_model("object mod03 and chain %s"%chain_id)obj_id="mod03"map="map3"mesh="msh3"else:atoms=cmd.get_model("object mod02 and chain %s"%chain_id)obj_id="mod02"map="map2"mesh="msh2"this=residue(chain_id,resid,atoms)cmd.last_residue=this#remember this residue in case reset command is issuedprint"Centering on /%s//%s/%s%d/CA; waiting for right mouse key"%(obj_id,chain_id,this.residue_type,resid,)cmd.center("/%s//%s/%d/CA"%(obj_id,chain_id,resid))cmd.isomesh(name=mesh,map=map,level=2.0,selection="object %s and chain %s and resid %d"%(obj_id,chain_id,resid),buffer=8)# No more edits below this linekr=kabsch_rotation(cmd.current_std_residue.sites(),this.sites())view_matrix=new_set_view(cmd.current_std_view,kr,this,verbose=False).view_matrix()cmd.set_view(view_matrix)cmd.global_ptr=cmd.global_ptr+1# END OF PROBLEM-SPECIFIC SECTION TO EDIT #####################################defseqwalk2():cmd.global_ptr-=2seqwalk()cmd.set_key("right",seqwalk)cmd.set_key("left",seqwalk2)defview_reset():cmd.current_std_view=view_matrix(cmd.get_view()).set_diff_to_CA(cmd.last_residue)cmd.current_std_residue=cmd.last_residuedefview_goto(number):cmd.global_ptr=cmd.no_of_structures*numbercmd.extend("view_reset",view_reset)# After customizing the view with GUI mouse & clicks, make the# view persistent by typing "view reset"cmd.extend("view_preset",view_preset)# Forget the user-customized view, go back to the generic view# defined at the beginning of the programcmd.extend("view_goto",view_goto)# Example: view_goto(36) --resets at residue 36pythonend
USAGE
Once the code is set up as described above, you must mouse-click in PyMOL's 3D display window to enable the right & left arrow keys. These keys will navigate through the amino acid sequence, displaying each residue in a predefined orientation. Maps, if defined, will be redrawn for each view.
Three commands can be issued at the prompt:
view_goto(N) -- will go to residue N. The view will not actually change until you mouse click in the 3D display window and hit the right arrow key.
view_reset -- will accept the current view (in relation to the present alpha carbon) as the default. This is extremely useful for preparing views for presentation, which compare sidechains or ligands in two homologous structures. For example, if you are interested in a Mg++ ion adjacent to residue 56, you will first use the arrow keys to center on the residue 56 alpha carbon. Then recenter on the Mg++ ion, and rotate to the exact view desired. Typing view_reset will then produce the same view of the Mg++ ion in both structures.
view_preset -- revert back to the generic view centered on the alpha carbon.
METHODOLOGY
Four atoms of the current residue (N, CA, CB, and C) are used for a Kabsch-style alignment against a reference orientation. For glycines, a hypothetical beta-carbon is modelled (but not shown) based on tetrahedral geometry. A known limitation of the approach is that the alignment is very local, i.e., neighboring residues may not precisely align between structures.
Adaptation of PyMOL's set_view command to display aligned views of separate structures was suggested by Herb Klei.
SUPPORTING MODULE
The following Python module, support.py, must be placed in the current working directory. Code is based on CCTBX (cctbx.sf.net) and is released under the cctbx license.
importmath###################################################################### This code has been adapted from cctbx.sf.net, file scitbx/matrix.pyflex=Nonenumpy=Noneclassrec(object):container_type=tupledef__init__(self,elems,n):assertlen(n)==2if(notisinstance(elems,self.container_type)):elems=self.container_type(elems)assertlen(elems)==n[0]*n[1]self.elems=elemsself.n=tuple(n)defn_rows(self):returnself.n[0]defn_columns(self):returnself.n[1]def__neg__(self):returnrec([-eforeinself.elems],self.n)def__add__(self,other):assertself.n==other.na=self.elemsb=other.elemsreturnrec([a[i]+b[i]foriinxrange(len(a))],self.n)def__sub__(self,other):assertself.n==other.na=self.elemsb=other.elemsreturnrec([a[i]-b[i]foriinxrange(len(a))],self.n)def__mul__(self,other):if(nothasattr(other,"elems")):if(notisinstance(other,(list,tuple))):returnrec([x*otherforxinself.elems],self.n)other=col(other)a=self.elemsar=self.n_rows()ac=self.n_columns()b=other.elemsif(other.n_rows()!=ac):raiseRuntimeError("Incompatible matrices:\n"" self.n: %s\n"" other.n: %s"%(str(self.n),str(other.n)))bc=other.n_columns()if(ac==0):# Roy Featherstone, Springer, New York, 2007, p. 53 footnotereturnrec((0,)*(ar*bc),(ar,bc))result=[]foriinxrange(ar):forkinxrange(bc):s=0forjinxrange(ac):s+=a[i*ac+j]*b[j*bc+k]result.append(s)if(ar==bc):returnsqr(result)returnrec(result,(ar,bc))def__rmul__(self,other):"scalar * matrix"if(isinstance(other,rec)):# work around odd Python 2.2 featurereturnother.__mul__(self)returnself*otherdeftranspose_multiply(self,other=None):a=self.elemsar=self.n_rows()ac=self.n_columns()if(otherisNone):result=[0]*(ac*ac)jac=0forjinxrange(ar):ik=0foriinxrange(ac):forkinxrange(ac):result[ik]+=a[jac+i]*a[jac+k]ik+=1jac+=acreturnsqr(result)b=other.elemsassertother.n_rows()==ar,"Incompatible matrices."bc=other.n_columns()result=[0]*(ac*bc)jac=0jbc=0forjinxrange(ar):ik=0foriinxrange(ac):forkinxrange(bc):result[ik]+=a[jac+i]*b[jbc+k]ik+=1jac+=acjbc+=bcif(ac==bc):returnsqr(result)returnrec(result,(ac,bc))def__div__(self,other):returnrec([e/otherforeinself.elems],self.n)def__truediv__(self,other):returnrec([e/otherforeinself.elems],self.n)def__floordiv__(self,other):returnrec([e//otherforeinself.elems],self.n)def__mod__(self,other):returnrec([e%otherforeinself.elems],self.n)def__call__(self,ir,ic):returnself.elems[ir*self.n_columns()+ic]def__len__(self):returnlen(self.elems)def__getitem__(self,i):returnself.elems[i]defas_float(self):returnrec([float(e)foreinself.elems],self.n)defas_int(self,rounding=True):ifrounding:returnrec([int(round(e))foreinself.elems],self.n)else:returnrec([int(e)foreinself.elems],self.n)defeach_abs(self):returnrec([abs(e)foreinself.elems],self.n)defmin(self):result=Noneforeinself.elems:if(resultisNoneorresult>e):result=ereturnresultdefmax(self):result=Noneforeinself.elems:if(resultisNoneorresult<e):result=ereturnresultdefmin_index(self):result=Noneforiinxrange(len(self.elems)):if(resultisNoneorself.elems[result]>self.elems[i]):result=ireturnresultdefmax_index(self):result=Noneforiinxrange(len(self.elems)):if(resultisNoneorself.elems[result]<self.elems[i]):result=ireturnresultdefsum(self):result=0foreinself.elems:result+=ereturnresultdefproduct(self):result=1foreinself.elems:result*=ereturnresultdeftrace(self):assertself.n_rows()==self.n_columns()n=self.n_rows()result=0foriinxrange(n):result+=self.elems[i*n+i]returnresultdefnorm_sq(self):result=0foreinself.elems:result+=e*ereturnresultdefround(self,digits):returnrec([round(x,digits)forxinself.elems],self.n)def__abs__(self):assertself.n_rows()==1orself.n_columns()==1returnmath.sqrt(self.norm_sq())length_sq=norm_sq# for compatibility with scitbx/vec3.hlength=__abs__defnormalize(self):returnself/abs(self)defdot(self,other=None):result=0a=self.elemsif(otherisNone):foriinxrange(len(a)):v=a[i]result+=v*velse:assertlen(self.elems)==len(other.elems)b=other.elemsforiinxrange(len(a)):result+=a[i]*b[i]returnresultdefcross(self,other):assertself.nin((3,1),(1,3))assertself.n==other.na=self.elemsb=other.elemsreturnrec((a[1]*b[2]-b[1]*a[2],a[2]*b[0]-b[2]*a[0],a[0]*b[1]-b[0]*a[1]),self.n)defis_r3_rotation_matrix_rms(self):if(self.n!=(3,3)):raiseRuntimeError("Not a 3x3 matrix.")rtr=self.transpose_multiply()return(rtr-identity(n=3)).norm_sq()**0.5defis_r3_rotation_matrix(self,rms_tolerance=1e-8):returnself.is_r3_rotation_matrix_rms()<rms_tolerancedefunit_quaternion_as_r3_rotation_matrix(self):assertself.nin[(1,4),(4,1)]q0,q1,q2,q3=self.elemsreturnsqr((2*(q0*q0+q1*q1)-1,2*(q1*q2-q0*q3),2*(q1*q3+q0*q2),2*(q1*q2+q0*q3),2*(q0*q0+q2*q2)-1,2*(q2*q3-q0*q1),2*(q1*q3-q0*q2),2*(q2*q3+q0*q1),2*(q0*q0+q3*q3)-1))defr3_rotation_matrix_as_unit_quaternion(self):# Based on work by:# Shepperd (1978), J. Guidance and Control, 1, 223-224.# Sam Buss, http://math.ucsd.edu/~sbuss/MathCG# Robert Hanson, jmol/Jmol/src/org/jmol/util/Quaternion.javaif(self.n!=(3,3)):raiseRuntimeError("Not a 3x3 matrix.")m00,m01,m02,m10,m11,m12,m20,m21,m22=self.elemstrace=m00+m11+m22if(trace>=0.5):w=(1+trace)**0.5d=w+ww*=0.5x=(m21-m12)/dy=(m02-m20)/dz=(m10-m01)/delse:if(m00>m11):if(m00>m22):mx=0else:mx=2elif(m11>m22):mx=1else:mx=2invalid_cutoff=0.8# not critical; true value is closer to 0.83invalid_message="Not a r3_rotation matrix."if(mx==0):x_sq=1+m00-m11-m22if(x_sq<invalid_cutoff):raiseRuntimeError(invalid_message)x=x_sq**0.5d=x+xx*=0.5w=(m21-m12)/dy=(m10+m01)/dz=(m20+m02)/delif(mx==1):y_sq=1+m11-m00-m22if(y_sq<invalid_cutoff):raiseRuntimeError(invalid_message)y=y_sq**0.5d=y+yy*=0.5w=(m02-m20)/dx=(m10+m01)/dz=(m21+m12)/delse:z_sq=1+m22-m00-m11if(z_sq<invalid_cutoff):raiseRuntimeError(invalid_message)z=z_sq**0.5d=z+zz*=0.5w=(m10-m01)/dx=(m20+m02)/dy=(m21+m12)/dreturncol((w,x,y,z))defunit_quaternion_product(self,other):assertself.nin[(1,4),(4,1)]assertother.nin[(1,4),(4,1)]q0,q1,q2,q3=self.elemso0,o1,o2,o3=other.elemsreturncol((q0*o0-q1*o1-q2*o2-q3*o3,q0*o1+q1*o0+q2*o3-q3*o2,q0*o2-q1*o3+q2*o0+q3*o1,q0*o3+q1*o2-q2*o1+q3*o0))defaxis_and_angle_as_unit_quaternion(self,angle,deg=False):assertself.nin((3,1),(1,3))if(deg):angle*=math.pi/180h=angle*0.5c,s=math.cos(h),math.sin(h)u,v,w=self.normalize().elemsreturncol((c,u*s,v*s,w*s))defaxis_and_angle_as_r3_rotation_matrix(self,angle,deg=False):uq=self.axis_and_angle_as_unit_quaternion(angle=angle,deg=deg)returnuq.unit_quaternion_as_r3_rotation_matrix()defrt_for_rotation_around_axis_through(self,point,angle,deg=False):assertself.nin((3,1),(1,3))assertpoint.nin((3,1),(1,3))r=(point-self).axis_and_angle_as_r3_rotation_matrix(angle=angle,deg=deg)returnrt((r,self-r*self))defortho(self):assertself.nin((3,1),(1,3))x,y,z=self.elemsa,b,c=abs(x),abs(y),abs(z)ifc<=aandc<=b:returncol((-y,x,0))ifb<=aandb<=c:returncol((-z,0,x))returncol((0,-z,y))defrotate_around_origin(self,axis,angle,deg=False):assertself.nin((3,1),(1,3))assertaxis.n==self.nifdeg:angle*=math.pi/180n=axis.normalize()x=selfc,s=math.cos(angle),math.sin(angle)returnx*c+n*n.dot(x)*(1-c)+n.cross(x)*sdefrotate(self,axis,angle,deg=False):importwarningswarnings.warn(message="The .rotate() method has been renamed to .rotate_around_origin()"" for clarity. Please update the code calling this method.",category=DeprecationWarning,stacklevel=2)returnself.rotate_around_origin(axis=axis,angle=angle,deg=deg)defvector_to_001_rotation(self,sin_angle_is_zero_threshold=1.e-10,is_normal_vector_threshold=1.e-10):assertself.nin((3,1),(1,3))x,y,c=self.elemsxxyy=x*x+y*yif(abs(xxyy+c*c-1)>is_normal_vector_threshold):raiseRuntimeError("self is not a normal vector.")s=(xxyy)**0.5if(s<sin_angle_is_zero_threshold):if(c>0):returnsqr((1,0,0,0,1,0,0,0,1))returnsqr((1,0,0,0,-1,0,0,0,-1))us=yvs=-xu=us/sv=vs/soc=1-creturnsqr((c+u*u*oc,u*v*oc,vs,u*v*oc,c+v*v*oc,-us,-vs,us,c))defouter_product(self,other=None):if(otherisNone):other=selfassertself.n[0]==1orself.n[1]==1assertother.n[0]==1orother.n[1]==1result=[]forainself.elems:forbinother.elems:result.append(a*b)returnrec(result,(len(self.elems),len(other.elems)))defcos_angle(self,other,value_if_undefined=None):self_norm_sq=self.norm_sq()if(self_norm_sq==0):returnvalue_if_undefinedother_norm_sq=other.norm_sq()if(other_norm_sq==0):returnvalue_if_undefinedd=self_norm_sq*other_norm_sqif(d==0):returnvalue_if_undefinedreturnself.dot(other)/math.sqrt(d)defangle(self,other,value_if_undefined=None,deg=False):cos_angle=self.cos_angle(other=other)if(cos_angleisNone):returnvalue_if_undefinedresult=math.acos(max(-1,min(1,cos_angle)))if(deg):result*=180/math.pireturnresultdefaccute_angle(self,other,value_if_undefined=None,deg=False):cos_angle=self.cos_angle(other=other)if(cos_angleisNone):returnvalue_if_undefinedif(cos_angle<0):cos_angle*=-1result=math.acos(min(1,cos_angle))if(deg):result*=180/math.pireturnresultdefis_square(self):returnself.n[0]==self.n[1]defdeterminant(self):assertself.is_square()m=self.elemsn=self.n[0]if(n==1):returnm[0]if(n==2):returnm[0]*m[3]-m[1]*m[2]if(n==3):returnm[0]*(m[4]*m[8]-m[5]*m[7]) \
-m[1]*(m[3]*m[8]-m[5]*m[6]) \
+m[2]*(m[3]*m[7]-m[4]*m[6])if(flexisnotNone):m=flex.double(m)m.resize(flex.grid(self.n))returnm.matrix_determinant_via_lu()returndeterminant_via_lu(m=self)defco_factor_matrix_transposed(self):n=self.nif(n==(0,0)):returnrec(elems=(),n=n)if(n==(1,1)):returnrec(elems=(1,),n=n)m=self.elemsif(n==(2,2)):returnrec(elems=(m[3],-m[1],-m[2],m[0]),n=n)if(n==(3,3)):returnrec(elems=(m[4]*m[8]-m[5]*m[7],-m[1]*m[8]+m[2]*m[7],m[1]*m[5]-m[2]*m[4],-m[3]*m[8]+m[5]*m[6],m[0]*m[8]-m[2]*m[6],-m[0]*m[5]+m[2]*m[3],m[3]*m[7]-m[4]*m[6],-m[0]*m[7]+m[1]*m[6],m[0]*m[4]-m[1]*m[3]),n=n)assertself.is_square()raiseRuntimeError("Not implemented.")definverse(self):assertself.is_square()n=self.nif(n[0]<4):determinant=self.determinant()assertdeterminant!=0returnself.co_factor_matrix_transposed()/determinantif(flexisnotNone):m=flex.double(self.elems)m.resize(flex.grid(n))m.matrix_inversion_in_place()returnrec(elems=m,n=n)if(numpyisnotNone):m=numpy.asarray(self.elems)m.shape=nm=numpy.ravel(numpy.linalg.inv(m))returnrec(elems=m,n=n)returninverse_via_lu(m=self)deftranspose(self):elems=[]forjinxrange(self.n_columns()):foriinxrange(self.n_rows()):elems.append(self(i,j))returnrec(elems,(self.n_columns(),self.n_rows()))def_mathematica_or_matlab_form(self,outer_open,outer_close,inner_open,inner_close,inner_close_follow,label,one_row_per_line,format,prefix):nr=self.n_rows()nc=self.n_columns()s=prefixindent=prefixif(label):s+=label+"="indent+=" "*(len(label)+1)s+=outer_openif(nc!=0):foririnxrange(nr):s+=inner_openforicinxrange(nc):if(formatisNone):s+=str(self(ir,ic))else:s+=format%self(ir,ic)if(ic+1!=nc):s+=", "elif(ir+1!=nrorlen(inner_open)!=0):s+=inner_closeif(ir+1!=nr):s+=inner_close_followif(one_row_per_line):s+="\n"s+=indents+=" "returns+outer_closedefmathematica_form(self,label="",one_row_per_line=False,format=None,prefix="",matrix_form=False):result=self._mathematica_or_matlab_form(outer_open="{",outer_close="}",inner_open="{",inner_close="}",inner_close_follow=",",label=label,one_row_per_line=one_row_per_line,format=format,prefix=prefix)ifmatrix_form:result+="//MatrixForm"result=result.replace('e','*^')returnresultdefmatlab_form(self,label="",one_row_per_line=False,format=None,prefix=""):returnself._mathematica_or_matlab_form(outer_open="[",outer_close="]",inner_open="",inner_close=";",inner_close_follow="",label=label,one_row_per_line=one_row_per_line,format=format,prefix=prefix)def__repr__(self):n0,n1=self.ne=self.elemsif(len(e)<=3):e=str(e)else:e="(%s, ..., %s)"%(str(e[0]),str(e[-1]))return"matrix.rec(elems=%s, n=(%d,%d))"%(e,n0,n1)def__str__(self):returnself.mathematica_form(one_row_per_line=True)defas_list_of_lists(self):result=[]nr,nc=self.nforirinxrange(nr):result.append(list(self.elems[ir*nc:(ir+1)*nc]))returnresultdefas_sym_mat3(self):assertself.n==(3,3)m=self.elemsreturn(m[0],m[4],m[8],(m[1]+m[3])/2.,(m[2]+m[6])/2.,(m[5]+m[7])/2.)defas_mat3(self):assertself.n==(3,3)returnself.elemsdefas_flex_double_matrix(self):assertflexisnotNoneresult=flex.double(self.elems)result.reshape(flex.grid(self.n))returnresultdefas_flex_int_matrix(self):assertflexisnotNoneresult=flex.int(self.elems)result.reshape(flex.grid(self.n))returnresultdefextract_block(self,stop,start=(0,0),step=(1,1)):assert0<=stop[0]<=self.n[0]assert0<=stop[1]<=self.n[1]i_rows=range(start[0],stop[0],step[0])i_colums=range(start[1],stop[1],step[1])result=[]foririni_rows:foricini_colums:result.append(self(ir,ic))returnrec(result,(len(i_rows),len(i_colums)))def__eq__(self,other):ifselfisother:returnTrueifotherisNone:returnFalseifissubclass(type(other),rec):returnself.elems==other.elemsforirinxrange(self.n_rows()):foricinxrange(self.n_columns()):ifself(ir,ic)!=other[ir,ic]:returnFalsereturnTruedefresolve_partitions(self):nr,nc=self.nresult_nr=0foririnxrange(nr):part_nr=0foricinxrange(nc):part=self(ir,ic)assertisinstance(part,rec)if(ic==0):part_nr=part.n[0]else:assertpart.n[0]==part_nrresult_nr+=part_nrresult_nc=0foricinxrange(nc):part_nc=0foririnxrange(nr):part=self(ir,ic)if(ir==0):part_nc=part.n[1]else:assertpart.n[1]==part_ncresult_nc+=part_ncresult_elems=[0]*(result_nr*result_nc)result_ir=0foririnxrange(nr):result_ic=0foricinxrange(nc):part=self(ir,ic)part_nr,part_nc=part.ni_part=0forpart_irinxrange(part_nr):i_result=(result_ir+part_ir)*result_nc+result_icforpart_icinxrange(part_nc):result_elems[i_result+part_ic]=part[i_part]i_part+=1result_ic+=part_ncassertresult_ic==result_ncresult_ir+=part_nrassertresult_ir==result_nrreturnrec(elems=result_elems,n=(result_nr,result_nc))classmutable_rec(rec):container_type=listdef__setitem__(self,i,x):self.elems[i]=xclassrow_mixin(object):def__init__(self,elems):super(row_mixin,self).__init__(elems,(1,len(elems)))classrow(row_mixin,rec):passclassmutable_row(row_mixin,mutable_rec):passclasscol_mixin(object):def__init__(self,elems):super(col_mixin,self).__init__(elems,(len(elems),1))defrandom(cls,n,a,b):uniform=random.uniformreturncls([uniform(a,b)foriinxrange(n)])random=classmethod(random)classcol(col_mixin,rec):passclassmutable_col(col_mixin,mutable_rec):passclasssqr(rec):def__init__(self,elems):l=len(elems)n=int(l**(.5)+0.5)assertl==n*nrec.__init__(self,elems,(n,n))################################################################################# This code has been adapted from cctbx.sf.net, file scitbx/matrix/eigensystem.hclassreal_symmetric:def__init__(self,symmat3):m=symmat3self.relative_epsilon=1.e-10self.absolute_epsilon=0self.a=[symmat3[0],symmat3[3],symmat3[1],symmat3[4],symmat3[5],symmat3[2]]self.vectors_=[0.,0.,0.,0.,0.,0.,0.,0.,0.]self.values_=[0.,0.,0.];self.min_abs_pivot_=self.real_symmetric_given_lower_triangle(self.a,3,self.vectors_,self.values_,self.relative_epsilon,self.absolute_epsilon);defreal_symmetric_given_lower_triangle(self,a,# size of memory pointed to by a must be n*(n+1)/2n,eigenvectors,eigenvalues,relative_epsilon,absolute_epsilon):assert(relative_epsilon>=0);assert(absolute_epsilon>=0);if(n==0):return0;# The matrix that will hold the results is initially = I.forxinxrange(n*n):eigenvectors[x]=0.0;forxinxrange(0,(n*n),(n+1)):eigenvectors[x]=1.0;# Setup variables#std::size_t il, ilq, ilr, im, imq, imr, ind, iq;#std::size_t j, k, km, l, ll, lm, lq, m, mm, mq;#FloatType am, anorm, anrmx, cosx, cosx2, sincs, sinx, sinx2, thr, x, y;# Initial and final norms (anorm & anrmx).anorm=0.0;iq=0;foriinxrange(n):forjinxrange(i+1):if(j!=i):anorm+=a[iq]*a[iq];iq+=1anorm=math.sqrt(2.0*anorm);anrmx=relative_epsilon*anorm/n;if(anrmx<absolute_epsilon):anrmx=absolute_epsilon;if(anorm>0.0):# Compute threshold and initialise flag.thr=anorm;while(thr>anrmx):# Compare threshold with final normthr/=n;ind=1;while(ind):ind=0;l=0;while(l!=n-1):# Test for l beyond penultimate columnlq=l*(l+1)/2;ll=l+lq;m=l+1;ilq=n*l;while(m!=n):# Test for m beyond last column# Compute sin & cos.mq=m*(m+1)/2;lm=l+mq;if(a[lm]*a[lm]>thr*thr):ind=1;mm=m+mq;x=0.5*(a[ll]-a[mm]);denominator=math.sqrt(a[lm]*a[lm]+x*x);assert(denominator!=0);y=-a[lm]/denominator;if(x<0.0):y=-y;sinx=y/math.sqrt(2.0*(1.0+(math.sqrt(1.0-y*y))));sinx2=sinx*sinx;cosx=math.sqrt(1.0-sinx2);cosx2=cosx*cosx;sincs=sinx*cosx;# Rotate l & m columns.imq=n*m;foriinxrange(n):iq=i*(i+1)/2;if(i!=landi!=m):if(i<m):im=i+mq;else:im=m+iq;if(i<l):il=i+lq;else:il=l+iq;x=a[il]*cosx-a[im]*sinx;a[im]=a[il]*sinx+a[im]*cosx;a[il]=x;ilr=ilq+i;imr=imq+i;x=(eigenvectors[ilr]*cosx) \
-(eigenvectors[imr]*sinx);eigenvectors[imr]=(eigenvectors[ilr]*sinx) \
+(eigenvectors[imr]*cosx);eigenvectors[ilr]=x;x=2.0*a[lm]*sincs;y=a[ll]*cosx2+a[mm]*sinx2-x;x=a[ll]*sinx2+a[mm]*cosx2+x;a[lm]=(a[ll]-a[mm])*sincs+a[lm]*(cosx2-sinx2);a[ll]=y;a[mm]=x;m+=1;l+=1;# Sort eigenvalues & eigenvectors in order of descending eigenvalue.k=0;foriinxrange(n-1):im=i;km=k;am=a[k];l=0;forjinxrange(n):if(j>ianda[l]>am):im=j;km=l;am=a[l];l+=j+2;if(im!=i):a[km]=a[k];a[k]=am;l=n*i;m=n*im;forjjinxrange(n):am=eigenvectors[l];eigenvectors[l]=eigenvectors[m];l+=1;eigenvectors[m]=am;m+=1k+=i+2;# place sorted eigenvalues into the matrix_vector structurek=0forjinxrange(n):eigenvalues[j]=a[k];k+=j+2;returnanrmx;defvectors(self):returnmatrix.sqr(self.vectors_)defvalues(self):returnmatrix.col(self.values_)classmodule:passeigensystem=module()eigensystem.real_symmetric=real_symmetricmatrix=module()matrix.sqr=sqrmatrix.col=colmatrix.rec=rec#######################################################################This code is adapted from cctbx.sf.net, file scitbx/math/superpose.pydefkabsch_rotation(reference_sites,other_sites):"""Kabsch, W. (1976). Acta Cryst. A32, 922-923.A solution for the best rotation to relate two sets of vectorsBased on a prototype by Erik McKee and Reetal K. Pai. """assertlen(reference_sites)==len(other_sites)sts=matrix.sqr(other_sites.transpose_multiply(reference_sites))sym_mat3_input=(sts*sts.transpose()).as_sym_mat3()eigs=eigensystem.real_symmetric(sym_mat3_input)vals=list(eigs.values())vecs=list(eigs.vectors())a3=list(matrix.col(vecs[:3]).cross(matrix.col(vecs[3:6])))a=matrix.sqr(list(vecs[:6])+a3)b=list(a*sts)foriinxrange(3):d=math.sqrt(math.fabs(vals[i]))if(d>0):forjinxrange(3):b[i*3+j]/=db3=list(matrix.col(b[:3]).cross(matrix.col(b[3:6])))b=matrix.sqr(b[:6]+b3)returnb.transpose()*a########################################This code is new for this application:classresidue:def__init__(self,chain,resid,atoms):self.C=Noneself.CA=Noneself.CB=Noneself.N=Noneforatominatoms.atom:ifchain!=str(atom.chain):continueifstr(resid)!=str(atom.resi):continueifatom.name=="N":self.N=matrix.col((atom.coord[0],atom.coord[1],atom.coord[2]))ifatom.name=="CA":self.residue_type=atom.resnself.CA=matrix.col((atom.coord[0],atom.coord[1],atom.coord[2]))ifatom.name=="C":self.C=matrix.col((atom.coord[0],atom.coord[1],atom.coord[2]))ifatom.name=="CB":self.CB=matrix.col((atom.coord[0],atom.coord[1],atom.coord[2]))assertself.N!=Noneassertself.CA!=Noneassertself.C!=Noneifself.CB==None:#generate a CB position for Glycineself.CB=self.constructed_CB()defconstructed_CB(self):#refer to the documentation for geometrical constructionunit_N=(self.N-self.CA).normalize()assertabs(unit_N.length()-1.0)<0.0001unit_C=(self.C-self.CA).normalize()on_bisector=(unit_N+unit_C).normalize()unit_rotation_axis=(unit_N-unit_C).normalize()expected_angle_bisector_to_CB=math.acos(-1./math.sqrt(3.))#Use Euler-Rodrigues formula for rotationunit_on_CB=self.rotation_formula(on_bisector,unit_rotation_axis,expected_angle_bisector_to_CB)O_to_CB=1.53*unit_on_CBreturnself.CA+O_to_CBdefrotation_formula(self,vector,axis,theta):returnmath.cos(theta)*vector+ \
math.sin(theta)*(axis.cross(vector))+ \
(1.-math.cos(theta))*((axis.dot(vector))*axis)defsites(self):diff_N=self.N-self.CAdiff_C=self.C-self.CAdiff_CB=self.CB-self.CAall=[]forsitein[diff_N,diff_C,diff_CB]:forcoordin[0,1,2]:all.append(site[coord])returnmatrix.rec(all,(3,3))classstd_residue(residue):def__init__(self):passclassview_matrix:def__init__(self,getview_output):#3x3 rotation matrix which transforms model to camera spaceself.rotmat=matrix.sqr(getview_output[0:9])#camera position in model space and relative to the origin of rotationself.camera_position=matrix.col(getview_output[9:12])#origin of rotation in model spaceself.origin_of_rotation=matrix.col(getview_output[12:15])#front plane distance from cameraself.front_plane=getview_output[15]self.back_plane=getview_output[16]self.orthoscopic_flag=getview_output[17]defset_diff_to_CA(self,residue):self.difference_to_this_CA=self.origin_of_rotation-residue.CAreturnselfclassnew_set_view:def__init__(self,std_view,kr,residue,verbose=True):R_prime=(kr.inverse()*std_view.rotmat)ifverbose:print"delta",std_view.origin_of_rotation-residue.CAtest=residue.CA+(kr.inverse()*std_view.difference_to_this_CA)ifverbose:print"set_view ( ",forxinR_prime.elems:print"%10.4f,"%x,forxinstd_view.camera_position.elems:print"%10.4f,"%x,forxinresidue.CA.elems:print"%10.7f,"%x,forxin[std_view.front_plane,std_view.back_plane,std_view.orthoscopic_flag]:print"%10.4f,"%x,print")"self.result=list(R_prime.elems)+\
list(std_view.camera_position.elems)+\
list(test.elems)+\
[std_view.front_plane,std_view.back_plane,std_view.orthoscopic_flag]defview_matrix(self):returnself.result