Source code for aquaduct.visual.pymol_connector

# -*- coding: utf-8 -*-

# Aqua-Duct, a tool facilitating analysis of the flow of solvent molecules in molecular dynamic simulations
# Copyright (C) 2016-2018  Tomasz Magdziarz, Alicja Płuciennik, Michał Stolarczyk <info@aquaduct.pl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import numpy as np
import cPickle as pickle
import os
import tarfile

import aquaduct.visual.pymol_cgo as cgo

from aquaduct.geom import traces
from aquaduct.traj.paths import PathTypesCodes
from aquaduct.utils.helpers import list_blocks_to_slices
from aquaduct.visual.helpers import color_codes, cc
from aquaduct.utils.helpers import create_tmpfile, listify


[docs]class BasicPymolCGO(object): cgo_entity_begin = [] cgo_entity_end = []
[docs] def __init__(self): self.cgo_entity = None self.previous = None self.clean()
[docs] def clean(self,empty=False): self.previous = None self.cgo_entity = [] if not empty: self.cgo_entity.extend(self.cgo_entity_begin)
[docs] def new(self): self.previous = None
[docs] def get(self): return self.cgo_entity + self.cgo_entity_end
[docs] @staticmethod def make_color_triple(color_definition): return tuple(color_definition)
[docs]class BasicPymolCGOLines(BasicPymolCGO): cgo_entity_begin = [cgo.BEGIN, cgo.LINES] cgo_entity_end = [cgo.END]
[docs] def add(self, coords=None, color=None): if color is not None: self.cgo_entity.append(cgo.COLOR) # self.cgo_entity.extend(map(float, color)) self.cgo_entity.append(self.make_color_triple(map(float, color))) if coords is not None: for nr, coord in enumerate(coords): if self.previous is not None: self.cgo_entity.append(cgo.VERTEX) self.cgo_entity.extend(map(float, self.previous)) self.cgo_entity.append(cgo.VERTEX) self.cgo_entity.extend(map(float, coord)) self.previous = coord
[docs]class BasicPymolCGOSpheres(BasicPymolCGO): cgo_entity_begin = [] cgo_entity_end = []
[docs] def add(self, coords=None, radius=None, color=None): # color to colors... if color is not None: color = np.matrix(color).A if radius is not None: radius = np.matrix(radius).A1 if coords is not None: for nr, coord in enumerate(coords): if color is not None: if len(color) > 1: c = color[nr] else: c = color[0] self.cgo_entity.append(cgo.COLOR) self.cgo_entity.append(self.make_color_triple(map(float, c))) # self.cgo_entity.extend(map(float, c)) self.cgo_entity.append(cgo.SPHERE) self.cgo_entity.extend(map(float, coord)) if radius is not None: if len(radius) > 1: r = radius[nr] else: r = radius else: r = 1. self.cgo_entity.append(float(r))
# [cgo.CONE] + xyz3 + xyz2 + [hradius, 0.0] + color2 + color2
[docs]class BasicPymolCGOPointers(BasicPymolCGO): cgo_entity_begin = [] cgo_entity_end = []
[docs] def add_cone(self, coords1=None, coords2=None, radius1=None, radius2=None, color1=None, color2=None): # color to colors... ??? if coords1 is not None and coords2 is not None: self.cgo_entity.append(cgo.CONE) self.cgo_entity.extend(map(float, coords1)) self.cgo_entity.extend(map(float, coords2)) self.cgo_entity.append(float(radius1)) self.cgo_entity.append(float(radius2)) self.cgo_entity.append(self.make_color_triple(map(float, color1))) self.cgo_entity.append(self.make_color_triple(map(float, color2))) # self.cgo_entity.extend(map(float, color1)) # self.cgo_entity.extend(map(float, color2)) self.cgo_entity.extend([cgo.NULL, cgo.POINTS])
[docs] def add_pointer(self, point=None, direction=None, length=None, color=None, reverse=False): vec = point - direction vec_len = np.sqrt(np.sum(vec ** 2)) vec = (vec / vec_len) * length vec = point + vec if reverse: self.add_cone(coords1=point, coords2=vec, radius1=length / 3., radius2=0, color1=color, color2=color) else: self.add_cone(coords1=vec, coords2=point, radius1=length / 3., radius2=0, color1=color, color2=color)
[docs]class SimpleTarWriteHelper(object):
[docs] def __init__(self): self.tar_fh = None self.tmp_file = create_tmpfile()
[docs] def open(self, filename): self.tar_fh = tarfile.open(filename, 'w:gz')
[docs] def save_object2tar(self, obj, name): with open(self.tmp_file, 'w') as f: pickle.dump(obj, f) self.save_file2tar(self.tmp_file, name)
[docs] def save_file2tar(self, filename, name): self.tar_fh.add(filename, arcname=name)
[docs] def __del__(self): if self.tar_fh is not None: self.tar_fh.close() os.unlink(self.tmp_file)
[docs]class ConnectToPymol(object): cgo_line_width = 2. ct_pymol = 'pymol' ct_file = 'file'
[docs] def __init__(self): self.connection_type = None # possible types are pymol and file self.script_fh = None self.data_fh = SimpleTarWriteHelper() self.cmd = None
[docs] @listify def decode_color(self, cgo_object): for element in cgo_object: if isinstance(element, tuple): for e in element: yield e else: yield element
[docs] def init_pymol(self): import pymol self.cmd = pymol.cmd pymol.finish_launching() self.cmd.set('cgo_line_width', ConnectToPymol.cgo_line_width) self.connection_type = self.ct_pymol
[docs] def init_script(self, filename): self.script_fh = open(filename, 'w') data_filename = os.path.splitext(os.path.basename(filename))[0] + '.tar.gz' self.data_fh.open(data_filename) self.connection_type = self.ct_file # TODO: Script generated by this object does not work properly if run via PyMOL. # init lines, imports etc. self.script_fh.write('''import argparse parser=argparse.ArgumentParser(description="Aqua-Duct visualization script") parser.add_argument("--save-session",action="store",dest="session",required=False,default=None,help="Pymol session file name.") parser.add_argument("--discard",action="store",dest="discard",required=False,default='',help="Objects to discard.") parser.add_argument("--keep",action="store",dest="keep",required=False,default='',help="Objects to keep.") parser.add_argument("--force-color",action="store",dest="fc",required=False,default='',help="Force specific color.") parser.add_argument("--fast",action="store_true",dest="fast",required=False,help="Disable all objects while loading.") args,unknown=parser.parse_known_args() import sys if unknown: print >> sys.stderr, "WARNING: Unknown options were used: "+" ".join(unknown) def _kd_order(): if args.keep=='' and args.discard!='': return 'd' if args.keep!='' and args.discard=='': return 'k' if args.keep=='' and args.discard=='': return None if sys.argv.index('--keep')<sys.argv.index('--discard'): return 'k' return 'd' kd_order = _kd_order() def discard(name): if len([d for d in args.discard.split() if d in name])>0: return True return False def keep(name): if len([k for k in args.keep.split() if k in name])>0: return True return False def proceed(name): if kd_order == 'k': if not keep(name): return False elif discard(name): return False elif kd_order == 'd': if discard(name): if not keep(name): return False return True from pymol import cmd,finish_launching finish_launching() print "Loading Aqua-Duct visualization..." cmd.set("cgo_line_width",%d) cmd.set("line_smooth","off") from os import close,unlink from os.path import splitext,isfile import tarfile import cPickle as pickle from tempfile import mkstemp fd, pdb_filename = mkstemp(suffix=".pdb") close(fd) max_state=0 arch_file="%s" if not isfile(arch_file): import pymol if pymol.IS_WINDOWS: print "Please open visualization script using 'Open with' context menu and choose PyMol executable." print "Alternatively, if you have PyMol installed as Python module, open visulaization script with Python executable." while (pymol._ext_gui is None): pymol = reload(pymol) while (not hasattr(pymol._ext_gui,'root')): pymol = reload(pymol) import tkFileDialog arch_file=tkFileDialog.askopenfilename(filetypes=[("AQ Vis Arch","*.tar.gz")],title="Select AQ visualization archive",parent=pymol._ext_gui.root) data_fh=tarfile.open(arch_file,"r:gz") def decode_color(cgo_object,fc=None): for element in cgo_object: if isinstance(element,tuple): if fc is None: for e in element: yield e else: for e in fc: yield e else: yield element def load_object(filename,name,state): if not proceed(name): return global max_state print "Loading %s" % splitext(filename)[0] obj=pickle.load(data_fh.extractfile(filename)) if name in args.fc.split(): forced_color=args.fc.split()[args.fc.split().index(name)+1] forced_color=cmd.get_color_tuple(forced_color) obj=decode_color(obj,fc=forced_color) else: obj=decode_color(obj) cmd.load_cgo(obj,name,state) if state<2: if args.fast: cmd.disable("all") else: cmd.refresh() if state>max_state: max_state=state def load_pdb(filename,name,state): if not proceed(name): return global max_state with open(pdb_filename,'w') as fpdb: fpdb.write(data_fh.extractfile(filename).read()) cmd.load(pdb_filename,state=state,object=name) if state>max_state: max_state=state ''' % (self.cgo_line_width, data_filename, "%s", "% s"))
[docs] def add_cgo_object(self, name, cgo_object, state=None): if state is None: state = 1 if self.connection_type == self.ct_pymol: self.cmd.load_cgo(self.decode_color(cgo_object), str(name), state) elif self.connection_type == self.ct_file: obj_name = '%s_%d.dump' % (name, state) self.data_fh.save_object2tar(cgo_object, obj_name) self.script_fh.write('''load_object("%s","%s",%d)''' % (obj_name, str(name), state)) self.script_fh.write(os.linesep)
# self.script_fh.write('''self.cmd.refresh()''') # self.script_fh.write(os.linesep)
[docs] def del_cgo_object(self, name, state=None): raise NotImplementedError("Deletion of CGO objects is not implemented yet.")
[docs] def load_pdb(self, name, filename, state=None): if state is None: state = 1 if self.connection_type == self.ct_pymol: self.cmd.load(filename, state=state, object=name) self.cmd.show_as('cartoon',name) self.cmd.color('silver',name) elif self.connection_type == self.ct_file: # save pdblile as string filename_new = '%s_%d.pdb' % (name, state) self.data_fh.save_file2tar(filename, filename_new) self.script_fh.write('''load_pdb("%s","%s",%d)''' % (filename_new, name, state)) self.script_fh.write(os.linesep) self.script_fh.write('''if proceed("%s"): cmd.show_as('cartoon','%s')''' % (name,name)) self.script_fh.write(os.linesep) self.script_fh.write('''if proceed("%s"): cmd.color('silver','%s')''' % (name,name)) self.script_fh.write(os.linesep)
[docs] def orient_on(self, name): if self.connection_type == self.ct_pymol: self.cmd.orient(name) elif self.connection_type == self.ct_file: self.script_fh.write('''if proceed("%s"): cmd.orient("%s")''' % (name, name)) self.script_fh.write(os.linesep)
[docs] def __del__(self): if self.connection_type == self.ct_file: self.script_fh.write('''data_fh.close() unlink(pdb_filename) if args.fast: cmd.enable("all") print "Aqua-Duct visualization loaded." if args.session: print "Preparing data to save session..." for state in range(max_state): cmd.set_frame(state+1) cmd.refresh() if (state+1)%100==0: print "wait... %d of %d done..." % (state+1,max_state) print "%d of %d done." % (state+1,max_state) print "Saving session..." cmd.set_frame(1) cmd.save(args.session,state=0) print "Let the Valve be always open!" print "Goodby!" cmd.quit() ''') self.script_fh.write(os.linesep) self.script_fh.close()
[docs]class SinglePathPlotter(object):
[docs] def __init__(self, pymol_connector, linearize=None): self.cgo_lines = BasicPymolCGOLines() self.cgo_spheres = BasicPymolCGOSpheres() self.cgo_pointers = BasicPymolCGOPointers() # linearize have to be callable! or False or None self.linearize = linearize self.pymol_connector = pymol_connector
# TODO: take care of proper colors handling for smoothed and not smoothed traces!
[docs] def add_single_path_continous_trace(self, spath, smooth=None, plot_in=True, plot_object=True, plot_out=True, plot_walk=True, **kwargs): self.cgo_lines.new() # get coords coords_cont = spath.get_coords_cont(smooth) # create slices if smooth: sls = tuple(list_blocks_to_slices(spath.types_cont)) else: sls = tuple(list_blocks_to_slices(spath.etypes_cont)) # create traces traces_list = tuple([coords_cont[sl] for sl in sls]) new_line = False for trace, sl in zip(traces.midpoints(traces_list), sls): if len(trace) > 0: # now trace has midpoints # get type and etype t = spath.types_cont[sl][0] et = spath.etypes_cont[sl][0] if smooth: et = et[0] # plot, if allowed if (plot_in and t == PathTypesCodes.path_in_code) or ( plot_object and t == PathTypesCodes.path_object_code) or ( plot_out and t == PathTypesCodes.path_out_code) or (plot_walk and t == PathTypesCodes.path_walk_code): # get color c = color_codes(et) # now, it is possible to linearize! if smooth and self.linearize: trace = self.linearize(trace) self.cgo_lines.add(trace, cc(c)) # new_line = True if new_line: self.cgo_lines.new() new_line = False
[docs] def paths_trace(self, spaths, smooth=None, name='paths', state=None, **kwargs): # if state_function is None if state is None: state = 1 self.cgo_lines.clean() for nr, spath in enumerate(spaths): self.add_single_path_continous_trace(spath, smooth=smooth, **kwargs) if self.cgo_lines.cgo_entity == self.cgo_lines.cgo_entity_begin: self.cgo_lines.clean(empty=True) else: self.pymol_connector.add_cgo_object(name, self.cgo_lines.get(), state=state)
[docs] def paths_inlets(self, spaths, smooth=None, color=None, plot_in=True, plot_out=True, name='in-out-let', state=None, **kwargs): if state is None: state = 1 self.cgo_pointers.clean() for nr, spath in enumerate(spaths): coords = spath.get_coords_cont(smooth=smooth) etypes = spath.etypes_cont if plot_in: if color is None: c = color_codes(etypes[0]) else: c = color self.cgo_pointers.add_pointer(point=coords[0], direction=coords[1], length=1., color=cc(c)) if plot_out: if color is None: c = color_codes(etypes[-1]) else: c = color self.cgo_pointers.add_pointer(point=coords[-1], direction=coords[-2], length=1., color=cc(c), reverse=True) self.pymol_connector.add_cgo_object(name, self.cgo_pointers.get(), state=state)
[docs] def scatter(self, coords, radius=0.4, color='r', name='scatter', state=None): if isinstance(color, str): color = cc(color) if state is None: state = 1 self.cgo_spheres.clean() self.cgo_spheres.add(coords=coords, radius=radius, color=color) self.pymol_connector.add_cgo_object(name, self.cgo_spheres.get(), state=state)
[docs] def convexhull(self, chull, color='m', name='convexhull', state=None): if isinstance(color, str): color = cc(color) if state is None: state = 1 self.cgo_lines.clean() for nr, facet in enumerate(chull.facets): if nr > 0: self.cgo_lines.new() self.cgo_lines.add(facet, color=color) self.pymol_connector.add_cgo_object(name, self.cgo_lines.get(), state=state)