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