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.openmm;
39
40 import ffx.crystal.Crystal;
41 import ffx.openmm.DoubleArray;
42 import ffx.openmm.Force;
43 import ffx.openmm.IntArray;
44 import ffx.openmm.amoeba.MultipoleForce;
45 import ffx.potential.bonded.Atom;
46 import ffx.potential.nonbonded.ParticleMeshEwald;
47 import ffx.potential.nonbonded.ReciprocalSpace;
48 import ffx.potential.nonbonded.pme.Polarization;
49 import ffx.potential.nonbonded.pme.SCFAlgorithm;
50 import ffx.potential.parameters.ForceField;
51 import ffx.potential.parameters.MultipoleType;
52 import ffx.potential.parameters.PolarizeType;
53
54 import java.util.logging.Level;
55 import java.util.logging.Logger;
56
57 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_CovalentType.OpenMM_AmoebaMultipoleForce_Covalent12;
58 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_CovalentType.OpenMM_AmoebaMultipoleForce_Covalent13;
59 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_CovalentType.OpenMM_AmoebaMultipoleForce_Covalent14;
60 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_CovalentType.OpenMM_AmoebaMultipoleForce_Covalent15;
61 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_CovalentType.OpenMM_AmoebaMultipoleForce_PolarizationCovalent11;
62 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_MultipoleAxisTypes.OpenMM_AmoebaMultipoleForce_Bisector;
63 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_MultipoleAxisTypes.OpenMM_AmoebaMultipoleForce_NoAxisType;
64 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_MultipoleAxisTypes.OpenMM_AmoebaMultipoleForce_ThreeFold;
65 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_MultipoleAxisTypes.OpenMM_AmoebaMultipoleForce_ZBisect;
66 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_MultipoleAxisTypes.OpenMM_AmoebaMultipoleForce_ZOnly;
67 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_MultipoleAxisTypes.OpenMM_AmoebaMultipoleForce_ZThenX;
68 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_NonbondedMethod.OpenMM_AmoebaMultipoleForce_NoCutoff;
69 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_NonbondedMethod.OpenMM_AmoebaMultipoleForce_PME;
70 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_PolarizationType.OpenMM_AmoebaMultipoleForce_Direct;
71 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_PolarizationType.OpenMM_AmoebaMultipoleForce_Extrapolated;
72 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaMultipoleForce_PolarizationType.OpenMM_AmoebaMultipoleForce_Mutual;
73 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_NmPerAngstrom;
74 import static java.lang.String.format;
75 import static org.apache.commons.math3.util.FastMath.sqrt;
76
77
78
79
80 public class AmoebaMultipoleForce extends MultipoleForce {
81
82 private static final Logger logger = Logger.getLogger(AmoebaMultipoleForce.class.getName());
83
84 public AmoebaMultipoleForce(OpenMMEnergy openMMEnergy) {
85 ParticleMeshEwald pme = openMMEnergy.getPmeNode();
86 if (pme == null) {
87 destroy();
88 return;
89 }
90
91 int[][] axisAtom = pme.getAxisAtoms();
92 double quadrupoleConversion = OpenMM_NmPerAngstrom * OpenMM_NmPerAngstrom;
93 double polarityConversion = OpenMM_NmPerAngstrom * OpenMM_NmPerAngstrom * OpenMM_NmPerAngstrom;
94 double dampingFactorConversion = sqrt(OpenMM_NmPerAngstrom);
95 double polarScale = 1.0;
96 SCFAlgorithm scfAlgorithm = null;
97
98 ForceField forceField = openMMEnergy.getMolecularAssembly().getForceField();
99
100 if (pme.getPolarizationType() != Polarization.MUTUAL) {
101 setPolarizationType(OpenMM_AmoebaMultipoleForce_Direct);
102 if (pme.getPolarizationType() == Polarization.NONE) {
103 polarScale = 0.0;
104 }
105 } else {
106 String algorithm = forceField.getString("SCF_ALGORITHM", "CG");
107 try {
108 algorithm = algorithm.replaceAll("-", "_").toUpperCase();
109 scfAlgorithm = SCFAlgorithm.valueOf(algorithm);
110 } catch (Exception e) {
111 scfAlgorithm = SCFAlgorithm.CG;
112 }
113
114 if (scfAlgorithm == SCFAlgorithm.EPT) {
115
116
117
118
119
120 setPolarizationType(OpenMM_AmoebaMultipoleForce_Extrapolated);
121 DoubleArray exptCoefficients = new DoubleArray(4);
122 exptCoefficients.set(0, -0.154);
123 exptCoefficients.set(1, 0.017);
124 exptCoefficients.set(2, 0.657);
125 exptCoefficients.set(3, 0.475);
126 setExtrapolationCoefficients(exptCoefficients);
127 exptCoefficients.destroy();
128 } else {
129 setPolarizationType(OpenMM_AmoebaMultipoleForce_Mutual);
130 }
131 }
132
133 DoubleArray dipoles = new DoubleArray(3);
134 DoubleArray quadrupoles = new DoubleArray(9);
135
136 OpenMMSystem system = openMMEnergy.getSystem();
137 double lambdaElec = system.getLambdaElec();
138
139 Atom[] atoms = openMMEnergy.getMolecularAssembly().getAtomArray();
140 int nAtoms = atoms.length;
141 for (int i = 0; i < nAtoms; i++) {
142 Atom atom = atoms[i];
143 MultipoleType multipoleType = pme.getMultipoleType(i);
144 PolarizeType polarType = pme.getPolarizeType(i);
145
146
147 int axisType = switch (multipoleType.frameDefinition) {
148 case NONE -> OpenMM_AmoebaMultipoleForce_NoAxisType;
149 case ZONLY -> OpenMM_AmoebaMultipoleForce_ZOnly;
150 case ZTHENX -> OpenMM_AmoebaMultipoleForce_ZThenX;
151 case BISECTOR -> OpenMM_AmoebaMultipoleForce_Bisector;
152 case ZTHENBISECTOR -> OpenMM_AmoebaMultipoleForce_ZBisect;
153 case THREEFOLD -> OpenMM_AmoebaMultipoleForce_ThreeFold;
154 };
155
156 double useFactor = 1.0;
157 if (!atoms[i].getUse() || !atoms[i].getElectrostatics()) {
158 useFactor = 0.0;
159 }
160
161 double lambdaScale = lambdaElec;
162 if (!atom.applyLambda()) {
163 lambdaScale = 1.0;
164 }
165
166 useFactor *= lambdaScale;
167
168
169 for (int j = 0; j < 3; j++) {
170 dipoles.set(j, multipoleType.dipole[j] * OpenMM_NmPerAngstrom * useFactor);
171 }
172 int l = 0;
173 for (int j = 0; j < 3; j++) {
174 for (int k = 0; k < 3; k++) {
175 quadrupoles.set(l++, multipoleType.quadrupole[j][k] * quadrupoleConversion * useFactor / 3.0);
176 }
177 }
178
179 int zaxis = -1;
180 int xaxis = -1;
181 int yaxis = -1;
182 int[] refAtoms = axisAtom[i];
183 if (refAtoms != null) {
184 zaxis = refAtoms[0];
185 if (refAtoms.length > 1) {
186 xaxis = refAtoms[1];
187 if (refAtoms.length > 2) {
188 yaxis = refAtoms[2];
189 }
190 }
191 } else {
192 axisType = OpenMM_AmoebaMultipoleForce_NoAxisType;
193 }
194
195 double charge = multipoleType.charge * useFactor;
196
197
198 addMultipole(charge, dipoles, quadrupoles, axisType, zaxis, xaxis, yaxis, polarType.thole,
199 polarType.pdamp * dampingFactorConversion, polarType.polarizability * polarityConversion * polarScale);
200 }
201 dipoles.destroy();
202 quadrupoles.destroy();
203
204 Crystal crystal = openMMEnergy.getCrystal();
205 if (!crystal.aperiodic()) {
206 setNonbondedMethod(OpenMM_AmoebaMultipoleForce_PME);
207 setCutoffDistance(pme.getEwaldCutoff() * OpenMM_NmPerAngstrom);
208 setAEwald(pme.getEwaldCoefficient() / OpenMM_NmPerAngstrom);
209
210 double ewaldTolerance = 1.0e-04;
211 setEwaldErrorTolerance(ewaldTolerance);
212
213 IntArray gridDimensions = new IntArray(3);
214 ReciprocalSpace recip = pme.getReciprocalSpace();
215 gridDimensions.set(0, recip.getXDim());
216 gridDimensions.set(1, recip.getYDim());
217 gridDimensions.set(2, recip.getZDim());
218 setPmeGridDimensions(gridDimensions);
219 gridDimensions.destroy();
220 } else {
221 setNonbondedMethod(OpenMM_AmoebaMultipoleForce_NoCutoff);
222 }
223
224 setMutualInducedMaxIterations(500);
225 setMutualInducedTargetEpsilon(pme.getPolarEps());
226
227 int[][] ip11 = pme.getPolarization11();
228
229 IntArray covalentMap = new IntArray(0);
230 for (int i = 0; i < nAtoms; i++) {
231 Atom ai = atoms[i];
232
233
234 covalentMap.resize(0);
235 for (Atom ak : ai.get12List()) {
236 covalentMap.append(ak.getIndex() - 1);
237 }
238 setCovalentMap(i, OpenMM_AmoebaMultipoleForce_Covalent12, covalentMap);
239
240
241 covalentMap.resize(0);
242 for (Atom ak : ai.get13List()) {
243 covalentMap.append(ak.getIndex() - 1);
244 }
245 setCovalentMap(i, OpenMM_AmoebaMultipoleForce_Covalent13, covalentMap);
246
247
248 covalentMap.resize(0);
249 for (Atom ak : ai.get14List()) {
250 covalentMap.append(ak.getIndex() - 1);
251 }
252 setCovalentMap(i, OpenMM_AmoebaMultipoleForce_Covalent14, covalentMap);
253
254
255 covalentMap.resize(0);
256 for (Atom ak : ai.get15List()) {
257 covalentMap.append(ak.getIndex() - 1);
258 }
259 setCovalentMap(i, OpenMM_AmoebaMultipoleForce_Covalent15, covalentMap);
260
261
262 covalentMap.resize(0);
263 for (int j = 0; j < ip11[i].length; j++) {
264 covalentMap.append(ip11[i][j]);
265 }
266 setCovalentMap(i, OpenMM_AmoebaMultipoleForce_PolarizationCovalent11, covalentMap);
267
268
269 }
270 covalentMap.destroy();
271
272 int forceGroup = forceField.getInteger("PME_FORCE_GROUP", 1);
273 setForceGroup(forceGroup);
274 logger.log(Level.INFO, format(" AMOEBA polarizable multipole force \t%d", forceGroup));
275
276 if (scfAlgorithm == SCFAlgorithm.EPT) {
277 logger.info(" Using extrapolated perturbation theory for polarization energy.");
278 }
279 }
280
281
282
283
284
285
286
287 public static Force constructForce(OpenMMEnergy openMMEnergy) {
288 ParticleMeshEwald pme = openMMEnergy.getPmeNode();
289 if (pme == null) {
290 return null;
291 }
292 return new AmoebaMultipoleForce(openMMEnergy);
293 }
294
295 public void updateForce(Atom[] atoms, OpenMMEnergy openMMEnergy) {
296 ParticleMeshEwald pme = openMMEnergy.getPmeNode();
297 double quadrupoleConversion = OpenMM_NmPerAngstrom * OpenMM_NmPerAngstrom;
298 double polarityConversion = OpenMM_NmPerAngstrom * OpenMM_NmPerAngstrom * OpenMM_NmPerAngstrom;
299 double dampingFactorConversion = sqrt(OpenMM_NmPerAngstrom);
300
301 double polarScale = 1.0;
302 if (pme.getPolarizationType() == Polarization.NONE) {
303 polarScale = 0.0;
304 }
305
306 DoubleArray dipoles = new DoubleArray(3);
307 DoubleArray quadrupoles = new DoubleArray(9);
308
309 double lambdaElec = openMMEnergy.getSystem().getLambdaElec();
310
311 for (Atom atom : atoms) {
312 int index = atom.getXyzIndex() - 1;
313 MultipoleType multipoleType = pme.getMultipoleType(index);
314 PolarizeType polarizeType = pme.getPolarizeType(index);
315 int[] axisAtoms = atom.getAxisAtomIndices();
316
317 double useFactor = 1.0;
318 if (!atom.getUse() || !atom.getElectrostatics()) {
319 useFactor = 0.0;
320 }
321
322 double lambdaScale = lambdaElec;
323 if (!atom.applyLambda()) {
324 lambdaScale = 1.0;
325 }
326 useFactor *= lambdaScale;
327
328
329 int axisType = switch (multipoleType.frameDefinition) {
330 case NONE -> OpenMM_AmoebaMultipoleForce_NoAxisType;
331 case ZONLY -> OpenMM_AmoebaMultipoleForce_ZOnly;
332 case ZTHENX -> OpenMM_AmoebaMultipoleForce_ZThenX;
333 case BISECTOR -> OpenMM_AmoebaMultipoleForce_Bisector;
334 case ZTHENBISECTOR -> OpenMM_AmoebaMultipoleForce_ZBisect;
335 case THREEFOLD -> OpenMM_AmoebaMultipoleForce_ThreeFold;
336 };
337
338
339 for (int j = 0; j < 3; j++) {
340 dipoles.set(j, multipoleType.dipole[j] * OpenMM_NmPerAngstrom * useFactor);
341 }
342 int l = 0;
343 for (int j = 0; j < 3; j++) {
344 for (int k = 0; k < 3; k++) {
345 quadrupoles.set(l++, multipoleType.quadrupole[j][k] * quadrupoleConversion / 3.0 * useFactor);
346 }
347 }
348
349 int zaxis = 1;
350 int xaxis = 1;
351 int yaxis = 1;
352
353 if (axisAtoms != null) {
354 zaxis = axisAtoms[0];
355 if (axisAtoms.length > 1) {
356 xaxis = axisAtoms[1];
357 if (axisAtoms.length > 2) {
358 yaxis = axisAtoms[2];
359 }
360 }
361 } else {
362 axisType = OpenMM_AmoebaMultipoleForce_NoAxisType;
363 }
364
365
366 setMultipoleParameters(index, multipoleType.charge * useFactor,
367 dipoles, quadrupoles, axisType, zaxis, xaxis, yaxis,
368 polarizeType.thole, polarizeType.pdamp * dampingFactorConversion,
369 polarizeType.polarizability * polarityConversion * polarScale * useFactor);
370 }
371
372 dipoles.destroy();
373 quadrupoles.destroy();
374 updateParametersInContext(openMMEnergy.getContext());
375 }
376
377 }