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