Coverage Report

Created: 2025-11-11 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/mol.cpp
Line
Count
Source
1
/**********************************************************************
2
mol.cpp - Handle molecules.
3
4
Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
5
Some portions Copyright (C) 2001-2008 by Geoffrey R. Hutchison
6
Some portions Copyright (C) 2003 by Michael Banck
7
8
This file is part of the Open Babel project.
9
For more information, see <http://openbabel.org/>
10
11
This program is free software; you can redistribute it and/or modify
12
it under the terms of the GNU General Public License as published by
13
the Free Software Foundation version 2 of the License.
14
15
This program is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
GNU General Public License for more details.
19
***********************************************************************/
20
#include <openbabel/babelconfig.h>
21
22
#include <openbabel/mol.h>
23
#include <openbabel/bond.h>
24
#include <openbabel/ring.h>
25
#include <openbabel/rotamer.h>
26
#include <openbabel/phmodel.h>
27
#include <openbabel/bondtyper.h>
28
#include <openbabel/obiter.h>
29
#include <openbabel/builder.h>
30
#include <openbabel/kekulize.h>
31
#include <openbabel/internalcoord.h>
32
#include <openbabel/math/matrix3x3.h>
33
#include <openbabel/obfunctions.h>
34
#include <openbabel/elements.h>
35
36
#include <openbabel/stereo/tetrahedral.h>
37
#include <openbabel/stereo/cistrans.h>
38
39
#include <sstream>
40
#include <set>
41
42
using namespace std;
43
44
namespace OpenBabel
45
{
46
47
  extern bool SwabInt;
48
  extern THREAD_LOCAL OBPhModel  phmodel;
49
  extern THREAD_LOCAL OBAromaticTyper  aromtyper;
50
  extern THREAD_LOCAL OBAtomTyper      atomtyper;
51
  extern THREAD_LOCAL OBBondTyper      bondtyper;
52
53
  /** \class OBMol mol.h <openbabel/mol.h>
54
      \brief Molecule Class
55
56
      The most important class in Open Babel is OBMol, or the molecule class.
57
      The OBMol class is designed to store all the basic information
58
      associated with a molecule, to make manipulations on the connection
59
      table of a molecule facile, and to provide member functions which
60
      automatically perceive information about a molecule. A guided tour
61
      of the OBMol class is a good place to start.
62
63
      An OBMol class can be declared:
64
      \code
65
      OBMol mol;
66
      \endcode
67
68
      For example:
69
      \code
70
      #include <iostream.h>
71
72
      #include <openbabel/mol.h>
73
      #include <openbabel/obconversion.h>
74
      int main(int argc,char **argv)
75
      {
76
      OBConversion conv(&cin,&cout);
77
      if(conv.SetInAndOutFormats("SDF","MOL2"))
78
      {
79
      OBMol mol;
80
      if(conv.Read(&mol))
81
      ...manipulate molecule
82
83
      conv->Write(&mol);
84
      }
85
      return(1);
86
      }
87
      \endcode
88
89
      will read in a molecule in SD file format from stdin
90
      (or the C++ equivalent cin) and write a MOL2 format file out
91
      to standard out. Additionally, The input and output formats can
92
      be altered using the OBConversion class
93
94
      Once a molecule has been read into an OBMol (or created via other methods)
95
      the atoms and bonds
96
      can be accessed by the following methods:
97
      \code
98
      OBAtom *atom;
99
      atom = mol.GetAtom(5); //random access of an atom
100
      \endcode
101
      or
102
      \code
103
      OBBond *bond;
104
      bond = mol.GetBond(14); //random access of a bond
105
      \endcode
106
      or
107
      \code
108
      FOR_ATOMS_OF_MOL(atom, mol) // iterator access (see OBMolAtomIter)
109
      \endcode
110
      or
111
      \code
112
      FOR_BONDS_OF_MOL(bond, mol) // iterator access (see OBMolBondIter)
113
      \endcode
114
      It is important to note that atom arrays currently begin at 1 and bond arrays
115
      begin at 0. Requesting atom 0 (\code
116
      OBAtom *atom = mol.GetAtom(0); \endcode
117
      will result in an error, but
118
      \code
119
      OBBond *bond = mol.GetBond(0);
120
      \endcode
121
      is perfectly valid.
122
      Note that this is expected to change in the near future to simplify coding
123
      and improve efficiency.
124
125
      The ambiguity of numbering issues and off-by-one errors led to the use
126
      of iterators in Open Babel. An iterator is essentially just a pointer, but
127
      when used in conjunction with Standard Template Library (STL) vectors
128
      it provides an unambiguous way to loop over arrays. OBMols store their
129
      atom and bond information in STL vectors. Since vectors are template
130
      based, a vector of any user defined type can be declared. OBMols declare
131
      vector<OBAtom*> and vector<OBBond*> to store atom and bond information.
132
      Iterators are then a natural way to loop over the vectors of atoms and bonds.
133
134
      A variety of predefined iterators have been created to simplify
135
      common looping requests (e.g., looping over all atoms in a molecule,
136
      bonds to a given atom, etc.)
137
138
      \code
139
      #include <openbabel/obiter.h>
140
      ...
141
      #define FOR_ATOMS_OF_MOL(a,m)     for( OBMolAtomIter     a(m); a; ++a )
142
      #define FOR_BONDS_OF_MOL(b,m)     for( OBMolBondIter     b(m); b; ++b )
143
      #define FOR_NBORS_OF_ATOM(a,p)    for( OBAtomAtomIter    a(p); a; ++a )
144
      #define FOR_BONDS_OF_ATOM(b,p)    for( OBAtomBondIter    b(p); b; ++b )
145
      #define FOR_RESIDUES_OF_MOL(r,m)  for( OBResidueIter     r(m); r; ++r )
146
      #define FOR_ATOMS_OF_RESIDUE(a,r) for( OBResidueAtomIter a(r); a; ++a )
147
      ...
148
      \endcode
149
150
      These convenience functions can be used like so:
151
      \code
152
      #include <openbabel/obiter.h>
153
      #include <openbabel/mol.h>
154
155
      OBMol mol;
156
      double exactMass = 0.0;
157
      FOR_ATOMS_OF_MOL(a, mol)
158
      {
159
      exactMass +=  a->GetExactMass();
160
      }
161
      \endcode
162
163
      Note that with these convenience macros, the iterator "a" (or
164
      whichever name you pick) is declared for you -- you do not need to
165
      do it beforehand.
166
  */
167
168
  //
169
  // OBMol member functions
170
  //
171
  void  OBMol::SetTitle(const char *title)
172
2
  {
173
2
    _title = title;
174
2
    Trim(_title);
175
2
  }
176
177
  void  OBMol::SetTitle(std::string &title)
178
164
  {
179
164
    _title = title;
180
164
    Trim(_title);
181
164
  }
182
183
  const char *OBMol::GetTitle(bool replaceNewlines) const
184
19
  {
185
19
    if (!replaceNewlines || _title.find('\n')== string::npos )
186
19
      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
19
  }
198
199
  bool SortVVInt(const vector<int> &a,const vector<int> &b)
200
0
  {
201
0
    return(a.size() > b.size());
202
0
  }
203
204
  bool SortAtomZ(const pair<OBAtom*,double> &a, const pair<OBAtom*,double> &b)
205
0
  {
206
0
    return (a.second < b.second);
207
0
  }
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
0
  {
311
0
    if (!IsPeriodic())
312
0
      {
313
0
        return(CalcTorsionAngle(a->GetVector(),
314
0
                                b->GetVector(),
315
0
                                c->GetVector(),
316
0
                                d->GetVector()));
317
0
      }
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
0
  }
336
337
  void OBMol::ContigFragList(std::vector<std::vector<int> >&cfl)
338
0
  {
339
0
    int j;
340
0
    OBAtom *atom;
341
0
    OBBond *bond;
342
0
    vector<OBAtom*>::iterator i;
343
0
    vector<OBBond*>::iterator k;
344
0
    OBBitVec used,curr,next,frag;
345
0
    vector<int> tmp;
346
347
0
    used.Resize(NumAtoms()+1);
348
0
    curr.Resize(NumAtoms()+1);
349
0
    next.Resize(NumAtoms()+1);
350
0
    frag.Resize(NumAtoms()+1);
351
352
0
    while ((unsigned)used.CountBits() < NumAtoms())
353
0
      {
354
0
        curr.Clear();
355
0
        frag.Clear();
356
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
357
0
          if (!used.BitIsSet(atom->GetIdx()))
358
0
            {
359
0
              curr.SetBitOn(atom->GetIdx());
360
0
              break;
361
0
            }
362
363
0
        frag |= curr;
364
0
        while (!curr.IsEmpty())
365
0
          {
366
0
            next.Clear();
367
0
            for (j = curr.NextBit(-1);j != curr.EndBit();j = curr.NextBit(j))
368
0
              {
369
0
                atom = GetAtom(j);
370
0
                for (bond = atom->BeginBond(k);bond;bond = atom->NextBond(k))
371
0
                  if (!used.BitIsSet(bond->GetNbrAtomIdx(atom)))
372
0
                    next.SetBitOn(bond->GetNbrAtomIdx(atom));
373
0
              }
374
375
0
            used |= curr;
376
0
            used |= next;
377
0
            frag |= next;
378
0
            curr = next;
379
0
          }
380
381
0
        tmp.clear();
382
0
        frag.ToVecInt(tmp);
383
0
        cfl.push_back(tmp);
384
0
      }
385
386
0
    sort(cfl.begin(),cfl.end(),SortVVInt);
387
0
  }
388
389
  void OBMol::FindAngles()
390
0
  {
391
    //if already has data return
392
0
    if(HasData(OBGenericDataType::AngleData))
393
0
      return;
394
395
    //get new data and attach it to molecule
396
0
    OBAngleData *angles = new OBAngleData;
397
0
    angles->SetOrigin(perceived);
398
0
    SetData(angles);
399
400
0
    OBAngle angle;
401
0
    OBAtom *b;
402
0
    int unique_angle;
403
404
0
    unique_angle = 0;
405
406
0
    FOR_ATOMS_OF_MOL(atom, this) {
407
0
      if(atom->GetAtomicNum() == OBElements::Hydrogen)
408
0
        continue;
409
410
0
      b = (OBAtom*) &*atom;
411
412
0
      FOR_NBORS_OF_ATOM(a, b) {
413
0
        FOR_NBORS_OF_ATOM(c, b) {
414
0
          if(&*a == &*c) {
415
0
            unique_angle = 1;
416
0
            continue;
417
0
          }
418
419
0
          if (unique_angle) {
420
0
            angle.SetAtoms((OBAtom*)b, (OBAtom*)&*a, (OBAtom*)&*c);
421
0
            angles->SetData(angle);
422
0
            angle.Clear();
423
0
          }
424
0
        }
425
0
        unique_angle = 0;
426
0
      }
427
0
    }
428
429
0
    return;
430
0
  }
431
432
  void OBMol::FindTorsions()
433
0
  {
434
    //if already has data return
435
0
    if(HasData(OBGenericDataType::TorsionData))
436
0
      return;
437
438
    //get new data and attach it to molecule
439
0
    OBTorsionData *torsions = new OBTorsionData;
440
0
    torsions->SetOrigin(perceived);
441
0
    SetData(torsions);
442
443
0
    OBTorsion torsion;
444
0
    vector<OBBond*>::iterator bi1,bi2,bi3;
445
0
    OBBond* bond;
446
0
    OBAtom *a,*b,*c,*d;
447
448
    //loop through all bonds generating torsions
449
0
    for(bond = BeginBond(bi1);bond;bond = NextBond(bi1))
450
0
      {
451
0
        b = bond->GetBeginAtom();
452
0
        c = bond->GetEndAtom();
453
0
        if(b->GetAtomicNum() == OBElements::Hydrogen || c->GetAtomicNum() == OBElements::Hydrogen)
454
0
          continue;
455
456
0
        for(a = b->BeginNbrAtom(bi2);a;a = b->NextNbrAtom(bi2))
457
0
          {
458
0
            if(a == c)
459
0
              continue;
460
461
0
            for(d = c->BeginNbrAtom(bi3);d;d = c->NextNbrAtom(bi3))
462
0
              {
463
0
                if ((d == b) || (d == a))
464
0
                  continue;
465
0
                torsion.AddTorsion(a,b,c,d);
466
0
              }
467
0
          }
468
        //add torsion to torsionData
469
0
        if(torsion.GetSize())
470
0
          torsions->SetData(torsion);
471
0
        torsion.Clear();
472
0
      }
473
474
0
    return;
475
0
  }
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
0
  {
608
0
    gtd.clear();
609
0
    gtd.resize(NumAtoms());
610
611
0
    int gtdcount,natom;
612
0
    OBBitVec used,curr,next;
613
0
    OBAtom *atom,*atom1;
614
0
    OBBond *bond;
615
0
    vector<OBAtom*>::iterator i;
616
0
    vector<OBBond*>::iterator j;
617
618
0
    next.Clear();
619
620
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
621
0
      {
622
0
        gtdcount = 0;
623
0
        used.Clear();
624
0
        curr.Clear();
625
0
        used.SetBitOn(atom->GetIdx());
626
0
        curr.SetBitOn(atom->GetIdx());
627
628
0
        while (!curr.IsEmpty())
629
0
          {
630
0
            next.Clear();
631
0
            for (natom = curr.NextBit(-1);natom != curr.EndBit();natom = curr.NextBit(natom))
632
0
              {
633
0
                atom1 = GetAtom(natom);
634
0
                for (bond = atom1->BeginBond(j);bond;bond = atom1->NextBond(j))
635
0
                  if (!used.BitIsSet(bond->GetNbrAtomIdx(atom1)) && !curr.BitIsSet(bond->GetNbrAtomIdx(atom1)))
636
0
                    if (bond->GetNbrAtom(atom1)->GetAtomicNum() != OBElements::Hydrogen)
637
0
                      next.SetBitOn(bond->GetNbrAtomIdx(atom1));
638
0
              }
639
640
0
            used |= next;
641
0
            curr = next;
642
0
            gtdcount++;
643
0
          }
644
0
        gtd[atom->GetIdx()-1] = gtdcount;
645
0
      }
646
0
    return(true);
647
0
  }
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
0
  {
808
0
    const OBAtom *atom;
809
0
    vector<OBAtom*>::const_iterator(i);
810
0
    unsigned int count = 0;
811
812
0
    for(atom = this->BeginAtom(i); atom; atom = this->NextAtom(i))
813
0
      {
814
0
        if (atom->GetAtomicNum() != OBElements::Hydrogen)
815
0
          count++;
816
0
      }
817
818
0
    return(count);
819
0
  }
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
1.25M
  {
832
1.25M
    if ((unsigned)idx < 1 || (unsigned)idx > NumAtoms())
833
7.89k
      {
834
7.89k
        obErrorLog.ThrowError(__FUNCTION__, "Requested Atom Out of Range", obDebug);
835
7.89k
        return nullptr;
836
7.89k
      }
837
838
1.24M
    return((OBAtom*)_vatom[idx-1]);
839
1.25M
  }
840
841
  OBAtom *OBMol::GetAtomById(unsigned long id) const
842
0
  {
843
0
    if (id >= _atomIds.size()) {
844
0
      obErrorLog.ThrowError(__FUNCTION__, "Requested atom with invalid id.", obDebug);
845
0
      return nullptr;
846
0
    }
847
848
0
    return((OBAtom*)_atomIds[id]);
849
0
  }
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
34
  {
860
34
    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
34
    return((OBBond*)_vbond[idx]);
867
34
  }
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
93
  {
881
93
    return(GetBond(GetAtom(bgn),GetAtom(end)));
882
93
  }
883
884
  OBBond *OBMol::GetBond(OBAtom *bgn,OBAtom *end) const
885
93
  {
886
93
    OBAtom *nbr;
887
93
    vector<OBBond*>::iterator i;
888
889
93
    if (!bgn || !end) return nullptr;
890
891
148
    for (nbr = bgn->BeginNbrAtom(i);nbr;nbr = bgn->NextNbrAtom(i))
892
55
      if (nbr == end)
893
0
        return((OBBond *)*i);
894
895
93
    return nullptr; //just to keep the SGI compiler happy
896
93
  }
897
898
  OBResidue *OBMol::GetResidue(int idx) const
899
0
  {
900
0
    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
0
    return (_residue[idx]);
907
0
  }
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
176
  {
926
176
    if (!HasSSSRPerceived())
927
110
      FindSSSR();
928
929
176
    OBRingData *rd = nullptr;
930
176
    if (!HasData("SSSR")) {
931
108
      rd = new OBRingData();
932
108
      rd->SetAttribute("SSSR");
933
108
      SetData(rd);
934
108
    }
935
936
176
    rd = (OBRingData *) GetData("SSSR");
937
176
    rd->SetOrigin(perceived);
938
176
    return(rd->GetData());
939
176
  }
940
941
  vector<OBRing*> &OBMol::GetLSSR()
942
0
  {
943
0
    if (!HasLSSRPerceived())
944
0
      FindLSSR();
945
946
0
    OBRingData *rd = nullptr;
947
0
    if (!HasData("LSSR")) {
948
0
      rd = new OBRingData();
949
0
      rd->SetAttribute("LSSR");
950
0
      SetData(rd);
951
0
    }
952
953
0
    rd = (OBRingData *) GetData("LSSR");
954
0
    rd->SetOrigin(perceived);
955
0
    return(rd->GetData());
956
0
  }
957
958
  double OBMol::GetMolWt(bool implicitH)
959
0
  {
960
0
    double molwt=0.0;
961
0
    OBAtom *atom;
962
0
    vector<OBAtom*>::iterator i;
963
964
0
    double hmass = OBElements::GetMass(1);
965
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i)) {
966
0
      molwt += atom->GetAtomicMass();
967
0
      if (implicitH)
968
0
        molwt += atom->GetImplicitHCount() * hmass;
969
0
    }
