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 com.sun.jna.ptr.DoubleByReference;
41 import com.sun.jna.ptr.IntByReference;
42 import ffx.crystal.Crystal;
43 import ffx.openmm.BondArray;
44 import ffx.openmm.Force;
45 import ffx.openmm.NonbondedForce;
46 import ffx.potential.bonded.Atom;
47 import ffx.potential.bonded.Bond;
48 import ffx.potential.nonbonded.NonbondedCutoff;
49 import ffx.potential.nonbonded.ParticleMeshEwald;
50 import ffx.potential.nonbonded.VanDerWaals;
51 import ffx.potential.nonbonded.VanDerWaalsForm;
52 import ffx.potential.parameters.ForceField;
53 import ffx.potential.parameters.MultipoleType;
54 import ffx.potential.parameters.VDWType;
55
56 import java.util.logging.Level;
57 import java.util.logging.Logger;
58
59 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AngstromsPerNm;
60 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_KJPerKcal;
61 import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_NmPerAngstrom;
62 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Boolean.OpenMM_False;
63 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Boolean.OpenMM_True;
64 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_NonbondedForce_NonbondedMethod.OpenMM_NonbondedForce_NoCutoff;
65 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_NonbondedForce_NonbondedMethod.OpenMM_NonbondedForce_PME;
66 import static ffx.potential.parameters.VDWType.EPSILON_RULE.GEOMETRIC;
67 import static ffx.potential.parameters.VDWType.RADIUS_RULE.ARITHMETIC;
68 import static ffx.potential.parameters.VDWType.RADIUS_SIZE.RADIUS;
69 import static ffx.potential.parameters.VDWType.RADIUS_TYPE.R_MIN;
70 import static ffx.potential.parameters.VDWType.VDW_TYPE.LENNARD_JONES;
71 import static java.lang.String.format;
72 import static org.apache.commons.math3.util.FastMath.abs;
73
74
75
76
77
78
79 public class FixedChargeNonbondedForce extends NonbondedForce {
80
81 private static final Logger logger = Logger.getLogger(FixedChargeNonbondedForce.class.getName());
82
83
84
85
86 private boolean[] chargeExclusion;
87
88
89
90 private boolean[] vdWExclusion;
91
92
93
94 private double[] exceptionChargeProd;
95
96
97
98 private double[] exceptionEps;
99
100 public FixedChargeNonbondedForce(OpenMMEnergy openMMEnergy) {
101 VanDerWaals vdW = openMMEnergy.getVdwNode();
102 if (vdW == null) {
103
104 destroy();
105 return;
106 }
107
108
109
110
111
112 VanDerWaalsForm vdwForm = vdW.getVDWForm();
113 if (vdwForm.vdwType != LENNARD_JONES || vdwForm.radiusRule != ARITHMETIC
114 || vdwForm.epsilonRule != GEOMETRIC) {
115 logger.info(format(" VDW Type: %s", vdwForm.vdwType));
116 logger.info(format(" VDW Radius Rule: %s", vdwForm.radiusRule));
117 logger.info(format(" VDW Epsilon Rule: %s", vdwForm.epsilonRule));
118 logger.log(Level.SEVERE, " Unsupported van der Waals functional form.");
119 return;
120 }
121
122
123 double radScale = 1.0;
124 if (vdwForm.radiusSize == RADIUS) {
125 radScale = 2.0;
126 }
127
128
129 if (vdwForm.radiusType == R_MIN) {
130 radScale /= 1.122462048309372981;
131 }
132
133
134 Atom[] atoms = openMMEnergy.getMolecularAssembly().getAtomArray();
135 for (Atom atom : atoms) {
136 VDWType vdwType = atom.getVDWType();
137 double sigma = OpenMM_NmPerAngstrom * vdwType.radius * radScale;
138 double eps = OpenMM_KJPerKcal * vdwType.wellDepth;
139 double charge = 0.0;
140 MultipoleType multipoleType = atom.getMultipoleType();
141 if (multipoleType != null && atom.getElectrostatics()) {
142 charge = multipoleType.charge;
143 }
144 addParticle(charge, sigma, eps);
145 }
146
147
148 double lj14Scale = vdwForm.getScale14();
149 double coulomb14Scale = 1.0 / 1.2;
150 ParticleMeshEwald pme = openMMEnergy.getPmeNode();
151 if (pme != null) {
152 coulomb14Scale = pme.getScale14();
153 }
154
155 Bond[] bonds = openMMEnergy.getBonds();
156 if (bonds != null && bonds.length > 0) {
157 BondArray bondArray = new BondArray(0);
158 for (Bond bond : bonds) {
159 int i1 = bond.getAtom(0).getXyzIndex() - 1;
160 int i2 = bond.getAtom(1).getXyzIndex() - 1;
161 bondArray.append(i1, i2);
162 }
163 createExceptionsFromBonds(bondArray, coulomb14Scale, lj14Scale);
164 bondArray.destroy();
165
166 int num = getNumExceptions();
167 chargeExclusion = new boolean[num];
168 vdWExclusion = new boolean[num];
169 exceptionChargeProd = new double[num];
170 exceptionEps = new double[num];
171 IntByReference particle1 = new IntByReference();
172 IntByReference particle2 = new IntByReference();
173 DoubleByReference chargeProd = new DoubleByReference();
174 DoubleByReference sigma = new DoubleByReference();
175 DoubleByReference eps = new DoubleByReference();
176 for (int i = 0; i < num; i++) {
177 getExceptionParameters(i, particle1, particle2, chargeProd, sigma, eps);
178 if (abs(chargeProd.getValue()) > 0.0) {
179 chargeExclusion[i] = false;
180 exceptionChargeProd[i] = chargeProd.getValue();
181 } else {
182 exceptionChargeProd[i] = 0.0;
183 chargeExclusion[i] = true;
184 }
185 if (abs(eps.getValue()) > 0.0) {
186 vdWExclusion[i] = false;
187 exceptionEps[i] = eps.getValue();
188 } else {
189 vdWExclusion[i] = true;
190 exceptionEps[i] = 0.0;
191 }
192 }
193 }
194
195 Crystal crystal = openMMEnergy.getCrystal();
196 if (crystal.aperiodic()) {
197 setNonbondedMethod(OpenMM_NonbondedForce_NoCutoff);
198 } else {
199 setNonbondedMethod(OpenMM_NonbondedForce_PME);
200 if (pme != null) {
201
202 double aEwald = OpenMM_AngstromsPerNm * pme.getEwaldCoefficient();
203 int nx = pme.getReciprocalSpace().getXDim();
204 int ny = pme.getReciprocalSpace().getYDim();
205 int nz = pme.getReciprocalSpace().getZDim();
206 setPMEParameters(aEwald, nx, ny, nz);
207 }
208
209 NonbondedCutoff nonbondedCutoff = vdW.getNonbondedCutoff();
210 double off = nonbondedCutoff.off;
211 double cut = nonbondedCutoff.cut;
212 setCutoffDistance(OpenMM_NmPerAngstrom * off);
213 setUseSwitchingFunction(OpenMM_True);
214 if (cut == off) {
215 logger.warning(" OpenMM does not properly handle cutoffs where cut == off!");
216 if (cut == Double.MAX_VALUE || cut == Double.POSITIVE_INFINITY) {
217 logger.info(" Detected infinite or max-value cutoff; setting cut to 1E+40 for OpenMM.");
218 cut = 1E40;
219 } else {
220 logger.info(format(" Detected cut %8.4g == off %8.4g; scaling cut to 0.99 of off for OpenMM.", cut, off));
221 cut *= 0.99;
222 }
223 }
224 setSwitchingDistance(OpenMM_NmPerAngstrom * cut);
225 }
226
227 setUseDispersionCorrection(OpenMM_False);
228
229 ForceField forceField = openMMEnergy.getMolecularAssembly().getForceField();
230 int forceGroup = forceField.getInteger("VDW_FORCE_GROUP", 1);
231 int pmeGroup = forceField.getInteger("PME_FORCE_GROUP", 1);
232 if (forceGroup != pmeGroup) {
233 logger.severe(format(" ERROR: VDW-FORCE-GROUP is %d while PME-FORCE-GROUP is %d. "
234 + "This is invalid for fixed-charge force fields with combined non-bonded forces.",
235 forceGroup, pmeGroup));
236 }
237
238 setForceGroup(forceGroup);
239 logger.log(Level.INFO, format(" Fixed charge non-bonded force \t%1d", forceGroup));
240 }
241
242
243
244
245
246
247
248 public static Force constructForce(OpenMMEnergy openMMEnergy) {
249 VanDerWaals vdW = openMMEnergy.getVdwNode();
250 if (vdW == null) {
251
252 return null;
253 }
254
255
256
257
258
259 VanDerWaalsForm vdwForm = vdW.getVDWForm();
260 if (vdwForm.vdwType != LENNARD_JONES || vdwForm.radiusRule != ARITHMETIC
261 || vdwForm.epsilonRule != GEOMETRIC) {
262 logger.info(format(" VDW Type: %s", vdwForm.vdwType));
263 logger.info(format(" VDW Radius Rule: %s", vdwForm.radiusRule));
264 logger.info(format(" VDW Epsilon Rule: %s", vdwForm.epsilonRule));
265 logger.log(Level.SEVERE, " Unsupported van der Waals functional form.");
266 return null;
267 }
268
269 return new FixedChargeNonbondedForce(openMMEnergy);
270 }
271
272
273
274
275
276
277
278 public void updateForce(Atom[] atoms, OpenMMEnergy openMMEnergy) {
279 VanDerWaals vdW = openMMEnergy.getVdwNode();
280
281
282 VanDerWaalsForm vdwForm = vdW.getVDWForm();
283 if (vdwForm.vdwType != LENNARD_JONES || vdwForm.radiusRule != ARITHMETIC
284 || vdwForm.epsilonRule != GEOMETRIC) {
285 logger.log(Level.SEVERE, " Unsupported van der Waals functional form.");
286 return;
287 }
288
289
290 double radScale = 1.0;
291 if (vdwForm.radiusSize == RADIUS) {
292 radScale = 2.0;
293 }
294
295
296 if (vdwForm.radiusType == R_MIN) {
297 radScale /= 1.122462048309372981;
298 }
299
300
301 for (Atom atom : atoms) {
302 int index = atom.getXyzIndex() - 1;
303 boolean applyLambda = atom.applyLambda();
304
305 double charge = Double.MIN_VALUE;
306 MultipoleType multipoleType = atom.getMultipoleType();
307 if (multipoleType != null && atom.getElectrostatics()) {
308 charge = multipoleType.charge;
309 }
310
311 VDWType vdwType = atom.getVDWType();
312 double sigma = OpenMM_NmPerAngstrom * vdwType.radius * radScale;
313 double eps = OpenMM_KJPerKcal * vdwType.wellDepth;
314
315 if (applyLambda) {
316 boolean vdwLambdaTerm = vdW.getLambdaTerm();
317
318 if (vdwLambdaTerm) {
319 eps = 0.0;
320 }
321
322 double permLambda = openMMEnergy.getPmeNode().getAlchemicalParameters().permLambda;
323 charge *= permLambda;
324 }
325
326 if (!atom.getUse()) {
327 eps = 0.0;
328 charge = 0.0;
329 }
330
331 setParticleParameters(index, charge, sigma, eps);
332 }
333
334
335 IntByReference particle1 = new IntByReference();
336 IntByReference particle2 = new IntByReference();
337 DoubleByReference chargeProd = new DoubleByReference();
338 DoubleByReference sigma = new DoubleByReference();
339 DoubleByReference eps = new DoubleByReference();
340
341 int numExceptions = getNumExceptions();
342 for (int i = 0; i < numExceptions; i++) {
343
344
345 if (chargeExclusion[i] && vdWExclusion[i]) {
346 continue;
347 }
348
349 getExceptionParameters(i, particle1, particle2, chargeProd, sigma, eps);
350
351 int i1 = particle1.getValue();
352 int i2 = particle2.getValue();
353
354 double qq = exceptionChargeProd[i];
355 double epsilon = exceptionEps[i];
356
357 Atom atom1 = atoms[i1];
358 Atom atom2 = atoms[i2];
359
360
361
362
363
364 double minEpsilon = 1.0e-12;
365
366 double lambdaElec = 1.0;
367 ParticleMeshEwald pme = openMMEnergy.getPmeNode();
368 if (pme != null) {
369 lambdaElec = pme.getAlchemicalParameters().permLambda;
370 }
371
372 boolean vdwLambdaTerm = vdW.getLambdaTerm();
373
374 if (lambdaElec < minEpsilon) {
375 lambdaElec = minEpsilon;
376 }
377
378 if (atom1.applyLambda()) {
379 qq *= lambdaElec;
380 if (vdwLambdaTerm) {
381 epsilon = minEpsilon;
382 }
383 }
384 if (atom2.applyLambda()) {
385 qq *= lambdaElec;
386 if (vdwLambdaTerm) {
387 epsilon = minEpsilon;
388 }
389 }
390 if (!atom1.getUse() || !atom2.getUse()) {
391 qq = minEpsilon;
392 epsilon = minEpsilon;
393 }
394
395 setExceptionParameters(i, i1, i2, qq, sigma.getValue(), epsilon);
396 }
397
398
399 updateParametersInContext(openMMEnergy.getContext());
400 }
401
402 }