Запускаем PyMOL.
# pymol launching
import time
import __main__
__main__.pymol_argv = [ 'pymol', '-x' ]
### Если вывод в графическое окно тормозит или не нужен, то:
##__main__.pymol_argv = [ 'pymol', '-cp' ]
import pymol
pymol.finish_launching()
from pymol import cmd
from IPython.display import Image
# Borrowed from http://kodomo.cmm.msu.su/~sapsan/v2/terms/term8/PyMolPractice1.html.
defaultImage = './pymolimg.png'
def prepareImage(width=600, height=600, sleep=2, filename=defaultImage):
## To save the rendered image
cmd.ray(width, height)
cmd.png(filename)
time.sleep(sleep)
Чистим рабочее пространство: мало ли что туда затесалось.
Загружаем интересующую структуру из PDB: 1lmp.
cmd.reinitialize()
cmd.fetch("1LMP", async=0)
prepareImage()
Image(defaultImage)
Как нам выбрать лиганды? Мы откуда-нибудь знаем, что нам доступны следующие селекторы: hetatom
, или het
, возвращающий все атомы, не принадлежащие белкам, и solvent
, или sol.
, возвращающий все атомы в молекулах воды. Вода нам не нужна, и её можно с чистой совестью удалить; все оставшиеся небелковые атомы будут лигандами. Их мы для удобства вынесем в отдельный объект.
cmd.remove("solvent")
# cmd.select("ligands", "het")
cmd.extract("ligands", "het")
cmd.show_as("sticks", "ligands")
prepareImage()
Image(defaultImage)
Fun fact: if you make a selection of X, then extract X, then manually delete the selection, you can't refer to the extracted object! It's fun. And a parable not to use GUI.
Кхм.
Теперь выделим связи лиганда с белком. Для этого у нас есть команда distance, с аргументами: именем, объектами, между атомами которых смотрятся расстояния, максимальным интересным расстоянием и режимом.
Максимальным расстоянием мы выберем 3.2 (по традиции, 3.2 есть граница между связями водородными и прочими); режимом — режим 2: это должны быть приблизительно водородные связи.
Технически, distance <...> mode=2
может ошибаться и строить не только водородные связи, и конечное решение о том, что является водородной связью, остаётся на пользователе. Как мы могли бы проверить адекватность?
Если быть честными, то никак: квалификации, чтобы на глаз отличить истинную водородную связь от ложной, у нас нет.
Можно вручную добавить водороды; в таком случае связи буду нарисованы не между донором и акцептором, а между водородом и акцептором, набор связей принципиально не поменяется.
cmd.h_add("all")
cmd.distance("hbonds", "1LMP", "ligands", 3.2, 2)
prepareImage()
Image(defaultImage)
Как сломать взаимодействие белка с лигандом? Двумя путями: отломать несколько водородных связей (2+), или вставить что-нибудь в пространство, которое занимает лиганд.
Пристальным взглядом смотрим на места контакта; замечаем, что ASP`52, будь он длиннее, вдавался бы в лиганд и не давал бы ему занимать подобающее место. Мутируем ASP в TYR.
cmd.wizard("mutagenesis")
cmd.do("refresh_wizard")
cmd.get_wizard().set_mode("TYR")
cmd.get_wizard().do_select("52/")
cmd.get_wizard().apply()
cmd.wizard(None)
Снимаем кино. В кино происходит совмещение двух белков — оригинального и мутированного — и фокус на мутированном остатке.
# Something below breaks hbonds in little pieces, so they're disabled.
cmd.disable("hbonds")
cmd.do('''
set matrix_mode, 1
set movie_auto_store, 1
set movie_auto_interpolate, 1
delete 1LMP_orig
fetch 1LMP, 1LMP_orig, async=0
translate [0, 0, 35], 1LMP_orig
rotate y, 40
rotate x, 25
orient
select tyr, resi 52 and 1LMP
select asp, resi 52 and 1LMP_orig
as sticks, tyr or asp
as sticks, ligand
color purple, n. C* in ligand
color cyan, n. CA+C+N+C* and 1LMP_orig
mset 1 x400
frame 1
mview store
mview store, object=1lmp
mview store, object=1lmp_orig
frame 200
center tyr
origin tyr
zoom tyr
super 1lmp_orig, 1lmp
mview store
mview store, object=1lmp
mview store, object=1lmp_orig
frame 400
orient ligand or tyr
zoom ligand or tyr
mview store
mview store, object=1lmp
mview store, object=1lmp_orig
frame 1
mplay
''')
Что такое сложноэфирная связь? Это связь R1-COOH + R2-OH → R1-COO-R2 + H2O.
Как присоединить флуор-метку:
cmd.reinitialize()
cmd.fetch("1LMP", async=0)
cmd.load("6-TAMRA.sdf")
cmd.remove("solvent")
# O-atoms in 6-TAMRA are not easily differentiated.
# Thankfully, one of them has a hydrogen attached.
cmd.select("O1-tamra", "n. O in 6-TAMRA within 2 of n. H in 6-TAMRA")
cmd.select("O1-serine", "SER`37/CB")
cmd.fuse("O1-tamra", "O1-serine")
cmd.torsion(-75)
cmd.select("sele", "1LMP///UNK/ or 1LMP///`36-38/")
cmd.orient("sele")
cmd.show_as("sticks", "sele")
# cmd.rotate()
prepareImage()
Image(defaultImage)
Как сделать ∞-альфа-спираль:
Поэтому:
cmd.reinitialize()
cmd.fragment("lys")
cmd.edit("lys/C")
for i in range(1, 25):
cmd.editor.attach_amino_acid("pk1", "lys", ss=1)
cmd.orient()
cmd.select("bbone", "n. CA+C+N in lys")
cmd.disable("bbone")
cmd.color("cyan","bbone")
cmd.rotate("z", -60)
cmd.show_as("sticks", "bbone")
prepareImage()
Image(defaultImage)
Как сделать ∞-DNA:
Что мы узнали в процессе:
cmd.reinitialize()
cmd.fetch("1bna", async=0)
cmd.hide("lines")
cmd.remove("solvent")
# 7-18 and 6-19 are good, but invert chains
# 5-20 and 6-19 are slightly worse, but keep the chains
cmd.extract("bp1", "A/`5/ or B/`20/")
cmd.extract("bp2_", "A/`6/ or B/`19/")
cmd.orient()
cmd.delete("1bna")
cmd.create("matrix", "bp1")
cmd.align("matrix", "bp2_")
cmd.delete("bp2_")
n = 30
cmd.alter("bp1/A///", "resi=1")
cmd.alter("bp1/B///", "resi={}".format(n))
# This works like so:
# Loop begins with a matrix pair, linked chain of length n and one unlinked
# pair. We apply matrix to the pair, make a copy, return pair to the
# original position, set resi of the new basepair as appropriate.
# Then we fuse and link the old pair with the chain, leaving a linked chain
# of length n+1 and an unlinked pair at the end of the loop.
# Loop starts with a chain of legth zero, and only links basepairs starting
# from 2nd. After the end, the extraneous basepair is deleted.
for i in range(1, n+1):
cmd.matrix_copy("matrix", "bp{}".format(i))
cmd.create("bp{}".format(i+1), "bp{}".format(i))
cmd.matrix_reset("bp{}".format(i))
cmd.alter("bp{}/A///".format(i+1), "resi={}".format(i+1))
cmd.alter("bp{}/B///".format(i+1), "resi={}".format(n-i))
if i <= 1: continue
cmd.fuse("bp{}".format(i), "bp1", mode=3)
cmd.delete("bp{}".format(i))
cmd.bond("bp1/A//`{}/P".format(i),
"bp1/A//`{}/O3'".format(i-1))
cmd.bond("bp1/B//`{}/P".format(n-i+2),
"bp1/B//`{}/O3'".format(n-i+1))
cmd.delete("bp{}".format(n+1))
cmd.delete("matrix")
cmd.orient()
cmd.rotate("z", -60)
cmd.show_as("sticks")
prepareImage()
Image(defaultImage)