970
0
    return(molwt);
971
0
  }
972
973
  double OBMol::GetExactMass(bool implicitH)
974
0
  {
975
0
    double mass=0.0;
976
0
    OBAtom *atom;
977
0
    vector<OBAtom*>::iterator i;
978
979
0
    double hmass = OBElements::GetExactMass(1, 1);
980
0
    for (atom = BeginAtom(i); atom; atom = NextAtom(i)) {
981
0
      mass += atom->GetExactMass();
982
0
      if (implicitH)
983
0
        mass += atom->GetImplicitHCount() * hmass;
984
0
    }
985
986
0
    return(mass);
987
0
  }
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
0
  {
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
0
    const int NumElements = 118 + 2;
999
0
    const int alphabetical[NumElements] = {
1000
0
      89, 47, 13, 95, 18, 33, 85, 79, 5, 56, 4, 107, 83, 97, 35, 6, 20, 48,
1001
0
      58, 98, 17, 96, 112, 27, 24, 55, 29, NumElements-1,
1002
0
      105, 110, 66, 68, 99, 63, 9, 26, 114, 100, 87, 31,
1003
0
      64, 32, 1, 2, 72, 80, 67, 108, 53, 49, 77, 19, 36, 57, 3, 103, 71, 116, 115, 101,
1004
0
      12, 25, 42, 109, 7, 11, 41, 60, 10, 113, 28, 102, 93, 8, 118, 76, 15, 91, 82, 46,
1005
0
      61, 84, 59, 78, 94, 88, 37, 75, 104, 111, 45, 86, 44, 16, 51, 21, 34, 106, 14,
1006
0
      62, 50, 38, NumElements, 73, 65, 43, 52, 90, 22, 81, 69, 117, 92, 23, 74, 54, 39, 70,
1007
0
      30, 40 };
1008
1009
0
    int atomicCount[NumElements];
1010
0
    stringstream formula;
1011
1012
0
    for (int i = 0; i < NumElements; ++i)
1013
0
      atomicCount[i] = 0;
1014
1015
0
    bool UseImplicitH = (NumBonds()!=0 || NumAtoms()==1);
1016
    // Do not use implicit hydrogens if explicitly required not to
1017
0
    if (!implicitH) UseImplicitH = false;
1018
0
    bool HasHvyAtoms = NumHvyAtoms()>0;
1019
0
    FOR_ATOMS_OF_MOL(a, *this)
1020
0
      {
1021
0
        int anum = a->GetAtomicNum();
1022
0
        if(anum==0)
1023
0
          continue;
1024
0
        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
0
        bool IsHiso = anum == 1 && a->GetIsotope()>=2;
1031
0
        if(UseImplicitH)
1032
0
          {
1033
0
            if (anum == 1 && !IsHiso && HasHvyAtoms)
1034
0
              continue; // skip explicit hydrogens except D,T
1035
0
            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
0
            else
1041
0
              atomicCount[0] += a->GetImplicitHCount() + a->ExplicitHydrogenCount();
1042
0
          }
1043
0
        if (IsHiso)
1044
0
          anum = NumElements + a->GetIsotope() - 3; //pseudo AtNo for D, T
1045
0
        atomicCount[anum - 1]++;
1046
0
      }
1047
1048
0
    if (atomicCount[5] != 0) // Carbon (i.e. 6 - 1 = 5)
1049
0
      {
1050
0
        if (atomicCount[5] > ones)
1051
0
          formula << "C" << sp << atomicCount[5] << sp;
1052
0
        else if (atomicCount[5] == 1)
1053
0
          formula << "C";
1054
1055
0
        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
0
        if (atomicCount[0] != 0) // Hydrogen (i.e., 1 - 1 = 0)
1059
0
          {
1060
0
            if (atomicCount[0] > ones)
1061
0
              formula << "H" << sp << atomicCount[0] << sp;
1062
0
            else if (atomicCount[0] == 1)
1063
0
              formula << "H";
1064
1065
0
            atomicCount[0] = 0;
1066
0
          }
1067
0
      }
1068
1069
0
    for (int j = 0; j < NumElements; ++j)
1070
0
      {
1071
0
        char DT[4] = {'D',0,'T',0};
1072
0
        const char* symb;
1073
0
        int alph = alphabetical[j]-1;
1074
0
        if (atomicCount[ alph ])
1075
0
          {
1076
0
            if(alph==NumElements-1)
1077
0
              symb = DT + 2;//T
1078
0
            else if (alph==NumElements-2)
1079
0
              symb = DT; //D
1080
0
            else
1081
0
              symb = OBElements::GetSymbol(alphabetical[j]);
1082
1083
0
            formula << symb << sp;
1084
0
            if(atomicCount[alph] > ones)
1085
0
              formula << atomicCount[alph] << sp;
1086
0
          }
1087
0
      }
1088
1089
0
    int chg = GetTotalCharge();
1090
0
    char ch = chg>0 ? '+' : '-' ;
1091
0
    chg = abs(chg);
1092
0
    while(chg--)
1093
0
      formula << ch << sp;
1094
1095
0
    string f_str = formula.str();
1096
0
    return (Trim(f_str));
1097
0
  }
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
0
  {
1105
0
    string attr = "Formula";
1106
0
    OBPairData *dp = (OBPairData *) GetData(attr);
1107
1108
0
    if (dp != nullptr) // we already set the formula (or it was read from a file)
1109
0
      return dp->GetValue();
1110
1111
0
    obErrorLog.ThrowError(__FUNCTION__,
1112
0
                          "Ran OpenBabel::SetFormula -- Hill order formula",
1113
0
                          obAuditMsg);
1114
1115
0
    string sformula = GetSpacedFormula(1, "");
1116
1117
0
    dp = new OBPairData;
1118
0
    dp->SetAttribute(attr);
1119
0
    dp->SetValue( sformula );
1120
0
    dp->SetOrigin( perceived ); // internal generation
1121
0
    SetData(dp);
1122
0
    return sformula;
1123
0
  }
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
0
  {
1142
0
    SetFlag(OB_TCHARGE_MOL);
1143
0
    _totalCharge = charge;
1144
0
  }
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
0
  {
1153
0
    if(HasFlag(OB_TCHARGE_MOL))
1154
0
      return(_totalCharge);
1155
0
    else // calculate from atomic formal charges (seems the best default)
1156
0
      {
1157
0
        obErrorLog.ThrowError(__FUNCTION__,
1158
0
                              "Ran OpenBabel::GetTotalCharge -- calculated from formal charges",
1159
0
                              obAuditMsg);
1160
1161
0
        OBAtom *atom;
1162
0
        vector<OBAtom*>::iterator i;
1163
0
        int chg = 0;
1164
1165
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1166
0
          chg += atom->GetFormalCharge();
1167
0
        return (chg);
1168
0
      }
1169
0
  }
