Coverage Report

Created: 2026-08-31 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/mol.cpp
Line
Count
Source
1
/**********************************************************************
2
mol.cpp - Handle molecules.
3
4
Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
5
Some portions Copyright (C) 2001-2008 by Geoffrey R. Hutchison
6
Some portions Copyright (C) 2003 by Michael Banck
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
#include <openbabel/babelconfig.h>
21
22
#include <openbabel/mol.h>
23
#include <openbabel/bond.h>
24
#include <openbabel/ring.h>
25
#include <openbabel/rotamer.h>
26
#include <openbabel/phmodel.h>
27
#include <openbabel/bondtyper.h>
28
#include <openbabel/obiter.h>
29
#include <openbabel/builder.h>
30
#include <openbabel/kekulize.h>
31
#include <openbabel/internalcoord.h>
32
#include <openbabel/math/matrix3x3.h>
33
#include <openbabel/obfunctions.h>
34
#include <openbabel/elements.h>
35
36
#include <openbabel/stereo/tetrahedral.h>
37
#include <openbabel/stereo/cistrans.h>
38
39
#include <sstream>
40
#include <set>
41
42
using namespace std;
43
44
namespace OpenBabel
45
{
46
47
  extern bool SwabInt;
48
  extern THREAD_LOCAL OBPhModel  phmodel;
49
  extern THREAD_LOCAL OBAromaticTyper  aromtyper;
50
  extern THREAD_LOCAL OBAtomTyper      atomtyper;
51
  extern THREAD_LOCAL OBBondTyper      bondtyper;
52
53
  /** \class OBMol mol.h <openbabel/mol.h>
54
      \brief Molecule Class
55
56
      The most important class in Open Babel is OBMol, or the molecule class.
57
      The OBMol class is designed to store all the basic information
58
      associated with a molecule, to make manipulations on the connection
59
      table of a molecule facile, and to provide member functions which
60
      automatically perceive information about a molecule. A guided tour
61
      of the OBMol class is a good place to start.
62
63
      An OBMol class can be declared:
64
      \code
65
      OBMol mol;
66
      \endcode
67
68
      For example:
69
      \code
70
      #include <iostream.h>
71
72
      #include <openbabel/mol.h>
73
      #include <openbabel/obconversion.h>
74
      int main(int argc,char **argv)
75
      {
76
      OBConversion conv(&cin,&cout);
77
      if(conv.SetInAndOutFormats("SDF","MOL2"))
78
      {
79
      OBMol mol;
80
      if(conv.Read(&mol))
81
      ...manipulate molecule
82
83
      conv->Write(&mol);
84
      }
85
      return(1);
86
      }
87
      \endcode
88
89
      will read in a molecule in SD file format from stdin
90
      (or the C++ equivalent cin) and write a MOL2 format file out
91
      to standard out. Additionally, The input and output formats can
92
      be altered using the OBConversion class
93
94
      Once a molecule has been read into an OBMol (or created via other methods)
95
      the atoms and bonds
96
      can be accessed by the following methods:
97
      \code
98
      OBAtom *atom;
99
      atom = mol.GetAtom(5); //random access of an atom
100
      \endcode
101
      or
102
      \code
103
      OBBond *bond;
104
      bond = mol.GetBond(14); //random access of a bond
105
      \endcode
106
      or
107
      \code
108
      FOR_ATOMS_OF_MOL(atom, mol) // iterator access (see OBMolAtomIter)
109
      \endcode
110
      or
111
      \code
112
      FOR_BONDS_OF_MOL(bond, mol) // iterator access (see OBMolBondIter)
113
      \endcode
114
      It is important to note that atom arrays currently begin at 1 and bond arrays
115
      begin at 0. Requesting atom 0 (\code
116
      OBAtom *atom = mol.GetAtom(0); \endcode
117
      will result in an error, but
118
      \code
119
      OBBond *bond = mol.GetBond(0);
120
      \endcode
121
      is perfectly valid.
122
      Note that this is expected to change in the near future to simplify coding
123
      and improve efficiency.
124
125
      The ambiguity of numbering issues and off-by-one errors led to the use
126
      of iterators in Open Babel. An iterator is essentially just a pointer, but
127
      when used in conjunction with Standard Template Library (STL) vectors
128
      it provides an unambiguous way to loop over arrays. OBMols store their
129
      atom and bond information in STL vectors. Since vectors are template
130
      based, a vector of any user defined type can be declared. OBMols declare
131
      vector<OBAtom*> and vector<OBBond*> to store atom and bond information.
132
      Iterators are then a natural way to loop over the vectors of atoms and bonds.
133
134
      A variety of predefined iterators have been created to simplify
135
      common looping requests (e.g., looping over all atoms in a molecule,
136
      bonds to a given atom, etc.)
137
138
      \code
139
      #include <openbabel/obiter.h>
140
      ...
141
      #define FOR_ATOMS_OF_MOL(a,m)     for( OBMolAtomIter     a(m); a; ++a )
142
      #define FOR_BONDS_OF_MOL(b,m)     for( OBMolBondIter     b(m); b; ++b )
143
      #define FOR_NBORS_OF_ATOM(a,p)    for( OBAtomAtomIter    a(p); a; ++a )
144
      #define FOR_BONDS_OF_ATOM(b,p)    for( OBAtomBondIter    b(p); b; ++b )
145
      #define FOR_RESIDUES_OF_MOL(r,m)  for( OBResidueIter     r(m); r; ++r )
146
      #define FOR_ATOMS_OF_RESIDUE(a,r) for( OBResidueAtomIter a(r); a; ++a )
147
      ...
148
      \endcode
149
150
      These convenience functions can be used like so:
151
      \code
152
      #include <openbabel/obiter.h>
153
      #include <openbabel/mol.h>
154
155
      OBMol mol;
156
      double exactMass = 0.0;
157
      FOR_ATOMS_OF_MOL(a, mol)
158
      {
159
      exactMass +=  a->GetExactMass();
160
      }
161
      \endcode
162
163
      Note that with these convenience macros, the iterator "a" (or
164
      whichever name you pick) is declared for you -- you do not need to
165
      do it beforehand.
166
  */
167
168
  //
169
  // OBMol member functions
170
  //
171
  void  OBMol::SetTitle(const char *title)
172
2
  {
173
2
    _title = title;
174
2
    Trim(_title);
175
2
  }
176
177
  void  OBMol::SetTitle(std::string &title)
178
18.0k
  {
179
18.0k
    _title = title;
180
18.0k
    Trim(_title);
181
18.0k
  }
182
183
  const char *OBMol::GetTitle(bool replaceNewlines) const
184
2.33k
  {
185
2.33k
    if (!replaceNewlines || _title.find('\n')== string::npos )
186
2.33k
      return(_title.c_str());
187
188
    //Only multiline titles use the following to replace newlines by spaces
189
0
    static string title;
190
0
    title=_title;
191
    // Single pass; previous implementation was O(n^2) on titles with many
192
    // newlines because find_first_of always restarted from position 0.
193
0
    for (char& c : title) {
194
0
      if (c == '\n' || c == '\r')
195
0
        c = ' ';
196
0
    }
197
198
0
    return(title.c_str());
199
2.33k
  }
200
201
  bool SortVVInt(const vector<int> &a,const vector<int> &b)
202
0
  {
203
0
    return(a.size() > b.size());
204
0
  }
205
206
  bool SortAtomZ(const pair<OBAtom*,double> &a, const pair<OBAtom*,double> &b)
207
0
  {
208
0
    return (a.second < b.second);
209
0
  }
210
211
  double OBMol::GetAngle( OBAtom* a, OBAtom* b, OBAtom* c)
212
0
  {
213
0
    return a->GetAngle( b, c );
214
0
  }
215
216
  double OBMol::GetTorsion(int a,int b,int c,int d)
217
0
  {
218
0
    return(GetTorsion((OBAtom*)_vatom[a-1],
219
0
                      (OBAtom*)_vatom[b-1],
220
0
                      (OBAtom*)_vatom[c-1],
221
0
                      (OBAtom*)_vatom[d-1]));
222
0
  }
223
224
  void OBMol::SetTorsion(OBAtom *a,OBAtom *b,OBAtom *c, OBAtom *d, double ang)
225
0
  {
226
0
    vector<int> tor;
227
0
    vector<int> atoms;
228
229
0
    obErrorLog.ThrowError(__FUNCTION__,
230
0
                          "Ran OpenBabel::SetTorsion", obAuditMsg);
231
232
0
    tor.push_back(a->GetCoordinateIdx());
233
0
    tor.push_back(b->GetCoordinateIdx());
234
0
    tor.push_back(c->GetCoordinateIdx());
235
0
    tor.push_back(d->GetCoordinateIdx());
236
237
0
    FindChildren(atoms, b->GetIdx(), c->GetIdx());
238
0
    int j;
239
0
    for (j = 0 ; (unsigned)j < atoms.size() ; j++ )
240
0
      atoms[j] = (atoms[j] - 1) * 3;
241
242
0
    double v2x,v2y,v2z;
243
0
    double radang,m[9];
244
0
    double x,y,z,mag,rotang,sn,cs,t,tx,ty,tz;
245
246
    //calculate the torsion angle
247
    // TODO: fix this calculation for periodic systems
248
0
    radang = CalcTorsionAngle(a->GetVector(),
249
0
                              b->GetVector(),
250
0
                              c->GetVector(),
251
0
                              d->GetVector()) / RAD_TO_DEG;
252
    //
253
    // now we have the torsion angle (radang) - set up the rot matrix
254
    //
255
256
    //find the difference between current and requested
257
0
    rotang = ang - radang;
258
259
0
    sn = sin(rotang);
260
0
    cs = cos(rotang);
261
0
    t = 1 - cs;
262
263
0
    v2x = _c[tor[1]]   - _c[tor[2]];
264
0
    v2y = _c[tor[1]+1] - _c[tor[2]+1];
265
0
    v2z = _c[tor[1]+2] - _c[tor[2]+2];
266
267
   //normalize the rotation vector
268
0
    mag = sqrt(SQUARE(v2x)+SQUARE(v2y)+SQUARE(v2z));
269
0
    x = v2x/mag;
270
0
    y = v2y/mag;
271
0
    z = v2z/mag;
272
273
    //set up the rotation matrix
274
0
    m[0]= t*x*x + cs;
275
0
    m[1] = t*x*y + sn*z;
276
0
    m[2] = t*x*z - sn*y;
277
0
    m[3] = t*x*y - sn*z;
278
0
    m[4] = t*y*y + cs;
279
0
    m[5] = t*y*z + sn*x;
280
0
    m[6] = t*x*z + sn*y;
281
0
    m[7] = t*y*z - sn*x;
282
0
    m[8] = t*z*z + cs;
283
284
    //
285
    //now the matrix is set - time to rotate the atoms
286
    //
287
0
    tx = _c[tor[1]];
288
0
    ty = _c[tor[1]+1];
289
0
    tz = _c[tor[1]+2];
290
0
    vector<int>::iterator i;
291
0
    for (i = atoms.begin(); i != atoms.end(); ++i)
292
0
      {
293
0
        j = *i;
294
295
0
        _c[j] -= tx;
296
0
        _c[j+1] -= ty;
297
0
        _c[j+2]-= tz;
298
0
        x = _c[j]*m[0] + _c[j+1]*m[1] + _c[j+2]*m[2];
299
0
        y = _c[j]*m[3] + _c[j+1]*m[4] + _c[j+2]*m[5];
300
0
        z = _c[j]*m[6] + _c[j+1]*m[7] + _c[j+2]*m[8];
301
0
        _c[j] = x;
302
0
        _c[j+1] = y;
303
0
        _c[j+2] = z;
304
0
        _c[j] += tx;
305
0
        _c[j+1] += ty;
306
0
        _c[j+2] += tz;
307
0
      }
308
0
  }
309
310
311
  double OBMol::GetTorsion(OBAtom *a,OBAtom *b,OBAtom *c,OBAtom *d)
312
0
  {
313
0
    if (!IsPeriodic())
314
0
      {
315
0
        return(CalcTorsionAngle(a->GetVector(),
316
0
                                b->GetVector(),
317
0
                                c->GetVector(),
318
0
                                d->GetVector()));
319
0
      }
320
0
    else
321
0
      {
322
0
        vector3 v1, v2, v3, v4;
323
        // Wrap the atomic positions in a continuous chain that makes sense based on the unit cell
324
        // Start by extracting the absolute Cartesian coordinates
325
0
        v1 = a->GetVector();
326
0
        v2 = b->GetVector();
327
0
        v3 = c->GetVector();
328
0
        v4 = d->GetVector();
329
        // Then redefine the positions based on proximity to the previous atom
330
        // to build a continuous chain of expanded Cartesian coordinates
331
0
        OBUnitCell *unitCell = (OBUnitCell * ) GetData(OBGenericDataType::UnitCell);
332
0
        v2 = unitCell->UnwrapCartesianNear(v2, v1);
333
0
        v3 = unitCell->UnwrapCartesianNear(v3, v2);
334
0
        v4 = unitCell->UnwrapCartesianNear(v4, v3);
335
0
        return(CalcTorsionAngle(v1, v2, v3, v4));
336
0
      }
337
0
  }
338
339
  void OBMol::ContigFragList(std::vector<std::vector<int> >&cfl)
340
0
  {
341
0
    int j;
342
0
    OBAtom *atom;
343
0
    OBBond *bond;
344
0
    vector<OBAtom*>::iterator i;
345
0
    vector<OBBond*>::iterator k;
346
0
    OBBitVec used,curr,next,frag;
347
0
    vector<int> tmp;
348
349
0
    used.Resize(NumAtoms()+1);
350
0
    curr.Resize(NumAtoms()+1);
351
0
    next.Resize(NumAtoms()+1);
352
0
    frag.Resize(NumAtoms()+1);
353
354
0
    while ((unsigned)used.CountBits() < NumAtoms())
355
0
      {
356
0
        curr.Clear();
357
0
        frag.Clear();
358
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
359
0
          if (!used.BitIsSet(atom->GetIdx()))
360
0
            {
361
0
              curr.SetBitOn(atom->GetIdx());
362
0
              break;
363
0
            }
364
365
0
        frag |= curr;
366
0
        while (!curr.IsEmpty())
367
0
          {
368
0
            next.Clear();
369
0
            for (j = curr.NextBit(-1);j != curr.EndBit();j = curr.NextBit(j))
370
0
              {
371
0
                atom = GetAtom(j);
372
0
                for (bond = atom->BeginBond(k);bond;bond = atom->NextBond(k))
373
0
                  if (!used.BitIsSet(bond->GetNbrAtomIdx(atom)))
374
0
                    next.SetBitOn(bond->GetNbrAtomIdx(atom));
375
0
              }
376
377
0
            used |= curr;
378
0
            used |= next;
379
0
            frag |= next;
380
0
            curr = next;
381
0
          }
382
383
0
        tmp.clear();
384
0
        frag.ToVecInt(tmp);
385
0
        cfl.push_back(tmp);
386
0
      }
387
388
0
    sort(cfl.begin(),cfl.end(),SortVVInt);
389
0
  }
390
391
  void OBMol::FindAngles()
392
0
  {
393
    //if already has data return
394
0
    if(HasData(OBGenericDataType::AngleData))
395
0
      return;
396
397
    //get new data and attach it to molecule
398
0
    OBAngleData *angles = new OBAngleData;
399
0
    angles->SetOrigin(perceived);
400
0
    SetData(angles);
401
402
0
    OBAngle angle;
403
0
    OBAtom *b;
404
0
    int unique_angle;
405
406
0
    unique_angle = 0;
407
408
0
    FOR_ATOMS_OF_MOL(atom, this) {
409
0
      if(atom->GetAtomicNum() == OBElements::Hydrogen)
410
0
        continue;
411
412
0
      b = (OBAtom*) &*atom;
413
414
0
      FOR_NBORS_OF_ATOM(a, b) {
415
0
        FOR_NBORS_OF_ATOM(c, b) {
416
0
          if(&*a == &*c) {
417
0
            unique_angle = 1;
418
0
            continue;
419
0
          }
420
421
0
          if (unique_angle) {
422
0
            angle.SetAtoms((OBAtom*)b, (OBAtom*)&*a, (OBAtom*)&*c);
423
0
            angles->SetData(angle);
424
0
            angle.Clear();
425
0
          }
426
0
        }
427
0
        unique_angle = 0;
428
0
      }
429
0
    }
430
431
0
    return;
432
0
  }
433
434
  void OBMol::FindTorsions()
435
0
  {
436
    //if already has data return
437
0
    if(HasData(OBGenericDataType::TorsionData))
438
0
      return;
439
440
    //get new data and attach it to molecule
441
0
    OBTorsionData *torsions = new OBTorsionData;
442
0
    torsions->SetOrigin(perceived);
443
0
    SetData(torsions);
444
445
0
    OBTorsion torsion;
446
0
    vector<OBBond*>::iterator bi1,bi2,bi3;
447
0
    OBBond* bond;
448
0
    OBAtom *a,*b,*c,*d;
449
450
    //loop through all bonds generating torsions
451
0
    for(bond = BeginBond(bi1);bond;bond = NextBond(bi1))
452
0
      {
453
0
        b = bond->GetBeginAtom();
454
0
        c = bond->GetEndAtom();
455
0
        if(b->GetAtomicNum() == OBElements::Hydrogen || c->GetAtomicNum() == OBElements::Hydrogen)
456
0
          continue;
457
458
0
        for(a = b->BeginNbrAtom(bi2);a;a = b->NextNbrAtom(bi2))
459
0
          {
460
0
            if(a == c)
461
0
              continue;
462
463
0
            for(d = c->BeginNbrAtom(bi3);d;d = c->NextNbrAtom(bi3))
464
0
              {
465
0
                if ((d == b) || (d == a))
466
0
                  continue;
467
0
                torsion.AddTorsion(a,b,c,d);
468
0
              }
469
0
          }
470
        //add torsion to torsionData
471
0
        if(torsion.GetSize())
472
0
          torsions->SetData(torsion);
473
0
        torsion.Clear();
474
0
      }
475
476
0
    return;
477
0
  }
478
479
  void OBMol::FindLargestFragment(OBBitVec &lf)
480
0
  {
481
0
    int j;
482
0
    OBAtom *atom;
483
0
    OBBond *bond;
484
0
    vector<OBAtom*>::iterator i;
485
0
    vector<OBBond*>::iterator k;
486
0
    OBBitVec used,curr,next,frag;
487
488
0
    lf.Clear();
489
0
    while ((unsigned)used.CountBits() < NumAtoms())
490
0
      {
491
0
        curr.Clear();
492
0
        frag.Clear();
493
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
494
0
          if (!used.BitIsSet(atom->GetIdx()))
495
0
            {
496
0
              curr.SetBitOn(atom->GetIdx());
497
0
              break;
498
0
            }
499
500
0
        frag |= curr;
501
0
        while (!curr.IsEmpty())
502
0
          {
503
0
            next.Clear();
504
0
            for (j = curr.NextBit(-1);j != curr.EndBit();j = curr.NextBit(j))
505
0
              {
506
0
                atom = GetAtom(j);
507
0
                for (bond = atom->BeginBond(k);bond;bond = atom->NextBond(k))
508
0
                  if (!used.BitIsSet(bond->GetNbrAtomIdx(atom)))
509
0
                    next.SetBitOn(bond->GetNbrAtomIdx(atom));
510
0
              }
511
512
0
            used |= curr;
513
0
            used |= next;
514
0
            frag |= next;
515
0
            curr = next;
516
0
          }
517
518
0
        if (lf.IsEmpty() || lf.CountBits() < frag.CountBits())
519
0
          lf = frag;
520
0
      }
521
0
  }
522
523
  //! locates all atoms for which there exists a path to 'end'
524
  //! without going through 'bgn'
525
  //! children must not include 'end'
526
  void OBMol::FindChildren(vector<OBAtom*> &children,OBAtom *bgn,OBAtom *end)
527
0
  {
528
0
    OBBitVec used,curr,next;
529
530
0
    used |= bgn->GetIdx();
531
0
    used |= end->GetIdx();
532
0
    curr |= end->GetIdx();
533
0
    children.clear();
534
535
0
    int i;
536
0
    OBAtom *atom,*nbr;
537
0
    vector<OBBond*>::iterator j;
538
539
0
    for (;;)
540
0
      {
541
0
        next.Clear();
542
0
        for (i = curr.NextBit(-1);i != curr.EndBit();i = curr.NextBit(i))
543
0
          {
544
0
            atom = GetAtom(i);
545
0
            for (nbr = atom->BeginNbrAtom(j);nbr;nbr = atom->NextNbrAtom(j))
546
0
              if (!used[nbr->GetIdx()])
547
0
                {
548
0
                  children.push_back(nbr);
549
0
                  next |= nbr->GetIdx();
550
0
                  used |= nbr->GetIdx();
551
0
                }
552
0
          }
553
0
        if (next.IsEmpty())
554
0
          break;
555
0
        curr = next;
556
0
      }
557
0
  }
558
559
  //! locates all atoms for which there exists a path to 'second'
560
  //! without going through 'first'
561
  //! children must not include 'second'
562
  void OBMol::FindChildren(vector<int> &children,int first,int second)
563
0
  {
564
0
    int i;
565
0
    OBBitVec used,curr,next;
566
567
0
    used.SetBitOn(first);
568
0
    used.SetBitOn(second);
569
0
    curr.SetBitOn(second);
570
571
0
    OBAtom *atom;
572
0
    while (!curr.IsEmpty())
573
0
      {
574
0
        next.Clear();
575
0
        for (i = curr.NextBit(-1);i != curr.EndBit();i = curr.NextBit(i))
576
0
          {
577
0
            atom = GetAtom(i);
578
0
            FOR_BONDS_OF_ATOM (bond, atom)
579
0
              if (!used.BitIsSet(bond->GetNbrAtomIdx(atom)))
580
0
                next.SetBitOn(bond->GetNbrAtomIdx(atom));
581
0
          }
582
583
0
        used |= next;
584
0
        curr = next;
585
0
      }
586
587
0
    used.SetBitOff(first);
588
0
    used.SetBitOff(second);
589
0
    used.ToVecInt(children);
590
0
  }
591
592
  /*! \brief Calculates the graph theoretical distance (GTD) of each atom.
593
   *
594
   * Creates a vector (indexed from zero) containing, for each atom
595
   * in the molecule, the number of bonds plus one to the most
596
   * distant non-H atom.
597
   *
598
   * For example, for the molecule H3CC(=O)Cl the GTD value for C1
599
   * would be 3, as the most distant non-H atom (either Cl or O) is
600
   * 2 bonds away.
601
   *
602
   * Since the GTD measures the distance to non-H atoms, the GTD values
603
   * for terminal H atoms tend to be larger than for non-H terminal atoms.
604
   * In the example above, the GTD values for the H atoms are all 4.
605
   */
606
  bool OBMol::GetGTDVector(vector<int> &gtd)
607
  //calculates the graph theoretical distance for every atom
608
  //and puts it into gtd
609
0
  {
610
0
    gtd.clear();
611
0
    gtd.resize(NumAtoms());
612
613
0
    int gtdcount,natom;
614
0
    OBBitVec used,curr,next;
615
0
    OBAtom *atom,*atom1;
616
0
    OBBond *bond;
617
0
    vector<OBAtom*>::iterator i;
618
0
    vector<OBBond*>::iterator j;
619
620
0
    next.Clear();
621
622
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
623
0
      {
624
0
        gtdcount = 0;
625
0
        used.Clear();
626
0
        curr.Clear();
627
0
        used.SetBitOn(atom->GetIdx());
628
0
        curr.SetBitOn(atom->GetIdx());
629
630
0
        while (!curr.IsEmpty())
631
0
          {
632
0
            next.Clear();
633
0
            for (natom = curr.NextBit(-1);natom != curr.EndBit();natom = curr.NextBit(natom))
634
0
              {
635
0
                atom1 = GetAtom(natom);
636
0
                for (bond = atom1->BeginBond(j);bond;bond = atom1->NextBond(j))
637
0
                  if (!used.BitIsSet(bond->GetNbrAtomIdx(atom1)) && !curr.BitIsSet(bond->GetNbrAtomIdx(atom1)))
638
0
                    if (bond->GetNbrAtom(atom1)->GetAtomicNum() != OBElements::Hydrogen)
639
0
                      next.SetBitOn(bond->GetNbrAtomIdx(atom1));
640
0
              }
641
642
0
            used |= next;
643
0
            curr = next;
644
0
            gtdcount++;
645
0
          }
646
0
        gtd[atom->GetIdx()-1] = gtdcount;
647
0
      }
648
0
    return(true);
649
0
  }
650
651
  /*!
652
  **\brief Calculates a set of graph invariant indexes using
653
  ** the graph theoretical distance, number of connected heavy atoms,
654
  ** aromatic boolean, ring boolean, atomic number, and
655
  ** summation of bond orders connected to the atom.
656
  ** Vector is indexed from zero
657
  */
