/src/openbabel/src/forcefields/forcefielduff.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | forcefielduff.cpp - UFF force field. |
3 | | |
4 | | Copyright (C) 2007-2008 by Geoffrey Hutchison |
5 | | Some portions Copyright (C) 2006-2008 by Tim Vandermeersch |
6 | | |
7 | | This file is part of the Open Babel project. |
8 | | For more information, see <http://openbabel.org/> |
9 | | |
10 | | This program is free software; you can redistribute it and/or modify |
11 | | it under the terms of the GNU General Public License as published by |
12 | | the Free Software Foundation version 2 of the License. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | ************************************************************************/ |
19 | | |
20 | | #include <openbabel/babelconfig.h> |
21 | | #include <openbabel/mol.h> |
22 | | #include <openbabel/locale.h> |
23 | | #include <openbabel/elements.h> |
24 | | #include <openbabel/atom.h> |
25 | | #include <openbabel/obiter.h> |
26 | | #include <openbabel/generic.h> |
27 | | #include <openbabel/bond.h> |
28 | | #include <openbabel/parsmart.h> |
29 | | |
30 | | #include <cstdlib> |
31 | | |
32 | | #include "forcefielduff.h" |
33 | | |
34 | | |
35 | | using namespace std; |
36 | | |
37 | | // This implementation was created based on open code and reference websites: |
38 | | // http://towhee.sourceforge.net/forcefields/uff.html |
39 | | // http://rdkit.org/ |
40 | | // http://franklin.chm.colostate.edu/mmac/uff.html |
41 | | // (for the last, use the Wayback Machine: http://www.archive.org/ |
42 | | |
43 | | // As well, the main UFF paper: |
44 | | // Rappe, A. K., et. al.; J. Am. Chem. Soc. (1992) 114(25) p. 10024-10035. |
45 | | |
46 | | namespace OpenBabel { |
47 | | |
48 | | template<bool gradients> |
49 | | void OBFFBondCalculationUFF::Compute() |
50 | 0 | { |
51 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b)) { |
52 | 0 | energy = 0.0; |
53 | 0 | return; |
54 | 0 | } |
55 | | |
56 | 0 | vector3 vab, da, db; |
57 | 0 | double delta2, dE; |
58 | |
|
59 | 0 | if (gradients) { |
60 | 0 | rab = OBForceField::VectorBondDerivative(pos_a, pos_b, force_a, force_b); |
61 | 0 | } else { |
62 | 0 | rab = OBForceField::VectorDistance(pos_a, pos_b); |
63 | 0 | } |
64 | | |
65 | | // Harmonic bond stretching |
66 | 0 | delta = rab - r0; // we pre-compute the r0 below |
67 | 0 | delta2 = delta * delta; |
68 | 0 | energy = kb * delta2; // we fold the 1/2 into kb below |
69 | |
|
70 | 0 | if (gradients) { |
71 | 0 | dE = 2.0 * kb * delta; |
72 | 0 | OBForceField::VectorSelfMultiply(force_a, dE); |
73 | 0 | OBForceField::VectorSelfMultiply(force_b, dE); |
74 | 0 | } |
75 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFBondCalculationUFF::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFBondCalculationUFF::Compute<false>() |
76 | | |
77 | | template<bool gradients> |
78 | | double OBForceFieldUFF::E_Bond() |
79 | 0 | { |
80 | 0 | vector<OBFFBondCalculationUFF>::iterator i; |
81 | 0 | double energy = 0.0; |
82 | |
|
83 | 0 | IF_OBFF_LOGLVL_HIGH { |
84 | 0 | OBFFLog("\nB O N D S T R E T C H I N G\n\n"); |
85 | 0 | OBFFLog("ATOM TYPES BOND BOND IDEAL FORCE\n"); |
86 | 0 | OBFFLog(" I J TYPE LENGTH LENGTH CONSTANT DELTA ENERGY\n"); |
87 | 0 | OBFFLog("------------------------------------------------------------------------\n"); |
88 | 0 | } |
89 | |
|
90 | 0 | for (i = _bondcalculations.begin(); i != _bondcalculations.end(); ++i) { |
91 | |
|
92 | 0 | i->template Compute<gradients>(); |
93 | 0 | energy += i->energy; |
94 | |
|
95 | 0 | if (gradients) { |
96 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
97 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
98 | 0 | } |
99 | |
|
100 | 0 | IF_OBFF_LOGLVL_HIGH { |
101 | 0 | snprintf(_logbuf, BUFF_SIZE, "%-5s %-5s %4.2f%8.3f %8.3f %8.3f %8.3f %8.3f\n", |
102 | 0 | (*i).a->GetType(), (*i).b->GetType(), |
103 | 0 | (*i).bt, (*i).rab, (*i).r0, (*i).kb, (*i).delta, (*i).energy); |
104 | 0 | OBFFLog(_logbuf); |
105 | 0 | } |
106 | 0 | } |
107 | |
|
108 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
109 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL BOND STRETCHING ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
110 | 0 | OBFFLog(_logbuf); |
111 | 0 | } |
112 | 0 | return energy; |
113 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_Bond<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_Bond<false>() |
114 | | |
115 | | template<bool gradients> |
116 | | void OBFFAngleCalculationUFF::Compute() |
117 | 0 | { |
118 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b, idx_c)) { |
119 | 0 | energy = 0.0; |
120 | 0 | return; |
121 | 0 | } |
122 | | |
123 | 0 | vector3 da, db, dc; |
124 | 0 | double dE; |
125 | |
|
126 | 0 | if (gradients) { |
127 | 0 | theta = OBForceField::VectorAngleDerivative(pos_a, pos_b, pos_c, force_a, force_b, force_c); |
128 | | |
129 | | // Supply a small nudge if the angle is degenerate |
130 | 0 | if (theta < 2.5 || theta > 357.5) { |
131 | 0 | vector3 v1; |
132 | 0 | v1.randomUnitVector(); |
133 | 0 | for (int i = 0; i < 3; ++i) |
134 | 0 | force_a[i] += v1[i]*0.1; |
135 | 0 | } |
136 | 0 | theta *= DEG_TO_RAD; |
137 | 0 | } else { |
138 | 0 | theta = a->GetAngle(b, c) * DEG_TO_RAD; |
139 | 0 | } |
140 | |
|
141 | 0 | if (!isfinite(theta)) |
142 | 0 | theta = 0.0; // doesn't explain why GetAngle is returning NaN but solves it for us |
143 | |
|
144 | 0 | double cosT; |
145 | |
|
146 | 0 | switch (coord) { |
147 | 0 | case 1: // sp -- linear case, minima at 180 degrees, max (amplitude 2*ka) at 0, 360 |
148 | | // Fixed typo from Rappe paper (i.e., it's NOT 1 - cosT) |
149 | 0 | energy = ka*(1.0 + cos(theta)); |
150 | 0 | break; |
151 | 0 | case 2: // sp2 -- trigonal planar or equatorial plane of trigonal bipyramidal |
152 | 0 | case 4: // square planar |
153 | 0 | case 6: // octahedral |
154 | | // ka already is pre-computed as ka/n^2 to save CPU cycles |
155 | | // UNLIKE Rappe paper, we add a penalty for angles close to zero, based on ESFF |
156 | | // i.e., if the angle is less than approx theta0, energy goes up exponentially |
157 | 0 | energy = ka * (1 - cos(n*theta)) + exp(-20.0*(theta - theta0 + 0.25)); |
158 | 0 | break; |
159 | 0 | case 7: // IF7 pentagonal -- pentagonal bipyramidal |
160 | | /* theta = 1/5 * 2 pi. cosT = .30901699 |
161 | | * theta = 2/5 * 2 pi. cosT = -.80901699 |
162 | | * theta = 3/5 * 2 pi. cosT = -.80901699 |
163 | | * theta = 4/5 * 2 pi. cosT = .30901699 |
164 | | */ |
165 | 0 | cosT = cos(theta); |
166 | 0 | energy = ka * c1 * (cosT - .30901699) * (cosT - .30906199) * (cosT + .80901699) * (cosT + .8091699); |
167 | 0 | break; |
168 | 0 | default: // general (sp3) coordination |
169 | 0 | cosT = cos(theta); |
170 | 0 | energy = ka*(c0 + c1*cosT + c2*(2.0*cosT*cosT - 1.0)); // use cos 2t = (2cos^2 - 1) |
171 | 0 | } |
172 | | |
173 | 0 | if (gradients) { |
174 | 0 | double sinT; |
175 | |
|
176 | 0 | switch (coord) { |
177 | 0 | case 1: // sp -- linear case |
178 | 0 | dE = -ka * sin(theta); |
179 | 0 | break; |
180 | 0 | case 2: // sp2 -- trigonal planar |
181 | 0 | case 6: // octahedral |
182 | 0 | case 4: // square planar |
183 | 0 | dE = ka * n * sin(n * theta) -20.0 * exp(-20.0*(theta - theta0 + 0.25)); |
184 | 0 | break; |
185 | 0 | case 7: // pentagonal bipyramidal |
186 | 0 | sinT = sin(theta); |
187 | 0 | cosT = cos(theta); |
188 | 0 | dE = |
189 | 0 | c1 * -ka * (2 * sinT * (cosT - .30906199) * (cosT + .80901699) * (cosT + .8091699) + |
190 | 0 | 2 * sinT * (cosT - .30901699) * (cosT - .30906199) * (cosT + .8091699)); |
191 | | //dE = -ka * c1 * sin(5*theta) * 5; |
192 | 0 | break; |
193 | 0 | default: // general (sp3) coordination |
194 | 0 | dE = -ka * (c1*sin(theta) + 2.0 * c2*sin(2 * theta)); |
195 | 0 | } |
196 | | |
197 | 0 | OBForceField::VectorSelfMultiply(force_a, dE); |
198 | 0 | OBForceField::VectorSelfMultiply(force_b, dE); |
199 | 0 | OBForceField::VectorSelfMultiply(force_c, dE); |
200 | 0 | } |
201 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFAngleCalculationUFF::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFAngleCalculationUFF::Compute<false>() |
202 | | |
203 | | template<bool gradients> |
204 | | double OBForceFieldUFF::E_Angle() |
205 | 0 | { |
206 | 0 | vector<OBFFAngleCalculationUFF>::iterator i; |
207 | 0 | double energy = 0.0; |
208 | |
|
209 | 0 | IF_OBFF_LOGLVL_HIGH { |
210 | 0 | OBFFLog("\nA N G L E B E N D I N G\n\n"); |
211 | 0 | OBFFLog("ATOM TYPES VALENCE IDEAL FORCE\n"); |
212 | 0 | OBFFLog(" I J K ANGLE ANGLE CONSTANT DELTA ENERGY\n"); |
213 | 0 | OBFFLog("-----------------------------------------------------------------------------\n"); |
214 | 0 | } |
215 | |
|
216 | 0 | for (i = _anglecalculations.begin(); i != _anglecalculations.end(); ++i) { |
217 | |
|
218 | 0 | i->template Compute<gradients>(); |
219 | 0 | energy += i->energy; |
220 | |
|
221 | 0 | if (gradients) { |
222 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
223 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
224 | 0 | AddGradient((*i).force_c, (*i).idx_c); |
225 | 0 | } |
226 | |
|
227 | 0 | IF_OBFF_LOGLVL_HIGH { |
228 | 0 | snprintf(_logbuf, BUFF_SIZE, "%-5s %-5s %-5s%8.3f %8.3f %8.3f %8.3f %8.3f\n", (*i).a->GetType(), (*i).b->GetType(), |
229 | 0 | (*i).c->GetType(), (*i).theta * RAD_TO_DEG, (*i).theta0, (*i).ka, (*i).delta, (*i).energy); |
230 | 0 | OBFFLog(_logbuf); |
231 | 0 | } |
232 | 0 | } |
233 | |
|
234 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
235 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL ANGLE BENDING ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
236 | 0 | OBFFLog(_logbuf); |
237 | 0 | } |
238 | 0 | return energy; |
239 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_Angle<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_Angle<false>() |
240 | | |
241 | | template<bool gradients> |
242 | | void OBFFTorsionCalculationUFF::Compute() |
243 | 0 | { |
244 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b, idx_c, idx_d)) { |
245 | 0 | energy = 0.0; |
246 | 0 | return; |
247 | 0 | } |
248 | | |
249 | 0 | vector3 da, db, dc, dd; |
250 | 0 | double cosine; |
251 | 0 | double dE; |
252 | |
|
253 | 0 | if (gradients) { |
254 | 0 | tor = OBForceField::VectorTorsionDerivative(pos_a, pos_b, pos_c, pos_d, |
255 | 0 | force_a, force_b, force_c, force_d); |
256 | 0 | if (!isfinite(tor)) |
257 | 0 | tor = 1.0e-3; |
258 | 0 | tor *= DEG_TO_RAD; |
259 | 0 | } else { |
260 | 0 | vector3 vab, vbc, vcd, abbc, bccd; |
261 | 0 | vab = a->GetVector() - b->GetVector(); |
262 | 0 | vbc = b->GetVector() - c->GetVector(); |
263 | 0 | vcd = c->GetVector() - d->GetVector(); |
264 | 0 | abbc = cross(vab, vbc); |
265 | 0 | bccd = cross(vbc, vcd); |
266 | |
|
267 | 0 | double dotAbbcBccd = dot(abbc,bccd); |
268 | 0 | tor = acos(dotAbbcBccd / (abbc.length() * bccd.length())); |
269 | 0 | if (IsNearZero(dotAbbcBccd) || !isfinite(tor)) { // stop any NaN or infinity |
270 | 0 | tor = 1.0e-3; // rather than NaN |
271 | 0 | } |
272 | 0 | else if (dotAbbcBccd > 0.0) { |
273 | 0 | tor = -tor; |
274 | 0 | } |
275 | 0 | } |
276 | |
|
277 | 0 | cosine = cos(tor * n); |
278 | 0 | energy = V * (1.0 - cosNPhi0*cosine); |
279 | |
|
280 | 0 | if (gradients) { |
281 | 0 | dE = -(V * n * cosNPhi0 * sin(n * tor)); |
282 | 0 | OBForceField::VectorSelfMultiply(force_a, dE); |
283 | 0 | OBForceField::VectorSelfMultiply(force_b, dE); |
284 | 0 | OBForceField::VectorSelfMultiply(force_c, dE); |
285 | 0 | OBForceField::VectorSelfMultiply(force_d, dE); |
286 | 0 | } |
287 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFTorsionCalculationUFF::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFTorsionCalculationUFF::Compute<false>() |
288 | | |
289 | | template<bool gradients> |
290 | | double OBForceFieldUFF::E_Torsion() |
291 | 0 | { |
292 | 0 | vector<OBFFTorsionCalculationUFF>::iterator i; |
293 | 0 | double energy = 0.0; |
294 | |
|
295 | 0 | IF_OBFF_LOGLVL_HIGH { |
296 | 0 | OBFFLog("\nT O R S I O N A L\n\n"); |
297 | 0 | OBFFLog("----ATOM TYPES----- FORCE TORSION\n"); |
298 | 0 | OBFFLog(" I J K L CONSTANT ANGLE ENERGY\n"); |
299 | 0 | OBFFLog("----------------------------------------------------------------\n"); |
300 | 0 | } |
301 | |
|
302 | 0 | for (i = _torsioncalculations.begin(); i != _torsioncalculations.end(); ++i) { |
303 | |
|
304 | 0 | i->template Compute<gradients>(); |
305 | 0 | energy += i->energy; |
306 | |
|
307 | 0 | if (gradients) { |
308 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
309 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
310 | 0 | AddGradient((*i).force_c, (*i).idx_c); |
311 | 0 | AddGradient((*i).force_d, (*i).idx_d); |
312 | 0 | } |
313 | |
|
314 | 0 | IF_OBFF_LOGLVL_HIGH { |
315 | 0 | snprintf(_logbuf, BUFF_SIZE, "%-5s %-5s %-5s %-5s%6.3f %8.3f %8.3f\n", |
316 | 0 | (*i).a->GetType(), (*i).b->GetType(), |
317 | 0 | (*i).c->GetType(), (*i).d->GetType(), (*i).V, |
318 | 0 | (*i).tor * RAD_TO_DEG, (*i).energy); |
319 | 0 | OBFFLog(_logbuf); |
320 | 0 | } |
321 | 0 | } |
322 | |
|
323 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
324 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL TORSIONAL ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
325 | 0 | OBFFLog(_logbuf); |
326 | 0 | } |
327 | |
|
328 | 0 | return energy; |
329 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_Torsion<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_Torsion<false>() |
330 | | |
331 | | /* |
332 | | // a |
333 | | // \ |
334 | | // b---d plane = a-b-c |
335 | | // / |
336 | | // c |
337 | | */ |
338 | | template<bool gradients> |
339 | | void OBFFOOPCalculationUFF::Compute() |
340 | 0 | { |
341 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b, idx_c, idx_d)) { |
342 | 0 | energy = 0.0; |
343 | 0 | return; |
344 | 0 | } |
345 | | |
346 | 0 | vector3 da, db, dc, dd; |
347 | 0 | double dE; |
348 | |
|
349 | 0 | if (gradients) { |
350 | 0 | angle = OBForceField::VectorOOPDerivative(pos_a, pos_b, pos_c, pos_d, |
351 | 0 | force_a, force_b, force_c, force_d); |
352 | 0 | angle *= DEG_TO_RAD; |
353 | |
|
354 | 0 | if (!isfinite(angle)) |
355 | 0 | angle = 0.0; // doesn't explain why GetAngle is returning NaN but solves it for us; |
356 | | |
357 | | // somehow we already get the -1 from the OOPDeriv -- so we'll omit it here |
358 | 0 | dE = koop * (c1*sin(angle) + 2.0 * c2 * sin(2.0*angle)); |
359 | 0 | OBForceField::VectorSelfMultiply(force_a, dE); |
360 | 0 | OBForceField::VectorSelfMultiply(force_b, dE); |
361 | 0 | OBForceField::VectorSelfMultiply(force_c, dE); |
362 | 0 | OBForceField::VectorSelfMultiply(force_d, dE); |
363 | 0 | } else { |
364 | 0 | angle = DEG_TO_RAD*Point2PlaneAngle(d->GetVector(), a->GetVector(), b->GetVector(), c->GetVector()); |
365 | 0 | if (!isfinite(angle)) |
366 | 0 | angle = 0.0; // doesn't explain why GetAngle is returning NaN but solves it for us; |
367 | 0 | } |
368 | |
|
369 | 0 | energy = koop * (c0 + c1 * cos(angle) + c2 * cos(2.0*angle)); |
370 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFOOPCalculationUFF::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFOOPCalculationUFF::Compute<false>() |
371 | | |
372 | | template<bool gradients> |
373 | | double OBForceFieldUFF::E_OOP() |
374 | 0 | { |
375 | 0 | vector<OBFFOOPCalculationUFF>::iterator i; |
376 | 0 | double energy = 0.0; |
377 | |
|
378 | 0 | IF_OBFF_LOGLVL_HIGH { |
379 | 0 | OBFFLog("\nO U T - O F - P L A N E B E N D I N G\n\n"); |
380 | 0 | OBFFLog("ATOM TYPES OOP FORCE \n"); |
381 | 0 | OBFFLog(" I J K L ANGLE CONSTANT ENERGY\n"); |
382 | 0 | OBFFLog("----------------------------------------------------------\n"); |
383 | 0 | } |
384 | |
|
385 | 0 | for (i = _oopcalculations.begin(); i != _oopcalculations.end(); ++i) { |
386 | 0 | i->template Compute<gradients>(); |
387 | 0 | energy += i->energy; |
388 | |
|
389 | 0 | if (gradients) { |
390 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
391 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
392 | 0 | AddGradient((*i).force_c, (*i).idx_c); |
393 | 0 | AddGradient((*i).force_d, (*i).idx_d); |
394 | 0 | } |
395 | |
|
396 | 0 | IF_OBFF_LOGLVL_HIGH { |
397 | 0 | snprintf(_logbuf, BUFF_SIZE, "%-5s %-5s %-5s %-5s%8.3f %8.3f %8.3f\n", (*i).a->GetType(), (*i).b->GetType(), (*i).c->GetType(), (*i).d->GetType(), |
398 | 0 | (*i).angle * RAD_TO_DEG, (*i).koop, (*i).energy); |
399 | 0 | OBFFLog(_logbuf); |
400 | 0 | } |
401 | 0 | } |
402 | |
|
403 | 0 | IF_OBFF_LOGLVL_HIGH { |
404 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL OUT-OF-PLANE BENDING ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
405 | 0 | OBFFLog(_logbuf); |
406 | 0 | } |
407 | 0 | return energy; |
408 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_OOP<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_OOP<false>() |
409 | | |
410 | | template<bool gradients> |
411 | | void OBFFVDWCalculationUFF::Compute() |
412 | 0 | { |
413 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b)) { |
414 | 0 | energy = 0.0; |
415 | 0 | return; |
416 | 0 | } |
417 | | |
418 | 0 | vector3 da, db; |
419 | 0 | double term6, term12, dE, term7, term13, rabSquared = 0.0; |
420 | |
|
421 | 0 | if (gradients) { |
422 | 0 | rab = OBForceField::VectorDistanceDerivative(pos_a, pos_b, force_a, force_b); |
423 | |
|
424 | 0 | if (rab < 1.0e-3) |
425 | 0 | rab = 1.0e-3; |
426 | |
|
427 | 0 | rabSquared = SQUARE(rab); |
428 | 0 | } else { |
429 | | // Get distance squared (saves a sqrt and multiply) |
430 | | // for every energy evaluation |
431 | 0 | double ab[3]; |
432 | 0 | for (unsigned int c = 0; c < 3; ++c) |
433 | 0 | rabSquared += SQUARE(a->GetCoordinate()[c] - b->GetCoordinate()[c]); |
434 | | |
435 | | // make sure the energy doesn't blow up |
436 | 0 | if (rabSquared < 1.0e-5) |
437 | 0 | rabSquared = 1.0e-5; |
438 | 0 | } |
439 | | |
440 | | // TODO: This actually should include zetas (not always exactly 6-12 for VDW paper) |
441 | |
|
442 | 0 | term6 = kaSquared / rabSquared; // ^2 |
443 | 0 | term6 = term6 * term6 * term6; // ^6 |
444 | 0 | term12 = term6 * term6; // ^12 |
445 | |
|
446 | 0 | energy = kab * ((term12) - (2.0 * term6)); |
447 | |
|
448 | 0 | if (gradients) { |
449 | 0 | term13 = term12 / rab; // ^13 |
450 | 0 | term7 = term6 / rab; // ^7 |
451 | 0 | dE = kab * 12.0 * (term7 - term13); |
452 | 0 | OBForceField::VectorSelfMultiply(force_a, dE); |
453 | 0 | OBForceField::VectorSelfMultiply(force_b, dE); |
454 | 0 | } |
455 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFVDWCalculationUFF::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFVDWCalculationUFF::Compute<false>() |
456 | | |
457 | | template<bool gradients> |
458 | | double OBForceFieldUFF::E_VDW() |
459 | 0 | { |
460 | 0 | vector<OBFFVDWCalculationUFF>::iterator i; |
461 | 0 | double energy = 0.0; |
462 | |
|
463 | 0 | IF_OBFF_LOGLVL_HIGH { |
464 | 0 | OBFFLog("\nV A N D E R W A A L S\n\n"); |
465 | 0 | OBFFLog("ATOM TYPES\n"); |
466 | 0 | OBFFLog(" I J Rij kij ENERGY\n"); |
467 | 0 | OBFFLog("-----------------------------------------\n"); |
468 | | // XX XX -000.000 -000.000 -000.000 -000.000 |
469 | 0 | } |
470 | |
|
471 | 0 | unsigned int j = 0; |
472 | 0 | for (i = _vdwcalculations.begin(); i != _vdwcalculations.end(); ++i, ++j) { |
473 | | // Cut-off check |
474 | 0 | if (_cutoff) |
475 | 0 | if (!_vdwpairs.BitIsSet(j)) |
476 | 0 | continue; |
477 | | |
478 | 0 | i->template Compute<gradients>(); |
479 | 0 | energy += i->energy; |
480 | |
|
481 | 0 | if (gradients) { |
482 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
483 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
484 | 0 | } |
485 | |
|
486 | 0 | IF_OBFF_LOGLVL_HIGH { |
487 | 0 | snprintf(_logbuf, BUFF_SIZE, "%-5s %-5s %8.3f %8.3f %8.3f\n", (*i).a->GetType(), (*i).b->GetType(), |
488 | 0 | (*i).rab, (*i).kab, (*i).energy); |
489 | 0 | OBFFLog(_logbuf); |
490 | 0 | } |
491 | 0 | } |
492 | |
|
493 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
494 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL VAN DER WAALS ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
495 | 0 | OBFFLog(_logbuf); |
496 | 0 | } |
497 | |
|
498 | 0 | return energy; |
499 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_VDW<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_VDW<false>() |
500 | | |
501 | | template<bool gradients> |
502 | | void OBFFElectrostaticCalculationUFF::Compute() |
503 | 0 | { |
504 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b)) { |
505 | 0 | energy = 0.0; |
506 | 0 | return; |
507 | 0 | } |
508 | | |
509 | 0 | vector3 da, db; |
510 | 0 | double dE, rab2; |
511 | |
|
512 | 0 | if (gradients) { |
513 | 0 | da = a->GetVector(); |
514 | 0 | db = b->GetVector(); |
515 | 0 | rab = OBForceField::VectorLengthDerivative(da, db); |
516 | 0 | } else |
517 | 0 | rab = a->GetDistance(b); |
518 | |
|
519 | 0 | if (IsNearZero(rab, 1.0e-3)) |
520 | 0 | rab = 1.0e-3; |
521 | |
|
522 | 0 | energy = qq / rab; |
523 | |
|
524 | 0 | if (gradients) { |
525 | 0 | rab2 = rab * rab; |
526 | 0 | dE = -qq / rab2; |
527 | 0 | da *= dE; |
528 | 0 | db *= dE; |
529 | 0 | da.Get(force_a); |
530 | 0 | db.Get(force_b); |
531 | 0 | } |
532 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFElectrostaticCalculationUFF::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFElectrostaticCalculationUFF::Compute<false>() |
533 | | |
534 | | template<bool gradients> |
535 | | double OBForceFieldUFF::E_Electrostatic() |
536 | 0 | { |
537 | 0 | vector<OBFFElectrostaticCalculationUFF>::iterator i; |
538 | 0 | double energy = 0.0; |
539 | |
|
540 | 0 | IF_OBFF_LOGLVL_HIGH { |
541 | 0 | OBFFLog("\nE L E C T R O S T A T I C I N T E R A C T I O N S\n\n"); |
542 | 0 | OBFFLog("ATOM TYPES\n"); |
543 | 0 | OBFFLog(" I J Rij 332.17*QiQj ENERGY\n"); |
544 | 0 | OBFFLog("-------------------------------------------\n"); |
545 | | // XX XX -000.000 -000.000 -000.000 |
546 | 0 | } |
547 | |
|
548 | 0 | unsigned int j = 0; |
549 | 0 | for (i = _electrostaticcalculations.begin(); i != _electrostaticcalculations.end(); ++i, ++j) { |
550 | | // Cut-off check |
551 | 0 | if (_cutoff) |
552 | 0 | if (!_elepairs.BitIsSet(j)) |
553 | 0 | continue; |
554 | | |
555 | 0 | i->template Compute<gradients>(); |
556 | 0 | energy += i->energy; |
557 | |
|
558 | 0 | if (gradients) { |
559 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
560 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
561 | 0 | } |
562 | |
|
563 | 0 | IF_OBFF_LOGLVL_HIGH { |
564 | 0 | snprintf(_logbuf, BUFF_SIZE, "%-5s %-5s %8.3f %8.3f %8.3f\n", (*i).a->GetType(), (*i).b->GetType(), |
565 | 0 | (*i).rab, (*i).qq, (*i).energy); |
566 | 0 | OBFFLog(_logbuf); |
567 | 0 | } |
568 | 0 | } |
569 | |
|
570 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
571 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL ELECTROSTATIC ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
572 | 0 | OBFFLog(_logbuf); |
573 | 0 | } |
574 | |
|
575 | 0 | return energy; |
576 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_Electrostatic<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldUFF::E_Electrostatic<false>() |
577 | | |
578 | | //*********************************************** |
579 | | //Make a global instance |
580 | | OBForceFieldUFF theForceFieldUFF("UFF", true); |
581 | | //*********************************************** |
582 | | |
583 | | OBForceFieldUFF::~OBForceFieldUFF() |
584 | 0 | { |
585 | 0 | } |
586 | | |
587 | | OBForceFieldUFF &OBForceFieldUFF::operator=(OBForceFieldUFF &src) |
588 | 0 | { |
589 | 0 | _mol = src._mol; |
590 | |
|
591 | 0 | _ffparams = src._ffparams; |
592 | |
|
593 | 0 | _bondcalculations = src._bondcalculations; |
594 | 0 | _anglecalculations = src._anglecalculations; |
595 | 0 | _torsioncalculations = src._torsioncalculations; |
596 | 0 | _oopcalculations = src._oopcalculations; |
597 | 0 | _vdwcalculations = src._vdwcalculations; |
598 | 0 | _electrostaticcalculations = src._electrostaticcalculations; |
599 | 0 | _init = src._init; |
600 | |
|
601 | 0 | return *this; |
602 | 0 | } |
603 | | |
604 | | double CalculateBondDistance(OBFFParameter *i, OBFFParameter *j, double bondorder) |
605 | 0 | { |
606 | 0 | double ri, rj; |
607 | 0 | double chiI, chiJ; |
608 | 0 | double rbo, ren; |
609 | 0 | ri = i->_dpar[0]; |
610 | 0 | rj = j->_dpar[0]; |
611 | 0 | chiI = i->_dpar[8]; |
612 | 0 | chiJ = j->_dpar[8]; |
613 | | |
614 | | // Precompute the equilibrium bond distance |
615 | | // From equation 3 |
616 | 0 | rbo = -0.1332*(ri+rj)*log(bondorder); |
617 | | // From equation 4 |
618 | 0 | ren = ri*rj*(pow((sqrt(chiI) - sqrt(chiJ)),2.0)) / (chiI*ri + chiJ*rj); |
619 | | // From equation 2 |
620 | | // NOTE: See http://towhee.sourceforge.net/forcefields/uff.html |
621 | | // There is a typo in the published paper |
622 | 0 | return(ri + rj + rbo - ren); |
623 | 0 | } |
624 | | |
625 | | bool OBForceFieldUFF::SetupVDWCalculation(OBAtom *a, OBAtom *b, OBFFVDWCalculationUFF &vdwcalc) |
626 | 0 | { |
627 | 0 | OBFFParameter *parameterA, *parameterB; |
628 | 0 | parameterA = GetParameterUFF(a->GetType(), _ffparams); |
629 | 0 | parameterB = GetParameterUFF(b->GetType(), _ffparams); |
630 | |
|
631 | 0 | if (parameterA == nullptr || parameterB == nullptr) { |
632 | 0 | IF_OBFF_LOGLVL_LOW { |
633 | 0 | snprintf(_logbuf, BUFF_SIZE, " COULD NOT FIND PARAMETERS FOR VDW INTERACTION %d-%d (IDX)...\n", |
634 | 0 | a->GetIdx(), b->GetIdx()); |
635 | 0 | OBFFLog(_logbuf); |
636 | 0 | } |
637 | 0 | return false; |
638 | 0 | } |
639 | | |
640 | 0 | vdwcalc.Ra = parameterA->_dpar[2]; |
641 | 0 | vdwcalc.ka = parameterA->_dpar[3]; |
642 | 0 | vdwcalc.Rb = parameterB->_dpar[2]; |
643 | 0 | vdwcalc.kb = parameterB->_dpar[3]; |
644 | |
|
645 | 0 | vdwcalc.a = &*a; |
646 | 0 | vdwcalc.b = &*b; |
647 | | |
648 | | //this calculations only need to be done once for each pair, |
649 | | //we do them now and save them for later use |
650 | 0 | vdwcalc.kab = KCAL_TO_KJ * sqrt(vdwcalc.ka * vdwcalc.kb); |
651 | | |
652 | | // 1-4 scaling |
653 | | // This isn't mentioned in the UFF paper, but is common for other methods |
654 | | // if (a->IsOneFour(b)) |
655 | | // vdwcalc.kab *= 0.5; |
656 | | |
657 | | // ka now represents the xij in equation 20 -- the expected vdw distance |
658 | 0 | vdwcalc.kaSquared = (vdwcalc.Ra * vdwcalc.Rb); |
659 | 0 | vdwcalc.ka = sqrt(vdwcalc.kaSquared); |
660 | |
|
661 | 0 | vdwcalc.SetupPointers(); |
662 | 0 | return true; |
663 | 0 | } |
664 | | |
665 | | int GetCoordination(OBAtom *b, int ipar) |
666 | 0 | { |
667 | 0 | int coordination; |
668 | | |
669 | | // Work out coordination |
670 | | // including possible hypervalent compounds |
671 | 0 | int valenceElectrons = 0; |
672 | 0 | switch(b->GetAtomicNum()) |
673 | 0 | { |
674 | 0 | case 15: |
675 | 0 | case 33: |
676 | 0 | case 51: |
677 | 0 | case 83: |
678 | | // old "group 5": P, As, Sb, Bi |
679 | 0 | valenceElectrons = 5; |
680 | 0 | break; |
681 | 0 | case 16: |
682 | 0 | case 34: |
683 | 0 | case 52: |
684 | 0 | case 84: |
685 | | // old "group 6": S, Se, Te, Po |
686 | 0 | valenceElectrons = 6; |
687 | 0 | break; |
688 | 0 | case 35: |
689 | 0 | case 53: |
690 | 0 | case 85: |
691 | | // old "group 7": Br, I, At |
692 | 0 | valenceElectrons = 7; |
693 | 0 | break; |
694 | 0 | case 36: |
695 | 0 | case 54: |
696 | 0 | case 86: |
697 | | // hypervalent noble gases (Kr, Xe, Rn) |
698 | 0 | valenceElectrons = 8; |
699 | 0 | break; |
700 | 0 | } |
701 | 0 | if (valenceElectrons) { |
702 | | // calculate the number of lone pairs |
703 | | // e.g. for IF3 => "T-shaped" |
704 | 0 | valenceElectrons -= b->GetFormalCharge(); // make sure to look for I+F4 -> see-saw |
705 | 0 | double lonePairs = (valenceElectrons - b->GetExplicitValence()) / 2.0; |
706 | | // we actually need to round up here -- single e- take room too. |
707 | 0 | int sites = (int)ceil(lonePairs); |
708 | 0 | coordination = b->GetExplicitDegree() + sites; |
709 | 0 | if (coordination <= 4) { // normal valency |
710 | 0 | coordination = ipar; |
711 | 0 | } else if (b->GetAtomicNum() == OBElements::Sulfur && b->CountFreeOxygens() == 3) { |
712 | | // SO3, should be planar |
713 | | // PR#2971473, thanks to Philipp Rumpf |
714 | 0 | coordination = 2; // i.e., sp2 |
715 | 0 | } |
716 | | /* planar coordination of hexavalent molecules.*/ |
717 | 0 | if (lonePairs == 0 && b->GetExplicitDegree() == 3 && b->GetExplicitValence() == 6) { |
718 | 0 | coordination = 2; |
719 | 0 | } |
720 | 0 | if (lonePairs == 0 && b->GetExplicitDegree() == 7) { |
721 | 0 | coordination = 7; |
722 | 0 | } |
723 | | // Check to see if coordination is really correct |
724 | | // if not (e.g., 5- or 7- or 8-coord...) |
725 | | // then create approximate angle bending terms |
726 | 0 | } else { |
727 | 0 | coordination = ipar; // coordination of central atom |
728 | 0 | } |
729 | 0 | if (b->GetExplicitDegree() > 4) { |
730 | 0 | coordination = b->GetExplicitDegree(); |
731 | 0 | } else { |
732 | 0 | int coordDifference = ipar - b->GetExplicitDegree(); |
733 | 0 | if (abs(coordDifference) > 2) |
734 | | // low valent, but very different than expected by ipar |
735 | 0 | coordination = b->GetExplicitDegree() - 1; // 4 coordinate == sp3 |
736 | 0 | } |
737 | 0 | return coordination; |
738 | 0 | } |
739 | | |
740 | | bool OBForceFieldUFF::SetupCalculations() |
741 | 0 | { |
742 | 0 | OBFFParameter *parameterA, *parameterB, *parameterC; |
743 | 0 | OBAtom *a, *b, *c, *d; |
744 | 0 | double bondorder; |
745 | 0 | OBFFBondCalculationUFF bondcalc; |
746 | 0 | OBFFAngleCalculationUFF anglecalc; |
747 | 0 | OBFFTorsionCalculationUFF torsioncalc; |
748 | 0 | OBFFOOPCalculationUFF oopcalc; |
749 | 0 | OBFFVDWCalculationUFF vdwcalc; |
750 | |
|
751 | 0 | IF_OBFF_LOGLVL_LOW |
752 | 0 | OBFFLog("\nS E T T I N G U P C A L C U L A T I O N S\n\n"); |
753 | | |
754 | | // Clear previous calculations |
755 | 0 | _bondcalculations.clear(); |
756 | 0 | _anglecalculations.clear(); |
757 | 0 | _torsioncalculations.clear(); |
758 | 0 | _oopcalculations.clear(); |
759 | 0 | _vdwcalculations.clear(); |
760 | | |
761 | | // Clear and reset any 5-coordinate axial/equatorial marks (i.e., strange coordination) |
762 | | // Now should fit standard VSEPR rules, although we can't easily handle lone pairs |
763 | 0 | int coordination; |
764 | 0 | FOR_ATOMS_OF_MOL(atom, _mol) { |
765 | | // remove any previous designation |
766 | 0 | atom->DeleteData("UFF_AXIAL_ATOM"); |
767 | 0 | atom->DeleteData("UFF_CENTRAL_ATOM"); |
768 | 0 | } |
769 | |
|
770 | 0 | FOR_ATOMS_OF_MOL(atom, _mol) { |
771 | 0 | parameterB = GetParameterUFF(atom->GetType(), _ffparams); |
772 | | |
773 | | // GitHub issue #1794 |
774 | 0 | if (parameterB == nullptr) { |
775 | 0 | snprintf(_logbuf, BUFF_SIZE, " COULD NOT FIND PARAMETERS FOR ATOM %d (IDX)...\n", |
776 | 0 | atom->GetIdx()); |
777 | 0 | obErrorLog.ThrowError(__FUNCTION__, _logbuf, obWarning); |
778 | 0 | IF_OBFF_LOGLVL_LOW |
779 | 0 | OBFFLog(_logbuf); |
780 | 0 | return false; |
781 | 0 | } |
782 | | |
783 | 0 | if (GetCoordination(&*atom, parameterB->_ipar[0]) == 5) { // we need to do work for trigonal-bipy! |
784 | | // First, find the two largest neighbors |
785 | 0 | OBAtom *largestNbr, *current, *secondLargestNbr = nullptr; |
786 | 0 | double largestRadius; |
787 | 0 | OBBondIterator i; |
788 | 0 | largestNbr = atom->BeginNbrAtom(i); |
789 | | // work out the radius |
790 | 0 | parameterA = GetParameterUFF(largestNbr->GetType(), _ffparams); |
791 | |
|
792 | 0 | if (parameterA == nullptr) { |
793 | 0 | IF_OBFF_LOGLVL_LOW { |
794 | 0 | snprintf(_logbuf, BUFF_SIZE, " COULD NOT FIND PARAMETERS FOR ATOM %d (IDX)...\n", |
795 | 0 | largestNbr->GetIdx()); |
796 | 0 | OBFFLog(_logbuf); |
797 | 0 | } |
798 | 0 | return false; |
799 | 0 | } |
800 | | |
801 | 0 | largestRadius = parameterA->_dpar[0]; |
802 | |
|
803 | 0 | for (current = atom->NextNbrAtom(i); current; current = atom->NextNbrAtom(i)) { |
804 | 0 | parameterA = GetParameterUFF(current->GetType(), _ffparams); |
805 | |
|
806 | 0 | if (parameterA == nullptr) { |
807 | 0 | IF_OBFF_LOGLVL_LOW { |
808 | 0 | snprintf(_logbuf, BUFF_SIZE, " COULD NOT FIND PARAMETERS FOR ATOM %d (IDX)...\n", |
809 | 0 | current->GetIdx()); |
810 | 0 | OBFFLog(_logbuf); |
811 | 0 | } |
812 | 0 | return false; |
813 | 0 | } |
814 | | |
815 | 0 | if (parameterA->_dpar[0] > largestRadius) { |
816 | | // New largest neighbor |
817 | 0 | secondLargestNbr = largestNbr; |
818 | 0 | largestRadius = parameterA->_dpar[0]; |
819 | 0 | largestNbr = current; |
820 | 0 | } |
821 | 0 | if (secondLargestNbr == nullptr) { |
822 | | // save this atom |
823 | 0 | secondLargestNbr = current; |
824 | 0 | } |
825 | 0 | } |
826 | | |
827 | | // OK, now we tag the central atom |
828 | 0 | OBPairData *label = new OBPairData; |
829 | 0 | label->SetAttribute("UFF_CENTRAL_ATOM"); |
830 | 0 | label->SetValue("True"); // doesn't really matter |
831 | 0 | atom->SetData(label); |
832 | |
|
833 | 0 | label = new OBPairData; |
834 | 0 | label->SetAttribute("UFF_AXIAL_ATOM"); |
835 | 0 | label->SetValue("True"); |
836 | 0 | largestNbr->SetData(label); |
837 | |
|
838 | 0 | if (secondLargestNbr != nullptr) { // check for NULL, no guarantee |
839 | 0 | label = new OBPairData; |
840 | 0 | label->SetAttribute("UFF_AXIAL_ATOM"); |
841 | 0 | label->SetValue("True"); |
842 | 0 | secondLargestNbr->SetData(label); |
843 | 0 | } |
844 | |
|
845 | 0 | } // end work for 5-coordinate angles |
846 | 0 | if (GetCoordination(&*atom, parameterB->_ipar[0]) == 7) { // pentagonal bipyramidal |
847 | | // First, find the two largest neighbors |
848 | 0 | OBAtom *largestNbr, *current, *secondLargestNbr = nullptr; |
849 | 0 | double largestRadius; |
850 | 0 | OBBondIterator i; |
851 | 0 | largestNbr = atom->BeginNbrAtom(i); |
852 | | // work out the radius |
853 | 0 | parameterA = GetParameterUFF(largestNbr->GetType(), _ffparams); |
854 | |
|
855 | 0 | if (parameterA == nullptr) { |
856 | 0 | IF_OBFF_LOGLVL_LOW { |
857 | 0 | snprintf(_logbuf, BUFF_SIZE, " COULD NOT FIND PARAMETERS FOR ATOM %d (IDX)...\n", |
858 | 0 | largestNbr->GetIdx()); |
859 | 0 | OBFFLog(_logbuf); |
860 | 0 | } |
861 | 0 | return false; |
862 | 0 | } |
863 | | |
864 | 0 | largestRadius = parameterA->_dpar[0]; |
865 | |
|
866 | 0 | for (current = atom->NextNbrAtom(i); current; current = atom->NextNbrAtom(i)) { |
867 | 0 | parameterA = GetParameterUFF(current->GetType(), _ffparams); |
868 | |
|
869 | 0 | if (parameterA == nullptr) { |
870 | 0 | IF_OBFF_LOGLVL_LOW { |
871 | 0 | snprintf(_logbuf, BUFF_SIZE, " COULD NOT FIND PARAMETERS FOR ATOM %d (IDX)...\n", |
872 | 0 | current->GetIdx()); |
873 | 0 | OBFFLog(_logbuf); |
874 | 0 | } |
875 | 0 | return false; |
876 | 0 | } |
877 | | |
878 | 0 | if (parameterA->_dpar[0] > largestRadius) { |
879 | | // New largest neighbor |
880 | 0 | secondLargestNbr = largestNbr; |
881 | 0 | largestRadius = parameterA->_dpar[0]; |
882 | 0 | largestNbr = current; |
883 | 0 | } |
884 | 0 | if (secondLargestNbr == nullptr) { |
885 | | // save this atom |
886 | 0 | secondLargestNbr = current; |
887 | 0 | } |
888 | 0 | } |
889 | | |
890 | | // OK, now we tag the central atom |
891 | 0 | OBPairData *label = new OBPairData; |
892 | 0 | label->SetAttribute("UFF_CENTRAL_ATOM"); |
893 | 0 | label->SetValue("True"); // doesn't really matter |
894 | 0 | atom->SetData(label); |
895 | | // And tag the axial substituents |
896 | 0 | label = new OBPairData; |
897 | 0 | label->SetAttribute("UFF_AXIAL_ATOM"); |
898 | 0 | label->SetValue("True"); |
899 | 0 | largestNbr->SetData(label); |
900 | 0 | if (secondLargestNbr != nullptr) { // check for NULL, no guarantee |
901 | 0 | label = new OBPairData; |
902 | 0 | label->SetAttribute("UFF_AXIAL_ATOM"); |
903 | 0 | label->SetValue("True"); |
904 | 0 | secondLargestNbr->SetData(label); |
905 | 0 | } |
906 | 0 | } |
907 | 0 | } // end loop through atoms |
908 | | |
909 | | // |
910 | | // Bond Calculations |
911 | 0 | IF_OBFF_LOGLVL_LOW |
912 | 0 | OBFFLog("SETTING UP BOND CALCULATIONS...\n"); |
913 | |
|
914 | 0 | FOR_BONDS_OF_MOL(bond, _mol) { |
915 | 0 | a = bond->GetBeginAtom(); |
916 | 0 | b = bond->GetEndAtom(); |
917 | | |
918 | | // skip this bond if the atoms are ignored |
919 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) || _constraints.IsIgnored(b->GetIdx()) ) |
920 | 0 | continue; |
921 | | |
922 | | // if there are any groups specified, check if the two bond atoms are in a single intraGroup |
923 | 0 | if (HasGroups()) { |
924 | 0 | bool validBond = false; |
925 | 0 | for (unsigned int i=0; i < _intraGroup.size(); ++i) { |
926 | 0 | if (_intraGroup[i].BitIsSet(a->GetIdx()) && _intraGroup[i].BitIsSet(b->GetIdx())) |
927 | 0 | validBond = true; |
928 | 0 | } |
929 | 0 | if (!validBond) |
930 | 0 | continue; |
931 | 0 | } |
932 | | |
933 | 0 | bondorder = bond->GetBondOrder(); |
934 | 0 | if (bond->IsAromatic()) |
935 | 0 | bondorder = 1.5; |
936 | | // e.g., in Cp rings, may not be "aromatic" by OB |
937 | | // but check for explicit hydrogen counts (e.g., biphenyl inter-ring is not aromatic) |
938 | 0 | if ((a->GetType()[2] == 'R' && b->GetType()[2] == 'R') |
939 | 0 | && (a->ExplicitHydrogenCount() == 1 && b->ExplicitHydrogenCount() == 1)) |
940 | 0 | bondorder = 1.5; |
941 | 0 | if (bond->IsAmide()) |
942 | 0 | bondorder = 1.41; |
943 | |
|
944 | 0 | bondcalc.a = a; |
945 | 0 | bondcalc.b = b; |
946 | 0 | bondcalc.bt = bondorder; |
947 | |
|
948 | 0 | parameterA = GetParameterUFF(a->GetType(), _ffparams); |
949 | 0 | parameterB = GetParameterUFF(b->GetType(), _ffparams); |
950 | |
|
951 | 0 | if (parameterA == nullptr || parameterB == nullptr) { |
952 | 0 | IF_OBFF_LOGLVL_LOW { |
953 | 0 | snprintf(_logbuf, BUFF_SIZE, " COULD NOT FIND PARAMETERS FOR BOND %d-%d (IDX)...\n", |
954 | 0 | a->GetIdx(), b->GetIdx()); |
955 | 0 | OBFFLog(_logbuf); |
956 | 0 | } |
957 | 0 | continue; |
958 | 0 | } |
959 | | |
960 | 0 | bondcalc.r0 = CalculateBondDistance(parameterA, parameterB, bondorder); |
961 | | |
962 | | // here we fold the 1/2 into the kij from equation 1a |
963 | | // Otherwise, this is equation 6 from the UFF paper. |
964 | 0 | bondcalc.kb = (0.5 * KCAL_TO_KJ * 664.12 |
965 | 0 | * parameterA->_dpar[5] * parameterB->_dpar[5]) |
966 | 0 | / (bondcalc.r0 * bondcalc.r0 * bondcalc.r0); |
967 | |
|
968 | 0 | bondcalc.SetupPointers(); |
969 | 0 | _bondcalculations.push_back(bondcalc); |
970 | 0 | } |
971 | | |
972 | | // |
973 | | // Angle Calculations |
974 | | // |
975 | 0 | IF_OBFF_LOGLVL_LOW |
976 | 0 | OBFFLog("SETTING UP ANGLE CALCULATIONS...\n"); |
977 | |
|
978 | 0 | double sinT0; |
979 | 0 | double rab, rbc, rac; |
980 | 0 | OBBond *bondPtr; |
981 | 0 | FOR_ANGLES_OF_MOL(angle, _mol) { |
982 | 0 | b = _mol.GetAtom((*angle)[0] + 1); |
983 | 0 | a = _mol.GetAtom((*angle)[1] + 1); |
984 | 0 | c = _mol.GetAtom((*angle)[2] + 1); |
985 | | |
986 | | // skip this angle if the atoms are ignored |
987 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) |
988 | 0 | || _constraints.IsIgnored(b->GetIdx()) |
989 | 0 | || _constraints.IsIgnored(c->GetIdx()) ) |
990 | 0 | continue; |
991 | | |
992 | | // if there are any groups specified, |
993 | | // check if the three angle atoms are in a single intraGroup |
994 | 0 | if (HasGroups()) { |
995 | 0 | bool validAngle = false; |
996 | 0 | for (unsigned int i=0; i < _intraGroup.size(); ++i) { |
997 | 0 | if (_intraGroup[i].BitIsSet(a->GetIdx()) && _intraGroup[i].BitIsSet(b->GetIdx()) && |
998 | 0 | _intraGroup[i].BitIsSet(c->GetIdx())) |
999 | 0 | validAngle = true; |
1000 | 0 | } |
1001 | 0 | if (!validAngle) |
1002 | 0 | continue; |
1003 | 0 | } |
1004 | | |
1005 | 0 | anglecalc.a = a; |
1006 | 0 | anglecalc.b = b; |
1007 | 0 | anglecalc.c = c; |
1008 | |
|
1009 | 0 | parameterA = GetParameterUFF(a->GetType(), _ffparams); |
1010 | 0 | parameterB = GetParameterUFF(b->GetType(), _ffparams); |
1011 | 0 | parameterC = GetParameterUFF(c->GetType(), _ffparams); |
1012 | |
|
1013 | 0 | if (parameterA == nullptr || parameterB == nullptr || parameterC == nullptr) { |
1014 | 0 | IF_OBFF_LOGLVL_LOW { |
1015 | 0 | snprintf(_logbuf, BUFF_SIZE, " COULD NOT FIND PARAMETERS FOR ANGLE %d-%d-%d (IDX)...\n", |
1016 | 0 | a->GetIdx(), b->GetIdx(), c->GetIdx()); |
1017 | 0 | OBFFLog(_logbuf); |
1018 | 0 | } |
1019 | 0 | return false; |
1020 | 0 | } |
1021 | | |
1022 | 0 | coordination = GetCoordination(b, parameterB->_ipar[0]); |
1023 | |
|
1024 | 0 | if (coordination != parameterB->_ipar[0]) { |
1025 | 0 | IF_OBFF_LOGLVL_LOW { |
1026 | 0 | snprintf(_logbuf, BUFF_SIZE, " CORRECTED COORDINATION FOR ANGLE %d-%d-%d (IDX)... WAS %d NOW %d\n", |
1027 | 0 | a->GetIdx(), b->GetIdx(), c->GetIdx(), parameterB->_ipar[0], coordination); |
1028 | 0 | OBFFLog(_logbuf); |
1029 | 0 | } |
1030 | 0 | } |
1031 | | |
1032 | | //double currentTheta; |
1033 | 0 | if (coordination > 7) { |
1034 | | // large coordination sphere (e.g., [ReH9]-2 or [Ce(NO3)6]-2) |
1035 | | // just resort to using VDW 1-3 interactions to push atoms into place |
1036 | | // there's not much else we can do without real parameters |
1037 | 0 | if (SetupVDWCalculation(a, c, vdwcalc)) { |
1038 | 0 | _vdwcalculations.push_back(vdwcalc); |
1039 | 0 | } |
1040 | | // We're not installing an angle term for this set |
1041 | | // We can't even approximate one. |
1042 | | // The downside is that we can't easily handle lone pairs. |
1043 | 0 | continue; |
1044 | |
|
1045 | 0 | } else if (coordination == 7) { // pentagonal bipyramidal |
1046 | | // This doesn't work so well because it's hard to classify between |
1047 | | // axial-equatorial (90 degrees) and proximal equatorial (~72 degrees). |
1048 | 0 | double currentTheta; |
1049 | 0 | currentTheta = a->GetAngle(&*b, &*c); |
1050 | |
|
1051 | 0 | anglecalc.c0 = 1.0; |
1052 | 0 | if (b->HasData("UFF_CENTRAL_ATOM") |
1053 | 0 | && a->HasData("UFF_AXIAL_ATOM") |
1054 | 0 | && c->HasData("UFF_AXIAL_ATOM")) { // axial ligands = linear |
1055 | 0 | anglecalc.coord = 1; // like sp |
1056 | 0 | anglecalc.theta0 = 180.0 * DEG_TO_RAD; |
1057 | 0 | anglecalc.c1 = 1.0; |
1058 | 0 | } else if ( (a->HasData("UFF_AXIAL_ATOM") && !c->HasData("UFF_AXIAL_ATOM")) |
1059 | 0 | || (c->HasData("UFF_AXIAL_ATOM") && !a->HasData("UFF_AXIAL_ATOM")) ) { // axial-equatorial ligands |
1060 | 0 | anglecalc.coord = 4; // like sq. planar or octahedral |
1061 | 0 | anglecalc.theta0 = 90.0 * DEG_TO_RAD; |
1062 | 0 | anglecalc.c1 = 1.0; |
1063 | 0 | } else { // equatorial - equatorial |
1064 | 0 | anglecalc.coord = 7; // unlike anything else, as theta0 is ignored. |
1065 | 0 | anglecalc.theta0 = (currentTheta > 108.0 ? 144.0 : 72.0) * DEG_TO_RAD; |
1066 | 0 | anglecalc.c1 = 1.0; |
1067 | 0 | } |
1068 | 0 | anglecalc.c2 = 0.0; |
1069 | | |
1070 | | /* |
1071 | | if (0) { |
1072 | | if (currentTheta >= 155.0) { // axial ligands = linear |
1073 | | anglecalc.coord = 1; // like sp |
1074 | | anglecalc.theta0 = 180.0 * DEG_TO_RAD; |
1075 | | anglecalc.c1 = 1.0; |
1076 | | } else if (currentTheta < 155.0 && currentTheta >= 110.0) { // distal equatorial |
1077 | | anglecalc.coord = 7; // like sp3 |
1078 | | anglecalc.theta0 = 144.0 * DEG_TO_RAD; |
1079 | | anglecalc.c1 = 1.0; |
1080 | | } else if (currentTheta < 110.0 && currentTheta >= 85.0) { // axial-equatorial |
1081 | | anglecalc.coord = 4; // like sq. planar or octahedral |
1082 | | anglecalc.theta0 = 90.0 * DEG_TO_RAD; |
1083 | | anglecalc.c1 = 1.0; |
1084 | | } else if (currentTheta < 85.0) { // proximal equatorial |
1085 | | anglecalc.coord = 7; // general case (i.e., like sp3) |
1086 | | anglecalc.theta0 = 72.0 * DEG_TO_RAD; |
1087 | | anglecalc.c1 = 1.0; |
1088 | | } |
1089 | | anglecalc.c2 = 0.0; |
1090 | | } else { |
1091 | | */ |
1092 | |
|
1093 | 0 | } else if (coordination == 5) { // trigonal bipyramidal |
1094 | 0 | anglecalc.c0 = 1.0; |
1095 | | // We've already done some of our work above -- look for axial markings |
1096 | 0 | if (b->HasData("UFF_CENTRAL_ATOM") |
1097 | 0 | && a->HasData("UFF_AXIAL_ATOM") |
1098 | 0 | && c->HasData("UFF_AXIAL_ATOM")) { // axial ligands = linear |
1099 | 0 | anglecalc.coord = 1; // like sp |
1100 | 0 | anglecalc.theta0 = 180.0 * DEG_TO_RAD; |
1101 | 0 | anglecalc.c1 = 1.0; |
1102 | 0 | } else if ( (a->HasData("UFF_AXIAL_ATOM") && !c->HasData("UFF_AXIAL_ATOM")) |
1103 | 0 | || (c->HasData("UFF_AXIAL_ATOM") && !a->HasData("UFF_AXIAL_ATOM")) ) { // axial-equatorial ligands |
1104 | 0 | anglecalc.coord = 4; // like sq. planar or octahedral |
1105 | 0 | anglecalc.theta0 = 90.0 * DEG_TO_RAD; |
1106 | 0 | anglecalc.c1 = 1.0; |
1107 | 0 | } else { // equatorial - equatorial |
1108 | 0 | anglecalc.coord = 2; // like sp2 |
1109 | 0 | anglecalc.theta0 = 120.0 * DEG_TO_RAD; |
1110 | 0 | anglecalc.c1 = -1.0; |
1111 | 0 | } |
1112 | 0 | anglecalc.c2 = 0.0; |
1113 | 0 | } |
1114 | 0 | else { // normal coordination: sp, sp2, sp3, square planar, octahedral |
1115 | 0 | anglecalc.coord = coordination; |
1116 | 0 | anglecalc.theta0 = parameterB->_dpar[1] * DEG_TO_RAD; |
1117 | 0 | if (coordination != parameterB->_ipar[0]) { |
1118 | 0 | switch (coordination) |
1119 | 0 | { |
1120 | 0 | case 1: |
1121 | 0 | anglecalc.theta0 = 180.0 * DEG_TO_RAD; |
1122 | 0 | break; |
1123 | 0 | case 2: |
1124 | 0 | anglecalc.theta0 = 120.0 * DEG_TO_RAD; |
1125 | 0 | break; |
1126 | 0 | case 4: // sq. planar |
1127 | 0 | case 5: // axial / equatorial |
1128 | 0 | case 6: // octahedral |
1129 | 0 | case 7: // axial equatorial |
1130 | 0 | anglecalc.theta0 = 90.0 * DEG_TO_RAD; |
1131 | 0 | break; |
1132 | 0 | case 3: // tetrahedral |
1133 | 0 | default: |
1134 | 0 | anglecalc.theta0 = 109.5 * DEG_TO_RAD; |
1135 | 0 | break; |
1136 | 0 | } |
1137 | 0 | } |
1138 | 0 | anglecalc.cosT0 = cos(anglecalc.theta0); |
1139 | 0 | sinT0 = sin(anglecalc.theta0); |
1140 | 0 | anglecalc.c2 = 1.0 / (4.0 * sinT0 * sinT0); |
1141 | 0 | anglecalc.c1 = -4.0 * anglecalc.c2 * anglecalc.cosT0; |
1142 | 0 | anglecalc.c0 = anglecalc.c2*(2.0*anglecalc.cosT0*anglecalc.cosT0 + 1.0); |
1143 | 0 | } |
1144 | | |
1145 | 0 | anglecalc.cosT0 = cos(anglecalc.theta0); |
1146 | 0 | anglecalc.zi = parameterA->_dpar[5]; |
1147 | 0 | anglecalc.zk = parameterC->_dpar[5]; |
1148 | | // Precompute the force constant |
1149 | 0 | bondPtr = _mol.GetBond(a,b); |
1150 | 0 | bondorder = bondPtr->GetBondOrder(); |
1151 | 0 | if (bondPtr->IsAromatic()) |
1152 | 0 | bondorder = 1.5; |
1153 | 0 | if (bondPtr->IsAmide()) |
1154 | 0 | bondorder = 1.41; |
1155 | 0 | rab = CalculateBondDistance(parameterA, parameterB, bondorder); |
1156 | |
|
1157 | 0 | bondPtr = _mol.GetBond(b,c); |
1158 | 0 | bondorder = bondPtr->GetBondOrder(); |
1159 | 0 | if (bondPtr->IsAromatic()) |
1160 | 0 | bondorder = 1.5; |
1161 | 0 | if (bondPtr->IsAmide()) |
1162 | 0 | bondorder = 1.41; |
1163 | 0 | rbc = CalculateBondDistance(parameterB, parameterC, bondorder); |
1164 | 0 | rac = sqrt(rab*rab + rbc*rbc - 2.0 * rab*rbc*anglecalc.cosT0); |
1165 | | |
1166 | | // Equation 13 from paper -- corrected by Towhee |
1167 | | // Note that 1/(rij * rjk) cancels with rij*rjk in eqn. 13 |
1168 | 0 | anglecalc.ka = (664.12 * KCAL_TO_KJ) * (anglecalc.zi * anglecalc.zk / (pow(rac, 5.0))); |
1169 | 0 | anglecalc.ka *= (3.0*rab*rbc*(1.0 - anglecalc.cosT0*anglecalc.cosT0) - rac*rac*anglecalc.cosT0); |
1170 | | // Make sure to divide by n^2 to save CPU cycles |
1171 | 0 | switch (anglecalc.coord) { |
1172 | 0 | case 2: // sp2, so divide by 3^2 |
1173 | 0 | anglecalc.n = 3; |
1174 | 0 | anglecalc.ka = anglecalc.ka / 9.0; |
1175 | 0 | break; |
1176 | 0 | case 4: // divide by 4^2 |
1177 | 0 | case 6: |
1178 | 0 | anglecalc.n = 4; |
1179 | 0 | anglecalc.ka = anglecalc.ka / 16.0; |
1180 | 0 | break; |
1181 | 0 | default: |
1182 | 0 | break; |
1183 | 0 | } |
1184 | | |
1185 | 0 | anglecalc.SetupPointers(); |
1186 | 0 | _anglecalculations.push_back(anglecalc); |
1187 | 0 | } |
1188 | | |
1189 | | // |
1190 | | // Torsion Calculations |
1191 | | // |
1192 | 0 | IF_OBFF_LOGLVL_LOW |
1193 | 0 | OBFFLog("SETTING UP TORSION CALCULATIONS...\n"); |
1194 | |
|
1195 | 0 | double torsiontype; |
1196 | 0 | double phi0 = 0.0; |
1197 | |
|
1198 | 0 | double vi, vj; |
1199 | 0 | FOR_TORSIONS_OF_MOL(t, _mol) { |
1200 | 0 | a = _mol.GetAtom((*t)[0] + 1); |
1201 | 0 | b = _mol.GetAtom((*t)[1] + 1); |
1202 | 0 | c = _mol.GetAtom((*t)[2] + 1); |
1203 | 0 | d = _mol.GetAtom((*t)[3] + 1); |
1204 | | |
1205 | | // skip this torsion if the atoms are ignored |
1206 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) || _constraints.IsIgnored(b->GetIdx()) || |
1207 | 0 | _constraints.IsIgnored(c->GetIdx()) || _constraints.IsIgnored(d->GetIdx()) ) |
1208 | 0 | continue; |
1209 | | |
1210 | | // if there are any groups specified, check if the four torsion atoms are in a single intraGroup |
1211 | 0 | if (HasGroups()) { |
1212 | 0 | bool validTorsion = false; |
1213 | 0 | for (unsigned int i=0; i < _intraGroup.size(); ++i) { |
1214 | 0 | if (_intraGroup[i].BitIsSet(a->GetIdx()) && _intraGroup[i].BitIsSet(b->GetIdx()) && |
1215 | 0 | _intraGroup[i].BitIsSet(c->GetIdx()) && _intraGroup[i].BitIsSet(d->GetIdx())) |
1216 | 0 | validTorsion = true; |
1217 | 0 | } |
1218 | 0 | if (!validTorsion) |
1219 | 0 | continue; |
1220 | 0 | } |
1221 | | |
1222 | 0 | OBBond *bc = _mol.GetBond(b, c); |
1223 | 0 | torsiontype = bc->GetBondOrder(); |
1224 | 0 | if (bc->IsAromatic()) |
1225 | 0 | torsiontype = 1.5; |
1226 | 0 | if (bc->IsAmide()) |
1227 | 0 | torsiontype = 1.41; |
1228 | |
|
1229 | 0 | torsioncalc.a = a; |
1230 | 0 | torsioncalc.b = b; |
1231 | 0 | torsioncalc.c = c; |
1232 | 0 | torsioncalc.d = d; |
1233 | 0 | torsioncalc.tt = torsiontype; |
1234 | |
|
1235 | 0 | parameterB = GetParameterUFF(b->GetType(), _ffparams); |
1236 | 0 | parameterC = GetParameterUFF(c->GetType(), _ffparams); |
1237 | |
|
1238 | 0 | if (parameterB == nullptr || parameterC == nullptr) { |
1239 | 0 | IF_OBFF_LOGLVL_LOW { |
1240 | 0 | snprintf(_logbuf, BUFF_SIZE, " COULD NOT FIND PARAMETERS FOR TORSION X-%d-%d-X (IDX)...\n", |
1241 | 0 | b->GetIdx(), c->GetIdx()); |
1242 | 0 | OBFFLog(_logbuf); |
1243 | 0 | } |
1244 | 0 | return false; |
1245 | 0 | } |
1246 | | |
1247 | 0 | if (parameterB->_ipar[0] == 3 && parameterC->_ipar[0] == 3) { |
1248 | | // two sp3 centers |
1249 | 0 | phi0 = 60.0; |
1250 | 0 | torsioncalc.n = 3; |
1251 | 0 | vi = parameterB->_dpar[6]; |
1252 | 0 | vj = parameterC->_dpar[6]; |
1253 | | |
1254 | | // exception for a pair of group 6 sp3 atoms |
1255 | 0 | switch (b->GetAtomicNum()) { |
1256 | 0 | case 8: |
1257 | 0 | vi = 2.0; |
1258 | 0 | torsioncalc.n = 2; |
1259 | 0 | phi0 = 90.0; |
1260 | 0 | break; |
1261 | 0 | case 16: |
1262 | 0 | case 34: |
1263 | 0 | case 52: |
1264 | 0 | case 84: |
1265 | 0 | vi = 6.8; |
1266 | 0 | torsioncalc.n = 2; |
1267 | 0 | phi0 = 90.0; |
1268 | 0 | } |
1269 | 0 | switch (c->GetAtomicNum()) { |
1270 | 0 | case 8: |
1271 | 0 | vj = 2.0; |
1272 | 0 | torsioncalc.n = 2; |
1273 | 0 | phi0 = 90.0; |
1274 | 0 | break; |
1275 | 0 | case 16: |
1276 | 0 | case 34: |
1277 | 0 | case 52: |
1278 | 0 | case 84: |
1279 | 0 | vj = 6.8; |
1280 | 0 | torsioncalc.n = 2; |
1281 | 0 | phi0 = 90.0; |
1282 | 0 | } |
1283 | | |
1284 | 0 | torsioncalc.V = 0.5 * KCAL_TO_KJ * sqrt(vi * vj); |
1285 | |
|
1286 | 0 | } else if (parameterB->_ipar[0] == 2 && parameterC->_ipar[0] == 2) { |
1287 | | // two sp2 centers |
1288 | 0 | phi0 = 180.0; |
1289 | 0 | torsioncalc.n = 2; |
1290 | 0 | torsioncalc.V = 0.5 * KCAL_TO_KJ * 5.0 * |
1291 | 0 | sqrt(parameterB->_dpar[7]*parameterC->_dpar[7]) * |
1292 | 0 | (1.0 + 4.18 * log(torsiontype)); |
1293 | 0 | } else if ((parameterB->_ipar[0] == 2 && parameterC->_ipar[0] == 3) |
1294 | 0 | || (parameterB->_ipar[0] == 3 && parameterC->_ipar[0] == 2)) { |
1295 | | // one sp3, one sp2 |
1296 | 0 | phi0 = 0.0; |
1297 | 0 | torsioncalc.n = 6; |
1298 | 0 | torsioncalc.V = 0.5 * KCAL_TO_KJ * 1.0; |
1299 | | |
1300 | | // exception for group 6 sp3 |
1301 | 0 | if (parameterC->_ipar[0] == 3) { |
1302 | 0 | switch (c->GetAtomicNum()) { |
1303 | 0 | case 8: |
1304 | 0 | case 16: |
1305 | 0 | case 34: |
1306 | 0 | case 52: |
1307 | 0 | case 84: |
1308 | 0 | torsioncalc.n = 2; |
1309 | 0 | phi0 = 90.0; |
1310 | 0 | } |
1311 | 0 | } |
1312 | 0 | if (parameterB->_ipar[0] == 3) { |
1313 | 0 | switch (b->GetAtomicNum()) { |
1314 | 0 | case 8: |
1315 | 0 | case 16: |
1316 | 0 | case 34: |
1317 | 0 | case 52: |
1318 | 0 | case 84: |
1319 | 0 | torsioncalc.n = 2; |
1320 | 0 | phi0 = 90.0; |
1321 | 0 | } |
1322 | 0 | } |
1323 | 0 | } |
1324 | | |
1325 | 0 | if (IsNearZero(torsioncalc.V)) // don't bother calcuating this torsion |
1326 | 0 | continue; |
1327 | | |
1328 | | // still need to implement special case of sp2-sp3 with sp2-sp2 |
1329 | | |
1330 | 0 | torsioncalc.cosNPhi0 = cos(torsioncalc.n * DEG_TO_RAD * phi0); |
1331 | 0 | torsioncalc.SetupPointers(); |
1332 | 0 | _torsioncalculations.push_back(torsioncalc); |
1333 | 0 | } |
1334 | | |
1335 | | // |
1336 | | // OOP/Inversion Calculations |
1337 | | // |
1338 | 0 | IF_OBFF_LOGLVL_LOW |
1339 | 0 | OBFFLog("SETTING UP OOP CALCULATIONS...\n"); |
1340 | |
|
1341 | 0 | double phi; |
1342 | | // The original Rappe paper in JACS isn't very clear about the parameters |
1343 | | // The following was adapted from Towhee |
1344 | 0 | FOR_ATOMS_OF_MOL(atom, _mol) { |
1345 | 0 | b = (OBAtom*) &*atom; |
1346 | |
|
1347 | 0 | switch (b->GetAtomicNum()) { |
1348 | 0 | case 6: // carbon |
1349 | 0 | case 7: // nitrogen |
1350 | 0 | case 8: // oxygen |
1351 | 0 | case 15: // phos. |
1352 | 0 | case 33: // as |
1353 | 0 | case 51: // sb |
1354 | 0 | case 83: // bi |
1355 | 0 | break; |
1356 | 0 | default: // no inversion term for this element |
1357 | 0 | continue; |
1358 | 0 | } |
1359 | | |
1360 | 0 | if (b->GetExplicitDegree() > 3) // no OOP for hypervalent atoms |
1361 | 0 | continue; |
1362 | | |
1363 | 0 | a = nullptr; |
1364 | 0 | c = nullptr; |
1365 | 0 | d = nullptr; |
1366 | |
|
1367 | 0 | if (EQn(b->GetType(), "N_3", 3) || |
1368 | 0 | EQn(b->GetType(), "N_2", 3) || |
1369 | 0 | EQn(b->GetType(), "N_R", 3) || |
1370 | 0 | EQn(b->GetType(), "O_2", 3) || |
1371 | 0 | EQn(b->GetType(), "O_R", 3)) { |
1372 | 0 | oopcalc.c0 = 1.0; |
1373 | 0 | oopcalc.c1 = -1.0; |
1374 | 0 | oopcalc.c2 = 0.0; |
1375 | 0 | oopcalc.koop = 6.0 * KCAL_TO_KJ; |
1376 | 0 | } |
1377 | 0 | else if (EQn(b->GetType(), "P_3+3", 5) || |
1378 | 0 | EQn(b->GetType(), "As3+3", 5) || |
1379 | 0 | EQn(b->GetType(), "Sb3+3", 5) || |
1380 | 0 | EQn(b->GetType(), "Bi3+3", 5)) { |
1381 | |
|
1382 | 0 | if (EQn(b->GetType(), "P_3+3", 5)) |
1383 | 0 | phi = 84.4339 * DEG_TO_RAD; |
1384 | 0 | else if (EQn(b->GetType(), "As3+3", 5)) |
1385 | 0 | phi = 86.9735 * DEG_TO_RAD; |
1386 | 0 | else if (EQn(b->GetType(), "Sb3+3", 5)) |
1387 | 0 | phi = 87.7047 * DEG_TO_RAD; |
1388 | 0 | else |
1389 | 0 | phi = 90.0 * DEG_TO_RAD; |
1390 | |
|
1391 | 0 | oopcalc.c1 = -4.0 * cos(phi); |
1392 | 0 | oopcalc.c2 = 1.0; |
1393 | 0 | oopcalc.c0 = -1.0*oopcalc.c1 * cos(phi) + oopcalc.c2*cos(2.0*phi); |
1394 | 0 | oopcalc.koop = 22.0 * KCAL_TO_KJ; |
1395 | 0 | } |
1396 | 0 | else if (!(EQn(b->GetType(), "C_2", 3) || EQn(b->GetType(), "C_R", 3))) |
1397 | 0 | continue; // inversion not defined for this atom type |
1398 | | |
1399 | 0 | FOR_NBORS_OF_ATOM(nbr, b) { |
1400 | 0 | if (a == nullptr) |
1401 | 0 | a = (OBAtom*) &*nbr; |
1402 | 0 | else if (c == nullptr) |
1403 | 0 | c = (OBAtom*) &*nbr; |
1404 | 0 | else |
1405 | 0 | d = (OBAtom*) &*nbr; |
1406 | 0 | } |
1407 | |
|
1408 | 0 | if (a == nullptr || c == nullptr || d == nullptr) |
1409 | 0 | continue; |
1410 | | |
1411 | | // skip this oop if the atoms are ignored |
1412 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) || |
1413 | 0 | _constraints.IsIgnored(b->GetIdx()) || |
1414 | 0 | _constraints.IsIgnored(c->GetIdx()) || |
1415 | 0 | _constraints.IsIgnored(d->GetIdx()) ) |
1416 | 0 | continue; |
1417 | | |
1418 | | // if there are any groups specified, |
1419 | | // check if the four oop atoms are in a single intraGroup |
1420 | 0 | if (HasGroups()) { |
1421 | 0 | bool validOOP = false; |
1422 | 0 | for (unsigned int i=0; i < _intraGroup.size(); ++i) { |
1423 | 0 | if (_intraGroup[i].BitIsSet(a->GetIdx()) && |
1424 | 0 | _intraGroup[i].BitIsSet(b->GetIdx()) && |
1425 | 0 | _intraGroup[i].BitIsSet(c->GetIdx()) && |
1426 | 0 | _intraGroup[i].BitIsSet(d->GetIdx())) |
1427 | 0 | validOOP = true; |
1428 | 0 | } |
1429 | 0 | if (!validOOP) |
1430 | 0 | continue; |
1431 | 0 | } |
1432 | | |
1433 | | // C atoms, we should check if we're bonded to O |
1434 | 0 | if (EQn(b->GetType(), "C_2", 3) || EQn(b->GetType(), "C_R", 3)) { |
1435 | 0 | oopcalc.c0 = 1.0; |
1436 | 0 | oopcalc.c1 = -1.0; |
1437 | 0 | oopcalc.c2 = 0.0; |
1438 | 0 | oopcalc.koop = 6.0 * KCAL_TO_KJ; |
1439 | 0 | if (EQn(a->GetType(), "O_2", 3) || |
1440 | 0 | EQn(c->GetType(), "O_2", 3) || |
1441 | 0 | EQn(d->GetType(), "O_2", 3)) { |
1442 | 0 | oopcalc.koop = 50.0 * KCAL_TO_KJ; |
1443 | 0 | } |
1444 | 0 | } |
1445 | | |
1446 | | // A-B-CD || C-B-AD PLANE = ABC |
1447 | 0 | oopcalc.a = a; |
1448 | 0 | oopcalc.b = b; |
1449 | 0 | oopcalc.c = c; |
1450 | 0 | oopcalc.d = d; |
1451 | 0 | oopcalc.koop /= 3.0; // three OOPs to consider |
1452 | |
|
1453 | 0 | oopcalc.SetupPointers(); |
1454 | 0 | _oopcalculations.push_back(oopcalc); |
1455 | | |
1456 | | // C-B-DA || D-B-CA PLANE BCD |
1457 | 0 | oopcalc.a = d; |
1458 | 0 | oopcalc.d = a; |
1459 | |
|
1460 | 0 | oopcalc.SetupPointers(); |
1461 | 0 | _oopcalculations.push_back(oopcalc); |
1462 | | |
1463 | | // A-B-DC || D-B-AC PLANE ABD |
1464 | 0 | oopcalc.a = a; |
1465 | 0 | oopcalc.c = d; |
1466 | 0 | oopcalc.d = c; |
1467 | |
|
1468 | 0 | oopcalc.SetupPointers(); |
1469 | 0 | _oopcalculations.push_back(oopcalc); |
1470 | 0 | } // for all atoms |
1471 | | |
1472 | | // |
1473 | | // VDW Calculations |
1474 | | // |
1475 | 0 | IF_OBFF_LOGLVL_LOW |
1476 | 0 | OBFFLog("SETTING UP VAN DER WAALS CALCULATIONS...\n"); |
1477 | |
|
1478 | 0 | FOR_PAIRS_OF_MOL(p, _mol) { |
1479 | 0 | a = _mol.GetAtom((*p)[0]); |
1480 | 0 | b = _mol.GetAtom((*p)[1]); |
1481 | | |
1482 | | // skip this vdw if the atoms are ignored |
1483 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) || _constraints.IsIgnored(b->GetIdx()) ) |
1484 | 0 | continue; |
1485 | | |
1486 | | // if there are any groups specified, check if the two atoms are in a single _interGroup or if |
1487 | | // two two atoms are in one of the _interGroups pairs. |
1488 | 0 | if (HasGroups()) { |
1489 | 0 | bool validVDW = false; |
1490 | 0 | for (unsigned int i=0; i < _interGroup.size(); ++i) { |
1491 | 0 | if (_interGroup[i].BitIsSet(a->GetIdx()) && _interGroup[i].BitIsSet(b->GetIdx())) |
1492 | 0 | validVDW = true; |
1493 | 0 | } |
1494 | 0 | for (unsigned int i=0; i < _interGroups.size(); ++i) { |
1495 | 0 | if (_interGroups[i].first.BitIsSet(a->GetIdx()) && _interGroups[i].second.BitIsSet(b->GetIdx())) |
1496 | 0 | validVDW = true; |
1497 | 0 | if (_interGroups[i].first.BitIsSet(b->GetIdx()) && _interGroups[i].second.BitIsSet(a->GetIdx())) |
1498 | 0 | validVDW = true; |
1499 | 0 | } |
1500 | |
|
1501 | 0 | if (!validVDW) |
1502 | 0 | continue; |
1503 | 0 | } |
1504 | | |
1505 | 0 | if (a->IsConnected(b)) { |
1506 | 0 | continue; |
1507 | 0 | } |
1508 | 0 | if (a->IsOneThree(b)) { |
1509 | 0 | continue; |
1510 | 0 | } |
1511 | | |
1512 | 0 | if (SetupVDWCalculation(a, b, vdwcalc)) { |
1513 | 0 | _vdwcalculations.push_back(vdwcalc); |
1514 | 0 | } |
1515 | 0 | } |
1516 | | |
1517 | | // NOTE: No electrostatics are set up |
1518 | | // If you want electrostatics with UFF, you will need to call |
1519 | | // SetupElectrostatics() manually |
1520 | |
|
1521 | 0 | return true; |
1522 | 0 | } |
1523 | | |
1524 | | bool OBForceFieldUFF::SetupElectrostatics() |
1525 | 0 | { |
1526 | | // |
1527 | | // Electrostatic Calculations |
1528 | | // |
1529 | 0 | OBAtom *a, *b; |
1530 | |
|
1531 | 0 | IF_OBFF_LOGLVL_LOW |
1532 | 0 | OBFFLog("SETTING UP ELECTROSTATIC CALCULATIONS...\n"); |
1533 | |
|
1534 | 0 | OBFFElectrostaticCalculationUFF elecalc; |
1535 | |
|
1536 | 0 | _electrostaticcalculations.clear(); |
1537 | | |
1538 | | // Note that while the UFF paper mentions an electrostatic term, |
1539 | | // it does not actually use it. Both Towhee and the UFF FAQ |
1540 | | // discourage the use of electrostatics with UFF. |
1541 | |
|
1542 | 0 | FOR_PAIRS_OF_MOL(p, _mol) { |
1543 | 0 | a = _mol.GetAtom((*p)[0]); |
1544 | 0 | b = _mol.GetAtom((*p)[1]); |
1545 | | |
1546 | | // skip this ele if the atoms are ignored |
1547 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) || _constraints.IsIgnored(b->GetIdx()) ) |
1548 | 0 | continue; |
1549 | | |
1550 | | // if there are any groups specified, check if the two atoms are in a single _interGroup or if |
1551 | | // two two atoms are in one of the _interGroups pairs. |
1552 | 0 | if (HasGroups()) { |
1553 | 0 | bool validEle = false; |
1554 | 0 | for (unsigned int i=0; i < _interGroup.size(); ++i) { |
1555 | 0 | if (_interGroup[i].BitIsSet(a->GetIdx()) && _interGroup[i].BitIsSet(b->GetIdx())) |
1556 | 0 | validEle = true; |
1557 | 0 | } |
1558 | 0 | for (unsigned int i=0; i < _interGroups.size(); ++i) { |
1559 | 0 | if (_interGroups[i].first.BitIsSet(a->GetIdx()) && _interGroups[i].second.BitIsSet(b->GetIdx())) |
1560 | 0 | validEle = true; |
1561 | 0 | if (_interGroups[i].first.BitIsSet(b->GetIdx()) && _interGroups[i].second.BitIsSet(a->GetIdx())) |
1562 | 0 | validEle = true; |
1563 | 0 | } |
1564 | |
|
1565 | 0 | if (!validEle) |
1566 | 0 | continue; |
1567 | 0 | } |
1568 | | |
1569 | 0 | if (a->IsConnected(b)) { |
1570 | 0 | continue; |
1571 | 0 | } |
1572 | 0 | if (a->IsOneThree(b)) { |
1573 | 0 | continue; |
1574 | 0 | } |
1575 | | |
1576 | | // Remember that at the moment, this term is not currently used |
1577 | | // These are also the Gasteiger charges, not the Qeq mentioned in the UFF paper |
1578 | 0 | elecalc.qq = KCAL_TO_KJ * 332.0637 * a->GetPartialCharge() * b->GetPartialCharge(); |
1579 | |
|
1580 | 0 | if (elecalc.qq) { |
1581 | 0 | elecalc.a = &*a; |
1582 | 0 | elecalc.b = &*b; |
1583 | |
|
1584 | 0 | elecalc.SetupPointers(); |
1585 | 0 | _electrostaticcalculations.push_back(elecalc); |
1586 | 0 | } |
1587 | 0 | } |
1588 | 0 | return true; |
1589 | 0 | } |
1590 | | |
1591 | | bool OBForceFieldUFF::SetupPointers() |
1592 | 0 | { |
1593 | 0 | for (unsigned int i = 0; i < _bondcalculations.size(); ++i) |
1594 | 0 | _bondcalculations[i].SetupPointers(); |
1595 | 0 | for (unsigned int i = 0; i < _anglecalculations.size(); ++i) |
1596 | 0 | _anglecalculations[i].SetupPointers(); |
1597 | 0 | for (unsigned int i = 0; i < _torsioncalculations.size(); ++i) |
1598 | 0 | _torsioncalculations[i].SetupPointers(); |
1599 | 0 | for (unsigned int i = 0; i < _oopcalculations.size(); ++i) |
1600 | 0 | _oopcalculations[i].SetupPointers(); |
1601 | 0 | for (unsigned int i = 0; i < _vdwcalculations.size(); ++i) |
1602 | 0 | _vdwcalculations[i].SetupPointers(); |
1603 | 0 | for (unsigned int i = 0; i < _electrostaticcalculations.size(); ++i) |
1604 | 0 | _electrostaticcalculations[i].SetupPointers(); |
1605 | |
|
1606 | 0 | return true; |
1607 | 0 | } |
1608 | | |
1609 | | bool OBForceFieldUFF::ParseParamFile() |
1610 | 0 | { |
1611 | 0 | vector<string> vs; |
1612 | 0 | char buffer[BUFF_SIZE]; |
1613 | |
|
1614 | 0 | OBFFParameter parameter; |
1615 | | |
1616 | | // open data/UFF.prm |
1617 | 0 | ifstream ifs; |
1618 | 0 | if (OpenDatafile(ifs, "UFF.prm").length() == 0) { |
1619 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Cannot open UFF.prm", obError); |
1620 | 0 | return false; |
1621 | 0 | } |
1622 | | |
1623 | | // Set the locale for number parsing to avoid locale issues: PR#1785463 |
1624 | 0 | obLocale.SetLocale(); |
1625 | |
|
1626 | 0 | while (ifs.getline(buffer, BUFF_SIZE)) { |
1627 | 0 | tokenize(vs, buffer); |
1628 | 0 | if (vs.size() < 13) |
1629 | 0 | continue; |
1630 | | |
1631 | 0 | if (EQn(buffer, "param", 5)) { |
1632 | | // set up all parameters from this |
1633 | 0 | parameter.clear(); |
1634 | 0 | parameter._a = vs[1]; // atom type |
1635 | 0 | parameter._dpar.push_back(atof(vs[2].c_str())); // r1 |
1636 | 0 | parameter._dpar.push_back(atof(vs[3].c_str())); // theta0 |
1637 | 0 | parameter._dpar.push_back(atof(vs[4].c_str())); // x1 |
1638 | 0 | parameter._dpar.push_back(atof(vs[5].c_str())); // D1 |
1639 | 0 | parameter._dpar.push_back(atof(vs[6].c_str())); // zeta |
1640 | 0 | parameter._dpar.push_back(atof(vs[7].c_str())); // Z1 |
1641 | 0 | parameter._dpar.push_back(atof(vs[8].c_str())); // Vi |
1642 | 0 | parameter._dpar.push_back(atof(vs[9].c_str())); // Uj |
1643 | 0 | parameter._dpar.push_back(atof(vs[10].c_str())); // Xi |
1644 | 0 | parameter._dpar.push_back(atof(vs[11].c_str())); // Hard |
1645 | 0 | parameter._dpar.push_back(atof(vs[12].c_str())); // Radius |
1646 | |
|
1647 | 0 | parameter.b = 0; // used for tracking number of angles in 5-coordinate |
1648 | 0 | parameter.c = 0; |
1649 | |
|
1650 | 0 | char coord = vs[1][1] ? vs[1][2] : '\0'; // 3rd character of atom type, if any |
1651 | 0 | switch (coord) { |
1652 | 0 | case '1': // linear |
1653 | 0 | parameter._ipar.push_back(1); |
1654 | 0 | break; |
1655 | 0 | case '2': // trigonal planar (sp2) |
1656 | 0 | case 'R': // aromatic (N_R) |
1657 | 0 | parameter._ipar.push_back(2); |
1658 | 0 | break; |
1659 | 0 | case '3': // tetrahedral (sp3) |
1660 | 0 | parameter._ipar.push_back(3); |
1661 | 0 | break; |
1662 | 0 | case '4': // square planar |
1663 | 0 | parameter._ipar.push_back(4); |
1664 | 0 | break; |
1665 | 0 | case '5': // trigonal bipyramidal -- not actually in parameterization |
1666 | 0 | parameter._ipar.push_back(5); |
1667 | 0 | break; |
1668 | 0 | case '6': // octahedral |
1669 | 0 | parameter._ipar.push_back(6); |
1670 | 0 | break; |
1671 | 0 | case '7': // pentagonal bipyramidal -- not actually in parameterization |
1672 | 0 | parameter._ipar.push_back(7); |
1673 | 0 | break; |
1674 | 0 | default: // general case (unknown coordination) |
1675 | | // These atoms appear to generally be linear coordination like Cl |
1676 | 0 | parameter._ipar.push_back(1); |
1677 | 0 | } |
1678 | | |
1679 | 0 | _ffparams.push_back(parameter); |
1680 | 0 | } |
1681 | 0 | } |
1682 | | |
1683 | 0 | if (ifs) |
1684 | 0 | ifs.close(); |
1685 | | |
1686 | | // return the locale to the original one |
1687 | 0 | obLocale.RestoreLocale(); |
1688 | |
|
1689 | 0 | return 0; |
1690 | 0 | } |
1691 | | |
1692 | | bool OBForceFieldUFF::SetTypes() |
1693 | 0 | { |
1694 | 0 | vector<vector<int> > _mlist; //!< match list for atom typing |
1695 | 0 | vector<pair<OBSmartsPattern*,string> > _vexttyp; //!< external atom type rules |
1696 | 0 | vector<vector<int> >::iterator j; |
1697 | 0 | vector<pair<OBSmartsPattern*,string> >::iterator i; |
1698 | 0 | OBSmartsPattern *sp; |
1699 | 0 | vector<string> vs; |
1700 | 0 | char buffer[BUFF_SIZE]; |
1701 | |
|
1702 | 0 | _mol.SetAtomTypesPerceived(); |
1703 | | |
1704 | | // open data/UFF.prm |
1705 | 0 | ifstream ifs; |
1706 | 0 | if (OpenDatafile(ifs, "UFF.prm").length() == 0) { |
1707 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Cannot open UFF.prm", obError); |
1708 | 0 | return false; |
1709 | 0 | } |
1710 | | |
1711 | 0 | while (ifs.getline(buffer, BUFF_SIZE)) { |
1712 | 0 | if (EQn(buffer, "atom", 4)) { |
1713 | 0 | tokenize(vs, buffer); |
1714 | |
|
1715 | 0 | sp = new OBSmartsPattern; |
1716 | 0 | if (sp->Init(vs[1])) { |
1717 | 0 | _vexttyp.push_back(pair<OBSmartsPattern*,string> (sp,vs[2])); |
1718 | 0 | } |
1719 | 0 | else { |
1720 | 0 | delete sp; |
1721 | 0 | sp = nullptr; |
1722 | 0 | obErrorLog.ThrowError(__FUNCTION__, " Could not parse atom type table from UFF.prm", obInfo); |
1723 | 0 | return false; |
1724 | 0 | } |
1725 | 0 | } |
1726 | 0 | } |
1727 | | |
1728 | 0 | for (i = _vexttyp.begin();i != _vexttyp.end();++i) { |
1729 | 0 | if (i->first->Match(_mol)) { |
1730 | 0 | _mlist = i->first->GetMapList(); |
1731 | 0 | for (j = _mlist.begin();j != _mlist.end();++j) { |
1732 | 0 | _mol.GetAtom((*j)[0])->SetType(i->second); |
1733 | 0 | } |
1734 | 0 | } |
1735 | 0 | } |
1736 | | |
1737 | | // Special atom types (i.e., P_3+q) |
1738 | | // (We can't easily do this with a SMARTS) |
1739 | 0 | FOR_ATOMS_OF_MOL(a, _mol) { |
1740 | 0 | if (a->GetAtomicNum() == 15) { |
1741 | | // loop through all the neighbors and see if we have a metal coordination |
1742 | 0 | bool organomet = false; |
1743 | 0 | int nbrElement; |
1744 | 0 | FOR_NBORS_OF_ATOM (nbr, &*a) { |
1745 | 0 | nbrElement = nbr->GetAtomicNum(); |
1746 | 0 | if ( (nbrElement >= 21 && nbrElement <= 31) // Sc to Ga |
1747 | 0 | || (nbrElement >= 39 && nbrElement <= 50) // Y to Sn |
1748 | 0 | || (nbrElement >= 57 && nbrElement <= 83) // La to Bi |
1749 | 0 | || (nbrElement >= 89) ) { |
1750 | 0 | organomet = true; |
1751 | 0 | break; // done! |
1752 | 0 | } |
1753 | 0 | } |
1754 | 0 | if (organomet) |
1755 | 0 | a->SetType("P_3+q"); |
1756 | 0 | } |
1757 | 0 | else if (a->GetAtomicNum() > 102) { // superheavy |
1758 | 0 | a->SetType("Lw6+3"); // prevent a crash with atoms beyond the parameterization Avogadro PR#741 |
1759 | 0 | } |
1760 | 0 | } |
1761 | |
|
1762 | 0 | IF_OBFF_LOGLVL_LOW { |
1763 | 0 | OBFFLog("\nA T O M T Y P E S\n\n"); |
1764 | 0 | OBFFLog("IDX\tTYPE\tRING\n"); |
1765 | |
|
1766 | 0 | FOR_ATOMS_OF_MOL (a, _mol) { |
1767 | 0 | snprintf(_logbuf, BUFF_SIZE, "%d\t%s\t%s\n", a->GetIdx(), a->GetType(), |
1768 | 0 | (a->IsInRing() ? (a->IsAromatic() ? "AR" : "AL") : "NO")); |
1769 | 0 | OBFFLog(_logbuf); |
1770 | 0 | } |
1771 | |
|
1772 | 0 | } |
1773 | |
|
1774 | 0 | if (ifs) |
1775 | 0 | ifs.close(); |
1776 | | |
1777 | | // Free memory |
1778 | 0 | for (i = _vexttyp.begin();i != _vexttyp.end();++i) { |
1779 | 0 | sp = i->first; |
1780 | 0 | delete sp; |
1781 | 0 | } |
1782 | |
|
1783 | 0 | return true; |
1784 | 0 | } |
1785 | | |
1786 | | double OBForceFieldUFF::Energy(bool gradients) |
1787 | 0 | { |
1788 | 0 | double energy; |
1789 | |
|
1790 | 0 | IF_OBFF_LOGLVL_MEDIUM |
1791 | 0 | OBFFLog("\nE N E R G Y\n\n"); |
1792 | |
|
1793 | 0 | if (gradients) { |
1794 | 0 | ClearGradients(); |
1795 | 0 | energy = E_Bond<true>(); |
1796 | 0 | energy += E_Angle<true>(); |
1797 | 0 | energy += E_Torsion<true>(); |
1798 | 0 | energy += E_OOP<true>(); |
1799 | 0 | energy += E_VDW<true>(); |
1800 | 0 | } else { |
1801 | 0 | energy = E_Bond<false>(); |
1802 | 0 | energy += E_Angle<false>(); |
1803 | 0 | energy += E_Torsion<false>(); |
1804 | 0 | energy += E_OOP<false>(); |
1805 | 0 | energy += E_VDW<false>(); |
1806 | 0 | } |
1807 | | |
1808 | | // The electrostatic term, by default is 0.0 |
1809 | | // You will need to call SetupEletrostatics if you want it |
1810 | | // energy += E_Electrostatic(gradients); |
1811 | |
|
1812 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
1813 | 0 | snprintf(_logbuf, BUFF_SIZE, "\nTOTAL ENERGY = %8.5f %s\n", energy, GetUnit().c_str()); |
1814 | 0 | OBFFLog(_logbuf); |
1815 | 0 | } |
1816 | |
|
1817 | 0 | return energy; |
1818 | 0 | } |
1819 | | |
1820 | | OBFFParameter* OBForceFieldUFF::GetParameterUFF(std::string a, vector<OBFFParameter> ¶meter) |
1821 | 0 | { |
1822 | 0 | for (unsigned int idx=0; idx < parameter.size(); ++idx) { |
1823 | 0 | if (a == parameter[idx]._a) { |
1824 | 0 | return ¶meter[idx]; |
1825 | 0 | } |
1826 | 0 | } |
1827 | 0 | return nullptr; |
1828 | 0 | } |
1829 | | |
1830 | | bool OBForceFieldUFF::ValidateGradients () |
1831 | 0 | { |
1832 | 0 | vector3 numgrad, anagrad, err; |
1833 | 0 | bool passed = true; // set to false if any component fails |
1834 | 0 | int coordIdx; |
1835 | |
|
1836 | 0 | OBFFLog("\nV A L I D A T E G R A D I E N T S\n\n"); |
1837 | 0 | OBFFLog("ATOM IDX NUMERICAL GRADIENT ANALYTICAL GRADIENT REL. ERROR (%) \n"); |
1838 | 0 | OBFFLog("----------------------------------------------------------------------------------------\n"); |
1839 | | // "XX (000.000, 000.000, 000.000) (000.000, 000.000, 000.000) (00.00, 00.00, 00.00)" |
1840 | |
|
1841 | 0 | FOR_ATOMS_OF_MOL (a, _mol) { |
1842 | 0 | coordIdx = (a->GetIdx() - 1) * 3; |
1843 | | |
1844 | | // OBFF_ENERGY (i.e., overall) |
1845 | 0 | numgrad = NumericalDerivative(&*a, OBFF_ENERGY); |
1846 | 0 | Energy(); // compute |
1847 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1848 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1849 | |
|
1850 | 0 | snprintf(_logbuf, BUFF_SIZE, "%2d (%7.3f, %7.3f, %7.3f) (%7.3f, %7.3f, %7.3f) (%5.2f, %5.2f, %5.2f)\n", a->GetIdx(), numgrad.x(), numgrad.y(), numgrad.z(), |
1851 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1852 | 0 | OBFFLog(_logbuf); |
1853 | | |
1854 | | // OBFF_EBOND |
1855 | 0 | numgrad = NumericalDerivative(&*a, OBFF_EBOND); |
1856 | 0 | ClearGradients(); |
1857 | 0 | E_Bond(); // compute |
1858 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1859 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1860 | |
|
1861 | 0 | snprintf(_logbuf, BUFF_SIZE, " bond (%7.3f, %7.3f, %7.3f) (%7.3f, %7.3f, %7.3f) (%5.2f, %5.2f, %5.2f)\n", numgrad.x(), numgrad.y(), numgrad.z(), |
1862 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1863 | 0 | OBFFLog(_logbuf); |
1864 | 0 | if (err.x() > 5.0 || err.y() > 5.0 || err.z() > 5.0) |
1865 | 0 | passed = false; |
1866 | | |
1867 | | // OBFF_EANGLE |
1868 | 0 | numgrad = NumericalDerivative(&*a, OBFF_EANGLE); |
1869 | 0 | ClearGradients(); |
1870 | 0 | E_Angle(); // compute |
1871 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1872 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1873 | |
|
1874 | 0 | snprintf(_logbuf, BUFF_SIZE, " angle (%7.3f, %7.3f, %7.3f) (%7.3f, %7.3f, %7.3f) (%5.2f, %5.2f, %5.2f)\n", numgrad.x(), numgrad.y(), numgrad.z(), |
1875 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1876 | 0 | OBFFLog(_logbuf); |
1877 | 0 | if (err.x() > 8.0 || err.y() > 8.0 || err.z() > 8.0) |
1878 | 0 | passed = false; |
1879 | | |
1880 | | // OBFF_ETORSION |
1881 | 0 | numgrad = NumericalDerivative(&*a, OBFF_ETORSION); |
1882 | 0 | ClearGradients(); |
1883 | 0 | E_Torsion(); // compute |
1884 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1885 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1886 | |
|
1887 | 0 | snprintf(_logbuf, BUFF_SIZE, " torsion (%7.3f, %7.3f, %7.3f) (%7.3f, %7.3f, %7.3f) (%5.2f, %5.2f, %5.2f)\n", numgrad.x(), numgrad.y(), numgrad.z(), |
1888 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1889 | 0 | OBFFLog(_logbuf); |
1890 | | // 8% tolerance here because some 180 torsions cause numerical instability |
1891 | 0 | if (err.x() > 8.0 || err.y() > 8.0 || err.z() > 8.0) |
1892 | 0 | passed = false; |
1893 | | |
1894 | | // OBFF_EOOP |
1895 | 0 | numgrad = NumericalDerivative(&*a, OBFF_EOOP); |
1896 | 0 | ClearGradients(); |
1897 | 0 | E_OOP(); // compute |
1898 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1899 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1900 | |
|
1901 | 0 | snprintf(_logbuf, BUFF_SIZE, " oop (%7.3f, %7.3f, %7.3f) (%7.3f, %7.3f, %7.3f) (%5.2f, %5.2f, %5.2f)\n", numgrad.x(), numgrad.y(), numgrad.z(), |
1902 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1903 | 0 | OBFFLog(_logbuf); |
1904 | | // We don't care if the OOP error is relatively large |
1905 | | // if (err.x() > 5.0 || err.y() > 5.0 || err.z() > 5.0) |
1906 | | // passed = false; |
1907 | | |
1908 | | // OBFF_EVDW |
1909 | 0 | numgrad = NumericalDerivative(&*a, OBFF_EVDW); |
1910 | 0 | ClearGradients(); |
1911 | 0 | E_VDW(); // compute |
1912 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1913 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1914 | |
|
1915 | 0 | snprintf(_logbuf, BUFF_SIZE, " vdw (%7.3f, %7.3f, %7.3f) (%7.3f, %7.3f, %7.3f) (%5.2f, %5.2f, %5.2f)\n", numgrad.x(), numgrad.y(), numgrad.z(), |
1916 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1917 | 0 | OBFFLog(_logbuf); |
1918 | 0 | if (err.x() > 5.0 || err.y() > 5.0 || err.z() > 5.0) |
1919 | 0 | passed = false; |
1920 | | |
1921 | | // OBFF_EELECTROSTATIC |
1922 | 0 | numgrad = NumericalDerivative(&*a, OBFF_EELECTROSTATIC); |
1923 | 0 | ClearGradients(); |
1924 | 0 | E_Electrostatic(); // compute |
1925 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1926 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1927 | |
|
1928 | 0 | snprintf(_logbuf, BUFF_SIZE, " electro (%7.3f, %7.3f, %7.3f) (%7.3f, %7.3f, %7.3f) (%5.2f, %5.2f, %5.2f)\n", numgrad.x(), numgrad.y(), numgrad.z(), |
1929 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1930 | 0 | OBFFLog(_logbuf); |
1931 | 0 | if (err.x() > 5.0 || err.y() > 5.0 || err.z() > 5.0) |
1932 | 0 | passed = false; |
1933 | 0 | } |
1934 | |
|
1935 | 0 | return passed; // did we pass every single component? |
1936 | 0 | } |
1937 | | |
1938 | | } // end namespace OpenBabel |
1939 | | |
1940 | | //! \file forcefieldUFF.cpp |
1941 | | //! \brief UFF force field |