1170
1171
  void   OBMol::SetTotalSpinMultiplicity(unsigned int spin)
1172
0
  {
1173
0
    SetFlag(OB_TSPIN_MOL);
1174
0
    _totalSpin = spin;
1175
0
  }
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
0
  {
1203
0
    if (HasFlag(OB_TSPIN_MOL))
1204
0
      return(_totalSpin);
1205
0
    else // calculate from atomic spin information (assuming high-spin case)
1206
0
      {
1207
0
        obErrorLog.ThrowError(__FUNCTION__,
1208
0
                              "Ran OpenBabel::GetTotalSpinMultiplicity -- calculating from atomic spins assuming high spin case",
1209
0
                              obAuditMsg);
1210
1211
0
        OBAtom *atom;
1212
0
        vector<OBAtom*>::iterator i;
1213
0
        unsigned int unpaired_electrons = 0;
1214
0
        int chg = GetTotalCharge();
1215
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1216
0
          {
1217
0
            if (atom->GetSpinMultiplicity() > 1)
1218
0
              unpaired_electrons += (atom->GetSpinMultiplicity() - 1);
1219
0
           chg += atom->GetAtomicNum();
1220
0
          }
1221
0
        if (chg % 2 != unpaired_electrons %2)
1222
0
          return ((abs(chg) % 2) + 1);
1223
0
        else
1224
0
          return (unpaired_electrons + 1);
1225
0
      }
1226
0
  }
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
0
  {
1236
0
    if (this == &source)
1237
0
      return *this;
1238
1239
0
    OBMol &src = (OBMol &)source;
1240
0
    vector<OBAtom*>::iterator i;
1241
0
    vector<OBBond*>::iterator j;
1242
0
    OBAtom *atom;
1243
0
    OBBond *bond;
1244
1245
0
    Clear();
1246
0
    BeginModify();
1247
1248
0
    _vatom.reserve(src.NumAtoms());
1249
0
    _atomIds.reserve(src.NumAtoms());
1250
0
    _vbond.reserve(src.NumBonds());
1251
0
    _bondIds.reserve(src.NumBonds());
1252
1253
0
    for (atom = src.BeginAtom(i);atom;atom = src.NextAtom(i))
1254
0
      AddAtom(*atom);
1255
0
    for (bond = src.BeginBond(j);bond;bond = src.NextBond(j))
1256
0
      AddBond(*bond);
1257
1258
0
    this->_title  = src.GetTitle();
1259
0
    this->_energy = src.GetEnergy();
1260
0
    this->_dimension = src.GetDimension();
1261
0
    this->SetTotalCharge(src.GetTotalCharge()); //also sets a flag
1262
0
    this->SetTotalSpinMultiplicity(src.GetTotalSpinMultiplicity()); //also sets a flag
1263
1264
0
    EndModify(); //zeros flags!
1265
1266
0
    if (src.HasFlag(OB_PATTERN_STRUCTURE))
1267
0
      this->SetFlag(OB_PATTERN_STRUCTURE);
1268
0
    if (src.HasFlag(OB_TSPIN_MOL))
1269
0
      this->SetFlag(OB_TSPIN_MOL);
1270
0
    if (src.HasFlag(OB_TCHARGE_MOL))
1271
0
      this->SetFlag(OB_TCHARGE_MOL);
1272
0
    if (src.HasFlag(OB_PCHARGE_MOL))
1273
0
      this->SetFlag(OB_PCHARGE_MOL);
1274
0
    if (src.HasFlag(OB_PERIODIC_MOL))
1275
0
      this->SetFlag(OB_PERIODIC_MOL);
1276
0
    if (src.HasFlag(OB_HYBRID_MOL))
1277
0
      this->SetFlag(OB_HYBRID_MOL);
1278
0
    if (src.HasFlag(OB_AROMATIC_MOL))
1279
0
      this->SetFlag(OB_AROMATIC_MOL);
1280
0
    if (src.HasFlag(OB_CHAINS_MOL))
1281
0
      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
0
    unsigned int NumRes = src.NumResidues();
1287
0
    if (NumRes)
1288
0
      {
1289
0
        unsigned int k;
1290
0
        OBResidue *src_res = nullptr;
1291
0
        OBResidue *res = nullptr;
1292
0
        OBAtom *src_atom = nullptr;
1293
0
        OBAtom *atom = nullptr;
1294
0
        vector<OBAtom*>::iterator ii;
1295
0
        for (k=0 ; k<NumRes ; ++k)
1296
0
          {
1297
0
            res = NewResidue();
1298
0
            src_res = src.GetResidue(k);
1299
0
            *res = *src_res; //does not copy atoms
1300
0
            for (src_atom=src_res->BeginAtom(ii) ; src_atom ; src_atom=src_res->NextAtom(ii))
1301
0
              {
1302
0
                atom = GetAtom(src_atom->GetIdx());
1303
0
                res->AddAtom(atom);
1304
0
                res->SetAtomID(atom,src_res->GetAtomID(src_atom));
1305
0
                res->SetHetAtom(atom,src_res->IsHetAtom(src_atom));
1306
0
                res->SetSerialNum(atom,src_res->GetSerialNum(src_atom));
1307
0
              }
1308
0
          }
1309
0
      }
1310
1311
    //Copy conformer information
1312
0
    if (src.NumConformers() > 1) {
1313
0
      int k;//,l;
1314
0
      vector<double*> conf;
1315
0
      int currConf = -1;
1316
0
      double* xyz = nullptr;
1317
0
      for (k=0 ; k<src.NumConformers() ; ++k) {
1318
0
        xyz = new double [3*src.NumAtoms()];
1319
0
        memcpy( xyz, src.GetConformer(k), sizeof( double )*3*src.NumAtoms() );
1320
0
        conf.push_back(xyz);
1321
1322
0
        if( src.GetConformer(k) == src._c ) {
1323
0
          currConf = k;
1324
0
        }
1325
0
      }
1326
1327
0
      SetConformers(conf);
1328
0
      if( currConf >= 0 && _vconf.size() ) {
1329
0
        _c = _vconf[currConf];
1330
0
      }
1331
0
    }
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
0
    vector<OBGenericData*>::iterator itr;
1337
0
    for(itr=src.BeginData();itr!=src.EndData();++itr)
1338
0
      {
1339
0
        OBGenericData* pCopiedData = (*itr)->Clone(this);
1340
0
        SetData(pCopiedData);
1341
0
      }
1342
1343
0
    if (src.HasChiralityPerceived())
1344
0
      SetChiralityPerceived();
1345
1346
0
    return(*this);
1347
0
  }
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
167
  {
1440
167
    if (obErrorLog.GetOutputLevel() >= obAuditMsg)
1441
0
      obErrorLog.ThrowError(__FUNCTION__,
1442
0
                            "Ran OpenBabel::Clear Molecule", obAuditMsg);
1443
1444
167
    vector<OBAtom*>::iterator i;
1445
167
    vector<OBBond*>::iterator j;
1446
167
    for (i = _vatom.begin();i != _vatom.end();++i)
1447
0
      {
1448
0
        DestroyAtom(*i);
1449
0
        *i = nullptr;
1450
0
      }
1451
167
    for (j = _vbond.begin();j != _vbond.end();++j)
1452
0
      {
1453
0
        DestroyBond(*j);
1454
0
        *j = nullptr;
1455
0
      }
1456
1457
167
    _atomIds.clear();
1458
167
    _bondIds.clear();
1459
167
    _natoms = _nbonds = 0;
1460
1461
    //Delete residues
1462
167
    unsigned int ii;
1463
167
    for (ii=0 ; ii<_residue.size() ; ++ii)
1464
0
      {
1465
0
        DestroyResidue(_residue[ii]);
1466
0
      }
1467
167
    _residue.clear();
1468
1469
    //clear out the multiconformer data
1470
167
    vector<double*>::iterator k;
1471
167
    for (k = _vconf.begin();k != _vconf.end();++k)
1472
0
      delete [] *k;
1473
167
    _vconf.clear();
1474
1475
    //Clear flags except OB_PATTERN_STRUCTURE which is left the same
1476
167
    _flags &= OB_PATTERN_STRUCTURE;
1477
1478
167
    _c = nullptr;
1479
167
    _mod = 0;
1480
1481
    // Clean up generic data via the base class
1482
167
    return(OBBase::Clear());
1483
167
  }
1484
1485
  void OBMol::BeginModify()
1486
151
  {
1487
    //suck coordinates from _c into _v for each atom
1488
151
    if (!_mod && !Empty())
1489
0
      {
1490
0
        OBAtom *atom;
1491
0
        vector<OBAtom*>::iterator i;
1492
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1493
0
          {
1494
0
            atom->SetVector();
1495
0
            atom->ClearCoordPtr();
1496
0
          }
1497
1498
0
        vector<double*>::iterator j;
1499
0
        for (j = _vconf.begin();j != _vconf.end();++j)
1500
0
          delete [] *j;
1501
1502
0
        _c = nullptr;
1503
0
        _vconf.clear();
1504
1505
        //Destroy rotamer list if necessary
1506
0
        if ((OBRotamerList *)GetData(OBGenericDataType::RotamerList))
1507
0
          {
1508
0
            delete (OBRotamerList *)GetData(OBGenericDataType::RotamerList);
1509
0
            DeleteData(OBGenericDataType::RotamerList);
1510
0
          }
1511
0
      }
1512
1513
151
    _mod++;
1514
151
  }
1515
1516
  void OBMol::EndModify(bool nukePerceivedData)
1517
110
  {
1518
110
    if (_mod == 0)
1519
0
      {
1520
0
        obErrorLog.ThrowError(__FUNCTION__, "_mod is negative - EndModify() called too many times", obDebug);
1521
0
        return;
1522
0
      }
1523
1524
110
    _mod--;
1525
1526
110
    if (_mod)
1527
0
      return;
1528
1529
    // wipe all but whether it has aromaticity perceived, is a reaction, or has periodic boundaries enabled
1530
110
    if (nukePerceivedData)
1531
110
      _flags = _flags & (OB_AROMATIC_MOL|OB_REACTION_MOL|OB_PERIODIC_MOL);
1532
1533
110
    _c = nullptr;
1534
1535
110
    if (Empty())
1536
44
      return;
1537
1538
    //if atoms present convert coords into array
1539
66
    double *c = new double [NumAtoms()*3];
1540
66
    _c = c;
1541
1542
66
    unsigned int idx;
1543
66
    OBAtom *atom;
1544
66
    vector<OBAtom*>::iterator j;
1545
475k
    for (idx=0,atom = BeginAtom(j);atom;atom = NextAtom(j),++idx)
1546
475k
      {
1547
475k
        atom->SetIdx(idx+1);
1548
475k
        (atom->GetVector()).Get(&_c[idx*3]);
1549
475k
        atom->SetCoordPtr(&_c);
1550
475k
      }
1551
66
    _vconf.push_back(c);
1552
1553
    // Always remove angle and torsion data, since they will interfere with the iterators
1554
    // PR#2812013
1555
66
    DeleteData(OBGenericDataType::AngleData);
1556
66
    DeleteData(OBGenericDataType::TorsionData);
1557
66
  }
1558
1559
  void OBMol::DestroyAtom(OBAtom *atom)
1560
479k
  {
1561
479k
    if (atom)
1562
479k
      {
1563
479k
        delete atom;
1564
479k
        atom = nullptr;
1565
479k
      }
1566
479k
  }
1567
1568
  void OBMol::DestroyBond(OBBond *bond)
1569
93
  {
1570
93
    if (bond)
1571
93
      {
1572
93
        delete bond;
1573
93
        bond = nullptr;
1574
93
      }
1575
93
  }
1576
1577
  void OBMol::DestroyResidue(OBResidue *residue)
1578
0
  {
1579
0
    if (residue)
1580
0
      {
1581
0
        delete residue;
1582
0
        residue = nullptr;
1583
0
      }
1584
0
  }
1585
1586
  OBAtom *OBMol::NewAtom()
1587
4.06k
  {
1588
4.06k
    return NewAtom(_atomIds.size());
1589
4.06k
  }
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
4.06k
  {
1597
    //   BeginModify();
1598
1599
    // resize _atomIds if needed
1600
4.06k
    if (id >= _atomIds.size()) {
1601
4.06k
      unsigned int size = _atomIds.size();
1602
4.06k
      _atomIds.resize(id+1);
1603
4.06k
      for (unsigned long i = size; i < id; ++i)
1604
0
        _atomIds[i] = nullptr;
1605
4.06k
    }
1606
1607
4.06k
    if (_atomIds.at(id))
1608
0
      return nullptr;
1609
1610
4.06k
    OBAtom *obatom = new OBAtom;
1611
4.06k
    obatom->SetIdx(_natoms+1);
1612
4.06k
    obatom->SetParent(this);
1613
1614
4.06k
    _atomIds[id] = obatom;
1615
4.06k
    obatom->SetId(id);
1616
1617
4.06k
#define OBAtomIncrement 100
1618
1619
4.06k
    if (_natoms+1 >= _vatom.size())
1620
84
      {
1621
84
        _vatom.resize(_natoms+OBAtomIncrement);
1622
84
        vector<OBAtom*>::iterator j;
1623
8.40k
        for (j = _vatom.begin(),j+=(_natoms+1);j != _vatom.end();++j)
1624
8.31k
          *j = nullptr;
1625
84
      }
1626
4.06k
#undef OBAtomIncrement
1627
1628
1629
4.06k
    _vatom[_natoms] = obatom;
1630
4.06k
    _natoms++;
1631
1632
4.06k
    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
4.06k
    return(obatom);
1659
4.06k
  }
1660
1661
  OBResidue *OBMol::NewResidue()
