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.pme;
39
40 import ffx.potential.parameters.ForceField;
41 import org.apache.commons.math3.analysis.DifferentiableMultivariateVectorFunction;
42 import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
43 import org.apache.commons.math3.optimization.general.LevenbergMarquardtOptimizer;
44
45 import java.util.logging.Level;
46 import java.util.logging.Logger;
47
48 import static ffx.numerics.math.ScalarMath.binomial;
49 import static java.lang.String.format;
50 import static java.util.Arrays.fill;
51
52 @SuppressWarnings("deprecation")
53 public class SCFPredictorParameters {
54
55 private static final Logger logger = Logger.getLogger(SCFPredictorParameters.class.getName());
56
57
58 public int predictorOrder;
59
60 public int predictorStartIndex;
61
62 public int predictorCount;
63
64 public double[][][][] predictorInducedDipole;
65
66 public double[][][][] predictorInducedDipoleCR;
67
68 private LeastSquaresPredictor leastSquaresPredictor;
69 public LevenbergMarquardtOptimizer leastSquaresOptimizer;
70
71 public final SCFPredictor scfPredictor;
72 private final int nAtoms;
73
74 public SCFPredictorParameters(SCFPredictor scfPredictor, int nAtoms) {
75 this.nAtoms = nAtoms;
76 this.scfPredictor = scfPredictor;
77 }
78
79
80 public void aspcPredictor(LambdaMode lambdaMode,
81 double[][][] inducedDipole, double[][][] inducedDipoleCR) {
82
83 if (predictorCount < 6) {
84 return;
85 }
86
87 int mode;
88 switch (lambdaMode) {
89 case CONDENSED_NO_LIGAND:
90 mode = 1;
91 break;
92 case VAPOR:
93 mode = 2;
94 break;
95 default:
96 mode = 0;
97 }
98
99 final double[] aspc = {
100 22.0 / 7.0, -55.0 / 14.0, 55.0 / 21.0, -22.0 / 21.0, 5.0 / 21.0, -1.0 / 42.0
101 };
102
103
104 int index = predictorStartIndex;
105
106
107 for (int k = 0; k < 6; k++) {
108
109
110 double c = aspc[k];
111 for (int i = 0; i < nAtoms; i++) {
112 for (int j = 0; j < 3; j++) {
113 inducedDipole[0][i][j] += c * predictorInducedDipole[mode][index][i][j];
114 inducedDipoleCR[0][i][j] += c * predictorInducedDipoleCR[mode][index][i][j];
115 }
116 }
117 index++;
118 if (index >= predictorOrder) {
119 index = 0;
120 }
121 }
122 }
123
124 public void init(ForceField forceField) {
125 predictorCount = 0;
126 int defaultOrder = 6;
127 predictorOrder = forceField.getInteger("SCF_PREDICTOR_ORDER", defaultOrder);
128 if (scfPredictor == SCFPredictor.LS) {
129 leastSquaresPredictor = new LeastSquaresPredictor();
130 double eps = 1.0e-4;
131 leastSquaresOptimizer =
132 new LevenbergMarquardtOptimizer(
133 new org.apache.commons.math3.optimization.SimpleVectorValueChecker(eps, eps));
134 } else if (scfPredictor == SCFPredictor.ASPC) {
135 predictorOrder = 6;
136 }
137 predictorStartIndex = 0;
138 }
139
140
141
142
143
144 public void leastSquaresPredictor(LambdaMode lambdaMode,
145 double[][][] inducedDipole, double[][][] inducedDipoleCR) {
146 if (predictorCount < 2) {
147 return;
148 }
149 try {
150
151
152
153
154
155 leastSquaresPredictor.lambdaMode = lambdaMode;
156 leastSquaresPredictor.updateJacobianAndTarget();
157 int maxEvals = 100;
158 fill(leastSquaresPredictor.initialSolution, 0.0);
159 leastSquaresPredictor.initialSolution[0] = 1.0;
160 org.apache.commons.math3.optimization.PointVectorValuePair optimum =
161 leastSquaresOptimizer.optimize(
162 maxEvals,
163 leastSquaresPredictor,
164 leastSquaresPredictor.calculateTarget(),
165 leastSquaresPredictor.weights,
166 leastSquaresPredictor.initialSolution);
167 double[] optimalValues = optimum.getPoint();
168 if (logger.isLoggable(Level.FINEST)) {
169 logger.finest(format("\n LS RMS: %10.6f", leastSquaresOptimizer.getRMS()));
170 logger.finest(format(" LS Iterations: %10d", leastSquaresOptimizer.getEvaluations()));
171 logger.finest(
172 format(" Jacobian Evals: %10d", leastSquaresOptimizer.getJacobianEvaluations()));
173 logger.finest(format(" Chi Square: %10.6f", leastSquaresOptimizer.getChiSquare()));
174 logger.finest(" LS Coefficients");
175 for (int i = 0; i < predictorOrder - 1; i++) {
176 logger.finest(format(" %2d %10.6f", i + 1, optimalValues[i]));
177 }
178 }
179
180 int mode;
181 switch (lambdaMode) {
182 case CONDENSED_NO_LIGAND:
183 mode = 1;
184 break;
185 case VAPOR:
186 mode = 2;
187 break;
188 default:
189 mode = 0;
190 }
191
192
193 int index = predictorStartIndex;
194
195
196
197 for (int k = 0; k < predictorOrder - 1; k++) {
198
199
200 double c = optimalValues[k];
201 for (int i = 0; i < nAtoms; i++) {
202 for (int j = 0; j < 3; j++) {
203 inducedDipole[0][i][j] += c * predictorInducedDipole[mode][index][i][j];
204 inducedDipoleCR[0][i][j] += c * predictorInducedDipoleCR[mode][index][i][j];
205 }
206 }
207 index++;
208 if (index >= predictorOrder) {
209 index = 0;
210 }
211 }
212 } catch (Exception e) {
213 logger.log(Level.WARNING, " Exception computing predictor coefficients", e);
214 }
215 }
216
217
218 public void polynomialPredictor(LambdaMode lambdaMode,
219 double[][][] inducedDipole, double[][][] inducedDipoleCR) {
220
221 if (predictorCount == 0) {
222 return;
223 }
224
225 int mode;
226 switch (lambdaMode) {
227 case CONDENSED_NO_LIGAND:
228 mode = 1;
229 break;
230 case VAPOR:
231 mode = 2;
232 break;
233 default:
234 mode = 0;
235 }
236
237
238 int n = predictorOrder;
239 if (predictorCount < predictorOrder) {
240 n = predictorCount;
241 }
242
243
244 int index = predictorStartIndex;
245
246
247 double sign = -1.0;
248
249
250 for (int k = 0; k < n; k++) {
251
252
253 sign *= -1.0;
254 double c = sign * binomial(n, k);
255 for (int i = 0; i < nAtoms; i++) {
256 for (int j = 0; j < 3; j++) {
257 inducedDipole[0][i][j] += c * predictorInducedDipole[mode][index][i][j];
258 inducedDipoleCR[0][i][j] += c * predictorInducedDipoleCR[mode][index][i][j];
259 }
260 }
261 index++;
262 if (index >= predictorOrder) {
263 index = 0;
264 }
265 }
266 }
267
268
269 public void saveMutualInducedDipoles(LambdaMode lambdaMode,
270 double[][][] inducedDipole, double[][][] inducedDipoleCR,
271 double[][] directDipole, double[][] directDipoleCR) {
272
273 int mode;
274 switch (lambdaMode) {
275 case CONDENSED_NO_LIGAND:
276 mode = 1;
277 break;
278 case VAPOR:
279 mode = 2;
280 break;
281 default:
282 mode = 0;
283 }
284
285
286 predictorStartIndex--;
287 if (predictorStartIndex < 0) {
288 predictorStartIndex = predictorOrder - 1;
289 }
290
291 if (predictorCount < predictorOrder) {
292 predictorCount++;
293 }
294
295 for (int i = 0; i < nAtoms; i++) {
296 for (int j = 0; j < 3; j++) {
297 predictorInducedDipole[mode][predictorStartIndex][i][j] =
298 inducedDipole[0][i][j] - directDipole[i][j];
299 predictorInducedDipoleCR[mode][predictorStartIndex][i][j] =
300 inducedDipoleCR[0][i][j] - directDipoleCR[i][j];
301 }
302 }
303 }
304
305 private class LeastSquaresPredictor implements DifferentiableMultivariateVectorFunction {
306
307 double[] weights;
308 double[] target;
309 double[] values;
310 double[][] jacobian;
311 double[] initialSolution;
312 private final MultivariateMatrixFunction multivariateMatrixFunction = this::jacobian;
313
314 public LambdaMode lambdaMode = null;
315
316 LeastSquaresPredictor() {
317 weights = new double[2 * nAtoms * 3];
318 target = new double[2 * nAtoms * 3];
319 values = new double[2 * nAtoms * 3];
320 jacobian = new double[2 * nAtoms * 3][predictorOrder - 1];
321 initialSolution = new double[predictorOrder - 1];
322 fill(weights, 1.0);
323 initialSolution[0] = 1.0;
324 }
325
326 @Override
327 public MultivariateMatrixFunction jacobian() {
328 return multivariateMatrixFunction;
329 }
330
331 @Override
332 public double[] value(double[] variables) {
333 int mode;
334 switch (lambdaMode) {
335 case CONDENSED_NO_LIGAND:
336 mode = 1;
337 break;
338 case VAPOR:
339 mode = 2;
340 break;
341 default:
342 mode = 0;
343 }
344
345 for (int i = 0; i < nAtoms; i++) {
346 int index = 6 * i;
347 values[index] = 0;
348 values[index + 1] = 0;
349 values[index + 2] = 0;
350 values[index + 3] = 0;
351 values[index + 4] = 0;
352 values[index + 5] = 0;
353 int pi = predictorStartIndex + 1;
354 if (pi >= predictorOrder) {
355 pi = 0;
356 }
357 for (int j = 0; j < predictorOrder - 1; j++) {
358 values[index] += variables[j] * predictorInducedDipole[mode][pi][i][0];
359 values[index + 1] += variables[j] * predictorInducedDipole[mode][pi][i][1];
360 values[index + 2] += variables[j] * predictorInducedDipole[mode][pi][i][2];
361 values[index + 3] += variables[j] * predictorInducedDipoleCR[mode][pi][i][0];
362 values[index + 4] += variables[j] * predictorInducedDipoleCR[mode][pi][i][1];
363 values[index + 5] += variables[j] * predictorInducedDipoleCR[mode][pi][i][2];
364 pi++;
365 if (pi >= predictorOrder) {
366 pi = 0;
367 }
368 }
369 }
370 return values;
371 }
372
373 double[] calculateTarget() {
374 return target;
375 }
376
377 void updateJacobianAndTarget() {
378 int mode;
379 switch (lambdaMode) {
380 case CONDENSED_NO_LIGAND:
381 mode = 1;
382 break;
383 case VAPOR:
384 mode = 2;
385 break;
386 default:
387 mode = 0;
388 }
389
390
391 int index = 0;
392 for (int i = 0; i < nAtoms; i++) {
393 target[index++] = predictorInducedDipole[mode][predictorStartIndex][i][0];
394 target[index++] = predictorInducedDipole[mode][predictorStartIndex][i][1];
395 target[index++] = predictorInducedDipole[mode][predictorStartIndex][i][2];
396 target[index++] = predictorInducedDipoleCR[mode][predictorStartIndex][i][0];
397 target[index++] = predictorInducedDipoleCR[mode][predictorStartIndex][i][1];
398 target[index++] = predictorInducedDipoleCR[mode][predictorStartIndex][i][2];
399 }
400
401
402 index = predictorStartIndex + 1;
403 if (index >= predictorOrder) {
404 index = 0;
405 }
406 for (int j = 0; j < predictorOrder - 1; j++) {
407 int ji = 0;
408 for (int i = 0; i < nAtoms; i++) {
409 jacobian[ji++][j] = predictorInducedDipole[mode][index][i][0];
410 jacobian[ji++][j] = predictorInducedDipole[mode][index][i][1];
411 jacobian[ji++][j] = predictorInducedDipole[mode][index][i][2];
412 jacobian[ji++][j] = predictorInducedDipoleCR[mode][index][i][0];
413 jacobian[ji++][j] = predictorInducedDipoleCR[mode][index][i][1];
414 jacobian[ji++][j] = predictorInducedDipoleCR[mode][index][i][2];
415 }
416 index++;
417 if (index >= predictorOrder) {
418 index = 0;
419 }
420 }
421 }
422
423 private double[][] jacobian(double[] variables) {
424 return jacobian;
425 }
426 }
427 }