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 ffx.utilities.FFXProperty;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.Element;
43
44 import java.util.Arrays;
45 import java.util.Comparator;
46 import java.util.HashMap;
47 import java.util.Map;
48 import java.util.logging.Level;
49 import java.util.logging.Logger;
50
51 import static ffx.potential.parameters.ForceField.ForceFieldType.ANGTORS;
52 import static ffx.utilities.Constants.DEGREES_PER_RADIAN;
53 import static ffx.utilities.Constants.KCAL_TO_KJ;
54 import static ffx.utilities.PropertyGroup.EnergyUnitConversion;
55 import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
56 import static java.lang.Double.parseDouble;
57 import static java.lang.Integer.parseInt;
58 import static java.lang.String.format;
59 import static java.util.Arrays.copyOf;
60
61
62
63
64
65
66
67 @FFXProperty(name = "angtors", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
68 [4 integers and 6 reals]
69 Provides the values for a single bond angle bending-torsional angle parameter.
70 The integer modifiers give the atom class numbers for the four kinds of atoms involved in the torsion and its contained angles.
71 The real number modifiers give the force constant values for both angles coupled with 1-, 2- and 3-fold torsional terms.
72 The default units for the force constants are kcal/mole/radian, but this can be controlled via the angtorunit keyword.
73 """)
74 public final class AngleTorsionType extends BaseType implements Comparator<String> {
75
76 public static final double DEFAULT_ANGTOR_UNIT = 1.0 / DEGREES_PER_RADIAN;
77
78
79
80
81 @FFXProperty(name = "angtorunit", propertyGroup = EnergyUnitConversion, defaultValue = "Pi/180", description = """
82 Sets the scale factor needed to convert the energy value computed by the angle bending-torsional angle
83 cross term into units of kcal/mole. The correct value is force field dependent and typically provided in the
84 header of the master force field parameter file.
85 """)
86 public double angtorunit = DEFAULT_ANGTOR_UNIT;
87
88
89
90
91 private static final Logger logger = Logger.getLogger(AngleTorsionType.class.getName());
92
93
94
95 public final int[] atomClasses;
96
97
98
99 public final double[] forceConstants;
100
101
102
103
104
105
106
107 public AngleTorsionType(int[] atomClasses, double[] forceConstants) {
108 super(ANGTORS, sortKey(atomClasses));
109 this.atomClasses = atomClasses;
110 this.forceConstants = forceConstants;
111 }
112
113
114
115
116
117
118
119
120
121 public static AngleTorsionType average(AngleTorsionType angleTorsionType1,
122 AngleTorsionType angleTorsionType2, int[] atomClasses) {
123 if (angleTorsionType1 == null || angleTorsionType2 == null || atomClasses == null) {
124 return null;
125 }
126 int len = angleTorsionType1.forceConstants.length;
127 if (len != angleTorsionType2.forceConstants.length) {
128 return null;
129 }
130 double[] forceConstants = new double[len];
131 for (int i = 0; i < len; i++) {
132 forceConstants[i] =
133 (angleTorsionType1.forceConstants[i] + angleTorsionType2.forceConstants[i]) / 2.0;
134 }
135 return new AngleTorsionType(atomClasses, forceConstants);
136 }
137
138
139
140
141
142
143
144
145 public static AngleTorsionType parse(String input, String[] tokens) {
146 if (tokens.length < 10) {
147 logger.log(Level.WARNING, "Invalid ANGTORS type:\n{0}", input);
148 } else {
149 try {
150 int[] atomClasses = new int[4];
151 atomClasses[0] = parseInt(tokens[1]);
152 atomClasses[1] = parseInt(tokens[2]);
153 atomClasses[2] = parseInt(tokens[3]);
154 atomClasses[3] = parseInt(tokens[4]);
155 double[] constants = new double[6];
156 constants[0] = parseDouble(tokens[5]);
157 constants[1] = parseDouble(tokens[6]);
158 constants[2] = parseDouble(tokens[7]);
159 constants[3] = parseDouble(tokens[8]);
160 constants[4] = parseDouble(tokens[9]);
161 constants[5] = parseDouble(tokens[10]);
162 return new AngleTorsionType(atomClasses, constants);
163 } catch (NumberFormatException e) {
164 String message = "Exception parsing ANGTORS type:\n" + input + "\n";
165 logger.log(Level.SEVERE, message, e);
166 }
167 }
168 return null;
169 }
170
171
172
173
174
175
176
177
178 public static String sortKey(int[] c) {
179 return c[0] + " " + c[1] + " " + c[2] + " " + c[3];
180 }
181
182
183
184
185
186
187 @Override
188 public int compare(String s1, String s2) {
189 String[] keys1 = s1.split(" ");
190 String[] keys2 = s2.split(" ");
191 int[] c1 = new int[4];
192 int[] c2 = new int[4];
193
194 for (int i = 0; i < 4; i++) {
195 c1[i] = parseInt(keys1[i]);
196 c2[i] = parseInt(keys2[i]);
197 }
198
199 if (c1[1] < c2[1]) {
200 return -1;
201 } else if (c1[1] > c2[1]) {
202 return 1;
203 } else if (c1[2] < c2[2]) {
204 return -1;
205 } else if (c1[2] > c2[2]) {
206 return 1;
207 } else if (c1[0] < c2[0]) {
208 return -1;
209 } else if (c1[0] > c2[0]) {
210 return 1;
211 } else if (c1[3] < c2[3]) {
212 return -1;
213 } else if (c1[3] > c2[3]) {
214 return 1;
215 }
216
217 return 0;
218 }
219
220
221
222
223 @Override
224 public boolean equals(Object o) {
225 if (this == o) {
226 return true;
227 }
228 if (o == null || getClass() != o.getClass()) {
229 return false;
230 }
231 AngleTorsionType angleTorsionType = (AngleTorsionType) o;
232 return Arrays.equals(atomClasses, angleTorsionType.atomClasses);
233 }
234
235
236
237
238 @Override
239 public int hashCode() {
240 return Arrays.hashCode(atomClasses);
241 }
242
243
244
245
246
247
248 public void incrementClasses(int increment) {
249 for (int i = 0; i < atomClasses.length; i++) {
250 atomClasses[i] += increment;
251 }
252 setKey(sortKey(atomClasses));
253 }
254
255
256
257
258
259
260
261 public AngleTorsionType patchClasses(HashMap<AtomType, AtomType> typeMap) {
262 int count = 0;
263 int len = atomClasses.length;
264
265
266 for (AtomType newType : typeMap.keySet()) {
267
268 for (int atomClass : atomClasses) {
269 if (atomClass == newType.atomClass) {
270 count++;
271 }
272 }
273 }
274
275
276 if (count == 1 || count == 2) {
277 int[] newClasses = copyOf(atomClasses, len);
278 for (AtomType newType : typeMap.keySet()) {
279 for (int i = 0; i < len; i++) {
280 if (atomClasses[i] == newType.atomClass) {
281 AtomType knownType = typeMap.get(newType);
282 newClasses[i] = knownType.atomClass;
283 }
284 }
285 }
286 return new AngleTorsionType(newClasses, forceConstants);
287 }
288 return null;
289 }
290
291
292
293
294
295
296 @Override
297 public String toString() {
298 return format("angtors %5d %5d %5d %5d %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f",
299 atomClasses[0], atomClasses[1], atomClasses[2], atomClasses[3], forceConstants[0],
300 forceConstants[1], forceConstants[2], forceConstants[3], forceConstants[4],
301 forceConstants[5]);
302 }
303
304
305
306
307
308
309
310
311 public static Element getXMLForce(Document doc, ForceField forceField) {
312 Map<String, AngleTorsionType> types = forceField.getAngleTorsionTypes();
313 if (!types.values().isEmpty()) {
314 Element node = doc.createElement("AmoebaAngleTorsionForce");
315 for (AngleTorsionType angleTorsionType : types.values()) {
316 node.appendChild(angleTorsionType.toXML(doc));
317 }
318 return node;
319 }
320 return null;
321 }
322
323
324
325
326
327
328
329 public Element toXML(Document doc) {
330 Element node = doc.createElement("Torsion");
331 node.setAttribute("class1", format("%d", atomClasses[0]));
332 node.setAttribute("class2", format("%d", atomClasses[1]));
333 node.setAttribute("class3", format("%d", atomClasses[2]));
334 node.setAttribute("class4", format("%d", atomClasses[3]));
335 node.setAttribute("v11", format("%f", forceConstants[0] * KCAL_TO_KJ));
336 node.setAttribute("v12", format("%f", forceConstants[1] * KCAL_TO_KJ));
337 node.setAttribute("v13", format("%f", forceConstants[2] * KCAL_TO_KJ));
338 node.setAttribute("v21", format("%f", forceConstants[3] * KCAL_TO_KJ));
339 node.setAttribute("v22", format("%f", forceConstants[4] * KCAL_TO_KJ));
340 node.setAttribute("v23", format("%f", forceConstants[5] * KCAL_TO_KJ));
341 return node;
342 }
343 }