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.parameters;
39
40 import static ffx.potential.parameters.ForceField.ForceFieldType.IMPTORS;
41 import static ffx.utilities.PropertyGroup.EnergyUnitConversion;
42 import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
43 import static java.lang.Double.parseDouble;
44 import static java.lang.Integer.parseInt;
45 import static org.apache.commons.math3.util.FastMath.cos;
46 import static org.apache.commons.math3.util.FastMath.sin;
47 import static org.apache.commons.math3.util.FastMath.toRadians;
48
49 import ffx.utilities.FFXProperty;
50
51 import java.util.Arrays;
52 import java.util.Comparator;
53 import java.util.HashMap;
54 import java.util.logging.Level;
55 import java.util.logging.Logger;
56
57
58
59
60
61
62
63 @FFXProperty(name = "imptors", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
64 [4 integers and up to 3 real/real/integer triples]
65 Provides the values for a single AMBER-style improper torsional angle parameter.
66 The first four integer modifiers give the atom class numbers for the atoms involved in the improper torsional angle to be defined.
67 By convention, the third atom class of the four is the trigonal atom on which the improper torsion is centered.
68 The torsional angle computed is literally that defined by the four atom classes in the order specified by the keyword.
69 Each of the remaining triples of real/real/integer modifiers give the half-amplitude,
70 phase offset in degrees and periodicity of a particular improper torsional term, respectively.
71 Periodicities through 3-fold are allowed for improper torsional parameters.
72 """)
73 public final class ImproperTorsionType extends BaseType implements Comparator<String> {
74
75
76
77
78 private static final Logger logger = Logger.getLogger(ImproperTorsionType.class.getName());
79
80
81
82
83 public final int[] atomClasses;
84
85
86
87 public final double k;
88
89
90
91 public final double phase;
92
93
94
95 public final int periodicity;
96
97
98
99 public final double cos;
100
101
102
103 public final double sin;
104
105
106
107
108 @FFXProperty(name = "imptorunit", propertyGroup = EnergyUnitConversion, defaultValue = "1.0", description = """
109 Sets the scale factor needed to convert the energy value computed by the AMBER-style improper
110 torsional angle potential into units of kcal/mole.
111 The correct value is force field dependent and typically provided in the header of the master force field parameter file.
112 """)
113 public double impTorUnit = DEFAULT_IMPTOR_UNIT;
114 public static final double DEFAULT_IMPTOR_UNIT = 1.0;
115
116
117
118
119
120
121
122
123
124 public ImproperTorsionType(int[] atomClasses, double k, double phase, int periodicity) {
125 super(IMPTORS, sortKey(atomClasses));
126 this.atomClasses = atomClasses;
127 double symm = 1.0;
128 this.periodicity = periodicity;
129 this.k = k / symm;
130 this.phase = phase;
131 cos = cos(toRadians(phase));
132 sin = sin(toRadians(phase));
133
134 assert (periodicity == 2);
135 }
136
137
138
139
140
141
142
143
144
145
146 public static ImproperTorsionType average(ImproperTorsionType improperTorsionType1,
147 ImproperTorsionType improperTorsionType2, int[] atomClasses) {
148
149 if (improperTorsionType1 == null || improperTorsionType2 == null || atomClasses == null) {
150 return null;
151 }
152
153 int periodicity = improperTorsionType1.periodicity;
154 if (periodicity != improperTorsionType2.periodicity) {
155 return null;
156 }
157
158 double forceConstant = (improperTorsionType1.k + improperTorsionType2.k) / 2.0;
159 double phase = (improperTorsionType1.phase + improperTorsionType2.phase) / 2.0;
160
161 return new ImproperTorsionType(atomClasses, forceConstant, phase, periodicity);
162 }
163
164
165
166
167
168
169
170
171 public static ImproperTorsionType parse(String input, String[] tokens) {
172 if (tokens.length < 8) {
173 logger.log(Level.WARNING, "Invalid IMPTORS type:\n{0}", input);
174 } else {
175 try {
176 int[] atomClasses = new int[4];
177 atomClasses[0] = parseInt(tokens[1]);
178 atomClasses[1] = parseInt(tokens[2]);
179 atomClasses[2] = parseInt(tokens[3]);
180 atomClasses[3] = parseInt(tokens[4]);
181 double k = parseDouble(tokens[5]);
182 double phase = parseDouble(tokens[6]);
183 int period = parseInt(tokens[7]);
184 return new ImproperTorsionType(atomClasses, k, phase, period);
185 } catch (NumberFormatException e) {
186 String message = "Exception parsing IMPTORS type:\n" + input + "\n";
187 logger.log(Level.SEVERE, message, e);
188 }
189 }
190 return null;
191 }
192
193
194
195
196
197
198
199
200 public static String sortKey(int[] c) {
201 if (c == null || c.length != 4) {
202 return null;
203 }
204 return c[0] + " " + c[1] + " " + c[2] + " " + c[3];
205 }
206
207
208
209
210
211
212
213
214
215
216 public boolean assigned(int[] inputClasses, boolean allowInitialWildCards,
217 boolean allowFinalWildCard) {
218
219 if (inputClasses[2] != atomClasses[2]) {
220 return false;
221 }
222
223
224 if (inputClasses[3] == atomClasses[3] || (atomClasses[3] == 0 && allowFinalWildCard)) {
225
226 } else if (inputClasses[1] == atomClasses[3]) {
227 int temp = inputClasses[3];
228 inputClasses[3] = inputClasses[1];
229 inputClasses[1] = temp;
230 } else if (inputClasses[0] == atomClasses[3]) {
231 int temp = inputClasses[3];
232 inputClasses[3] = inputClasses[0];
233 inputClasses[0] = temp;
234 } else {
235 return false;
236 }
237
238
239 if (inputClasses[1] == atomClasses[1] || (atomClasses[1] == 0 && allowInitialWildCards)) {
240
241 } else if (inputClasses[0] == atomClasses[1]) {
242 int temp = inputClasses[1];
243 inputClasses[1] = inputClasses[0];
244 inputClasses[0] = temp;
245 } else {
246 return false;
247 }
248
249
250 return (inputClasses[0] == atomClasses[0] || (atomClasses[0] == 0 && allowInitialWildCards));
251 }
252
253
254
255
256
257
258
259
260 @Override
261 public int compare(String s1, String s2) {
262 String[] keys1 = s1.split(" ");
263 String[] keys2 = s2.split(" ");
264 int[] c1 = new int[4];
265 int[] c2 = new int[4];
266
267 for (int i = 0; i < 4; i++) {
268 c1[i] = parseInt(keys1[i]);
269 c2[i] = parseInt(keys2[i]);
270 }
271
272 if (c1[2] < c2[2]) {
273 return -1;
274 } else if (c1[2] > c2[2]) {
275 return 1;
276 } else if (c1[0] < c2[0]) {
277 return -1;
278 } else if (c1[0] > c2[0]) {
279 return 1;
280 } else if (c1[1] < c2[1]) {
281 return -1;
282 } else if (c1[1] > c2[1]) {
283 return 1;
284 } else if (c1[3] < c2[3]) {
285 return -1;
286 } else if (c1[3] > c2[3]) {
287 return 1;
288 }
289
290 return 0;
291 }
292
293
294
295
296
297
298
299
300 @Override
301 public boolean equals(Object o) {
302 if (this == o) {
303 return true;
304 }
305 if (o == null || getClass() != o.getClass()) {
306 return false;
307 }
308 ImproperTorsionType improperTorsionType = (ImproperTorsionType) o;
309 return Arrays.equals(atomClasses, improperTorsionType.atomClasses);
310 }
311
312
313
314
315
316
317
318
319 @Override
320 public int hashCode() {
321 return Arrays.hashCode(atomClasses);
322 }
323
324
325
326
327
328
329 public void incrementClasses(int increment) {
330 for (int i = 0; i < atomClasses.length; i++) {
331 if (atomClasses[i] != 0) {
332 atomClasses[i] += increment;
333 }
334 }
335 setKey(sortKey(atomClasses));
336 }
337
338
339
340
341
342
343 public boolean noZeroClasses() {
344 return atomClasses[0] != 0 && atomClasses[1] != 0 && atomClasses[3] != 0;
345 }
346
347
348
349
350
351
352 public void patchClasses(HashMap<AtomType, AtomType> typeMap) {
353 int count = 0;
354 for (AtomType newType : typeMap.keySet()) {
355 for (int atomClass : atomClasses) {
356 if (atomClass == newType.atomClass) {
357 count++;
358 }
359 }
360 }
361 if (count > 0 && count < atomClasses.length) {
362 for (AtomType newType : typeMap.keySet()) {
363 for (int i = 0; i < atomClasses.length; i++) {
364 if (atomClasses[i] == newType.atomClass) {
365 AtomType knownType = typeMap.get(newType);
366 atomClasses[i] = knownType.atomClass;
367 }
368 }
369 }
370 setKey(sortKey(atomClasses));
371 }
372 }
373
374
375
376
377
378
379
380
381 @Override
382 public String toString() {
383 StringBuilder imptorsBuffer = new StringBuilder("imptors");
384 for (int i : atomClasses) {
385 imptorsBuffer.append(String.format(" %5d", i));
386 }
387 imptorsBuffer.append(String.format(" %7.3f %7.3f %1d", k, phase, periodicity));
388
389 return imptorsBuffer.toString();
390 }
391 }