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.potential.nonbonded;
39
40 import ffx.numerics.atomic.AtomicDoubleArray3D;
41 import ffx.potential.MolecularAssembly;
42 import ffx.potential.bonded.Atom;
43 import ffx.potential.bonded.BondedTerm;
44 import ffx.potential.bonded.LambdaInterface;
45 import ffx.potential.parameters.ForceField;
46 import org.apache.commons.configuration2.CompositeConfiguration;
47
48 import java.util.ArrayList;
49 import java.util.List;
50 import java.util.logging.Level;
51 import java.util.logging.Logger;
52
53 import static ffx.numerics.math.DoubleMath.length;
54 import static java.lang.Double.parseDouble;
55 import static java.lang.Integer.parseInt;
56 import static java.lang.String.format;
57 import static java.lang.System.arraycopy;
58 import static org.apache.commons.math3.util.FastMath.pow;
59
60
61
62
63
64
65
66 public class RestrainPosition extends BondedTerm implements LambdaInterface {
67
68 private static final Logger logger = Logger.getLogger(RestrainPosition.class.getName());
69
70 private final double[][] equilibriumCoordinates;
71
72
73
74
75 private final double forceConstant;
76
77
78
79
80 private final double flatBottom;
81
82 private final double[] a1 = new double[3];
83 private final double[] dx = new double[3];
84 private final double lambdaExp = 1.0;
85 private final double[] lambdaGradient;
86 private final int nAtoms;
87 private final boolean lambdaTerm;
88 private double lambda = 1.0;
89 private double lambdaPow = pow(lambda, lambdaExp);
90 private double dLambdaPow = lambdaExp * pow(lambda, lambdaExp - 1.0);
91 private double d2LambdaPow = 0;
92 private double dEdL = 0.0;
93 private double d2EdL2 = 0.0;
94
95
96
97
98
99
100
101
102
103
104 public RestrainPosition(Atom[] atoms, double[][] equilibriumCoordinates,
105 double forceConst, double flatBottom, boolean lambdaTerm) {
106 this.atoms = atoms;
107 this.equilibriumCoordinates = equilibriumCoordinates;
108 this.forceConstant = forceConst;
109 this.flatBottom = flatBottom;
110 nAtoms = atoms.length;
111 this.lambdaTerm = lambdaTerm;
112 if (lambdaTerm) {
113 lambdaGradient = new double[nAtoms * 3];
114 } else {
115 lambdaGradient = null;
116 lambda = 1.0;
117 lambdaPow = 1.0;
118 dLambdaPow = 0.0;
119 d2LambdaPow = 0.0;
120 }
121
122 if (logger.isLoggable(Level.FINE)) {
123 logger.info(" RestrainPosition: " + lambdaTerm);
124 for (Atom atom : atoms) {
125 logger.fine(atom.toString());
126 }
127 }
128 }
129
130
131
132
133
134
135 public Atom[] getAtoms() {
136 Atom[] retArray = new Atom[nAtoms];
137 arraycopy(atoms, 0, retArray, 0, nAtoms);
138 return retArray;
139 }
140
141
142
143
144
145
146 public double getForceConstant() {
147 return forceConstant;
148 }
149
150
151
152
153 @Override
154 public double getLambda() {
155 return lambda;
156 }
157
158
159
160
161 @Override
162 public void setLambda(double lambda) {
163 if (lambdaTerm) {
164 this.lambda = lambda;
165 double lambdaWindow = 1.0;
166 if (this.lambda <= lambdaWindow) {
167 double dldgl = 1.0 / lambdaWindow;
168 double l = dldgl * this.lambda;
169 double l2 = l * l;
170 double l3 = l2 * l;
171 double l4 = l2 * l2;
172 double l5 = l4 * l;
173 double c3 = 10.0;
174 double c4 = -15.0;
175 double c5 = 6.0;
176 double threeC3 = 3.0 * c3;
177 double sixC3 = 6.0 * c3;
178 double fourC4 = 4.0 * c4;
179 double twelveC4 = 12.0 * c4;
180 double fiveC5 = 5.0 * c5;
181 double twentyC5 = 20.0 * c5;
182 lambdaPow = c3 * l3 + c4 * l4 + c5 * l5;
183 dLambdaPow = (threeC3 * l2 + fourC4 * l3 + fiveC5 * l4) * dldgl;
184 d2LambdaPow = (sixC3 * l + twelveC4 * l2 + twentyC5 * l3) * dldgl * dldgl;
185 } else {
186 lambdaPow = 1.0;
187 dLambdaPow = 0.0;
188 d2LambdaPow = 0.0;
189 }
190 } else {
191 this.lambda = 1.0;
192 lambdaPow = 1.0;
193 dLambdaPow = 0.0;
194 d2LambdaPow = 0.0;
195 }
196 }
197
198
199
200
201
202
203 public int getNumAtoms() {
204 return nAtoms;
205 }
206
207
208
209
210
211
212
213 public double[][] getEquilibriumCoordinates() {
214 double[][] equilibriumCoords = new double[nAtoms][3];
215 for (int i = 0; i < nAtoms; i++) {
216 for (int j = 0; j < 3; j++) {
217
218 equilibriumCoords[i][j] = equilibriumCoordinates[j][i];
219 }
220 }
221 return equilibriumCoords;
222 }
223
224
225
226
227 @Override
228 public double getd2EdL2() {
229 if (lambdaTerm) {
230 return d2EdL2;
231 } else {
232 return 0.0;
233 }
234 }
235
236
237
238
239 @Override
240 public double getdEdL() {
241 if (lambdaTerm) {
242 return dEdL;
243 } else {
244 return 0.0;
245 }
246 }
247
248
249
250
251 @Override
252 public void getdEdXdL(double[] gradient) {
253 if (lambdaTerm) {
254 int n3 = nAtoms * 3;
255 for (int i = 0; i < n3; i++) {
256 gradient[i] += lambdaGradient[i];
257 }
258 }
259 }
260
261
262
263
264
265
266
267
268 @Override
269 public double energy(boolean gradient, int threadID, AtomicDoubleArray3D grad, AtomicDoubleArray3D lambdaGrad) {
270 double e = 0.0;
271 dEdL = 0.0;
272 d2EdL2 = 0.0;
273 double fx2 = forceConstant * 2.0;
274 for (int i = 0; i < nAtoms; i++) {
275
276 Atom atom = atoms[i];
277
278 atom.getXYZ(a1);
279 dx[0] = a1[0] - equilibriumCoordinates[0][i];
280 dx[1] = a1[1] - equilibriumCoordinates[1][i];
281 dx[2] = a1[2] - equilibriumCoordinates[2][i];
282 double r = length(dx);
283 double dr = r;
284 if (flatBottom > 0.0) {
285 dr = Math.max(0.0, r - flatBottom);
286 }
287 value = dr;
288 double dr2 = dr * dr;
289 e += dr2;
290 if (gradient || lambdaTerm) {
291 double scale = fx2 * dr;
292 if (r > 0.0) {
293 scale /= r;
294 }
295 final double dedx = dx[0] * scale;
296 final double dedy = dx[1] * scale;
297 final double dedz = dx[2] * scale;
298 final int index = atom.getIndex() - 1;
299 if (gradient) {
300 grad.add(threadID, index, lambdaPow * dedx, lambdaPow * dedy, lambdaPow * dedz);
301 }
302 if (lambdaTerm) {
303 lambdaGrad.add(threadID, index, dLambdaPow * dedx, dLambdaPow * dedy, dLambdaPow * dedz);
304 }
305 }
306 }
307
308 if (lambdaTerm) {
309 dEdL = dLambdaPow * forceConstant * e;
310 d2EdL2 = d2LambdaPow * forceConstant * e;
311 }
312
313 energy = forceConstant * e * lambdaPow;
314
315
316
317 return energy;
318 }
319
320 public static RestrainPosition[] parseRestrainPositions(MolecularAssembly molecularAssembly) {
321 List<RestrainPosition> restrainPositionList = new ArrayList<>();
322 ForceField forceField = molecularAssembly.getForceField();
323 CompositeConfiguration properties = forceField.getProperties();
324 if (properties.containsKey("restrain-position")) {
325 Atom[] atoms = molecularAssembly.getAtomArray();
326 String[] lines = properties.getStringArray("restrain-position");
327 for (String line : lines) {
328 RestrainPosition restrain = parseRestrainPosition(line, atoms, false);
329 if (restrain != null) {
330 restrainPositionList.add(restrain);
331 }
332 }
333 }
334 if (properties.containsKey("restrain-position-lambda")) {
335 Atom[] atoms = molecularAssembly.getAtomArray();
336 String[] lines = properties.getStringArray("restrain-position-lambda");
337 for (String line : lines) {
338 RestrainPosition restrain = parseRestrainPosition(line, atoms, true);
339 if (restrain != null) {
340 restrainPositionList.add(restrain);
341 }
342 }
343 }
344
345 if (restrainPositionList.isEmpty()) {
346 return null;
347 }
348
349 return restrainPositionList.toArray(new RestrainPosition[0]);
350 }
351
352
353
354
355
356
357
358
359
360 public static RestrainPosition parseRestrainPosition(String line, Atom[] atoms, boolean useLambda) {
361 String[] tokens = line.split("\\s+");
362 if (tokens.length < 1) {
363 throw new IllegalArgumentException("Invalid restraint line: " + line);
364 }
365
366 int nAtoms = atoms.length;
367 Atom[] atomArray;
368 double[][] coordinates;
369 double forceConstant = 50.0;
370 double flatBottom = 0.0;
371
372 int start = parseInt(tokens[0]);
373 if (start < 0) {
374
375 start = -start - 1;
376 int end = parseInt(tokens[1]) - 1;
377 if (start >= nAtoms || end < start || end >= nAtoms) {
378 logger.severe(format(" Property restrain-position could not be parsed:\n %s.", line));
379 return null;
380 }
381 int n = end - start + 1;
382 atomArray = new Atom[n];
383 coordinates = new double[3][n];
384 for (int j = start, index = 0; j <= end; j++, index++) {
385 atomArray[index] = atoms[j];
386 coordinates[0][index] = atoms[j].getX();
387 coordinates[1][index] = atoms[j].getY();
388 coordinates[2][index] = atoms[j].getZ();
389 }
390 if (tokens.length > 2) {
391 forceConstant = parseDouble(tokens[2]);
392 }
393 if (tokens.length > 3) {
394 flatBottom = parseDouble(tokens[3]);
395 }
396 logger.fine(format(" Restrain-Position of Atoms %d to %d (K=%8.4f, D=%8.4f)",
397 start + 1, end + 1, forceConstant, flatBottom));
398 } else {
399
400 Atom atom = atoms[start - 1];
401 atomArray = new Atom[1];
402 atomArray[0] = atom;
403 double x = atom.getX();
404 double y = atom.getY();
405 double z = atom.getZ();
406 if (tokens.length > 1) {
407 x = parseDouble(tokens[1]);
408 }
409 if (tokens.length > 2) {
410 y = parseDouble(tokens[2]);
411 }
412 if (tokens.length > 3) {
413 z = parseDouble(tokens[3]);
414 }
415 if (tokens.length > 4) {
416 forceConstant = parseDouble(tokens[4]);
417 }
418 if (tokens.length > 5) {
419 flatBottom = parseDouble(tokens[5]);
420 }
421 logger.fine(format(
422 " Restrain-Position of %s to (%12.6f, %12.6f, %12.6f) with K=%8.4f and D=%8.4f",
423 atom, x, y, z, forceConstant, flatBottom));
424 coordinates = new double[3][1];
425 coordinates[0][0] = x;
426 coordinates[1][0] = y;
427 coordinates[2][0] = z;
428 }
429 return new RestrainPosition(atomArray, coordinates, forceConstant, flatBottom, useLambda);
430 }
431 }