Изучение работы методов контроля температуры в молекулярной динамике

Будем изучать, как реализован контроль температуры в GROMACS, и сравнивать различные модели на примере молекулы этана.

In [100]:
%matplotlib inline

(Некоторые ячейки не запущены в отчетном варианте блокнота, чтобы не было большой выдачи)

In [ ]:
%%bash
cat /usr/share/gromacs/top/oplsaa.ff/atomtypes.atp
In [102]:
import rdkit
from rdkit import Chem
from rdkit.Chem import AllChem, Draw
from rdkit.Chem import Descriptors
from rdkit import RDConfig
from IPython.display import Image,display
import numpy as np
import matplotlib.pyplot as plt
In [103]:
# создадим этан
mol=Chem.MolFromSmiles('CC')
AllChem.Compute2DCoords(mol)
m3d=Chem.AddHs(mol)
Chem.AllChem.EmbedMolecule(m3d)
AllChem.MMFFOptimizeMolecule(m3d,maxIters=500,nonBondedThresh=200 )
Out[103]:
0
In [111]:
#посмотрим, какие нужно задать углы и связи
# и придумаем циклы 
## связи
bonds = m3d.GetBonds()
for i,b in enumerate(bonds):
        print b.GetBeginAtomIdx()+1 , b.GetEndAtomIdx()+1
 
 ## углы    
for b1 in m3d.GetBonds():
     for b2 in m3d.GetBonds():
        if b1.GetBeginAtomIdx() == b2.GetBeginAtomIdx() and b1.GetIdx() != b2.GetIdx():
            print b1.GetEndAtomIdx()+1 , b1.GetBeginAtomIdx()+1, b2.GetEndAtomIdx()+1
## dihedrals            
for b1 in m3d.GetBonds():
    for b2 in m3d.GetBonds():
        for b3 in m3d.GetBonds():
            if b1.GetBeginAtomIdx() == b2.GetBeginAtomIdx() and b2.GetEndAtomIdx() == b3.GetBeginAtomIdx()and b1.GetIdx() != b2.GetIdx():
                print b1.GetEndAtomIdx()+1 , b1.GetBeginAtomIdx()+1,b2.GetBeginAtomIdx()+1, b2.GetEndAtomIdx()+1
        
1 2
1 3
1 4
1 5
2 6
2 7
2 8
2 1 3
2 1 4
2 1 5
3 1 2
3 1 4
3 1 5
4 1 2
4 1 3
4 1 5
5 1 2
5 1 3
5 1 4
6 2 7
6 2 8
7 2 6
7 2 8
8 2 6
8 2 7
3 1 1 2
3 1 1 2
3 1 1 2
4 1 1 2
4 1 1 2
4 1 1 2
5 1 1 2
5 1 1 2
5 1 1 2
In [112]:
t = """#include "/usr/share/gromacs/top/oplsaa.ff/forcefield.itp"

[ moleculetype ]
; Name            nrexcl
et            3

[ atoms ]
;   nr  type  resnr  residue  atom   cgnr     charge       mass
    1   opls_139      1    ETH      C1      1    -0.189      12.01
    2   opls_139      1    ETH      C2      2    -0.155      12.01
    3   opls_140      1    ETH      H1      3     0.0059       1.008
    4   opls_140      1    ETH      H2      4     0.0059       1.008
    5   opls_140      1    ETH      H3      5     0.0059       1.008
    6   opls_140      1    ETH      H4      6     0.0056       1.008
    7   opls_140      1    ETH      H5      7     0.0056       1.008
    8   opls_140      1    ETH      H6      8     0.0056       1.008
    
[ bonds ]
;  ai    aj funct  b0       kb
     1   2   1  
     1   3   1
     1   4   1  
     1   5   1 
     2   6   1
     2   7   1
     2   8   1

[ angles ]
;  ai    aj    ak funct  phi0   kphi
;around c1
    3     1     4     1  
    4     1     5     1  
    3     1     5     1  
    2     1     3     1  
    2     1     4     1  
    2     1     5     1  
;around c2
    7     2     8     1  
    8     2     6     1  
    7     2     6     1  
    1     2     6     1  
    1     2     7     1  
    1     2     8     1  

[ dihedrals ]
;  ai    aj    ak    al funct  
    3    1     2     6      3  
    3    1     2     7      3 
    3    1     2     8      3  
    4    1     2     6      3
    4    1     2     7      3 
    4    1     2     8      3
    5    1     2     6      3
    5    1     2     7      3
    5    1     2     8      3

[ pairs ]
; список атомов 1-4
;  ai    aj funct
   3  6
   3  7
   3  8
   4  6
   4  7
   4  8
   5  6
   5  7
   5  8

[ System ]
; any text here
first one
[ molecules ]
;Name count
 et    1
"""
with open("et.top","w") as topology:
    topology.write(t)