1662
0
  {
1663
0
    OBResidue *obresidue = new OBResidue;
1664
0
    obresidue->SetIdx(_residue.size());
1665
0
    _residue.push_back(obresidue);
1666
0
    return(obresidue);
1667
0
  }
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
475k
  {
1719
    //    BeginModify();
1720
1721
    // Use the existing atom Id unless either it's invalid or forceNewId has been specified
1722
475k
    unsigned long id;
1723
475k
    if (forceNewId)
1724
0
      id = _atomIds.size();
1725
475k
    else {
1726
475k
      id = atom.GetId();
1727
475k
      if (id == NoId)
1728
475k
        id = _atomIds.size();
1729
475k
    }
1730
1731
475k
    OBAtom *obatom = new OBAtom;
1732
475k
    *obatom = atom;
1733
475k
    obatom->SetIdx(_natoms+1);
1734
475k
    obatom->SetParent(this);
1735
1736
    // resize _atomIds if needed
1737
475k
    if (id >= _atomIds.size()) {
1738
475k
      unsigned int size = _atomIds.size();
1739
475k
      _atomIds.resize(id+1);
1740
475k
      for (unsigned long i = size; i < id; ++i)
1741
0
        _atomIds[i] = nullptr;
1742
475k
    }
1743
1744
475k
    obatom->SetId(id);
1745
475k
    _atomIds[id] = obatom;
1746
1747
475k
#define OBAtomIncrement 100
1748
1749
475k
    if (_natoms+1 >= _vatom.size())
1750
4.81k
      {
1751
4.81k
        _vatom.resize(_natoms+OBAtomIncrement);
1752
4.81k
        vector<OBAtom*>::iterator j;
1753
481k
        for (j = _vatom.begin(),j+=(_natoms+1);j != _vatom.end();++j)
1754
476k
          *j = nullptr;
1755
4.81k
      }
1756
475k
#undef OBAtomIncrement
1757
1758
475k
    _vatom[_natoms] = (OBAtom*)obatom;
1759
475k
    _natoms++;
1760
1761
475k
    if (HasData(OBGenericDataType::VirtualBondData))
1762
0
      {
1763
        /*add bonds that have been queued*/
1764
0
        OBVirtualBond *vb;
1765
0
        vector<OBGenericData*> verase;
1766
0
        vector<OBGenericData*>::iterator i;
1767
0
        for (i = BeginData();i != EndData();++i)
1768
0
          if ((*i)->GetDataType() == OBGenericDataType::VirtualBondData)
1769
0
            {
1770
0
              vb = (OBVirtualBond*)*i;
1771
0
              if (vb->GetBgn() > _natoms || vb->GetEnd() > _natoms)
1772
0
                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
0
        if (!verase.empty())
1782
0
          DeleteData(verase);
1783
0
      }
1784
1785
    //    EndModify();
1786
1787
475k
    return(true);
1788
475k
  }
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
0
  {
1912
0
    OBAtom *atom;
1913
0
    vector<OBAtom*>::iterator i;
1914
0
    vector<OBAtom*> delatoms;
1915
1916
0
    obErrorLog.ThrowError(__FUNCTION__,
1917
0
                          "Ran OpenBabel::DeleteHydrogens -- nonpolar",
1918
0
                          obAuditMsg);
1919
1920
1921
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1922
0
      if (atom->IsNonPolarHydrogen() && IsSuppressibleHydrogen(atom))
1923
0
        delatoms.push_back(atom);
1924
1925
0
    if (delatoms.empty())
1926
0
      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
0
  }
1951
1952
  bool OBMol::DeleteHydrogens()
1953
0
  {
1954
0
    OBAtom *atom;//,*nbr;
1955
0
    vector<OBAtom*>::iterator i;
1956
0
    vector<OBAtom*> delatoms,va;
1957
1958
0
    obErrorLog.ThrowError(__FUNCTION__,
1959
0
                          "Ran OpenBabel::DeleteHydrogens", obAuditMsg);
1960
1961
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
1962
0
      if (atom->GetAtomicNum() == OBElements::Hydrogen && IsSuppressibleHydrogen(atom))
1963
0
        delatoms.push_back(atom);
1964
1965
0
    SetHydrogensAdded(false);
1966
1967
0
    if (delatoms.empty())
1968
0
      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
0
  }
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
0
  {
2085
0
    return(AddNewHydrogens(polaronly ? PolarHydrogen : AllHydrogen, correctForPH, pH));
2086
0
  }
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
0
  {
2105
0
    double rad = OBElements::GetCovalentRad(elem);
2106
0
    switch (hyb) {
2107
0
    case 2:
2108
0
      return rad * 0.95;
2109
0
    case 1:
2110
0
      return rad * 0.90;
2111
0
    default:
2112
0
      return rad;
2113
0
    }
2114
0
  }
2115
2116
  bool OBMol::AddNewHydrogens(HydrogenType whichHydrogen, bool correctForPH, double pH)
2117
0
  {
2118
0
    if (!IsCorrectedForPH() && correctForPH)
2119
0
      CorrectForPH(pH);
2120
2121
0
    if (HasHydrogensAdded())
2122
0
      return(true);
2123
2124
0
    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
0
    if (whichHydrogen == AllHydrogen)
2138
0
      obErrorLog.ThrowError(__FUNCTION__,
2139
0
                            "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
0
    if (!_vconf.empty() && !Empty()) {
2149
0
      OBAtom *atom;
2150
0
      vector<OBAtom*>::iterator i;
2151
0
      for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2152
0
        {
2153
0
          atom->SetVector();
2154
0
        }
2155
0
    }
2156
2157
0
    SetHydrogensAdded(); // This must come after EndModify() as EndModify() wipes the flags
2158
    // If chirality was already perceived, remember this (to avoid wiping information
2159
0
    if (hasChiralityPerceived)
2160
0
      this->SetChiralityPerceived();
2161
2162
    //count up number of hydrogens to add
2163
0
    OBAtom *atom,*h;
2164
0
    int hcount,count=0;
2165
0
    vector<pair<OBAtom*,int> > vhadd;
2166
0
    vector<OBAtom*>::iterator i;
2167
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2168
0
      {
2169
0
        if (whichHydrogen == PolarHydrogen && !AtomIsNSOP(atom))
2170
0
          continue;
2171
0
        if (whichHydrogen == NonPolarHydrogen && AtomIsNSOP(atom))
2172
0
          continue;
2173
2174
0
        hcount = atom->GetImplicitHCount();
2175
0
        atom->SetImplicitHCount(0);
2176
2177
0
        if (hcount)
2178
0
          {
2179
0
            vhadd.push_back(pair<OBAtom*,int>(atom,hcount));
2180
0
            count += hcount;
2181
0
          }
2182
0
      }
2183
2184
0
    if (count == 0) {
2185
      // Make sure to clear SSSR and aromatic flags we may have tripped above
2186
0
      _flags &= (~(OB_SSSR_MOL|OB_AROMATIC_MOL));
2187
0
      return(true);
2188
0
    }
2189
0
    bool hasCoords = HasNonZeroCoords();
2190
2191
    //realloc memory in coordinate arrays for new hydrogens
2192
0
    double *tmpf;
2193
0
    vector<double*>::iterator j;
2194
0
    for (j = _vconf.begin();j != _vconf.end();++j)
2195
0
      {
2196
0
        tmpf = new double [(NumAtoms()+count)*3];
2197
0
        memset(tmpf,'\0',sizeof(double)*(NumAtoms()+count)*3);
2198
0
        if (hasCoords)
2199
0
          memcpy(tmpf,(*j),sizeof(double)*NumAtoms()*3);
2200
0
        delete []*j;
2201
0
        *j = tmpf;
2202
0
      }
2203
2204
0
    IncrementMod();
2205
2206
0
    int m,n;
2207
0
    vector3 v;
2208
0
    vector<pair<OBAtom*,int> >::iterator k;
2209
0
    double hbrad = CorrectedBondRad(1, 0);
2210
2211
0
    for (k = vhadd.begin();k != vhadd.end();++k)
2212
0
      {
2213
0
        atom = k->first;
2214
0
        double bondlen = hbrad + CorrectedBondRad(atom->GetAtomicNum(), atom->GetHyb());
2215
0
        for (m = 0;m < k->second;++m)
2216
0
          {
2217
0
            int badh = 0;
2218
0
            for (n = 0;n < NumConformers();++n)
2219
0
              {
2220
0
                SetConformer(n);
2221
0
                if (hasCoords)
2222
0
                  {
2223
                    // Ensure that add hydrogens only returns finite coords
2224
                    //atom->GetNewBondVector(v,bondlen);
2225
0
                    v = OBBuilder::GetNewBondVector(atom,bondlen);
2226
0
                    if (isfinite(v.x()) || isfinite(v.y()) || isfinite(v.z())) {
2227
0
                      _c[(NumAtoms())*3]   = v.x();
2228
0
                      _c[(NumAtoms())*3+1] = v.y();
2229
0
                      _c[(NumAtoms())*3+2] = v.z();
2230
0
                    }
2231
0
                    else {
2232
0
                      _c[(NumAtoms())*3]   = 0.0;
2233
0
                      _c[(NumAtoms())*3+1] = 0.0;
2234
0
                      _c[(NumAtoms())*3+2] = 0.0;
2235
0
                      obErrorLog.ThrowError(__FUNCTION__,
2236
0
                                            "Ran OpenBabel::AddHydrogens -- no reasonable bond geometry for desired hydrogen.",
2237
0
                                            obAuditMsg);
2238
0
                      badh++;
2239
0
                    }
2240
0
                  }
2241
0
                else
2242
0
                  memset((char*)&_c[NumAtoms()*3],'\0',sizeof(double)*3);
2243
0
              }
2244
0
            if(badh == 0 || badh < NumConformers())
2245
0
              {
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
0
                OBResidue *res = atom->HasResidue() ? atom->GetResidue() : nullptr;
2251
0
                h = NewAtom();
2252
0
                h->SetType("H");
2253
0
                h->SetAtomicNum(1);
2254
0
                string aname = "H";
2255
2256
0
                if(res)
2257
0
                {
2258
0
                  res->AddAtom(h);
2259
0
                  res->SetAtomID(h,aname);
2260
2261
                  //hydrogen should inherit hetatm status of heteroatom (default is false)
2262
0
                  if(res->IsHetAtom(atom))
2263
0
                  {
2264
0
                      res->SetHetAtom(h, true);
2265
0
                  }
2266
0
                }
2267
2268
0
                int bondFlags = 0;
2269
0
                AddBond(atom->GetIdx(),h->GetIdx(),1, bondFlags);
2270
0
                if (_c) {
2271
0
                  h->SetCoordPtr(&_c);
2272
0
                }
2273
0
                OpenBabel::ImplicitRefToStereo(*this, atom->GetId(), h->GetId());
2274
0
              }
2275
0
          }
2276
0
      }
2277
2278
0
    DecrementMod();
2279
2280
    //reset atom type and partial charge flags
2281
0
    _flags &= (~(OB_PCHARGE_MOL|OB_ATOMTYPES_MOL|OB_SSSR_MOL|OB_AROMATIC_MOL|OB_HYBRID_MOL));
2282
2283
0
    return(true);
2284
0
  }
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
0
  {
2402
0
    std::vector<OBGenericData*> vdata = mol.GetAllData(OBGenericDataType::StereoData);
2403
0
    for (std::vector<OBGenericData*>::iterator data = vdata.begin(); data != vdata.end(); ++data) {
2404
0
      OBStereo::Type datatype = ((OBStereoBase*)*data)->GetType();
2405
2406
0
      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
0
      if (datatype == OBStereo::CisTrans) {
2413
0
        OBCisTransStereo *ct = dynamic_cast<OBCisTransStereo*>(*data);
2414
0
        OBCisTransStereo::Config ct_cfg = ct->GetConfig();
2415
0
        if (ct_cfg.begin == atomId || ct_cfg.end == atomId ||
2416
0
            std::find(ct_cfg.refs.begin(), ct_cfg.refs.end(), atomId) != ct_cfg.refs.end())
2417
0
          mol.DeleteData(ct);
2418
0
      }
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
0
    }
2427
0
  }
2428
2429
  bool OBMol::DeleteAtom(OBAtom *atom, bool destroyAtom)
2430
0
  {
2431
0
    if (atom->GetAtomicNum() == OBElements::Hydrogen)
2432
0
      return(DeleteHydrogen(atom));
2433
2434
0
    BeginModify();
2435
    //don't need to do anything with coordinates b/c
2436
    //BeginModify() blows away coordinates
2437
2438
    //find bonds to delete
2439
0
    OBAtom *nbr;
2440
0
    vector<OBBond*> vdb;
2441
0
    vector<OBBond*>::iterator j;
2442
0
    for (nbr = atom->BeginNbrAtom(j);nbr;nbr = atom->NextNbrAtom(j))
2443
0
      vdb.push_back(*j);
2444
2445
0
    for (j = vdb.begin();j != vdb.end();++j)
2446
0
      DeleteBond((OBBond *)*j); //delete bonds
2447
2448
0
    _atomIds[atom->GetId()] = nullptr;
2449
0
    _vatom.erase(_vatom.begin()+(atom->GetIdx()-1));
2450
0
    _natoms--;
2451
2452
    //reset all the indices to the atoms
2453
0
    int idx;
2454
0
    vector<OBAtom*>::iterator i;
2455
0
    OBAtom *atomi;
2456
0
    for (idx=1,atomi = BeginAtom(i);atomi;atomi = NextAtom(i),++idx)
2457
0
      atomi->SetIdx(idx);
2458
2459
0
    EndModify();
2460
2461
    // Delete any stereo objects involving this atom
2462
0
    OBStereo::Ref id = atom->GetId();
2463
0
    DeleteStereoOnAtom(*this, id);
2464
2465
0
    if (destroyAtom)
2466
0
      DestroyAtom(atom);
2467
2468
0
    SetSSSRPerceived(false);
2469
0
    SetLSSRPerceived(false);
2470
0
    return(true);
2471
0
  }
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
0
  {
2491
0
    BeginModify();
2492
2493
0
    (bond->GetBeginAtom())->DeleteBond(bond);
2494
0
    (bond->GetEndAtom())->DeleteBond(bond);
2495
0
    _bondIds[bond->GetId()] = nullptr;
2496
0
    _vbond.erase(_vbond.begin() + bond->GetIdx()); // bond index starts at 0!!!
2497
0
    _nbonds--;
2498
2499
0
    vector<OBBond*>::iterator i;
2500
0
    int j;
2501
0
    OBBond *bondi;
2502
0
    for (bondi = BeginBond(i),j=0;bondi;bondi = NextBond(i),++j)
2503
0
      bondi->SetIdx(j);
2504
2505
0
    EndModify();
2506
2507
0
    if (destroyBond)
2508
0
      DestroyBond(bond);
2509
2510
0
    SetSSSRPerceived(false);
2511
0
    SetLSSRPerceived(false);
2512
0
    return(true);
2513
0
  }
2514
2515
  bool OBMol::AddBond(int first,int second,int order,int flags,int insertpos)
2516
93
  {
2517
    // Don't add the bond if it already exists
2518
93
    if (first == second || GetBond(first, second) != nullptr)
2519
0
      return(false);
2520
2521
    //    BeginModify();
2522
2523
93
    if ((unsigned)first <= NumAtoms() && (unsigned)second <= NumAtoms())
2524
      //atoms exist and bond doesn't
2525
93
      {
2526
93
        OBBond *bond = new OBBond;
2527
93
        if (!bond)
2528
0
          {
2529
            //EndModify();
2530
0
            return(false);
2531
0
          }
2532
2533
93
        OBAtom *bgn,*end;
2534
93
        bgn = GetAtom(first);
2535
93
        end = GetAtom(second);
2536
93
        if (!bgn || !end)
2537
0
          {
2538
0
            obErrorLog.ThrowError(__FUNCTION__, "Unable to add bond - invalid atom index", obDebug);
2539
0
            return(false);
2540
0
          }
2541
93
        bond->Set(_nbonds,bgn,end,order,flags);
2542
93
        bond->SetParent(this);
2543
2544
93
        bond->SetId(_bondIds.size());
2545
93
        _bondIds.push_back(bond);
2546
2547
93
#define OBBondIncrement 100
2548
93
        if (_nbonds+1 >= _vbond.size())
2549
19
          {
2550
19
            _vbond.resize(_nbonds+OBBondIncrement);
2551
19
            vector<OBBond*>::iterator i;
2552
1.90k
            for (i = _vbond.begin(),i+=(_nbonds+1);i != _vbond.end();++i)
2553
1.88k
              *i = nullptr;
2554
19
          }
2555
93
#undef  OBBondIncrement
2556
2557
93
        _vbond[_nbonds] = (OBBond*)bond;
2558
93
        _nbonds++;
2559
2560
93
        if (insertpos == -1)
2561
93
          {
2562
93
            bgn->AddBond(bond);
2563
93
            end->AddBond(bond);
2564
93
          }
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
93
      }
2579
0
    else //at least one atom doesn't exist yet - add to bond_q
2580
0
      SetData(new OBVirtualBond(first,second,order,flags));
2581
2582
    //    EndModify();
2583
2584
93
    return(true);
2585
93
  }
2586
2587
  bool OBMol::AddBond(OBBond &bond)
2588
0
  {
2589
0
    if(!AddBond(bond.GetBeginAtomIdx(),
2590
0
                   bond.GetEndAtomIdx(),
2591
0
                   bond.GetBondOrder(),
2592
0
                   bond.GetFlags()))
2593
0
      return false;
2594
    //copy the bond's generic data
2595
0
    OBDataIterator diter;
2596
0
    for(diter=bond.BeginData(); diter!=bond.EndData();++diter)
2597
0
      GetBond(NumBonds()-1)->CloneData(*diter);
2598
0
    return true;
2599
0
  }
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
179
  {
2720
179
    _natoms = _nbonds = 0;
2721
179
    _mod = 0;
2722
179
    _totalCharge = 0;
2723
179
    _dimension = 3;
2724
179
    _vatom.clear();
2725
179
    _atomIds.clear();
2726
179
    _vbond.clear();
2727
179
    _bondIds.clear();
2728
179
    _vdata.clear();
2729
179
    _title = "";
2730
179
    _c = nullptr;
2731
179
    _flags = 0;
2732
179
    _vconf.clear();
2733
179
    _autoPartialCharge = true;
2734
179
    _autoFormalCharge = true;
2735
179
    _energy = 0.0;
2736
179
  }
2737
2738
0
  OBMol::OBMol(const OBMol &mol) : OBBase(mol)
2739
0
  {
2740
0
    _natoms = _nbonds = 0;
2741
0
    _mod = 0;
2742
0
    _totalCharge = 0;
2743
0
    _dimension = 3;
2744
0
    _vatom.clear();
2745
0
    _atomIds.clear();
2746
0
    _vbond.clear();
2747
0
    _bondIds.clear();
2748
0
    _vdata.clear();
2749
0
    _title = "";
2750
0
    _c = nullptr;
2751
0
    _flags = 0;
2752
0
    _vconf.clear();
2753
0
    _autoPartialCharge = true;
2754
0
    _autoFormalCharge = true;
2755
    //NF  _compressed = false;
2756
0
    _energy = 0.0;
2757
0
    *this = mol;
2758
0
  }
2759
2760
  OBMol::~OBMol()
2761
167
  {
2762
167
    OBAtom    *atom;
2763
167
    OBBond    *bond;
2764
167
    OBResidue *residue;
2765
167
    vector<OBAtom*>::iterator i;
2766
167
    vector<OBBond*>::iterator j;
2767
167
    vector<OBResidue*>::iterator r;
2768
479k
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2769
479k
      DestroyAtom(atom);
2770
260
    for (bond = BeginBond(j);bond;bond = NextBond(j))
2771
93
      DestroyBond(bond);
2772
167
    for (residue = BeginResidue(r);residue;residue = NextResidue(r))
2773
0
      DestroyResidue(residue);
2774
2775
    //clear out the multiconformer data
2776
167
    vector<double*>::iterator k;
2777
233
    for (k = _vconf.begin();k != _vconf.end();++k)
2778
66
      delete [] *k;
2779
167
    _vconf.clear();
2780
167
  }
2781
2782
  bool OBMol::HasNonZeroCoords()
2783
0
  {
2784
0
    OBAtom *atom;
2785
0
    vector<OBAtom*>::iterator i;
2786
2787
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2788
0
      if (atom->GetVector() != VZero)
2789
0
        return(true);
2790
2791
0
    return(false);
2792
0
  }
2793
2794
  bool OBMol::Has2D(bool Not3D)
2795
105
  {
2796
105
    bool hasX,hasY;
2797
105
    OBAtom *atom;
2798
105
    vector<OBAtom*>::iterator i;
2799
2800
105
    hasX = hasY = false;
2801
475k
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2802
475k
      {
2803
475k
        if (!hasX && !IsNearZero(atom->x()))
2804
3
          hasX = true;
2805
475k
        if (!hasY && !IsNearZero(atom->y()))
2806
22
          hasY = true;
2807
475k
        if(Not3D && atom->z())
2808
0
          return false;
2809
475k
      }
2810
105
    if (hasX || hasY) //was && but this excluded vertically or horizontally aligned linear mols
2811
25
      return(true);
2812
80
    return(false);
2813
105
  }
2814
2815
  bool OBMol::Has3D()
2816
110
  {
2817
110
    bool hasX,hasY,hasZ;
2818
110
    OBAtom *atom;
2819
110
    vector<OBAtom*>::iterator i;
2820
2821
110
    hasX = hasY = hasZ = false;
2822
    //    if (this->_c == NULL) **Test removed** Prevented function use during molecule construction
2823
    //      return(false);
2824
475k
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
2825
475k
      {
2826
475k
        if (!hasX && !IsNearZero(atom->x()))
2827
8
          hasX = true;
2828
475k
        if (!hasY && !IsNearZero(atom->y()))
2829
27
          hasY = true;
2830
475k
        if (!hasZ && !IsNearZero(atom->z()))
2831
11
          hasZ = true;
2832
2833
475k
        if (hasX && hasY && hasZ)
2834
5
          return(true);
2835
475k
      }
2836
105
    return(false);
2837
110
  }
2838
2839
  void OBMol::SetCoordinates(double *newCoords)
2840
0
  {
2841
0
    bool noCptr = (_c == nullptr); // did we previously have a coordinate ptr
2842
0
    if (noCptr) {
2843
0
      _c = new double [NumAtoms()*3];
2844
0
    }
2845
2846
    // copy from external to internal
2847
0
    memcpy((char*)_c, (char*)newCoords, sizeof(double)*3*NumAtoms());
2848
2849
0
    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
0
  }
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
0
  {
2946
0
    if(a->GetExplicitValence() == 5 && a->GetAtomicNum() == 15)
2947
0
    {
2948
      //only allow octhedral bonding for F and Cl
2949
0
      if(n->GetAtomicNum() == 9 || n->GetAtomicNum() == 17)
2950
0
        return true;
2951
0
      else
2952
0
        return false;
2953
0
    }
2954
    //other things to check?
2955
0
    return true;
2956
0
  }
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
0
  {
2967
0
    if (Empty())
2968
0
      return;
2969
0
    if (_dimension != 3) return; // not useful on non-3D structures
2970
2971
0
    if (IsPeriodic())
2972
0
      obErrorLog.ThrowError(__FUNCTION__,
2973
0
                            "Ran OpenBabel::ConnectTheDots -- using periodic boundary conditions",
2974
0
                            obAuditMsg);
2975
0
    else
2976
0
      obErrorLog.ThrowError(__FUNCTION__,
2977
0
                            "Ran OpenBabel::ConnectTheDots", obAuditMsg);
2978
2979
2980
0
    int j,k,max;
2981
0
    double maxrad = 0;
2982
0
    bool unset = false;
2983
0
    OBAtom *atom,*nbr;
2984
0
    vector<OBAtom*>::iterator i;
2985
0
    vector<pair<OBAtom*,double> > zsortedAtoms;
2986
0
    vector<double> rad;
2987
0
    vector<int> zsorted;
2988
0
    vector<int> bondCount; // existing bonds (e.g., from residues in PDB)
2989
2990
0
    double *c = new double [NumAtoms()*3];
2991
0
    rad.resize(_natoms);
2992
2993
0
    for (j = 0, atom = BeginAtom(i) ; atom ; atom = NextAtom(i), ++j)
2994
0
      {
2995
0
        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
0
        if(atom->GetExplicitValence() >= OBElements::GetMaxBonds(atom->GetAtomicNum()))
2999
0
          continue;
3000
0
        if(atom->GetAtomicNum() == 7 && atom->GetFormalCharge() == 0 && atom->GetExplicitValence() >= 3)
3001
0
          continue;
3002
0
        (atom->GetVector()).Get(&c[j*3]);
3003
0
        pair<OBAtom*,double> entry(atom, atom->GetVector().z());
3004
0
        zsortedAtoms.push_back(entry);
3005
0
      }
3006
0
    sort(zsortedAtoms.begin(), zsortedAtoms.end(), SortAtomZ);
3007
3008
0
    max = zsortedAtoms.size();
3009
3010
0
    for ( j = 0 ; j < max ; j++ )
3011
0
      {
3012
0
        atom   = zsortedAtoms[j].first;
3013
0
        rad[j] = OBElements::GetCovalentRad(atom->GetAtomicNum());
3014
0
        maxrad = std::max(rad[j],maxrad);
3015
0
        zsorted.push_back(atom->GetIdx()-1);
3016
0
      }
3017
3018
0
    int idx1, idx2;
3019
0
    double d2,cutoff,zd;
3020
0
    vector3 atom1, atom2, wrapped_coords;  // Only used for periodic coords
3021
0
    for (j = 0 ; j < max ; ++j)
3022
0
      {
3023
0
        double maxcutoff = SQUARE(rad[j]+maxrad+0.45);
3024
0
        idx1 = zsorted[j];
3025
0
        for (k = j + 1 ; k < max ; k++ )
3026
0
          {
3027
0
            idx2 = zsorted[k];
3028
3029
            // bonded if closer than elemental Rcov + tolerance
3030
0
            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
0
            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
0
            else
3043
0
              {
3044
0
                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
0
                if (zd > maxcutoff )
3049
0
                  break;
3050
3051
0
                d2  = SQUARE(c[idx1*3]   - c[idx2*3]);
3052
0
                if (d2 > cutoff)
3053
0
                  continue; // x's bigger than cutoff
3054
0
                d2 += SQUARE(c[idx1*3+1] - c[idx2*3+1]);
3055
0
                if (d2 > cutoff)
3056
0
                  continue; // x^2 + y^2 bigger than cutoff
3057
0
                d2 += zd;
3058
0
              }
3059
3060
0
            if (d2 > cutoff)
3061
0
              continue;
3062
0
            if (d2 < 0.16) // 0.4 * 0.4 = 0.16
3063
0
              continue;
3064
3065
0
            atom = GetAtom(idx1+1);
3066
0
            nbr  = GetAtom(idx2+1);
3067
3068
0
            if (atom->IsConnected(nbr))
3069
0
              continue;
3070
3071
0
            if (!validAdditionalBond(atom,nbr) || !validAdditionalBond(nbr, atom))
3072
0
              continue;
3073
3074
0
            AddBond(idx1+1,idx2+1,1);
3075
0
          }
3076
0
      }
3077
3078
    // If between BeginModify and EndModify, coord pointers are NULL
3079
    // setup molecule to handle current coordinates
3080
3081
0
    if (_c == nullptr)
3082
0
      {
3083
0
        _c = c;
3084
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3085
0
          atom->SetCoordPtr(&_c);
3086
0
        _vconf.push_back(c);
3087
0
        unset = true;
3088
0
      }
3089
3090
    // Cleanup -- delete long bonds that exceed max valence
3091
0
    OBBond *maxbond, *bond;
3092
0
    double maxlength;
3093
0
    vector<OBBond*>::iterator l, m;
3094
0
    int valCount;
3095
0
    bool changed;
3096
0
    BeginModify(); //prevent needless re-perception in DeleteBond
3097
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3098
0
      {
3099
0
        while (atom->GetExplicitValence() > static_cast<unsigned int>(OBElements::GetMaxBonds(atom->GetAtomicNum()))
3100
0
               || atom->SmallestBondAngle() < 45.0)
3101
0
          {
3102
0
            bond = atom->BeginBond(l);
3103
0
            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
0
            valCount = 0;
3109
0
            while (valCount < bondCount[atom->GetIdx() - 1]) {
3110
0
              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
0
              if (!bond) // so we add an additional check
3121
0
                break;
3122
0
              maxbond = bond;
3123
0
              valCount++;
3124
0
            }
3125
0
            if (!bond) // no new bonds added for this atom, just skip it
3126
0
              break;
3127
3128
            // delete bonds between hydrogens when over max valence
3129
0
            if (atom->GetAtomicNum() == OBElements::Hydrogen)
3130
0
              {
3131
0
                m = l;
3132
0
                changed = false;
3133
0
                for (;bond;bond = atom->NextBond(m))
3134
0
                  {
3135
0
                    if (bond->GetNbrAtom(atom)->GetAtomicNum() == OBElements::Hydrogen)
3136
0
                      {
3137
0
                        DeleteBond(bond);
3138
0
                        changed = true;
3139
0
                        break;
3140
0
                      }
3141
0
                  }
3142
0
                if (changed)
3143
0
                  {
3144
                    // bond deleted, reevaluate BOSum
3145
0
                    continue;
3146
0
                  }
3147
0
                else
3148
0
                  {
3149
                    // reset to first new bond
3150
0
                    bond = maxbond;
3151
0
                  }
3152
0
              }
3153
3154
0
            maxlength = maxbond->GetLength();
3155
0
            for (bond = atom->NextBond(l);bond;bond = atom->NextBond(l))
3156
0
              {
3157
0
                if (bond->GetLength() > maxlength)
3158
0
                  {
3159
0
                    maxbond = bond;
3160
0
                    maxlength = bond->GetLength();
3161
0
                  }
3162
0
              }
3163
0
            DeleteBond(maxbond); // delete the new bond with the longest length
3164
0
          }
3165
0
      }
3166
0
    EndModify();
3167
0
    if (unset)
3168
0
      {
3169
0
        if (_c != nullptr){
3170
0
          delete [] _c;
3171
3172
          // Note that the above delete doesn't set _c value to nullptr
3173
0
          _c = nullptr;
3174
0
        }
3175
3176
0
        for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3177
0
          atom->ClearCoordPtr();
3178
0
  if (_vconf.size() > 0)
3179
0
    _vconf.resize(_vconf.size()-1);
3180
0
      }
3181
3182
0
    if (_c != nullptr)
3183
0
      delete [] c;
3184
0
  }
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
0
  {
3196
0
    if (Empty())
3197
0
      return;
3198
0
    if (_dimension != 3) return; // not useful on non-3D structures
3199
3200
0
    obErrorLog.ThrowError(__FUNCTION__,
3201
0
                          "Ran OpenBabel::PerceiveBondOrders", obAuditMsg);
3202
3203
0
    OBAtom *atom, *b, *c;
3204
0
    vector3 v1, v2;
3205
0
    double angle;//, dist1, dist2;
3206
0
    vector<OBAtom*>::iterator i;
3207
0
    vector<OBBond*>::iterator j;//,k;
3208
3209
    //  BeginModify();
3210
3211
    // Pass 1: Assign estimated hybridization based on avg. angles
3212
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3213
0
      {
3214
0
        angle = atom->AverageBondAngle();
3215
3216
        //        cout << atom->GetAtomicNum() << " " << angle << endl;
3217
3218
0
        if (angle > 155.0)
3219
0
          atom->SetHyb(1);
3220
0
        else if (angle <= 155.0 && angle > 115.0)
3221
0
          atom->SetHyb(2);
3222
3223
        // special case for imines
3224
0
        if (atom->GetAtomicNum() == OBElements::Nitrogen
3225
0
            && atom->ExplicitHydrogenCount() == 1
3226
0
            && atom->GetExplicitDegree() == 2
3227
0
            && angle > 109.5)
3228
0
          atom->SetHyb(2);
3229
0
        else if(atom->GetAtomicNum() == OBElements::Nitrogen
3230
0
            && atom->GetExplicitDegree() == 2
3231
0
            && atom->IsInRing()) //azete
3232
0
          atom->SetHyb(2);
3233
0
      } // pass 1
3234
3235
    // Make sure upcoming calls to GetHyb() don't kill these temporary values
3236
0
    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
0
    vector<OBRing*> rlist;
3243
0
    vector<OBRing*>::iterator ringit;
3244
0
    vector<int> path;
3245
0
    double torsions = 0.0;
3246
3247
0
    if (!HasSSSRPerceived())
3248
0
      FindSSSR();
3249
0
    rlist = GetSSSR();
3250
0
    for (ringit = rlist.begin(); ringit != rlist.end(); ++ringit)
3251
0
      {
3252
0
        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
0
        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
0
      }
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
0
    bool openNbr;
3301
0
    for (atom = BeginAtom(i);atom;atom = NextAtom(i))
3302
0
      {
3303
0
        if (atom->GetHyb() == 2 || atom->GetHyb() == 1)
3304
0
          {
3305
0
            openNbr = false;
3306
0
            for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j))
3307
0
              {
3308
0
                if (b->GetHyb() < 3 || b->GetExplicitDegree() == 1)
3309
0
                  {
3310
0
                    openNbr = true;
3311
0
                    break;
3312
0
                  }
3313
0
              }
3314
0
            if (!openNbr && atom->GetHyb() == 2)
3315
0
              atom->SetHyb(3);
3316
0
            else if (!openNbr && atom->GetHyb() == 1)
3317
0
              atom->SetHyb(2);
3318
0
          }
3319
0
      } // 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
0
    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
0
    bool needs_kekulization = false; // are there any aromatic bonds?
3338
0
    bool typed; // has this ring been typed?
3339
0
    unsigned int loop, loopSize;
3340
0
    for (ringit = rlist.begin(); ringit != rlist.end(); ++ringit)
3341
0
      {
3342
0
        typed = false;
3343
0
        loopSize = (*ringit)->PathSize();
3344
0
        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
0
      }
3367
3368
    // Kekulization is necessary if an aromatic bond is present
3369
0
    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
0
    vector<pair<OBAtom*,double> > sortedAtoms;
3404
0
    vector<double> rad;
3405
0
    vector<int> sorted;
3406
0
    int iter, max;
3407
0
    double maxElNeg, shortestBond, currentElNeg;
3408
0
    double bondLength, testLength;
3409
3410
0
    for (atom = BeginAtom(i) ; atom ; atom = NextAtom(i))
3411
0
      {
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
0
        shortestBond = 1.0e5;
3415
0
        for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j))