658
  void OBMol::GetGIVector(vector<unsigned int> &vid)
659
0
  {
660
0
    vid.clear();
661
0
    vid.resize(NumAtoms()+1);
662
663
0
    vector<int> v;
664
0
    GetGTDVector(v);
665
666
0
    int i;
667
0
    OBAtom *atom;
668
0
    vector<OBAtom*>::iterator j;
669
0
    for (i=0,atom = BeginAtom(j);atom;atom = NextAtom(j),++i)
670
0
      {
671
0
        vid[i] =  (unsigned int)v[i];
672
0
        vid[i] += (unsigned int)(atom->GetHvyDegree()*100);
673
0
        vid[i] += (unsigned int)(((atom->IsAromatic()) ? 1 : 0)*1000);
674
0
        vid[i] += (unsigned int)(((atom->IsInRing()) ? 1 : 0)*10000);
675
0
        vid[i] += (unsigned int)(atom->GetAtomicNum()*100000);
676
0
        vid[i] += (unsigned int)(atom->GetImplicitHCount()*10000000);
677
0
      }
678
0
  }
679
680
  static bool OBComparePairSecond(const pair<OBAtom*,unsigned int> &a,const pair<OBAtom*,unsigned int> &b)
681
0
  {
682
0
    return(a.second < b.second);
683
0
  }
684
685
  static bool OBComparePairFirst(const pair<OBAtom*,unsigned int> &a,const pair<OBAtom*,unsigned int> &b)
686
0
  {
687
0
    return(a.first->GetIdx() < b.first->GetIdx());
688
0
  }
689
690
  //! counts the number of unique symmetry classes in a list
691
  static void ClassCount(vector<pair<OBAtom*,unsigned int> > &vp,unsigned int &count)
692
0
  {
693
0
    count = 0;
694
0
    vector<pair<OBAtom*,unsigned int> >::iterator k;
695
0
    sort(vp.begin(),vp.end(),OBComparePairSecond);
696
#if 0 // original version
697
698
    unsigned int id=0; // [ejk] appease gcc's bogus "might be undef'd" warning
699
    for (k = vp.begin();k != vp.end();++k)
700
      {
701
        if (k == vp.begin())
702
          {
703
            id = k->second;
704
            k->second = count = 0;
705
          }
706
        else
707
          if (k->second != id)
708
            {
709
              id = k->second;
710
              k->second = ++count;
711
            }
712
          else
713
            k->second = count;
714
      }
715
    count++;
716
#else // get rid of warning, moves test out of loop, returns 0 for empty input
717
718
0
    k = vp.begin();
719
0
    if (k != vp.end())
720
0
      {
721
0
        unsigned int id = k->second;
722
0
        k->second = 0;
723
0
        ++k;
724
0
        for (;k != vp.end(); ++k)
725
0
          {
726
0
            if (k->second != id)
727
0
              {
728
0
                id = k->second;
729
0
                k->second = ++count;
730
0
              }
731
0
            else
732
0
              k->second = count;
733
0
          }
734
0
        ++count;
735
0
      }
736
0
    else
737
0
      {
738
        // [ejk] thinks count=0 might be OK for an empty list, but orig code did
739
        //++count;
740
0
      }
741
0
#endif
742
0
  }
743
744
  //! creates a new vector of symmetry classes base on an existing vector
745
  //! helper routine to GetGIDVector
746
  static void CreateNewClassVector(vector<pair<OBAtom*,unsigned int> > &vp1,vector<pair<OBAtom*,unsigned int> > &vp2)
747
0
  {
748
0
    unsigned int m,id;
749
0
    OBAtom *nbr;
750
0
    vector<OBBond*>::iterator j;
751
0
    vector<unsigned int>::iterator k;
752
0
    vector<pair<OBAtom*,unsigned int> >::iterator i;
753
0
    sort(vp1.begin(),vp1.end(),OBComparePairFirst);
754
0
    vp2.clear();
755
0
    for (i = vp1.begin();i != vp1.end();++i)
756
0
      {
757
0
        vector<unsigned int> vtmp;
758
0
        for (nbr = i->first->BeginNbrAtom(j);nbr;nbr = i->first->NextNbrAtom(j))
759
0
          vtmp.push_back(vp1[nbr->GetIdx()-1].second);
760
0
        sort(vtmp.begin(),vtmp.end(),OBCompareUnsigned);
761
        // Base-100 positional sum of the neighbour classes, computed modulo
762
        // 2^32 (bit-identical to the previous int overflow, but done in 64-bit
763
        // intermediates so it does not invoke overflow). See the matching
764
        // routine in graphsym.cpp for details.
765
0
        id = i->second;
766
0
        for (m=100,k = vtmp.begin();k != vtmp.end();++k)
767
0
          {
768
0
            id = static_cast<unsigned int>(id + static_cast<unsigned long long>(*k) * m);
769
0
            m  = static_cast<unsigned int>(static_cast<unsigned long long>(m) * 100);
770
0
          }
771
772
0
        vp2.push_back(pair<OBAtom*,unsigned int> (i->first,id));
773
0
      }
774
0
  }
775
776
  /*!
777
  **\brief Calculates a set of symmetry identifiers for a molecule.
778
  ** Atoms with the same symmetry ID are symmetrically equivalent.
779
  ** Vector is indexed from zero
780
  */
781
  void OBMol::GetGIDVector(vector<unsigned int> &vgid)
782
0
  {
783
0
    vector<unsigned int> vgi;
784
0
    GetGIVector(vgi);  //get vector of graph invariants
785
786
0
    int i;
787
0
    OBAtom *atom;
788
0
    vector<OBAtom*>::iterator j;
789
0
    vector<pair<OBAtom*,unsigned int> > vp1,vp2;
790
0
    for (i=0,atom = BeginAtom(j);atom;atom = NextAtom(j),++i)
791
0
      vp1.push_back(pair<OBAtom*,unsigned int> (atom,vgi[i]));
792
793
0
    unsigned int nclass1,nclass2; //number of classes
794
0
    ClassCount(vp1,nclass1);
795
796
0
    if (nclass1 < NumAtoms())
797
0
      {
798
0
        for (i = 0;i < 100;++i) //sanity check - shouldn't ever hit this number
799
0
          {
800
0
            CreateNewClassVector(vp1,vp2);
801
0
            ClassCount(vp2,nclass2);
802
0
            vp1 = vp2;
803
0
            if (nclass1 == nclass2)
804
0
              break;
805
0
            nclass1 = nclass2;
806
0
          }
807
0
      }
808
809
0
    vgid.clear();
810
0
    sort(vp1.begin(),vp1.end(),OBComparePairFirst);
811
0
    vector<pair<OBAtom*,unsigned int> >::iterator k;
812
0
    for (k = vp1.begin();k != vp1.end();++k)
813
0
      vgid.push_back(k->second);
814
0
  }
815
816
  unsigned int OBMol::NumHvyAtoms() const
817
0
  {
818
0
    const OBAtom *atom;
819
0
    vector<OBAtom*>::const_iterator(i);
820
0
    unsigned int count = 0;
821
822
0
    for(atom = this->BeginAtom(i); atom; atom = this->NextAtom(i))
823
0
      {
824
0
        if (atom->GetAtomicNum() != OBElements::Hydrogen)
825
0
          count++;
826
0
      }
827
828
0
    return(count);
829
0
  }
830
831
  unsigned int OBMol::NumRotors(bool sampleRingBonds)
832
0
  {
833
0
    OBRotorList rl;
834
0
    rl.FindRotors(*this, sampleRingBonds);
835
0
    return rl.Size();
836
0
  }
837
838
  //! Returns a pointer to the atom after a safety check
839
  //! 0 < idx <= NumAtoms
840
  OBAtom *OBMol::GetAtom(int idx) const
841
4.64M
  {
842
4.64M
    if ((unsigned)idx < 1 || (unsigned)idx > NumAtoms())
843
15.2k
      {
844
15.2k
        obErrorLog.ThrowError(__FUNCTION__, "Requested Atom Out of Range", obDebug);
845
15.2k
        return nullptr;
846
15.2k
      }
847
848
4.62M
    return((OBAtom*)_vatom[idx-1]);
849
4.64M
  }
850
851
  OBAtom *OBMol::GetAtomById(unsigned long id) const
852
9.51k
  {
853
9.51k
    if (id >= _atomIds.size()) {
854
0
      obErrorLog.ThrowError(__FUNCTION__, "Requested atom with invalid id.", obDebug);
855
0
      return nullptr;
856
0
    }
857
858
9.51k
    return((OBAtom*)_atomIds[id]);
859
9.51k
  }
860
861
  OBAtom *OBMol::GetFirstAtom() const
862
0
  {
863
0
    return _vatom.empty() ? nullptr : (OBAtom*)_vatom[0];
864
0
  }
865
866
  //! Returns a pointer to the bond after a safety check
867
  //! 0 <= idx < NumBonds
868
  OBBond *OBMol::GetBond(int idx) const
869
6.56k
  {
870
6.56k
    if (idx < 0 || (unsigned)idx >= NumBonds())
871
28
      {
872
28
        obErrorLog.ThrowError(__FUNCTION__, "Requested Bond Out of Range", obDebug);
873
28
        return nullptr;
874
28
      }
875
876
6.53k
    return((OBBond*)_vbond[idx]);
877
6.56k
  }
878
879
  OBBond *OBMol::GetBondById(unsigned long id) const
880
5.11k
  {
881
5.11k
    if (id >= _bondIds.size()) {
882
0
      obErrorLog.ThrowError(__FUNCTION__, "Requested bond with invalid id.", obDebug);
883
0
      return nullptr;
884
0
    }
885
886
5.11k
    return((OBBond*)_bondIds[id]);
887
5.11k
  }
888
889
  OBBond *OBMol::GetBond(int bgn, int end) const
890
112k
  {
891
112k
    return(GetBond(GetAtom(bgn),GetAtom(end)));
892
112k
  }
893
894
  OBBond *OBMol::GetBond(OBAtom *bgn,OBAtom *end) const
895
118k
  {
896
118k
    OBAtom *nbr;
897
118k
    vector<OBBond*>::iterator i;
898
899
118k
    if (!bgn || !end) return nullptr;
900
901
227k
    for (nbr = bgn->BeginNbrAtom(i);nbr;nbr = bgn->NextNbrAtom(i))
902
210k
      if (nbr == end)
903
101k
        return((OBBond *)*i);
904
905
16.6k
    return nullptr; //just to keep the SGI compiler happy
906
118k
  }
907
908
  OBResidue *OBMol::GetResidue(int idx) const
909
0
  {
910
0
    if (idx < 0 || (unsigned)idx >= NumResidues())
911
0
      {
912
0
        obErrorLog.ThrowError(__FUNCTION__, "Requested Residue Out of Range", obDebug);
913
0
        return nullptr;
914
0
      }
915
916
0
    return (_residue[idx]);
917
0
  }
918
919
  std::vector<OBInternalCoord*> OBMol::GetInternalCoord()
920
0
  {
921
0
    if (_internals.empty())
922
0
      {
923
0
        _internals.push_back(nullptr);
924
0
        for(unsigned int i = 1; i <= NumAtoms(); ++i)
925
0
          {
926
0
            _internals.push_back(new OBInternalCoord);
927
0
          }
928
0
        CartesianToInternal(_internals, *this);
929
0
      }
930
0
    return _internals;
931
0
  }
932
933
  //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#findSmallestSetOfSmallestRings">blue-obelisk:findSmallestSetOfSmallestRings</a>.
934
  vector<OBRing*> &OBMol::GetSSSR()
935
30.1k
  {
936
30.1k
    if (!HasSSSRPerceived())
937
14.8k
      FindSSSR();
938
939
    // SDF / MDL files can inject "<SSSR>" as a property field, which
940
    // ends up stored as an OBPairData under that attribute. The legacy
941
    // C-style cast misinterpreted it as OBRingData (UBSAN catches the
942
    // vptr mismatch). Validate the type and replace if wrong.
943
30.1k
    OBGenericData *existing = GetData("SSSR");
944
30.1k
    OBRingData *rd = dynamic_cast<OBRingData *>(existing);
945
30.1k
    if (rd == nullptr) {
946
12.5k
      if (existing) DeleteData(existing);
947
12.5k
      rd = new OBRingData();
948
12.5k
      rd->SetAttribute("SSSR");
949
12.5k
      SetData(rd);
950
12.5k
    }
951
30.1k
    rd->SetOrigin(perceived);
952
30.1k
    return(rd->GetData());
953
30.1k
  }
954
955
  vector<OBRing*> &OBMol::GetLSSR()
956
2.34k
  {
957
2.34k
    if (!HasLSSRPerceived())
958
2.34k
      FindLSSR();
959
960
    // Same type-confusion guard as GetSSSR().
961
2.34k
    OBGenericData *existing = GetData("LSSR");
962
2.34k
    OBRingData *rd = dynamic_cast<OBRingData *>(existing);
963
2.34k
    if (rd == nullptr) {
964
319
      if (existing) DeleteData(existing);
965
319
      rd = new OBRingData();
966
319
      rd->SetAttribute("LSSR");
967
319
      SetData(rd);
968
319
    }
969
2.34k
    rd->SetOrigin(perceived);
970
2.34k
    return(rd->GetData());
971
2.34k
  }
972
973
  double OBMol::GetMolWt(bool implicitH)
974
0
  {
975
0
    double molwt=0.0;
976
0
    OBAtom *atom;
977
0
    vector<OBAtom*>::iterator i;
978
979
0
    double hmass = OBElements::GetMass(1);
980
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i)) {
981
0
      molwt += atom->GetAtomicMass();
982
0
      if (implicitH)
983
0
        molwt += atom->GetImplicitHCount() * hmass;
984
0
    }
985
0
    return(molwt);
986
0
  }
987
988
  double OBMol::GetExactMass(bool implicitH)
989
0
  {
990
0
    double mass=0.0;
991
0
    OBAtom *atom;
992
0
    vector<OBAtom*>::iterator i;
993
994
0
    double hmass = OBElements::GetExactMass(1, 1);
995
0
    for (atom = BeginAtom(i); atom; atom = NextAtom(i)) {
996
0
      mass += atom->GetExactMass();
997
0
      if (implicitH)
998
0
        mass += atom->GetImplicitHCount() * hmass;
999
0
    }
1000
1001
0
    return(mass);
1002
0
  }
1003
1004
  //! Stochoimetric formula in spaced format e.g. C 4 H 6 O 1
1005
  //! No pair data is stored. Normally use without parameters: GetSpacedFormula()
1006
  //! \since version 2.1
1007
  string OBMol::GetSpacedFormula(int ones, const char* sp, bool implicitH)
1008
0
  {
1009
    //Default ones=0, sp=" ".
1010
    //Using ones=1 and sp="" will give unspaced formula (and no pair data entry)
1011
    // These are the atomic numbers of the elements in alphabetical order, plus
1012
    // pseudo atomic numbers for D, T isotopes.
1013
0
    const int NumElements = 118 + 2;
1014
0
    const int alphabetical[NumElements] = {
1015
0
      89, 47, 13, 95, 18, 33, 85, 79, 5, 56, 4, 107, 83, 97, 35, 6, 20, 48,
1016
0
      58, 98, 17, 96, 112, 27, 24, 55, 29, NumElements-1,
1017
0
      105, 110, 66, 68, 99, 63, 9, 26, 114, 100, 87, 31,
1018
0
      64, 32, 1, 2, 72, 80, 67, 108, 53, 49, 77, 19, 36, 57, 3, 103, 71, 116, 115, 101,
1019
0
      12, 25, 42, 109, 7, 11, 41, 60, 10, 113, 28, 102, 93, 8, 118, 76, 15, 91, 82, 46,
1020
0
      61, 84, 59, 78, 94, 88, 37, 75, 104, 111, 45, 86, 44, 16, 51, 21, 34, 106, 14,
1021
0
      62, 50, 38, NumElements, 73, 65, 43, 52, 90, 22, 81, 69, 117, 92, 23, 74, 54, 39, 70,
1022
0
      30, 40 };
1023
1024
0
    int atomicCount[NumElements];
1025
0
    stringstream formula;
1026
1027
0
    for (int i = 0; i < NumElements; ++i)
1028
0
      atomicCount[i] = 0;
1029
1030
0
    bool UseImplicitH = (NumBonds()!=0 || NumAtoms()==1);
1031
    // Do not use implicit hydrogens if explicitly required not to
1032
0
    if (!implicitH) UseImplicitH = false;
1033
0
    bool HasHvyAtoms = NumHvyAtoms()>0;
1034
0
    FOR_ATOMS_OF_MOL(a, *this)
1035
0
      {
1036
0
        int anum = a->GetAtomicNum();
1037
0
        if(anum==0)
1038
0
          continue;
1039
0
        if(anum > (NumElements-2)) {
1040
0
          char buffer[BUFF_SIZE];  // error buffer
1041
0
          snprintf(buffer, BUFF_SIZE, "Skipping unknown element with atomic number %d", anum);
1042
0
          obErrorLog.ThrowError(__FUNCTION__, buffer, obWarning);
1043
0
          continue;
1044
0
        }
1045
0
        unsigned int iso = a->GetIsotope();
1046
0
        bool IsHiso = anum == 1 && (iso == 2 || iso == 3);
1047
0
        if(UseImplicitH)
1048
0
          {
1049
0
            if (anum == 1 && !IsHiso && HasHvyAtoms)
1050
0
              continue; // skip explicit hydrogens except D,T
1051
0
            if(anum==1)
1052
0
              {
1053
0
                if (IsHiso && HasHvyAtoms)
1054
0
                  --atomicCount[0]; //one of the implicit hydrogens is now explicit
1055
0
              }
1056
0
            else
1057
0
              atomicCount[0] += a->GetImplicitHCount() + a->ExplicitHydrogenCount();
1058
0
          }
1059
0
        if (IsHiso)
1060
0
          anum = NumElements + iso - 3; //pseudo AtNo for D, T
1061
0
        atomicCount[anum - 1]++;
1062
0
      }
1063
1064
0
    if (atomicCount[5] != 0) // Carbon (i.e. 6 - 1 = 5)
1065
0
      {
1066
0
        if (atomicCount[5] > ones)
1067
0
          formula << "C" << sp << atomicCount[5] << sp;
1068
0
        else if (atomicCount[5] == 1)
1069
0
          formula << "C";
1070
1071
0
        atomicCount[5] = 0; // So we don't output C twice
1072
1073
        // only output H if there's also carbon -- otherwise do it alphabetical
1074
0
        if (atomicCount[0] != 0) // Hydrogen (i.e., 1 - 1 = 0)
1075
0
          {
1076
0
            if (atomicCount[0] > ones)
1077
0
              formula << "H" << sp << atomicCount[0] << sp;
1078
0
            else if (atomicCount[0] == 1)
1079
0
              formula << "H";
1080
1081
0
            atomicCount[0] = 0;
1082
0
          }
1083
0
      }
1084
1085
0
    for (int j = 0; j < NumElements; ++j)
1086
0
      {
1087
0
        char DT[4] = {'D',0,'T',0};
1088
0
        const char* symb;
1089
0
        int alph = alphabetical[j]-1;
1090
0
        if (atomicCount[ alph ])
1091
0
          {
1092
0
            if(alph==NumElements-1)
1093
0
              symb = DT + 2;//T
1094
0
            else if (alph==NumElements-2)
1095
0
              symb = DT; //D
1096
0
            else
1097
0
              symb = OBElements::GetSymbol(alphabetical[j]);
1098
1099
0
            formula << symb << sp;
1100
0
            if(atomicCount[alph] > ones)
1101
0
              formula << atomicCount[alph] << sp;
1102
0
          }
1103
0
      }
1104
1105
0
    int chg = GetTotalCharge();
1106
0
    char ch = chg>0 ? '+' : '-' ;
1107
0
    chg = abs(chg);
1108
0
    while(chg--)
1109
0
      formula << ch << sp;
1110
1111
0
    string f_str = formula.str();
1112
0
    return (Trim(f_str));
1113
0
  }
1114
1115
  //! Stochoimetric formula (e.g., C4H6O).
1116
  //!   This is either set by OBMol::SetFormula() or generated on-the-fly
1117
  //!   using the "Hill order" -- i.e., C first if present, then H if present
1118
  //!   all other elements in alphabetical order.
1119
  string OBMol::GetFormula()
1120
0
  {
1121
0
    string attr = "Formula";
1122
0
    OBPairData *dp = (OBPairData *) GetData(attr);
1123
1124
0
    if (dp != nullptr) // we already set the formula (or it was read from a file)
1125
0
      return dp->GetValue();
1126
1127
0
    obErrorLog.ThrowError(__FUNCTION__,
1128
0
                          "Ran OpenBabel::SetFormula -- Hill order formula",
1129
0
                          obAuditMsg);
1130
1131
0
    string sformula = GetSpacedFormula(1, "");
1132
1133
0
    dp = new OBPairData;
1134
0
    dp->SetAttribute(attr);
1135
0
    dp->SetValue( sformula );
1136
0
    dp->SetOrigin( perceived ); // internal generation
1137
0
    SetData(dp);
1138
0
    return sformula;
1139
0
  }
1140
1141
  void OBMol::SetFormula(string molFormula)
1142
0
  {
1143
0
    string attr = "Formula";
1144
0
    OBPairData *dp = (OBPairData *) GetData(attr);
1145
0
    if (dp == nullptr)
1146
0
      {
1147
0
        dp = new OBPairData;
1148
0
        dp->SetAttribute(attr);
1149
0
        SetData(dp);
1150
0
      }
1151
0
    dp->SetValue(molFormula);
1152
    // typically file input, but this needs to be revisited
1153
0
    dp->SetOrigin(fileformatInput);
1154
0
  }
1155
1156
  void OBMol::SetTotalCharge(int charge)
1157
0
  {
1158
0
    SetFlag(OB_TCHARGE_MOL);
1159
0
    _totalCharge = charge;
1160
0
  }
1161
1162
  //! Returns the total molecular charge -- if it has not previously been set
1163
  //!  it is calculated from the atomic formal charge information.
1164
  //!  (This may or may not be correct!)
1165
  //!  If you set atomic charges with OBAtom::SetFormalCharge()
1166
  //!   you really should set the molecular charge with OBMol::SetTotalCharge()
1167
  int OBMol::GetTotalCharge()
1168
0
  {
1169
0
    if(HasFlag(OB_TCHARGE_MOL))
1170
0
      return(_totalCharge);
1171
0
    else // calculate from atomic formal charges (seems the best default)
1172
0
      {
1173
0
        obErrorLog.ThrowError(__FUNCTION__,
1174
0
                              "Ran OpenBabel::GetTotalCharge -- calculated from formal charges",
1175
0
                              obAuditMsg);
1176
1177
0
        OBAtom *atom;
1178
0
        vector<OBAtom*>::iterator i;
1179
0
        int chg = 0;
1180
1181
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1182
0
          chg += atom->GetFormalCharge();
1183
0
        return (chg);
1184
0
      }
1185
0
  }
1186
1187
  void   OBMol::SetTotalSpinMultiplicity(unsigned int spin)
1188
0
  {
1189
0
    SetFlag(OB_TSPIN_MOL);
1190
0
    _totalSpin = spin;
1191
0
  }
1192
1193
0
  void OBMol::SetInternalCoord(std::vector<OBInternalCoord*> int_coord) {
1194
0
    if (int_coord[0] != nullptr) {
1195
0
      std::vector<OBInternalCoord*>::iterator it = int_coord.begin();
1196
0
      int_coord.insert(it, nullptr);
1197
0
    }
1198
1199
0
    if (int_coord.size() != _natoms + 1) {
1200
0
      string error = "Number of internal coordinates is not the same as";
1201
0
      error += " the number of atoms in molecule";
1202
0
      obErrorLog.ThrowError(__FUNCTION__, error, obError);
1203
0
      return;
1204
0
    }
1205
1206
0
    _internals = int_coord;
1207
1208
0
    return;
1209
0
  }
1210
1211
  //! Returns the total spin multiplicity -- if it has not previously been set
1212
  //!  It is calculated from the atomic spin multiplicity information
1213
  //!  assuming the high-spin case (i.e. it simply sums the number of unpaired
1214
  //!  electrons assuming no further pairing of spins.
1215
  //!  if it fails (gives singlet for odd number of electronic systems),
1216
  //!  then assign wrt parity of the total electrons.
1217
  unsigned int OBMol::GetTotalSpinMultiplicity()