In [113]:
et_gro = """etane
8
    1ETH     C1    1   0.577   0.217   0.574
    1ETH     C2    2   0.680   0.252   0.467
    1ETH     H1    3   0.478   0.241   0.538
    1ETH     H2    4   0.597   0.274   0.664
    1ETH     H3    5   0.583   0.111   0.597
    1ETH     H4    6   0.676   0.358   0.445
    1ETH     H5    7   0.660   0.195   0.377
    1ETH     H6    8   0.780   0.228   0.504
   1.50000   1.50000   1.50000
"""   
with open("et.gro","w") as grot:
    grot.write(et_gro)

Переходим к моделированию:
1)построим входные файлы для молекулярно-динамического движка mdrun
2)для каждого из них запустим mdrun
3)конвертируем в pdb файлы, которые нужно визуализировать
4)сравниваем потенциальную энергию и кинетическую энергию для каждой из 5 систем

In [ ]:
%%bash
export i='be vr nh an sd'
for k in $i; do
    grompp -f $k.mdp -c et.gro -p et.top -o et_$k.tpr
    mdrun -deffnm et_$k -v -nt 1
    echo 0 | trjconv -f et_$k.trr -s et_$k.tpr -o et_$k.pdb
    echo 10 11 | g_energy -f et_$k.edr -o et_en_$k.xvg -xvg none;
done

Файлы для визуального анализа:
Andersen.pdb
Berendsen.pdb
Velocity rescale.pdb
Nose-Hoover.pdb
Stochastic.pdb

Визуальный анализ показывает, что метод Андерсена чрезмерно ограничивает движение молекулы, а в методе стохастической молекулярной динамики, напротив, слишком велико отклонени молекулы от первоначального положения.

In [38]:
for i in ['be','vr','nh','an','sd']:
    a= np.loadtxt("et_en_"+i+'.xvg')
    t=a[:,0]
    y1=a[:,1]
    y2=a[:,2]
    plt.plot(t, y1, 'g-',label='potential')
    plt.plot(t, y2,'y-',label='kinetic' )
    plt.xlabel('time')
    plt.ylabel('energy, kJ/mol')
    plt.title('method '+i)
    plt.legend()
    plt.show()  

Графики зависимости энергий от времени.
Метод Берендсена: амплитуда изменения энергии уменьшается со временем до небольшого значения - 3-5 kJ/mol.
Метод "Velocity rescale": изменение энергии в диапазоне 10-50 kJ/mol, амплитуда в среднем не меняется.
Метод Нуза-Хувера: не понятно, каково было начальное значение температуры, разброс очень большой, амплитуда примерно сохраняется.
Метод Андерсена: амплитуда не меняется, ее значение мало по сравнению с другими методами.
Метод стохастической молекулярной динамики: зависимость схожа с графиком для "Velocity rescale", но потенциальная энергия здесь чуть меньше кинетической.

In [ ]:
%%bash
export i='be vr nh an sd'
for k in $i; do
    g_bond -f et_$k.trr -s et_$k.tpr -o bond_$k.xvg -d distance_$k -n b.ndx -xvg none;
done
In [85]:
from scipy.stats import gaussian_kde
In [97]:
for i in ['be','vr','nh','an','sd']:
    a= np.loadtxt("distance_"+i+'.xvg')
    y=a[:,1]
    plt.hist(y, bins=17, color="green", range=(0.143,0.167))
    plt.xlabel("C-C bond's length")
    plt.title('method '+i)
    plt.show() 
   

    

Распределение Больцмана показывает распределение молекул по скоростям. Метод Берендсена дает неплохие результаты: гистограмма отдаленно напоминает распределение Больцмана (можно заметить небольшое смещение пика влево).
Метод "Velocity rescale": наибольшее сходство с распределением Больцмана по сравнению со всеми остальными гистограммами.
Метод Нуза-Хувера и метод стохастической молекулярной динамики: распределение слишком симметрично и скорее напоминает нормальное.
Метод Андерсена: узкое распределение - разброс длины связи маленький.

Метод "Velocity rescale" дает самую реалистичную картину поведения молекулы этана при поддержании температуры. Также не плохи методы Берендсена и Нуза-Хувера. Однако первый накладывает значительные ограничения на динамику молекулы, как видно на графике изменения энергии. Второй также показывает странное изменение энергии от времени - слишком большой разброс (хотя длина связи изменяется не значительно по сравнению с другими). Метод Андерсена плохо подходит для описания естественного поведения молекулы при постоянной температуре: молекула слишком мало двигается. Видео, гистограмма и график для энергий подтверждают это. Метод стохастической молекулярной динамики плохо подходит: молекула слишком подвижна, меняет не только длину связи, торсионные углы, но и положение в пространстве.

Лучшим методом для поддержания температуры является "Velocity rescale". Время работы не сильно различается между методами. Заметно дольше работает только метод стохастической молекулярной динамики.

In [114]:
from IPython.display import HTML, display
import tabulate
table = [["method", "time, s"],["Andersen",3.361],["Berendsen",3.418],["Velocity rescale",3.482],["Nose-Hoover",3.517],["Stochastic",4.442]]
display(HTML(tabulate.tabulate(table, tablefmt='html')))
method time, s
Andersen 3.361
Berendsen 3.418
Velocity rescale3.482
Nose-Hoover 3.517
Stochastic 4.442
In [ ]: