Coverage Report

Created: 2025-10-10 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/ops/forcefield.cpp
Line
Count
Source
1
/**********************************************************************
2
forcefield.cpp - A OBOp to calculate and minimize the energy using a
3
                 forcefield (re-wrap of obminimize and obenergy)
4
5
Copyright (C) 2006-2007 by Tim Vandermeersch
6
          (C) 2009 by Chris Morley
7
8
This file is part of the Open Babel project.
9
For more information, see <http://openbabel.org/>
10
11
This program is free software; you can redistribute it and/or modify
12
it under the terms of the GNU General Public License as published by
13
the Free Software Foundation version 2 of the License.
14
15
This program is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
GNU General Public License for more details.
19
***********************************************************************/
20
21
/******************************************************************************
22
**** CURRENTLY ONLY SUITABLE FOR USE WITH THE OBABEL COMMANDLINE INTERFACE ****
23
This allows options to have parameters, e.g. --ff Ghemical
24
Compile with tools/obabel.cpp rather than tools/babel.cpp
25
26
*******************************************************************************/
27
28
#include <openbabel/babelconfig.h>
29
#include <iostream>
30
#include <cstdlib>
31
#include<openbabel/op.h>
32
#include<openbabel/mol.h>
33
#include<openbabel/forcefield.h>
34
#include<openbabel/generic.h>
35
36
namespace OpenBabel
37
{
38
  using namespace std;
39
40
  //////////////////////////////////////////////////////////
41
  //
42
  //  OpEnergy
43
  //
44
  //////////////////////////////////////////////////////////
45
46
  class OpEnergy : public OBOp
47
  {
48
    public:
49
2
      OpEnergy(const char *ID) : OBOp(ID, false) {}
50
51
      const char* Description() override
52
0
      {
53
0
        return "ForceField Energy Evaluation (not displayed in GUI)\n"
54
0
          "Typical usage: obabel infile.xxx -O outfile.yy --energy --log\n"
55
0
          " options:         description\n"
56
0
          " --log        output a log of the energies (default = no log)\n"
57
0
          " --epsilon #  set the dielectric constant for electrostatics\n"
58
0
          " --noh        don't add explicit hydrogens (default = make explicit)\n"
59
0
          " --ff #       select a forcefield (default = MMFF94)\n"
60
0
          " The hydrogens are made explicit by default before energy evaluation.\n"
61
0
          " The energy is put in an OBPairData object \"Energy\" which is\n"
62
0
          "   accessible via an SDF or CML property or --append (to title).\n"
63
0
          ;
64
0
      }
65
66
      bool WorksWith(OBBase* pOb) const override
67
0
      {
68
0
        return dynamic_cast<OBMol*>(pOb) != nullptr;
69
0
      }
70
      bool Do(OBBase* pOb, const char* OptionText, OpMap* pmap, OBConversion*) override;
71
  };
72
73
  //////////////////////////////////////////////////////////
74
  OpEnergy theOpEnergy("energy"); //Global instance
75
76
  //////////////////////////////////////////////////////////
77
  bool OpEnergy::Do(OBBase* pOb, const char* OptionText, OpMap* pmap, OBConversion*)
78
0
  {
79
0
    OBMol* pmol = dynamic_cast<OBMol*>(pOb);
80
0
    if(!pmol)
81
0
      return false;
82
83
0
    bool log = false;
84
0
    bool addh = true;
85
86
0
    string ff = "MMFF94";
87
0
    double epsilon = 1.0;
88
0
    OpMap::const_iterator iter = pmap->find("ff");
89
0
    if(iter!=pmap->end())
90
0
      ff = iter->second;
91
0
    OBForceField* pFF = OBForceField::FindForceField(ff);
92
0
    iter = pmap->find("epsilon");
93
0
    if (iter!=pmap->end())
94
0
      epsilon = atof(iter->second.c_str());
95
96
0
    iter = pmap->find("log");
97
0
    if(iter!=pmap->end())
98
0
      log=true;
99
100
0
    iter = pmap->find("noh");
101
0
    if(iter!=pmap->end())
102
0
      addh=false;
103
104
0
    if (addh)
105
0
      pmol->AddHydrogens(false, false);
106
107
    // set some force field variables
108
0
    pFF->SetLogFile(&clog);
109
0
    pFF->SetLogLevel(log ? OBFF_LOGLVL_MEDIUM : OBFF_LOGLVL_NONE);
110
111
0
    pFF->SetDielectricConstant(epsilon);
112
0
    if (!pFF->Setup(*pmol)) {
113
0
      cerr  << "Could not setup force field." << endl;
114
0
      return false;
115
0
    }
116
117
    //Put the energy in a OBPairData object
118
0
    OBPairData *dp = new OBPairData;
119
0
    dp->SetAttribute("Energy");
120
0
    stringstream ss;
121
0
    ss << pFF->Energy(false);
122
0
    dp->SetValue(ss.str());
123
0
    dp->SetOrigin(fileformatInput);
124
0
    pmol->SetData(dp);
125
126
0
    return true;
127
0
  }
128
129
  //////////////////////////////////////////////////////////
130
  //
131
  //  OpMinimize
132
  //
133
  //////////////////////////////////////////////////////////
134
135
  class OpMinimize : public OBOp
136
  {
137
    public:
138
2
      OpMinimize(const char* ID) : OBOp(ID, false) {}
139
140
      const char* Description() override
141
0
      {
142
0
        return "ForceField Energy Minimization (not displayed in GUI)\n"
143
0
          "Typical usage: obabel infile.xxx -O outfile.yyy --minimize --steps 1500 --sd\n"
144
0
          " options:         description:\n"
145
0
          " --log        output a log of the minimization process(default= no log)\n"
146
0
          " --crit #     set convergence criteria (default=1e-6)\n"
147
0
          " --sd         use steepest descent algorithm (default = conjugate gradient)\n"
148
0
          " --newton     use Newton2Num linesearch (default = Simple)\n"
149
0
          " --ff #       select a forcefield (default = Ghemical)\n"
150
0
          " --steps #    specify the maximum number of steps (default = 2500)\n"
151
0
          " --cut        use cut-off (default = don't use cut-off)\n"
152
0
          " --noh        don't add explicit hydrogens (default = make explicit)\n"
153
0
          " --epsilon #  relative dielectric constant (default = 1.0)\n"
154
0
          " --rvdw #     specify the VDW cut-off distance (default = 6.0)\n"
155
0
          " --rele #     specify the Electrostatic cut-off distance (default = 10.0)\n"
156
0
          " --freq #     specify the frequency to update the non-bonded pairs (default = 10)\n"
157
0
          " The hydrogens are made explicit before minimization by default.\n"
158
0
          " The energy is put in an OBPairData object \"Energy\" which is\n"
159
0
          "   accessible via an SDF or CML property or --append (to title).\n"
160
0
          ;
161
0
      }
162
163
      bool WorksWith(OBBase* pOb) const override
164
0
      {
165
0
        return dynamic_cast<OBMol*>(pOb) != nullptr;
166
0
      }
167
      bool Do(OBBase* pOb, const char* OptionText, OpMap* pmap, OBConversion*) override;
168
  };
169
170
  //////////////////////////////////////////////////////////
171
  OpMinimize theOpMinimize("minimize"); //Global instance
172
173
  //////////////////////////////////////////////////////////
174
  bool OpMinimize::Do(OBBase* pOb, const char* OptionText, OpMap* pmap, OBConversion*)
175
0
  {
176
0
    OBMol* pmol = dynamic_cast<OBMol*>(pOb);
177
0
    if(!pmol)
178
0
      return false;
179
180
0
    int steps = 2500;
181
0
    double crit = 1e-6;
182
0
    bool sd = false;
183
0
    bool cut = false;
184
0
    bool addh = true;
185
0
    bool newton = true;
186
0
    double epsilon = 1.0;
187
0
    double rvdw = 6.0;
188
0
    double rele = 10.0;
189
0
    int freq = 10;
190
0
    bool log = false;
191
192
0
    string ff = "MMFF94";
193
0
    OpMap::const_iterator iter = pmap->find("ff");
194
0
    if(iter!=pmap->end())
195
0
      ff = iter->second;
196
0
    OBForceField* pFF = OBForceField::FindForceField(ff);
197
198
0
    iter = pmap->find("sd");
199
0
    if(iter!=pmap->end())
200
0
      sd=true;
201
202
0
    iter = pmap->find("newton");
203
0
    if(iter!=pmap->end())
204
0
      newton=true;
205
206
0
    iter = pmap->find("cut");
207
0
    if(iter!=pmap->end())
208
0
      cut=true;
209
210
0
    iter = pmap->find("noh");
211
0
    if(iter!=pmap->end())
212
0
      addh=false;
213
214
0
    iter = pmap->find("crit");
215
0
    if(iter!=pmap->end())
216
0
      crit = atof(iter->second.c_str());
217
218
0
    iter = pmap->find("steps");
219
0
    if(iter!=pmap->end())
220
0
      steps = atoi(iter->second.c_str());
221
222
0
    iter = pmap->find("epsilon");
223
0
    if(iter!=pmap->end())
224
0
      epsilon = atof(iter->second.c_str());
225
226
0
    iter = pmap->find("rvdw");
227
0
    if(iter!=pmap->end())
228
0
      rvdw = atof(iter->second.c_str());
229
230
0
    iter = pmap->find("rele");
231
0
    if(iter!=pmap->end())
232
0
      rele = atof(iter->second.c_str());
233
234
0
    iter = pmap->find("pf");
235
0
    if(iter!=pmap->end()) {
236
0
      freq = atoi(iter->second.c_str());
237
0
      if (freq < 1)
238
0
        freq = 10; // don't divide by zero
239
0
    }
240
241
0
    iter = pmap->find("log");
242
0
    if(iter!=pmap->end())
243
0
      log=true;
244
245
0
    if (newton)
246
0
      pFF->SetLineSearchType(LineSearchType::Newton2Num);
247
248
    // set some force field variables
249
0
    pFF->SetLogFile(&clog);
250
0
    pFF->SetLogLevel(log ? OBFF_LOGLVL_LOW : OBFF_LOGLVL_NONE);
251
0
    pFF->SetVDWCutOff(rvdw);
252
0
    pFF->SetElectrostaticCutOff(rele);
253
0
    pFF->SetUpdateFrequency(freq);
254
0
    pFF->SetDielectricConstant(epsilon);
255
0
    pFF->EnableCutOff(cut);
256
257
0
    if (addh)
258
0
      pmol->AddHydrogens(false, false);
259
260
0
    if (!pFF->Setup(*pmol)) {
261
0
      cerr  << "Could not setup force field." << endl;
262
0
      return false;
263
0
    }
264
265
0
    bool done = true;
266
0
    if (sd)
267
0
      pFF->SteepestDescent(steps, crit);
268
0
    else
269
0
      pFF->ConjugateGradients(steps, crit);
270
271
0
    pFF->GetCoordinates(*pmol);
272
273
    //Put the energy in a OBPairData object
274
0
    OBPairData *dp = new OBPairData;
275
0
    dp->SetAttribute("Energy");
276
0
    stringstream ss;
277
0
    ss << pFF->Energy(false);
278
0
    dp->SetValue(ss.str());
279
0
    dp->SetOrigin(fileformatInput);
280
0
    pmol->SetData(dp);
281
282
0
    return true;
283
0
  }
284
285
}//namespace