Coverage Report

Created: 2026-01-17 06:15

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