A package for building molecular dynamics inputs for SPONGE
Project description
Xponge
简介
Xponge是由Python编写的轻量化能高度自定义的分子动力学模拟的前后处理工具。Python版本需大于3.6,安装直接使用pip即可
pip install Xponge
安装成功以后,在Python脚本中直接import
即可使用
import Xponge
Xponge最基本的功能除了依赖于Python的标准库外,还依赖了numpy、pubchempy,这两个包将会在pip install Xponge时将会自动安装,而一些更复杂的功能可能包括其他模块,这些模块需要自行安装。
Xponge目前还处于早期开发版本,此处介绍一些基本功能,不代表最终版本效果。
力场生成
A. 现有力场
A1. 现有残基,无初始坐标文件
- 引入现有力场
import Xponge
import Xponge.forcefield.AMBER.ff14SB
会得到包含参考文献的打印信息
Reference for ff14SB.py:
James A. Maier, Carmenza Martinez, Koushik Kasavajhala, Lauren Wickstrom, Kevin E. Hauser, and Carlos Simmerling
ff14SB: Improving the accuracy of protein side chain and backbone parameters from ff99SB
Journal of Chemical Theory and Computation 2015 11 (8), 3696-3713
DOI: 10.1021/acs.jctc.5b00255
- 查看现有残基
print(Xponge.ResidueType.types)
得到结果:
{'ACE': Type of Residue: ACE, 'ASH': Type of Residue: ASH, 'CYM': Type of Residue: CYM, 'GLH': Type of Residue: GLH, 'LYN': Type of Residue: LYN, 'NME': Type of Residue: NME, 'NALA': Type of Residue: NALA, 'ALA': Type of Residue: ALA, 'CALA': Type of Residue: CALA, 'NARG': Type of Residue: NARG, 'ARG': Type of Residue: ARG, 'CARG': Type of Residue: CARG, 'NASN': Type of Residue: NASN, 'ASN': Type of Residue: ASN, 'CASN': Type of Residue: CASN, 'NASP': Type of Residue: NASP, 'ASP': Type of Residue: ASP, 'CASP': Type of Residue: CASP, 'NCYS': Type of Residue: NCYS, 'CYS': Type of Residue: CYS, 'CCYS': Type of Residue: CCYS, 'NCYX': Type of Residue: NCYX, 'CYX': Type of Residue: CYX, 'CCYX': Type of Residue: CCYX, 'NGLN': Type of Residue: NGLN, 'GLN': Type of Residue: GLN, 'CGLN': Type of Residue: CGLN, 'NGLU': Type of Residue: NGLU, 'GLU': Type of Residue: GLU, 'CGLU': Type of Residue: CGLU, 'NGLY': Type of Residue: NGLY, 'GLY': Type of Residue: GLY, 'CGLY': Type of Residue: CGLY, 'NHID': Type of Residue: NHID, 'HID': Type of Residue: HID, 'CHID': Type of Residue: CHID, 'NHIE': Type of Residue: NHIE, 'HIE': Type of Residue: HIE, 'CHIE': Type of Residue: CHIE, 'NHIP': Type of Residue: NHIP, 'HIP': Type of Residue: HIP, 'CHIP': Type of Residue: CHIP, 'NHE': Type of Residue: NHE, 'HYP': Type of Residue: HYP, 'CHYP': Type of Residue: CHYP, 'NILE': Type of Residue: NILE, 'ILE': Type of Residue: ILE, 'CILE': Type of Residue: CILE, 'NLEU': Type of Residue: NLEU, 'LEU': Type of Residue: LEU, 'CLEU': Type of Residue: CLEU, 'NLYS': Type of Residue: NLYS, 'LYS': Type of Residue: LYS, 'CLYS': Type of Residue: CLYS, 'NMET': Type of Residue: NMET, 'MET': Type of Residue: MET, 'CMET': Type of Residue: CMET, 'NPHE': Type of Residue: NPHE, 'PHE': Type of Residue: PHE, 'CPHE': Type of Residue: CPHE, 'NPRO': Type of Residue: NPRO, 'PRO': Type of Residue: PRO, 'CPRO': Type of Residue: CPRO, 'NSER': Type of Residue: NSER, 'SER': Type of Residue: SER, 'CSER': Type of Residue: CSER, 'NTHR': Type of Residue: NTHR, 'THR': Type of Residue: THR, 'CTHR': Type of Residue: CTHR, 'NTRP': Type of Residue: NTRP, 'TRP': Type of Residue: TRP, 'CTRP': Type of Residue: CTRP, 'NTYR': Type of Residue: NTYR, 'TYR': Type of Residue: TYR, 'CTYR': Type of Residue: CTYR, 'NVAL': Type of Residue: NVAL, 'VAL': Type of Residue: VAL, 'CVAL': Type of Residue: CVAL, 'HIS': Type of Residue: HIE, 'NHIS': Type of Residue: NHIE, 'CHIS': Type of Residue: CHIE}
- 使用现有残基构建初始结构
残基类(Xponge.ResidueType)、残基(Xponge.Residue)、分子(Xponge.Molecule)之间,可以使用+
进行连接获得分子(Xponge.Molecule),也可以*
整数(int)进行多次自我连接。连接规则由刚才import的力场文件决定。
protein = ACE + ALA * 3 + NME
- 保存输出
残基类(Xponge.ResidueType)、残基(Xponge.Residue)、分子(Xponge.Molecule)可以使用Save_PDB
、Save_SPONGE_Input
、Save_Mol2
、Save_NPZ
分别保存为对应的文件格式。
Save_PDB(protein, "protein.pdb")
Save_SPONGE_Input(protein, "protein")
使用VMD观看保存的PDB
vmd protein.pdb
或利用vmd的SPONGE插件
vmd -sponge_mass ./protein_mass.txt -sponge_crd ./protein_coordinate.txt
可获得如下结果
- 动力学模拟
此处仅作示例,因此保存的分子较小,人为修改生成的protein_coordinate.txt文件的最后一行,将盒子的长度改为50.0,以避免过小的周期性边界造成的影响。
修改好后,新建"mdin.txt"文件,并填写
basic test of Xponge
mode = NVT
thermostat = Andersen_thermostat
dt = 2e-3
constrain_mode = SHAKE
cutoff = 8.0
step_limit = 5000
coordinate_in_file = protein_coordinate.txt
angle_in_file = protein_angle.txt
bond_in_file = protein_bond.txt
charge_in_file = protein_charge.txt
dihedral_in_file = protein_dihedral.txt
exclude_in_file = protein_exclude.txt
LJ_in_file = protein_LJ.txt
mass_in_file = protein_mass.txt
nb14_in_file = protein_nb14.txt
residue_in_file = protein_residue.txt
使用SPONGE运行,获得如下结果
---------------------------------------------------------------------------------------
step = 1000, time = 2.000, temperature = 305.35,
potential = 47.21, LJ = -3.73, PME = -232.74,
nb14_LJ = 26.62, nb14_EE = 196.30, bond = 12.74,
angle = 16.63, dihedral = 32.51,
---------------------------------------------------------------------------------------
step = 2000, time = 4.000, temperature = 194.94,
potential = 51.01, LJ = -2.02, PME = -229.18,
nb14_LJ = 30.27, nb14_EE = 188.48, bond = 12.04,
angle = 22.31, dihedral = 30.75,
---------------------------------------------------------------------------------------
step = 3000, time = 6.000, temperature = 246.57,
potential = 49.60, LJ = -4.42, PME = -222.75,
nb14_LJ = 31.28, nb14_EE = 189.09, bond = 11.10,
angle = 16.47, dihedral = 30.80,
---------------------------------------------------------------------------------------
step = 4000, time = 8.000, temperature = 273.81,
potential = 55.53, LJ = -4.54, PME = -231.54,
nb14_LJ = 30.79, nb14_EE = 196.70, bond = 9.60,
angle = 18.88, dihedral = 29.40,
---------------------------------------------------------------------------------------
step = 5000, time = 10.000, temperature = 344.12,
potential = 48.44, LJ = -5.35, PME = -230.99,
nb14_LJ = 29.09, nb14_EE = 198.53, bond = 6.32,
angle = 19.48, dihedral = 31.79,
---------------------------------------------------------------------------------------
部分内容在后续例子中相同,后续例子只在有必要的时候展示重复操作的结果部分。
- 该部分完整Python代码
import Xponge
import Xponge.forcefield.AMBER.ff14SB
protein = ACE + ALA * 3 + NME
Save_PDB(protein, "protein.pdb")
Save_SPONGE_Input(protein, "protein")
A2. 现有残基,有初始坐标
目前推荐是从PDB文件中读入
- 引入现有力场
import Xponge
import Xponge.forcefield.AMBER.ff14SB
- 读入现有文件
示例pdb文件:由H++补氢并添加了SSBOND信息的新冠病毒Spike蛋白与ACE2受体结合的PDB
protein = loadpdb("0.15_80_10_pH6.5_6lzg.result.pdb")
- 该部分完整Python代码
import Xponge
import Xponge.forcefield.AMBER.ff14SB
protein = loadpdb("0.15_80_10_pH6.5_6lzg.result.pdb")
Save_PDB(protein, "protein.pdb")
Save_SPONGE_Input(protein, "protein")
注意事项
- PDB文件格式是标准化的,修改前请确认理解PDB的格式
- 从Protein Data Bank下载的PDB文件不包含氢,目前SPONGE不支持自动补氢,请手动或使用其他工具(如H++)补氢。补氢时需要注意氢的命名需与标准相同
- H++产生的PDB不包含SSBOND,需自行将原始PDB中SSBOND部分添加至H++产生的PDB中,Xponge能自动将二硫键的半胱氨酸残基更名。另外,Xponge能够自动根据质子判断组氨酸的残基名
A3. 非现有单独残基,已知信息
只需要构建好残基类(Xponge.ResidueType),将非现有残基转化为现有残基
以gaff力场下的自定义水为例
方案1 从mol2文件中构建
编写mol2文件
@<TRIPOS>MOLECULE
TP3
3 2 1 0 1
SMALL
USER_CHARGES
@<TRIPOS>ATOM
1 O 0.000000 0.000000 0.000000 oh 1 WAT -0.8340 ****
2 H1 0.957200 0.000000 0.000000 ho 1 WAT 0.4170 ****
3 H2 -0.239988 0.926627 0.000000 ho 1 WAT 0.4170 ****
@<TRIPOS>BOND
1 1 2 1
2 1 3 1
@<TRIPOS>SUBSTRUCTURE
1 WAT 1 **** 0 **** ****
然后在Python中
import Xponge
import Xponge.forcefield.AMBER.gaff
TP3 = loadmol2("WAT.mol2")
Save_SPONGE_Input(TP3, "tp3")
Xponge会根据mol2文件中的残基名(此例中的"WAT")在内部添加残基类(Xponge.ResidueType),而loadmol2最后会返回的(此例中的"TP3")是包含该mol2中所有残基的分子(Xponge.Molecule)。参与PDB中残基识别只与残基类(Xponge.ResidueType)有关。
方案2 Python代码构建
#构建新的空残基类
import Xponge
import Xponge.forcefield.AMBER.gaff
WAT = Xponge.ResidueType(name = "WAT")
#添加原子信息
WAT.Add_Atom(name = "O", atom_type = Xponge.AtomType.types["oh"], x = 0, y = 0, z = 0)
WAT.O.Update(**{"charge[e]": -0.8340})
WAT.Add_Atom(name = "H1", atom_type = Xponge.AtomType.types["ho"], x = 0.9572, y = 0, z = 0)
WAT.H1.Update(**{"charge[e]": 0.4170})
WAT.Add_Atom(name = "H2", atom_type = Xponge.AtomType.types["ho"], x = -0.239988, y = 0.926627, z = 0)
WAT.H2.Update(**{"charge[e]": 0.4170})
#添加连接信息
WAT.Add_Connectivity(WAT.O, WAT.H1)
WAT.Add_Connectivity(WAT.O, WAT.H2)
Save_SPONGE_Input(WAT, "TP3")
A4. 非现有单独残基,未知信息
以gaff力场下的2,3-二甲基苯甲酸乙酯为例
方案1 通过IUPAC名或SMILES结构式从PubChem中获取基本的结构
import Xponge
#从PubChem中获取结构
assign = Get_Assignment_From_PubChem("ethyl 2,6-dimethylbenzoate", "name")
#也可以使用SMILES
#assign = Get_Assignment_From_PubChem("CCOC(=O)C1=C(C=CC=C1C)C", "smiles")
#自动推断原子类别
import Xponge.forcefield.AMBER.gaff
assign.Determine_Atom_Type("GAFF")
#保存assignment至mol2文件中
Save_Mol2(assign, "EDF_ASN.mol2")
#通过vmd判断出等价性
equivalence = [[9,10], range(16,22), [3,4], [5,6], [13,14]]
q = assign.Calculate_Charge("RESP", basis = "6-311g*", grid_density = 1,
extra_equivalence = equivalence, opt = True)
EDF = assign.To_ResidueType("EDF", q)
Save_Mol2(EDF, "EDF.mol2")
最后获得的"EDF.mol2"即可进行下一步计算
@<TRIPOS>MOLECULE
EDF
27 27 1 0 1
SMALL
USER_CHARGES
@<TRIPOS>ATOM
1 O 1.826 -0.000 0.361 os 1 EDF -0.563454
2 O1 1.320 -0.002 -1.787 o 1 EDF -0.656809
3 C -0.453 -0.000 -0.196 ca 1 EDF -0.456248
4 C1 -1.103 -1.216 -0.002 ca 1 EDF 0.283884
5 C2 -1.101 1.217 -0.003 ca 1 EDF 0.283884
6 C3 -2.432 -1.197 0.395 ca 1 EDF -0.284050
7 C4 -2.431 1.200 0.394 ca 1 EDF -0.284050
8 C5 -3.091 0.002 0.592 ca 1 EDF -0.137956
9 C6 0.979 -0.001 -0.654 c 1 EDF 1.049933
10 C7 -0.382 -2.527 -0.219 c3 1 EDF -0.396791
11 C8 -0.379 2.527 -0.221 c3 1 EDF -0.396791
12 C9 3.218 -0.000 0.060 c3 1 EDF 0.506223
13 C10 3.968 0.001 1.373 c3 1 EDF -0.267413
14 H -2.953 -2.125 0.550 ha 1 EDF 0.170890
15 H1 -2.951 2.129 0.548 ha 1 EDF 0.170890
16 H2 -4.122 0.002 0.899 ha 1 EDF 0.163931
17 H3 -0.010 -2.607 -1.234 hc 1 EDF 0.118938
18 H4 0.467 -2.623 0.451 hc 1 EDF 0.118938
19 H5 -1.041 -3.366 -0.037 hc 1 EDF 0.118938
20 H6 -0.007 2.605 -1.236 hc 1 EDF 0.118938
21 H7 -1.037 3.366 -0.041 hc 1 EDF 0.118938
22 H8 0.470 2.622 0.449 hc 1 EDF 0.118938
23 H9 3.449 -0.875 -0.533 h1 1 EDF -0.046089
24 H10 3.449 0.872 -0.535 h1 1 EDF -0.046089
25 H11 5.037 0.001 1.189 hc 1 EDF 0.064160
26 H12 3.723 0.880 1.958 hc 1 EDF 0.064160
27 H13 3.723 -0.876 1.960 hc 1 EDF 0.064160
@<TRIPOS>BOND
1 1 9 1
2 1 12 1
3 2 9 1
4 3 4 1
5 3 5 1
6 3 9 1
7 4 6 1
8 4 10 1
9 5 7 1
10 5 11 1
11 6 8 1
12 6 14 1
13 7 8 1
14 7 15 1
15 8 16 1
16 10 17 1
17 10 18 1
18 10 19 1
19 11 20 1
20 11 21 1
21 11 22 1
22 12 13 1
23 12 23 1
24 12 24 1
25 13 25 1
26 13 26 1
27 13 27 1
@<TRIPOS>SUBSTRUCTURE
1 EDF 1 **** 0 **** ****
目前Xponge不支持自动判断等价原子,因此需要靠vmd来手动判断
目前Xponge的assignment只支持只由C、H、O、N、S、P、F、Cl、Br、I元素构成的物质
方案2 其他方式构建assignment的mol2文件
只需将对应的Get_Assignment_From_PubChem更改为Get_Assignment_From_Mol2即可
assign = Get_Assignment_From_Mol2("EDF_ASN.mol2")
注意,任务(Xponge.assign.Assign)和分子(Xponge.Molecule)均可读mol2,但是它们是不一样的,而且文件中的信息有效性也不同
任务(Xponge.assign.Assign)使用Get_Assignment_From_Mol2读取,其中的原子类别为元素符号,且bond部分中的键需要明确键级
分子(Xponge.Molecule)使用loadmol2读取,其中的原子类别为力场中的符号,且bond部分中的键不会读取键级
方案3 Python代码构建
关键在于构建任务类(Xponge.assign.Assign),其他步骤相同。此处以构建水分子为例。
import Xponge
import Xponge.forcefield.AMBER.gaff
assign = Xponge.assign.Assign()
assign.Add_Atom(element = "O", x = 0, y = 0, z = 0)
assign.Add_Atom(element = "H", x = 0.9572, y = 0, z = 0)
assign.Add_Atom(element = "H", x = -0.239988, y = 0.926627, z = 0)
assign.Add_Bond(0,1,1) #0号原子和1号原子之间形成一个单键
assign.Add_Bond(0,2,1) #0号原子和2号原子之间形成一个单键
#注意,在推断原子类型前需要推断环和键的信息,虽然对水没有用
assign.Determine_Ring_And_Bond_Type()
assign.Determine_Atom_Type("GAFF")
q = assign.Calculate_Charge("RESP", extra_equivalence = [[1,2]])
WAT = assign.To_ResidueType("WAT", q)
Save_Mol2(WAT, "WAT.mol2")
最终即获得"WAT.mol2":
@<TRIPOS>MOLECULE
WAT
3 2 1 0 1
SMALL
USER_CHARGES
@<TRIPOS>ATOM
1 O 0.000 0.000 0.000 oh 1 WAT -0.799534
2 H 0.957 0.000 0.000 ho 1 WAT 0.399767
3 H1 -0.240 0.927 0.000 ho 1 WAT 0.399767
@<TRIPOS>BOND
1 1 2 1
2 1 3 1
@<TRIPOS>SUBSTRUCTURE
1 WAT 1 **** 0 **** ****
A5. 非现有非单独残基
方案1 mol2文件构建
以ff14SB力场下丙氨酸二肽的构建为例
实际上,ff14SB力场中包含丙氨酸二肽,此处只为示例
先构建dipeptide.mol2文件
@<TRIPOS>MOLECULE
ACE
22 21 3 0 1
SMALL
USER_CHARGES
@<TRIPOS>ATOM
1 H1 0.466 -8.051 1.242 HC 1 ACE 0.112300
2 CH3 0.289 -7.339 0.436 CT 1 ACE -0.366200
3 H2 -0.703 -6.901 0.548 HC 1 ACE 0.112300
4 H3 0.352 -7.853 -0.524 HC 1 ACE 0.112300
5 C 1.325 -6.213 0.457 C 1 ACE 0.597200
6 O 2.209 -6.195 1.311 O 1 ACE -0.567900
7 N 1.275 -5.267 -0.433 N 2 ALA -0.415700
8 H 0.563 -5.249 -1.150 H 2 ALA 0.271900
9 CA 2.256 -4.201 -0.413 CX 2 ALA 0.033700
10 HA 2.199 -3.671 0.538 H1 2 ALA 0.082300
11 CB 3.668 -4.752 -0.580 CT 2 ALA -0.182500
12 HB1 3.744 -5.277 -1.532 HC 2 ALA 0.060300
13 HB2 4.384 -3.930 -0.561 HC 2 ALA 0.060300
14 HB3 3.888 -5.443 0.234 HC 2 ALA 0.060300
15 C 2.009 -3.207 -1.539 C 2 ALA 0.597300
16 O 1.072 -3.368 -2.318 O 2 ALA -0.567900
17 N 2.791 -2.179 -1.682 N 3 NME -0.415700
18 H 3.571 -2.011 -1.063 H 3 NME 0.271900
19 CH3 2.555 -1.233 -2.754 CT 3 NME -0.149000
20 HH31 1.686 -1.546 -3.331 H1 3 NME 0.097600
21 HH32 2.374 -0.244 -2.333 H1 3 NME 0.097600
22 HH33 3.429 -1.195 -3.405 H1 3 NME 0.097600
@<TRIPOS>BOND
1 1 2 1
2 2 3 1
3 2 4 1
4 2 5 1
5 5 6 1
6 5 7 1
7 7 8 1
8 7 9 1
9 9 10 1
10 9 11 1
11 9 15 1
12 11 12 1
13 11 13 1
14 11 14 1
15 15 16 1
16 15 17 1
17 17 18 1
18 17 19 1
19 19 20 1
20 19 21 1
21 19 22 1
@<TRIPOS>SUBSTRUCTURE
1 ACE 1 **** 0 **** ****
2 ALA 7 **** 0 **** ****
3 NME 17 **** 0 **** ****
然后在python中读入力场参数后加载mol2文件即可正常使用该残基,并会自动按照mol2的前后连接关系在后续的PDB读入中连接。
import Xponge
import Xponge.forcefield.AMBER.ff14SB
protein = loadmol2("dipeptide.mol2")
如果不需要使用+
和*
构建新分子,则到处就已经满足使用了。如果希望正确使用+
和*
,则需要再设置连接信息
res = Xponge.ResidueType.types["ALA"]
res.head = "N" #残基的头部(与前一个残基连接的原子)atom name是N
res.head_length = 1.3 #与前一个残基连接的键长是1.3埃
res.head_next = "CA" #残基的主链头部第二个原子atom name是CA
res.tail = "C" #残基的尾部(与后一个残基连接的原子)atom name是N
res.tail_length = 1.3 #与后一个残基连接的键长是1.3埃
res.tail_next = "CA" #残基的主链尾部第二个原子atom name是CA
#清空ff14SB中已有的信息,普通构建未有残基不需要
while res.head_link_conditions:
res.head_link_conditions.pop()
while res.head_link_conditions:
res.tail_link_conditions.pop()
import numpy as np
#与前一个残基连接时,CA-N-前一个残基的tail原子形成的角为120/180 * np.pi
res.head_link_conditions.append({"atoms":["CA", "N"], "parameter": 120/180 * np.pi})
#与前一个残基连接时,H-CA-N-前一个残基的tail原子形成的二面角为-np.pi
res.head_link_conditions.append({"atoms":["H", "CA", "N"], "parameter": -np.pi})
#与后一个残基连接时,CA-C-后一个残基的head原子形成的角为120/180 * np.pi
res.tail_link_conditions.append({"atoms":["CA", "C"], "parameter": 120/180 * np.pi})
#与后一个残基连接时,O-CA-C-后一个残基的head原子形成的二面角为-np.pi
res.tail_link_conditions.append({"atoms":["O", "CA", "C"], "parameter": -np.pi})
刚体连接有6个自由度,分别是1个bond,2个angle,3个dihedral。其中1个bond由键长决定,1个主链dihedral(tail_next - tail - head - head_next)设定为180度防止重叠,剩余4个自由度由前一个残基和后一个残基分别定义一个angle和dihedral来实现
如果希望在PDB中,端基不同,则可产生不同的残基类(Xponge.ResidueType),如NALA、CALA,然后在全局设置中设置
Xponge.GlobalSetting.Add_PDB_Residue_Name_Mapping("head", "ALA", "NALA")
Xponge.GlobalSetting.Add_PDB_Residue_Name_Mapping("tail", "ALA", "CALA")
特别地,对于组氨酸判别不同质子态和半胱氨酸判别二硫键,需要额外设置
Xponge.GlobalSetting.HISMap["DeltaH"] = "HD1"
Xponge.GlobalSetting.HISMap["EpsilonH"] = "HE2"
Xponge.GlobalSetting.HISMap["HIS"].update({"HIS": {"HID":"HID", "HIE":"HIE", "HIP":"HIP"},
"CHIS":{"HID":"CHID", "HIE":"CHIE", "HIP":"CHIP"},
"NHIS":{"HID":"NHID", "HIE":"NHIE", "HIP":"NHIP"}})
Xponge.ResidueType.types["CYX"].connect_atoms["ssbond"] = "SG"
方案2 Python代码构建
类似A3方案2构建残基类(Xponge.ResidueType),然后再按A5方案1设置连接信息即可。
B. 非现有力场
B1. 新力场参数
方案1 字符串/文本文件/npz文件读取
以在AMBER力场形式下构建OPLSAA力场的硝基甲烷为例。数据来源于GMXTPP。
import Xponge
import Xponge.forcefield.AMBER
#构建新的原子类
Xponge.AtomType.New_From_String("""name mass charge[e] LJtype
opls_760 14.01 0.54 opls_760
opls_761 16.00 -0.37 opls_761
opls_762 12.01 0.02 opls_762
opls_763 1.008 0.06 opls_763""")
#构建新的LJ参数信息
Xponge.forcefield.BASE.LJ.LJType.New_From_String("""name sigma[nm] epsilon[kJ/mol]
opls_760-opls_760 0.3250000 0.5020800
opls_761-opls_761 0.2960000 0.7112800
opls_762-opls_762 0.3500000 0.2761440
opls_763-opls_763 0.2500000 0.0627600
""")
#构建新的成键信息
#New_From_NPZ待更新
temp_dict = {"opls_762-opls_763": {"b[nm]":0.109, "k[kJ/mol·nm^-2]": 284512},
"opls_762-opls_760": {"b[nm]":0.14900, "k[kJ/mol·nm^-2]": 313800},
"opls_760-opls_761": {"b[nm]":0.1225, "k[kJ/mol·nm^-2]": 460240}}
Xponge.forcefield.BASE.BOND.BondType.New_From_Dict(temp_dict)
Xponge.forcefield.BASE.ANGLE.AngleType.New_From_File("test_angle.txt")
Xponge.forcefield.BASE.DIHEDRAL.ProperType.New_From_String("""name phi0[degree] k[kJ/mol] periodicity reset
opls_763-opls_762-opls_760-opls_761 0 0 0 1
""")
Xponge.forcefield.BASE.DIHEDRAL.ImproperType.New_From_String("""name phi0[degree] k[kJ/mol] periodicity
opls_761-opls_761-opls_760-X 180.0 43.93200 2
""")
#OPLS力场的nb14的修正系数与amber不同
Xponge.forcefield.BASE.NB14.NB14Type.New_From_String("""name kLJ kee
opls_763-opls_761 0.5 0.5
""")
NIM = Xponge.ResidueType(name = "NIM")
#添加原子信息
NIM.Add_Atom(name = "CT", atom_type = Xponge.AtomType.types["opls_762"], x = 1.107, y = 1.13, z = 1.117)
NIM.Add_Atom(name = "HC1", atom_type = Xponge.AtomType.types["opls_763"], x = 1.143, y = 1.029, z = 1.117)
NIM.Add_Atom(name = "HC2", atom_type = Xponge.AtomType.types["opls_763"], x = 1.143, y = 1.18, z = 1.03)
NIM.Add_Atom(name = "HC3", atom_type = Xponge.AtomType.types["opls_763"], x = 1., y = 1.13, z = 1.117)
NIM.Add_Atom(name = "NO", atom_type = Xponge.AtomType.types["opls_760"], x = 1.156, y = 1.199, z = 1.237)
NIM.Add_Atom(name = "ON1", atom_type = Xponge.AtomType.types["opls_761"], x = 1.177, y = 1.321, z = 1.234)
NIM.Add_Atom(name = "ON2", atom_type = Xponge.AtomType.types["opls_761"], x = 1.177, y = 1.135, z = 1.341)
#添加连接信息
NIM.Add_Connectivity(NIM.CT, NIM.HC1)
NIM.Add_Connectivity(NIM.CT, NIM.HC2)
NIM.Add_Connectivity(NIM.CT, NIM.HC3)
NIM.Add_Connectivity(NIM.CT, NIM.NO)
NIM.Add_Connectivity(NIM.ON1, NIM.NO)
NIM.Add_Connectivity(NIM.ON2, NIM.NO)
Save_PDB(NIM)
Save_SPONGE_Input(NIM)
代码中的"test_angle.txt"文件如下:
name b[degree] k[kJ/mol·rad^-2]
opls_763-opls_762-opls_763 107.800 276.144
opls_763-opls_762-opls_760 105.000 292.880
opls_762-opls_760-opls_761 117.500 669.440
opls_761-opls_760-opls_761 125.000 669.440
方案2 AMBER格式读取(parmdat和frcmod)
例如ff14SB和ff19SB的实现,先使用loadparmdat
和loadfrcmod
获得字符串,然后再使用各类型的New_From_String
即可
import Xponge
import Xponge.forcefield.AMBER
atoms, bonds, angles, propers, impropers, LJs = loadparmdat("parm19.dat")
Xponge.AtomType.New_From_String(atoms)
Xponge.forcefield.BASE.BOND.BondType.New_From_String(bonds)
Xponge.forcefield.BASE.ANGLE.AngleType.New_From_String(angles)
Xponge.forcefield.BASE.DIHEDRAL.ProperType.New_From_String(propers)
Xponge.forcefield.BASE.DIHEDRAL.ImproperType.New_From_String(impropers)
Xponge.forcefield.BASE.LJ.LJType.New_From_String(LJs)
atoms, bonds, angles, propers, impropers, LJs, cmap = loadfrcmod("ff19SB.frcmod")
Xponge.AtomType.New_From_String(atoms)
Xponge.forcefield.BASE.BOND.BondType.New_From_String(bonds)
Xponge.forcefield.BASE.ANGLE.AngleType.New_From_String(angles)
Xponge.forcefield.BASE.DIHEDRAL.ProperType.New_From_String(propers)
Xponge.forcefield.BASE.DIHEDRAL.ImproperType.New_From_String(impropers)
Xponge.forcefield.BASE.LJ.LJType.New_From_String(LJs)
from Xponge.forcefield.BASE import RCMAP
Xponge.forcefield.BASE.RCMAP.CMAP.Residue_Map.update(cmap)
方案3 GROMACS格式读取(forcefield.itp)
例如CHARMM27的蛋白质力场的实现,先使用loadffitp
获得参数字典,然后再使用各类型的New_From_String
或New_From_Dict
即可
import Xponge
import Xponge.forcefield.CHARMM27
output = loadffitp("forcefield.itp")
Xponge.AtomType.New_From_String(output["atomtypes"])
Xponge.forcefield.BASE.BOND.BondType.New_From_String(output["bonds"])
output["dihedrals"] += "X-X-X-X 0 0 1 0\n"
Xponge.forcefield.BASE.DIHEDRAL.ProperType.New_From_String(output["dihedrals"])
Xponge.forcefield.BASE.LJ.LJType.New_From_String(output["LJ"])
Xponge.forcefield.BASE.UREY_BRADLEY.UreyBradleyType.New_From_String(output["Urey-Bradley"])
Xponge.forcefield.BASE.IMPROPER.ImproperType.New_From_String(output["impropers"])
Xponge.forcefield.BASE.NB14_EXTRA.NB14Type.New_From_String(output["nb14_extra"])
Xponge.forcefield.BASE.NB14.NB14Type.New_From_String(output["nb14"])
Xponge.forcefield.BASE.ACMAP.CMAP.New_From_Dict(output["cmaps"])
loadffitp
目前功能还很不全,并不一定完全支持函数形式,对于不支持的形式待更新或给出错误提示
loadffitp
只能加载力场项的itp文件,也即不包含“[ moleculetype ]”的itp
loadffitp
中对于宏的处理不会去寻找gromacs的路径,也即文件中的#include “xxx.itp”
只会在当前目录下寻找
B2. 新力场形式
类型1 非键
非键可以看作本身是原子性质,以目前实现的静电和LJ作用为例
静电
本身实现于Xponge.forcefield.BASE.CHARGE中
import Xponge
#给原子类别添加性质
Xponge.AtomType.Add_Property({"charge":float})
#给电荷添加单位
#三个参数的意义是:性质"charge"的单位是"charge",程序内部单位是"SPONGE"
Xponge.AtomType.Set_Property_Unit("charge", "charge", "e")
#通过修饰器@Xponge.Molecule.Set_Save_SPONGE_Input使得Save_SPONGE_Input时调用该函数
#被调用的函数接受三个参数,分别是构建的分子、前缀名和保存的路径
@Xponge.Molecule.Set_Save_SPONGE_Input
def write_charge(self, prefix, dirname):
towrite = "%d\n"%(len(self.atoms)) #通过self.atoms获取所有原子
towrite += "\n".join(["%.6f"%(atom.charge * 18.2223) for atom in self.atoms]) #通过atom.charge可以调用上面Add_Property的性质
f = open(os.path.join(dirname, prefix + "_charge.txt"),"w")
f.write(towrite)
f.close()
LJ作用
本身实现于Xponge.forcefield.BASE.LJ中
import Xponge
#给原子类别添加性质
Xponge.AtomType.Add_Property({"LJtype":str})
#生成一个新的类型(Xponge.Type)的子类,参数分别是名字、性质
LJType = Xponge.Generate_New_Pairwise_Force_Type("LJ", {"epsilon": float, "rmin": float, "sigma":float, "A":float, "B":float})
#设置单位(性质、量纲、程序内部单位)
LJType.Set_Property_Unit("rmin", "distance", "A")
LJType.Set_Property_Unit("sigma", "distance", "A")
LJType.Set_Property_Unit("epsilon", "energy", "kcal/mol")
LJType.Set_Property_Unit("A", "energy·distance^6", "kcal/mol·A^6")
LJType.Set_Property_Unit("B", "energy·distance^12", "kcal/mol·A^12")
#通过修饰器Xponge.GlobalSetting.Add_Unit_Transfer_Function(LJType)使得LJType初始化进行单位转化时调用
@Xponge.GlobalSetting.Add_Unit_Transfer_Function(LJType)
def LJ_Unit_Transfer(self):
if self.A != None and self.B != None:
self.sigma = (self.A / self.B) ** (1/6)
self.epsilon = 0.25 * B * self.sigma ** (-6)
self.A = None
self.B = None
if self.sigma != None:
self.rmin = self.sigma * (4 ** (1/12) / 2)
self.sigma = None
#定义默认的组合规则供内部使用,自己定义的
def Lorentz_Berthelot_For_A(epsilon1, rmin1, epsilon2, rmin2):
return np.sqrt(epsilon1 * epsilon2) * ((rmin1 + rmin2) ** 12)
def Lorents_Berthelot_For_B(epsilon1, rmin1, epsilon2, rmin2):
return np.sqrt(epsilon1 * epsilon2) * 2 * ((rmin1 + rmin2) ** 6)
#通过修饰器@Xponge.Molecule.Set_Save_SPONGE_Input使得Save_SPONGE_Input时调用该函数
#被调用的函数接受三个参数,分别是构建的分子、前缀名和保存的路径
@Xponge.Molecule.Set_Save_SPONGE_Input
def write_LJ(self, prefix, dirname):
LJtypes = []
LJtypemap = {}
for atom in self.atoms:
if atom.LJtype not in LJtypemap.keys():
LJtypemap[atom.LJtype] = len(LJtypes)
LJtypes.append(atom.LJtype)
As = []
Bs = []
for i in range(len(LJtypes)):
LJ_i = LJType.types[LJtypes[i] + "-" + LJtypes[i]]
for j in range(len(LJtypes)):
LJ_j = LJType.types[LJtypes[j] + "-" + LJtypes[j]]
finded = False
findnames = [LJtypes[i] + "-" + LJtypes[j], LJtypes[j] + "-" + LJtypes[i]]
for findname in findnames:
if findname in LJType.types.keys():
finded = True
LJ_ij = LJType.types[findname]
As.append(LJType.combining_method_A(LJ_ij.epsilon, LJ_ij.rmin, LJ_ij.epsilon, LJ_ij.rmin))
Bs.append(LJType.combining_method_B(LJ_ij.epsilon, LJ_ij.rmin, LJ_ij.epsilon, LJ_ij.rmin))
break
if not finded:
As.append(LJType.combining_method_A(LJ_i.epsilon, LJ_i.rmin, LJ_j.epsilon, LJ_j.rmin))
Bs.append(LJType.combining_method_B(LJ_i.epsilon, LJ_i.rmin, LJ_j.epsilon, LJ_j.rmin))
checks = {}
count = 0
for i in range(len(LJtypes)):
check_string_A = ""
check_string_B = ""
for j in range(len(LJtypes)):
check_string_A += "%16.7e"%As[count] + " "
check_string_B += "%16.7e"%Bs[count] + " "
count += 1
checks[i] = check_string_A+check_string_B
same_type = { i: i for i in range(len(LJtypes))}
for i in range(len(LJtypes)-1, -1, -1):
for j in range(i+1, len(LJtypes)):
if checks[i] == checks[j]:
same_type[j] = i
real_LJtypes = []
real_As = []
real_Bs = []
tosub = 0
for i in range(len(LJtypes)):
if same_type[i] == i:
real_LJtypes.append(LJtypes[i])
same_type[i] -= tosub
else:
same_type[i] = same_type[same_type[i]]
tosub += 1
for i in range(len(real_LJtypes)):
LJ_i = LJType.types[real_LJtypes[i] + "-" + real_LJtypes[i]]
for j in range(i+1):
LJ_j = LJType.types[real_LJtypes[j] + "-" + real_LJtypes[j]]
finded = False
findnames = [real_LJtypes[i] + "-" + real_LJtypes[j], real_LJtypes[j] + "-" + real_LJtypes[i]]
for findname in findnames:
if findname in LJType.types.keys():
finded = True
LJ_ij = LJType.types[findname]
real_As.append(LJType.combining_method_A(LJ_ij.epsilon, LJ_ij.rmin, LJ_ij.epsilon, LJ_ij.rmin))
real_Bs.append(LJType.combining_method_B(LJ_ij.epsilon, LJ_ij.rmin, LJ_ij.epsilon, LJ_ij.rmin))
break
if not finded:
real_As.append(LJType.combining_method_A(LJ_i.epsilon, LJ_i.rmin, LJ_j.epsilon, LJ_j.rmin))
real_Bs.append(LJType.combining_method_B(LJ_i.epsilon, LJ_i.rmin, LJ_j.epsilon, LJ_j.rmin))
towrite = "%d %d\n\n"%(len(self.atoms), len(real_LJtypes))
count = 0
for i in range(len(real_LJtypes)):
for j in range(i+1):
towrite += "%16.7e"%real_As[count] + " "
count += 1
towrite +="\n"
towrite += "\n"
count = 0
for i in range(len(real_LJtypes)):
for j in range(i+1):
towrite += "%16.7e"%real_Bs[count] + " "
count += 1
towrite +="\n"
towrite += "\n"
towrite += "\n".join(["%d"%(same_type[LJtypemap[atom.LJtype]]) for atom in self.atoms])
f = open(os.path.join(dirname, prefix + "_LJ.txt"),"w")
f.write(towrite)
f.close()
类型2 成键
基于原子判断的(atom-specific)
最简单的例子是AMBER力场的简谐bond
import Xponge
#生成一个新的类型(Xponge.Type)的子类,参数分别是名字、成键关系、性质和是否是必须的
BondType = Xponge.Generate_New_Bonded_Force_Type("bond", "1-2", {"k":float, "b":float}, True)
#设置性质单位
BondType.Set_Property_Unit("k", "energy·distance^-2", "kcal/mol·A^-2")
BondType.Set_Property_Unit("b", "distance", "A")
#保存操作,通过self.bonded_forces["bond"]("bond"对应于上面的产生类型时输出的字符串)获取相关信息
#而对应的self.bonded_forces["bond"]的成员的属性中,atoms是成键的原子,k、b是上面自己定义的
#self.atom_index是一个字典,原子为key,原子序号为value
@Xponge.Molecule.Set_Save_SPONGE_Input
def write_bond(self, prefix, dirname):
bonds = []
for bond in self.bonded_forces["bond"]:
order = list(range(2))
if bond.k != 0:
if self.atom_index[bond.atoms[order[0]]] > self.atom_index[bond.atoms[order[-1]]]:
temp_order = order[::-1]
else:
temp_order = order
bonds.append("%d %d %f %f"%(self.atom_index[bond.atoms[temp_order[0]]]
, self.atom_index[bond.atoms[temp_order[1]]], bond.k, bond.b))
if (bonds):
towrite = "%d\n"%len(bonds)
bonds.sort(key = lambda x: list(map(int, x.split()[:2])))
towrite += "\n".join(bonds)
f = open(os.path.join(dirname, prefix + "_bond.txt"),"w")
f.write(towrite)
f.close()
复杂的则以AMBER力场的二面角为例
import Xponge
#AMBER力场的二面角包括恰当和非恰当两类
#恰当二面角参数分别是名字、成键关系、性质、是否是必须的和可重复的参数(恰当二面角部分参数可重复给定并叠加计算)
ProperType = Generate_New_Bonded_Force_Type("dihedral", "1-2-3-4", {"k":float, "phi0": float, "periodicity":int}, True, ["k", "phi0", "periodicity"])
ProperType.Set_Property_Unit("k", "energy", "kcal/mol")
ProperType.Set_Property_Unit("phi0", "angle", "rad")
ImproperType = Generate_New_Bonded_Force_Type("improper", "1-3-2-3", {"k":float, "phi0": float, "periodicity":int}, False)
#非恰当二面角是复杂的拓扑,需要手动指定
#该矩阵的意思是成键的第i个原子和第j个原子之间的关系,只有上三角的值有效,例如ImproperType.topology_matrix[1][2]指成键的第2个原子和第3个原子是1-2关系
ImproperType.topology_matrix = [[1, 3, 2, 3],
[1, 1, 2, 3],
[1, 1, 1, 2],
[1, 1, 1, 1]]
ImproperType.Set_Property_Unit("k", "energy", "kcal/mol")
ImproperType.Set_Property_Unit("phi0", "angle", "rad")
#确认相同的力。默认是正倒序,例如ProperType中的A-B-C-D和D-C-B-A是同一种力
#如果不是默认情况,需要手动设置,接受两个参数cls(自己的类型,此处即ImproperType)和atom_list(可能是一个"A-B-C-D"的字符串,也可能是[A, B, C, D]的列表)
#输出是一个列表,包含了所有的相同的atom_list
#例如,对于非恰当二面角,A-B-C-D中A、B、D可以随意互换位置
@ImproperType.Set_Same_Force_Function
def Improper_Same_Force(cls, atom_list):
temp = []
if type(atom_list) == str:
atom_list_temp = [ atom.strip() for atom in atom_list.split("-")]
center_atom = atom_list_temp.pop(2)
for atom_permutation in Xponge.permutations(atom_list_temp):
atom_permutation = list(atom_permutation)
atom_permutation.insert(2, center_atom)
temp.append("-".join(atom_permutation))
else:
atom_list_temp = [ atom for atom in atom_list]
center_atom = atom_list_temp.pop(2)
for atom_permutation in Xponge.permutations(atom_list_temp):
atom_permutation = list(atom_permutation)
atom_permutation.insert(2, center_atom)
temp.append(atom_permutation)
return temp
#输出
#对于self.bonded_forces["dihedral"]的元素,因为设置了可叠加的性质,叠加度属性由multiple_numbers获取,每个值由对应的性质+"s"获取
@Molecule.Set_Save_SPONGE_Input
def write_dihedral(self, prefix, dirname):
dihedrals = []
for dihedral in self.bonded_forces.get("dihedral", []):
order = list(range(4))
if self.atom_index[dihedral.atoms[order[0]]] > self.atom_index[dihedral.atoms[order[-1]]]:
temp_order = order[::-1]
else:
temp_order = order
for i in range(dihedral.multiple_numbers):
if dihedral.ks[i] != 0:
dihedrals.append("%d %d %d %d %d %f %f"%(self.atom_index[dihedral.atoms[temp_order[0]]]
, self.atom_index[dihedral.atoms[temp_order[1]]], self.atom_index[dihedral.atoms[temp_order[2]]]
, self.atom_index[dihedral.atoms[temp_order[3]]], dihedral.periodicitys[i], dihedral.ks[i], dihedral.phi0s[i]))
for dihedral in self.bonded_forces.get("improper", []):
order = list(range(4))
if dihedral.k != 0:
if self.atom_index[dihedral.atoms[order[0]]] > self.atom_index[dihedral.atoms[order[-1]]]:
temp_order = order[::-1]
else:
temp_order = order
dihedrals.append("%d %d %d %d %d %f %f"%(self.atom_index[dihedral.atoms[temp_order[0]]]
, self.atom_index[dihedral.atoms[temp_order[1]]], self.atom_index[dihedral.atoms[temp_order[2]]]
, self.atom_index[dihedral.atoms[temp_order[3]]], dihedral.periodicity, dihedral.k, dihedral.phi0))
if (dihedrals):
towrite = "%d\n"%len(dihedrals)
dihedrals.sort(key = lambda x: list(map(float, x.split())))
towrite += "\n".join(dihedrals)
f = open(os.path.join(dirname, prefix + "_dihedral.txt"),"w")
f.write(towrite)
f.close()
基于残基判断的(residue-specific)
以ff19SB力场中的cmap为例(RCMAP)
import Xponge
#仍然创建一个种类
CMAP = Xponge.Generate_New_Bonded_Force_Type("residue_specific_cmap", "1-2-3-4-5", {}, False)
#所有信息相当于都自己处理
CMAP.Residue_Map = {}
@Xponge.Molecule.Set_Save_SPONGE_Input
def write_cmap(self, prefix, dirname):
cmaps = []
resolutions = []
used_types = []
used_types_map = {}
atoms = []
for cmap in self.bonded_forces["residue_specific_cmap"]:
resname = cmap.atoms[2].residue.type.name
#通过判断2号原子的resname来决定信息
if resname in CMAP.Residue_Map.keys():
if CMAP.Residue_Map[resname]["count"] not in used_types_map.keys():
used_types_map[CMAP.Residue_Map[resname]["count"]] = len(used_types)
used_types.append(CMAP.Residue_Map[resname]["parameters"])
resolutions.append(str(CMAP.Residue_Map[resname]["resolution"]))
cmaps.append("%d %d %d %d %d %d"%(self.atom_index[cmap.atoms[0]], self.atom_index[cmap.atoms[1]],
self.atom_index[cmap.atoms[2]], self.atom_index[cmap.atoms[3]],
self.atom_index[cmap.atoms[4]], used_types_map[CMAP.Residue_Map[resname]["count"]]))
if (cmap):
towrite = "%d %d\n"%(len(cmaps), len(resolutions))
towrite += " ".join(resolutions) + "\n\n"
for i in range(len(used_types_map)):
resol = int(resolutions[i])
for j in range(resol):
for k in range(resol):
towrite += "%f "%used_types[i][j * 24 + k]
towrite += "\n"
towrite += "\n"
towrite += "\n".join(cmaps)
f = open(os.path.join(dirname, prefix + "_cmap.txt"),"w")
f.write(towrite)
f.close()
类型3 虚拟原子
虚拟原子是被当作一种成键作用对待的。目前添加新的虚拟原子需要修改Xponge.forcefield.BASE.VIRTUAL_ATOM
import Xponge
#创建成键相互作用,其中参数包含atomN,N从0到(依赖的原子数-1),作为判断依赖的原子
VirtualType2 = Xponge.Generate_New_Bonded_Force_Type("vatom2", "1", {"atom0":int, "atom1":int, "atom2":int, "k1":float, "k2":float}, False)
#在Xponge.GlobalSetting.VirtualAtomTypes注册:key是名字,value是依赖的原子数
Xponge.GlobalSetting.VirtualAtomTypes["vatom2"] = 3
@GlobalSetting.Molecule.Set_Save_SPONGE_Input
def write_virtual_atoms(self, prefix, dirname):
vatoms = []
for vatom in self.bonded_forces["vatom2"]:
vatoms.append("2 %d %d %d %d %f %f"%(self.atom_index[vatom.atoms[0]],
self.atom_index[vatom.atoms[0]] + vatom.atom0,
self.atom_index[vatom.atoms[0]] + vatom.atom1,
self.atom_index[vatom.atoms[0]] + vatom.atom2,
vatom.k1, vatom.k2))
if (vatoms):
towrite = ""
towrite += "\n".join(vatoms)
f = open(os.path.join(dirname, prefix + "_vatom.txt"),"w")
f.write(towrite)
f.close()
B3. 新的原子类型推断
from Xponge import assign
#注册新的推测规则
GAFF = assign.Judge_Rule("GAFF")
#通过GAFF.Add_Judge_Rule(原子种类)进行判断
#程序会按添加顺序从上到下依次判断,直到判断True为止,返回此时的原子种类
#判断函数接受两个参数:需要判断的原子的编号i和判断任务Assign
#Assign.Atom_Judge(i, "O1"): 判断i号原子是不是氧元素且只有一根键
@GAFF.Add_Judge_Rule("o")
def temp(i, Assign):
return Assign.Atom_Judge(i, "O1")
#Assign.Atom_Judge(i, "O2"): 判断i号原子是不是氧元素且只有两根键
#"RG3" in Assign.atom_marker[i].keys():判断"RG3"有没有在i号原子的标志里,也即是不是在三元环上
#标志由Assign.Determine_Ring_And_Bond_Type()产生,key是标志,value是标志的数量
@GAFF.Add_Judge_Rule("op")
def temp(i, Assign):
return Assign.Atom_Judge(i, "O2") and "RG3" in Assign.atom_marker[i].keys()
@GAFF.Add_Judge_Rule("oq")
def temp(i, Assign):
return Assign.Atom_Judge(i, "O2") and "RG4" in Assign.atom_marker[i].keys()
#更复杂的,寻找连接的原子是否是氢
#Assign.bonds[i]是一个字典,key是连接原子的编号,value是成键的键级
@GAFF.Add_Judge_Rule("oh")
def temp(i, Assign):
tofind = False
if Assign.Atom_Judge(i, "O2") or Assign.Atom_Judge(i, "O3"):
for bonded_atom in Assign.bonds[i].keys():
if Assign.Atom_Judge(bonded_atom, "H1"):
tofind = True
break
return tofind
@GAFF.Add_Judge_Rule("os")
def temp(i, Assign):
return Assign.Atom_Judge(i, "O2")
定义好后,即可对Assign使用Determine_Atom_Type("GAFF")
进行原子种类推断
B4. 新的力场组合
新的力场组合除了加载新的力场形式和力场参数外,还需要设置一些"描述性"的力场设置,例如 AMBER力场中,默认的LJ组合规则、14作用设置、排除到4号连接原子
from Xponge import *
from Xponge.forcefield.BASE import CHARGE, MASS, LJ, BOND, ANGLE, DIHEDRAL, NB14, VIRTUAL_ATOM, EXCLUDE
import os
AMBER_DATA_DIR = os.path.dirname(__file__)
LJ.LJType.combining_method_A = LJ.Lorentz_Berthelot_For_A
LJ.LJType.combining_method_B = LJ.Lorents_Berthelot_For_B
NB14.NB14Type.New_From_String(r"""
name kLJ kee
X-X 0.5 0.833333
""")
EXCLUDE.Exclude(4)
CHARMM27力场中,默认的LJ组合规则、二面角设置、不需要的力场判断、排除到4号连接原子
from Xponge import *
from Xponge.forcefield.BASE import CHARGE, MASS, LJ, BOND, DIHEDRAL, NB14, NB14_EXTRA, UREY_BRADLEY, IMPROPER, VIRTUAL_ATOM, ACMAP, EXCLUDE
import OS
CHARMM27_DATA_DIR = os.path.dirname(__file__)
LJ.LJType.combining_method_A = LJ.Lorentz_Berthelot_For_A
LJ.LJType.combining_method_B = LJ.Lorents_Berthelot_For_B
GlobalSetting.Set_Invisible_Bonded_Forces(["improper"])
DIHEDRAL.ProperType.New_From_String(r"""
name k reset phi0 periodicity
X-X-X-X 0 0 0 0
""")
EXCLUDE.Exclude(4)
结构处理
A. 内坐标更改
A1. 更改键长
Impose_Bond
可以更改键长
import Xponge
import Xponge.forcefield.AMBER.ff14SB
t = ACE + NME
Save_Mol2(t, "imposing.mol2")
Impose_Bond(t, t.residues[0].C, t.residues[1].N, 5)
Save_Mol2(t, "imposed.mol2")
#不同残基之间可以impose_bond任意两个原子,按残基分别移动
Impose_Bond(t, t.residues[0].C, t.residues[1].CH3, 5)
Save_Mol2(t, "imposed2.mol2")
#同一个残基内没有键连的原子、成环互有叠加的不能impose_bond,因为不知道怎么移动
#Impose_Bond(t, t.residues[0].C, t.residues[0].H2, 5)
#AssertionError
上述产生的mol2文件在vmd中观察
A2. 更改键角
Impose_Angle
可以更改键角
import Xponge
import Xponge.forcefield.AMBER.ff14SB
t = ACE + NME
#不同残基之间可以impose_bond任意两个原子,按残基分别移动
#Impose_Angle要求第2、3个原子之间符合Impose_Bond的要求
Impose_Angle(t, t.residues[0].C, t.residues[1].N, t.residues[1].CH3, 3.1415926 / 2)
Save_Mol2(t, "imposed.mol2")
产生的mol2文件在vmd中观察
A3. 更改二面角
Imporse_Dihedral
可以更改二面角
import Xponge
import Xponge.forcefield.AMBER.ff14SB
t = ACE + ALA * 10 + NME
Save_Mol2(t, "imposing.mol2")
#Impose_Dihedral要求第2、3个原子之间符合Impose_Bond的要求
for i in range(1,len(t.residues)-1):
head = t.residues[i-1]
res = t.residues[i]
tail = t.residues[i+1]
Impose_Dihedral(t, head.C, res.N, res.CA, res.C, -3.1415926/3)
Impose_Dihedral(t, res.N, res.CA, res.C, tail.N, -3.1415926/3)
Save_Mol2(t, "imposed.mol2")
改变前的mol2文件在vmd中观察(Draw Method分别使用Line、Ribbon)
改变后的mol2文件在vmd中观察(Draw Method分别使用Line、Ribbon)
B. 溶剂与离子添加
import Xponge
import Xponge.forcefield.AMBER.ff14SB
import Xponge.forcefield.AMBER.tip3p
t = NALA + ARG + NME
c = round(t.charge)
#添加溶剂盒子,距离边界x<0的5埃,y<0的10埃,z<15的埃,x>0的30埃,y>0的25埃,z>0的20埃
Process_Box(t, WAT, [5,10,15,30,25,20])
#也可以简单的提供一个数
#Process_Box(t, WAT, 30)
#替代,将残基名为"WAT"的部分替代为钾离子和氯原子
Ion_Replace(t, lambda res: res.type.name == "WAT", {CL:30 + c, K:30})
#重新排序
#非必要,只为好看
t.residues.sort(key = lambda residue: {"CL":2, "K":1, "WAT":3}.get(residue.type.name, 0))
#打印出电荷,确保没错
print(t.charge)
Save_PDB(t, "test.pdb")
Save_SPONGE_Input(t, "test")
C. 重复结构产生
暂未实现
D. 主轴旋转
import Xponge
import Xponge.forcefield.AMBER.ff14SB
t = ACE + ALA * 100 + NME
Save_Mol2(t,"before.mol2")
Molecule_Rotate(t)
Save_Mol2(t,"after.mol2")
旋转前图像(从z轴看过去)
旋转后图像(从z轴看过去)
旋转后图像稍旋转观察角度
E. 质量重分配
import Xponge
import Xponge.forcefield.AMBER.ff14SB
t = ACE + ALA + NME
import Xponge.forcefield.AMBER.tip3p
Process_Box(t, WAT, [5,10,15,30,25,20])
HMass_Repartition(t)
Save_SPONGE_Input(t, "test")
F. 残基突变
暂未实现
G. 结构堆叠
暂未实现
后处理
A. 轨迹分析
调用MDAnalysis库进行计算,需自行安装MDAnalysis
#从Xponge中import Universe来加载
from Xponge.analysis.MDAnalysis import Universe
u = Universe("test.pdb", "mdcrd.dat", "mdbox.txt")
#下面都是MDAnalysis的API
O = u.select_atoms("resname WAT and name O")
from MDAnalysis.analysis import rdf as RDF
rdf = RDF.InterRDF(O, O)
rdf.run()
import matplotlib.pyplot as plt
plt.plot(rdf.results.bins[1:], rdf.results.rdf[1:])
plt.show()
最终画出来的图像为
B. 格式转化
使用python -m Xponge -h
可以获得更多信息
B1. dat2nc
将SPONGE的.dat轨迹文件转化为AMBER的.nc文件,详情见python -m Xponge dat2nc -h
B2. gro2crd
将GROMACS的.gro坐标文件转化为SPONGE的_coordinate.txt文件,详情见python -m Xponge gro2crd -h
B3. nc2rst7
将AMBER的.nc重开文件转化为AMBER的可读重开文件,详情见python -m Xponge nc2rst7 -h
B4. maskgen
调用VMD,选择原子,生成对应的原子序号,详情见python -m Xponge maskgen -h
B5. exgen
输入SPONGE的bond-like, angle-like, dihedral-like和约束文件来获得排除文件,详情见python -m Xponge exgen -h
B6. dat1frame
从SPONGE的.dat文件中抽出一帧作为SPONGE的_coordinate.txt文件,详情见python -m Xponge dat1frame -h
C. FEP处理
暂未整合
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.