1218
0
  {
1219
0
    if (HasFlag(OB_TSPIN_MOL))
1220
0
      return(_totalSpin);
1221
0
    else // calculate from atomic spin information (assuming high-spin case)
1222
0
      {
1223
0
        obErrorLog.ThrowError(__FUNCTION__,
1224
0
                              "Ran OpenBabel::GetTotalSpinMultiplicity -- calculating from atomic spins assuming high spin case",
1225
0
                              obAuditMsg);
1226
1227
0
        OBAtom *atom;
1228
0
        vector<OBAtom*>::iterator i;
1229
0
        unsigned int unpaired_electrons = 0;
1230
0
        int chg = GetTotalCharge();
1231
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1232
0
          {
1233
0
            if (atom->GetSpinMultiplicity() > 1)
1234
0
              unpaired_electrons += (atom->GetSpinMultiplicity() - 1);
1235
0
           chg += static_cast<int>(atom->GetAtomicNum());
1236
0
          }
1237
0
        if (chg % 2 != unpaired_electrons %2)
1238
0
          return ((abs(chg) % 2) + 1);
1239
0
        else
1240
0
          return (unpaired_electrons + 1);
1241
0
      }
1242
0
  }
1243
1244
  OBMol &OBMol::operator=(const OBMol &source)
1245
  //atom and bond info is copied from src to dest
1246
  //Conformers are now copied also, MM 2/7/01
1247
  //Residue information are copied, MM 4-27-01
1248
  //All OBGenericData incl OBRotameterList is copied, CM 2006
1249
  //Zeros all flags except OB_TCHARGE_MOL, OB_PCHARGE_MOL, OB_HYBRID_MOL
1250
  //OB_TSPIN_MOL, OB_AROMATIC_MOL, OB_PERIODIC_MOL, OB_CHAINS_MOL and OB_PATTERN_STRUCTURE which are copied
1251
0
  {
1252
0
    if (this == &source)
1253
0
      return *this;
1254
1255
0
    OBMol &src = (OBMol &)source;
1256
0
    vector<OBAtom*>::iterator i;
1257
0
    vector<OBBond*>::iterator j;
1258
0
    OBAtom *atom;
1259
0
    OBBond *bond;
1260
1261
0
    Clear();
1262
0
    BeginModify();
1263
1264
0
    _vatom.reserve(src.NumAtoms());
1265
0
    _atomIds.reserve(src.NumAtoms());
1266
0
    _vbond.reserve(src.NumBonds());
1267
0
    _bondIds.reserve(src.NumBonds());
1268
1269
0
    for (atom = src.BeginAtom(i);atom;atom = src.NextAtom(i))
1270
0
      AddAtom(*atom);
1271
0
    for (bond = src.BeginBond(j);bond;bond = src.NextBond(j))
1272
0
      AddBond(*bond);
1273
1274
0
    this->_title  = src.GetTitle();
1275
0
    this->_energy = src.GetEnergy();
1276
0
    this->_dimension = src.GetDimension();
1277
0
    this->SetTotalCharge(src.GetTotalCharge()); //also sets a flag
1278
0
    this->SetTotalSpinMultiplicity(src.GetTotalSpinMultiplicity()); //also sets a flag
1279
1280
0
    EndModify(); //zeros flags!
1281
1282
0
    if (src.HasFlag(OB_PATTERN_STRUCTURE))
1283
0
      this->SetFlag(OB_PATTERN_STRUCTURE);
1284
0
    if (src.HasFlag(OB_TSPIN_MOL))
1285
0
      this->SetFlag(OB_TSPIN_MOL);
1286
0
    if (src.HasFlag(OB_TCHARGE_MOL))
1287
0
      this->SetFlag(OB_TCHARGE_MOL);
1288
0
    if (src.HasFlag(OB_PCHARGE_MOL))
1289
0
      this->SetFlag(OB_PCHARGE_MOL);
1290
0
    if (src.HasFlag(OB_PERIODIC_MOL))
1291
0
      this->SetFlag(OB_PERIODIC_MOL);
1292
0
    if (src.HasFlag(OB_HYBRID_MOL))
1293
0
      this->SetFlag(OB_HYBRID_MOL);
1294
0
    if (src.HasFlag(OB_AROMATIC_MOL))
1295
0
      this->SetFlag(OB_AROMATIC_MOL);
1296
0
    if (src.HasFlag(OB_CHAINS_MOL))
1297
0
      this->SetFlag(OB_CHAINS_MOL);
1298
    //this->_flags = src.GetFlags(); //Copy all flags. Perhaps too drastic a change
1299
1300
1301
    //Copy Residue information
1302
0
    unsigned int NumRes = src.NumResidues();
1303
0
    if (NumRes)
1304
0
      {
1305
0
        unsigned int k;
1306
0
        OBResidue *src_res = nullptr;
1307
0
        OBResidue *res = nullptr;
1308
0
        OBAtom *src_atom = nullptr;
1309
0
        OBAtom *atom = nullptr;
1310
0
        vector<OBAtom*>::iterator ii;
1311
0
        for (k=0 ; k<NumRes ; ++k)
1312
0
          {
1313
0
            res = NewResidue();
1314
0
            src_res = src.GetResidue(k);
1315
0
            *res = *src_res; //does not copy atoms
1316
0
            for (src_atom=src_res->BeginAtom(ii) ; src_atom ; src_atom=src_res->NextAtom(ii))
1317
0
              {
1318
0
                atom = GetAtom(src_atom->GetIdx());
1319
0
                res->AddAtom(atom);
1320
0
                res->SetAtomID(atom,src_res->GetAtomID(src_atom));
1321
0
                res->SetHetAtom(atom,src_res->IsHetAtom(src_atom));
1322
0
                res->SetSerialNum(atom,src_res->GetSerialNum(src_atom));
1323
0
              }
1324
0
          }
1325
0
      }
1326
1327
    //Copy conformer information
1328
0
    if (src.NumConformers() > 1) {
1329
0
      int k;//,l;
1330
0
      vector<double*> conf;
1331
0
      int currConf = -1;
1332
0
      double* xyz = nullptr;
1333
0
      for (k=0 ; k<src.NumConformers() ; ++k) {
1334
0
        xyz = new double [3*src.NumAtoms()];
1335
0
        memcpy( xyz, src.GetConformer(k), sizeof( double )*3*src.NumAtoms() );
1336
0
        conf.push_back(xyz);
1337
1338
0
        if( src.GetConformer(k) == src._c ) {
1339
0
          currConf = k;
1340
0
        }
1341
0
      }
1342
1343
0
      SetConformers(conf);
1344
0
      if( currConf >= 0 && _vconf.size() ) {
1345
0
        _c = _vconf[currConf];
1346
0
      }
1347
0
    }
1348
1349
    //Copy all the OBGenericData, providing the new molecule, this,
1350
    //for those classes like OBRotameterList which contain Atom pointers
1351
    //OBGenericData classes can choose not to be cloned by returning NULL
1352
0
    vector<OBGenericData*>::iterator itr;
1353
0
    for(itr=src.BeginData();itr!=src.EndData();++itr)
1354
0
      {
1355
0
        OBGenericData* pCopiedData = (*itr)->Clone(this);
1356
0
        SetData(pCopiedData);
1357
0
      }
1358
1359
0
    if (src.HasChiralityPerceived())
1360
0
      SetChiralityPerceived();
1361
1362
0
    return(*this);
1363
0
  }
1364
1365
  OBMol &OBMol::operator+=(const OBMol &source)
1366
0
  {
1367
0
    OBMol &src = (OBMol &)source;
1368
0
    vector<OBAtom*>::iterator i;
1369
0
    vector<OBBond*>::iterator j;
1370
0
    vector<OBResidue*>::iterator k;
1371
0
    OBAtom *atom;
1372
0
    OBBond *bond;
1373
0
    OBResidue *residue;
1374
1375
0
    BeginModify();
1376
1377
0
    int prevatms = NumAtoms();
1378
1379
0
    string extitle(src.GetTitle());
1380
0
    if(!extitle.empty())
1381
0
      _title += "_" + extitle;
1382
1383
    // First, handle atoms and bonds
1384
0
    map<unsigned long int, unsigned long int> correspondingId;
1385
0
    for (atom = src.BeginAtom(i) ; atom ; atom = src.NextAtom(i)) {
1386
0
      AddAtom(*atom, true); // forceNewId=true (don't reuse the original Id)
1387
0
      OBAtom *addedAtom = GetAtom(NumAtoms());
1388
0
      correspondingId[atom->GetId()] = addedAtom->GetId();
1389
0
    }
1390
0
    correspondingId[OBStereo::ImplicitRef] = OBStereo::ImplicitRef;
1391
1392
0
    for (bond = src.BeginBond(j) ; bond ; bond = src.NextBond(j)) {
1393
0
      bond->SetId(NoId);//Need to remove ID which relates to source mol rather than this mol
1394
0
      AddBond(bond->GetBeginAtomIdx() + prevatms,
1395
0
              bond->GetEndAtomIdx() + prevatms,
1396
0
              bond->GetBondOrder(), bond->GetFlags());
1397
0
    }
1398
1399
    // Now update all copied residues too
1400
0
    for (residue = src.BeginResidue(k); residue; residue = src.NextResidue(k)) {
1401
0
      AddResidue(*residue);
1402
1403
0
      FOR_ATOMS_OF_RESIDUE(resAtom, residue)
1404
0
        {
1405
          // This is the equivalent atom in our combined molecule
1406
0
          atom = GetAtom(resAtom->GetIdx() + prevatms);
1407
          // So we add this to the last-added residue
1408
          // (i.e., what we just copied)
1409
0
          (_residue[_residue.size() - 1])->AddAtom(atom);
1410
0
        }
1411
0
    }
1412
1413
    // Copy the stereo
1414
0
    std::vector<OBGenericData*> vdata = src.GetAllData(OBGenericDataType::StereoData);
1415
0
    for (std::vector<OBGenericData*>::iterator data = vdata.begin(); data != vdata.end(); ++data) {
1416
0
      OBStereo::Type datatype = ((OBStereoBase*)*data)->GetType();
1417
0
      if (datatype == OBStereo::CisTrans) {
1418
0
        OBCisTransStereo *ct = dynamic_cast<OBCisTransStereo*>(*data);
1419
0
        OBCisTransStereo *nct = new OBCisTransStereo(this);
1420
0
        OBCisTransStereo::Config ct_cfg = ct->GetConfig();
1421
0
        ct_cfg.begin = correspondingId[ct_cfg.begin];
1422
0
        ct_cfg.end = correspondingId[ct_cfg.end];
1423
0
        for(OBStereo::RefIter ri = ct_cfg.refs.begin(); ri != ct_cfg.refs.end(); ++ri)
1424
0
          *ri = correspondingId[*ri];
1425
0
        nct->SetConfig(ct_cfg);
1426
0
        SetData(nct);
1427
0
      }
1428
0
      else if (datatype == OBStereo::Tetrahedral) {
1429
0
        OBTetrahedralStereo *ts = dynamic_cast<OBTetrahedralStereo*>(*data);
1430
0
        OBTetrahedralStereo *nts = new OBTetrahedralStereo(this);
1431
0
        OBTetrahedralStereo::Config ts_cfg = ts->GetConfig();
1432
0
        ts_cfg.center = correspondingId[ts_cfg.center];
1433
0
        ts_cfg.from = correspondingId[ts_cfg.from];
1434
0
        for(OBStereo::RefIter ri = ts_cfg.refs.begin(); ri != ts_cfg.refs.end(); ++ri)
1435
0
          *ri = correspondingId[*ri];
1436
0
        nts->SetConfig(ts_cfg);
1437
0
        SetData(nts);
1438
0
      }
1439
0
    }
1440
1441
    // TODO: This is actually a weird situation (e.g., adding a 2D mol to 3D one)
1442
    // We should do something to update the src coordinates if they're not 3D
1443
0
    if(src.GetDimension()<_dimension)
1444
0
      _dimension = src.GetDimension();
1445
    // TODO: Periodicity is similarly weird (e.g., adding nonperiodic data to
1446
    // a crystal, or two incompatible lattice parameters).  For now, just assume
1447
    // we intend to keep the lattice of the source (no updates necessary)
1448
1449
0
    EndModify();
1450
1451
0
    return(*this);
1452
0
  }
1453
1454
  bool OBMol::Clear()
1455
17.5k
  {
1456
17.5k
    if (obErrorLog.GetOutputLevel() >= obAuditMsg)
1457
0
      obErrorLog.ThrowError(__FUNCTION__,
1458
0
                            "Ran OpenBabel::Clear Molecule", obAuditMsg);
1459
1460
    // Destroy residues first: ~OBResidue() walks its atom list to clear
1461
    // back-pointers via SetResidue(nullptr). If atoms were destroyed
1462
    // first, those calls would dereference dead pointers (UBSAN trips
1463
    // in residue.cpp:853). The symmetric path in ~OBAtom() already
1464
    // handles the reverse direction by checking _residue != nullptr.
1465
17.5k
    unsigned int ii;
1466
17.5k
    for (ii=0 ; ii<_residue.size() ; ++ii)
1467
0
      {
1468
0
        DestroyResidue(_residue[ii]);
1469
0
      }
1470
17.5k
    _residue.clear();
1471
1472
17.5k
    vector<OBAtom*>::iterator i;
1473
17.5k
    vector<OBBond*>::iterator j;
1474
17.5k
    for (i = _vatom.begin();i != _vatom.end();++i)
1475
0
      {
1476
0
        DestroyAtom(*i);
1477
0
        *i = nullptr;
1478
0
      }
1479
17.5k
    for (j = _vbond.begin();j != _vbond.end();++j)
1480
0
      {
1481
0
        DestroyBond(*j);
1482
0
        *j = nullptr;
1483
0
      }
1484
1485
17.5k
    _atomIds.clear();
1486
17.5k
    _bondIds.clear();
1487
17.5k
    _natoms = _nbonds = 0;
1488
1489
    //clear out the multiconformer data
1490
17.5k
    vector<double*>::iterator k;
1491
17.5k
    for (k = _vconf.begin();k != _vconf.end();++k)
1492
0
      delete [] *k;
1493
17.5k
    _vconf.clear();
1494
1495
    //Clear flags except OB_PATTERN_STRUCTURE which is left the same
1496
17.5k
    _flags &= OB_PATTERN_STRUCTURE;
1497
1498
17.5k
    _c = nullptr;
1499
17.5k
    _mod = 0;
1500
1501
    // Clean up generic data via the base class
1502
17.5k
    return(OBBase::Clear());
1503
17.5k
  }
1504
1505
  void OBMol::BeginModify()
1506
17.3k
  {
1507
    //suck coordinates from _c into _v for each atom
1508
17.3k
    if (!_mod && !Empty())
1509
0
      {
1510
0
        OBAtom *atom;
1511
0
        vector<OBAtom*>::iterator i;
1512
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1513
0
          {
1514
0
            atom->SetVector();
1515
0
            atom->ClearCoordPtr();
1516
0
          }
1517
1518
0
        vector<double*>::iterator j;
1519
0
        for (j = _vconf.begin();j != _vconf.end();++j)
1520
0
          delete [] *j;
1521
1522
0
        _c = nullptr;
1523
0
        _vconf.clear();
1524
1525
        //Destroy rotamer list if necessary
1526
0
        if ((OBRotamerList *)GetData(OBGenericDataType::RotamerList))
1527
0
          {
1528
0
            delete (OBRotamerList *)GetData(OBGenericDataType::RotamerList);
1529
0
            DeleteData(OBGenericDataType::RotamerList);
1530
0
          }
1531
0
      }
1532
1533
17.3k
    _mod++;
1534
17.3k
  }
1535
1536
  void OBMol::EndModify(bool nukePerceivedData)
1537
14.9k
  {
1538
14.9k
    if (_mod == 0)
1539
0
      {
1540
0
        obErrorLog.ThrowError(__FUNCTION__, "_mod is negative - EndModify() called too many times", obDebug);
1541
0
        return;
1542
0
      }
1543
1544
14.9k
    _mod--;
1545
1546
14.9k
    if (_mod)
1547
0
      return;
1548
1549
    // wipe all but whether it has aromaticity perceived, is a reaction, or has periodic boundaries enabled
1550
14.9k
    if (nukePerceivedData)
1551
14.9k
      _flags = _flags & (OB_AROMATIC_MOL|OB_REACTION_MOL|OB_PERIODIC_MOL);
1552
1553
14.9k
    _c = nullptr;
1554
1555
14.9k
    if (Empty())
1556
2.45k
      return;
1557
1558
    //if atoms present convert coords into array
1559
12.4k
    double *c = new double [NumAtoms()*3];
1560
12.4k
    _c = c;
1561
1562
12.4k
    unsigned int idx;
1563
12.4k
    OBAtom *atom;
1564
12.4k
    vector<OBAtom*>::iterator j;
1565
1.65M
    for (idx=0,atom = BeginAtom(j);atom;atom = NextAtom(j),++idx)
1566
1.64M
      {
1567
1.64M
        atom->SetIdx(idx+1);
1568
1.64M
        (atom->GetVector()).Get(&_c[idx*3]);
1569
1.64M
        atom->SetCoordPtr(&_c);
1570
1.64M
      }
1571
12.4k
    _vconf.push_back(c);
1572
1573
    // Always remove angle and torsion data, since they will interfere with the iterators
1574
    // PR#2812013
1575
12.4k
    DeleteData(OBGenericDataType::AngleData);
1576
12.4k
    DeleteData(OBGenericDataType::TorsionData);
1577
12.4k
  }
1578
1579
  void OBMol::DestroyAtom(OBAtom *atom)
1580
1.66M
  {
1581
1.66M
    if (atom)
1582
1.66M
      {
1583
1.66M
        delete atom;
1584
1.66M
        atom = nullptr;
1585
1.66M
      }
1586
1.66M
  }
1587
1588
  void OBMol::DestroyBond(OBBond *bond)
1589
16.6k
  {
1590
16.6k
    if (bond)
1591
16.6k
      {
1592
16.6k
        delete bond;
1593
16.6k
        bond = nullptr;
1594
16.6k
      }
1595
16.6k
  }
1596
1597
  void OBMol::DestroyResidue(OBResidue *residue)
1598
0
  {
1599
0
    if (residue)
1600
0
      {
1601
0
        delete residue;
1602
0
        residue = nullptr;
1603
0
      }
1604
0
  }
1605
1606
  OBAtom *OBMol::NewAtom()
1607
125k
  {
1608
125k
    return NewAtom(_atomIds.size());
1609
125k
  }
1610
1611
  //! \brief Instantiate a New Atom and add it to the molecule
1612
  //!
1613
  //! Checks bond_queue for any bonds that should be made to the new atom
1614
  //! and updates atom indexes.
1615
  OBAtom *OBMol::NewAtom(unsigned long id)
1616
125k
  {
1617
    //   BeginModify();
1618
1619
    // resize _atomIds if needed
1620
125k
    if (id >= _atomIds.size()) {
1621
125k
      unsigned int size = _atomIds.size();
1622
125k
      _atomIds.resize(id+1);
1623
125k
      for (unsigned long i = size; i < id; ++i)
1624
0
        _atomIds[i] = nullptr;
1625
125k
    }
1626
1627
125k
    if (_atomIds.at(id))
1628
0
      return nullptr;
1629
1630
125k
    OBAtom *obatom = new OBAtom;
1631
125k
    obatom->SetIdx(_natoms+1);
1632
125k
    obatom->SetParent(this);
1633
1634
125k
    _atomIds[id] = obatom;
1635
125k
    obatom->SetId(id);
1636
1637
125k
#define OBAtomIncrement 100
1638
1639
125k
    if (_natoms+1 >= _vatom.size())
1640
11.6k
      {
1641
11.6k
        _vatom.resize(_natoms+OBAtomIncrement);
1642
11.6k
        vector<OBAtom*>::iterator j;
1643
1.16M
        for (j = _vatom.begin(),j+=(_natoms+1);j != _vatom.end();++j)
1644
1.15M
          *j = nullptr;
1645
11.6k
      }
1646
125k
#undef OBAtomIncrement
1647
1648
1649
125k
    _vatom[_natoms] = obatom;
1650
125k
    _natoms++;
1651
1652
125k
    if (HasData(OBGenericDataType::VirtualBondData))
1653
0
      {
1654
        /*add bonds that have been queued*/
1655
0
        OBVirtualBond *vb;
1656
0
        vector<OBGenericData*> verase;
1657
0
        vector<OBGenericData*>::iterator i;
1658
0
        for (i = BeginData();i != EndData();++i)
1659
0
          if ((*i)->GetDataType() == OBGenericDataType::VirtualBondData)
1660
0
            {
1661
0
              vb = (OBVirtualBond*)*i;
1662
0
              if (vb->GetBgn() > _natoms || vb->GetEnd() > _natoms)
1663
0
                continue;
1664
0
              if (obatom->GetIdx() == static_cast<unsigned int>(vb->GetBgn())
1665
0
                  || obatom->GetIdx() == static_cast<unsigned int>(vb->GetEnd()))
1666
0
                {
1667
0
                  AddBond(vb->GetBgn(),vb->GetEnd(),vb->GetOrder());
1668
0
                  verase.push_back(*i);
1669
0
                }
1670
0
            }
1671
1672
0
        if (!verase.empty())
1673
0
          DeleteData(verase);
1674
0
      }
1675
1676
    // EndModify();
1677
1678
125k
    return(obatom);
1679
125k
  }
1680
1681
  OBResidue *OBMol::NewResidue()
1682
0
  {
1683
0
    OBResidue *obresidue = new OBResidue;
1684
0
    obresidue->SetIdx(_residue.size());
1685
0
    _residue.push_back(obresidue);
1686
0
    return(obresidue);
1687
0
  }
1688
1689
  OBBond *OBMol::NewBond()
1690
0
  {
1691
0
    return NewBond(_bondIds.size());
1692
0
  }
1693
1694
  //! \since version 2.1
1695
  //! \brief Instantiate a New Bond and add it to the molecule
1696
  //!
1697
  //! Sets the proper Bond index and insures this molecule is set as the parent.
1698
  OBBond *OBMol::NewBond(unsigned long id)
1699
0
  {
1700
    // resize _bondIds if needed
1701
0
    if (id >= _bondIds.size()) {
1702
0
      unsigned int size = _bondIds.size();
1703
0
      _bondIds.resize(id+1);
1704
0
      for (unsigned long i = size; i < id; ++i)
1705
0
        _bondIds[i] = nullptr;
1706
0
    }
1707
1708
0
    if (_bondIds.at(id))
1709
0
      return nullptr;
1710
1711
0
    OBBond *pBond = new OBBond;
1712
0
    pBond->SetParent(this);
1713
0
    pBond->SetIdx(_nbonds);
1714
1715
0
    _bondIds[id] = pBond;
1716
0
    pBond->SetId(id);
1717
1718
0
#define OBBondIncrement 100
1719
0
    if (_nbonds+1 >= _vbond.size())
1720
0
      {
1721
0
        _vbond.resize(_nbonds+OBBondIncrement);
1722
0
        vector<OBBond*>::iterator i;
1723
0
        for (i = _vbond.begin(),i+=(_nbonds+1);i != _vbond.end();++i)
1724
0
          *i = nullptr;
1725
0
      }
1726
0
#undef  OBBondIncrement
1727
1728
0
    _vbond[_nbonds] = (OBBond*)pBond;
1729
0
    _nbonds++;
1730
1731
0
    return(pBond);
1732
0
  }
1733
1734
  //! \brief Add an atom to a molecule
1735
  //!
1736
  //! Also checks bond_queue for any bonds that should be made to the new atom
1737
  bool OBMol::AddAtom(OBAtom &atom, bool forceNewId)
1738
1.53M
  {
1739
    //    BeginModify();
1740
1741
    // Use the existing atom Id unless either it's invalid or forceNewId has been specified
1742
1.53M
    unsigned long id;
1743
1.53M
    if (forceNewId)
1744
0
      id = _atomIds.size();
1745
1.53M
    else {
1746
1.53M
      id = atom.GetId();
1747
1.53M
      if (id == NoId)
1748
1.53M
        id = _atomIds.size();
1749
1.53M
    }
1750
1751
1.53M
    OBAtom *obatom = new OBAtom;
1752
1.53M
    *obatom = atom;
1753
1.53M
    obatom->SetIdx(_natoms+1);
1754
1.53M
    obatom->SetParent(this);
1755
1756
    // resize _atomIds if needed
1757
1.53M
    if (id >= _atomIds.size()) {
1758
1.53M
      unsigned int size = _atomIds.size();
1759
1.53M
      _atomIds.resize(id+1);
1760
1.53M
      for (unsigned long i = size; i < id; ++i)
1761
0
        _atomIds[i] = nullptr;
1762
1.53M
    }
1763
1764
1.53M
    obatom->SetId(id);
1765
1.53M
    _atomIds[id] = obatom;
1766
1767
1.53M
#define OBAtomIncrement 100
1768
1769
1.53M
    if (_natoms+1 >= _vatom.size())
1770
17.7k
      {
1771
17.7k
        _vatom.resize(_natoms+OBAtomIncrement);
1772
17.7k
        vector<OBAtom*>::iterator j;
1773
1.77M
        for (j = _vatom.begin(),j+=(_natoms+1);j != _vatom.end();++j)
1774
1.76M
          *j = nullptr;
1775
17.7k
      }
1776
1.53M
#undef OBAtomIncrement
1777
1778
1.53M
    _vatom[_natoms] = (OBAtom*)obatom;
1779
1.53M
    _natoms++;
1780
1781
1.53M
    if (HasData(OBGenericDataType::VirtualBondData))
1782
0
      {
1783
        /*add bonds that have been queued*/
1784
0
        OBVirtualBond *vb;
1785
0
        vector<OBGenericData*> verase;
1786
0
        vector<OBGenericData*>::iterator i;
1787
0
        for (i = BeginData();i != EndData();++i)
1788
0
          if ((*i)->GetDataType() == OBGenericDataType::VirtualBondData)
1789
0
            {
1790
0
              vb = (OBVirtualBond*)*i;
1791
0
              if (vb->GetBgn() > _natoms || vb->GetEnd() > _natoms)
1792
0
                continue;
1793
0
              if (obatom->GetIdx() == static_cast<unsigned int>(vb->GetBgn())
1794
0
                  || obatom->GetIdx() == static_cast<unsigned int>(vb->GetEnd()))
1795
0
                {
1796
0
                  AddBond(vb->GetBgn(),vb->GetEnd(),vb->GetOrder());
1797
0
                  verase.push_back(*i);
1798
0
                }
1799
0
            }
1800
1801
0
        if (!verase.empty())
1802
0
          DeleteData(verase);
1803
0
      }
1804
1805
    //    EndModify();
1806
1807
1.53M
    return(true);
1808
1.53M
  }
1809
1810
  bool OBMol::InsertAtom(OBAtom &atom)
1811
0
  {
1812
0
    BeginModify();
1813
0
    AddAtom(atom);
1814
0
    EndModify();
1815
1816
0
    return(true);
1817
0
  }
1818
1819
  bool OBMol::AddResidue(OBResidue &residue)
1820
0
  {
1821
0
    BeginModify();
1822
1823
0
    OBResidue *obresidue = new OBResidue;
1824
0
    *obresidue = residue;
1825
1826
0
    obresidue->SetIdx(_residue.size());
1827
1828
0
    _residue.push_back(obresidue);
1829
1830
0
    EndModify();
1831
1832
0
    return(true);
1833
0
  }
1834
1835
  bool OBMol::StripSalts(unsigned int threshold)
1836
0
  {
1837
0
    vector<vector<int> > cfl;
1838
0
    vector<vector<int> >::iterator i,max;
1839
1840
0
    ContigFragList(cfl);
1841
0
    if (cfl.empty() || cfl.size() == 1)
1842
0
      {
1843
0
        return(false);
1844
0
      }
1845
1846
1847
0
    obErrorLog.ThrowError(__FUNCTION__, "Ran OpenBabel::StripSalts", obAuditMsg);
1848
1849
0
    max = cfl.begin();
1850
0
    for (i = cfl.begin();i != cfl.end();++i)
1851
0
      {
1852
0
        if ((*max).size() < (*i).size())
1853
0
          max = i;
1854
0
      }
1855
1856
0
    vector<int>::iterator j;
1857
0
    vector<OBAtom*> delatoms;
1858
0
    set<int> atomIndices;
1859
0
    for (i = cfl.begin(); i != cfl.end(); ++i)
1860
0
      {
1861
0
        if (i->size() < threshold || (threshold == 0 && i != max))
1862
0
          {
1863
0
            for (j = (*i).begin(); j != (*i).end(); ++j)
1864
0
              {
1865
0
                if (atomIndices.find( *j ) == atomIndices.end())
1866
0
                  {
1867
0
                    delatoms.push_back(GetAtom(*j));
1868
0
                    atomIndices.insert(*j);
1869
0
                  }
1870
0
              }
1871
0
          }
1872
0
      }
1873
1874
0
    if (!delatoms.empty())
1875
0
      {
1876
        //      int tmpflags = _flags & (~(OB_SSSR_MOL));
1877
0
        BeginModify();
1878
0
        vector<OBAtom*>::iterator k;
1879
0
        for (k = delatoms.begin(); k != delatoms.end(); ++k)
1880
0
          DeleteAtom((OBAtom*)*k);
1881
0
        EndModify();
1882
        //      _flags = tmpflags;  // Gave crash when SmartsPattern::Match()
1883
        // was called susequently
1884
        // Hans De Winter; 03-nov-2010
1885
0
      }
1886
1887
0
    return (true);
1888
0
  }
1889
1890
  // Convenience function used by the DeleteHydrogens methods
1891
  static bool IsSuppressibleHydrogen(OBAtom *atom)
1892
0
  {
1893
0
    if (atom->GetIsotope() == 0 && atom->GetHvyDegree() == 1 && atom->GetFormalCharge() == 0
1894
0
        && !atom->GetData("Atom Class"))
1895
0
      return true;
1896
0
    else
1897
0
      return false;
1898
0
  }
1899
1900
  bool OBMol::DeletePolarHydrogens()
1901
0
  {
1902
0
    OBAtom *atom;
1903
0
    vector<OBAtom*>::iterator i;
1904
0
    vector<OBAtom*> delatoms;
1905
1906
0
    obErrorLog.ThrowError(__FUNCTION__,
1907
0
                          "Ran OpenBabel::DeleteHydrogens -- polar",
1908
0
                          obAuditMsg);
1909
1910
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1911
0
      if (atom->IsPolarHydrogen() && IsSuppressibleHydrogen(atom))
1912
0
        delatoms.push_back(atom);
1913
1914
0
    if (delatoms.empty())
1915
0
      return(true);
1916
1917
0
    IncrementMod();
1918
1919
0
    for (i = delatoms.begin();i != delatoms.end();++i)
1920
0
      DeleteAtom((OBAtom *)*i);
1921
1922
0
    DecrementMod();
1923
1924
0
    SetSSSRPerceived(false);
1925
0
    SetLSSRPerceived(false);
1926
0
    return(true);
1927
0
  }
1928
1929
1930
  bool OBMol::DeleteNonPolarHydrogens()
1931
0
  {
1932
0
    OBAtom *atom;
1933
0
    vector<OBAtom*>::iterator i;
1934
0
    vector<OBAtom*> delatoms;
1935
1936
0
    obErrorLog.ThrowError(__FUNCTION__,
1937
0
                          "Ran OpenBabel::DeleteHydrogens -- nonpolar",
1938
0
                          obAuditMsg);
1939
1940
1941
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1942
0
      if (atom->IsNonPolarHydrogen() && IsSuppressibleHydrogen(atom))
1943
0
        delatoms.push_back(atom);
1944
1945
0
    if (delatoms.empty())
1946
0
      return(true);
1947
1948
    /*
1949
      int idx1,idx2;
1950
      vector<double*>::iterator j;
1951
      for (idx1=0,idx2=0,atom = BeginAtom(i);atom;atom = NextAtom(i),++idx1)
1952
      if (atom->GetAtomicNum() != OBElements::Hydrogen)
1953
      {
1954
      for (j = _vconf.begin();j != _vconf.end();++j)
1955
      memcpy((char*)&((*j)[idx2*3]),(char*)&((*j)[idx1*3]),sizeof(double)*3);
1956
      idx2++;
1957
      }
1958
    */
1959
1960
0
    IncrementMod();
1961
1962
0
    for (i = delatoms.begin();i != delatoms.end();++i)
1963
0
      DeleteAtom((OBAtom *)*i);
1964
1965
0
    DecrementMod();
1966
1967
0
    SetSSSRPerceived(false);
1968
0
    SetLSSRPerceived(false);
1969
0
    return(true);
1970
0
  }
1971
1972
  bool OBMol::DeleteHydrogens()
1973
0
  {
1974
0
    OBAtom *atom;//,*nbr;
1975
0
    vector<OBAtom*>::iterator i;
1976
0
    vector<OBAtom*> delatoms,va;
1977
1978
0
    obErrorLog.ThrowError(__FUNCTION__,
1979
0
                          "Ran OpenBabel::DeleteHydrogens", obAuditMsg);
1980
1981
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1982
0
      if (atom->GetAtomicNum() == OBElements::Hydrogen && IsSuppressibleHydrogen(atom))
1983
0
        delatoms.push_back(atom);
1984
1985
0
    SetHydrogensAdded(false);
1986
1987
0
    if (delatoms.empty())
1988
0
      return(true);
1989
1990
    /* decide whether these flags need to be reset
1991
       _flags &= (~(OB_ATOMTYPES_MOL));
1992
       _flags &= (~(OB_HYBRID_MOL));
1993
       _flags &= (~(OB_PCHARGE_MOL));
1994
       _flags &= (~(OB_IMPVAL_MOL));
1995
    */
1996
1997
0
    IncrementMod();
1998
1999
    // This is slow -- we need methods to delete a set of atoms
2000
    //  and to delete a set of bonds
2001
    // Calling this sequentially does result in correct behavior
2002
    //  (e.g., fixing PR# 1704551)
2003
0
    OBBondIterator bi;
2004
0
    for (i = delatoms.begin(); i != delatoms.end(); ++i) {
2005
0
      OBAtom* nbr = (*i)->BeginNbrAtom(bi);
2006
0
      if (nbr) // defensive
2007
0
        nbr->SetImplicitHCount(nbr->GetImplicitHCount() + 1);
2008
0
      DeleteAtom((OBAtom *)*i);
2009
0
    }
2010
2011
0
    DecrementMod();
2012
2013
0
    SetSSSRPerceived(false);
2014
0
    SetLSSRPerceived(false);
2015
0
    return(true);
2016
0
  }
2017
2018
  bool OBMol::DeleteHydrogens(OBAtom *atom)
2019
  //deletes all hydrogens attached to the atom passed to the function
2020
0
  {
2021
0
    OBAtom *nbr;
2022
0
    vector<OBAtom*>::iterator i;
2023
0
    vector<OBBond*>::iterator k;
2024
0
    vector<OBAtom*> delatoms;
2025
2026
0
    for (nbr = atom->BeginNbrAtom(k);nbr;nbr = atom->NextNbrAtom(k))
2027
0
      if (nbr->GetAtomicNum() == OBElements::Hydrogen && IsSuppressibleHydrogen(atom))
2028
0
        delatoms.push_back(nbr);
2029
2030
0
    if (delatoms.empty())
2031
0
      return(true);
2032
2033
0
    IncrementMod();
2034
0
    for (i = delatoms.begin();i != delatoms.end();++i)
2035
0
      DeleteHydrogen((OBAtom*)*i);
2036
0
    DecrementMod();
2037
2038
0
    SetHydrogensAdded(false);
2039
0
    SetSSSRPerceived(false);
2040
0
    SetLSSRPerceived(false);
2041
0
    return(true);
2042
0
  }
2043
2044
  bool OBMol::DeleteHydrogen(OBAtom *atom)
2045
  //deletes the hydrogen atom passed to the function
2046
0
  {
2047
0
    if (atom->GetAtomicNum() != OBElements::Hydrogen)
2048
0
      return false;
2049
2050
    // OBAngleData/OBTorsionData cache raw OBAtom* pointers; drop them now so
2051
    // a later FOR_ANGLES_OF_MOL doesn't read freed memory.
2052
0
    DeleteData(OBGenericDataType::AngleData);
2053
0
    DeleteData(OBGenericDataType::TorsionData);
2054
2055
0
    unsigned atomidx = atom->GetIdx();
2056
2057
    //find bonds to delete
2058
0
    OBAtom *nbr;
2059
0
    vector<OBBond*> vdb;
2060
0
    vector<OBBond*>::iterator j;
2061
0
    for (nbr = atom->BeginNbrAtom(j);nbr;nbr = atom->NextNbrAtom(j))
2062
0
      vdb.push_back(*j);
2063
2064
0
    IncrementMod();
2065
0
    for (j = vdb.begin();j != vdb.end();++j)
2066
0
      DeleteBond((OBBond*)*j); //delete bonds
2067
0
    DecrementMod();
2068
2069
0
    int idx;
2070
0
    if (atomidx != NumAtoms())
2071
0
      {
2072
0
        idx = atom->GetCoordinateIdx();
2073
0
        int size = NumAtoms()-atom->GetIdx();
2074
0
        vector<double*>::iterator k;
2075
0
        for (k = _vconf.begin();k != _vconf.end();++k)
2076
0
          memmove((char*)&(*k)[idx],(char*)&(*k)[idx+3],sizeof(double)*3*size);
2077
2078
0
      }
2079
2080
    // Deleting hydrogens does not invalidate the stereo objects
2081
    // - however, any explicit refs to the hydrogen atom must be
2082
    //   converted to implicit refs
2083
0
    OBStereo::Ref id = atom->GetId();
2084
0
    StereoRefToImplicit(*this, id);
2085
2086
0
    _atomIds[id] = nullptr;
2087
0
    _vatom.erase(_vatom.begin()+(atomidx-1));
2088
0
    _natoms--;
2089
2090
    //reset all the indices to the atoms
2091
0
    vector<OBAtom*>::iterator i;
2092
0
    OBAtom *atomi;
2093
0
    for (idx=1,atomi = BeginAtom(i);atomi;atomi = NextAtom(i),++idx)
2094
0
      atomi->SetIdx(idx);
2095
2096
0
    SetHydrogensAdded(false);
2097
2098
0
    DestroyAtom(atom);
2099
2100
0
    SetSSSRPerceived(false);
2101
0
    SetLSSRPerceived(false);
2102
0
    return(true);
2103
0
  }
2104
2105
  /*
2106
  this has become a wrapper for backward compatibility
2107
  */
2108
  bool OBMol::AddHydrogens(bool polaronly, bool correctForPH, double pH)
2109
0
  {
2110
0
    return(AddNewHydrogens(polaronly ? PolarHydrogen : AllHydrogen, correctForPH, pH));
2111
0
  }
2112
2113
  static bool AtomIsNSOP(OBAtom *atom)
2114
0
  {
2115
0
    switch (atom->GetAtomicNum()) {
2116
0
    case OBElements::Nitrogen:
2117
0
    case OBElements::Sulfur:
2118
0
    case OBElements::Oxygen:
2119
0
    case OBElements::Phosphorus:
2120
0
      return true;
2121
0
    default:
2122
0
      return false;
2123
0
    }
2124
0
  }
2125
2126
  //! \return a "corrected" bonding radius based on the hybridization.
2127
  //! Scales the covalent radius by 0.95 for sp2 and 0.90 for sp hybrids
2128
  static double CorrectedBondRad(unsigned int elem, unsigned int hyb)
2129
0
  {
2130
0
    double rad = OBElements::GetCovalentRad(elem);
2131
0
    switch (hyb) {
2132
0
    case 2:
2133
0
      return rad * 0.95;
2134
0
    case 1:
2135
0
      return rad * 0.90;
2136
0
    default:
2137
0
      return rad;
2138
0
    }
2139
0
  }
2140
2141
  bool OBMol::AddNewHydrogens(HydrogenType whichHydrogen, bool correctForPH, double pH)
2142
0
  {
2143
0
    if (!IsCorrectedForPH() && correctForPH)
2144
0
      CorrectForPH(pH);
2145
2146
0
    if (HasHydrogensAdded())
2147
0
      return(true);
2148
2149
0
    bool hasChiralityPerceived = this->HasChiralityPerceived(); // remember
2150
2151
    /*
2152
    //
2153
    // This was causing bug #1892844 in avogadro. We also want to add hydrogens if the molecule has no bonds.
2154
    //
2155
    if(NumBonds()==0 && NumAtoms()!=1)
2156
    {
2157
    obErrorLog.ThrowError(__FUNCTION__,
2158
    "Did not run OpenBabel::AddHydrogens on molecule with no bonds", obAuditMsg);
2159
    return true;
2160
    }
2161
    */
2162
0
    if (whichHydrogen == AllHydrogen)
2163
0
      obErrorLog.ThrowError(__FUNCTION__,
2164
0
                            "Ran OpenBabel::AddHydrogens", obAuditMsg);
2165
0
    else if (whichHydrogen == PolarHydrogen)
2166
0
      obErrorLog.ThrowError(__FUNCTION__,
2167
0
                            "Ran OpenBabel::AddHydrogens -- polar only", obAuditMsg);
2168
0
    else
2169
0
      obErrorLog.ThrowError(__FUNCTION__,
2170
0
                            "Ran OpenBabel::AddHydrogens -- nonpolar only", obAuditMsg);
2171
2172
    // Make sure we have conformers (PR#1665519)
2173
0
    if (!_vconf.empty() && !Empty()) {
2174
0
      OBAtom *atom;
2175
0
      vector<OBAtom*>::iterator i;
2176
0
      for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2177
0
        {
2178
0
          atom->SetVector();
2179
0
        }
2180
0
    }
2181
2182
0
    SetHydrogensAdded(); // This must come after EndModify() as EndModify() wipes the flags
2183
    // If chirality was already perceived, remember this (to avoid wiping information
2184
0
    if (hasChiralityPerceived)
2185
0
      this->SetChiralityPerceived();
2186
2187
    //count up number of hydrogens to add
2188
0
    OBAtom *atom,*h;
2189
0
    int hcount,count=0;
2190
0
    vector<pair<OBAtom*,int> > vhadd;
2191
0
    vector<OBAtom*>::iterator i;
2192
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2193
0
      {
2194
0
        if (whichHydrogen == PolarHydrogen && !AtomIsNSOP(atom))
2195
0
          continue;
2196
0
        if (whichHydrogen == NonPolarHydrogen && AtomIsNSOP(atom))
2197
0
          continue;
2198
2199
0
        hcount = atom->GetImplicitHCount();
2200
0
        atom->SetImplicitHCount(0);
2201
2202
0
        if (hcount)
2203
0
          {
2204
0
            vhadd.push_back(pair<OBAtom*,int>(atom,hcount));
2205
0
            count += hcount;
2206
0
          }
2207
0
      }
2208
2209
0
    if (count == 0) {
2210
      // Make sure to clear SSSR and aromatic flags we may have tripped above
2211
0
      _flags &= (~(OB_SSSR_MOL|OB_AROMATIC_MOL));
2212
0
      return(true);
2213
0
    }
2214
0
    bool hasCoords = HasNonZeroCoords();
2215
2216
    //realloc memory in coordinate arrays for new hydrogens
2217
0
    double *tmpf;
2218
0
    vector<double*>::iterator j;
2219
0
    for (j = _vconf.begin();j != _vconf.end();++j)
2220
0
      {
2221
0
        tmpf = new double [(NumAtoms()+count)*3];
2222
0
        memset(tmpf,'\0',sizeof(double)*(NumAtoms()+count)*3);
2223
0
        if (hasCoords)
2224
0
          memcpy(tmpf,(*j),sizeof(double)*NumAtoms()*3);
2225
0
        delete []*j;
2226
0
        *j = tmpf;
2227
0
      }
2228
2229
0
    IncrementMod();
2230
2231
0
    int m,n;
2232
0
    vector3 v;
2233
0
    vector<pair<OBAtom*,int> >::iterator k;
2234
0
    double hbrad = CorrectedBondRad(1, 0);
2235
2236
0
    for (k = vhadd.begin();k != vhadd.end();++k)
2237
0
      {
2238
0
        atom = k->first;
2239
0
        double bondlen = hbrad + CorrectedBondRad(atom->GetAtomicNum(), atom->GetHyb());
2240
0
        for (m = 0;m < k->second;++m)
2241
0
          {
2242
0
            int badh = 0;
2243
0
            for (n = 0;n < NumConformers();++n)
2244
0
              {
2245
0
                SetConformer(n);
2246
0
                if (hasCoords)
2247
0
                  {
2248
                    // Ensure that add hydrogens only returns finite coords
2249
                    //atom->GetNewBondVector(v,bondlen);
2250
0
                    v = OBBuilder::GetNewBondVector(atom,bondlen);
2251
0
                    if (isfinite(v.x()) || isfinite(v.y()) || isfinite(v.z())) {
2252
0
                      _c[(NumAtoms())*3]   = v.x();
2253
0
                      _c[(NumAtoms())*3+1] = v.y();
2254
0
                      _c[(NumAtoms())*3+2] = v.z();
2255
0
                    }
2256
0
                    else {
2257
0
                      _c[(NumAtoms())*3]   = 0.0;
2258
0
                      _c[(NumAtoms())*3+1] = 0.0;
2259
0
                      _c[(NumAtoms())*3+2] = 0.0;
2260
0
                      obErrorLog.ThrowError(__FUNCTION__,
2261
0
                                            "Ran OpenBabel::AddHydrogens -- no reasonable bond geometry for desired hydrogen.",
2262
0
                                            obAuditMsg);
2263
0
                      badh++;
2264
0
                    }
2265
0
                  }
2266
0
                else
2267
0
                  memset((char*)&_c[NumAtoms()*3],'\0',sizeof(double)*3);
2268
0
              }
2269
0
            if(badh == 0 || badh < NumConformers())
2270
0
              {
2271
                // Add the new H atom to the appropriate residue list
2272
                //but avoid doing perception by checking for existence of residue
2273
                //just in case perception is trigger, make sure GetResidue is called
2274
                //before adding the hydrogen to the molecule
2275
0
                OBResidue *res = atom->HasResidue() ? atom->GetResidue() : nullptr;
2276
0
                h = NewAtom();
2277
0
                h->SetType("H");
2278
0
                h->SetAtomicNum(1);
2279
0
                string aname = "H";
2280
2281
0
                if(res)
2282
0
                {
2283
0
                  res->AddAtom(h);
2284
0
                  res->SetAtomID(h,aname);
2285
2286
                  //hydrogen should inherit hetatm status of heteroatom (default is false)
2287
0
                  if(res->IsHetAtom(atom))
2288
0
                  {
2289
0
                      res->SetHetAtom(h, true);
2290
0
                  }
2291
0
                }
2292
2293
0
                int bondFlags = 0;
2294
0
                AddBond(atom->GetIdx(),h->GetIdx(),1, bondFlags);
2295
0
                if (_c) {
2296
0
                  h->SetCoordPtr(&_c);
2297
0
                }
2298
0
                OpenBabel::ImplicitRefToStereo(*this, atom->GetId(), h->GetId());
2299
0
              }
2300
0
          }
2301
0
      }
2302
2303
0
    DecrementMod();
2304
2305
    //reset atom type and partial charge flags
2306
0
    _flags &= (~(OB_PCHARGE_MOL|OB_ATOMTYPES_MOL|OB_SSSR_MOL|OB_AROMATIC_MOL|OB_HYBRID_MOL));
2307
2308
0
    return(true);
2309
0
  }
2310
2311
  bool OBMol::AddPolarHydrogens()
2312
0
  {
2313
0
    return(AddNewHydrogens(PolarHydrogen));
2314
0
  }
2315
2316
  bool OBMol::AddNonPolarHydrogens()
2317
0
  {
2318
0
    return(AddNewHydrogens(NonPolarHydrogen));
2319
0
  }
2320
2321
  bool OBMol::AddHydrogens(OBAtom *atom)
2322
0
  {
2323
0
    int hcount = atom->GetImplicitHCount();
2324
0
    if (hcount == 0)
2325
0
      return true;
2326
2327
0
    atom->SetImplicitHCount(0);
2328
2329
0
    vector<pair<OBAtom*, int> > vhadd;
2330
0
    vhadd.push_back(pair<OBAtom*,int>(atom, hcount));
2331
2332
    //realloc memory in coordinate arrays for new hydroges
2333
0
    double *tmpf;
2334
0
    vector<double*>::iterator j;
2335
0
    for (j = _vconf.begin();j != _vconf.end();++j)
2336
0
      {
2337
0
        tmpf = new double [(NumAtoms()+hcount)*3+10];
2338
0
        memcpy(tmpf,(*j),sizeof(double)*NumAtoms()*3);
2339
0
        delete []*j;
2340
0
        *j = tmpf;
2341
0
      }
2342
2343
0
    IncrementMod();
2344
2345
0
    int m,n;
2346
0
    vector3 v;
2347
0
    vector<pair<OBAtom*,int> >::iterator k;
2348
0
    double hbrad = CorrectedBondRad(1,0);
2349
2350
0
    OBAtom *h;
2351
0
    for (k = vhadd.begin();k != vhadd.end();++k)
2352
0
      {
2353
0
        atom = k->first;
2354
0
        double bondlen = hbrad + CorrectedBondRad(atom->GetAtomicNum(),atom->GetHyb());
2355
0
        for (m = 0;m < k->second;++m)
2356
0
          {
2357
0
            for (n = 0;n < NumConformers();++n)
2358
0
              {
2359
0
                SetConformer(n);
2360
                //atom->GetNewBondVector(v,bondlen);
2361
0
                v = OBBuilder::GetNewBondVector(atom,bondlen);
2362
0
                _c[(NumAtoms())*3]   = v.x();
2363
0
                _c[(NumAtoms())*3+1] = v.y();
2364
0
                _c[(NumAtoms())*3+2] = v.z();
2365
0
              }
2366
0
            h = NewAtom();
2367
0
            h->SetType("H");
2368
0
            h->SetAtomicNum(1);
2369
2370
0
            int bondFlags = 0;
2371
0
            AddBond(atom->GetIdx(),h->GetIdx(),1, bondFlags);
2372
0
            h->SetCoordPtr(&_c);
2373
0
            OpenBabel::ImplicitRefToStereo(*this, atom->GetId(), h->GetId());
2374
0
          }
2375
0
      }
2376
2377
0
    DecrementMod();
2378
0
    SetConformer(0);
2379
2380
    //reset atom type and partial charge flags
2381
    //_flags &= (~(OB_PCHARGE_MOL|OB_ATOMTYPES_MOL));
2382
2383
0
    return(true);
2384
0
  }
