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 javax.annotation.Nullable;
45 import java.util.Arrays;
46 import java.util.Comparator;
47 import java.util.HashMap;
48 import java.util.Map;
49 import java.util.logging.Level;
50 import java.util.logging.Logger;
51
52 import static ffx.potential.parameters.ForceField.ForceFieldType.PITORS;
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.lang.String.valueOf;
60
61
62
63
64
65
66
67 @FFXProperty(name = "pitors", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
68 [2 integers and 1 real]
69 Provides the values for a single pi-orbital torsional angle potential parameter.
70 The two integer modifiers give the atom class numbers for the atoms involved in the central bond of the torsional angle to be parameterized.
71 The real modifier gives the value of the 2-fold Fourier amplitude for the torsional angle between p-orbitals centered on the defined bond atom classes.
72 The default units for the stretch-torsion force constant can be controlled via the pitorsunit keyword.
73 """)
74 public final class PiOrbitalTorsionType extends BaseType implements Comparator<String> {
75
76
77
78
79 private static final Logger logger = Logger.getLogger(PiOrbitalTorsionType.class.getName());
80
81 public static final double DEFAULT_PITORS_UNIT = 1.0;
82
83
84
85
86 @FFXProperty(name = "pitorsunit", propertyGroup = EnergyUnitConversion, defaultValue = "1.0", description = """
87 Sets the scale factor needed to convert the energy value computed by the pi-orbital torsional angle potential into units of kcal/mole.
88 The correct value is force field dependent and typically provided in the header of the master force field parameter file.
89 """)
90 public double piTorsUnit = DEFAULT_PITORS_UNIT;
91
92
93
94
95 public final int[] atomClasses;
96
97
98
99
100 public double forceConstant;
101
102
103
104
105
106
107
108 public PiOrbitalTorsionType(int[] atomClasses, double forceConstant) {
109 super(PITORS, sortKey(atomClasses));
110 this.atomClasses = atomClasses;
111 this.forceConstant = forceConstant;
112 }
113
114
115
116
117
118
119
120
121
122
123 public static PiOrbitalTorsionType average(@Nullable PiOrbitalTorsionType piOrbitalTorsionType1,
124 @Nullable PiOrbitalTorsionType piOrbitalTorsionType2,
125 @Nullable int[] atomClasses) {
126 if (piOrbitalTorsionType1 == null || piOrbitalTorsionType2 == null || atomClasses == null) {
127 return null;
128 }
129
130 double forceConstant =
131 (piOrbitalTorsionType1.forceConstant + piOrbitalTorsionType2.forceConstant) / 2.0;
132
133 return new PiOrbitalTorsionType(atomClasses, forceConstant);
134 }
135
136
137
138
139
140
141
142
143 public static PiOrbitalTorsionType parse(String input, String[] tokens) {
144 if (tokens.length < 4) {
145 logger.log(Level.WARNING, "Invalid PITORS type:\n{0}", input);
146 } else {
147 try {
148 int[] atomClasses = new int[2];
149 atomClasses[0] = parseInt(tokens[1]);
150 atomClasses[1] = parseInt(tokens[2]);
151 double forceConstant = parseDouble(tokens[3]);
152 return new PiOrbitalTorsionType(atomClasses, forceConstant);
153 } catch (NumberFormatException e) {
154 String message = "Exception parsing PITORS type:\n" + input + "\n";
155 logger.log(Level.SEVERE, message, e);
156 }
157 }
158 return null;
159 }
160
161
162
163
164
165
166
167 public static String sortKey(int[] c) {
168 if (c == null || c.length != 2) {
169 return null;
170 }
171
172 int temp;
173 if (c[1] <= c[0]) {
174 temp = c[1];
175 c[1] = c[0];
176 c[0] = temp;
177 }
178 return c[0] + " " + c[1];
179 }
180
181
182
183
184 @Override
185 public int compare(String s1, String s2) {
186 String[] keys1 = s1.split(" ");
187 String[] keys2 = s2.split(" ");
188
189 for (int i = 0; i < 2; i++) {
190 int c1 = parseInt(keys1[i]);
191 int c2 = parseInt(keys2[i]);
192 if (c1 < c2) {
193 return -1;
194 } else if (c1 > c2) {
195 return 1;
196 }
197 }
198 return 0;
199 }
200
201
202
203
204 @Override
205 public boolean equals(Object o) {
206 if (this == o) {
207 return true;
208 }
209 if (o == null || getClass() != o.getClass()) {
210 return false;
211 }
212 PiOrbitalTorsionType piOrbitalTorsionType = (PiOrbitalTorsionType) o;
213 return Arrays.equals(atomClasses, piOrbitalTorsionType.atomClasses);
214 }
215
216
217
218
219 @Override
220 public int hashCode() {
221 return Arrays.hashCode(atomClasses);
222 }
223
224
225
226
227
228
229 public void incrementClasses(int increment) {
230 for (int i = 0; i < atomClasses.length; i++) {
231 atomClasses[i] += increment;
232 }
233 setKey(sortKey(atomClasses));
234 }
235
236
237
238
239
240
241
242 public PiOrbitalTorsionType patchClasses(HashMap<AtomType, AtomType> typeMap) {
243 int count = 0;
244 int len = atomClasses.length;
245
246
247 for (AtomType newType : typeMap.keySet()) {
248
249 for (int atomClass : atomClasses) {
250 if (atomClass == newType.atomClass) {
251 count++;
252 }
253 }
254 }
255
256
257 if (count == 1) {
258 int[] newClasses = Arrays.copyOf(atomClasses, len);
259 for (AtomType newType : typeMap.keySet()) {
260 for (int i = 0; i < len; i++) {
261 if (atomClasses[i] == newType.atomClass) {
262 AtomType knownType = typeMap.get(newType);
263 newClasses[i] = knownType.atomClass;
264 }
265 }
266 }
267 return new PiOrbitalTorsionType(newClasses, forceConstant);
268 }
269 return null;
270 }
271
272
273
274
275
276
277 @Override
278 public String toString() {
279 return format("pitors %5d %5d %4.2f", atomClasses[0], atomClasses[1], forceConstant);
280 }
281
282
283
284
285
286
287
288
289 public static Element getXMLElement(Document doc, ForceField forceField) {
290 Map<String, PiOrbitalTorsionType> types = forceField.getPiOrbitalTorsionTypes();
291 if (!types.values().isEmpty()) {
292 Element node = doc.createElement("AmoebaPiTorsionForce");
293 node.setAttribute("piTorsionUnit", valueOf(forceField.getDouble("pitorsunit", DEFAULT_PITORS_UNIT)));
294 for (PiOrbitalTorsionType piOrbitalTorsionType : types.values()) {
295 node.appendChild(piOrbitalTorsionType.toXML(doc));
296 }
297 return node;
298 }
299 return null;
300 }
301
302
303
304
305
306
307
308 public Element toXML(Document doc) {
309 Element node = doc.createElement("PiTorsion");
310 node.setAttribute("class1", format("%d", atomClasses[0]));
311 node.setAttribute("class2", format("%d", atomClasses[1]));
312
313 node.setAttribute("k", format("%f", forceConstant * KCAL_TO_KJ));
314 return node;
315 }
316
317 }