You are not allowed to perform this action.

Clear message

Converting Chimera transformation matrices to Spider convention for use in Sparx

Updated to Python3 on 04/10/2020

Map sv.hdf can have pixel size stored in the header as attributes apix_x, apix_y, apix_z. All three have to be present. The units are Angstroms. If they are not, the pixel size is defaulted to 1A.

A map sv.hdf is read into chimera, rotated, and the resulting orientation matrix stored:

matrixget or1.ori

Then we run the program below, say we named it orichimera.py, as:

orichimera.py sv.hdf or1.ori rsv.hdf

rsv.hdf opened in a new chimera will be in the same orientation as rotated sv.hdf in the first chimera.

#!/usr/bin/env python

# Moves a map according to the matrices output by Chimera as
# a result of a fitting.


from EMAN2 import *
from sparx import *
from math import sqrt
import sys

from os import system
from sys import argv
from sys import exit


def chi2sx(tchi, nx, ny, nz):
        if nx % 2 == 0:
                na = nx//2
        else:
                na = (nx-1)//2
        if ny % 2 == 0:
                nb = ny//2
        else:
                nb = (ny-1)//2
        if nz % 2 == 0:
                nc = nz//2
        else:
                nc = (nz-1)//2
        vcenter = Vec3f(na,nb,nc)
        shift_sx = tchi*vcenter - vcenter
        txlist = tchi.get_matrix()
        txlist[3]  = shift_sx[0]
        txlist[7]  = shift_sx[1]
        txlist[11] = shift_sx[2]
        tsx = Transform(txlist)
        return tsx


def sx2chi(tsx, nx, ny, nz):
        if nx % 2 == 0:
                na = nx//2
        else:
                na = (nx-1)//2
        if ny % 2 == 0:
                nb = ny//2
        else:
                nb = (ny-1)//2
        if nz % 2 == 0:
                nc = nz//2
        else:
                nc = (nz-1)//2
        vcenter = Vec3f(na,nb,nc)
        shift_chi = tsx*(-vcenter) + vcenter
        txlist = tsx.get_matrix()
        txlist[3]  = shift_chi[0]
        txlist[7]  = shift_chi[1]
        txlist[11] = shift_chi[2]
        tchi = Transform(txlist)
        return tchi


# map to be rotated:
mapf = get_image(argv[1])

# size of mapf:
nx = mapf.get_xsize()
ny = mapf.get_ysize()
nz = mapf.get_zsize()

pix = mapf.get_attr_default("apix_x", 1.0)
piy = mapf.get_attr_default("apix_y", 1.0)
piz = mapf.get_attr_default("apix_z", 1.0)

# paste here the entries of the matrices output by Chimera:
# (Note: the units of the translation vector are Angstrom;
# remember to set the correct pixel sizes in Chimera before
# doing the fit)

matfile = open(argv[2],"r")
strg = matfile.readline()
# fitted map:
mat_f = []

for i in range(3):
        strg = matfile.readline()
        word = strg.split()
        for j in range(4):
                mat_f.append(float(word[j]))
matfile.close()
print( mat_f )

# change translation units to pixels of map to be fitted:
mat_f[3] /= pix
mat_f[7] /= piy
mat_f[11] /= piz
print( mat_f )
chi_f = Transform(mat_f)  #.inverse()

sx_c = chi2sx(chi_f,nx,ny,nz)

params_mov = sx_c.get_params("spider")
print(  params_mov )
map_moved  = rot_shift3D(mapf, params_mov["phi"],params_mov["theta"],params_mov["psi"],params_mov["tx"],params_mov["ty"],params_mov["tz"])

map_moved.write_image(argv[3])

Application to docking

In Chimera, one can orient two (or more) volumes with respect to one another for purposes of fitting etc . However, since the rotation conventions used in Chimera is different from the Spider convention used in Sparx, there is no built-in way (in Chimera) for extracting the angles and shifts that can be applied (via Sparx) to the volumes so that next time they are loaded, they are automatically in the desired orientation with respect to one another. Also, it is often useful or necessary to obtain the transformation matrix that determines the relative orientation between the volumes. The program align_chimera.py can be used to achieve this.

More specifically, suppose you have volumes A and B loaded in Chimera. Under Features in Volume Viewer, select Coordinates and enter the correct voxel size for your structures, and then orient the volumes relative to each other. Once the volumes are in their desired orientation, select "Command Line" under Favorites in Chimera, and type matrixget at the Command prompt. Chimera will then prompt you for the name under which the transformation matrices will be saved. Suppose the file name we save it under is volmat.

The file volmat will look something like this:

Model 0.0

Model 1.0

where Model 0.0 corresponds to the volume with ID 0 in Chimera, and Model 1.0 corresponds to the volume with ID 1 in Chimera. Suppose volume A had ID 0 in Chimera, and volume B had ID 1 in Chimera.

