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.Force;
42 import ffx.openmm.IntArray;
43 import ffx.openmm.amoeba.VdwForce;
44 import ffx.potential.ForceFieldEnergy;
45 import ffx.potential.bonded.Atom;
46 import ffx.potential.extended.ExtendedSystem;
47 import ffx.potential.nonbonded.NonbondedCutoff;
48 import ffx.potential.nonbonded.VanDerWaals;
49 import ffx.potential.nonbonded.VanDerWaalsForm;
50 import ffx.potential.parameters.ForceField;
51 import ffx.potential.parameters.VDWPairType;
52 import ffx.potential.parameters.VDWType;
53
54 import java.util.HashMap;
55 import java.util.Map;
56 import java.util.logging.Level;
57 import java.util.logging.Logger;
58
59 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_AlchemicalMethod.OpenMM_AmoebaVdwForce_Annihilate;
60 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_AlchemicalMethod.OpenMM_AmoebaVdwForce_Decouple;
61 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_NonbondedMethod.OpenMM_AmoebaVdwForce_CutoffPeriodic;
62 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AmoebaVdwForce_NonbondedMethod.OpenMM_AmoebaVdwForce_NoCutoff;
63 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_KJPerKcal;
64 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_NmPerAngstrom;
65 import static java.lang.Math.sqrt;
66 import static java.lang.String.format;
67
68
69
70
71 public class AmoebaVdwForce extends VdwForce {
72
73 private static final Logger logger = Logger.getLogger(AmoebaVdwForce.class.getName());
74
75
76
77
78
79
80
81
82 private int vdWClassForNoInteraction = 0;
83
84
85
86
87 private final Map<Integer, Integer> vdwClassToOpenMMType = new HashMap<>();
88
89
90
91
92
93
94 public AmoebaVdwForce(OpenMMEnergy openMMEnergy) {
95 VanDerWaals vdW = openMMEnergy.getVdwNode();
96 if (vdW == null) {
97 destroy();
98 return;
99 }
100
101
102 configureForce(openMMEnergy);
103
104
105 ExtendedSystem extendedSystem = vdW.getExtendedSystem();
106 double[] vdwPrefactorAndDerivs = new double[3];
107
108 int[] ired = vdW.getReductionIndex();
109 Atom[] atoms = openMMEnergy.getMolecularAssembly().getAtomArray();
110 int nAtoms = atoms.length;
111 for (int i = 0; i < nAtoms; i++) {
112 Atom atom = atoms[i];
113 VDWType vdwType = atom.getVDWType();
114 int atomClass = vdwType.atomClass;
115 int type = vdwClassToOpenMMType.get(atomClass);
116 int isAlchemical = atom.applyLambda() ? 1 : 0;
117 double scaleFactor = 1.0;
118 if (extendedSystem != null) {
119 extendedSystem.getVdwPrefactor(i, vdwPrefactorAndDerivs);
120 scaleFactor = vdwPrefactorAndDerivs[0];
121 }
122 addParticle(ired[i], type, vdwType.reductionFactor, isAlchemical, scaleFactor);
123 }
124
125
126 int[][] bondMask = vdW.getMask12();
127 int[][] angleMask = vdW.getMask13();
128 IntArray exclusions = new IntArray(0);
129 for (int i = 0; i < nAtoms; i++) {
130 exclusions.append(i);
131 final int[] bondMaski = bondMask[i];
132 for (int value : bondMaski) {
133 exclusions.append(value);
134 }
135 final int[] angleMaski = angleMask[i];
136 for (int value : angleMaski) {
137 exclusions.append(value);
138 }
139 setParticleExclusions(i, exclusions);
140 exclusions.resize(0);
141 }
142 exclusions.destroy();
143 }
144
145
146
147
148
149
150
151 public AmoebaVdwForce(int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
152
153 ForceFieldEnergy forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(topology);
154 VanDerWaals vdW = forceFieldEnergy.getVdwNode();
155 if (vdW == null) {
156 destroy();
157 return;
158 }
159
160 double scale = sqrt(openMMDualTopologyEnergy.getTopologyScale(topology));
161
162
163 configureForce(forceFieldEnergy);
164
165
166 ExtendedSystem extendedSystem = vdW.getExtendedSystem();
167 if (extendedSystem != null) {
168 logger.severe(" Extended system is not supported for dual-topology simulations.");
169 }
170
171 int nAtoms = openMMDualTopologyEnergy.getNumberOfAtoms();
172
173 int[] ired = vdW.getReductionIndex();
174
175
176 for (int i = 0; i < nAtoms; i++) {
177 Atom atom = openMMDualTopologyEnergy.getDualTopologyAtom(topology, i);
178 int top = atom.getTopologyIndex();
179 if (top == topology) {
180 int index = atom.getArrayIndex();
181 int ir = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, ired[index]);
182 VDWType vdwType = atom.getVDWType();
183 int atomClass = vdwType.atomClass;
184 int type = vdwClassToOpenMMType.get(atomClass);
185 int isAlchemical = atom.applyLambda() ? 1 : 0;
186 addParticle(ir, type, vdwType.reductionFactor, isAlchemical, scale);
187 } else {
188
189 int index = atom.getTopologyAtomIndex();
190 int type = vdwClassToOpenMMType.get(vdWClassForNoInteraction);
191 int isAlchemical = 1;
192 double scaleFactor = 0.0;
193 addParticle(index, type, 1.0, isAlchemical, scaleFactor);
194 }
195 }
196
197
198 int[][] bondMask = vdW.getMask12();
199 int[][] angleMask = vdW.getMask13();
200 IntArray exclusions = new IntArray(0);
201 for (int index = 0; index < nAtoms; index++) {
202 Atom atom = openMMDualTopologyEnergy.getDualTopologyAtom(topology, index);
203 if (atom.getTopologyIndex() != topology) {
204 continue;
205 }
206 exclusions.append(index);
207
208
209
210 final int[] bondMaski = bondMask[atom.getArrayIndex()];
211 for (int value : bondMaski) {
212
213 value = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, value);
214 exclusions.append(value);
215 }
216
217
218
219 final int[] angleMaski = angleMask[atom.getArrayIndex()];
220 for (int value : angleMaski) {
221
222 value = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, value);
223 exclusions.append(value);
224 }
225 setParticleExclusions(index, exclusions);
226
227
228 exclusions.resize(0);
229 }
230 exclusions.destroy();
231 }
232
233
234
235
236
237
238 private void configureForce(ForceFieldEnergy forceFieldEnergy) {
239 VanDerWaals vdW = forceFieldEnergy.getVdwNode();
240 VanDerWaalsForm vdwForm = vdW.getVDWForm();
241
242 double radScale = 1.0;
243 if (vdwForm.radiusSize == VDWType.RADIUS_SIZE.DIAMETER) {
244 radScale = 0.5;
245 }
246
247 ForceField forceField = forceFieldEnergy.getMolecularAssembly().getForceField();
248 Map<String, VDWType> vdwTypes = forceField.getVDWTypes();
249 for (VDWType vdwType : vdwTypes.values()) {
250 int atomClass = vdwType.atomClass;
251 if (!vdwClassToOpenMMType.containsKey(atomClass)) {
252 double eps = OpenMM_KJPerKcal * vdwType.wellDepth;
253 double rad = OpenMM_NmPerAngstrom * vdwType.radius * radScale;
254
255 if (rad == 0) {
256 rad = OpenMM_NmPerAngstrom * radScale;
257 }
258 int type = addParticleType(rad, eps);
259 vdwClassToOpenMMType.put(atomClass, type);
260 if (atomClass <= vdWClassForNoInteraction) {
261 vdWClassForNoInteraction = atomClass - 1;
262 }
263 }
264 }
265
266
267 int type = addParticleType(OpenMM_NmPerAngstrom, 0.0);
268 vdwClassToOpenMMType.put(vdWClassForNoInteraction, type);
269
270 Map<String, VDWPairType> vdwPairTypeMap = forceField.getVDWPairTypes();
271 for (VDWPairType vdwPairType : vdwPairTypeMap.values()) {
272 int c1 = vdwPairType.atomClasses[0];
273 int c2 = vdwPairType.atomClasses[1];
274 int type1 = vdwClassToOpenMMType.get(c1);
275 int type2 = vdwClassToOpenMMType.get(c2);
276 double rMin = vdwPairType.radius * OpenMM_NmPerAngstrom;
277 double eps = vdwPairType.wellDepth * OpenMM_KJPerKcal;
278 addTypePair(type1, type2, rMin, eps);
279 addTypePair(type2, type1, rMin, eps);
280 }
281
282
283 NonbondedCutoff nonbondedCutoff = vdW.getNonbondedCutoff();
284 setCutoffDistance(nonbondedCutoff.off * OpenMM_NmPerAngstrom);
285 if (vdW.getDoLongRangeCorrection()) {
286 setUseDispersionCorrection(true);
287 } else {
288 setUseDispersionCorrection(false);
289 }
290
291
292 Crystal crystal = forceFieldEnergy.getCrystal();
293 if (crystal.aperiodic()) {
294 setNonbondedMethod(OpenMM_AmoebaVdwForce_NoCutoff);
295 } else {
296 setNonbondedMethod(OpenMM_AmoebaVdwForce_CutoffPeriodic);
297 }
298
299
300 if (vdW.getLambdaTerm()) {
301 boolean annihilate = vdW.getIntramolecularSoftcore();
302 if (annihilate) {
303 setAlchemicalMethod(OpenMM_AmoebaVdwForce_Annihilate);
304 } else {
305 setAlchemicalMethod(OpenMM_AmoebaVdwForce_Decouple);
306 }
307 setSoftcoreAlpha(vdW.getAlpha());
308 setSoftcorePower((int) vdW.getBeta());
309 }
310
311 int forceGroup = forceField.getInteger("VDW_FORCE_GROUP", 1);
312 setForceGroup(forceGroup);
313
314 logger.log(Level.INFO, vdW.toString());
315 logger.log(Level.FINE, format(" Force group:\t\t%d\n", forceGroup));
316 }
317
318
319
320
321
322
323
324 public static Force constructForce(OpenMMEnergy openMMEnergy) {
325 VanDerWaals vdW = openMMEnergy.getVdwNode();
326 if (vdW == null) {
327 return null;
328 }
329 return new AmoebaVdwForce(openMMEnergy);
330 }
331
332
333
334
335
336
337
338
339 public static Force constructForce(int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
340 ForceFieldEnergy forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(topology);
341 VanDerWaals vdW = forceFieldEnergy.getVdwNode();
342 if (vdW == null) {
343 return null;
344 }
345 return new AmoebaVdwForce(topology, openMMDualTopologyEnergy);
346 }
347
348
349
350
351
352
353
354 public void updateForce(Atom[] atoms, OpenMMEnergy openMMEnergy) {
355 VanDerWaals vdW = openMMEnergy.getVdwNode();
356 VanDerWaalsForm vdwForm = vdW.getVDWForm();
357 double radScale = 1.0;
358 if (vdwForm.radiusSize == VDWType.RADIUS_SIZE.DIAMETER) {
359 radScale = 0.5;
360 }
361
362 ExtendedSystem extendedSystem = vdW.getExtendedSystem();
363 double[] vdwPrefactorAndDerivs = new double[3];
364
365 int[] ired = vdW.getReductionIndex();
366 for (Atom atom : atoms) {
367 int index = atom.getArrayIndex();
368 VDWType vdwType = atom.getVDWType();
369
370
371 int type = vdwClassToOpenMMType.get(vdwType.atomClass);
372 if (!atom.getUse()) {
373
374 type = vdwClassToOpenMMType.get(vdWClassForNoInteraction);
375 }
376 int isAlchemical = atom.applyLambda() ? 1 : 0;
377 double eps = OpenMM_KJPerKcal * vdwType.wellDepth;
378 double rad = OpenMM_NmPerAngstrom * vdwType.radius * radScale;
379
380 double scaleFactor = 1.0;
381 if (extendedSystem != null) {
382 extendedSystem.getVdwPrefactor(index, vdwPrefactorAndDerivs);
383 scaleFactor = vdwPrefactorAndDerivs[0];
384 }
385
386 setParticleParameters(index, ired[index], rad, eps, vdwType.reductionFactor, isAlchemical, type, scaleFactor);
387 }
388 updateParametersInContext(openMMEnergy.getContext());
389 }
390
391
392
393
394
395
396
397
398 public void updateForce(Atom[] atoms, int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
399 ForceFieldEnergy forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(topology);
400 double scale = sqrt(openMMDualTopologyEnergy.getTopologyScale(topology));
401
402 VanDerWaals vdW = forceFieldEnergy.getVdwNode();
403 VanDerWaalsForm vdwForm = vdW.getVDWForm();
404 double radScale = 1.0;
405 if (vdwForm.radiusSize == VDWType.RADIUS_SIZE.DIAMETER) {
406 radScale = 0.5;
407 }
408
409
410 int[] ired = vdW.getReductionIndex();
411
412 for (Atom atom : atoms) {
413 if (atom.getTopologyIndex() != topology) {
414
415 continue;
416 }
417
418
419 int indexDT = atom.getTopologyAtomIndex();
420
421 int ir = ired[atom.getArrayIndex()];
422 ir = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, ir);
423
424 VDWType vdwType = atom.getVDWType();
425
426 int type = vdwClassToOpenMMType.get(vdwType.atomClass);
427 if (!atom.getUse()) {
428
429 type = vdwClassToOpenMMType.get(vdWClassForNoInteraction);
430 }
431 int isAlchemical = atom.applyLambda() ? 1 : 0;
432 double eps = OpenMM_KJPerKcal * vdwType.wellDepth;
433 double rad = OpenMM_NmPerAngstrom * vdwType.radius * radScale;
434 setParticleParameters(indexDT, ir, rad, eps, vdwType.reductionFactor, isAlchemical, type, scale);
435 }
436 updateParametersInContext(openMMDualTopologyEnergy.getContext());
437 }
438
439 }