3416
0
          {
3417
0
            if (b->GetAtomicNum()!=1) shortestBond =
3418
0
                                        std::min(shortestBond,(atom->GetBond(b))->GetLength());
3419
0
          }
3420
0
        pair<OBAtom*,double> entry(atom,
3421
0
                                   OBElements::GetElectroNeg(atom->GetAtomicNum())*1e6+shortestBond);
3422
3423
0
        sortedAtoms.push_back(entry);
3424
0
      }
3425
0
    sort(sortedAtoms.begin(), sortedAtoms.end(), SortAtomZ);
3426
3427
0
    max = sortedAtoms.size();
3428
0
    for (iter = 0 ; iter < max ; iter++ )
3429
0
      {
3430
0
        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
0
        if ( (atom->GetHyb() == 1 || atom->GetExplicitDegree() == 1)
3437
0
             && atom->GetExplicitValence() + 2  <= static_cast<unsigned int>(OBElements::GetMaxBonds(atom->GetAtomicNum()))
3438
0
             )
3439
0
          {
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
0
            if (atom->HasNonSingleBond() ||
3445
0
                (atom->GetAtomicNum() == 7 && atom->GetExplicitValence() + 2 > 3))
3446
0
              continue;
3447
3448
0
            maxElNeg = 0.0;
3449
0
            shortestBond = 5000.0;
3450
0
            c = nullptr;
3451
0
            for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j))
