Coverage Report

Created: 2026-02-26 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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> &parameter)
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 = &parameter[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 = &parameter[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 = &parameter[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 = &parameter[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