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