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.mc;
39
40 import ffx.algorithms.dynamics.thermostats.Thermostat;
41 import ffx.algorithms.optimize.Minimize;
42 import ffx.potential.ForceFieldEnergy;
43 import ffx.potential.MolecularAssembly;
44 import ffx.potential.bonded.Atom;
45 import ffx.potential.utils.Loop;
46
47 import java.util.List;
48 import java.util.Random;
49 import java.util.concurrent.ThreadLocalRandom;
50 import java.util.logging.Logger;
51
52 import static ffx.utilities.Constants.R;
53 import static java.lang.String.format;
54 import static org.apache.commons.math3.util.FastMath.exp;
55 import static org.apache.commons.math3.util.FastMath.random;
56
57
58
59
60
61
62 public class MCLoop implements MonteCarloListener {
63
64 private static final Logger logger = Logger.getLogger(MCLoop.class.getName());
65
66 private final MolecularAssembly molecularAssembly;
67
68 private final Thermostat thermostat;
69
70
71
72 private final int firstResidue;
73
74
75
76 private final int endResidue;
77
78 private final Random random = new Random();
79
80 private final ForceFieldEnergy forceFieldEnergy;
81
82 final Loop loop;
83
84 private int stepCount = 0;
85
86 private final int mcStepFrequency;
87
88 private int numMovesAccepted;
89
90 private int iterations;
91
92 private Atom[] atoms;
93
94 private boolean skipAlgorithm = false;
95
96
97
98
99
100
101
102
103
104
105 MCLoop(MolecularAssembly molecularAssembly, int mcStepFrequency, Thermostat thermostat,
106 int firstResidue, int endResidue) {
107 numMovesAccepted = 0;
108
109 this.molecularAssembly = molecularAssembly;
110 this.atoms = molecularAssembly.getAtomArray();
111 this.forceFieldEnergy = molecularAssembly.getPotentialEnergy();
112 this.mcStepFrequency = (mcStepFrequency == 0) ? Integer.MAX_VALUE : mcStepFrequency;
113 this.thermostat = thermostat;
114
115 double systemReferenceEnergy = molecularAssembly.getPotentialEnergy().energy(false, true);
116 this.firstResidue = firstResidue;
117 this.endResidue = endResidue;
118 this.iterations = 1;
119
120 if ((endResidue - firstResidue) < 3) {
121 logger.info("MCLoop requires at least 3 residues. First and last residues are anchors.");
122 skipAlgorithm = true;
123 }
124 String sb =
125 " Running MCLoop:\n" + format(" mcStepFrequency: %4d\n", mcStepFrequency) + format(
126 " referenceEnergy: %7.2f\n", systemReferenceEnergy);
127 logger.info(sb);
128
129 loop = new Loop(molecularAssembly);
130 }
131
132
133
134
135
136
137 public double getAcceptanceRate() {
138
139 int numTries = stepCount / mcStepFrequency;
140 return (double) numMovesAccepted / numTries;
141 }
142
143
144
145
146
147
148 @Override
149 public boolean mcUpdate(double temperature) {
150
151 stepCount++;
152 if (skipAlgorithm) {
153 return false;
154 }
155
156 if ((stepCount % mcStepFrequency != 0)) {
157
158 return false;
159 }
160
161 atoms = molecularAssembly.getAtomArray();
162
163
164 int midResidue;
165 midResidue = ThreadLocalRandom.current().nextInt(firstResidue + 1, endResidue);
166
167 List<double[]> loopSolutions;
168 loopSolutions = loop.generateLoops(midResidue - 1, midResidue + 1);
169
170 for (int i = 1; i < iterations; i++) {
171
172 midResidue = ThreadLocalRandom.current().nextInt(firstResidue + 1, endResidue);
173
174 if (!loopSolutions.isEmpty()) {
175 List<double[]> tempLoops = loop.generateLoops(midResidue - 1, midResidue + 1,
176 loopSolutions.get(random.nextInt(loopSolutions.size())));
177 loopSolutions.addAll(tempLoops);
178 } else {
179 loopSolutions = loop.generateLoops(midResidue - 1, midResidue + 1);
180 }
181 }
182 int numLoopsFound = loopSolutions.size();
183
184 if (numLoopsFound <= 1) {
185 return false;
186 }
187
188
189 return tryLoopStep(loopSolutions);
190 }
191
192
193
194
195
196
197 public void setIterations(int iterations) {
198 this.iterations = iterations;
199 }
200
201
202
203
204
205
206
207 private boolean tryLoopStep(List<double[]> loopSolutions) {
208
209
210 double[] newCoords = loopSolutions.get(random.nextInt(loopSolutions.size()));
211
212 double[] oldCoords = storeActiveCoordinates();
213
214
215 Minimize minimize1 = new Minimize(null, forceFieldEnergy, null);
216 minimize1.minimize();
217 double originalLoopEnergy = forceFieldEnergy.energy(false, true);
218
219
220 performLoopMove(newCoords);
221 Minimize minimize2 = new Minimize(null, forceFieldEnergy, null);
222 minimize2.minimize();
223 double newLoopEnergy = forceFieldEnergy.energy(false, true);
224
225 double temperature = thermostat.getCurrentTemperature();
226 double kT = R * temperature;
227
228 double dE = Math.abs(originalLoopEnergy - newLoopEnergy);
229 if (newLoopEnergy < originalLoopEnergy) {
230 dE = -dE;
231 }
232
233 StringBuilder sb = new StringBuilder();
234 sb.append(" Assessing possible MC loop move step:\n");
235 sb.append(format(" original loop: %16.8f\n", originalLoopEnergy));
236 sb.append(format(" possible loop: %16.8f\n", newLoopEnergy));
237 sb.append(" -----\n");
238
239
240 if (dE < 0) {
241 sb.append(" Accepted!");
242 logger.info(sb.toString());
243 numMovesAccepted++;
244 return true;
245 }
246 double criterion = exp(-dE / kT);
247
248 double metropolis = random();
249 sb.append(format(" criterion: %9.4f\n", criterion));
250 sb.append(format(" rng: %9.4f\n", metropolis));
251 if ((metropolis < criterion)) {
252 sb.append(" Accepted!");
253 logger.info(sb.toString());
254 numMovesAccepted++;
255 return true;
256 }
257 sb.append(" Denied.");
258 logger.info(sb.toString());
259
260
261 performLoopMove(oldCoords);
262 return false;
263 }
264
265
266
267
268
269
270 private void performLoopMove(double[] newCoordinates) {
271 int index = 0;
272 for (Atom a : atoms) {
273 double x = newCoordinates[index++];
274 double y = newCoordinates[index++];
275 double z = newCoordinates[index++];
276 a.moveTo(x, y, z);
277 }
278 }
279
280 private double[] storeActiveCoordinates() {
281 double[] coords = new double[atoms.length * 3];
282 int index = 0;
283 for (Atom a : atoms) {
284 coords[index++] = a.getX();
285 coords[index++] = a.getY();
286 coords[index++] = a.getZ();
287 }
288 return coords;
289 }
290
291 }