3452
0
              {
3453
0
                currentElNeg = OBElements::GetElectroNeg(b->GetAtomicNum());
3454
0
                if ( (b->GetHyb() == 1 || b->GetExplicitDegree() == 1)
3455
0
                     && b->GetExplicitValence() + 2 <= static_cast<unsigned int>(OBElements::GetMaxBonds(b->GetAtomicNum()))
3456
0
                     && (currentElNeg > maxElNeg ||
3457
0
                         (IsApprox(currentElNeg,maxElNeg, 1.0e-6)
3458
0
                          && (atom->GetBond(b))->GetLength() < shortestBond)) )
3459
0
                  {
3460
0
                    if (b->HasNonSingleBond() ||
3461
0
                        (b->GetAtomicNum() == 7 && b->GetExplicitValence() + 2 > 3))
3462
0
                      continue;
3463
3464
                    // Test terminal bonds against expected triple bond lengths
3465
0
                    bondLength = (atom->GetBond(b))->GetLength();
3466
0
                    if (atom->GetExplicitDegree() == 1 || b->GetExplicitDegree() == 1) {
3467
0
                      testLength = CorrectedBondRad(atom->GetAtomicNum(), atom->GetHyb())
3468
0
                        + CorrectedBondRad(b->GetAtomicNum(), b->GetHyb());
3469
0
                      if (bondLength > 0.9 * testLength)
3470
0
                        continue; // too long, ignore it
3471
0
                    }
3472
3473
0
                    shortestBond = bondLength;
3474
0
                    maxElNeg = OBElements::GetElectroNeg(b->GetAtomicNum());
3475
0
                    c = b; // save this atom for later use
3476
0
                  }
3477
0
              }
3478
0
            if (c)
3479
0
              (atom->GetBond(c))->SetBondOrder(3);
3480
0
          }
3481
        // Possible sp2-hybrid atoms
3482
0
        else if ( (atom->GetHyb() == 2 || atom->GetExplicitDegree() == 1)
3483
0
                  && atom->GetExplicitValence() + 1 <= static_cast<unsigned int>(OBElements::GetMaxBonds(atom->GetAtomicNum())) )
3484
0
          {
3485
            // as above
3486
0
            if (atom->HasNonSingleBond() ||
3487
0
                (atom->GetAtomicNum() == 7 && atom->GetExplicitValence() + 1 > 3))
3488
0
              continue;
3489
3490
            // Don't build multiple bonds to ring sulfurs
3491
            //  except thiopyrylium
3492
0
            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
0
            maxElNeg = 0.0;
3500
0
            shortestBond = 5000.0;
3501
0
            c = nullptr;
3502
0
            for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j))
3503
0
              {
3504
0
                currentElNeg = OBElements::GetElectroNeg(b->GetAtomicNum());
3505
0
                if ( (b->GetHyb() == 2 || b->GetExplicitDegree() == 1)
3506
0
                     && b->GetExplicitValence() + 1 <= static_cast<unsigned int>(OBElements::GetMaxBonds(b->GetAtomicNum()))
3507
0
                     && (GetBond(atom, b))->IsDoubleBondGeometry()
3508
0
                     && (currentElNeg > maxElNeg || (IsApprox(currentElNeg,maxElNeg, 1.0e-6)) ) )
3509
0
                  {
3510
0
                    if (b->HasNonSingleBond() ||
3511
0
                        (b->GetAtomicNum() == 7 && b->GetExplicitValence() + 1 > 3))
3512
0
                      continue;
3513
3514
0
                    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
0
                    bondLength = (atom->GetBond(b))->GetLength();
3523
0
                    if (atom->GetExplicitDegree() == 1 || b->GetExplicitDegree() == 1) {
3524
0
                      testLength = CorrectedBondRad(atom->GetAtomicNum(), atom->GetHyb())
3525
0
                        + CorrectedBondRad(b->GetAtomicNum(), b->GetHyb());
3526
0
                      if (bondLength > 0.93 * testLength)
3527
0
                        continue; // too long, ignore it
3528
0
                    }
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
0
                    double difference = shortestBond - (atom->GetBond(b))->GetLength();
3534
0
                    if ( (difference > 0.1)
3535
0
                         || ( (difference > -0.01) &&
3536
0
                              ( (!atom->IsInRing() || !c || !c->IsInRing() || b->IsInRing())
3537
0
                                || (atom->IsInRing() && c && !c->IsInRing() && b->IsInRing()) ) ) ) {
3538
0
                      shortestBond = (atom->GetBond(b))->GetLength();
3539
0
                      maxElNeg = OBElements::GetElectroNeg(b->GetAtomicNum());
3540
0
                      c = b; // save this atom for later use
3541
0
                    } // is this bond better than previous choices
3542
0
                  }
3543
0
              } // loop through neighbors
3544
0
            if (c)
3545
0
              (atom->GetBond(c))->SetBondOrder(2);
3546
0
          }
3547
0
      } // pass 6
3548
3549
    // Now let the atom typer go to work again
3550
0
    _flags &= (~(OB_HYBRID_MOL));
3551
0
    _flags &= (~(OB_AROMATIC_MOL));
