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 package ffx.algorithms.dynamics.integrators;
39
40 import static ffx.utilities.Constants.KCAL_TO_GRAM_ANG2_PER_PS2;
41 import static ffx.utilities.Constants.kB;
42 import static java.lang.System.arraycopy;
43 import static java.util.Arrays.copyOf;
44 import static org.apache.commons.math3.util.FastMath.exp;
45 import static org.apache.commons.math3.util.FastMath.sqrt;
46
47 import ffx.potential.SystemState;
48 import ffx.numerics.Potential;
49 import ffx.potential.constraint.ShakeChargeConstraint;
50
51 import java.util.Arrays;
52 import java.util.Random;
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public class Stochastic extends Integrator {
67
68
69
70
71 private final double friction;
72
73
74
75 private final Random random;
76
77
78
79 private final double[] vFriction;
80
81
82
83 private final double[] vRandom;
84
85
86
87 private double inverseFriction;
88
89
90
91 private double fdt;
92
93
94
95 private double efdt;
96
97
98
99 private double temperature;
100
101
102
103
104
105
106
107 public Stochastic(double friction, SystemState state) {
108 super(state);
109 this.friction = friction;
110 if (friction >= 0) {
111 inverseFriction = 1.0 / friction;
112 } else {
113 inverseFriction = Double.POSITIVE_INFINITY;
114 }
115 int nVariables = state.getNumberOfVariables();
116 vFriction = new double[nVariables];
117 vRandom = new double[nVariables];
118 fdt = friction * dt;
119 efdt = exp(-fdt);
120 temperature = 298.15;
121 random = new Random();
122 }
123
124
125
126
127
128
129
130 @Override
131 public void postForce(double[] gradient) {
132 copyAccelerationToPrevious();
133 double[] a = state.a();
134 double[] v = state.v();
135 double[] mass = state.getMass();
136 for (int i = 0; i < state.getNumberOfVariables(); i++) {
137 double m = mass[i];
138 if (m > 0.0) {
139 a[i] = -KCAL_TO_GRAM_ANG2_PER_PS2 * gradient[i] / m;
140 v[i] += (0.5 * a[i] * vFriction[i] + vRandom[i]);
141 }
142 }
143 }
144
145
146
147
148
149
150
151 @Override
152 public void preForce(Potential potential) throws RuntimeException {
153 boolean useChargeConstraint = false;
154 if (!constraints.isEmpty()) {
155 useChargeConstraint = constraints.get(0) instanceof ShakeChargeConstraint;
156 }
157 ShakeChargeConstraint chargeConstraint = null;
158 boolean done = false;
159 int maxIter = 5000;
160 int iter = 0;
161 double[] mass = state.getMass();
162 double[] x = state.x();
163 double[] v = state.v();
164 double[] a = state.a();
165 double[] aConstrained = new double[a.length];
166 Arrays.fill(aConstrained, 0.0);
167 if (useChargeConstraint) {
168 chargeConstraint = (ShakeChargeConstraint) constraints.get(0);
169 }
170 while (!done && iter < maxIter) {
171 iter++;
172 for (int i = 0; i < state.getNumberOfVariables(); i++) {
173 double m = mass[i];
174 if (m <= 0.0) {
175 continue;
176 }
177 double pfric;
178 double afric;
179 double prand;
180 if (fdt <= 0.0) {
181
182 pfric = 1.0;
183 vFriction[i] = dt;
184 afric = 0.5 * dt * dt;
185 prand = 0.0;
186 vRandom[i] = 0.0;
187 } else {
188 double pterm;
189 double vterm;
190 double rho;
191 if (fdt >= 0.05) {
192
193 pfric = efdt;
194 vFriction[i] = (1.0 - efdt) * inverseFriction;
195 afric = (dt - vFriction[i]) * inverseFriction;
196 pterm = 2.0 * fdt - 3.0 + (4.0 - efdt) * efdt;
197 vterm = 1.0 - efdt * efdt;
198 rho = (1.0 - efdt) * (1.0 - efdt) / sqrt(pterm * vterm);
199 } else {
200
201 double fdt2 = fdt * fdt;
202 double fdt3 = fdt * fdt2;
203 double fdt4 = fdt * fdt3;
204 double fdt5 = fdt * fdt4;
205 double fdt6 = fdt * fdt5;
206 double fdt7 = fdt * fdt6;
207 double fdt8 = fdt * fdt7;
208 double fdt9 = fdt * fdt8;
209 afric =
210 (fdt2 / 2.0 - fdt3 / 6.0 + fdt4 / 24.0 - fdt5 / 120.0 + fdt6 / 720.0 - fdt7 / 5040.0
211 + fdt8 / 40320.0 - fdt9 / 362880.0) / (friction * friction);
212 vFriction[i] = dt - friction * afric;
213 pfric = 1.0 - friction * vFriction[i];
214 pterm =
215 2.0 * fdt3 / 3.0 - fdt4 / 2.0 + 7.0 * fdt5 / 30.0 - fdt6 / 12.0 + 31.0 * fdt7 / 1260.0
216 - fdt8 / 160.0 + 127.0 * fdt9 / 90720.0;
217 vterm = 2.0 * fdt - 2.0 * fdt2 + 4.0 * fdt3 / 3.0 - 2.0 * fdt4 / 3.0 + 4.0 * fdt5 / 15.0
218 - 4.0 * fdt6 / 45.0 + 8.0 * fdt7 / 315.0 - 2.0 * fdt8 / 315.0 + 4.0 * fdt9 / 2835.0;
219 rho = sqrt(3.0) * (0.5 - fdt / 16.0 - 17.0 * fdt2 / 1280.0 + 17.0 * fdt3 / 6144.0
220 + 40967.0 * fdt4 / 34406400.0 - 57203.0 * fdt5 / 275251200.0
221 - 1429487.0 * fdt6 / 13212057600.0 + 1877509.0 * fdt7 / 105696460800.0);
222 }
223
224 double ktm = kB * temperature / m;
225 double psig = sqrt(ktm * pterm) / friction;
226 double vsig = sqrt(ktm * vterm);
227 double rhoc = sqrt(1.0 - rho * rho);
228 double pnorm = random.nextGaussian();
229 double vnorm = random.nextGaussian();
230 prand = psig * pnorm;
231 vRandom[i] = vsig * (rho * pnorm + rhoc * vnorm);
232 }
233
234
235
236
237 if (iter == 1) {
238 x[i] += (v[i] * vFriction[i] + a[i] * afric + prand);
239 v[i] = v[i] * pfric + 0.5 * a[i] * vFriction[i];
240 } else {
241 x[i] += (aConstrained[i] * afric);
242 }
243 }
244 done = true;
245 if (useChargeConstraint) {
246 done = chargeConstraint.applyChargeConstraintToStep(x, aConstrained, mass, dt);
247 }
248 }
249 if (iter == maxIter) {
250 throw new RuntimeException("SHAKE -- Warning, Distance Constraints not Satisfied");
251 }
252 }
253
254
255
256
257
258
259 public void setRandomSeed(long seed) {
260 random.setSeed(seed);
261 }
262
263
264
265
266
267
268 public void setTemperature(double temperature) {
269 this.temperature = temperature;
270 }
271
272
273
274
275
276
277 @Override
278 public void setTimeStep(double dt) {
279 this.dt = dt;
280 fdt = friction * dt;
281 efdt = exp(-fdt);
282 if (friction >= 0) {
283 inverseFriction = 1.0 / friction;
284 } else {
285 inverseFriction = Double.POSITIVE_INFINITY;
286 }
287 }
288
289
290
291
292 @Override
293 public String toString() {
294 return "Stochastic";
295 }
296 }