-
Notifications
You must be signed in to change notification settings - Fork 0
/
crawler.jl
276 lines (243 loc) · 8.18 KB
/
crawler.jl
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
include("integrators.jl")
include("IO.jl")
using ArgParse
using LinearAlgebra
using SpecialFunctions
#=
crawler.jl is a small educational MD engine built from scratch that can energy-minimize and simulate the dynamics
of generic polymers using periodic boundary conditions. It supports bond, angle, dihedral, coulomb, and VDW (LJ) forces.
It also supports bond length constraints with the SHAKE algorithm and Langevin dynamics (beta) with or without constraints
crawler.jl takes in and outputs xyz files for both structures and trajectories and requires a topology file (see below)
The goal is to learn more about MD and learn Julia.
>>> source files <<<
structs.jl: definitions of complex datatypes such as Topology and Energies
IO.jl: read/write-related stuff
integrators.jl: minimization, velocity Verlet, in the future also velocity Verlet with SHAKE
forces.jl: energies and gradients (forces)
ewald.jl: short- and long-range Coulomb energy and forces
constraints.jl: SHAKE algorithm for bond length constraints
util.jl: utility functions used throughout (periodic boundary conditions, etc.)
>>> Sources <<<<
- steepest descent minimization algorithm
https://en.wikipedia.org/wiki/Gradient_descent, Barzilai–Borwein method for choosing step size
- velocity Verlet algorithm, Langevin dynamics, matrix-SHAKE algorithm
Mark Tuckerman, Statistical Mechanics: Theory and Molecular Simulation
- angle and dihedral Cartesian derivatives
https://grigoryanlab.org/docs/dynamics_derivatives.pdf
https://salilab.org/modeller/9v6/manual/node436.html
Topology format (must be in this order!)
<atomTypes>
<end>
<masses>
1 atomType1
2 ...
<end>
<masses>
1 mass1
2 ...
<end>
<bonds>
atom1Idx atom2Idx length k
<end>
<angles>
atom1 atom2 atom3 angle k
<end>
<dihedrals>
atom1 atom2 atom3 atom4 angle k
<end>
<charges>
1 q1
2 ...
<end>
<vdw>
atom1 atom2 sigma epsilon
<end>
<box>
x y z
<end>
this program uses atomic units everywhere!
charge: elementary charges
length: bohr radii (bohr)
energy: Hartrees
mass: electron masses
time: h-bar / E_h
angles: degrees (in topology), radians (internally)
bond spring constants are in units of (Hartree / bohr^2), corresponding to N/m in SI
angle and dihedral spring constants are in units of (Hartree / (bohr.rad))
The force field expression is simple and based on harmonic potentials for most terms:
V(x) = bond + angle + dihedral + coulomb + lennard-jones (VDW), or
V(x) = Σ(1/2)*k*x^2 + Σ(1/2)*kAngle*(θ - θ0)^2 + Σ(1/2)*kDihedral*(α-α0)^2 + ΣΣ(qi*qj)/rij + Σ4ϵ((σ / r)^12 - (σ / r)^6)
=#
function main()
argTable = ArgParseSettings()
@add_arg_table argTable begin
"--topology", "-t"
help = "system topology file"
arg_type = String
required = true
"--coordinates", "-c"
help = "xyz file with initial coordinates"
arg_type = String
required = true
"--integrator", "-i"
help = "min or vv"
arg_type = String
required = true
"--deltat", "-d"
help = "timestep size"
arg_type = Float64
default = nothing
"--steps", "-s"
default= 0
help = "number of steps"
arg_type = Int64
"--trajectory", "-x"
help = "trajectory (xyz file)"
default = "traj.xyz"
arg_type = String
"--logfile", "-l"
help = "text file to log progress"
default = "log.csv"
arg_type = String
"--outfile", "-o"
default = "min.xyz"
help = "outfile for minimizations"
arg_type = String
"--constrainbonds", "--b"
help = "turn on SHAKE algorithm to constrain bond lengths"
action = :store_true
"--loginterval", "-r"
help = "how often to print to logfile (number of steps), default 1"
default = 1
arg_type = Int64
"--gamma", "-g"
default = 0.0
arg_type = Float64
help = "collision frequency (1 / time) for Langevin dynamics"
"--temperature", "-T"
default = 0.0
arg_type = Float64
help = "desired (starting) temperature"
end
args = parse_args(argTable)
topologyfile = args["topology"]
coords = args["coordinates"]
traj = args["trajectory"]
dt = args["deltat"]
integrator = args["integrator"]
loginterval = args["loginterval"]
logfile = args["logfile"]
constrainbonds = args["constrainbonds"]
steps = args["steps"]
outfile = args["outfile"]
temp = args["temperature"]
collisionFreq = args["gamma"]
kb = 3.167e-6 # atomic units
top = readTopology(topologyfile)
xyz = readCoordinates(coords)
if args["integrator"] == "min"
println("finding local energy minimum...")
minimizeEnergy!(top, xyz, steps, outfile)
elseif args["integrator"] == "vv"
println("setting velocities to temperature...")
vel = zeros((top.nAtoms, 3))
# https://physics.stackexchange.com/questions/159674/velocity-maxwell-boltzmann-distribution-for-dummies
for i in 1:size(vel, 1)
mi = top.masses[i]
# sample from Maxwell-Boltzmann distribution
vel[i, 1] = sqrt((kb * temp) / mi) * randn()
vel[i, 2] = sqrt((kb * temp) / mi) * randn()
vel[i, 3] = sqrt((kb * temp) / mi) * randn()
end
if dt == nothing
ks = top.bondKs
ms = top.masses
dt = (1 / (sqrt(maximum(ks) / minimum(ms)))) * 0.1
println("setting deltat to $(dt) based on bond spring constants and masses...")
end
initiateReport(traj, logfile)
if constrainbonds == true
println("running NVE dynamics with constrained bonds...")
# for now, supports only a single bond length for all bonds, trivial to extend later
bondLength = norm(xyz[top.bondIdx[1, :], :] .- xyz[top.bondIdx[2, :], :])
nBonds = size(top.bondIdx, 1)
λs = zeros(nBonds) # initial guess for Lagrange multipliers
for frameNumber=1:steps
if frameNumber % loginterval == 0
println("step $(frameNumber) of $(steps)")
report = true
else
report = false
end
λs = integrateVelocityVerletSHAKE!(top, xyz, vel, dt, frameNumber, report, traj, logfile, bondLength, λs)
end
else
println("running NVE dynamics...")
for frameNumber=1:steps
if frameNumber % loginterval == 0
println("step $(frameNumber) of $(steps)")
report = true
else
report = false
end
integrateVelocityVerlet!(top, xyz, vel, dt, frameNumber, report, traj, logfile)
end
end
elseif args["integrator"] == "langevin"
if args["gamma"] == 0.0
println("choose a non-zero γ (collision frequency) for Langevin dynamics!")
exit()
end
if args["steps"] == 0
println("set number of steps [--steps or -s]")
exit()
end
if dt == nothing
ks = top.bondKs
ms = top.masses
dt = (1 / (sqrt(maximum(ks) / minimum(ms)))) * 0.1
println("setting deltat to $(dt) based on bond spring constants and masses...")
end
println("setting velocities to temperature...")
vel = zeros((top.nAtoms, 3))
# https://physics.stackexchange.com/questions/159674/velocity-maxwell-boltzmann-distribution-for-dummies
for i in 1:size(vel, 1)
mi = top.masses[i]
# sample from Maxwell-Boltzmann distribution
vel[i, 1] = sqrt((kb * temp) / (2 * mi)) * randn()
vel[i, 2] = sqrt((kb * temp) / (2 * mi)) * randn()
vel[i, 3] = sqrt((kb * temp) / (2 * mi)) * randn()
end
initiateReport(traj, logfile)
if constrainbonds == true
println("running Langevin dynamics with constrained bonds...")
bondLength = norm(xyz[top.bondIdx[1, :], :] .- xyz[top.bondIdx[2, :], :])
nBonds = size(top.bondIdx, 1)
λs = zeros(nBonds) # initial guess for Lagrange multipliers
for frameNumber=1:steps
if frameNumber % loginterval == 0
println("step $(frameNumber) of $(steps)")
report = true
else
report = false
end
λs = integrateLangevinSHAKE!(top, xyz, vel, dt, temp, collisionFreq, frameNumber, report, traj, logfile, bondLength, λs)
end
else
println("running Langevin dynamics...")
for frameNumber=1:steps
if frameNumber % loginterval == 0
println("step $(frameNumber) of $(steps)")
report = true
else
report = false
end
integrateLangevin!(top, xyz, vel, dt, temp, collisionFreq, frameNumber, report, traj, logfile)
end
end
else
error("pick 'min' (steepest descent), 'vv' (velocity Verlet) integrator, or 'langevin' (Langevin dynamics (beta))")
end
println("Crawler has crawled. Have a nice day.")
end
main()