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