measure distance between two atoms, in each state, in pymol

447 Views Asked by At

I'm using Pymol to measure the distance between two atoms of a nucleic acid. I have a pdb. file from molecular dynamics calculation. When I open that file in pymol I have multiple states of the molecule. I want to measure the distance between two specific atoms and for each state, and export the distances to a file. Then I can visualise the distribution in distance. Can anyone please suggest a method to do this using pymol.

1

There are 1 best solutions below

1
On BEST ANSWER

my attempt , code using 1m82.pdb as example molecule and

distance("dist_1m82" , "/1m82/A/A/U`22/P" , "/1m82/A/A/U`22/N1" , mode = 0) as distance command (you could use get_distance if you don't want/need to create a distance object:

#!/usr/bin/env python3

import pymol

from pymol import (
                    cmd ,
                    )

pdb_code = '1m82'


print('########## PYMOL VERSION ##########################################')
print('         ',  cmd.get_version() )
print('###################################################################')


pymol.finish_launching()


# cmd.feedback("enable" , "all" , "blather")


cmd.set('internal_gui_width' ,  '500')

# cmd.load('1m82.pdb' , '1m82' , state = 0)

cmd.fetch(pdb_code  ,  pdb_code  , state = 0 , quiet = 0 , async_ = 0)

number_of_states = cmd.count_states(pdb_code)

print('states_count -----> ' , number_of_states)

cmd.split_states(pdb_code)

cmd.sync()

cmd.delete(pdb_code)


distances = []

for state_num in range(1, number_of_states + 1):
    
      
      obj_name = pdb_code+ "_" + str(state_num).zfill(4)
    
      ## PyMOLWiki doesnt show default mode number for distance command https://pymolwiki.org/index.php/Distance
      dis = cmd.distance("dist_"+obj_name , '/'+obj_name+"/A/A/U`22/P" , '/'+obj_name+"/A/A/U`22/N1" , mode = 0)
      
      distances.append(('dist_'+obj_name , dis))


print('\n ---------\n')

for dist in distances:
    
    print(dist)

output:

########## PYMOL VERSION ##########################################
          ('2.3.0' ....

states_count ----->  31

 ---------

('dist_1m82_0001', 5.300721168518066)
('dist_1m82_0002', 5.251534938812256)
('dist_1m82_0003', 5.176976203918457)
('dist_1m82_0004', 5.227502822875977)
('dist_1m82_0005', 4.940760135650635)
('dist_1m82_0006', 5.036609649658203)
('dist_1m82_0007', 5.408339977264404)
('dist_1m82_0008', 5.204244613647461)
('dist_1m82_0009', 5.257850646972656)
('dist_1m82_0010', 5.196445941925049)
('dist_1m82_0011', 5.122419357299805)
('dist_1m82_0012', 5.103713512420654)
('dist_1m82_0013', 5.154935836791992)
('dist_1m82_0014', 5.303188323974609)
('dist_1m82_0015', 5.011679649353027)
('dist_1m82_0016', 5.331982612609863)
('dist_1m82_0017', 5.318058967590332)
('dist_1m82_0018', 5.257812023162842)
('dist_1m82_0019', 5.262117385864258)
('dist_1m82_0020', 5.251483917236328)
('dist_1m82_0021', 5.132598400115967)
('dist_1m82_0022', 5.1809163093566895)
('dist_1m82_0023', 5.067441463470459)
('dist_1m82_0024', 5.514594078063965)
('dist_1m82_0025', 5.220001697540283)
('dist_1m82_0026', 5.40653133392334)
('dist_1m82_0027', 5.2190141677856445)
('dist_1m82_0028', 5.212803363800049)
('dist_1m82_0029', 5.346554279327393)
('dist_1m82_0030', 5.3021650314331055)
('dist_1m82_0031', 5.262298107147217)

picture , distances are drawn as light yellow dashed lines :

enter image description here

As per the

Then I can visualise the distribution in distance

part of your question, I need to create a .py file like:

runnable.py :

#!/usr/bin/env python3

import matplotlib.pyplot as plt

y = [i[1] for i in stored.distances]

print('\n\ny : \n' , y ,'\n\n')
fig, axs = plt.subplots()

axs.hist(y , bins = 5)

plt.show()

then add at the end of my first script these lines :

stored.distances = distances

cmd.do('run runnable.py')

of course runnable.py is in the same folder of my main script

this will get you an extra window a matplotlib one like this one:

enter image description here

where, this need to be checked you'll find the distribution of the calculated distance.

Unfortunately you get an Error too, better a warning like:


runnable.py:17: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.
  fig, axs = plt.subplots()
runnable.py:21: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.
.....
....and many other lines.....

but at least it'll run fast.

Trying to put the runnable.py inside main script or reverting to use :

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg

will result in:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is ........, parent's thread is QThread(0x7fa9a800cd40), current thread is QThread(0x30374d0)
QObject::installEventFilter(): Cannot filter events for objects in a different thread.

with the window showinh the right histogram (the one above), at this point a pymol window very slow to load and sending out the message above tens of time.

I guess this second part needs more time to be answered.

Addendum

redefining runnable.py as runnable_2.py so that my script carries:

stored.distances = distances
cmd.do('run runnable_2.py') 

and runnable_2.py , copied and adapted from https://www.pythonguis.com/tutorials/plotting-matplotlib/is:

import matplotlib
matplotlib.use('Qt5Agg')

# from PyQt5 import QtCore, QtWidgets

from pymol.Qt import QtWidgets, QtCore


print('\n\n###################################\n')
print(' QtCore.qVersion( ---> ' ,QtCore.qVersion())
print('\n\n###################################\n')

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvas, self).__init__(fig)


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, values , *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.y = values 
        
        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=5, height=4, dpi=100)
        sc.axes.hist(self.y , bins = 5)
        self.setCentralWidget(sc)

        self.show()
        
        # matplotlib.plot.show()


y = [i[1] for i in stored.distances]
w = MainWindow(y)

I get the histogram depicted above (now being a PyMOL window , not a matplotlib one) without any sort of error.

While using from pymol.Qt import QtWidgets, QtCore and the runnable_2.py QWidgets inside the main script (so not started by cmd.do('run , ...)`

keeps being slow to present the histogram and carries the:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is ...........), parent's thread is QThread(0x7f5ddc00cd40), current thread is QThread(0x30ae7b0)
QObject::installEventFilter(): Cannot filter events for objects in a different thread.
QObject::installEventFilter(): Cannot filter events for objects in a different thread.

I guess I am missing something like this post for pymol using PyQt:

Create a new Tk window/thread in the PyMOL session and output matplotlib graph on it