forked from SSAGESLabs/PySAGES
-
Notifications
You must be signed in to change notification settings - Fork 0
/
alanine-dipeptide.py
167 lines (123 loc) · 5.28 KB
/
alanine-dipeptide.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/usr/bin/env python3
"""
Metadynamics simulation of Alanine Dipeptide in vacuum with OpenMM and PySAGES.
Example command to run the simulation `python3 alanine-dipeptide.py --time-steps 1000`
For other supported commandline parameters, check `python3 alanine-dipeptide.py --help`
"""
# %%
import argparse
import os
import sys
import time
import matplotlib.pyplot as plt
import numpy
import pysages
from pysages.approxfun import compute_mesh
from pysages.colvars import DihedralAngle
from pysages.methods import MetaDLogger, Metadynamics
from pysages.utils import try_import
openmm = try_import("openmm", "simtk.openmm")
unit = try_import("openmm.unit", "simtk.unit")
app = try_import("openmm.app", "simtk.openmm.app")
# %%
pi = numpy.pi
kB = unit.BOLTZMANN_CONSTANT_kB * unit.AVOGADRO_CONSTANT_NA
kB = kB.value_in_unit(unit.kilojoules_per_mole / unit.kelvin)
T = 298.15 * unit.kelvin
dt = 2.0 * unit.femtoseconds
adp_pdb = os.path.join(os.pardir, os.pardir, "inputs", "alanine-dipeptide", "adp-vacuum.pdb")
# %%
def generate_simulation(pdb_filename=adp_pdb, T=T, dt=dt):
pdb = app.PDBFile(pdb_filename)
ff = app.ForceField("amber99sb.xml")
cutoff_distance = 1.0 * unit.nanometer
topology = pdb.topology
system = ff.createSystem(
topology, constraints=app.HBonds, nonbondedMethod=app.PME, nonbondedCutoff=cutoff_distance
)
# Set dispersion correction use.
forces = {}
for i in range(system.getNumForces()):
force = system.getForce(i)
forces[force.__class__.__name__] = force
forces["NonbondedForce"].setUseDispersionCorrection(True)
forces["NonbondedForce"].setEwaldErrorTolerance(1.0e-5)
positions = pdb.getPositions(asNumpy=True)
integrator = openmm.LangevinIntegrator(T, 1 / unit.picosecond, dt)
integrator.setRandomNumberSeed(42)
# platform = openmm.Platform.getPlatformByName(platform)
# simulation = app.Simulation(topology, system, integrator, platform)
simulation = app.Simulation(topology, system, integrator)
simulation.context.setPositions(positions)
simulation.minimizeEnergy()
simulation.reporters.append(app.PDBReporter("output.pdb", 1000))
simulation.reporters.append(
app.StateDataReporter("log.dat", 1000, step=True, potentialEnergy=True, temperature=True)
)
return simulation
# %%
def get_args(argv):
available_args = [
("well-tempered", "w", bool, 0, "Whether to use well-tempered metadynamics"),
("use-grids", "g", bool, 0, "Whether to use grid acceleration"),
("log", "l", bool, 0, "Whether to use a callback to log data into a file"),
("time-steps", "t", int, 5e5, "Number of simulation steps"),
]
parser = argparse.ArgumentParser(description="Example script to run metadynamics")
for (name, short, T, val, doc) in available_args:
parser.add_argument("--" + name, "-" + short, type=T, default=T(val), help=doc)
return parser.parse_args(argv)
# %%
def main(argv=[]):
args = get_args(argv)
cvs = [DihedralAngle([4, 6, 8, 14]), DihedralAngle([6, 8, 14, 16])]
height = 1.2 # kJ/mol
sigma = [0.35, 0.35] # radians
deltaT = 5000 if args.well_tempered else None
stride = 500 # frequency for depositing gaussians
timesteps = args.time_steps
ngauss = timesteps // stride + 1 # total number of gaussians
# Grid for storing bias potential and its gradient
grid = pysages.Grid(lower=(-pi, -pi), upper=(pi, pi), shape=(50, 50), periodic=True)
grid = grid if args.use_grids else None
# Method
method = Metadynamics(cvs, height, sigma, stride, ngauss, deltaT=deltaT, kB=kB, grid=grid)
# Logging
hills_file = "hills.dat"
callback = MetaDLogger(hills_file, stride) if args.log else None
tic = time.perf_counter()
run_result = pysages.run(method, generate_simulation, timesteps, callback)
toc = time.perf_counter()
print(f"Completed the simulation in {toc - tic:0.4f} seconds.")
# Analysis: Calculate free energy using the deposited bias potential
# generate CV values on a grid to evaluate bias potential
plot_grid = pysages.Grid(lower=(-pi, -pi), upper=(pi, pi), shape=(64, 64), periodic=True)
xi = (compute_mesh(plot_grid) + 1) / 2 * plot_grid.size + plot_grid.lower
# determine bias factor depending on method (for standard = 1
# and for well-tempered = (T+deltaT)/deltaT)
alpha = (
1
if method.deltaT is None
else (T.value_in_unit(unit.kelvin) + method.deltaT) / method.deltaT
)
kT = kB * T.value_in_unit(unit.kelvin)
# extract metapotential function from result
result = pysages.analyze(run_result)
metapotential = result["metapotential"]
# report in kT and set min free energy to zero
A = metapotential(xi) * -alpha / kT
A = A - A.min()
A = A.reshape(plot_grid.shape)
# plot and save free energy to a PNG file
fig, ax = plt.subplots(dpi=120)
im = ax.imshow(A, interpolation="bicubic", origin="lower", extent=[-pi, pi, -pi, pi])
ax.contour(A, levels=12, linewidths=0.75, colors="k", extent=[-pi, pi, -pi, pi])
ax.set_xlabel(r"$\phi$")
ax.set_ylabel(r"$\psi$")
cbar = plt.colorbar(im)
cbar.ax.set_ylabel(r"$A~[k_{B}T]$", rotation=270, labelpad=20)
fig.savefig("adp-fe.png", dpi=fig.dpi)
return result
# %%
if __name__ == "__main__":
main(sys.argv[1:])