/src/openbabel/src/forcefields/forcefieldghemical.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | forcefieldghemical.cpp - Ghemical force field. |
3 | | |
4 | | Copyright (C) 2006-2007 by Tim Vandermeersch <tim.vandermeersch@gmail.com> |
5 | | |
6 | | This file is part of the Open Babel project. |
7 | | For more information, see <http://openbabel.org/> |
8 | | |
9 | | This program is free software; you can redistribute it and/or modify |
10 | | it under the terms of the GNU General Public License as published by |
11 | | the Free Software Foundation version 2 of the License. |
12 | | |
13 | | This program is distributed in the hope that it will be useful, |
14 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | GNU General Public License for more details. |
17 | | ***********************************************************************/ |
18 | | |
19 | | #include <openbabel/babelconfig.h> |
20 | | #include <openbabel/mol.h> |
21 | | #include <openbabel/locale.h> |
22 | | |
23 | | #include "forcefieldghemical.h" |
24 | | #include <openbabel/bond.h> |
25 | | #include <openbabel/obiter.h> |
26 | | #include <openbabel/oberror.h> |
27 | | #include <openbabel/parsmart.h> |
28 | | #include <openbabel/obutil.h> |
29 | | |
30 | | #include <cstdlib> |
31 | | |
32 | | using namespace std; |
33 | | |
34 | | namespace OpenBabel |
35 | | { |
36 | | template<bool gradients> |
37 | | void OBFFBondCalculationGhemical::Compute() |
38 | 0 | { |
39 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b)) { |
40 | 0 | energy = 0.0; |
41 | 0 | return; |
42 | 0 | } |
43 | | |
44 | 0 | double delta2; |
45 | |
|
46 | 0 | if (gradients) { |
47 | 0 | rab = OBForceField::VectorBondDerivative(pos_a, pos_b, force_a, force_b); |
48 | 0 | delta = rab - r0; |
49 | 0 | delta2 = delta * delta; |
50 | |
|
51 | 0 | const double dE = 2.0 * kb * delta; |
52 | |
|
53 | 0 | OBForceField::VectorSelfMultiply(force_a, dE); |
54 | 0 | OBForceField::VectorSelfMultiply(force_b, dE); |
55 | 0 | } else { |
56 | 0 | rab = OBForceField::VectorDistance(pos_a, pos_b); |
57 | 0 | delta = rab - r0; |
58 | 0 | delta2 = delta * delta; |
59 | 0 | } |
60 | |
|
61 | 0 | energy = kb * delta2; |
62 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFBondCalculationGhemical::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFBondCalculationGhemical::Compute<false>() |
63 | | |
64 | | template<bool gradients> |
65 | | double OBForceFieldGhemical::E_Bond() |
66 | 0 | { |
67 | 0 | vector<OBFFBondCalculationGhemical>::iterator i; |
68 | 0 | double energy = 0.0; |
69 | |
|
70 | 0 | IF_OBFF_LOGLVL_HIGH { |
71 | 0 | OBFFLog("\nB O N D S T R E T C H I N G\n\n"); |
72 | 0 | OBFFLog("ATOM TYPES BOND BOND IDEAL FORCE\n"); |
73 | 0 | OBFFLog(" I J TYPE LENGTH LENGTH CONSTANT DELTA ENERGY\n"); |
74 | 0 | OBFFLog("------------------------------------------------------------------------\n"); |
75 | 0 | } |
76 | |
|
77 | 0 | for (i = _bondcalculations.begin(); i != _bondcalculations.end(); ++i) { |
78 | |
|
79 | 0 | i->template Compute<gradients>(); |
80 | 0 | energy += i->energy; |
81 | |
|
82 | 0 | if (gradients) { |
83 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
84 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
85 | 0 | } |
86 | |
|
87 | 0 | IF_OBFF_LOGLVL_HIGH { |
88 | 0 | snprintf(_logbuf, BUFF_SIZE, "%s %s %d %8.3f %8.3f %8.3f %8.3f %8.3f\n", (*i).a->GetType(), (*i).b->GetType(), |
89 | 0 | (*i).bt, (*i).rab, (*i).r0, (*i).kb, (*i).delta, (*i).energy); |
90 | 0 | OBFFLog(_logbuf); |
91 | 0 | } |
92 | 0 | } |
93 | |
|
94 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
95 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL BOND STRETCHING ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
96 | 0 | OBFFLog(_logbuf); |
97 | 0 | } |
98 | 0 | return energy; |
99 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldGhemical::E_Bond<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldGhemical::E_Bond<false>() |
100 | | |
101 | | template<bool gradients> |
102 | | void OBFFAngleCalculationGhemical::Compute() |
103 | 0 | { |
104 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b, idx_c)) { |
105 | 0 | energy = 0.0; |
106 | 0 | return; |
107 | 0 | } |
108 | | |
109 | 0 | double delta2; |
110 | |
|
111 | 0 | if (gradients) { |
112 | 0 | theta = OBForceField::VectorAngleDerivative(pos_a, pos_b, pos_c, force_a, force_b, force_c); |
113 | 0 | delta = theta - theta0; |
114 | |
|
115 | 0 | const double dE = RAD_TO_DEG * 2.0 * ka * delta; |
116 | |
|
117 | 0 | OBForceField::VectorSelfMultiply(force_a, dE); |
118 | 0 | OBForceField::VectorSelfMultiply(force_b, dE); |
119 | 0 | OBForceField::VectorSelfMultiply(force_c, dE); |
120 | 0 | } else { |
121 | 0 | theta = OBForceField::VectorAngle(pos_a, pos_b, pos_c); |
122 | 0 | delta = theta - theta0; |
123 | 0 | } |
124 | |
|
125 | 0 | if (!isfinite(theta)) |
126 | 0 | theta = 0.0; // doesn't explain why GetAngle is returning NaN but solves it for us; |
127 | |
|
128 | 0 | delta2 = delta * delta; |
129 | |
|
130 | 0 | energy = ka * delta2; |
131 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFAngleCalculationGhemical::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFAngleCalculationGhemical::Compute<false>() |
132 | | |
133 | | template<bool gradients> |
134 | | double OBForceFieldGhemical::E_Angle() |
135 | 0 | { |
136 | 0 | vector<OBFFAngleCalculationGhemical>::iterator i; |
137 | 0 | double energy = 0.0; |
138 | |
|
139 | 0 | IF_OBFF_LOGLVL_HIGH { |
140 | 0 | OBFFLog("\nA N G L E B E N D I N G\n\n"); |
141 | 0 | OBFFLog("ATOM TYPES VALENCE IDEAL FORCE\n"); |
142 | 0 | OBFFLog(" I J K ANGLE ANGLE CONSTANT DELTA ENERGY\n"); |
143 | 0 | OBFFLog("-----------------------------------------------------------------------------\n"); |
144 | 0 | } |
145 | |
|
146 | 0 | for (i = _anglecalculations.begin(); i != _anglecalculations.end(); ++i) { |
147 | |
|
148 | 0 | i->template Compute<gradients>(); |
149 | 0 | energy += i->energy; |
150 | |
|
151 | 0 | if (gradients) { |
152 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
153 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
154 | 0 | AddGradient((*i).force_c, (*i).idx_c); |
155 | 0 | } |
156 | |
|
157 | 0 | IF_OBFF_LOGLVL_HIGH { |
158 | 0 | snprintf(_logbuf, BUFF_SIZE, "%s %s %s %8.3f %8.3f %8.3f %8.3f %8.3f\n", (*i).a->GetType(), (*i).b->GetType(), |
159 | 0 | (*i).c->GetType(), (*i).theta, (*i).theta0, (*i).ka, (*i).delta, (*i).energy); |
160 | 0 | OBFFLog(_logbuf); |
161 | 0 | } |
162 | 0 | } |
163 | |
|
164 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
165 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL ANGLE BENDING ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
166 | 0 | OBFFLog(_logbuf); |
167 | 0 | } |
168 | 0 | return energy; |
169 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldGhemical::E_Angle<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldGhemical::E_Angle<false>() |
170 | | |
171 | | template<bool gradients> |
172 | | void OBFFTorsionCalculationGhemical::Compute() |
173 | 0 | { |
174 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b, idx_c, idx_d)) { |
175 | 0 | energy = 0.0; |
176 | 0 | return; |
177 | 0 | } |
178 | | |
179 | 0 | if (gradients) { |
180 | 0 | tor = DEG_TO_RAD * OBForceField::VectorTorsionDerivative(pos_a, pos_b, pos_c, pos_d, |
181 | 0 | force_a, force_b, force_c, force_d); |
182 | 0 | if (!isfinite(tor)) |
183 | 0 | tor = 1.0e-3; |
184 | |
|
185 | 0 | const double sine = sin(tor); |
186 | 0 | const double sine2 = sin(2.0 * tor); |
187 | 0 | const double sine3 = sin(3.0 * tor); |
188 | 0 | const double dE = k1 * sine - k2 * 2.0 * sine2 + k3 * 3.0 * sine3; |
189 | |
|
190 | 0 | OBForceField::VectorSelfMultiply(force_a, dE); |
191 | 0 | OBForceField::VectorSelfMultiply(force_b, dE); |
192 | 0 | OBForceField::VectorSelfMultiply(force_c, dE); |
193 | 0 | OBForceField::VectorSelfMultiply(force_d, dE); |
194 | 0 | } else { |
195 | 0 | tor = DEG_TO_RAD * OBForceField::VectorTorsion(pos_a, pos_b, pos_c, pos_d); |
196 | 0 | if (!isfinite(tor)) // stop any NaN or infinity |
197 | 0 | tor = 1.0e-3; // rather than NaN |
198 | 0 | } |
199 | |
|
200 | 0 | const double cosine = cos(tor); |
201 | 0 | const double cosine2 = cos(2.0 * tor); |
202 | 0 | const double cosine3 = cos(3.0 * tor); |
203 | |
|
204 | 0 | const double phi1 = 1.0 + cosine; |
205 | 0 | const double phi2 = 1.0 - cosine2; |
206 | 0 | const double phi3 = 1.0 + cosine3; |
207 | |
|
208 | 0 | energy = k1 * phi1 + k2 * phi2 + k3 * phi3; |
209 | |
|
210 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFTorsionCalculationGhemical::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFTorsionCalculationGhemical::Compute<false>() |
211 | | |
212 | | template<bool gradients> |
213 | | double OBForceFieldGhemical::E_Torsion() |
214 | 0 | { |
215 | 0 | vector<OBFFTorsionCalculationGhemical>::iterator i; |
216 | 0 | double energy = 0.0; |
217 | |
|
218 | 0 | IF_OBFF_LOGLVL_HIGH { |
219 | 0 | OBFFLog("\nT O R S I O N A L\n\n"); |
220 | 0 | OBFFLog("----ATOM TYPES----- FORCE TORSION\n"); |
221 | 0 | OBFFLog(" I J K L CONSTANT s ANGLE n ENERGY\n"); |
222 | 0 | OBFFLog("----------------------------------------------------------------\n"); |
223 | 0 | } |
224 | |
|
225 | 0 | for (i = _torsioncalculations.begin(); i != _torsioncalculations.end(); ++i) { |
226 | |
|
227 | 0 | i->template Compute<gradients>(); |
228 | 0 | energy += i->energy; |
229 | |
|
230 | 0 | if (gradients) { |
231 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
232 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
233 | 0 | AddGradient((*i).force_c, (*i).idx_c); |
234 | 0 | AddGradient((*i).force_d, (*i).idx_d); |
235 | 0 | } |
236 | |
|
237 | 0 | IF_OBFF_LOGLVL_HIGH { |
238 | 0 | snprintf(_logbuf, BUFF_SIZE, "%s %s %s %s %6.3f %5.0f %8.3f %1.0f %8.3f\n", (*i).a->GetType(), (*i).b->GetType(), |
239 | 0 | (*i).c->GetType(), (*i).d->GetType(), (*i).V, (*i).s, (*i).tor, (*i).n, (*i).energy); |
240 | 0 | OBFFLog(_logbuf); |
241 | 0 | } |
242 | 0 | } |
243 | |
|
244 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
245 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL TORSIONAL ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
246 | 0 | OBFFLog(_logbuf); |
247 | 0 | } |
248 | |
|
249 | 0 | return energy; |
250 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldGhemical::E_Torsion<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldGhemical::E_Torsion<false>() |
251 | | |
252 | | template<bool gradients> |
253 | | void OBFFVDWCalculationGhemical::Compute() |
254 | 0 | { |
255 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b)) { |
256 | 0 | energy = 0.0; |
257 | 0 | return; |
258 | 0 | } |
259 | | |
260 | 0 | if (gradients) { |
261 | 0 | rab = OBForceField::VectorDistanceDerivative(pos_a, pos_b, force_a, force_b); |
262 | 0 | } else { |
263 | 0 | rab = OBForceField::VectorDistance(pos_a, pos_b); |
264 | 0 | } |
265 | |
|
266 | 0 | const double term_a = rab / sigma12; |
267 | 0 | const double term_b = rab / sigma6; |
268 | 0 | const double term12 = pow(term_a, 12); |
269 | 0 | const double term6 = pow(term_b, 6); |
270 | |
|
271 | 0 | energy = (1.0 / term12) - (1.0 / term6); |
272 | |
|
273 | 0 | if (gradients) { |
274 | 0 | const double term13 = term_a * term12; // ^13 |
275 | 0 | const double term7 = term_b * term6; // ^7 |
276 | 0 | const double dE = - (12.0 / sigma12) * (1.0 / term13) + (6.0 / sigma6) * (1.0 / term7); |
277 | 0 | OBForceField::VectorSelfMultiply(force_a, dE); |
278 | 0 | OBForceField::VectorSelfMultiply(force_b, dE); |
279 | 0 | } |
280 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFVDWCalculationGhemical::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFVDWCalculationGhemical::Compute<false>() |
281 | | |
282 | | template<bool gradients> |
283 | | double OBForceFieldGhemical::E_VDW() |
284 | 0 | { |
285 | 0 | vector<OBFFVDWCalculationGhemical>::iterator i; |
286 | 0 | double energy = 0.0; |
287 | |
|
288 | 0 | IF_OBFF_LOGLVL_HIGH { |
289 | 0 | OBFFLog("\nV A N D E R W A A L S\n\n"); |
290 | 0 | OBFFLog("ATOM TYPES\n"); |
291 | 0 | OBFFLog(" I J Rij kij ENERGY\n"); |
292 | 0 | OBFFLog("-----------------------------------------\n"); |
293 | | // XX XX -000.000 -000.000 -000.000 -000.000 |
294 | 0 | } |
295 | |
|
296 | 0 | unsigned int j = 0; |
297 | 0 | for (i = _vdwcalculations.begin(); i != _vdwcalculations.end(); ++i, ++j) { |
298 | | // Cut-off check |
299 | 0 | if (_cutoff) |
300 | 0 | if (!_vdwpairs.BitIsSet(j)) |
301 | 0 | continue; |
302 | | |
303 | 0 | i->template Compute<gradients>(); |
304 | 0 | energy += i->energy; |
305 | |
|
306 | 0 | if (gradients) { |
307 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
308 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
309 | 0 | } |
310 | |
|
311 | 0 | IF_OBFF_LOGLVL_HIGH { |
312 | 0 | snprintf(_logbuf, BUFF_SIZE, "%s %s %8.3f %8.3f %8.3f\n", (*i).a->GetType(), (*i).b->GetType(), |
313 | 0 | (*i).rab, (*i).kab, (*i).energy); |
314 | 0 | OBFFLog(_logbuf); |
315 | 0 | } |
316 | 0 | } |
317 | |
|
318 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
319 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL VAN DER WAALS ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
320 | 0 | OBFFLog(_logbuf); |
321 | 0 | } |
322 | |
|
323 | 0 | return energy; |
324 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldGhemical::E_VDW<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldGhemical::E_VDW<false>() |
325 | | |
326 | | template<bool gradients> |
327 | | void OBFFElectrostaticCalculationGhemical::Compute() |
328 | 0 | { |
329 | 0 | if (OBForceField::IgnoreCalculation(idx_a, idx_b)) { |
330 | 0 | energy = 0.0; |
331 | 0 | return; |
332 | 0 | } |
333 | | |
334 | 0 | if (gradients) { |
335 | 0 | rab = OBForceField::VectorDistanceDerivative(pos_a, pos_b, force_a, force_b); |
336 | 0 | const double rab2 = rab * rab; |
337 | 0 | const double dE = -qq / rab2; |
338 | 0 | OBForceField::VectorSelfMultiply(force_a, dE); |
339 | 0 | OBForceField::VectorSelfMultiply(force_b, dE); |
340 | 0 | } else { |
341 | 0 | rab = OBForceField::VectorDistance(pos_a, pos_b); |
342 | 0 | } |
343 | |
|
344 | 0 | if (IsNearZero(rab, 1.0e-3)) |
345 | 0 | rab = 1.0e-3; |
346 | |
|
347 | 0 | energy = qq / rab; |
348 | 0 | } Unexecuted instantiation: void OpenBabel::OBFFElectrostaticCalculationGhemical::Compute<true>() Unexecuted instantiation: void OpenBabel::OBFFElectrostaticCalculationGhemical::Compute<false>() |
349 | | |
350 | | template<bool gradients> |
351 | | double OBForceFieldGhemical::E_Electrostatic() |
352 | 0 | { |
353 | 0 | vector<OBFFElectrostaticCalculationGhemical>::iterator i; |
354 | 0 | double energy = 0.0; |
355 | |
|
356 | 0 | IF_OBFF_LOGLVL_HIGH { |
357 | 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"); |
358 | 0 | OBFFLog("ATOM TYPES\n"); |
359 | 0 | OBFFLog(" I J Rij 332.17*QiQj ENERGY\n"); |
360 | 0 | OBFFLog("-------------------------------------------\n"); |
361 | | // XX XX -000.000 -000.000 -000.000 |
362 | 0 | } |
363 | |
|
364 | 0 | unsigned int j = 0; |
365 | 0 | for (i = _electrostaticcalculations.begin(); i != _electrostaticcalculations.end(); ++i, ++j) { |
366 | | // Cut-off check |
367 | 0 | if (_cutoff) |
368 | 0 | if (!_elepairs.BitIsSet(j)) |
369 | 0 | continue; |
370 | | |
371 | 0 | i->template Compute<gradients>(); |
372 | 0 | energy += i->energy; |
373 | |
|
374 | 0 | if (gradients) { |
375 | 0 | AddGradient((*i).force_a, (*i).idx_a); |
376 | 0 | AddGradient((*i).force_b, (*i).idx_b); |
377 | 0 | } |
378 | |
|
379 | 0 | IF_OBFF_LOGLVL_HIGH { |
380 | 0 | snprintf(_logbuf, BUFF_SIZE, "%s %s %8.3f %8.3f %8.3f\n", (*i).a->GetType(), (*i).b->GetType(), |
381 | 0 | (*i).rab, (*i).qq, (*i).energy); |
382 | 0 | OBFFLog(_logbuf); |
383 | 0 | } |
384 | 0 | } |
385 | |
|
386 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
387 | 0 | snprintf(_logbuf, BUFF_SIZE, " TOTAL ELECTROSTATIC ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
388 | 0 | OBFFLog(_logbuf); |
389 | 0 | } |
390 | |
|
391 | 0 | return energy; |
392 | 0 | } Unexecuted instantiation: double OpenBabel::OBForceFieldGhemical::E_Electrostatic<true>() Unexecuted instantiation: double OpenBabel::OBForceFieldGhemical::E_Electrostatic<false>() |
393 | | |
394 | | //*********************************************** |
395 | | //Make a global instance |
396 | | OBForceFieldGhemical theForceFieldGhemical("Ghemical", true); |
397 | | //*********************************************** |
398 | | |
399 | | OBForceFieldGhemical::~OBForceFieldGhemical() |
400 | 0 | { |
401 | 0 | } |
402 | | |
403 | | OBForceFieldGhemical &OBForceFieldGhemical::operator=(OBForceFieldGhemical &src) |
404 | 0 | { |
405 | 0 | _mol = src._mol; |
406 | 0 | _init = src._init; |
407 | |
|
408 | 0 | _ffbondparams = src._ffbondparams; |
409 | 0 | _ffangleparams = src._ffangleparams; |
410 | 0 | _fftorsionparams = src._fftorsionparams; |
411 | 0 | _ffvdwparams = src._ffvdwparams; |
412 | |
|
413 | 0 | _bondcalculations = src._bondcalculations; |
414 | 0 | _anglecalculations = src._anglecalculations; |
415 | 0 | _torsioncalculations = src._torsioncalculations; |
416 | 0 | _vdwcalculations = src._vdwcalculations; |
417 | 0 | _electrostaticcalculations = src._electrostaticcalculations; |
418 | |
|
419 | 0 | return *this; |
420 | 0 | } |
421 | | |
422 | | bool OBForceFieldGhemical::SetupCalculations() |
423 | 0 | { |
424 | 0 | OBFFParameter *parameter; |
425 | 0 | OBAtom *a, *b, *c, *d; |
426 | |
|
427 | 0 | IF_OBFF_LOGLVL_LOW |
428 | 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"); |
429 | | |
430 | | // |
431 | | // Bond Calculations |
432 | 0 | IF_OBFF_LOGLVL_LOW |
433 | 0 | OBFFLog("SETTING UP BOND CALCULATIONS...\n"); |
434 | |
|
435 | 0 | OBFFBondCalculationGhemical bondcalc; |
436 | 0 | int bondtype; |
437 | |
|
438 | 0 | _bondcalculations.clear(); |
439 | |
|
440 | 0 | FOR_BONDS_OF_MOL(bond, _mol) { |
441 | 0 | a = bond->GetBeginAtom(); |
442 | 0 | b = bond->GetEndAtom(); |
443 | | |
444 | | // skip this bond if the atoms are ignored |
445 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) || _constraints.IsIgnored(b->GetIdx()) ) |
446 | 0 | continue; |
447 | | |
448 | | // if there are any groups specified, check if the two bond atoms are in a single intraGroup |
449 | 0 | if (HasGroups()) { |
450 | 0 | bool validBond = false; |
451 | 0 | for (unsigned int i=0; i < _intraGroup.size(); ++i) { |
452 | 0 | if (_intraGroup[i].BitIsSet(a->GetIdx()) && _intraGroup[i].BitIsSet(b->GetIdx())) |
453 | 0 | validBond = true; |
454 | 0 | } |
455 | 0 | if (!validBond) |
456 | 0 | continue; |
457 | 0 | } |
458 | | |
459 | 0 | bondtype = bond->GetBondOrder(); |
460 | 0 | if (bond->IsAromatic()) |
461 | 0 | bondtype = 5; |
462 | |
|
463 | 0 | bondcalc.a = a; |
464 | 0 | bondcalc.b = b; |
465 | 0 | bondcalc.bt = bondtype; |
466 | |
|
467 | 0 | parameter = GetParameterGhemical(bondtype, a->GetType(), b->GetType(), nullptr, nullptr, _ffbondparams); |
468 | 0 | if (parameter == nullptr) { |
469 | 0 | parameter = GetParameterGhemical(bondtype, "FFFF", a->GetType(), nullptr, nullptr, _ffbondparams); |
470 | 0 | if (parameter == nullptr) { |
471 | 0 | parameter = GetParameterGhemical(bondtype, "FFFF", b->GetType(), nullptr, nullptr, _ffbondparams); |
472 | 0 | if (parameter == nullptr) { |
473 | 0 | bondcalc.kb = KCAL_TO_KJ * 500.0; |
474 | 0 | bondcalc.r0 = 1.100; |
475 | 0 | bondcalc.SetupPointers(); |
476 | |
|
477 | 0 | _bondcalculations.push_back(bondcalc); |
478 | |
|
479 | 0 | IF_OBFF_LOGLVL_LOW { |
480 | 0 | snprintf(_logbuf, BUFF_SIZE, "COULD NOT FIND PARAMETERS FOR BOND %s-%s, USING DEFAULT PARAMETERS\n", a->GetType(), b->GetType()); |
481 | 0 | OBFFLog(_logbuf); |
482 | 0 | } |
483 | |
|
484 | 0 | continue; |
485 | 0 | } |
486 | 0 | } |
487 | 0 | } |
488 | 0 | bondcalc.kb = KCAL_TO_KJ * parameter->_dpar[1]; |
489 | 0 | bondcalc.r0 = parameter->_dpar[0]; |
490 | 0 | bondcalc.SetupPointers(); |
491 | |
|
492 | 0 | _bondcalculations.push_back(bondcalc); |
493 | 0 | } |
494 | | |
495 | | // |
496 | | // Angle Calculations |
497 | | // |
498 | 0 | IF_OBFF_LOGLVL_LOW |
499 | 0 | OBFFLog("SETTING UP ANGLE CALCULATIONS...\n"); |
500 | |
|
501 | 0 | OBFFAngleCalculationGhemical anglecalc; |
502 | |
|
503 | 0 | _anglecalculations.clear(); |
504 | |
|
505 | 0 | FOR_ANGLES_OF_MOL(angle, _mol) { |
506 | 0 | b = _mol.GetAtom((*angle)[0] + 1); |
507 | 0 | a = _mol.GetAtom((*angle)[1] + 1); |
508 | 0 | c = _mol.GetAtom((*angle)[2] + 1); |
509 | | |
510 | | // skip this angle if the atoms are ignored |
511 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) || _constraints.IsIgnored(b->GetIdx()) || _constraints.IsIgnored(c->GetIdx()) ) |
512 | 0 | continue; |
513 | | |
514 | | // if there are any groups specified, check if the three angle atoms are in a single intraGroup |
515 | 0 | if (HasGroups()) { |
516 | 0 | bool validAngle = false; |
517 | 0 | for (unsigned int i=0; i < _intraGroup.size(); ++i) { |
518 | 0 | if (_intraGroup[i].BitIsSet(a->GetIdx()) && _intraGroup[i].BitIsSet(b->GetIdx()) && |
519 | 0 | _intraGroup[i].BitIsSet(c->GetIdx())) |
520 | 0 | validAngle = true; |
521 | 0 | } |
522 | 0 | if (!validAngle) |
523 | 0 | continue; |
524 | 0 | } |
525 | | |
526 | 0 | anglecalc.a = a; |
527 | 0 | anglecalc.b = b; |
528 | 0 | anglecalc.c = c; |
529 | |
|
530 | 0 | parameter = GetParameter(a->GetType(), b->GetType(), c->GetType(), nullptr, _ffangleparams); |
531 | 0 | if (parameter == nullptr) { |
532 | 0 | parameter = GetParameter("FFFF", b->GetType(), c->GetType(), nullptr, _ffangleparams); |
533 | 0 | if (parameter == nullptr) { |
534 | 0 | parameter = GetParameter(a->GetType(), b->GetType(), "FFFF", nullptr, _ffangleparams); |
535 | 0 | if (parameter == nullptr) { |
536 | 0 | parameter = GetParameter("FFFF", b->GetType(), "FFFF", nullptr, _ffangleparams); |
537 | 0 | if (parameter == nullptr) { |
538 | 0 | anglecalc.ka = KCAL_TO_KJ * 0.020; |
539 | 0 | anglecalc.theta0 = 120.0; |
540 | 0 | anglecalc.SetupPointers(); |
541 | |
|
542 | 0 | _anglecalculations.push_back(anglecalc); |
543 | |
|
544 | 0 | IF_OBFF_LOGLVL_LOW { |
545 | 0 | snprintf(_logbuf, BUFF_SIZE, "COULD NOT FIND PARAMETERS FOR ANGLE %s-%s-%s, USING DEFAULT PARAMETERS\n", a->GetType(), b->GetType(), c->GetType()); |
546 | 0 | OBFFLog(_logbuf); |
547 | 0 | } |
548 | |
|
549 | 0 | continue; |
550 | 0 | } |
551 | 0 | } |
552 | 0 | } |
553 | 0 | } |
554 | 0 | anglecalc.ka = KCAL_TO_KJ * parameter->_dpar[1]; |
555 | 0 | anglecalc.theta0 = parameter->_dpar[0]; |
556 | 0 | anglecalc.SetupPointers(); |
557 | |
|
558 | 0 | _anglecalculations.push_back(anglecalc); |
559 | 0 | } |
560 | | |
561 | | // |
562 | | // Torsion Calculations |
563 | | // |
564 | 0 | IF_OBFF_LOGLVL_LOW |
565 | 0 | OBFFLog("SETTING UP TORSION CALCULATIONS...\n"); |
566 | |
|
567 | 0 | OBFFTorsionCalculationGhemical torsioncalc; |
568 | 0 | int torsiontype; |
569 | 0 | int s; |
570 | |
|
571 | 0 | _torsioncalculations.clear(); |
572 | |
|
573 | 0 | FOR_TORSIONS_OF_MOL(t, _mol) { |
574 | 0 | a = _mol.GetAtom((*t)[0] + 1); |
575 | 0 | b = _mol.GetAtom((*t)[1] + 1); |
576 | 0 | c = _mol.GetAtom((*t)[2] + 1); |
577 | 0 | d = _mol.GetAtom((*t)[3] + 1); |
578 | | |
579 | | // skip this torsion if the atoms are ignored |
580 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) || _constraints.IsIgnored(b->GetIdx()) || |
581 | 0 | _constraints.IsIgnored(c->GetIdx()) || _constraints.IsIgnored(d->GetIdx()) ) |
582 | 0 | continue; |
583 | | |
584 | | // if there are any groups specified, check if the four torsion atoms are in a single intraGroup |
585 | 0 | if (HasGroups()) { |
586 | 0 | bool validTorsion = false; |
587 | 0 | for (unsigned int i=0; i < _intraGroup.size(); ++i) { |
588 | 0 | if (_intraGroup[i].BitIsSet(a->GetIdx()) && _intraGroup[i].BitIsSet(b->GetIdx()) && |
589 | 0 | _intraGroup[i].BitIsSet(c->GetIdx()) && _intraGroup[i].BitIsSet(d->GetIdx())) |
590 | 0 | validTorsion = true; |
591 | 0 | } |
592 | 0 | if (!validTorsion) |
593 | 0 | continue; |
594 | 0 | } |
595 | | |
596 | 0 | OBBond *bc = _mol.GetBond(b, c); |
597 | 0 | torsiontype = bc->GetBondOrder(); |
598 | 0 | if (bc->IsAromatic()) |
599 | 0 | torsiontype = 5; |
600 | |
|
601 | 0 | torsioncalc.a = a; |
602 | 0 | torsioncalc.b = b; |
603 | 0 | torsioncalc.c = c; |
604 | 0 | torsioncalc.d = d; |
605 | 0 | torsioncalc.tt = torsiontype; |
606 | |
|
607 | 0 | parameter = GetParameterGhemical(torsiontype, a->GetType(), b->GetType(), c->GetType(), d->GetType(), _fftorsionparams); |
608 | 0 | if (parameter == nullptr) { |
609 | 0 | parameter = GetParameterGhemical(torsiontype, "FFFF", b->GetType(), c->GetType(), d->GetType(), _fftorsionparams); |
610 | 0 | if (parameter == nullptr) { |
611 | 0 | parameter = GetParameterGhemical(torsiontype, a->GetType(), b->GetType(), c->GetType(), "FFFF", _fftorsionparams); |
612 | 0 | if (parameter == nullptr) { |
613 | 0 | parameter = GetParameterGhemical(torsiontype, "FFFF", b->GetType(), c->GetType(), "FFFF", _fftorsionparams); |
614 | 0 | if (parameter == nullptr) { |
615 | 0 | torsioncalc.V = 0.0; |
616 | 0 | torsioncalc.s = 1.0; |
617 | 0 | torsioncalc.n = 1.0; |
618 | |
|
619 | 0 | torsioncalc.k1 = 0.0; |
620 | 0 | torsioncalc.k2 = 0.0; |
621 | 0 | torsioncalc.k3 = 0.0; |
622 | 0 | torsioncalc.SetupPointers(); |
623 | 0 | _torsioncalculations.push_back(torsioncalc); |
624 | |
|
625 | 0 | IF_OBFF_LOGLVL_LOW { |
626 | 0 | snprintf(_logbuf, BUFF_SIZE, "COULD NOT FIND PARAMETERS FOR TORSION %s-%s-%s-%s, USING DEFAULT PARAMETERS\n", a->GetType(), b->GetType(), c->GetType(), d->GetType()); |
627 | 0 | OBFFLog(_logbuf); |
628 | 0 | } |
629 | |
|
630 | 0 | continue; |
631 | 0 | } |
632 | 0 | } |
633 | 0 | } |
634 | 0 | } |
635 | 0 | torsioncalc.V = KCAL_TO_KJ * parameter->_dpar[0]; |
636 | 0 | torsioncalc.s = parameter->_dpar[1]; |
637 | 0 | torsioncalc.n = parameter->_dpar[2]; |
638 | |
|
639 | 0 | s = (int) (torsioncalc.s * torsioncalc.n); |
640 | 0 | switch(s) { |
641 | 0 | case +3: |
642 | 0 | torsioncalc.k1 = 0.0; |
643 | 0 | torsioncalc.k2 = 0.0; |
644 | 0 | torsioncalc.k3 = torsioncalc.V; |
645 | 0 | break; |
646 | 0 | case +2: |
647 | 0 | torsioncalc.k1 = 0.0; |
648 | 0 | torsioncalc.k2 = -torsioncalc.V; |
649 | 0 | torsioncalc.k3 = 0.0; |
650 | 0 | break; |
651 | 0 | case +1: |
652 | 0 | torsioncalc.k1 = torsioncalc.V; |
653 | 0 | torsioncalc.k2 = 0.0; |
654 | 0 | torsioncalc.k3 = 0.0; |
655 | 0 | break; |
656 | 0 | case -1: |
657 | 0 | torsioncalc.k1 = -torsioncalc.V; |
658 | 0 | torsioncalc.k2 = 0.0; |
659 | 0 | torsioncalc.k3 = 0.0; |
660 | 0 | break; |
661 | 0 | case -2: |
662 | 0 | torsioncalc.k1 = 0.0; |
663 | 0 | torsioncalc.k2 = torsioncalc.V; |
664 | 0 | torsioncalc.k3 = 0.0; |
665 | 0 | break; |
666 | 0 | case -3: |
667 | 0 | torsioncalc.k1 = 0.0; |
668 | 0 | torsioncalc.k2 = 0.0; |
669 | 0 | torsioncalc.k3 = -torsioncalc.V; |
670 | 0 | break; |
671 | 0 | } |
672 | | |
673 | 0 | torsioncalc.SetupPointers(); |
674 | 0 | _torsioncalculations.push_back(torsioncalc); |
675 | 0 | } |
676 | | |
677 | | // |
678 | | // VDW Calculations |
679 | | // |
680 | 0 | IF_OBFF_LOGLVL_LOW |
681 | 0 | OBFFLog("SETTING UP VAN DER WAALS CALCULATIONS...\n"); |
682 | |
|
683 | 0 | OBFFVDWCalculationGhemical vdwcalc; |
684 | 0 | OBFFParameter *parameter_a, *parameter_b; |
685 | |
|
686 | 0 | _vdwcalculations.clear(); |
687 | |
|
688 | 0 | FOR_PAIRS_OF_MOL(p, _mol) { |
689 | 0 | a = _mol.GetAtom((*p)[0]); |
690 | 0 | b = _mol.GetAtom((*p)[1]); |
691 | | |
692 | | // skip this vdw if the atoms are ignored |
693 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) || _constraints.IsIgnored(b->GetIdx()) ) |
694 | 0 | continue; |
695 | | |
696 | | // if there are any groups specified, check if the two atoms are in a single _interGroup or if |
697 | | // two two atoms are in one of the _interGroups pairs. |
698 | 0 | if (HasGroups()) { |
699 | 0 | bool validVDW = false; |
700 | 0 | for (unsigned int i=0; i < _interGroup.size(); ++i) { |
701 | 0 | if (_interGroup[i].BitIsSet(a->GetIdx()) && _interGroup[i].BitIsSet(b->GetIdx())) |
702 | 0 | validVDW = true; |
703 | 0 | } |
704 | 0 | for (unsigned int i=0; i < _interGroups.size(); ++i) { |
705 | 0 | if (_interGroups[i].first.BitIsSet(a->GetIdx()) && _interGroups[i].second.BitIsSet(b->GetIdx())) |
706 | 0 | validVDW = true; |
707 | 0 | if (_interGroups[i].first.BitIsSet(b->GetIdx()) && _interGroups[i].second.BitIsSet(a->GetIdx())) |
708 | 0 | validVDW = true; |
709 | 0 | } |
710 | |
|
711 | 0 | if (!validVDW) |
712 | 0 | continue; |
713 | 0 | } |
714 | | |
715 | 0 | parameter_a = GetParameter(a->GetType(), nullptr, nullptr, nullptr, _ffvdwparams); |
716 | 0 | if (parameter_a == nullptr) { // no vdw parameter -> use hydrogen |
717 | 0 | vdwcalc.Ra = 1.5; |
718 | 0 | vdwcalc.ka = 0.042; |
719 | |
|
720 | 0 | IF_OBFF_LOGLVL_LOW { |
721 | 0 | snprintf(_logbuf, BUFF_SIZE, "COULD NOT FIND VDW PARAMETERS FOR ATOM %s, USING HYDROGEN VDW PARAMETERS\n", a->GetType()); |
722 | 0 | OBFFLog(_logbuf); |
723 | 0 | } |
724 | 0 | } else { |
725 | 0 | vdwcalc.Ra = parameter_a->_dpar[0]; |
726 | 0 | vdwcalc.ka = parameter_a->_dpar[1]; |
727 | 0 | } |
728 | |
|
729 | 0 | parameter_b = GetParameter(b->GetType(), nullptr, nullptr, nullptr, _ffvdwparams); |
730 | 0 | if (parameter_b == nullptr) { // no vdw parameter -> use hydrogen |
731 | 0 | vdwcalc.Rb = 1.5; |
732 | 0 | vdwcalc.kb = 0.042; |
733 | |
|
734 | 0 | IF_OBFF_LOGLVL_LOW { |
735 | 0 | snprintf(_logbuf, BUFF_SIZE, "COULD NOT FIND VDW PARAMETERS FOR ATOM %s, USING HYDROGEN VDW PARAMETERS\n", b->GetType()); |
736 | 0 | OBFFLog(_logbuf); |
737 | 0 | } |
738 | 0 | } else { |
739 | 0 | vdwcalc.Rb = parameter_b->_dpar[0]; |
740 | 0 | vdwcalc.kb = parameter_b->_dpar[1]; |
741 | 0 | } |
742 | |
|
743 | 0 | vdwcalc.a = &*a; |
744 | 0 | vdwcalc.b = &*b; |
745 | | |
746 | | //this calculations only need to be done once for each pair, |
747 | | //we do them now and save them for later use |
748 | 0 | vdwcalc.kab = KCAL_TO_KJ * sqrt(vdwcalc.ka * vdwcalc.kb); |
749 | | |
750 | | // 1-4 scaling |
751 | 0 | if (a->IsOneFour(b)) |
752 | 0 | vdwcalc.kab *= 0.5; |
753 | | /* |
754 | | vdwcalc.is14 = false; |
755 | | FOR_NBORS_OF_ATOM (nbr, a) |
756 | | FOR_NBORS_OF_ATOM (nbr2, &*nbr) |
757 | | FOR_NBORS_OF_ATOM (nbr3, &*nbr2) |
758 | | if (b == &*nbr3) { |
759 | | vdwcalc.is14 = true; |
760 | | vdwcalc.kab *= 0.5; |
761 | | } |
762 | | */ |
763 | | |
764 | | // not sure why this is needed, but validation showed it works... |
765 | | /* |
766 | | if (a->IsInRingSize(6) && b->IsInRingSize(6) && IsInSameRing(a, b)) |
767 | | vdwcalc.samering = true; |
768 | | else if ((a->IsInRingSize(5) || a->IsInRingSize(4)) && (b->IsInRingSize(5) || b->IsInRingSize(4))) |
769 | | vdwcalc.samering = true; |
770 | | else |
771 | | vdwcalc.samering = false; |
772 | | */ |
773 | |
|
774 | 0 | vdwcalc.sigma12 = (vdwcalc.Ra + vdwcalc.Rb) * pow(1.0 * vdwcalc.kab , 1.0 / 12.0); |
775 | 0 | vdwcalc.sigma6 = (vdwcalc.Ra + vdwcalc.Rb) * pow(2.0 * vdwcalc.kab , 1.0 / 6.0); |
776 | 0 | vdwcalc.SetupPointers(); |
777 | |
|
778 | 0 | _vdwcalculations.push_back(vdwcalc); |
779 | 0 | } |
780 | | |
781 | | // |
782 | | // Electrostatic Calculations |
783 | | // |
784 | 0 | IF_OBFF_LOGLVL_LOW |
785 | 0 | OBFFLog("SETTING UP ELECTROSTATIC CALCULATIONS...\n"); |
786 | |
|
787 | 0 | OBFFElectrostaticCalculationGhemical elecalc; |
788 | |
|
789 | 0 | _electrostaticcalculations.clear(); |
790 | |
|
791 | 0 | FOR_PAIRS_OF_MOL(p, _mol) { |
792 | 0 | a = _mol.GetAtom((*p)[0]); |
793 | 0 | b = _mol.GetAtom((*p)[1]); |
794 | | |
795 | | // skip this ele if the atoms are ignored |
796 | 0 | if ( _constraints.IsIgnored(a->GetIdx()) || _constraints.IsIgnored(b->GetIdx()) ) |
797 | 0 | continue; |
798 | | |
799 | | // if there are any groups specified, check if the two atoms are in a single _interGroup or if |
800 | | // two two atoms are in one of the _interGroups pairs. |
801 | 0 | if (HasGroups()) { |
802 | 0 | bool validEle = false; |
803 | 0 | for (unsigned int i=0; i < _interGroup.size(); ++i) { |
804 | 0 | if (_interGroup[i].BitIsSet(a->GetIdx()) && _interGroup[i].BitIsSet(b->GetIdx())) |
805 | 0 | validEle = true; |
806 | 0 | } |
807 | 0 | for (unsigned int i=0; i < _interGroups.size(); ++i) { |
808 | 0 | if (_interGroups[i].first.BitIsSet(a->GetIdx()) && _interGroups[i].second.BitIsSet(b->GetIdx())) |
809 | 0 | validEle = true; |
810 | 0 | if (_interGroups[i].first.BitIsSet(b->GetIdx()) && _interGroups[i].second.BitIsSet(a->GetIdx())) |
811 | 0 | validEle = true; |
812 | 0 | } |
813 | |
|
814 | 0 | if (!validEle) |
815 | 0 | continue; |
816 | 0 | } |
817 | | |
818 | 0 | elecalc.qq = KCAL_TO_KJ * 332.17 * a->GetPartialCharge() * b->GetPartialCharge() / _epsilon; |
819 | |
|
820 | 0 | if (elecalc.qq) { |
821 | 0 | elecalc.a = &*a; |
822 | 0 | elecalc.b = &*b; |
823 | | |
824 | | // 1-4 scaling |
825 | 0 | if (a->IsOneFour(b)) |
826 | 0 | elecalc.qq *= 0.5; |
827 | |
|
828 | 0 | elecalc.SetupPointers(); |
829 | 0 | _electrostaticcalculations.push_back(elecalc); |
830 | 0 | } |
831 | 0 | } |
832 | |
|
833 | 0 | return true; |
834 | 0 | } |
835 | | |
836 | | bool OBForceFieldGhemical::SetupPointers() |
837 | 0 | { |
838 | 0 | for (unsigned int i = 0; i < _bondcalculations.size(); ++i) |
839 | 0 | _bondcalculations[i].SetupPointers(); |
840 | 0 | for (unsigned int i = 0; i < _anglecalculations.size(); ++i) |
841 | 0 | _anglecalculations[i].SetupPointers(); |
842 | 0 | for (unsigned int i = 0; i < _torsioncalculations.size(); ++i) |
843 | 0 | _torsioncalculations[i].SetupPointers(); |
844 | 0 | for (unsigned int i = 0; i < _vdwcalculations.size(); ++i) |
845 | 0 | _vdwcalculations[i].SetupPointers(); |
846 | 0 | for (unsigned int i = 0; i < _electrostaticcalculations.size(); ++i) |
847 | 0 | _electrostaticcalculations[i].SetupPointers(); |
848 | |
|
849 | 0 | return true; |
850 | 0 | } |
851 | | |
852 | | |
853 | | bool OBForceFieldGhemical::ParseParamFile() |
854 | 0 | { |
855 | 0 | vector<string> vs; |
856 | 0 | char buffer[80]; |
857 | |
|
858 | 0 | OBFFParameter parameter; |
859 | | |
860 | | // open data/ghemical.prm |
861 | 0 | ifstream ifs; |
862 | 0 | if (OpenDatafile(ifs, "ghemical.prm").length() == 0) { |
863 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Cannot open ghemical.prm", obError); |
864 | 0 | return false; |
865 | 0 | } |
866 | | |
867 | | // Set the locale for number parsing to avoid locale issues: PR#1785463 |
868 | 0 | obLocale.SetLocale(); |
869 | |
|
870 | 0 | while (ifs.getline(buffer, 80)) { |
871 | 0 | tokenize(vs, buffer); |
872 | |
|
873 | 0 | if (EQn(buffer, "bond", 4)) { |
874 | 0 | parameter.clear(); |
875 | 0 | parameter._a = vs[1]; |
876 | 0 | parameter._b = vs[2]; |
877 | 0 | parameter._dpar.push_back(atof(vs[4].c_str())); // length |
878 | 0 | parameter._dpar.push_back(atof(vs[5].c_str())); // force cte |
879 | 0 | parameter._ipar.resize(1); |
880 | 0 | if (EQn(vs[3].c_str(), "S", 1)) |
881 | 0 | parameter._ipar[0] = 1; |
882 | 0 | if (EQn(vs[3].c_str(), "D", 1)) |
883 | 0 | parameter._ipar[0] = 2; |
884 | 0 | if (EQn(vs[3].c_str(), "T", 1)) |
885 | 0 | parameter._ipar[0] = 3; |
886 | 0 | if (EQn(vs[3].c_str(), "C", 1)) |
887 | 0 | parameter._ipar[0] = 5; |
888 | 0 | _ffbondparams.push_back(parameter); |
889 | 0 | } |
890 | 0 | if (EQn(buffer, "angle", 5)) { |
891 | 0 | parameter.clear(); |
892 | 0 | parameter._a = vs[1]; |
893 | 0 | parameter._b = vs[2]; |
894 | 0 | parameter._c = vs[3]; |
895 | 0 | parameter._dpar.push_back(atof(vs[5].c_str())); // angle |
896 | 0 | parameter._dpar.push_back(atof(vs[6].c_str())); // force cte |
897 | 0 | _ffangleparams.push_back(parameter); |
898 | 0 | } |
899 | 0 | if (EQn(buffer, "torsion", 7)) { |
900 | 0 | parameter.clear(); |
901 | 0 | parameter._a = vs[1]; |
902 | 0 | parameter._b = vs[2]; |
903 | 0 | parameter._c = vs[3]; |
904 | 0 | parameter._d = vs[4]; |
905 | 0 | parameter._dpar.resize(3); |
906 | 0 | parameter._dpar[0] = atof(vs[6].c_str()); // force cte |
907 | 0 | parameter._dpar[2] = atof(vs[8].c_str()); // n |
908 | 0 | if (EQn(vs[7].c_str(), "+", 1)) |
909 | 0 | parameter._dpar[1] = +1; // s |
910 | 0 | else if (EQn(vs[7].c_str(), "-", 1)) |
911 | 0 | parameter._dpar[1] = -1; // s |
912 | |
|
913 | 0 | parameter._ipar.resize(1); |
914 | 0 | if (EQn(vs[5].c_str(), "?S?", 3)) |
915 | 0 | parameter._ipar[0] = 1; |
916 | 0 | else if (EQn(vs[5].c_str(), "?D?", 3)) |
917 | 0 | parameter._ipar[0] = 2; |
918 | 0 | else if (EQn(vs[5].c_str(), "?T?", 3)) |
919 | 0 | parameter._ipar[0] = 3; |
920 | 0 | else if (EQn(vs[5].c_str(), "?C?", 3)) |
921 | 0 | parameter._ipar[0] = 5; |
922 | 0 | _fftorsionparams.push_back(parameter); |
923 | 0 | } |
924 | 0 | if (EQn(buffer, "vdw", 3)) { |
925 | 0 | parameter.clear(); |
926 | 0 | parameter._a = vs[1]; |
927 | 0 | parameter._dpar.push_back(atof(vs[2].c_str())); // r |
928 | 0 | parameter._dpar.push_back(atof(vs[3].c_str())); // force cte |
929 | 0 | _ffvdwparams.push_back(parameter); |
930 | 0 | } |
931 | 0 | if (EQn(buffer, "charge", 6)) { |
932 | 0 | parameter.clear(); |
933 | 0 | parameter._a = vs[1]; |
934 | 0 | parameter._b = vs[2]; |
935 | 0 | parameter._ipar.resize(1); |
936 | 0 | if (EQn(vs[3].c_str(), "S", 1)) |
937 | 0 | parameter._ipar[0] = 1; |
938 | 0 | else if (EQn(vs[3].c_str(), "D", 1)) |
939 | 0 | parameter._ipar[0] = 2; |
940 | 0 | parameter._dpar.push_back(atof(vs[4].c_str())); // charge |
941 | 0 | _ffchargeparams.push_back(parameter); |
942 | 0 | } |
943 | 0 | } |
944 | |
|
945 | 0 | if (ifs) |
946 | 0 | ifs.close(); |
947 | | |
948 | | // return the locale to the original one |
949 | 0 | obLocale.RestoreLocale(); |
950 | |
|
951 | 0 | return 0; |
952 | 0 | } |
953 | | |
954 | | bool OBForceFieldGhemical::SetTypes() |
955 | 0 | { |
956 | 0 | vector<vector<int> > _mlist; //!< match list for atom typing |
957 | 0 | vector<pair<OBSmartsPattern*,string> > _vexttyp; //!< external atom type rules |
958 | 0 | vector<vector<int> >::iterator j; |
959 | 0 | vector<pair<OBSmartsPattern*,string> >::iterator i; |
960 | 0 | OBSmartsPattern *sp; |
961 | 0 | vector<string> vs; |
962 | 0 | char buffer[80]; |
963 | |
|
964 | 0 | _mol.SetAtomTypesPerceived(); |
965 | | |
966 | | // open data/ghemical.prm |
967 | 0 | ifstream ifs; |
968 | 0 | if (OpenDatafile(ifs, "ghemical.prm").length() == 0) { |
969 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Cannot open ghemical.prm", obError); |
970 | 0 | return false; |
971 | 0 | } |
972 | | |
973 | | // Set the locale for number parsing to avoid locale issues: PR#1785463 |
974 | 0 | obLocale.SetLocale(); |
975 | |
|
976 | 0 | while (ifs.getline(buffer, 80)) { |
977 | 0 | if (EQn(buffer, "atom", 4)) { |
978 | 0 | tokenize(vs, buffer); |
979 | |
|
980 | 0 | sp = new OBSmartsPattern; |
981 | 0 | if (sp->Init(vs[1])) |
982 | 0 | _vexttyp.push_back(pair<OBSmartsPattern*,string> (sp,vs[2])); |
983 | 0 | else { |
984 | 0 | delete sp; |
985 | 0 | sp = nullptr; |
986 | 0 | obErrorLog.ThrowError(__FUNCTION__, " Could not parse atom type table from ghemical.prm", obInfo); |
987 | 0 | return false; |
988 | 0 | } |
989 | 0 | } |
990 | 0 | } |
991 | | |
992 | 0 | for (i = _vexttyp.begin();i != _vexttyp.end();++i) { |
993 | 0 | if (i->first->Match(_mol)) { |
994 | 0 | _mlist = i->first->GetMapList(); |
995 | 0 | for (j = _mlist.begin();j != _mlist.end();++j) { |
996 | 0 | _mol.GetAtom((*j)[0])->SetType(i->second); |
997 | 0 | } |
998 | 0 | } |
999 | 0 | } |
1000 | |
|
1001 | 0 | SetPartialCharges(); |
1002 | |
|
1003 | 0 | IF_OBFF_LOGLVL_LOW { |
1004 | 0 | OBFFLog("\nA T O M T Y P E S\n\n"); |
1005 | 0 | OBFFLog("IDX\tTYPE\tRING\n"); |
1006 | |
|
1007 | 0 | FOR_ATOMS_OF_MOL (a, _mol) { |
1008 | 0 | snprintf(_logbuf, BUFF_SIZE, "%d\t%s\t%s\n", a->GetIdx(), a->GetType(), |
1009 | 0 | (a->IsInRing() ? (a->IsAromatic() ? "AR" : "AL") : "NO")); |
1010 | 0 | OBFFLog(_logbuf); |
1011 | 0 | } |
1012 | |
|
1013 | 0 | OBFFLog("\nC H A R G E S\n\n"); |
1014 | 0 | OBFFLog("IDX\tCHARGE\n"); |
1015 | |
|
1016 | 0 | FOR_ATOMS_OF_MOL (a, _mol) { |
1017 | 0 | snprintf(_logbuf, BUFF_SIZE, "%d\t%f\n", a->GetIdx(), a->GetPartialCharge()); |
1018 | 0 | OBFFLog(_logbuf); |
1019 | 0 | } |
1020 | 0 | } |
1021 | | |
1022 | | // DEBUG (validation) |
1023 | | //FOR_ATOMS_OF_MOL (a, _mol) |
1024 | | // if (atoi(a->GetType()) != 0) |
1025 | | // cout << "ATOMTYPE " << atoi(a->GetType()) << endl; |
1026 | | // else |
1027 | | // cout << "ATOMTYPE " << a->GetType() << endl; |
1028 | |
|
1029 | 0 | if (ifs) |
1030 | 0 | ifs.close(); |
1031 | | |
1032 | | // return the locale to the original one |
1033 | 0 | obLocale.RestoreLocale(); |
1034 | |
|
1035 | 0 | return true; |
1036 | 0 | } |
1037 | | |
1038 | | bool OBForceFieldGhemical::SetPartialCharges() |
1039 | 0 | { |
1040 | 0 | OBAtom *a, *b; |
1041 | 0 | int bondtype; |
1042 | |
|
1043 | 0 | _mol.SetAutomaticPartialCharge(false); |
1044 | 0 | _mol.SetPartialChargesPerceived(); |
1045 | | |
1046 | | // set all partial charges to 0.0 |
1047 | 0 | FOR_ATOMS_OF_MOL (atom, _mol) |
1048 | 0 | atom->SetPartialCharge(0.0); |
1049 | |
|
1050 | 0 | FOR_BONDS_OF_MOL (bond, _mol) { |
1051 | 0 | a = bond->GetBeginAtom(); |
1052 | 0 | b = bond->GetEndAtom(); |
1053 | 0 | bondtype = bond->GetBondOrder(); |
1054 | |
|
1055 | 0 | string _a(a->GetType()); |
1056 | 0 | string _b(b->GetType()); |
1057 | |
|
1058 | 0 | for (unsigned int idx=0; idx < _ffchargeparams.size(); ++idx) { |
1059 | 0 | if (((_a == _ffchargeparams[idx]._a) && (_b == _ffchargeparams[idx]._b)) && (bondtype == _ffchargeparams[idx]._ipar[0])) { |
1060 | 0 | a->SetPartialCharge(a->GetPartialCharge() - _ffchargeparams[idx]._dpar[0]); |
1061 | 0 | b->SetPartialCharge(b->GetPartialCharge() + _ffchargeparams[idx]._dpar[0]); |
1062 | 0 | } else if (((_a == _ffchargeparams[idx]._b) && (_b == _ffchargeparams[idx]._a)) && (bondtype == _ffchargeparams[idx]._ipar[0])) { |
1063 | 0 | a->SetPartialCharge(a->GetPartialCharge() + _ffchargeparams[idx]._dpar[0]); |
1064 | 0 | b->SetPartialCharge(b->GetPartialCharge() - _ffchargeparams[idx]._dpar[0]); |
1065 | 0 | } |
1066 | 0 | } |
1067 | 0 | } |
1068 | |
|
1069 | 0 | return true; |
1070 | 0 | } |
1071 | | |
1072 | | double OBForceFieldGhemical::Energy(bool gradients) |
1073 | 0 | { |
1074 | 0 | double energy = 0.0; |
1075 | |
|
1076 | 0 | IF_OBFF_LOGLVL_MEDIUM |
1077 | 0 | OBFFLog("\nE N E R G Y\n\n"); |
1078 | |
|
1079 | 0 | if (gradients) { |
1080 | 0 | ClearGradients(); |
1081 | 0 | energy = E_Bond<true>(); |
1082 | 0 | energy += E_Angle<true>(); |
1083 | 0 | energy += E_Torsion<true>(); |
1084 | 0 | energy += E_VDW<true>(); |
1085 | 0 | energy += E_Electrostatic<true>(); |
1086 | 0 | } else { |
1087 | 0 | energy = E_Bond<false>(); |
1088 | 0 | energy += E_Angle<false>(); |
1089 | 0 | energy += E_Torsion<false>(); |
1090 | 0 | energy += E_VDW<false>(); |
1091 | 0 | energy += E_Electrostatic<false>(); |
1092 | 0 | } |
1093 | |
|
1094 | 0 | IF_OBFF_LOGLVL_MEDIUM { |
1095 | 0 | snprintf(_logbuf, BUFF_SIZE, "\nTOTAL ENERGY = %8.3f %s\n", energy, GetUnit().c_str()); |
1096 | 0 | OBFFLog(_logbuf); |
1097 | 0 | } |
1098 | |
|
1099 | 0 | return energy; |
1100 | 0 | } |
1101 | | |
1102 | | OBFFParameter* OBForceFieldGhemical::GetParameterGhemical(int type, const char* a, const char* b, const char* c, const char* d, |
1103 | | vector<OBFFParameter> ¶meter) |
1104 | 0 | { |
1105 | 0 | OBFFParameter *par; |
1106 | 0 | if (a == nullptr) |
1107 | 0 | return nullptr; |
1108 | | |
1109 | 0 | if (b == nullptr) { |
1110 | 0 | string _a(a); |
1111 | 0 | for (unsigned int idx=0; idx < parameter.size(); ++idx) |
1112 | 0 | if ((_a == parameter[idx]._a) && (type == parameter[idx]._ipar[0])) { |
1113 | 0 | par = ¶meter[idx]; |
1114 | 0 | return par; |
1115 | 0 | } |
1116 | 0 | return nullptr; |
1117 | 0 | } |
1118 | 0 | if (c == nullptr) { |
1119 | 0 | string _a(a); |
1120 | 0 | string _b(b); |
1121 | 0 | for (unsigned int idx=0; idx < parameter.size(); ++idx) { |
1122 | 0 | if ( ((_a == parameter[idx]._a) && (_b == parameter[idx]._b) && |
1123 | 0 | (type == parameter[idx]._ipar[0])) || |
1124 | 0 | (((_a == parameter[idx]._b) && (_b == parameter[idx]._a)) && |
1125 | 0 | (type == parameter[idx]._ipar[0])) ) { |
1126 | 0 | par = ¶meter[idx]; |
1127 | 0 | return par; |
1128 | 0 | } |
1129 | 0 | } |
1130 | 0 | return nullptr; |
1131 | 0 | } |
1132 | 0 | if (d == nullptr) { |
1133 | 0 | string _a(a); |
1134 | 0 | string _b(b); |
1135 | 0 | string _c(c); |
1136 | 0 | for (unsigned int idx=0; idx < parameter.size(); ++idx) { |
1137 | 0 | if ( ((_a == parameter[idx]._a) && (_b == parameter[idx]._b) && |
1138 | 0 | (_c == parameter[idx]._c) && |
1139 | 0 | (type == parameter[idx]._ipar[0]))|| |
1140 | 0 | ((_a == parameter[idx]._c) && (_b == parameter[idx]._b) && |
1141 | 0 | (_c == parameter[idx]._a) && (type == parameter[idx]._ipar[0])) ) { |
1142 | 0 | par = ¶meter[idx]; |
1143 | 0 | return par; |
1144 | 0 | } |
1145 | 0 | } |
1146 | 0 | return nullptr; |
1147 | 0 | } |
1148 | 0 | string _a(a); |
1149 | 0 | string _b(b); |
1150 | 0 | string _c(c); |
1151 | 0 | string _d(d); |
1152 | |
|
1153 | 0 | for (unsigned int idx=0; idx < parameter.size(); ++idx) { |
1154 | 0 | if ( ((_a == parameter[idx]._a) && (_b == parameter[idx]._b) && |
1155 | 0 | (_c == parameter[idx]._c) && (_d == parameter[idx]._d) && |
1156 | 0 | (type == parameter[idx]._ipar[0])) || |
1157 | 0 | ((_a == parameter[idx]._d) && (_b == parameter[idx]._c) && |
1158 | 0 | (_c == parameter[idx]._b) && (_d == parameter[idx]._a) && |
1159 | 0 | (type == parameter[idx]._ipar[0])) ) { |
1160 | 0 | par = ¶meter[idx]; |
1161 | 0 | return par; |
1162 | 0 | } |
1163 | 0 | } |
1164 | | |
1165 | 0 | return nullptr; |
1166 | 0 | } |
1167 | | |
1168 | | bool OBForceFieldGhemical::ValidateGradients () |
1169 | 0 | { |
1170 | 0 | vector3 numgrad, anagrad, err; |
1171 | 0 | int coordIdx; |
1172 | |
|
1173 | 0 | bool passed = true; // set to false if any component fails |
1174 | |
|
1175 | 0 | OBFFLog("\nV A L I D A T E G R A D I E N T S\n\n"); |
1176 | 0 | OBFFLog("ATOM IDX NUMERICAL GRADIENT ANALYTICAL GRADIENT REL. ERROR (%) \n"); |
1177 | 0 | OBFFLog("----------------------------------------------------------------------------------------\n"); |
1178 | | // "XX (000.000, 000.000, 000.000) (000.000, 000.000, 000.000) (00.00, 00.00, 00.00)" |
1179 | |
|
1180 | 0 | FOR_ATOMS_OF_MOL (a, _mol) { |
1181 | 0 | coordIdx = (a->GetIdx() - 1) * 3; |
1182 | | |
1183 | | // OBFF_ENERGY |
1184 | 0 | numgrad = NumericalDerivative(&*a, OBFF_ENERGY); |
1185 | 0 | Energy(); // compute |
1186 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1187 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1188 | |
|
1189 | 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(), |
1190 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1191 | 0 | OBFFLog(_logbuf); |
1192 | | |
1193 | | // OBFF_EBOND |
1194 | 0 | numgrad = NumericalDerivative(&*a, OBFF_EBOND); |
1195 | 0 | ClearGradients(); |
1196 | 0 | E_Bond(); |
1197 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1198 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1199 | |
|
1200 | 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(), |
1201 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1202 | 0 | OBFFLog(_logbuf); |
1203 | | // 8% tolerance here because some bonds have slight instability |
1204 | 0 | if (err.x() > 8.0 || err.y() > 8.0 || err.z() > 8.0) |
1205 | 0 | passed = false; |
1206 | | |
1207 | | // OBFF_EANGLE |
1208 | 0 | numgrad = NumericalDerivative(&*a, OBFF_EANGLE); |
1209 | 0 | ClearGradients(); |
1210 | 0 | E_Angle(); |
1211 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1212 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1213 | |
|
1214 | 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(), |
1215 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1216 | 0 | OBFFLog(_logbuf); |
1217 | 0 | if (err.x() > 5.0 || err.y() > 5.0 || err.z() > 5.0) |
1218 | 0 | passed = false; |
1219 | | |
1220 | | // OBFF_ETORSION |
1221 | 0 | numgrad = NumericalDerivative(&*a, OBFF_ETORSION); |
1222 | 0 | ClearGradients(); |
1223 | 0 | E_Torsion(); |
1224 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1225 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1226 | |
|
1227 | 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(), |
1228 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1229 | 0 | OBFFLog(_logbuf); |
1230 | 0 | if (err.x() > 5.0 || err.y() > 5.0 || err.z() > 5.0) |
1231 | 0 | passed = false; |
1232 | | |
1233 | | // OBFF_EVDW |
1234 | 0 | numgrad = NumericalDerivative(&*a, OBFF_EVDW); |
1235 | 0 | ClearGradients(); |
1236 | 0 | E_VDW(); |
1237 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1238 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1239 | |
|
1240 | 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(), |
1241 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1242 | 0 | OBFFLog(_logbuf); |
1243 | 0 | if (err.x() > 5.0 || err.y() > 5.0 || err.z() > 5.0) |
1244 | 0 | passed = false; |
1245 | | |
1246 | | // OBFF_EELECTROSTATIC |
1247 | 0 | numgrad = NumericalDerivative(&*a, OBFF_EELECTROSTATIC); |
1248 | 0 | ClearGradients(); |
1249 | 0 | E_Electrostatic(); |
1250 | 0 | anagrad.Set(_gradientPtr[coordIdx], _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]); |
1251 | 0 | err = ValidateGradientError(numgrad, anagrad); |
1252 | |
|
1253 | 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(), |
1254 | 0 | anagrad.x(), anagrad.y(), anagrad.z(), err.x(), err.y(), err.z()); |
1255 | 0 | OBFFLog(_logbuf); |
1256 | 0 | if (err.x() > 5.0 || err.y() > 5.0 || err.z() > 5.0) |
1257 | 0 | passed = false; |
1258 | 0 | } |
1259 | |
|
1260 | 0 | return passed; // are all components good enough? |
1261 | 0 | } |
1262 | | |
1263 | | } // end namespace OpenBabel |
1264 | | |
1265 | | //! \file forcefieldghemical.cpp |
1266 | | //! \brief Ghemical force field |