The idea is to determine the shifts and Euler angles (in Spider convention) that can be applied to volume A (via rot_shift3D in Sparx) so that it is in the desired orientation with volume B. To achieve this, simply replace maf_file in the code below with the name of volume A. It's assumed that both volumes A and B are located in the directory you are running align_chimera.py in. Next, replace pixf with the voxel size that you set when you oriented the volumes. Replace mat_r with the entries of the transformation matrix listed under Model 1.0 (i.e., volume B, the "reference" volume) in volmat, and replace mat_f with the entries of the transformation matrix listed under Model 0.0 (i.e., volume A, the volume being "fitted") in volmat. Note that it doesn't really matter which volume you take to be "reference" so long as you're being consistent.

Once you run the program, there will be two outputs: volf.spi and vol-to-ecol_sx.matrix.

volf.spi is volume A rotated such that if you load volf.spi and volume B together in Chimera, they will appear in the desired rotation. You can change the name volf.spi to whatever name you want in the line

by replacing volf with the desired name.

vol-to-ecol_sx.matrix contains the entries of the transformation matrix corresponding the the shifts and Euler angles which were applied to volume A to obtain volf.spi (i.e, the rotated volume A which is in desired orientation with volume B).

Note that params_mov contains the Euler angles (in Spider convention) that was applied to volume A to get its oriented version (volf.spi).

align_chimera.py

#!/usr/bin/env python

# Moves a map according to the matrices output by Chimera as
# a result of a fitting.

from EMAN2 import *
from sparx import *
from math import sqrt
import sys

from os import system
from sys import argv
from sys import exit

def chi2sx(tchi, nx, ny, nz):
        if nx % 2 == 0:
                na = nx//2
        else:
                na = (nx-1)//2
        if ny % 2 == 0:
                nb = ny//2
        else:
                nb = (ny-1)//2
        if nz % 2 == 0:
                nc = nz//2
        else:
                nc = (nz-1)//2
        vcenter = Vec3f(na,nb,nc)
        shift_sx = tchi*vcenter - vcenter
        txlist = tchi.get_matrix()
        txlist[3]  = shift_sx[0]
        txlist[7]  = shift_sx[1]
        txlist[11] = shift_sx[2]
        tsx = Transform(txlist)
        return tsx


def sx2chi(tsx, nx, ny, nz):
        if nx % 2 == 0:
                na = nx//2
        else:
                na = (nx-1)///2
        if ny % 2 == 0:
                nb = ny//2
        else:
                nb = (ny-1)//2
        if nz % 2 == 0:
                nc = nz//2
        else:
                nc = (nz-1)//2
        vcenter = Vec3f(na,nb,nc)
        shift_chi = tsx*(-vcenter) + vcenter
        txlist = tsx.get_matrix()
        txlist[3]  = shift_chi[0]
        txlist[7]  = shift_chi[1]
        txlist[11] = shift_chi[2]
        tchi = Transform(txlist)
        return tchi



# map to be fitted:
mapf_file = "vol0014.spi"
mapf = get_image(mapf_file)

# pixel size of mapf (in Angstrom):
pixf = 2.8

# size of mapf:
nx = mapf.get_xsize()
ny = mapf.get_ysize()
nz = mapf.get_zsize()

# paste here the entries of the matrices output by Chimera:
# (Note: the units of the translation vector are Angstrom;
# remember to set the correct pixel sizes in Chimera before
# doing the fit)

# reference map:
mat_r  = [1.0, 0.0, 0.0, 0.0, \
        0.0, 1.0, 0.0, 0.0, \
        0.0, 0.0, 1.0, 0.0]

# fitted map:
mat_f = [0.999942, -0.0063274, 0.00870694, -4.28387, \
        0.00624902, 0.99994, 0.00899946, -11.3096, \
        -0.00876336, -0.00894453, 0.999922, -3.47032]

###########################################################

# change translation units to pixels of map to be fitted:
mat_r[3] /= pixf; mat_f[3] /= pixf
mat_r[7] /= pixf; mat_f[7] /= pixf
mat_r[11] /= pixf; mat_f[11] /= pixf

chi_r = Transform(mat_r)
chi_f = Transform(mat_f)

# relative transformation:
chi_c = chi_r.inverse()*chi_f

sx_c = chi2sx(chi_c,nx,ny,nz)

params_mov = sx_c.get_params("spider")
map_moved  = rot_shift3D(mapf, params_mov["phi"],params_mov["theta"],params_mov["psi"],params_mov["tx"],params_mov["ty"],params_mov["tz"])

drop_image(map_moved,"volf.spi","s")

cmat = sx_c.get_matrix()

matfile = open("vol-to-ecol_sx.matrix","w")

# note that shift units are Angstrom:
for i in range(3):
        row = "    %9.6f %9.6f %9.6f %8.4f\n" % (cmat[4*i],cmat[4*i+1],cmat[4*i+2],cmat[4*i+3]*pixf)
        matfile.write(row)

matfile.close()

exit(0)