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.thermostats;
39
40 import ffx.numerics.Constraint;
41 import ffx.numerics.Potential.VARIABLE_TYPE;
42 import ffx.potential.SystemState;
43
44 import java.util.ArrayList;
45 import java.util.List;
46 import java.util.Random;
47 import java.util.logging.Level;
48 import java.util.logging.Logger;
49
50 import static ffx.utilities.Constants.KCAL_TO_GRAM_ANG2_PER_PS2;
51 import static ffx.utilities.Constants.kB;
52 import static java.lang.String.format;
53 import static org.apache.commons.math3.util.FastMath.max;
54 import static org.apache.commons.math3.util.FastMath.sqrt;
55
56
57
58
59
60
61
62
63
64
65 public abstract class Thermostat {
66
67 private static final Logger logger = Logger.getLogger(Thermostat.class.getName());
68
69
70
71 private final double[] centerOfMass = new double[3];
72
73
74
75 private final double[] linearMomentum = new double[3];
76
77
78
79 private final double[] angularMomentum = new double[3];
80
81
82
83 private final int constrainedDoF;
84
85
86
87 protected ThermostatEnum name;
88
89
90
91 protected double kT;
92
93
94
95
96 protected int degreesOfFreedom;
97
98
99
100 protected final SystemState state;
101
102
103
104 protected VARIABLE_TYPE[] type;
105
106
107
108 protected Random random;
109
110
111
112 protected List<Constraint> constraints;
113
114
115
116 double targetTemperature;
117
118
119
120 private boolean removeCenterOfMassMotion;
121
122
123
124 private boolean quiet = false;
125
126
127
128
129
130
131
132
133 public Thermostat(SystemState state, VARIABLE_TYPE[] type, double targetTemperature) {
134 this(state, type, targetTemperature, new ArrayList<>());
135 }
136
137 public Thermostat(SystemState state, VARIABLE_TYPE[] type, double targetTemperature, List<Constraint> constraints) {
138 this.state = state;
139 this.type = type;
140 int n = state.getNumberOfVariables();
141 assert (n > 3);
142 assert (type.length == n);
143 random = new Random();
144 setTargetTemperature(targetTemperature);
145
146 this.constraints = new ArrayList<>(constraints);
147
148
149 double nConstrained = constraints.stream().mapToInt(Constraint::getNumDegreesFrozen).sum();
150
151 double[] mass = state.getMass();
152 int massConstrained = 0;
153 for (int i = 0; i < n; i++) {
154 if (mass[i] <= 0.0) {
155 massConstrained++;
156 }
157 }
158
159 if (massConstrained > 0 && nConstrained > 0) {
160 logger.severe("Mass-constraints with other constraints are not supported.");
161 }
162 constrainedDoF = (int) max(nConstrained, massConstrained);
163
164 removeCenterOfMassMotion = true;
165
166
167 degreesOfFreedom = n - 3 - constrainedDoF;
168
169
170 computeKineticEnergy();
171 }
172
173
174
175
176
177
178
179 public static ThermostatEnum parseThermostat(String str) {
180 try {
181 return ThermostatEnum.valueOf(str.toUpperCase());
182 } catch (Exception e) {
183 logger.info(format(" Could not parse %s as a thermostat; defaulting to Berendsen.", str));
184 return ThermostatEnum.BERENDSEN;
185 }
186 }
187
188
189
190
191
192
193
194 public void centerOfMassMotion(boolean remove, boolean print) {
195 double totalMass = 0.0;
196 for (int i = 0; i < 3; i++) {
197 centerOfMass[i] = 0.0;
198 linearMomentum[i] = 0.0;
199 angularMomentum[i] = 0.0;
200 }
201
202 int nVariables = state.getNumberOfVariables();
203 double[] x = state.x();
204 double[] v = state.v();
205 double[] mass = state.getMass();
206
207 int index = 0;
208 while (index < nVariables) {
209 double m = mass[index];
210 if (m <= 0.0 || type[index] == VARIABLE_TYPE.OTHER) {
211 index++;
212 continue;
213 }
214 assert (type[index] == VARIABLE_TYPE.X);
215 double xx = x[index];
216 double vx = v[index++];
217 assert (type[index] == VARIABLE_TYPE.Y);
218 double yy = x[index];
219 double vy = v[index++];
220 assert (type[index] == VARIABLE_TYPE.Z);
221 double zz = x[index];
222 double vz = v[index++];
223 totalMass += m;
224 centerOfMass[0] += xx * m;
225 centerOfMass[1] += yy * m;
226 centerOfMass[2] += zz * m;
227 linearMomentum[0] += vx * m;
228 linearMomentum[1] += vy * m;
229 linearMomentum[2] += vz * m;
230 angularMomentum[0] += (yy * vz - zz * vy) * m;
231 angularMomentum[1] += (zz * vx - xx * vz) * m;
232 angularMomentum[2] += (xx * vy - yy * vx) * m;
233 }
234
235 angularMomentum[0] -= (centerOfMass[1] * linearMomentum[2] - centerOfMass[2] * linearMomentum[1]) / totalMass;
236 angularMomentum[1] -= (centerOfMass[2] * linearMomentum[0] - centerOfMass[0] * linearMomentum[2]) / totalMass;
237 angularMomentum[2] -= (centerOfMass[0] * linearMomentum[1] - centerOfMass[1] * linearMomentum[0]) / totalMass;
238 centerOfMass[0] /= totalMass;
239 centerOfMass[1] /= totalMass;
240 centerOfMass[2] /= totalMass;
241 linearMomentum[0] /= totalMass;
242 linearMomentum[1] /= totalMass;
243 linearMomentum[2] /= totalMass;
244
245 if (print) {
246 String sb = format(
247 " Center of Mass (%12.3f,%12.3f,%12.3f)\n Linear Momentum (%12.3f,%12.3f,%12.3f)\n Angular Momentum (%12.3f,%12.3f,%12.3f)",
248 centerOfMass[0], centerOfMass[1], centerOfMass[2], linearMomentum[0], linearMomentum[1],
249 linearMomentum[2], angularMomentum[0], angularMomentum[1], angularMomentum[2]);
250 logger.info(sb);
251 }
252
253 if (remove) {
254 removeCenterOfMassMotion(print);
255 centerOfMassMotion(false, print);
256 }
257 }
258
259
260
261
262 public final void computeKineticEnergy() {
263 double e = 0.0;
264 double[] v = state.v();
265 double[] mass = state.getMass();
266 for (int i = 0; i < state.getNumberOfVariables(); i++) {
267 double m = mass[i];
268 if (m > 0.0) {
269 double velocity = v[i];
270 double v2 = velocity * velocity;
271 e += m * v2;
272 }
273 }
274 state.setTemperature(e / (kB * degreesOfFreedom));
275 e *= 0.5 / KCAL_TO_GRAM_ANG2_PER_PS2;
276 state.setKineticEnergy(e);
277 }
278
279
280
281
282
283
284 public abstract void halfStep(double dt);
285
286
287
288
289
290
291 public abstract void fullStep(double dt);
292
293
294
295
296
297
298
299
300 public double getCurrentTemperature() {
301 return state.getTemperature();
302 }
303
304
305
306
307
308
309 public int getDegreesOfFreedom() {
310 return degreesOfFreedom;
311 }
312
313
314
315
316
317
318
319
320 public double getKineticEnergy() {
321 return state.getKineticEnergy();
322 }
323
324
325
326
327
328
329 public boolean getRemoveCenterOfMassMotion() {
330 return removeCenterOfMassMotion;
331 }
332
333
334
335
336
337
338
339 public void setRemoveCenterOfMassMotion(boolean remove) {
340 removeCenterOfMassMotion = remove;
341 int nVariables = state.getNumberOfVariables();
342 if (removeCenterOfMassMotion) {
343 degreesOfFreedom = nVariables - 3 - constrainedDoF;
344 } else {
345 degreesOfFreedom = nVariables - constrainedDoF;
346 }
347 }
348
349
350
351
352
353
354 public double getTargetTemperature() {
355 return targetTemperature;
356 }
357
358
359
360
361
362
363
364 public void setTargetTemperature(double t) {
365
366 assert (t > 0.0);
367 targetTemperature = t;
368 kT = t * kB;
369 }
370
371
372
373
374
375
376
377 public void maxwell(double targetTemperature) {
378 if (logger.isLoggable(Level.FINE)) {
379 logger.fine("\n Initializing velocities to target temperature");
380 }
381
382 setTargetTemperature(targetTemperature);
383
384 double[] v = state.v();
385 double[] mass = state.getMass();
386 for (int i = 0; i < state.getNumberOfVariables(); i++) {
387 double m = mass[i];
388 if (m > 0.0) {
389 v[i] = random.nextGaussian() * sqrt(kB * targetTemperature / m);
390 }
391 }
392
393
394 if (removeCenterOfMassMotion) {
395 centerOfMassMotion(true, !quiet);
396 }
397
398
399 computeKineticEnergy();
400
401
402
403
404
405
406
407
408 double scale = sqrt(targetTemperature / state.getTemperature());
409 for (int i = 0; i < state.getNumberOfVariables(); i++) {
410 double m = mass[i];
411 if (m > 0.0) {
412 v[i] *= scale;
413 }
414 }
415
416
417 computeKineticEnergy();
418
419 log(Level.INFO);
420 }
421
422
423
424
425
426 public void maxwell() {
427 maxwell(targetTemperature);
428 }
429
430
431
432
433
434
435
436
437 public double[] maxwellIndividual(double mass) {
438 double[] vv = new double[3];
439 if (mass > 0.0) {
440 for (int i = 0; i < 3; i++) {
441 vv[i] = random.nextGaussian() * sqrt(kB * targetTemperature / mass);
442 }
443 }
444 return vv;
445 }
446
447
448
449
450
451
452 public void setQuiet(boolean quiet) {
453 this.quiet = quiet;
454 }
455
456
457
458
459
460
461
462 public void setRandomSeed(long seed) {
463 random.setSeed(seed);
464 }
465
466
467
468
469 @Override
470 public String toString() {
471 return logTemp();
472 }
473
474 public String logTemp() {
475 StringBuilder sb = new StringBuilder(
476 format(" Target temperature: %7.2f Kelvin\n", targetTemperature));
477 sb.append(format(" Current temperature: %7.2f Kelvin\n", state.getTemperature()));
478 sb.append(format(" Number of variables: %7d\n", state.getNumberOfVariables()));
479 sb.append(format(" Number of degrees of freedom: %7d\n", degreesOfFreedom));
480 sb.append(format(" Kinetic Energy: %7.2f\n", state.getKineticEnergy()));
481 sb.append(format(" kT per degree of freedom: %7.2f",
482 KCAL_TO_GRAM_ANG2_PER_PS2 * state.getKineticEnergy() / (degreesOfFreedom * kT)));
483 return sb.toString();
484 }
485
486
487
488
489
490
491
492 protected void log(Level level) {
493 if (logger.isLoggable(level) && !quiet) {
494 logger.log(level, logTemp());
495 }
496 }
497
498
499
500
501
502
503
504 private void removeCenterOfMassMotion(boolean print) {
505 double xx = 0.0;
506 double yy = 0.0;
507 double zz = 0.0;
508 double xy = 0.0;
509 double xz = 0.0;
510 double yz = 0.0;
511 int index = 0;
512 double[] x = state.x();
513 double[] mass = state.getMass();
514 while (index < state.getNumberOfVariables()) {
515 if (type[index] == VARIABLE_TYPE.OTHER) {
516 index++;
517 continue;
518 }
519 double m = mass[index];
520 assert (type[index] == VARIABLE_TYPE.X);
521 double xi = x[index++] - centerOfMass[0];
522 assert (type[index] == VARIABLE_TYPE.Y);
523 double yi = x[index++] - centerOfMass[1];
524 assert (type[index] == VARIABLE_TYPE.Z);
525 double zi = x[index++] - centerOfMass[2];
526 xx += xi * xi * m;
527 yy += yi * yi * m;
528 zz += zi * zi * m;
529 xy += xi * yi * m;
530 xz += xi * zi * m;
531 yz += yi * zi * m;
532 }
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559 index = 0;
560 double[] v = state.v();
561 while (index < state.getNumberOfVariables()) {
562 if (type[index] == VARIABLE_TYPE.OTHER) {
563 index++;
564 continue;
565 }
566 double m = mass[index];
567 if (m > 0.0) {
568 v[index++] -= linearMomentum[0] / m;
569 v[index++] -= linearMomentum[1] / m;
570 v[index++] -= linearMomentum[2] / m;
571 } else {
572 index += 3;
573 }
574 }
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594 if (print) {
595 logger.info(" Center of mass motion removed.");
596 }
597 }
598 }