2385
2386
  bool OBMol::CorrectForPH(double pH)
2387
0
  {
2388
0
    if (IsCorrectedForPH())
2389
0
      return(true);
2390
0
    phmodel.CorrectForPH(*this, pH);
2391
2392
0
    obErrorLog.ThrowError(__FUNCTION__,
2393
0
                          "Ran OpenBabel::CorrectForPH", obAuditMsg);
2394
2395
0
    return(true);
2396
0
  }
2397
2398
  //! \brief set spin multiplicity for H-deficient atoms
2399
  /**
2400
     If NoImplicitH is true then the molecule has no implicit hydrogens. Individual atoms
2401
     on which ForceNoH() has been called also have no implicit hydrogens.
2402
     If NoImplicitH is false (the default), then if there are any explicit hydrogens
2403
     on an atom then they constitute all the hydrogen on that atom. However, a hydrogen
2404
     atom with its _isotope!=0 is not considered explicit hydrogen for this purpose.
2405
     In addition, an atom which has had ForceImplH()called for it is never considered
2406
     hydrogen deficient, e.g. unbracketed atoms in SMILES.
2407
     Any discrepancy with the expected atom valency is interpreted as the atom being a
2408
     radical of some sort and iits _spinMultiplicity is set to 2 when it is one hydrogen short
2409
     and 3 when it is two hydrogens short and similarly for greater hydrogen deficiency.
2410
2411
     So SMILES C[CH] is interpreted as methyl carbene, CC[H][H] as ethane, and CC[2H] as CH3CH2D.
2412
  **/
2413
2414
2415
2416
  bool OBMol::AssignSpinMultiplicity(bool /*NoImplicitH*/)
2417
0
  {
2418
    // TODO: The following functions simply returns true, as it has been made
2419
    // redundant by changes to the handling of implicit hydrogens, and spin.
2420
    // This needs to be sorted out properly at some point.
2421
0
    return true;
2422
0
  }
2423
2424
  // Used by DeleteAtom below. Code based on StereoRefToImplicit
2425
  static void DeleteStereoOnAtom(OBMol& mol, OBStereo::Ref atomId)
2426
0
  {
2427
0
    std::vector<OBGenericData*> vdata = mol.GetAllData(OBGenericDataType::StereoData);
2428
0
    for (std::vector<OBGenericData*>::iterator data = vdata.begin(); data != vdata.end(); ++data) {
2429
0
      OBStereo::Type datatype = ((OBStereoBase*)*data)->GetType();
2430
2431
0
      if (datatype != OBStereo::CisTrans && datatype != OBStereo::Tetrahedral) {
2432
0
        obErrorLog.ThrowError(__FUNCTION__,
2433
0
            "This function should be updated to handle additional stereo types.\nSome stereochemistry objects may contain explicit refs to hydrogens which have been removed.", obWarning);
2434
0
        continue;
2435
0
      }
2436
2437
0
      if (datatype == OBStereo::CisTrans) {
2438
0
        OBCisTransStereo *ct = dynamic_cast<OBCisTransStereo*>(*data);
2439
0
        OBCisTransStereo::Config ct_cfg = ct->GetConfig();
2440
0
        if (ct_cfg.begin == atomId || ct_cfg.end == atomId ||
2441
0
            std::find(ct_cfg.refs.begin(), ct_cfg.refs.end(), atomId) != ct_cfg.refs.end())
2442
0
          mol.DeleteData(ct);
2443
0
      }
2444
0
      else if (datatype == OBStereo::Tetrahedral) {
2445
0
        OBTetrahedralStereo *ts = dynamic_cast<OBTetrahedralStereo*>(*data);
2446
0
        OBTetrahedralStereo::Config ts_cfg = ts->GetConfig();
2447
0
        if (ts_cfg.from == atomId ||
2448
0
            std::find(ts_cfg.refs.begin(), ts_cfg.refs.end(), atomId) != ts_cfg.refs.end())
2449
0
          mol.DeleteData(ts);
2450
0
      }
2451
0
    }
2452
0
  }
2453
2454
  bool OBMol::DeleteAtom(OBAtom *atom, bool destroyAtom)
2455
0
  {
2456
0
    if (atom->GetAtomicNum() == OBElements::Hydrogen)
2457
0
      return(DeleteHydrogen(atom));
2458
2459
    // OBAngleData/OBTorsionData cache raw OBAtom* pointers; drop them now so
2460
    // a later FOR_ANGLES_OF_MOL doesn't read freed memory.
2461
0
    DeleteData(OBGenericDataType::AngleData);
2462
0
    DeleteData(OBGenericDataType::TorsionData);
2463
2464
0
    BeginModify();
2465
    //don't need to do anything with coordinates b/c
2466
    //BeginModify() blows away coordinates
2467
2468
    //find bonds to delete
2469
0
    OBAtom *nbr;
2470
0
    vector<OBBond*> vdb;
2471
0
    vector<OBBond*>::iterator j;
2472
0
    for (nbr = atom->BeginNbrAtom(j);nbr;nbr = atom->NextNbrAtom(j))
2473
0
      vdb.push_back(*j);
2474
2475
0
    for (j = vdb.begin();j != vdb.end();++j)
2476
0
      DeleteBond((OBBond *)*j); //delete bonds
2477
2478
0
    _atomIds[atom->GetId()] = nullptr;
2479
0
    _vatom.erase(_vatom.begin()+(atom->GetIdx()-1));
2480
0
    _natoms--;
2481
2482
    //reset all the indices to the atoms
2483
0
    int idx;
2484
0
    vector<OBAtom*>::iterator i;
2485
0
    OBAtom *atomi;
2486
0
    for (idx=1,atomi = BeginAtom(i);atomi;atomi = NextAtom(i),++idx)
2487
0
      atomi->SetIdx(idx);
2488
2489
0
    EndModify();
2490
2491
    // Delete any stereo objects involving this atom
2492
0
    OBStereo::Ref id = atom->GetId();
2493
0
    DeleteStereoOnAtom(*this, id);
2494
2495
0
    if (destroyAtom)
2496
0
      DestroyAtom(atom);
2497
2498
0
    SetSSSRPerceived(false);
2499
0
    SetLSSRPerceived(false);
2500
0
    return(true);
2501
0
  }
2502
2503
  bool OBMol::DeleteResidue(OBResidue *residue, bool destroyResidue)
2504
0
  {
2505
0
    unsigned short idx = residue->GetIdx();
2506
0
    _residue.erase(_residue.begin() + idx);
2507
2508
0
    for ( unsigned short i = idx ; i < _residue.size() ; i++ )
2509
0
      _residue[i]->SetIdx(i);
2510
2511
0
    if (destroyResidue)
2512
0
      DestroyResidue(residue);
2513
2514
0
    SetSSSRPerceived(false);
2515
0
    SetLSSRPerceived(false);
2516
0
    return(true);
2517
0
  }
2518
2519
  bool OBMol::DeleteBond(OBBond *bond, bool destroyBond)
2520
0
  {
2521
    // Cached angles/torsions are derived from bond connectivity. They aren't
2522
    // a UAF risk here, but they no longer match the topology — invalidate.
2523
0
    DeleteData(OBGenericDataType::AngleData);
2524
0
    DeleteData(OBGenericDataType::TorsionData);
2525
2526
0
    BeginModify();
2527
2528
0
    (bond->GetBeginAtom())->DeleteBond(bond);
2529
0
    (bond->GetEndAtom())->DeleteBond(bond);
2530
0
    _bondIds[bond->GetId()] = nullptr;
2531
0
    _vbond.erase(_vbond.begin() + bond->GetIdx()); // bond index starts at 0!!!
2532
0
    _nbonds--;
2533
2534
0
    vector<OBBond*>::iterator i;
2535
0
    int j;
2536
0
    OBBond *bondi;
2537
0
    for (bondi = BeginBond(i),j=0;bondi;bondi = NextBond(i),++j)
2538
0
      bondi->SetIdx(j);
2539
2540
0
    EndModify();
2541
2542
0
    if (destroyBond)
2543
0
      DestroyBond(bond);
2544
2545
0
    SetSSSRPerceived(false);
2546
0
    SetLSSRPerceived(false);
2547
0
    return(true);
2548
0
  }
2549
2550
  bool OBMol::AddBond(int first,int second,int order,int flags,int insertpos)
2551
17.7k
  {
2552
    // Don't add the bond if it already exists
2553
17.7k
    if (first == second || GetBond(first, second) != nullptr)
2554
1.12k
      return(false);
2555
2556
    //    BeginModify();
2557
2558
16.6k
    if ((unsigned)first <= NumAtoms() && (unsigned)second <= NumAtoms())
2559
      //atoms exist and bond doesn't
2560
16.6k
      {
2561
16.6k
        OBBond *bond = new OBBond;
2562
16.6k
        if (!bond)
2563
0
          {
2564
            //EndModify();
2565
0
            return(false);
2566
0
          }
2567
2568
16.6k
        OBAtom *bgn,*end;
2569
16.6k
        bgn = GetAtom(first);
2570
16.6k
        end = GetAtom(second);
2571
16.6k
        if (!bgn || !end)
2572
4
          {
2573
4
            obErrorLog.ThrowError(__FUNCTION__, "Unable to add bond - invalid atom index", obDebug);
2574
4
            delete bond;
2575
4
            return(false);
2576
4
          }
2577
16.6k
        bond->Set(_nbonds,bgn,end,order,flags);
2578
16.6k
        bond->SetParent(this);
2579
2580
16.6k
        bond->SetId(_bondIds.size());
2581
16.6k
        _bondIds.push_back(bond);
2582
2583
16.6k
#define OBBondIncrement 100
2584
16.6k
        if (_nbonds+1 >= _vbond.size())
2585
4.32k
          {
2586
4.32k
            _vbond.resize(_nbonds+OBBondIncrement);
2587
4.32k
            vector<OBBond*>::iterator i;
2588
432k
            for (i = _vbond.begin(),i+=(_nbonds+1);i != _vbond.end();++i)
2589
427k
              *i = nullptr;
2590
4.32k
          }
2591
16.6k
#undef  OBBondIncrement
2592
2593
16.6k
        _vbond[_nbonds] = (OBBond*)bond;
2594
16.6k
        _nbonds++;
2595
2596
16.6k
        if (insertpos == -1)
2597
16.6k
          {
2598
16.6k
            bgn->AddBond(bond);
2599
16.6k
            end->AddBond(bond);
2600
16.6k
          }
2601
0
        else
2602
0
          {
2603
0
            if (insertpos >= static_cast<int>(bgn->GetExplicitDegree()))
2604
0
              bgn->AddBond(bond);
2605
0
            else //need to insert the bond for the connectivity order to be preserved
2606
0
              {    //otherwise stereochemistry gets screwed up
2607
0
                vector<OBBond*>::iterator bi;
2608
0
                bgn->BeginNbrAtom(bi);
2609
0
                bi += insertpos;
2610
0
                bgn->InsertBond(bi,bond);
2611
0
              }
2612
0
            end->AddBond(bond);
2613
0
          }
2614
16.6k
      }
2615
0
    else //at least one atom doesn't exist yet - add to bond_q
2616
0
      SetData(new OBVirtualBond(first,second,order,flags));
2617
2618
    //    EndModify();
2619
2620
16.6k
    return(true);
2621
16.6k
  }
2622
2623
  bool OBMol::AddBond(OBBond &bond)
2624
0
  {
2625
0
    if(!AddBond(bond.GetBeginAtomIdx(),
2626
0
                   bond.GetEndAtomIdx(),
2627
0
                   bond.GetBondOrder(),
2628
0
                   bond.GetFlags()))
2629
0
      return false;
2630
    //copy the bond's generic data
2631
0
    OBDataIterator diter;
2632
0
    for(diter=bond.BeginData(); diter!=bond.EndData();++diter)
2633
0
      GetBond(NumBonds()-1)->CloneData(*diter);
2634
0
    return true;
2635
0
  }
2636
2637
  void OBMol::Align(OBAtom *a1,OBAtom *a2,vector3 &p1,vector3 &p2)
2638
0
  {
2639
0
    vector<int> children;
2640
2641
0
    obErrorLog.ThrowError(__FUNCTION__,
2642
0
                          "Ran OpenBabel::Align", obAuditMsg);
2643
2644
    //find which atoms to rotate
2645
0
    FindChildren(children,a1->GetIdx(),a2->GetIdx());
2646
0
    children.push_back(a2->GetIdx());
2647
2648
    //find the rotation vector and angle
2649
0
    vector3 v1,v2,v3;
2650
0
    v1 = p2 - p1;
2651
0
    v2 = a2->GetVector() - a1->GetVector();
2652
0
    v3 = cross(v1,v2);
2653
0
    double angle = vectorAngle(v1,v2);
2654
2655
    //find the rotation matrix
2656
0
    matrix3x3 m;
2657
0
    m.RotAboutAxisByAngle(v3,angle);
2658
2659
    //rotate atoms
2660
0
    vector3 v;
2661
0
    OBAtom *atom;
2662
0
    vector<int>::iterator i;
2663
0
    for (i = children.begin();i != children.end();++i)
2664
0
      {
2665
0
        atom = GetAtom(*i);
2666
0
        v = atom->GetVector();
2667
0
        v -= a1->GetVector();
2668
0
        v *= m;   //rotate the point
2669
0
        v += p1;  //translate the vector
2670
0
        atom->SetVector(v);
2671
0
      }
2672
    //set a1 = p1
2673
0
    a1->SetVector(p1);
2674
0
  }
2675
2676
  void OBMol::ToInertialFrame()
2677
0
  {
2678
0
    double m[9];
2679
0
    for (int i = 0;i < NumConformers();++i)
2680
0
      ToInertialFrame(i,m);
2681
0
  }
2682
2683
  void OBMol::ToInertialFrame(int conf,double *rmat)
2684
0
  {
2685
0
    unsigned int i;
2686
0
    double x,y,z;
2687
0
    double mi;
2688
0
    double mass = 0.0;
2689
0
    double center[3],m[3][3];
2690
2691
0
    obErrorLog.ThrowError(__FUNCTION__,
2692
0
                          "Ran OpenBabel::ToInertialFrame", obAuditMsg);
2693
2694
0
    for (i = 0;i < 3;++i)
2695
0
      memset(&m[i],'\0',sizeof(double)*3);
2696
0
    memset(center,'\0',sizeof(double)*3);
2697
2698
0
    SetConformer(conf);
2699
0
    OBAtom *atom;
2700
0
    vector<OBAtom*>::iterator j;
2701
    //find center of mass
2702
0
    for (atom = BeginAtom(j);atom;atom = NextAtom(j))
2703
0
      {
2704
0
        mi = atom->GetAtomicMass();
2705
0
        center[0] += mi*atom->x();
2706
0
        center[1] += mi*atom->y();
2707
0
        center[2] += mi*atom->z();
2708
0
        mass += mi;
2709
0
      }
2710
2711
0
    center[0] /= mass;
2712
0
    center[1] /= mass;
2713
0
    center[2] /= mass;
2714
2715
    //calculate inertial tensor
2716
0
    for (atom = BeginAtom(j);atom;atom = NextAtom(j))
2717
0
      {
2718
0
        x = atom->x()-center[0];
2719
0
        y = atom->y()-center[1];
2720
0
        z = atom->z()-center[2];
2721
0
        mi = atom->GetAtomicMass();
2722
2723
0
        m[0][0] += mi*(y*y+z*z);
2724
0
        m[0][1] -= mi*x*y;
2725
0
        m[0][2] -= mi*x*z;
2726
        //        m[1][0] -= mi*x*y;
2727
0
        m[1][1] += mi*(x*x+z*z);
2728
0
        m[1][2] -= mi*y*z;
2729
        //        m[2][0] -= mi*x*z;
2730
        //        m[2][1] -= mi*y*z;
2731
0
        m[2][2] += mi*(x*x+y*y);
2732
0
      }
2733
    // Fill in the lower triangle using symmetry across the diagonal
2734
0
    m[1][0] = m[0][1];
2735
0
    m[2][0] = m[0][2];
2736
0
    m[2][1] = m[1][2];
2737
2738
    /* find rotation matrix for moment of inertia */
2739
0
    ob_make_rmat(m,rmat);
2740
2741
    /* rotate all coordinates */
2742
0
    double *c = GetConformer(conf);
2743
0
    for(i=0; i < NumAtoms();++i)
2744
0
      {
2745
0
        x = c[i*3]-center[0];
2746
0
        y = c[i*3+1]-center[1];
2747
0
        z = c[i*3+2]-center[2];
2748
0
        c[i*3]   = x*rmat[0] + y*rmat[1] + z*rmat[2];
2749
0
        c[i*3+1] = x*rmat[3] + y*rmat[4] + z*rmat[5];
2750
0
        c[i*3+2] = x*rmat[6] + y*rmat[7] + z*rmat[8];
2751
0
      }
2752
0
  }
2753
2754
  OBMol::OBMol()
2755
17.5k
  {
2756
17.5k
    _natoms = _nbonds = 0;
2757
17.5k
    _mod = 0;
2758
17.5k
    _totalCharge = 0;
2759
17.5k
    _dimension = 3;
2760
17.5k
    _vatom.clear();
2761
17.5k
    _atomIds.clear();
2762
17.5k
    _vbond.clear();
2763
17.5k
    _bondIds.clear();
2764
17.5k
    _vdata.clear();
2765
17.5k
    _title = "";
2766
17.5k
    _c = nullptr;
2767
17.5k
    _flags = 0;
2768
17.5k
    _vconf.clear();
2769
17.5k
    _autoPartialCharge = true;
2770
17.5k
    _autoFormalCharge = true;
2771
17.5k
    _energy = 0.0;
2772
17.5k
  }
2773
2774
0
  OBMol::OBMol(const OBMol &mol) : OBBase(mol)
2775
0
  {
2776
0
    _natoms = _nbonds = 0;
2777
0
    _mod = 0;
2778
0
    _totalCharge = 0;
2779
0
    _dimension = 3;
2780
0
    _vatom.clear();
2781
0
    _atomIds.clear();
2782
0
    _vbond.clear();
2783
0
    _bondIds.clear();
2784
0
    _vdata.clear();
2785
0
    _title = "";
2786
0
    _c = nullptr;
2787
0
    _flags = 0;
2788
0
    _vconf.clear();
2789
0
    _autoPartialCharge = true;
2790
0
    _autoFormalCharge = true;
2791
    //NF  _compressed = false;
2792
0
    _energy = 0.0;
2793
0
    *this = mol;
2794
0
  }
2795
2796
  OBMol::~OBMol()
2797
17.5k
  {
2798
17.5k
    OBAtom    *atom;
2799
17.5k
    OBBond    *bond;
2800
17.5k
    OBResidue *residue;
2801
17.5k
    vector<OBAtom*>::iterator i;
2802
17.5k
    vector<OBBond*>::iterator j;
2803
17.5k
    vector<OBResidue*>::iterator r;
2804
    // Destroy residues before atoms so ~OBResidue() can clear back-
2805
    // pointers on still-live atoms (see Clear() for the same reason).
2806
17.5k
    for (residue = BeginResidue(r);residue;residue = NextResidue(r))
2807
0
      DestroyResidue(residue);
2808
1.68M
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2809
1.66M
      DestroyAtom(atom);
2810
34.1k
    for (bond = BeginBond(j);bond;bond = NextBond(j))
2811
16.6k
      DestroyBond(bond);
2812
2813
    //clear out the multiconformer data
2814
17.5k
    vector<double*>::iterator k;
2815
30.0k
    for (k = _vconf.begin();k != _vconf.end();++k)
2816
12.4k
      delete [] *k;
2817
17.5k
    _vconf.clear();
2818
17.5k
  }
2819
2820
  bool OBMol::HasNonZeroCoords()
2821
0
  {
2822
0
    OBAtom *atom;
2823
0
    vector<OBAtom*>::iterator i;
2824
2825
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2826
0
      if (atom->GetVector().length_2() != 0.0)
2827
0
        return(true);
2828
2829
0
    return(false);
2830
0
  }
2831
2832
  bool OBMol::Has2D(bool Not3D)
2833
14.2k
  {
2834
14.2k
    bool hasX,hasY;
2835
14.2k
    OBAtom *atom;
2836
14.2k
    vector<OBAtom*>::iterator i;
2837
2838
14.2k
    hasX = hasY = false;
2839
1.60M
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2840
1.59M
      {
2841
1.59M
        if (!hasX && fabs(atom->x()) >= 2e-6)
2842
1.86k
          hasX = true;
2843
1.59M
        if (!hasY && fabs(atom->y()) >= 2e-6)
2844
1.59k
          hasY = true;
2845
1.59M
        if(Not3D && atom->z())
2846
0
          return false;
2847
1.59M
      }
2848
14.2k
    if (hasX || hasY) //was && but this excluded vertically or horizontally aligned linear mols
2849
3.06k
      return(true);
2850
11.2k
    return(false);
2851
14.2k
  }
2852
2853
  bool OBMol::Has3D()
2854
14.8k
  {
2855
14.8k
    bool hasX,hasY,hasZ;
2856
14.8k
    OBAtom *atom;
2857
14.8k
    vector<OBAtom*>::iterator i;
2858
2859
14.8k
    hasX = hasY = hasZ = false;
2860
    //    if (this->_c == NULL) **Test removed** Prevented function use during molecule construction
2861
    //      return(false);
2862
1.61M
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2863
1.60M
      {
2864
1.60M
        if (!hasX && fabs(atom->x()) >= 2e-6)
2865
2.46k
          hasX = true;
2866
1.60M
        if (!hasY && fabs(atom->y()) >= 2e-6)
2867
2.20k
          hasY = true;
2868
1.60M
        if (!hasZ && fabs(atom->z()) >= 2e-6)
2869
2.17k
          hasZ = true;
2870
2871
1.60M
        if (hasX && hasY && hasZ)
2872
605
          return(true);
2873
1.60M
      }
2874
14.2k
    return(false);
2875
14.8k
  }
2876
2877
  void OBMol::SetCoordinates(double *newCoords)
2878
0
  {
2879
0
    bool noCptr = (_c == nullptr); // did we previously have a coordinate ptr
2880
0
    if (noCptr) {
2881
0
      _c = new double [NumAtoms()*3];
2882
0
    }
2883
2884
    // copy from external to internal
2885
0
    memcpy((char*)_c, (char*)newCoords, sizeof(double)*3*NumAtoms());
2886
2887
0
    if (noCptr) {
2888
0
      OBAtom *atom;
2889
0
      vector<OBAtom*>::iterator i;
2890
0
      for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2891
0
        atom->SetCoordPtr(&_c);
2892
0
      _vconf.push_back(newCoords);
2893
0
    }
2894
0
  }
2895
2896
  //! Renumber the atoms according to the order of indexes in the supplied vector
2897
  //! This with assemble an atom vector and call RenumberAtoms(vector<OBAtom*>)
2898
  //! It will return without action if the supplied vector is empty or does not
2899
  //! have the same number of atoms as the molecule.
2900
  //!
2901
  //! \since version 2.3
2902
  void OBMol::RenumberAtoms(vector<int> v)
2903
0
  {
2904
0
    if (Empty() || v.size() != NumAtoms())
2905
0
      return;
2906
2907
0
    vector <OBAtom*> va;
2908
0
    va.reserve(NumAtoms());
2909
2910
0
    vector<int>::iterator i;
2911
0
    for (i = v.begin(); i != v.end(); ++i)
2912
0
      va.push_back( GetAtom(*i) );
2913
2914
0
    this->RenumberAtoms(va);
2915
0
  }
2916
2917
  //! Renumber the atoms in this molecule according to the order in the supplied
2918
  //! vector. This will return without action if the supplied vector is empty or
