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 static ffx.numerics.math.DoubleMath.length2;
41 import static java.lang.String.format;
42 import static java.lang.System.arraycopy;
43 import static java.util.Arrays.fill;
44 import static org.apache.commons.math3.util.FastMath.pow;
45
46 import ffx.potential.bonded.Atom;
47 import ffx.potential.bonded.LambdaInterface;
48 import ffx.potential.parameters.AtomType;
49 import ffx.potential.parameters.ForceField;
50 import java.util.Arrays;
51 import java.util.Comparator;
52 import java.util.logging.Logger;
53 import org.apache.commons.configuration2.CompositeConfiguration;
54
55
56
57
58
59
60
61 public class CoordRestraint implements LambdaInterface {
62
63 private static final Logger logger = Logger.getLogger(CoordRestraint.class.getName());
64
65 private final Atom[] atoms;
66 private final double[][] initialCoordinates;
67
68 private final double forceConstant;
69
70 private final double[] a1 = new double[3];
71 private final double[] dx = new double[3];
72 private final double lambdaExp = 1.0;
73 private final double[] lambdaGradient;
74 private final String atomIndexRestraints;
75 private final int atom1Index;
76 private final int atom2Index;
77 private final int atom3Index;
78 private final String atomTypeRestraints;
79 private final AtomType atom1Type;
80 private final AtomType atom2Type;
81 private final AtomType atom3Type;
82 private int nAtoms = 0;
83 private double lambda = 1.0;
84 private double lambdaPow = pow(lambda, lambdaExp);
85 private double dLambdaPow = lambdaExp * pow(lambda, lambdaExp - 1.0);
86 private double d2LambdaPow = 0;
87 private double dEdL = 0.0;
88 private double d2EdL2 = 0.0;
89 private boolean lambdaTerm;
90 private boolean ignoreHydrogen = true;
91
92
93
94
95
96
97
98
99 public CoordRestraint(Atom[] atoms, ForceField forceField) {
100 this(atoms, forceField, forceField.getBoolean("RESTRAIN_WITH_LAMBDA", true));
101 }
102
103
104
105
106
107
108
109
110
111 public CoordRestraint(Atom[] atoms, ForceField forceField, boolean useLambda) {
112 this(atoms, forceField, useLambda, forceField.getDouble("RESTRAINT_K", 10.0));
113 }
114
115
116
117
118
119
120
121
122
123
124 public CoordRestraint(Atom[] atoms, ForceField forceField, boolean useLambda, double forceConst) {
125 this.atoms = atoms;
126 nAtoms = atoms.length;
127
128 if (useLambda) {
129 lambdaTerm = forceField.getBoolean("LAMBDATERM", false);
130 } else {
131 lambdaTerm = false;
132 }
133
134 if (lambdaTerm) {
135 lambdaGradient = new double[nAtoms * 3];
136 } else {
137 lambdaGradient = null;
138 this.lambda = 1.0;
139 lambdaPow = 1.0;
140 dLambdaPow = 0.0;
141 d2LambdaPow = 0.0;
142 }
143
144 forceConstant = forceConst;
145
146 logger.info(
147 format(
148 "\n Coordinate Restraint Atoms (k = %6.3f, lambdaTerm=%s):",
149 forceConstant * 2.0, lambdaTerm));
150
151 initialCoordinates = new double[3][nAtoms];
152 for (int i = 0; i < nAtoms; i++) {
153 Atom a = atoms[i];
154 initialCoordinates[0][i] = a.getX();
155 initialCoordinates[1][i] = a.getY();
156 initialCoordinates[2][i] = a.getZ();
157 }
158
159 CompositeConfiguration properties = forceField.getProperties();
160
161
162 atomIndexRestraints = properties.getString("pdbAtomRestraints");
163 if (atomIndexRestraints != null) {
164 String[] tokens = atomIndexRestraints.split(",");
165
166 atom1Index = Integer.parseInt(tokens[0]);
167 atom2Index = Integer.parseInt(tokens[1]);
168 atom3Index = Integer.parseInt(tokens[2]);
169 } else {
170 atom1Index = -1;
171 atom2Index = -1;
172 atom3Index = -1;
173 }
174
175
176
177
178
179 atomTypeRestraints = properties.getString("xyzAtomRestraints");
180 if (atomTypeRestraints != null) {
181 String[] tokens = atomTypeRestraints.split(",");
182
183 atom1Type = atoms[Integer.parseInt(tokens[0])].getAtomType();
184 atom2Type = atoms[Integer.parseInt(tokens[1])].getAtomType();
185 atom3Type = atoms[Integer.parseInt(tokens[2])].getAtomType();
186 } else {
187 atom1Type = null;
188 atom2Type = null;
189 atom3Type = null;
190 }
191
192 Arrays.stream(atoms).sorted(Comparator.comparingInt(Atom::getIndex)).forEach(Atom::print);
193 }
194
195
196
197
198
199
200 public Atom[] getAtoms() {
201 Atom[] retArray = new Atom[nAtoms];
202 arraycopy(atoms, 0, retArray, 0, nAtoms);
203 return retArray;
204 }
205
206
207
208
209
210
211
212 public double[][] getCoordinatePin(double[][] xyz) {
213 if (xyz == null) {
214 xyz = new double[nAtoms][3];
215 } else if (xyz.length != nAtoms) {
216 throw new IllegalArgumentException(" Incorrect number of atoms!");
217 }
218 for (int i = 0; i < nAtoms; i++) {
219 arraycopy(initialCoordinates[i], 0, xyz[i], 0, 3);
220 }
221 return xyz;
222 }
223
224
225
226
227
228
229 public double getForceConstant() {
230 return forceConstant;
231 }
232
233
234 @Override
235 public double getLambda() {
236 return lambda;
237 }
238
239
240 @Override
241 public void setLambda(double lambda) {
242 if (lambdaTerm) {
243 this.lambda = lambda;
244
245 double lambdaWindow = 1.0;
246 if (this.lambda <= lambdaWindow) {
247 double dldgl = 1.0 / lambdaWindow;
248 double l = dldgl * this.lambda;
249 double l2 = l * l;
250 double l3 = l2 * l;
251 double l4 = l2 * l2;
252 double l5 = l4 * l;
253 double c3 = 10.0;
254 double c4 = -15.0;
255 double c5 = 6.0;
256 double threeC3 = 3.0 * c3;
257 double sixC3 = 6.0 * c3;
258 double fourC4 = 4.0 * c4;
259 double twelveC4 = 12.0 * c4;
260 double fiveC5 = 5.0 * c5;
261 double twentyC5 = 20.0 * c5;
262 lambdaPow = c3 * l3 + c4 * l4 + c5 * l5;
263 dLambdaPow = (threeC3 * l2 + fourC4 * l3 + fiveC5 * l4) * dldgl;
264 d2LambdaPow = (sixC3 * l + twelveC4 * l2 + twentyC5 * l3) * dldgl * dldgl;
265 } else {
266 lambdaPow = 1.0;
267 dLambdaPow = 0.0;
268 d2LambdaPow = 0.0;
269 }
270 } else {
271 this.lambda = 1.0;
272 lambdaPow = 1.0;
273 dLambdaPow = 0.0;
274 d2LambdaPow = 0.0;
275 }
276 }
277
278
279
280
281
282
283 public int getNumAtoms() {
284 return nAtoms;
285 }
286
287
288
289
290
291
292
293 public double[][] getOriginalCoordinates() {
294 double[][] retArray = new double[nAtoms][3];
295 for (int i = 0; i < nAtoms; i++) {
296 for (int j = 0; j < 3; j++) {
297
298 retArray[i][j] = initialCoordinates[j][i];
299 }
300 }
301 return retArray;
302 }
303
304
305 @Override
306 public double getd2EdL2() {
307 if (lambdaTerm) {
308 return d2EdL2;
309 } else {
310 return 0.0;
311 }
312 }
313
314
315 @Override
316 public double getdEdL() {
317 if (lambdaTerm) {
318 return dEdL;
319 } else {
320 return 0.0;
321 }
322 }
323
324
325 @Override
326 public void getdEdXdL(double[] gradient) {
327 if (lambdaTerm) {
328 int n3 = nAtoms * 3;
329 for (int i = 0; i < n3; i++) {
330 gradient[i] += lambdaGradient[i];
331 }
332 }
333 }
334
335
336 public void resetCoordinatePin() {
337 double[] aixyz = new double[3];
338 for (int i = 0; i < nAtoms; i++) {
339 atoms[i].getXYZ(aixyz);
340 arraycopy(aixyz, 0, initialCoordinates[i], 0, 3);
341 }
342 }
343
344
345
346
347
348
349
350
351 public double residual(boolean gradient, boolean print) {
352
353 if (lambdaTerm) {
354 dEdL = 0.0;
355 d2EdL2 = 0.0;
356 fill(lambdaGradient, 0.0);
357 }
358
359 int atomIndex = 0;
360
361
362 int moleculeNumber = 1;
363 double residual = 0.0;
364 double fx2 = forceConstant * 2.0;
365 for (int i = 0; i < nAtoms; i++) {
366
367 Atom atom = atoms[i];
368 if (atomTypeRestraints != null) {
369 if (atom.getAtomType() == atom1Type
370 || atom.getAtomType() == atom2Type
371 || atom.getAtomType() == atom3Type) {
372 atom.getXYZ(a1);
373
374 dx[0] = a1[0] - initialCoordinates[0][i];
375 dx[1] = a1[1] - initialCoordinates[1][i];
376 dx[2] = a1[2] - initialCoordinates[2][i];
377 } else {
378 continue;
379 }
380 double r2 = length2(dx);
381 residual += r2;
382 if (gradient || lambdaTerm) {
383 final double dedx = dx[0] * fx2;
384 final double dedy = dx[1] * fx2;
385 final double dedz = dx[2] * fx2;
386 if (gradient) {
387 atom.addToXYZGradient(lambdaPow * dedx, lambdaPow * dedy, lambdaPow * dedz);
388 }
389 if (lambdaTerm) {
390 int j3 = i * 3;
391 lambdaGradient[j3] = dLambdaPow * dedx;
392 lambdaGradient[j3 + 1] = dLambdaPow * dedy;
393 lambdaGradient[j3 + 2] = dLambdaPow * dedz;
394 }
395 }
396 } else if (atomIndexRestraints != null) {
397 atomIndex += 1;
398
399 if (atom.getResidueNumber() != moleculeNumber) {
400 atomIndex = 1;
401 moleculeNumber = atom.getResidueNumber();
402 }
403 if (atomIndex == atom1Index || atomIndex == atom2Index || atomIndex == atom3Index) {
404 atom.getXYZ(a1);
405
406 dx[0] = a1[0] - initialCoordinates[0][i];
407 dx[1] = a1[1] - initialCoordinates[1][i];
408 dx[2] = a1[2] - initialCoordinates[2][i];
409 } else {
410 continue;
411 }
412 double r2 = length2(dx);
413 residual += r2;
414 if (gradient || lambdaTerm) {
415 final double dedx = dx[0] * fx2;
416 final double dedy = dx[1] * fx2;
417 final double dedz = dx[2] * fx2;
418 if (gradient) {
419 atom.addToXYZGradient(lambdaPow * dedx, lambdaPow * dedy, lambdaPow * dedz);
420 }
421 if (lambdaTerm) {
422 int j3 = i * 3;
423 lambdaGradient[j3] = dLambdaPow * dedx;
424 lambdaGradient[j3 + 1] = dLambdaPow * dedy;
425 lambdaGradient[j3 + 2] = dLambdaPow * dedz;
426 }
427 }
428 } else {
429
430 if (ignoreHydrogen && atom.isHydrogen()) {
431 continue;
432 }
433
434 atom.getXYZ(a1);
435
436 dx[0] = a1[0] - initialCoordinates[0][i];
437 dx[1] = a1[1] - initialCoordinates[1][i];
438 dx[2] = a1[2] - initialCoordinates[2][i];
439 double r2 = length2(dx);
440 residual += r2;
441 if (gradient || lambdaTerm) {
442 final double dedx = dx[0] * fx2;
443 final double dedy = dx[1] * fx2;
444 final double dedz = dx[2] * fx2;
445 if (gradient) {
446 atom.addToXYZGradient(lambdaPow * dedx, lambdaPow * dedy, lambdaPow * dedz);
447 }
448 if (lambdaTerm) {
449 int j3 = i * 3;
450 lambdaGradient[j3] = dLambdaPow * dedx;
451 lambdaGradient[j3 + 1] = dLambdaPow * dedy;
452 lambdaGradient[j3 + 2] = dLambdaPow * dedz;
453 }
454 }
455 }
456 }
457 if (lambdaTerm) {
458 dEdL = dLambdaPow * forceConstant * residual;
459 d2EdL2 = d2LambdaPow * forceConstant * residual;
460 }
461
462 return forceConstant * residual * lambdaPow;
463 }
464
465
466
467
468
469
470 public void setCoordinatePin(double[][] newInitialCoordinates) {
471 if (newInitialCoordinates.length != initialCoordinates.length) {
472 throw new IllegalArgumentException(" Incorrect number of atoms!");
473 }
474 for (int i = 0; i < 3; i++) {
475 arraycopy(
476 newInitialCoordinates[i], 0, initialCoordinates[i], 0, initialCoordinates[0].length);
477 }
478 }
479
480
481
482
483
484
485 public void setIgnoreHydrogen(boolean ignoreHydrogen) {
486 this.ignoreHydrogen = ignoreHydrogen;
487 }
488 }