3552
0
    _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
0
    }
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
0
  {
3605
0
    for (int i = 0;i < NumConformers();++i)
3606
0
      Translate(v,i);
3607
0
  }
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
0
  {
3614
0
    obErrorLog.ThrowError(__FUNCTION__,
3615
0
                          "Ran OpenBabel::Translate", obAuditMsg);
3616
3617
0
    int i,size;
3618
0
    double x,y,z;
3619
0
    double *c = (nconf == OB_CURRENT_CONFORMER)? _c : GetConformer(nconf);
3620
3621
0
    x = v.x();
3622
0
    y = v.y();
3623
0
    z = v.z();
3624
0
    size = NumAtoms();
3625
0
    for (i = 0;i < size;++i)
3626
0
      {
3627
0
        c[i*3  ] += x;
3628
0
        c[i*3+1] += y;
3629
0
        c[i*3+2] += z;
3630
0
      }
3631
0
  }
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
0
  {
3674
0
    if (!HasData(OBGenericDataType::ConformerData))
3675
0
      SetData(new OBConformerData);
3676
0
    OBConformerData *cd = (OBConformerData*) GetData(OBGenericDataType::ConformerData);
3677
0
    cd->SetEnergies(energies);
3678
0
  }
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
0
  {
3705
0
    vector<double*>::iterator i;
3706
0
    for (i = _vconf.begin();i != _vconf.end();++i)
3707
0
      delete [] *i;
3708
3709
0
    _vconf = v;
3710
0
    _c = _vconf.empty() ? nullptr : _vconf[0];
3711
3712
0
  }
3713
3714
  void OBMol::SetConformer(unsigned int i)
3715
0
  {
3716
0
    if (i < _vconf.size())
3717
0
      _c = _vconf[i];
3718
0
  }
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
0
  {
3741
0
    if (idx < 0 || idx >= (signed)_vconf.size())
3742
0
      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
0
  {
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
0
    const int BLOCKS[113] = {0,1,2,1,1,2,2,2,2,2,2,1,1,2,2,2,2,2,2,1,1,3,3,3,3,3,3,3,3,3,
3866
0
                             3,2,2,2,2,2,2,1,1,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,1,1,4,4,4,
3867
0
                             4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,1,1,4,
3868
0
                             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
0
    bool converted = false;
3871
    // Get contiguous fragments of molecule
3872
0
    vector<vector<int> > cfl;
3873
0
    ContigFragList(cfl);
3874
    // Iterate over contiguous fragments
3875
0
    for (vector< vector<int> >::iterator i = cfl.begin(); i != cfl.end(); ++i) {
3876
      // Get all zero-order bonds in contiguous fragment
3877
0
      vector<OBBond*> bonds;
3878
0
      for(vector<int>::const_iterator j = i->begin(); j != i->end(); ++j) {
3879
0
        FOR_BONDS_OF_ATOM(b, GetAtom(*j)) {
3880
0
          if (b->GetBondOrder() == 0 && !(find(bonds.begin(), bonds.end(), &*b) != bonds.end())) {
3881
0
            bonds.push_back(&*b);
3882
0
          }
3883
0
        }
3884
0
      }
3885
      // Convert zero-order bonds
3886
0
      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
0
    }
3947
0
    return converted;
3948
0
  }
3949
3950
  OBAtom *OBMol::BeginAtom(OBAtomIterator &i)
3951
5.05k
  {
3952
5.05k
    i = _vatom.begin();
3953
5.05k
    return i == _vatom.end() ? nullptr : (OBAtom*)*i;
3954
5.05k
  }
3955
3956
  const OBAtom* OBMol::BeginAtom(OBAtomConstIterator &i) const
3957
0
  {
3958
0
    i = _vatom.cbegin();
3959
0
    return i == _vatom.cend() ? nullptr : (OBAtom *)*i;
3960
0
  }
3961
3962
  OBAtom *OBMol::NextAtom(OBAtomIterator &i)
3963
33.7M
  {
3964
33.7M
    ++i;
3965
33.7M
    return i == _vatom.end() ? nullptr : (OBAtom*)*i;
3966
33.7M
  }
3967
3968
  const OBAtom* OBMol::NextAtom(OBAtomConstIterator &i) const
3969
0
  {
3970
0
    ++i;
3971
0
    return i == _vatom.cend() ? nullptr : (OBAtom *)*i;
3972
0
  }
3973
3974
  OBBond *OBMol::BeginBond(OBBondIterator &i)
3975
411
  {
3976
411
    i = _vbond.begin();
3977
411
    return i == _vbond.end() ? nullptr : (OBBond*)*i;
3978
411
  }
3979
3980
  OBBond *OBMol::NextBond(OBBondIterator &i)
3981
125
  {
3982
125
    ++i;
3983
125
    return i == _vbond.end() ? nullptr : (OBBond*)*i;
3984
125
  }
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
0
  {
4017
0
    vector<OBMol> result;
4018
0
    if( NumAtoms() == 0 )
4019
0
      return result; // nothing to do, but let's prevent a crash
4020
4021
0
    OBMolAtomDFSIter iter( this, StartIndex );
4022
0
    OBMol newMol;
4023
0
    while( GetNextFragment( iter, newMol ) ) {
4024
0
      result.push_back( newMol );
4025
0
      newMol.Clear();
4026
0
    }
4027
4028
0
    return result;
4029
0
  }
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
0
  {
4112
0
    if (!atoms)
4113
0
      return false;
4114
4115
0
    bool record_atomorder = atomorder != nullptr;
4116
0
    bool record_bondorder = bondorder != nullptr;
4117
0
    bool bonds_specified = excludebonds != nullptr;
4118
4119
0
    newmol.SetDimension(GetDimension());
4120
4121
    // If the parent is set to periodic, then also apply boundary conditions to the fragments
4122
0
    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
0
    newmol.SetFlag(_flags & OB_AROMATIC_MOL);
4129
    // The fragment will preserve the "chains perceived" flag of the parent
4130
0
    newmol.SetFlag(_flags & OB_CHAINS_MOL);
4131
    // We will check for residues only if the parent has chains perceived already
4132
0
    bool checkresidues = HasChainsPerceived();
4133
4134
    // Now add the atoms
4135
0
    map<OBAtom*, OBAtom*> AtomMap;//key is from old mol; value from new mol
4136
0
    for (int bit = atoms->FirstBit(); bit != atoms->EndBit(); bit = atoms->NextBit(bit)) {
4137
0
      OBAtom* atom = this->GetAtom(bit);
4138
0
      if (!atom)
4139
0
        return false;
4140
0
      newmol.AddAtom(*atom); // each subsequent atom
4141
0
      if (record_atomorder)
4142
0
        atomorder->push_back(bit);
4143
0
      AtomMap[&*atom] = newmol.GetAtom(newmol.NumAtoms());
4144
0
    }
4145
4146
    //Add the residues
4147
0
    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
0
    std::vector<OBGenericData*>::iterator data;
4172
0
    std::vector<OBGenericData*> stereoData = GetAllData(OBGenericDataType::StereoData);
4173
0
    for (data = stereoData.begin(); data != stereoData.end(); ++data) {
4174
0
      if (static_cast<OBStereoBase*>(*data)->GetType() == OBStereo::CisTrans) {
4175
0
        OBCisTransStereo *ct = dynamic_cast<OBCisTransStereo*>(*data);
4176
4177
        // Check that the entirety of this cistrans cfg occurs in this substructure
4178
0
        OBCisTransStereo::Config cfg = ct->GetConfig();
4179
0
        OBAtom* begin = GetAtomById(cfg.begin);
4180
0
        if (AtomMap.find(begin) == AtomMap.end())
4181
0
          continue;
4182
0
        OBAtom* end = GetAtomById(cfg.end);
4183
0
        if (AtomMap.find(end) == AtomMap.end())
4184
0
          continue;
4185
0
        bool skip_cfg = false;
4186
0
        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
0
        for (OBStereo::RefIter ri = cfg.refs.begin(); ri != cfg.refs.end(); ++ri) {
4205
0
          if (*ri != OBStereo::ImplicitRef && AtomMap.find(GetAtomById(*ri)) == AtomMap.end()) {
4206
0
            skip_cfg = true;
4207
0
            break;
4208
0
          }
4209
0
        }
4210
0
        if (skip_cfg)
4211
0
          continue;
4212
4213
0
        OBCisTransStereo::Config newcfg;
4214
0
        newcfg.specified = cfg.specified;
4215
0
        newcfg.begin = cfg.begin == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(cfg.begin)]->GetId();
4216
0
        newcfg.end = cfg.end == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(cfg.end)]->GetId();
4217
0
        OBStereo::Refs refs;
4218
0
        for (OBStereo::RefIter ri = cfg.refs.begin(); ri != cfg.refs.end(); ++ri) {
4219
0
          OBStereo::Ref ref = *ri == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(*ri)]->GetId();
4220
0
          refs.push_back(ref);
4221
0
        }
4222
0
        newcfg.refs = refs;
4223
4224
0
        OBCisTransStereo *newct = new OBCisTransStereo(this);
4225
0
        newct->SetConfig(newcfg);
4226
0
        newmol.SetData(newct);
4227
0
      }
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
0
    }
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
0
    FOR_BONDS_OF_MOL(bond, this) {
4281
0
      bool skipping_bond = bonds_specified && excludebonds->BitIsSet(bond->GetIdx());
4282
0
      map<OBAtom*, OBAtom*>::iterator posB = AtomMap.find(bond->GetBeginAtom());
4283
0
      map<OBAtom*, OBAtom*>::iterator posE = AtomMap.find(bond->GetEndAtom());
4284
0
      if (posB == AtomMap.end() && posE == AtomMap.end())
4285
0
        continue;
4286
4287
0
      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
0
      else {
4343
0
        newmol.AddBond((posB->second)->GetIdx(), posE->second->GetIdx(),
4344
0
                       bond->GetBondOrder(), bond->GetFlags());
4345
0
        if (record_bondorder)
4346
0
          bondorder->push_back(bond->GetIdx());
4347
0
      }
4348
0
    }
4349
4350
0
    return true;
4351
0
  }
4352
4353
0
  bool OBMol::GetNextFragment( OBMolAtomDFSIter& iter, OBMol& newmol ) {
4354
0
    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
0
    OBBitVec infragment(this->NumAtoms()+1);
4359
0
    do { //for each atom in fragment
4360
0
      infragment.SetBitOn(iter->GetIdx());
4361
0
    } while ((iter++).next());
4362
4363
0
    bool ok = CopySubstructure(newmol, &infragment);
4364
4365
0
    return ok;
4366
0
  }
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
0
  {
4375
0
    int extraCharge = charge - GetTotalCharge(); //GetTotalCharge() gets charge on atoms
4376
4377
0
    FOR_ATOMS_OF_MOL (atom, this)
4378
0
    {
4379
0
      unsigned int atomicnum = atom->GetAtomicNum();
4380
0
      if (atomicnum == 1)
4381
0
        continue;
4382
0
      int charge = atom->GetFormalCharge();
4383
0
      unsigned bosum = atom->GetExplicitValence();
4384
0
      unsigned int totalValence = bosum + atom->GetImplicitHCount();
4385
0
      unsigned int typicalValence = GetTypicalValence(atomicnum, bosum, charge);
4386
0
      int diff = typicalValence - totalValence;
4387
0
      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
0
    }
4400
0
    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
0
    return true;
4406
0
 }
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.