2919
  //! does not have the same number of atoms as the molecule.
2920
  void OBMol::RenumberAtoms(vector<OBAtom*> &v)
2921
0
  {
2922
0
    if (Empty())
2923
0
      return;
2924
2925
0
    obErrorLog.ThrowError(__FUNCTION__,
2926
0
                          "Ran OpenBabel::RenumberAtoms", obAuditMsg);
2927
2928
0
    OBAtom *atom;
2929
0
    vector<OBAtom*> va;
2930
0
    vector<OBAtom*>::iterator i;
2931
2932
0
    va = v;
2933
2934
    //make sure all atoms are represented in the vector
2935
0
    if (va.empty() || va.size() != NumAtoms())
2936
0
      return;
2937
2938
0
    OBBitVec bv;
2939
0
    for (i = va.begin();i != va.end();++i)
2940
0
      bv |= (*i)->GetIdx();
2941
2942
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2943
0
      if (!bv[atom->GetIdx()])
2944
0
        va.push_back(atom);
2945
2946
0
    int j,k;
2947
0
    double *c;
2948
0
    double *ctmp = new double [NumAtoms()*3];
2949
2950
0
    for (j = 0;j < NumConformers();++j)
2951
0
      {
2952
0
        c = GetConformer(j);
2953
0
        for (k=0,i = va.begin();i != va.end(); ++i,++k)
2954
0
          memcpy((char*)&ctmp[k*3],(char*)&c[((OBAtom*)*i)->GetCoordinateIdx()],sizeof(double)*3);
2955
0
        memcpy((char*)c,(char*)ctmp,sizeof(double)*3*NumAtoms());
2956
0
      }
2957
2958
0
    for (k=1,i = va.begin();i != va.end(); ++i,++k)
2959
0
      (*i)->SetIdx(k);
2960
2961
0
    delete [] ctmp;
2962
2963
0
    _vatom.clear();
2964
0
    for (i = va.begin();i != va.end();++i)
2965
0
      _vatom.push_back(*i);
2966
2967
0
    DeleteData(OBGenericDataType::RingData);
2968
0
    DeleteData("OpenBabel Symmetry Classes");
2969
0
    DeleteData("LSSR");
2970
0
    DeleteData("SSSR");
2971
0
    UnsetFlag(OB_LSSR_MOL);
2972
0
    UnsetFlag(OB_SSSR_MOL);
2973
0
  }
2974
2975
  bool WriteTitles(ostream &ofs, OBMol &mol)
2976
0
  {
2977
0
    ofs << mol.GetTitle() << endl;
2978
0
    return true;
2979
0
  }
2980
2981
  //check that unreasonable bonds aren't being added
2982
  static bool validAdditionalBond(OBAtom *a, OBAtom *n)
2983
0
  {
2984
0
    if(a->GetExplicitValence() == 5 && a->GetAtomicNum() == 15)
2985
0
    {
2986
      //only allow octhedral bonding for F and Cl
2987
0
      if(n->GetAtomicNum() == 9 || n->GetAtomicNum() == 17)
2988
0
        return true;
2989
0
      else
2990
0
        return false;
2991
0
    }
2992
    //other things to check?
2993
0
    return true;
2994
0
  }
2995
2996
  /*! This method adds single bonds between all atoms
2997
    closer than their combined atomic covalent radii,
2998
    then "cleans up" making sure bonded atoms are not
2999
    closer than 0.4A and the atom does not exceed its valence.
3000
    It implements blue-obelisk:rebondFrom3DCoordinates.
3001
3002
  */
3003
  void OBMol::ConnectTheDots(void)
3004
0
  {
3005
0
    if (Empty())
3006
0
      return;
3007
0
    if (_dimension != 3) return; // not useful on non-3D structures
3008
3009
0
    if (IsPeriodic())
3010
0
      obErrorLog.ThrowError(__FUNCTION__,
3011
0
                            "Ran OpenBabel::ConnectTheDots -- using periodic boundary conditions",
3012
0
                            obAuditMsg);
3013
0
    else
3014
0
      obErrorLog.ThrowError(__FUNCTION__,
3015
0
                            "Ran OpenBabel::ConnectTheDots", obAuditMsg);
3016
3017
3018
0
    int j,k,max;
3019
0
    double maxrad = 0;
3020
0
    bool unset = false;
3021
0
    OBAtom *atom,*nbr;
3022
0
    vector<OBAtom*>::iterator i;
3023
0
    vector<pair<OBAtom*,double> > zsortedAtoms;
3024
0
    vector<double> rad;
3025
0
    vector<int> zsorted;
3026
0
    vector<int> bondCount; // existing bonds (e.g., from residues in PDB)
3027
3028
0
    double *c = new double [NumAtoms()*3];
3029
0
    rad.resize(_natoms);
3030
3031
0
    for (j = 0, atom = BeginAtom(i) ; atom ; atom = NextAtom(i), ++j)
3032
0
      {
3033
0
        bondCount.push_back(atom->GetExplicitDegree());
3034
        //don't consider atoms with a full valance already
3035
        //this is both for correctness (trust existing bonds) and performance
3036
0
        if(atom->GetExplicitValence() >= OBElements::GetMaxBonds(atom->GetAtomicNum()))
3037
0
          continue;
3038
0
        if(atom->GetAtomicNum() == 7 && atom->GetFormalCharge() == 0 && atom->GetExplicitValence() >= 3)
3039
0
          continue;
3040
0
        (atom->GetVector()).Get(&c[j*3]);
3041
0
        pair<OBAtom*,double> entry(atom, atom->GetVector().z());
3042
0
        zsortedAtoms.push_back(entry);
3043
0
      }
3044
0
    sort(zsortedAtoms.begin(), zsortedAtoms.end(), SortAtomZ);
3045
3046
0
    max = zsortedAtoms.size();
3047
3048
0
    for ( j = 0 ; j < max ; j++ )
3049
0
      {
3050
0
        atom   = zsortedAtoms[j].first;
3051
0
        rad[j] = OBElements::GetCovalentRad(atom->GetAtomicNum());
3052
0
        maxrad = std::max(rad[j],maxrad);
3053
0
        zsorted.push_back(atom->GetIdx()-1);
3054
0
      }
3055
3056
0
    int idx1, idx2;
3057
0
    double d2,cutoff,zd;
3058
0
    vector3 atom1, atom2, wrapped_coords;  // Only used for periodic coords
3059
0
    for (j = 0 ; j < max ; ++j)
3060
0
      {
3061
0
        double maxcutoff = SQUARE(rad[j]+maxrad+0.45);
3062
0
        idx1 = zsorted[j];
3063
0
        for (k = j + 1 ; k < max ; k++ )
3064
0
          {
3065
0
            idx2 = zsorted[k];
3066
3067
            // bonded if closer than elemental Rcov + tolerance
3068
0
            cutoff = SQUARE(rad[j] + rad[k] + 0.45);
3069
3070
            // Use minimum image convention if the unit cell is periodic
3071
            // Otherwise, use a simpler (faster) distance calculation based on raw coordinates
3072
0
            if (IsPeriodic())
3073
0
              {
3074
0
                atom1 = vector3(c[idx1*3], c[idx1*3+1], c[idx1*3+2]);
3075
0
                atom2 = vector3(c[idx2*3], c[idx2*3+1], c[idx2*3+2]);
3076
0
                OBUnitCell *unitCell = (OBUnitCell * ) GetData(OBGenericDataType::UnitCell);
3077
0
                wrapped_coords = unitCell->MinimumImageCartesian(atom1 - atom2);
3078
0
                d2 = wrapped_coords.length_2();
3079
0
              }
3080
0
            else
3081
0
              {
3082
0
                zd  = SQUARE(c[idx1*3+2] - c[idx2*3+2]);
3083
                // bigger than max cutoff, which is determined using largest radius,
3084
                // not the radius of k (which might be small, ie H, and cause an early  termination)
3085
                // since we sort by z, anything beyond k will also fail
3086
0
                if (zd > maxcutoff )
3087
0
                  break;
3088
3089
0
                d2  = SQUARE(c[idx1*3]   - c[idx2*3]);
3090
0
                if (d2 > cutoff)
3091
0
                  continue; // x's bigger than cutoff
3092
0
                d2 += SQUARE(c[idx1*3+1] - c[idx2*3+1]);
3093
0
                if (d2 > cutoff)
3094
0
                  continue; // x^2 + y^2 bigger than cutoff
3095
0
                d2 += zd;
3096
0
              }
3097
3098
0
            if (d2 > cutoff)
3099
0
              continue;
3100
0
            if (d2 < 0.16) // 0.4 * 0.4 = 0.16
3101
0
              continue;
3102
3103
0
            atom = GetAtom(idx1+1);
3104
0
            nbr  = GetAtom(idx2+1);
3105
3106
0
            if (atom->IsConnected(nbr))
3107
0
              continue;
3108
3109
0
            if (!validAdditionalBond(atom,nbr) || !validAdditionalBond(nbr, atom))
3110
0
              continue;
3111
3112
0
            AddBond(idx1+1,idx2+1,1);
3113
0
          }
3114
0
      }
3115
3116
    // If between BeginModify and EndModify, coord pointers are NULL
3117
    // setup molecule to handle current coordinates
3118
3119
0
    if (_c == nullptr)
3120
0
      {
3121
0
        _c = c;
3122
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3123
0
          atom->SetCoordPtr(&_c);
3124
0
        _vconf.push_back(c);
3125
0
        unset = true;
3126
0
      }
3127
3128
    // Cleanup -- delete long bonds that exceed max valence
3129
0
    OBBond *maxbond, *bond;
3130
0
    double maxlength;
3131
0
    vector<OBBond*>::iterator l, m;
3132
0
    int valCount;
3133
0
    bool changed;
3134
0
    BeginModify(); //prevent needless re-perception in DeleteBond
3135
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3136
0
      {
3137
0
        while (atom->GetExplicitValence() > static_cast<unsigned int>(OBElements::GetMaxBonds(atom->GetAtomicNum()))
3138
0
               || atom->SmallestBondAngle() < 45.0)
3139
0
          {
3140
0
            bond = atom->BeginBond(l);
3141
0
            maxbond = bond;
3142
            // Fix from Liu Zhiguo 2008-01-26
3143
            // loop past any bonds
3144
            // which existed before ConnectTheDots was called
3145
            // (e.g., from PDB resdata.txt)
3146
0
            valCount = 0;
3147
0
            while (valCount < bondCount[atom->GetIdx() - 1]) {
3148
0
              bond = atom->NextBond(l);
3149
              // timvdm: 2008-03-05
3150
              // NextBond only returns NULL if the iterator l == _bonds.end().
3151
              // This was casuing problems as follows:
3152
              // NextBond = 0x????????
3153
              // NextBond = 0x????????
3154
              // NextBond = 0x????????
3155
              // NextBond = 0x????????
3156
              // NextBond = NULL  <-- this NULL was not detected
3157
              // NextBond = 0x????????
3158
0
              if (!bond) // so we add an additional check
3159
0
                break;
3160
0
              maxbond = bond;
3161
0
              valCount++;
3162
0
            }
3163
0
            if (!bond) // no new bonds added for this atom, just skip it
3164
0
              break;
3165
3166
            // delete bonds between hydrogens when over max valence
3167
0
            if (atom->GetAtomicNum() == OBElements::Hydrogen)
3168
0
              {
3169
0
                m = l;
3170
0
                changed = false;
3171
0
                for (;bond;bond = atom->NextBond(m))
3172
0
                  {
3173
0
                    if (bond->GetNbrAtom(atom)->GetAtomicNum() == OBElements::Hydrogen)
3174
0
                      {
3175
0
                        DeleteBond(bond);
3176
0
                        changed = true;
3177
0
                        break;
3178
0
                      }
3179
0
                  }
3180
0
                if (changed)
3181
0
                  {
3182
                    // bond deleted, reevaluate BOSum
3183
0
                    continue;
3184
0
                  }
3185
0
                else
3186
0
                  {
3187
                    // reset to first new bond
3188
0
                    bond = maxbond;
3189
0
                  }
3190
0
              }
3191
3192
0
            maxlength = maxbond->GetLength();
3193
0
            for (bond = atom->NextBond(l);bond;bond = atom->NextBond(l))
3194
0
              {
3195
0
                if (bond->GetLength() > maxlength)
3196
0
                  {
3197
0
                    maxbond = bond;
3198
0
                    maxlength = bond->GetLength();
3199
0
                  }
3200
0
              }
3201
0
            DeleteBond(maxbond); // delete the new bond with the longest length
3202
0
          }
3203
0
      }
3204
0
    EndModify();
3205
0
    if (unset)
3206
0
      {
3207
0
        if (_c != nullptr){
3208
0
          delete [] _c;
3209
3210
          // Note that the above delete doesn't set _c value to nullptr
3211
0
          _c = nullptr;
3212
0
        }
3213
3214
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3215
0
          atom->ClearCoordPtr();
3216
0
  if (_vconf.size() > 0)
3217
0
    _vconf.resize(_vconf.size()-1);
3218
0
      }
3219
3220
0
    if (_c != nullptr)
3221
0
      delete [] c;
3222
0
  }
3223
3224
  /*! This method uses bond angles and geometries from current
3225
    connectivity to guess atom types and then filling empty valences
3226
    with multiple bonds. It currently has a pass to detect some
3227
    frequent functional groups. It still needs a pass to detect aromatic
3228
    rings to "clean up."
3229
    AssignSpinMultiplicity(true) is called at the end of the function. The true
3230
    states that there are no implict hydrogens in the molecule.
3231
  */
3232
  void OBMol::PerceiveBondOrders()
3233
0
  {
3234
0
    if (Empty())
3235
0
      return;
3236
0
    if (_dimension != 3) return; // not useful on non-3D structures
3237
3238
0
    obErrorLog.ThrowError(__FUNCTION__,
3239
0
                          "Ran OpenBabel::PerceiveBondOrders", obAuditMsg);
3240
3241
0
    OBAtom *atom, *b, *c;
3242
0
    vector3 v1, v2;
3243
0
    double angle;//, dist1, dist2;
3244
0
    vector<OBAtom*>::iterator i;
3245
0
    vector<OBBond*>::iterator j;//,k;
3246
3247
    //  BeginModify();
3248
3249
    // Pass 1: Assign estimated hybridization based on avg. angles
3250
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3251
0
      {
3252
0
        angle = atom->AverageBondAngle();
3253
3254
        //        cout << atom->GetAtomicNum() << " " << angle << endl;
3255
3256
0
        if (angle > 155.0)
3257
0
          atom->SetHyb(1);
3258
0
        else if (angle <= 155.0 && angle > 115.0)
3259
0
          atom->SetHyb(2);
3260
3261
        // special case for imines
3262
0
        if (atom->GetAtomicNum() == OBElements::Nitrogen
3263
0
            && atom->ExplicitHydrogenCount() == 1
3264
0
            && atom->GetExplicitDegree() == 2
3265
0
            && angle > 109.5)
3266
0
          atom->SetHyb(2);
3267
0
        else if(atom->GetAtomicNum() == OBElements::Nitrogen
3268
0
            && atom->GetExplicitDegree() == 2
3269
0
            && atom->IsInRing()) //azete
3270
0
          atom->SetHyb(2);
3271
0
      } // pass 1
3272
3273
    // Make sure upcoming calls to GetHyb() don't kill these temporary values
3274
0
    SetHybridizationPerceived();
3275
3276
    // Pass 2: look for 5-member rings with torsions <= 7.5 degrees
3277
    //         and 6-member rings with torsions <= 12 degrees
3278
    //         (set all atoms with at least two bonds to sp2)
3279
3280
0
    vector<OBRing*> rlist;
3281
0
    vector<OBRing*>::iterator ringit;
3282
0
    vector<int> path;
3283
0
    double torsions = 0.0;
3284
3285
0
    if (!HasSSSRPerceived())
3286
0
      FindSSSR();
3287
0
    rlist = GetSSSR();
3288
0
    for (ringit = rlist.begin(); ringit != rlist.end(); ++ringit)
3289
0
      {
3290
0
        if ((*ringit)->Size() == 5)
3291
0
          {
3292
0
            path = (*ringit)->_path;
3293
0
            torsions =
3294
0
              ( fabs(GetTorsion(path[0], path[1], path[2], path[3])) +
3295
0
                fabs(GetTorsion(path[1], path[2], path[3], path[4])) +
3296
0
                fabs(GetTorsion(path[2], path[3], path[4], path[0])) +
3297
0
                fabs(GetTorsion(path[3], path[4], path[0], path[1])) +
3298
0
                fabs(GetTorsion(path[4], path[0], path[1], path[2])) ) / 5.0;
3299
0
            if (torsions <= 7.5)
3300
0
              {
3301
0
                for (unsigned int ringAtom = 0; ringAtom != path.size(); ++ringAtom)
3302
0
                  {
3303
0
                    b = GetAtom(path[ringAtom]);
3304
                    // if an aromatic ring atom has valence 3, it is already set
3305
                    // to sp2 because the average angles should be 120 anyway
3306
                    // so only look for valence 2
3307
0
                    if (b->GetExplicitDegree() == 2)
3308
0
                      b->SetHyb(2);
3309
0
                  }
3310
0
              }
3311
0
          }
3312
0
        else if ((*ringit)->Size() == 6)
3313
0
          {
3314
0
            path = (*ringit)->_path;
3315
0
            torsions =
3316
0
              ( fabs(GetTorsion(path[0], path[1], path[2], path[3])) +
3317
0
                fabs(GetTorsion(path[1], path[2], path[3], path[4])) +
3318
0
                fabs(GetTorsion(path[2], path[3], path[4], path[5])) +
3319
0
                fabs(GetTorsion(path[3], path[4], path[5], path[0])) +
3320
0
                fabs(GetTorsion(path[4], path[5], path[0], path[1])) +
3321
0
                fabs(GetTorsion(path[5], path[0], path[1], path[2])) ) / 6.0;
3322
0
            if (torsions <= 12.0)
3323
0
              {
3324
0
                for (unsigned int ringAtom = 0; ringAtom != path.size(); ++ringAtom)
3325
0
                  {
3326
0
                    b = GetAtom(path[ringAtom]);
3327
0
                    if (b->GetExplicitDegree() == 2 || b->GetExplicitDegree() == 3)
3328
0
                      b->SetHyb(2);
3329
0
                  }
3330
0
              }
3331
0
          }
3332
0
      }
3333
3334
    // Pass 3: "Antialiasing" If an atom marked as sp hybrid isn't
3335
    //          bonded to another or an sp2 hybrid isn't bonded
3336
    //          to another (or terminal atoms in both cases)
3337
    //          mark them to a lower hybridization for now
3338
0
    bool openNbr;
3339
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3340
0
      {
3341
0
        if (atom->GetHyb() == 2 || atom->GetHyb() == 1)
3342
0
          {
3343
0
            openNbr = false;
3344
0
            for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j))
3345
0
              {
3346
0
                if (b->GetHyb() < 3 || b->GetExplicitDegree() == 1)
3347
0
                  {
3348
0
                    openNbr = true;
3349
0
                    break;
3350
0
                  }
3351
0
              }
3352
0
            if (!openNbr && atom->GetHyb() == 2)
3353
0
              atom->SetHyb(3);
3354
0
            else if (!openNbr && atom->GetHyb() == 1)
3355
0
              atom->SetHyb(2);
3356
0
          }
3357
0
      } // pass 3
3358
3359
    // Pass 4: Check for known functional group patterns and assign bonds
3360
    //         to the canonical form
3361
    //      Currently we have explicit code to do this, but a "bond typer"
3362
    //      is in progress to make it simpler to test and debug.
3363
0
    bondtyper.AssignFunctionalGroupBonds(*this);
3364
3365
    // Pass 5: Check for aromatic rings and assign bonds as appropriate
3366
    // This is just a quick and dirty approximation that marks everything
3367
    //  as potentially aromatic
3368
3369
    // This doesn't work perfectly, but it's pretty decent.
3370
    //  Need to have a list of SMARTS patterns for common rings
3371
    //  which would "break ties" on complicated multi-ring systems
3372
    // (Most of the current problems lie in the interface with the
3373
    //   Kekulize code anyway, not in marking everything as potentially aromatic)
3374
3375
0
    bool needs_kekulization = false; // are there any aromatic bonds?
3376
0
    bool typed; // has this ring been typed?
3377
0
    unsigned int loop, loopSize;
3378
0
    for (ringit = rlist.begin(); ringit != rlist.end(); ++ringit)
3379
0
      {
3380
0
        typed = false;
3381
0
        loopSize = (*ringit)->Size();
3382
0
        if (loopSize == 5 || loopSize == 6 || loopSize == 7)
3383
0
          {
3384
0
            path = (*ringit)->_path;
3385
0
            for(loop = 0; loop < loopSize; ++loop)
3386
0
              {
3387
0
                atom = GetAtom(path[loop]);
3388
0
                if(atom->HasBondOfOrder(2) || atom->HasBondOfOrder(3)
3389
0
                   || atom->GetHyb() != 2)
3390
0
                  {
3391
0
                    typed = true;
3392
0
                    break;
3393
0
                  }
3394
0
              }
3395
3396
0
            if (!typed)
3397
0
              for(loop = 0; loop < loopSize; ++loop)
3398
0
                {
3399
                  //    cout << " set aromatic " << path[loop] << endl;
3400
0
                  (GetBond(path[loop], path[(loop+1) % loopSize]))->SetAromatic();
3401
0
                  needs_kekulization = true;
3402
0
                }
3403
0
          }
3404
0
      }
3405
3406
    // Kekulization is necessary if an aromatic bond is present
3407
0
    if (needs_kekulization) {
3408
0
      this->SetAromaticPerceived();
3409
      // First of all, set the atoms at the ends of the aromatic bonds to also
3410
      // be aromatic. This information is required for OBKekulize.
3411
0
      FOR_BONDS_OF_MOL(bond, this) {
3412
0
        if (bond->IsAromatic()) {
3413
0
          bond->GetBeginAtom()->SetAromatic();
3414
0
          bond->GetEndAtom()->SetAromatic();
3415
0
        }
3416
0
      }
3417
0
      bool ok = OBKekulize(this);
3418
0
      if (!ok) {
3419
0
        stringstream errorMsg;
3420
0
        errorMsg << "Failed to kekulize aromatic bonds in OBMol::PerceiveBondOrders";
3421
0
        std::string title = this->GetTitle();
3422
0
        if (!title.empty())
3423
0
          errorMsg << " (title is " << title << ")";
3424
0
        errorMsg << endl;
3425
0
        obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning);
3426
        // return false; Should we return false for a kekulization failure?
3427
0
      }
3428
0
      this->SetAromaticPerceived(false);
3429
0
    }
3430
3431
    // Quick pass.. eliminate inter-ring sulfur atom multiple bonds
3432
    // dkoes - I have removed this code - if double bonds are set,
3433
    // we should trust them.  See pdb_ligands_sdf/4iph_1fj.sdf for
3434
    // a case where the charge isn't set, but we break the molecule
3435
    // if we remove the double bond.  Also, the previous code was
3436
    // fragile - relying on the total mol charge being set.  If we
3437
    // are going to do anything, we should "perceive" a formal charge
3438
    // in the case of a ring sulfur with a double bond (thiopyrylium)
3439
3440
    // Pass 6: Assign remaining bond types, ordered by atom electronegativity
3441
0
    vector<pair<OBAtom*,double> > sortedAtoms;
3442
0
    vector<double> rad;
3443
0
    vector<int> sorted;
3444
0
    int iter, max;
3445
0
    double maxElNeg, shortestBond, currentElNeg;
3446
0
    double bondLength, testLength;
3447
3448
0
    for (atom = BeginAtom(i) ; atom ; atom = NextAtom(i))
3449
0
      {
3450
        // if atoms have the same electronegativity, make sure those with shorter bonds
3451
        // are handled first (helps with assignment of conjugated single/double bonds)
3452
0
        shortestBond = 1.0e5;
3453
0
        for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j))
3454
0
          {
3455
0
            if (b->GetAtomicNum()!=1) shortestBond =
3456
0
                                        std::min(shortestBond,(atom->GetBond(b))->GetLength());
3457
0
          }
3458
0
        pair<OBAtom*,double> entry(atom,
3459
0
                                   OBElements::GetElectroNeg(atom->GetAtomicNum())*1e6+shortestBond);
3460
3461
0
        sortedAtoms.push_back(entry);
3462
0
      }
3463
0
    sort(sortedAtoms.begin(), sortedAtoms.end(), SortAtomZ);
3464
3465
0
    max = sortedAtoms.size();
3466
0
    for (iter = 0 ; iter < max ; iter++ )
3467
0
      {
3468
0
        atom = sortedAtoms[iter].first;
3469
        // Debugging statement
3470
        //        cout << " atom->Hyb " << atom->GetAtomicNum() << " " << atom->GetIdx() << " " << atom->GetHyb()
3471
        //             << " BO: " << atom->GetExplicitValence() << endl;
3472
3473
        // Possible sp-hybrids
3474
0
        if ( (atom->GetHyb() == 1 || atom->GetExplicitDegree() == 1)
3475
0
             && atom->GetExplicitValence() + 2  <= static_cast<unsigned int>(OBElements::GetMaxBonds(atom->GetAtomicNum()))
3476
0
             )
3477
0
          {
3478
3479
            // loop through the neighbors looking for a hybrid or terminal atom
3480
            // (and pick the one with highest electronegativity first)
3481
            // *or* pick a neighbor that's a terminal atom
3482
0
            if (atom->HasNonSingleBond() ||
3483
0
                (atom->GetAtomicNum() == 7 && atom->GetExplicitValence() + 2 > 3))
3484
0
              continue;
3485
3486
0
            maxElNeg = 0.0;
3487
0
            shortestBond = 5000.0;
3488
0
            c = nullptr;
3489
0
            for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j))
3490
0
              {
3491
0
                currentElNeg = OBElements::GetElectroNeg(b->GetAtomicNum());
3492
0
                if ( (b->GetHyb() == 1 || b->GetExplicitDegree() == 1)
3493
0
                     && b->GetExplicitValence() + 2 <= static_cast<unsigned int>(OBElements::GetMaxBonds(b->GetAtomicNum()))
3494
0
                     && (currentElNeg > maxElNeg ||
3495
0
                         (IsApprox(currentElNeg,maxElNeg, 1.0e-6)
3496
0
                          && (atom->GetBond(b))->GetLength() < shortestBond)) )
3497
0
                  {
3498
0
                    if (b->HasNonSingleBond() ||
3499
0
                        (b->GetAtomicNum() == 7 && b->GetExplicitValence() + 2 > 3))
3500
0
                      continue;
3501
3502
                    // Test terminal bonds against expected triple bond lengths
3503
0
                    bondLength = (atom->GetBond(b))->GetLength();
3504
0
                    if (atom->GetExplicitDegree() == 1 || b->GetExplicitDegree() == 1) {
3505
0
                      testLength = CorrectedBondRad(atom->GetAtomicNum(), atom->GetHyb())
3506
0
                        + CorrectedBondRad(b->GetAtomicNum(), b->GetHyb());
3507
0
                      if (bondLength > 0.9 * testLength)
3508
0
                        continue; // too long, ignore it
3509
0
                    }
3510
3511
0
                    shortestBond = bondLength;
3512
0
                    maxElNeg = OBElements::GetElectroNeg(b->GetAtomicNum());
3513
0
                    c = b; // save this atom for later use
3514
0
                  }
3515
0
              }
3516
0
            if (c)
3517
0
              (atom->GetBond(c))->SetBondOrder(3);
3518
0
          }
3519
        // Possible sp2-hybrid atoms
3520
0
        else if ( (atom->GetHyb() == 2 || atom->GetExplicitDegree() == 1)
3521
0
                  && atom->GetExplicitValence() + 1 <= static_cast<unsigned int>(OBElements::GetMaxBonds(atom->GetAtomicNum())) )
3522
0
          {
3523
            // as above
3524
0
            if (atom->HasNonSingleBond() ||
3525
0
                (atom->GetAtomicNum() == 7 && atom->GetExplicitValence() + 1 > 3))
3526
0
              continue;
3527
3528
            // Don't build multiple bonds to ring sulfurs
3529
            //  except thiopyrylium
3530
0
            if (atom->IsInRing() && atom->GetAtomicNum() == 16) {
3531
0
              if (_totalCharge > 1 && atom->GetFormalCharge() == 0)
3532
0
                atom->SetFormalCharge(+1);
3533
0
              else
3534
0
                continue;
3535
0
            }
3536
3537
0
            maxElNeg = 0.0;
3538
0
            shortestBond = 5000.0;
3539
0
            c = nullptr;
3540
0
            for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j))
3541
0
              {
3542
0
                currentElNeg = OBElements::GetElectroNeg(b->GetAtomicNum());
3543
0
                if ( (b->GetHyb() == 2 || b->GetExplicitDegree() == 1)
3544
0
                     && b->GetExplicitValence() + 1 <= static_cast<unsigned int>(OBElements::GetMaxBonds(b->GetAtomicNum()))
3545
0
                     && (GetBond(atom, b))->IsDoubleBondGeometry()
3546
0
                     && (currentElNeg > maxElNeg || (IsApprox(currentElNeg,maxElNeg, 1.0e-6)) ) )
3547
0
                  {
3548
0
                    if (b->HasNonSingleBond() ||
3549
0
                        (b->GetAtomicNum() == 7 && b->GetExplicitValence() + 1 > 3))
3550
0
                      continue;
3551
3552
0
                    if (b->IsInRing() && b->GetAtomicNum() == 16) {
3553
0
                      if (_totalCharge > 1 && b->GetFormalCharge() == 0)
3554
0
                        b->SetFormalCharge(+1);
3555
0
                      else
3556
0
                        continue;
3557
0
                    }
3558
3559
                    // Test terminal bonds against expected double bond lengths
3560
0
                    bondLength = (atom->GetBond(b))->GetLength();
3561
0
                    if (atom->GetExplicitDegree() == 1 || b->GetExplicitDegree() == 1) {
3562
0
                      testLength = CorrectedBondRad(atom->GetAtomicNum(), atom->GetHyb())
3563
0
                        + CorrectedBondRad(b->GetAtomicNum(), b->GetHyb());
3564
0
                      if (bondLength > 0.93 * testLength)
3565
0
                        continue; // too long, ignore it
3566
0
                    }
3567
3568
                    // OK, see if this is better than the previous choice
3569
                    // If it's much shorter, pick it (e.g., fulvene)
3570
                    // If they're close (0.1A) then prefer the bond in the ring
3571
0
                    double difference = shortestBond - (atom->GetBond(b))->GetLength();
3572
0
                    if ( (difference > 0.1)
3573
0
                         || ( (difference > -0.01) &&
3574
0
                              ( (!atom->IsInRing() || !c || !c->IsInRing() || b->IsInRing())
3575
0
                                || (atom->IsInRing() && c && !c->IsInRing() && b->IsInRing()) ) ) ) {
3576
0
                      shortestBond = (atom->GetBond(b))->GetLength();
3577
0
                      maxElNeg = OBElements::GetElectroNeg(b->GetAtomicNum());
3578
0
                      c = b; // save this atom for later use
3579
0
                    } // is this bond better than previous choices
3580
0
                  }
3581
0
              } // loop through neighbors
3582
0
            if (c)
3583
0
              (atom->GetBond(c))->SetBondOrder(2);
3584
0
          }
3585
0
      } // pass 6
3586
3587
    // Now let the atom typer go to work again
3588
0
    _flags &= (~(OB_HYBRID_MOL));
3589
0
    _flags &= (~(OB_AROMATIC_MOL));
3590
0
    _flags &= (~(OB_ATOMTYPES_MOL));
3591
    //  EndModify(true); // "nuke" perceived data
3592
3593
    //Set _spinMultiplicity other than zero for atoms which are hydrogen
3594
    //deficient and which have implicit valency definitions (essentially the
3595
    //organic subset in SMILES). There are assumed to no implicit hydrogens.
3596
    //AssignSpinMultiplicity(true); // TODO: sort out radicals
3597
0
    }
3598
3599
  void OBMol::Center()
3600
0
  {
3601
0
    for (int i = 0;i < NumConformers();++i)
3602
0
      Center(i);
3603
0
  }
3604
3605
  vector3 OBMol::Center(int nconf)
3606
0
  {
3607
0
    obErrorLog.ThrowError(__FUNCTION__,
3608
0
                          "Ran OpenBabel::Center", obAuditMsg);
3609
3610
0
    SetConformer(nconf);
3611
3612
0
    OBAtom *atom;
3613
0
    vector<OBAtom*>::iterator i;
3614
3615
0
    double x=0.0,y=0.0,z=0.0;
3616
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3617
0
      {
3618
0
        x += atom->x();
3619
0
        y += atom->y();
3620
0
        z += atom->z();
3621
0
      }
3622
3623
0
    x /= (double)NumAtoms();
3624
0
    y /= (double)NumAtoms();
3625
0
    z /= (double)NumAtoms();
3626
3627
0
    vector3 vtmp;
3628
0
    vector3 v(x,y,z);
3629
3630
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3631
0
      {
3632
0
        vtmp = atom->GetVector() - v;
3633
0
        atom->SetVector(vtmp);
3634
0
      }
3635
3636
0
    return(v);
3637
0
  }
3638
3639
3640
  /*! this method adds the vector v to all atom positions in all conformers */
3641
  void OBMol::Translate(const vector3 &v)
3642
0
  {
3643
0
    for (int i = 0;i < NumConformers();++i)
3644
0
      Translate(v,i);
3645
0
  }
3646
3647
  /*! this method adds the vector v to all atom positions in the
3648
    conformer nconf. If nconf == OB_CURRENT_CONFORMER, then the atom
3649
    positions in the current conformer are translated. */
3650
  void OBMol::Translate(const vector3 &v, int nconf)
3651
0
  {
3652
0
    obErrorLog.ThrowError(__FUNCTION__,
3653
0
                          "Ran OpenBabel::Translate", obAuditMsg);
3654
3655
0
    int i,size;
3656
0
    double x,y,z;
3657
0
    double *c = (nconf == OB_CURRENT_CONFORMER)? _c : GetConformer(nconf);
3658
3659
0
    x = v.x();
3660
0
    y = v.y();
3661
0
    z = v.z();
3662
0
    size = NumAtoms();
3663
0
    for (i = 0;i < size;++i)
3664
0
      {
3665
0
        c[i*3  ] += x;
3666
0
        c[i*3+1] += y;
3667
0
        c[i*3+2] += z;
3668
0
      }
3669
0
  }
3670
3671
  void OBMol::Rotate(const double u[3][3])
3672
0
  {
3673
0
    int i,j,k;
3674
0
    double m[9];
3675
0
    for (k=0,i = 0;i < 3;++i)
3676
0
      for (j = 0;j < 3;++j)
3677
0
        m[k++] = u[i][j];
3678
3679
0
    for (i = 0;i < NumConformers();++i)
3680
0
      Rotate(m,i);
3681
0
  }
3682
3683
  void OBMol::Rotate(const double m[9])
3684
0
  {
3685
0
    for (int i = 0;i < NumConformers();++i)
3686
0
      Rotate(m,i);
3687
0
  }
3688
3689
  void OBMol::Rotate(const double m[9],int nconf)
3690
0
  {
3691
0
    int i,size;
3692
0
    double x,y,z;
3693
0
    double *c = (nconf == OB_CURRENT_CONFORMER)? _c : GetConformer(nconf);
3694
3695
0
    obErrorLog.ThrowError(__FUNCTION__,
3696
0
                          "Ran OpenBabel::Rotate", obAuditMsg);
3697
3698
0
    size = NumAtoms();
3699
0
    for (i = 0;i < size;++i)
3700
0
      {
3701
0
        x = c[i*3  ];
3702
0
        y = c[i*3+1];
3703
0
        z = c[i*3+2];
3704
0
        c[i*3  ] = m[0]*x + m[1]*y + m[2]*z;
3705
0
        c[i*3+1] = m[3]*x + m[4]*y + m[5]*z;
3706
0
        c[i*3+2] = m[6]*x + m[7]*y + m[8]*z;
3707
0
      }
3708
0
  }
3709
3710
  void OBMol::SetEnergies(std::vector<double> &energies)
3711
0
  {
3712
0
    if (!HasData(OBGenericDataType::ConformerData))
3713
0
      SetData(new OBConformerData);
3714
0
    OBConformerData *cd = (OBConformerData*) GetData(OBGenericDataType::ConformerData);
3715
0
    cd->SetEnergies(energies);
3716
0
  }
3717
3718
  vector<double> OBMol::GetEnergies()
3719
0
  {
3720
0
    if (!HasData(OBGenericDataType::ConformerData))
3721
0
      SetData(new OBConformerData);
3722
0
    OBConformerData *cd = (OBConformerData*) GetData(OBGenericDataType::ConformerData);
3723
0
    vector<double> energies = cd->GetEnergies();
3724
3725
0
    return energies;
3726
0
  }
3727
3728
  double OBMol::GetEnergy(int ci)
3729
0
  {
3730
0
    if (!HasData(OBGenericDataType::ConformerData))
3731
0
      SetData(new OBConformerData);
3732
0
    OBConformerData *cd = (OBConformerData*) GetData(OBGenericDataType::ConformerData);
3733
0
    vector<double> energies = cd->GetEnergies();
3734
3735
0
    if (((unsigned int)ci >= energies.size()) || (ci < 0))
3736
0
      return 0.0;
3737
3738
0
    return energies[ci];
3739
0
  }
3740
3741
  void OBMol::SetConformers(vector<double*> &v)
3742
0
  {
3743
0
    vector<double*>::iterator i;
3744
0
    for (i = _vconf.begin();i != _vconf.end();++i)
3745
0
      delete [] *i;
3746
3747
0
    _vconf = v;
3748
0
    _c = _vconf.empty() ? nullptr : _vconf[0];
3749
3750
0
  }
3751
3752
  void OBMol::SetConformer(unsigned int i)
3753
0
  {
3754
0
    if (i < _vconf.size())
3755
0
      _c = _vconf[i];
3756
0
  }
3757
3758
  void OBMol::CopyConformer(double *c,int idx)
3759
0
  {
3760
    //    obAssert(!_vconf.empty() && (unsigned)idx < _vconf.size());
3761
0
    memcpy((char*)c, (char*)_vconf[idx], sizeof(double)*3*NumAtoms());
3762
0
  }
3763
3764
  // void OBMol::CopyConformer(double *c,int idx)
3765
  // {
3766
  //   obAssert(!_vconf.empty() && (unsigned)idx < _vconf.size());
3767
3768
  //   unsigned int i;
3769
  //   for (i = 0;i < NumAtoms();++i)
3770
  //     {
3771
  //       _vconf[idx][i*3  ] = (double)c[i*3  ];
3772
  //       _vconf[idx][i*3+1] = (double)c[i*3+1];
3773
  //       _vconf[idx][i*3+2] = (double)c[i*3+2];
3774
  //     }
3775
  // }
3776
3777
  void OBMol::DeleteConformer(int idx)
3778
0
  {
3779
0
    if (idx < 0 || idx >= (signed)_vconf.size())
3780
0
      return;
3781
3782
0
    delete [] _vconf[idx];
3783
0
    _vconf.erase((_vconf.begin()+idx));
3784
0
  }
3785
3786
  ///Converts for instance [N+]([O-])=O to N(=O)=O
3787
  bool OBMol::ConvertDativeBonds()
3788
0
  {
3789
0
    obErrorLog.ThrowError(__FUNCTION__,
3790
0
                          "Ran OpenBabel::ConvertDativeBonds", obAuditMsg);
3791
3792
    //Look for + and - charges on adjacent atoms
3793
0
    OBAtom* patom;
3794
0
    vector<OBAtom*>::iterator i;
3795
0
    bool converted = false;
3796
0
    for (patom = BeginAtom(i);patom;patom = NextAtom(i))
3797
0
      {
3798
0
        vector<OBBond*>::iterator itr;
3799
0
        OBBond *pbond;
3800
0
        for (pbond = patom->BeginBond(itr);patom->GetFormalCharge() && pbond;pbond = patom->NextBond(itr))
3801
0
          {
3802
0
            OBAtom* pNbratom = pbond->GetNbrAtom(patom);
3803
0
            int chg1 = patom->GetFormalCharge();
3804
0
            int chg2 = pNbratom->GetFormalCharge();
3805
0
            if((chg1>0 && chg2<0)|| (chg1<0 && chg2>0))
3806
0
              {
3807
                //dative bond. Reduce charges and increase bond order
3808
0
                converted =true;
3809
0
                if(chg1>0)
3810
0
                  --chg1;
3811
0
                else
3812
0
                  ++chg1;
3813
0
                patom->SetFormalCharge(chg1);
3814
0
                if(chg2>0)
3815
0
                  --chg2;
3816
0
                else
3817
0
                  ++chg2;
3818
0
                pNbratom->SetFormalCharge(chg2);
3819
0
                pbond->SetBondOrder(pbond->GetBondOrder()+1);
3820
0
              }
3821
0
          }
3822
0
      }
3823
0
    return converted; //false if no changes made
3824
0
  }
3825
3826
  static bool IsNotCorH(OBAtom* atom)
3827
0
  {
3828
0
    switch (atom->GetAtomicNum())
3829
0
    {
3830
0
    case OBElements::Hydrogen:
3831
0
    case OBElements::Carbon:
3832
0
      return false;
3833
0
    }
3834
0
    return true;
3835
0
  }
3836
3837
  //This maybe would be better using smirks from a datafile
3838
  bool OBMol::MakeDativeBonds()
3839
0
  {
3840
    //! Converts 5-valent N to charged form of dative bonds,
3841
    //! e.g. -N(=O)=O converted to -[N+]([O-])=O. Returns true if conversion occurs.
3842
0
    BeginModify();
3843
    //AddHydrogens();
3844
0
    bool converted = false;
3845
0
    OBAtom* patom;
3846
0
    vector<OBAtom*>::iterator ai;
3847
0
    for (patom = BeginAtom(ai);patom;patom = NextAtom(ai)) //all atoms
3848
0
    {
3849
0
      if(patom->GetAtomicNum() == OBElements::Nitrogen // || patom->GetAtomicNum() == OBElements::Phosphorus) not phosphorus!
3850
0
        && (patom->GetExplicitValence()==5 || (patom->GetExplicitValence()==4 && patom->GetFormalCharge()==0)))
3851
0
      {
3852
        // Find the bond to be modified. Prefer a bond to a hetero-atom,
3853
        // and the highest order bond if there is a choice.
3854
0
        OBBond *bond, *bestbond;
3855
0
        OBBondIterator bi;
3856
0
        for (bestbond = bond = patom->BeginBond(bi); bond; bond = patom->NextBond(bi))
3857
0
        {
3858
0
          unsigned int bo = bond->GetBondOrder();
3859
0
          if(bo>=2 && bo<=4)
3860
0
          {
3861
0
            bool het = IsNotCorH(bond->GetNbrAtom(patom));
3862
0
            bool oldhet = IsNotCorH(bestbond->GetNbrAtom(patom));
3863
0
            bool higherorder = bo > bestbond->GetBondOrder();
3864
0
            if((het && !oldhet) || (((het && oldhet) || (!het && !oldhet)) && higherorder))
3865
0
              bestbond = bond;
3866
0
          }
3867
0
        }
3868
        //Make the charged form
3869
0
        bestbond->SetBondOrder(bestbond->GetBondOrder()-1);
3870
0
        patom->SetFormalCharge(+1);
3871
0
        OBAtom* at = bestbond->GetNbrAtom(patom);
3872
0
        at->SetFormalCharge(-1);
3873
0
        converted=true;
3874
0
      }
3875
0
    }
3876
0
    EndModify();
3877
0
    return converted;
3878
0
  }
3879
3880
  /**
3881
   *  This function is useful when writing to legacy formats (such as MDL MOL) that do
3882
   *  not support zero-order bonds. It is worth noting that some compounds cannot be
3883
   *  well represented using just single, double and triple bonds, even with adjustments
3884
   *  to adjacent charges. In these cases, simply converting zero-order bonds to single
3885
   *  bonds is all that can be done.
3886
   *
3887
   @verbatim
3888
   Algorithm from:
3889
   Clark, A. M. Accurate Specification of Molecular Structures: The Case for
3890
   Zero-Order Bonds and Explicit Hydrogen Counting. Journal of Chemical Information
3891
   and Modeling, 51, 3149-3157 (2011). http://pubs.acs.org/doi/abs/10.1021/ci200488k
3892
   @endverbatim
3893
  */
3894
  bool OBMol::ConvertZeroBonds()
3895
0
  {
3896
    // TODO: Option to just remove zero-order bonds entirely
3897
3898
    // TODO: Is it OK to not wrap this in BeginModify() and EndModify()?
3899
    // If we must, I think we need to manually remember HasImplicitValencePerceived and
3900
    // re-set it after EndModify()
3901
3902
    // Periodic table block for element (1=s, 2=p, 3=d, 4=f)
3903
0
    const int BLOCKS[113] = {0,1,2,1,1,2,2,2,2,2,2,1,1,2,2,2,2,2,2,1,1,3,3,3,3,3,3,3,3,3,
3904
0
                             3,2,2,2,2,2,2,1,1,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,1,1,4,4,4,
3905
0
                             4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,1,1,4,
3906
0
                             4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3};
3907
3908
0
    bool converted = false;
3909
    // Get contiguous fragments of molecule
3910
0
    vector<vector<int> > cfl;
3911
0
    ContigFragList(cfl);
3912
    // Iterate over contiguous fragments
3913
0
    for (vector< vector<int> >::iterator i = cfl.begin(); i != cfl.end(); ++i) {
3914
      // Get all zero-order bonds in contiguous fragment
3915
0
      vector<OBBond*> bonds;
3916
0
      for(vector<int>::const_iterator j = i->begin(); j != i->end(); ++j) {
3917
0
        FOR_BONDS_OF_ATOM(b, GetAtom(*j)) {
3918
0
          if (b->GetBondOrder() == 0 && !(find(bonds.begin(), bonds.end(), &*b) != bonds.end())) {
3919
0
            bonds.push_back(&*b);
3920
0
          }
3921
0
        }
3922
0
      }
3923
      // Convert zero-order bonds
3924
0
      while (bonds.size() > 0) {
3925
        // Pick a bond using scoring system
3926
0
        int bi = 0;
3927
0
        if (bonds.size() > 1) {
3928
0
          vector<int> scores(bonds.size());
3929
0
          for (unsigned int n = 0; n < bonds.size(); n++) {
3930
0
            OBAtom *bgn = bonds[n]->GetBeginAtom();
3931
0
            OBAtom *end = bonds[n]->GetEndAtom();
3932
0
            int score = 0;
3933
0
            score += bgn->GetAtomicNum() + end->GetAtomicNum();
3934
0
            score += abs(bgn->GetFormalCharge()) + abs(end->GetFormalCharge());
3935
0
            pair<int, int> lb = bgn->LewisAcidBaseCounts();
3936
0
            pair<int, int> le = end->LewisAcidBaseCounts();
3937
0
            if (lb.first > 0 && lb.second > 0 && le.first > 0 && le.second > 0) {
3938
0
              score += 100;   // Both atoms are Lewis acids *and* Lewis bases
3939
0
            } else if ((lb.first > 0 && le.second > 0) && (lb.second > 0 && le.first > 0)) {
3940
0
              score -= 1000;  // Lewis acid/base direction is mono-directional
3941
0
            }
3942
0
            int bcount = bgn->GetImplicitHCount();
3943
0
            FOR_BONDS_OF_ATOM(b, bgn) { bcount += 1; }
3944
0
            int ecount = end->GetImplicitHCount();
3945
0
            FOR_BONDS_OF_ATOM(b, end) { ecount += 1; }
3946
0
            if (bcount == 1 || ecount == 1) {
3947
0
              score -= 10; // If the start or end atoms have only 1 neighbour
3948
0
            }
3949
0
            scores[n] = score;
3950
0
          }
3951
0
          for (unsigned int n = 1; n < scores.size(); n++) {
3952
0
            if (scores[n] < scores[bi]) {
3953
0
              bi = n;
3954
0
            }
3955
0
          }
3956
0
        }
3957
0
        OBBond *bond = bonds[bi];
3958
0
        bonds.erase(bonds.begin() + bi);
3959
0
        OBAtom *bgn = bond->GetBeginAtom();
3960
0
        OBAtom *end = bond->GetEndAtom();
3961
        // _ele is an unsigned char (0..255), but BLOCKS only covers known
3962
        // elements (Z<=112). Treat anything outside the table as block 0
3963
        // so the heuristics below leave the bond as a plain single bond.
3964
0
        unsigned int zb = bgn->GetAtomicNum();
3965
0
        unsigned int ze = end->GetAtomicNum();
3966
0
        int blockb = (zb < sizeof(BLOCKS)/sizeof(BLOCKS[0])) ? BLOCKS[zb] : 0;
3967
0
        int blocke = (ze < sizeof(BLOCKS)/sizeof(BLOCKS[0])) ? BLOCKS[ze] : 0;
3968
0
        pair<int, int> lb = bgn->LewisAcidBaseCounts();
3969
0
        pair<int, int> le = end->LewisAcidBaseCounts();
3970
0
        int chg = 0; // Amount to adjust atom charges
3971
0
        int ord = 1; // New bond order
3972
0
        if (lb.first > 0 && lb.second > 0 && le.first > 0 && le.second > 0) {
3973
0
          ord = 2;  // both atoms are amphoteric, so turn it into a double bond
3974
0
        } else if (lb.first > 0 && blockb == 2 && blocke >= 3) {
3975
0
          ord = 2;  // p-block lewis acid with d/f-block element: make into double bond
3976
0
        } else if (le.first > 0 && blocke == 2 && blockb >= 3) {
3977
0
          ord = 2;  // p-block lewis acid with d/f-block element: make into double bond
3978
0
        } else if (lb.first > 0 && le.second > 0) {
3979
0
          chg = -1;  // lewis acid/base goes one way only; charge separate it
3980
0
        } else if (lb.second > 0 && le.first > 0) {
3981
0
          chg = 1;  //  no matching capacity; do not charge separate
3982
0
        }
3983
        // adjust bond order and atom charges accordingly
3984
0
        bgn->SetFormalCharge(bgn->GetFormalCharge()+chg);
3985
0
        end->SetFormalCharge(end->GetFormalCharge()-chg);
3986
0
        bond->SetBondOrder(ord);
3987
0
        converted = true;
3988
0
      }
3989
0
    }
3990
0
    return converted;
3991
0
  }
3992
3993
  OBAtom *OBMol::BeginAtom(OBAtomIterator &i)
3994
904k
  {
3995
904k
    i = _vatom.begin();
3996
904k
    return i == _vatom.end() ? nullptr : (OBAtom*)*i;
3997
904k
  }
3998
3999
  const OBAtom* OBMol::BeginAtom(OBAtomConstIterator &i) const
4000
0
  {
4001
0
    i = _vatom.cbegin();
4002
0
    return i == _vatom.cend() ? nullptr : (OBAtom *)*i;
4003
0
  }
4004
4005
  OBAtom *OBMol::NextAtom(OBAtomIterator &i)
4006
116M
  {
4007
116M
    ++i;
4008
116M
    return i == _vatom.end() ? nullptr : (OBAtom*)*i;
4009
116M
  }
4010
4011
  const OBAtom* OBMol::NextAtom(OBAtomConstIterator &i) const
4012
0
  {
4013
0
    ++i;
4014
0
    return i == _vatom.cend() ? nullptr : (OBAtom *)*i;
4015
0
  }
4016
4017
  OBBond *OBMol::BeginBond(OBBondIterator &i)
4018
65.7k
  {
4019
65.7k
    i = _vbond.begin();
4020
65.7k
    return i == _vbond.end() ? nullptr : (OBBond*)*i;
4021
65.7k
  }
4022
4023
  OBBond *OBMol::NextBond(OBBondIterator &i)
4024
109k
  {
4025
109k
    ++i;
4026
109k
    return i == _vbond.end() ? nullptr : (OBBond*)*i;
4027
109k
  }
4028
4029
  //! \since version 2.4
4030
  int OBMol::AreInSameRing(OBAtom *a, OBAtom *b)
4031
0
  {
4032
0
    bool a_in, b_in;
4033
0
    vector<OBRing*> vr;
4034
0
    vr = GetLSSR();
4035
4036
0
    vector<OBRing*>::iterator i;
4037
0
    vector<int>::iterator j;
4038
4039
0
    for (i = vr.begin();i != vr.end();++i) {
4040
0
      a_in = false;
4041
0
      b_in = false;
4042
      // Go through the path of the ring and see if a and/or b match
4043
      // each node in the path
4044
0
      for(j = (*i)->_path.begin();j != (*i)->_path.end();++j) {
4045
0
        if ((unsigned)(*j) == a->GetIdx())
4046
0
          a_in = true;
4047
0
        if ((unsigned)(*j) == b->GetIdx())
4048
0
          b_in = true;
4049
0
      }
4050
4051
0
      if (a_in && b_in)
4052
0
        return (*i)->Size();
4053
0
    }
4054
4055
0
    return 0;
4056
0
  }
4057
4058
  vector<OBMol> OBMol::Separate(int StartIndex)
4059
0
  {
4060
0
    vector<OBMol> result;
4061
0
    if( NumAtoms() == 0 )
4062
0
      return result; // nothing to do, but let's prevent a crash
4063
4064
0
    OBMolAtomDFSIter iter( this, StartIndex );
4065
0
    OBMol newMol;
4066
0
    while( GetNextFragment( iter, newMol ) ) {
4067
0
      result.push_back( newMol );
4068
0
      newMol.Clear();
4069
0
    }
4070
4071
0
    return result;
4072
0
  }
4073
4074
  //! \brief Copy part of a molecule to another molecule
4075
  /**
4076
  This function copies a substructure of a molecule to another molecule. The key
4077
  information needed is an OBBitVec indicating which atoms to include and (optionally)
4078
  an OBBitVec indicating which bonds to exclude. By default, only bonds joining
4079
  included atoms are copied.
4080
4081
  When an atom is copied, but not all of its bonds are, by default hydrogen counts are
4082
  adjusted to account for the missing bonds. That is, given the SMILES "CF", if we
4083
  copy the two atoms but exclude the bond, we will end up with "C.F". This behavior
4084
  can be changed by specifiying a value other than 1 for the \p correctvalence parameter.
4085
  A value of 0 will yield "[C].[F]" while 2 will yield "C*.F*" (see \p correctvalence below
4086
  for more information).
4087
4088
  Aromaticity is preserved as present in the original OBMol. If this is not desired,
4089
  the user should call OBMol::SetAromaticPerceived(false) on the new OBMol.
4090
4091
  Stereochemistry is only preserved if the corresponding elements are wholly present in
4092
  the substructure. For example, all four atoms and bonds of a tetrahedral stereocenter
4093
  must be copied.
4094
4095
  Residue information is preserved if the original OBMol is marked as having
4096
  its residues perceived. If this is not desired, either call
4097
  OBMol::SetChainsPerceived(false) in advance on the original OBMol to avoid copying
4098
  the residues (and then reset it afterwards), or else call it on the new OBMol so
4099
  that residue information will be reperceived (when requested).
4100
4101
  Here is an example of using this method to copy ring systems to a new molecule.
4102
  Given the molecule represented by the SMILES string, "FC1CC1c2ccccc2I", we will
4103
  end up with a new molecule represented by the SMILES string, "C1CC1.c2ccccc2".
4104
  \code{.cpp}
4105
  OBBitVec atoms(mol.NumAtoms() + 1); // the maximum size needed
4106
  FOR_ATOMS_OF_MOL(atom, mol) {
4107
    if(atom->IsInRing())
4108
      atoms.SetBitOn(atom->Idx());
4109
  }
4110
  OBBitVec excludebonds(mol.NumBonds()); // the maximum size needed
4111
  FOR_BONDS_OF_MOL(bond, mol) {
4112
    if(!bond->IsInRing())
4113
      excludebonds.SetBitOn(bond->Idx());
4114
  }
4115
  OBMol newmol;
4116
  mol.CopySubstructure(&newmol, &atoms, &excludebonds);
4117
  \endcode
4118
4119
  When used from Python, note that "None" may be used to specify an empty value for
4120
  the \p excludebonds parameter.
4121
4122
  \remark Some alternatives to using this function, which may be preferred in some
4123
          instances due to efficiency or convenience are:
4124
          -# Copying the entire OBMol, and then deleting the unwanted parts
4125
          -# Modifiying the original OBMol, and then restoring it
4126
          -# Using the SMILES writer option -xf to specify fragment atom idxs
4127
4128
  \return A boolean indicating success or failure. Currently failure is only reported
4129
          if one of the specified atoms is not present, or \p atoms is a NULL
4130
          pointer.
4131
4132
  \param newmol   The molecule to which to add the substructure. Note that atoms are
4133
                  appended to this molecule.
4134
  \param atoms    An OBBitVec, indexed by atom Idx, specifying which atoms to copy
4135
  \param excludebonds  An OBBitVec, indexed by bond Idx, specifying a list of bonds
4136
                       to exclude. By default, all bonds between the specified atoms are
4137
                       included - this parameter overrides that.
4138
  \param correctvalence  A value of 0, 1 (default) or 2 that indicates how atoms with missing
4139
                         bonds are handled:
4140
                        0 - Leave the implicit hydrogen count unchanged;
4141
                        1 - Adjust the implicit hydrogen count to correct for
4142
                            the missing bonds;
4143
                        2 - Replace the missing bonds with bonds to dummy atoms
4144
  \param atomorder Record the Idxs of the original atoms. That is, the first element
4145
                   in this vector will be the Idx of the atom in the original OBMol
4146
                   that corresponds to the first atom in the new OBMol. Note that
4147
                   the information is appended to this vector.
4148
  \param bondorder Record the Idxs of the original bonds. See \p atomorder above.
4149
4150
  **/
4151
4152
  bool OBMol::CopySubstructure(OBMol& newmol, OBBitVec *atoms, OBBitVec *excludebonds, unsigned int correctvalence,
4153
                               std::vector<unsigned int> *atomorder, std::vector<unsigned int> *bondorder)
4154
0
  {
4155
0
    if (!atoms)
4156
0
      return false;
4157
4158
0
    bool record_atomorder = atomorder != nullptr;
4159
0
    bool record_bondorder = bondorder != nullptr;
4160
0
    bool bonds_specified = excludebonds != nullptr;
4161
4162
0
    newmol.SetDimension(GetDimension());
4163
4164
    // If the parent is set to periodic, then also apply boundary conditions to the fragments
4165
0
    if (IsPeriodic()) {
4166
0
      OBUnitCell* parent_uc = (OBUnitCell*)GetData(OBGenericDataType::UnitCell);
4167
0
      newmol.SetData(parent_uc->Clone(nullptr));
4168
0
      newmol.SetPeriodicMol();
4169
0
    }
4170
    // If the parent had aromaticity perceived, then retain that for the fragment
4171
0
    newmol.SetFlag(_flags & OB_AROMATIC_MOL);
4172
    // The fragment will preserve the "chains perceived" flag of the parent
4173
0
    newmol.SetFlag(_flags & OB_CHAINS_MOL);
4174
    // We will check for residues only if the parent has chains perceived already
4175
0
    bool checkresidues = HasChainsPerceived();
4176
4177
    // Now add the atoms
4178
0
    map<OBAtom*, OBAtom*> AtomMap;//key is from old mol; value from new mol
4179
0
    for (int bit = atoms->FirstBit(); bit != atoms->EndBit(); bit = atoms->NextBit(bit)) {
4180
0
      OBAtom* atom = this->GetAtom(bit);
4181
0
      if (!atom)
4182
0
        return false;
4183
0
      newmol.AddAtom(*atom); // each subsequent atom
4184
0
      if (record_atomorder)
4185
0
        atomorder->push_back(bit);
4186
0
      AtomMap[&*atom] = newmol.GetAtom(newmol.NumAtoms());
4187
0
    }
4188
4189
    //Add the residues
4190
0
    if (checkresidues) {
4191
0
      map<OBResidue*, OBResidue*> ResidueMap; // map from old->new
4192
0
      for (int bit = atoms->FirstBit(); bit != atoms->EndBit(); bit = atoms->NextBit(bit)) {
4193
0
        OBAtom* atom = this->GetAtom(bit);
4194
0
        OBResidue* res = atom->GetResidue();
4195
0
        if (!res) continue;
4196
0
        map<OBResidue*, OBResidue*>::iterator mit = ResidueMap.find(res);
4197
0
        OBResidue *newres;
4198
0
        if (mit == ResidueMap.end()) {
4199
0
          newres = newmol.NewResidue();
4200
0
          *newres = *res;
4201
0
          ResidueMap[res] = newres;
4202
0
        } else {
4203
0
          newres = mit->second;
4204
0
        }
4205
0
        OBAtom* newatom = AtomMap[&*atom];
4206
0
        newres->AddAtom(newatom);
4207
0
        newres->SetAtomID(newatom, res->GetAtomID(atom));
4208
0
        newres->SetHetAtom(newatom, res->IsHetAtom(atom));
4209
0
        newres->SetSerialNum(newatom, res->GetSerialNum(atom));
4210
0
      }
4211
0
    }
4212
4213
    // Update Stereo
4214
0
    std::vector<OBGenericData*>::iterator data;
4215
0
    std::vector<OBGenericData*> stereoData = GetAllData(OBGenericDataType::StereoData);
4216
0
    for (data = stereoData.begin(); data != stereoData.end(); ++data) {
4217
0
      if (static_cast<OBStereoBase*>(*data)->GetType() == OBStereo::CisTrans) {
4218
0
        OBCisTransStereo *ct = dynamic_cast<OBCisTransStereo*>(*data);
4219
4220
        // Check that the entirety of this cistrans cfg occurs in this substructure
4221
0
        OBCisTransStereo::Config cfg = ct->GetConfig();
4222
0
        OBAtom* begin = GetAtomById(cfg.begin);
4223
0
        if (AtomMap.find(begin) == AtomMap.end())
4224
0
          continue;
4225
0
        OBAtom* end = GetAtomById(cfg.end);
4226
0
        if (AtomMap.find(end) == AtomMap.end())
4227
0
          continue;
4228
0
        bool skip_cfg = false;
4229
0
        if (bonds_specified) {
4230
0
          FOR_BONDS_OF_ATOM(bond, begin) {
4231
0
            if (excludebonds->BitIsSet(bond->GetIdx())) {
4232
0
              skip_cfg = true;
4233
0
              break;
4234
0
            }
4235
0
          }
4236
0
          if (skip_cfg)
4237
0
            continue;
4238
0
          FOR_BONDS_OF_ATOM(bond, end) {
4239
0
            if (excludebonds->BitIsSet(bond->GetIdx())) {
4240
0
              skip_cfg = true;
4241
0
              break;
4242
0
            }
4243
0
          }
4244
0
          if (skip_cfg)
4245
0
            continue;
4246
0
        }
4247
0
        for (OBStereo::RefIter ri = cfg.refs.begin(); ri != cfg.refs.end(); ++ri) {
4248
0
          if (*ri != OBStereo::ImplicitRef && AtomMap.find(GetAtomById(*ri)) == AtomMap.end()) {
4249
0
            skip_cfg = true;
4250
0
            break;
4251
0
          }
4252
0
        }
4253
0
        if (skip_cfg)
4254
0
          continue;
4255
4256
0
        OBCisTransStereo::Config newcfg;
4257
0
        newcfg.specified = cfg.specified;
4258
0
        newcfg.begin = cfg.begin == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(cfg.begin)]->GetId();
4259
0
        newcfg.end = cfg.end == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(cfg.end)]->GetId();
4260
0
        OBStereo::Refs refs;
4261
0
        for (OBStereo::RefIter ri = cfg.refs.begin(); ri != cfg.refs.end(); ++ri) {
4262
0
          OBStereo::Ref ref = *ri == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(*ri)]->GetId();
4263
0
          refs.push_back(ref);
4264
0
        }
4265
0
        newcfg.refs = refs;
4266
4267
0
        OBCisTransStereo *newct = new OBCisTransStereo(this);
4268
0
        newct->SetConfig(newcfg);
4269
0
        newmol.SetData(newct);
4270
0
      }
4271
0
      else if (static_cast<OBStereoBase*>(*data)->GetType() == OBStereo::Tetrahedral) {
4272
0
        OBTetrahedralStereo *tet = dynamic_cast<OBTetrahedralStereo*>(*data);
4273
0
        OBTetrahedralStereo::Config cfg = tet->GetConfig();
4274
4275
        // Check that the entirety of this tet cfg occurs in this substructure
4276
0
        OBAtom *center = GetAtomById(cfg.center);
4277
0
        std::map<OBAtom*, OBAtom*>::iterator centerit = AtomMap.find(center);
4278
0
        if (centerit == AtomMap.end())
4279
0
          continue;
4280
0
        if (cfg.from != OBStereo::ImplicitRef && AtomMap.find(GetAtomById(cfg.from)) == AtomMap.end())
4281
0
          continue;
4282
0
        bool skip_cfg = false;
4283
0
        if (bonds_specified) {
4284
0
          FOR_BONDS_OF_ATOM(bond, center) {
4285
0
            if (excludebonds->BitIsSet(bond->GetIdx())) {
4286
0
              skip_cfg = true;
4287
0
              break;
4288
0
            }
4289
0
          }
4290
0
          if (skip_cfg)
4291
0
            continue;
4292
0
        }
4293
0
        for (OBStereo::RefIter ri = cfg.refs.begin(); ri != cfg.refs.end(); ++ri) {
4294
0
          if (*ri != OBStereo::ImplicitRef && AtomMap.find(GetAtomById(*ri)) == AtomMap.end()) {
4295
0
            skip_cfg = true;
4296
0
            break;
4297
0
          }
4298
0
        }
4299
0
        if (skip_cfg)
4300
0
          continue;
4301
4302
0
        OBTetrahedralStereo::Config newcfg;
4303
0
        newcfg.specified = cfg.specified;
4304
0
        newcfg.center = centerit->second->GetId();
4305
0
        newcfg.from = cfg.from == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(cfg.from)]->GetId();
4306
0
        OBStereo::Refs refs;
4307
0
        for (OBStereo::RefIter ri = cfg.refs.begin(); ri != cfg.refs.end(); ++ri) {
4308
0
          OBStereo::Ref ref = *ri == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(*ri)]->GetId();
4309
0
          refs.push_back(ref);
4310
0
        }
4311
0
        newcfg.refs = refs;
4312
4313
0
        OBTetrahedralStereo *newtet = new OBTetrahedralStereo(this);
4314
0
        newtet->SetConfig(newcfg);
4315
0
        newmol.SetData(newtet);
4316
0
      }
4317
0
    }
4318
4319
    // Options:
4320
    // 1. Bonds that do not connect atoms in the subset are ignored
4321
    // 2. As 1. but implicit Hs are added to replace them
4322
    // 3. As 1. but asterisks are added to replace them
4323
0
    FOR_BONDS_OF_MOL(bond, this) {
4324
0
      bool skipping_bond = bonds_specified && excludebonds->BitIsSet(bond->GetIdx());
4325
0
      map<OBAtom*, OBAtom*>::iterator posB = AtomMap.find(bond->GetBeginAtom());
4326
0
      map<OBAtom*, OBAtom*>::iterator posE = AtomMap.find(bond->GetEndAtom());
4327
0
      if (posB == AtomMap.end() && posE == AtomMap.end())
4328
0
        continue;
4329
4330
0
      if (posB == AtomMap.end() || posE == AtomMap.end() || skipping_bond) {
4331
0
        switch(correctvalence) {
4332
0
        case 1:
4333
0
          if (posB == AtomMap.end() || (skipping_bond && posE != AtomMap.end()))
4334
0
            posE->second->SetImplicitHCount(posE->second->GetImplicitHCount() + bond->GetBondOrder());
4335
0
          if (posE == AtomMap.end() || (skipping_bond && posB != AtomMap.end()))
4336
0
            posB->second->SetImplicitHCount(posB->second->GetImplicitHCount() + bond->GetBondOrder());
4337
0
          break;
4338
0
        case 2: {
4339
0
            OBAtom *atomB, *atomE;
4340
0
            if (skipping_bond) {
4341
0
              for(int N=0; N<2; ++N) {
4342
0
                atomB = nullptr;
4343
0
                atomE = nullptr;
4344
0
                if (N==0) {
4345
0
                  if (posB != AtomMap.end()) {
4346
0
                    atomB = posB->second;
4347
0
                    atomE = newmol.NewAtom();
4348
0
                    if (record_atomorder)
4349
0
                      atomorder->push_back(bond->GetEndAtomIdx());
4350
0
                  }
4351
0
                } else if (posE != AtomMap.end()) {
4352
0
                  atomE = posE->second;
4353
0
                  atomB = newmol.NewAtom();
4354
0
                  if (record_atomorder)
4355
0
                    atomorder->push_back(bond->GetBeginAtomIdx());
4356
0
                }
4357
0
                if (atomB == nullptr || atomE == nullptr)
4358
0
                  continue;
4359
0
                newmol.AddBond(atomB->GetIdx(), atomE->GetIdx(),
4360
0
                  bond->GetBondOrder(), bond->GetFlags());
4361
0
                if (record_bondorder)
4362
0
                  bondorder->push_back(bond->GetIdx());
4363
0
              }
4364
0
            }
4365
0
            else {
4366
0
              atomB = (posB == AtomMap.end()) ? newmol.NewAtom() : posB->second;
4367
0
              atomE = (posE == AtomMap.end()) ? newmol.NewAtom() : posE->second;
4368
0
              if (record_atomorder) {
4369
0
                if (posB == AtomMap.end())
4370
0
                  atomorder->push_back(bond->GetBeginAtomIdx());
4371
0
                else
4372
0
                  atomorder->push_back(bond->GetEndAtomIdx());
4373
0
              }
4374
0
              newmol.AddBond(atomB->GetIdx(), atomE->GetIdx(),
4375
0
                bond->GetBondOrder(), bond->GetFlags());
4376
0
              if (record_bondorder)
4377
0
                bondorder->push_back(bond->GetIdx());
4378
0
            }
4379
0
          }
4380
0
          break;
4381
0
        default:
4382
0
          break;
4383
0
        }
4384
0
      }
4385
0
      else {
4386
0
        newmol.AddBond((posB->second)->GetIdx(), posE->second->GetIdx(),
4387
0
                       bond->GetBondOrder(), bond->GetFlags());
4388
0
        if (record_bondorder)
4389
0
          bondorder->push_back(bond->GetIdx());
4390
0
      }
4391
0
    }
4392
4393
0
    return true;
4394
0
  }
4395
4396
0
  bool OBMol::GetNextFragment( OBMolAtomDFSIter& iter, OBMol& newmol ) {
4397
0
    if( ! iter ) return false;
4398
4399
    // We want to keep the atoms in their original order rather than use
4400
    // the DFS order so just record the information first
4401
0
    OBBitVec infragment(this->NumAtoms()+1);
4402
0
    do { //for each atom in fragment
4403
0
      infragment.SetBitOn(iter->GetIdx());
4404
0
    } while ((iter++).next());
4405
4406
0
    bool ok = CopySubstructure(newmol, &infragment);
4407
4408
0
    return ok;
4409
0
  }
4410
4411
  // Put the specified molecular charge on a single atom (which is expected for InChIFormat).
4412
  // Assumes all the hydrogen is explicitly included in the molecule,
4413
  // and that SetTotalCharge() has not been called. (This function is an alternative.)
4414
  // Returns false if cannot assign all the charge.
4415
  // Not robust in the general case, but see below for the more common simpler cases.
4416
  bool OBMol::AssignTotalChargeToAtoms(int charge)
4417
0
  {
4418
0
    int extraCharge = charge - GetTotalCharge(); //GetTotalCharge() gets charge on atoms
4419
4420
0
    FOR_ATOMS_OF_MOL (atom, this)
4421
0
    {
4422
0
      unsigned int atomicnum = atom->GetAtomicNum();
4423
0
      if (atomicnum == 1)
4424
0
        continue;
4425
0
      int charge = atom->GetFormalCharge();
4426
0
      unsigned bosum = atom->GetExplicitValence();
4427
0
      unsigned int totalValence = bosum + atom->GetImplicitHCount();
4428
0
      unsigned int typicalValence = GetTypicalValence(atomicnum, bosum, charge);
4429
0
      int diff = typicalValence - totalValence;
4430
0
      if(diff != 0)
4431
0
      {
4432
0
        int c;
4433
0
        if(extraCharge == 0)
4434
0
          c = diff > 0 ? -1 : +1; //e.g. CH3C(=O)O, NH4 respectively
4435
0
        else
4436
0
          c = extraCharge < 0 ? -1 : 1;
4437
0
        if (totalValence == GetTypicalValence(atomicnum, bosum, charge + c)) {
4438
0
          atom->SetFormalCharge(charge + c);
4439
0
          extraCharge -= c;
4440
0
        }
4441
0
      }
4442
0
    }
4443
0
    if(extraCharge != 0)
4444
0
    {
4445
0
      obErrorLog.ThrowError(__FUNCTION__, "Unable to assign all the charge to atoms", obWarning);
4446
0
      return false;
4447
0
    }
4448
0
    return true;
4449
0
 }
4450
  /* These cases work ok
4451
   original      charge  result
4452
  [NH4]             +1   [NH4+]
4453
  -C(=O)[O]         -1   -C(=O)[O-]
4454
  -[CH2]            +1   -C[CH2+]
4455
  -[CH2]            -1   -C[CH2-]
4456
  [NH3]CC(=O)[O]     0   [NH3+]CC(=O)[O-]
4457
  S(=O)(=O)([O])[O] -2   S(=O)(=O)([O-])[O-]
4458
  [NH4].[Cl]         0   [NH4+].[Cl-]
4459
  */
4460
4461
} // end namespace OpenBabel
4462
4463
//! \file mol.cpp
4464
//! \brief Handle molecules. Implementation of OBMol.