ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-4/src/openbabel/typer.cpp
Revision: 3057
Committed: Thu Oct 19 20:49:05 2006 UTC (17 years, 11 months ago) by gezelter
File size: 31274 byte(s)
Log Message:
updated OpenBabel to version 2.0.2

File Contents

# User Rev Content
1 tim 2440 /**********************************************************************
2     typer.cpp - Open Babel atom typer.
3    
4     Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
5 gezelter 3057 Some portions Copyright (C) 2001-2006 by Geoffrey R. Hutchison
6 tim 2440
7     This file is part of the Open Babel project.
8     For more information, see <http://openbabel.sourceforge.net/>
9    
10     This program is free software; you can redistribute it and/or modify
11     it under the terms of the GNU General Public License as published by
12     the Free Software Foundation version 2 of the License.
13    
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17     GNU General Public License for more details.
18     ***********************************************************************/
19    
20     #include "mol.hpp"
21     #include "typer.hpp"
22     #include "atomtyp.hpp"
23     #include "aromatic.hpp"
24    
25     #ifdef WIN32
26     #pragma warning (disable : 4786)
27     #endif
28    
29     using namespace std;
30    
31     namespace OpenBabel
32     {
33    
34     OBAromaticTyper aromtyper;
35     OBAtomTyper atomtyper;
36    
37     /*! \class OBAtomTyper
38     \brief Assigns atom types, hybridization, implicit valence and formal charges
39    
40     The OBAtomTyper class is designed to read in a list of atom typing
41     rules and apply them to molecules. The code that performs atom
42     typing is not usually used directly as atom typing, hybridization
43     assignment, implicit valence assignment and charge are all done
44     automatically when their corresponding values are requested of
45     atoms:
46     \code
47     atom->GetType();
48     atom->GetFormalCharge();
49     atom->GetHyb();
50     \endcode
51     */
52     OBAtomTyper::OBAtomTyper()
53     {
54     _init = false;
55 gezelter 3057 STR_DEFINE(_dir, FRC_PATH );
56 gezelter 2450 _envvar = "FORCE_PARAM_PATH";
57 tim 2440 _filename = "atomtyp.txt";
58     _subdir = "data";
59     _dataptr = AtomTypeData;
60     }
61    
62     void OBAtomTyper::ParseLine(const char *buffer)
63     {
64     vector<string> vs;
65     OBSmartsPattern *sp;
66    
67     if (EQn(buffer,"INTHYB",6))
68     {
69     tokenize(vs,buffer);
70     if (vs.empty() || vs.size() < 3)
71     {
72 tim 2518 obErrorLog.ThrowError(__func__, " Could not parse INTHYB line in atom type table from atomtyp.txt", obInfo);
73 tim 2440 return;
74     }
75    
76     sp = new OBSmartsPattern;
77     if (sp->Init(vs[1]))
78     _vinthyb.push_back(pair<OBSmartsPattern*,int> (sp,atoi((char*)vs[2].c_str())));
79     else
80     {
81     delete sp;
82     sp = NULL;
83 tim 2518 obErrorLog.ThrowError(__func__, " Could not parse INTHYB line in atom type table from atomtyp.txt", obInfo);
84 tim 2440 return;
85     }
86     }
87     else if (EQn(buffer,"IMPVAL",6))
88     {
89     tokenize(vs,buffer);
90     if (vs.empty() || vs.size() < 3)
91     {
92 tim 2518 obErrorLog.ThrowError(__func__, " Could not parse IMPVAL line in atom type table from atomtyp.txt", obInfo);
93 tim 2440 return;
94     }
95    
96     sp = new OBSmartsPattern;
97     if (sp->Init(vs[1]))
98     _vimpval.push_back(pair<OBSmartsPattern*,int> (sp,atoi((char*)vs[2].c_str())));
99     else
100     {
101 tim 2518 obErrorLog.ThrowError(__func__, " Could not parse IMPVAL line in atom type table from atomtyp.txt", obInfo);
102 tim 2440 delete sp;
103     sp = NULL;
104     return;
105     }
106     }
107     else if (EQn(buffer,"EXTTYP",6))
108     {
109     tokenize(vs,buffer);
110     if (vs.empty() || vs.size() < 3)
111     {
112 tim 2518 obErrorLog.ThrowError(__func__, " Could not parse EXTTYP line in atom type table from atomtyp.txt", obInfo);
113 tim 2440 return;
114     }
115     sp = new OBSmartsPattern;
116     if (sp->Init(vs[1]))
117     _vexttyp.push_back(pair<OBSmartsPattern*,string> (sp,vs[2]));
118     else
119     {
120     delete sp;
121     sp = NULL;
122 tim 2518 obErrorLog.ThrowError(__func__, " Could not parse EXTTYP line in atom type table from atomtyp.txt", obInfo);
123 tim 2440 return;
124     }
125     }
126     }
127    
128     OBAtomTyper::~OBAtomTyper()
129     {
130     vector<pair<OBSmartsPattern*,int> >::iterator i;
131     for (i = _vinthyb.begin();i != _vinthyb.end();i++)
132     {
133     delete i->first;
134     i->first = NULL;
135     }
136     for (i = _vimpval.begin();i != _vimpval.end();i++)
137     {
138     delete i->first;
139     i->first = NULL;
140     }
141    
142     vector<pair<OBSmartsPattern*,string> >::iterator j;
143     for (j = _vexttyp.begin();j != _vexttyp.end();j++)
144     {
145     delete j->first;
146     j->first = NULL;
147     }
148    
149     }
150    
151     void OBAtomTyper::AssignTypes(OBMol &mol)
152     {
153     if (!_init)
154     Init();
155    
156 tim 2518 obErrorLog.ThrowError(__func__,
157 tim 2440 "Ran OpenBabel::AssignTypes", obAuditMsg);
158    
159     mol.SetAtomTypesPerceived();
160    
161     vector<vector<int> >::iterator j;
162     vector<pair<OBSmartsPattern*,string> >::iterator i;
163    
164     for (i = _vexttyp.begin();i != _vexttyp.end();i++)
165     if (i->first->Match(mol))
166     {
167     _mlist = i->first->GetMapList();
168     for (j = _mlist.begin();j != _mlist.end();j++)
169     mol.GetAtom((*j)[0])->SetType(i->second);
170     }
171     }
172    
173     void OBAtomTyper::AssignHyb(OBMol &mol)
174     {
175     if (!_init)
176     Init();
177    
178     aromtyper.AssignAromaticFlags(mol);
179    
180     mol.SetHybridizationPerceived();
181 tim 2518 obErrorLog.ThrowError(__func__,
182 tim 2440 "Ran OpenBabel::AssignHybridization", obAuditMsg);
183    
184     OBAtom *atom;
185     vector<OBNodeBase*>::iterator k;
186     for (atom = mol.BeginAtom(k);atom;atom = mol.NextAtom(k))
187     atom->SetHyb(0);
188    
189     vector<vector<int> >::iterator j;
190     vector<pair<OBSmartsPattern*,int> >::iterator i;
191    
192     for (i = _vinthyb.begin();i != _vinthyb.end();i++)
193     if (i->first->Match(mol))
194     {
195     _mlist = i->first->GetMapList();
196     for (j = _mlist.begin();j != _mlist.end();j++)
197     mol.GetAtom((*j)[0])->SetHyb(i->second);
198     }
199     }
200    
201     void OBAtomTyper::AssignImplicitValence(OBMol &mol)
202     {
203     // FF Make sure that valence has not been perceived
204     if(mol.HasImplicitValencePerceived())
205     return;
206    
207     if (!_init)
208     Init();
209    
210     mol.SetImplicitValencePerceived();
211 tim 2518 obErrorLog.ThrowError(__func__,
212 tim 2440 "Ran OpenBabel::AssignImplicitValence", obAuditMsg);
213    
214     // FF Ensure that the aromatic typer will not be called
215     int oldflags = mol.GetFlags(); // save the current state flags
216     mol.SetAromaticPerceived(); // and set the aromatic perceived flag on
217    
218     OBAtom *atom;
219     vector<OBNodeBase*>::iterator k;
220     for (atom = mol.BeginAtom(k);atom;atom = mol.NextAtom(k))
221     atom->SetImplicitValence(atom->GetValence());
222    
223     vector<vector<int> >::iterator j;
224     vector<pair<OBSmartsPattern*,int> >::iterator i;
225    
226     for (i = _vimpval.begin();i != _vimpval.end();i++)
227     if (i->first->Match(mol))
228     {
229     _mlist = i->first->GetMapList();
230     for (j = _mlist.begin();j != _mlist.end();j++)
231     mol.GetAtom((*j)[0])->SetImplicitValence(i->second);
232     }
233    
234     if (!mol.HasAromaticCorrected())
235     CorrectAromaticNitrogens(mol);
236    
237     for (atom = mol.BeginAtom(k);atom;atom = mol.NextAtom(k))
238     {
239     if (atom->GetImplicitValence() < atom->GetValence())
240     atom->SetImplicitValence(atom->GetValence());
241     }
242    
243     //FF moved to OBMol::AssignSpinMultiplicity();
244     //begin CM 18 Sept 2003
245     //if there are any explicit Hs on an atom, then they consitute all the Hs
246     //Any discrepancy with the expected atom valency is because it is a radical of some sort
247     //Also adjust the ImplicitValence for radical atoms
248     //for (atom = mol.BeginAtom(k);atom;atom = mol.NextAtom(k))
249     // {
250     // if (!atom->IsHydrogen() && atom->ExplicitHydrogenCount()!=0)
251     // {
252     // int diff=atom->GetImplicitValence() - (atom->GetHvyValence() + atom->ExplicitHydrogenCount());
253     // if (diff)
254     // atom->SetSpinMultiplicity(diff+1);//radicals =2; all carbenes =3
255     // }
256    
257     // int mult=atom->GetSpinMultiplicity();
258     // if(mult) //radical or carbene
259     // atom->DecrementImplicitValence();
260     // if(mult==1 || mult==3) //e.g.singlet or triplet carbene
261     // atom->DecrementImplicitValence();
262     // }
263     //end CM
264     //FF end
265    
266     // FF Come back to the initial flags
267     mol.SetFlags(oldflags);
268    
269     return;
270     }
271    
272    
273     void OBAtomTyper::CorrectAromaticNitrogens(OBMol &mol)
274     {
275     if (!_init)
276     Init();
277    
278     if (mol.HasAromaticCorrected())
279     return;
280     mol.SetAromaticCorrected();
281    
282     return;
283    
284     // int j;
285     // OBAtom *atom,*nbr,*a;
286     // vector<OBNodeBase*>::iterator i,m;
287     // vector<OBEdgeBase*>::iterator k;
288     // OBBitVec curr,used,next;
289     // vector<OBNodeBase*> v_N,v_OS;
290    
291     // vector<bool> _aromNH;
292     // _aromNH.clear();
293     // _aromNH.resize(mol.NumAtoms()+1);
294    
295     // for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
296     // if (atom->IsAromatic() && !atom->IsCarbon() && !used[atom->GetIdx()])
297     // {
298     // vector<OBNodeBase*> rsys;
299     // rsys.push_back(atom);
300     // curr |= atom->GetIdx();
301     // used |= atom->GetIdx();
302    
303     // while (!curr.Empty())
304     // {
305     // next.Clear();
306     // for (j = curr.NextBit(0);j != curr.EndBit();j = curr.NextBit(j))
307     // {
308     // atom = mol.GetAtom(j);
309     // for (nbr = atom->BeginNbrAtom(k);nbr;nbr = atom->NextNbrAtom(k))
310     // {
311     // if (!(*k)->IsAromatic())
312     // continue;
313     // if (used[nbr->GetIdx()])
314     // continue;
315    
316     // rsys.push_back(nbr);
317     // next |= nbr->GetIdx();
318     // used |= nbr->GetIdx();
319     // }
320     // }
321    
322     // curr = next;
323     // }
324    
325     // //count number of electrons in the ring system
326     // v_N.clear();
327     // v_OS.clear();
328     // int nelectrons = 0;
329    
330     // for (m = rsys.begin();m != rsys.end();m++)
331     // {
332     // a = (OBAtom *)*m;
333    
334     // int hasExoDoubleBond = false;
335     // for (nbr = a->BeginNbrAtom(k);nbr;nbr = a->NextNbrAtom(k))
336     // if ((*k)->GetBO() == 2 && !(*k)->IsInRing())
337     // if (nbr->IsOxygen() || nbr->IsSulfur() || nbr->IsNitrogen())
338     // hasExoDoubleBond = true;
339    
340     // if (a->IsCarbon() && hasExoDoubleBond)
341     // continue;
342    
343     // if (a->IsCarbon())
344     // nelectrons++;
345     // else
346     // if (a->IsOxygen() || a->IsSulfur())
347     // {
348     // v_OS.push_back(a);
349     // nelectrons += 2;
350     // }
351     // else
352     // if (a->IsNitrogen())
353     // {
354     // v_N.push_back(a); //store nitrogens
355     // nelectrons++;
356     // }
357     // }
358    
359     // //calculate what the number of electrons should be for aromaticity
360     // int naromatic = 2+4*((int)((double)(rsys.size()-2)*0.25+0.5));
361    
362     // if (nelectrons > naromatic) //try to give one electron back to O or S
363     // for (m = v_OS.begin();m != v_OS.end();m++)
364     // {
365     // if (naromatic == nelectrons)
366     // break;
367     // if ((*m)->GetValence() == 2 && (*m)->GetHvyValence() == 2)
368     // {
369     // nelectrons--;
370     // ((OBAtom*)*m)->SetFormalCharge(1);
371     // }
372     // }
373    
374     // if (v_N.empty())
375     // continue; //no nitrogens found in ring
376    
377     // //check for protonated nitrogens
378     // for (m = v_N.begin();m != v_N.end();m++)
379     // {
380     // if (naromatic == nelectrons)
381     // break;
382     // if (((OBAtom*)*m)->GetValence() == 3 &&
383     // ((OBAtom*)*m)->GetHvyValence() == 2)
384     // {
385     // nelectrons++;
386     // _aromNH[((OBAtom*)*m)->GetIdx()] = true;
387     // }
388     // if (((OBAtom*)*m)->GetFormalCharge() == -1)
389     // {
390     // nelectrons++;
391     // _aromNH[((OBAtom*)*m)->GetIdx()] = false;
392     // }
393     // }
394    
395     // //charge up tert nitrogens
396     // for (m = v_N.begin();m != v_N.end();m++)
397     // if (((OBAtom*)*m)->GetHvyValence() == 3
398     // && ((OBAtom*)*m)->BOSum() < 5)
399     // ((OBAtom*)*m)->SetFormalCharge(1);
400    
401     // //try to uncharge nitrogens first
402     // for (m = v_N.begin();m != v_N.end();m++)
403     // {
404     // if (((OBAtom*)*m)->BOSum() > 4)
405     // continue; //skip n=O
406     // if (naromatic == nelectrons)
407     // break;
408     // if (((OBAtom*)*m)->GetHvyValence() == 3)
409     // {
410     // nelectrons++;
411     // ((OBAtom*)*m)->SetFormalCharge(0);
412     // }
413     // }
414    
415     // if (naromatic == nelectrons)
416     // continue;
417    
418     // //try to protonate amides next
419     // for (m = v_N.begin();m != v_N.end();m++)
420     // {
421     // if (naromatic == nelectrons)
422     // break;
423     // if (((OBAtom*)*m)->IsAmideNitrogen() &&
424     // ((OBAtom*)*m)->GetValence() == 2 &&
425     // ((OBAtom*)*m)->GetHvyValence() == 2 &&
426     // !_aromNH[((OBAtom*)*m)->GetIdx()])
427     // {
428     // nelectrons++;
429     // _aromNH[((OBAtom*)*m)->GetIdx()] = true;
430     // }
431     // }
432    
433     // if (naromatic == nelectrons)
434     // continue;
435    
436     // //protonate amines in 5 membered rings first - try to match kekule
437     // for (m = v_N.begin();m != v_N.end();m++)
438     // {
439     // if (naromatic == nelectrons)
440     // break;
441     // if (((OBAtom*)*m)->GetValence() == 2 &&
442     // !_aromNH[((OBAtom*)*m)->GetIdx()] &&
443     // ((OBAtom*)*m)->IsInRingSize(5) &&
444     // ((OBAtom*)*m)->BOSum() == 2)
445     // {
446     // nelectrons++;
447     // _aromNH[((OBAtom*)*m)->GetIdx()] = true;
448     // }
449     // }
450    
451     // if (naromatic == nelectrons)
452     // continue;
453    
454     // //protonate amines in 5 membered rings first - no kekule restriction
455     // for (m = v_N.begin();m != v_N.end();m++)
456     // {
457     // if (naromatic == nelectrons)
458     // break;
459     // if ((*m)->GetValence() == 2 && !_aromNH[(*m)->GetIdx()] &&
460     // (*m)->IsInRingSize(5))
461     // {
462     // nelectrons++;
463     // _aromNH[(*m)->GetIdx()] = true;
464     // }
465     // }
466    
467     // if (naromatic == nelectrons)
468     // continue;
469    
470     // //then others -- try to find an atom w/o a double bond first
471     // for (m = v_N.begin();m != v_N.end();m++)
472     // {
473     // if (naromatic == nelectrons)
474     // break;
475     // if (((OBAtom*)*m)->GetHvyValence() == 2 &&
476     // !_aromNH[((OBAtom*)*m)->GetIdx()] &&
477     // !((OBAtom*)*m)->HasDoubleBond())
478     // {
479     // nelectrons++;
480     // _aromNH[(*m)->GetIdx()] = true;
481     // }
482     // }
483    
484     // for (m = v_N.begin();m != v_N.end();m++)
485     // {
486     // if (naromatic == nelectrons)
487     // break;
488     // if ((*m)->GetHvyValence() == 2 && !_aromNH[(*m)->GetIdx()])
489     // {
490     // nelectrons++;
491     // _aromNH[(*m)->GetIdx()] = true;
492     // }
493     // }
494     // }
495    
496     // for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
497     // if (_aromNH[atom->GetIdx()] && atom->GetValence() == 2)
498     // atom->SetImplicitValence(3);
499     }
500    
501     /*! \class OBAromaticTyper
502     \brief Assigns aromatic typing to atoms and bonds
503    
504     The OBAromaticTyper class is designed to read in a list of
505     aromatic perception rules and apply them to molecules. The code
506     that performs typing is not usually used directly -- it is usually
507     done automatically when their corresponding values are requested of atoms
508     or bonds.
509     \code
510     atom->IsAromatic();
511     bond->IsAromatic();
512     bond->IsDouble(); // needs to check aromaticity and define Kekule structures
513     \endcode
514     */
515     OBAromaticTyper::OBAromaticTyper()
516     {
517     _init = false;
518 gezelter 3057 STR_DEFINE(_dir, FRC_PATH );
519 gezelter 2450 _envvar = "FORCE_PARAM_PATH";
520 tim 2440 _filename = "aromatic.txt";
521     _subdir = "data";
522     _dataptr = AromaticData;
523     }
524    
525     void OBAromaticTyper::ParseLine(const char *buffer)
526     {
527     OBSmartsPattern *sp;
528     char temp_buffer[BUFF_SIZE];
529    
530     if (buffer[0] == '#')
531     return;
532     vector<string> vs;
533     tokenize(vs,buffer);
534 gezelter 3057 if (vs.empty())
535     return;
536    
537     if (vs.size() == 3)
538 tim 2440 {
539 gezelter 3057 strncpy(temp_buffer,vs[0].c_str(), BUFF_SIZE - 1);
540     temp_buffer[BUFF_SIZE - 1] = '\0';
541 tim 2440 sp = new OBSmartsPattern();
542     if (sp->Init(temp_buffer))
543     {
544     _vsp.push_back(sp);
545     _verange.push_back(pair<int,int>
546     (atoi((char*)vs[1].c_str()),
547     atoi((char*)vs[2].c_str())));
548     }
549     else
550     {
551 tim 2518 obErrorLog.ThrowError(__func__, " Could not parse line in aromatic typer from aromatic.txt", obInfo);
552 tim 2440 delete sp;
553     sp = NULL;
554     return;
555     }
556     }
557     else
558 tim 2518 obErrorLog.ThrowError(__func__, " Could not parse line in aromatic typer from aromatic.txt", obInfo);
559 tim 2440 }
560    
561     OBAromaticTyper::~OBAromaticTyper()
562     {
563     vector<OBSmartsPattern*>::iterator i;
564     for (i = _vsp.begin();i != _vsp.end();i++)
565     {
566     delete *i;
567     *i = NULL;
568     }
569     }
570    
571     void OBAromaticTyper::AssignAromaticFlags(OBMol &mol)
572     {
573     if (!_init)
574     Init();
575    
576     if (mol.HasAromaticPerceived())
577     return;
578     mol.SetAromaticPerceived();
579 tim 2518 obErrorLog.ThrowError(__func__,
580 tim 2440 "Ran OpenBabel::AssignAromaticFlags", obAuditMsg);
581    
582     _vpa.clear();
583     _vpa.resize(mol.NumAtoms()+1);
584     _velec.clear();
585     _velec.resize(mol.NumAtoms()+1);
586     _root.clear();
587     _root.resize(mol.NumAtoms()+1);
588    
589     OBBond *bond;
590     OBAtom *atom;
591     vector<OBNodeBase*>::iterator i;
592     vector<OBEdgeBase*>::iterator j;
593    
594     //unset all aromatic flags
595     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
596     atom->UnsetAromatic();
597     for (bond = mol.BeginBond(j);bond;bond = mol.NextBond(j))
598     bond->UnsetAromatic();
599    
600     int idx;
601     vector<vector<int> >::iterator m;
602     vector<OBSmartsPattern*>::iterator k;
603    
604     //mark atoms as potentially aromatic
605     for (idx=0,k = _vsp.begin();k != _vsp.end();k++,idx++)
606     if ((*k)->Match(mol))
607     {
608     _mlist = (*k)->GetMapList();
609     for (m = _mlist.begin();m != _mlist.end();m++)
610     {
611     _vpa[(*m)[0]] = true;
612     _velec[(*m)[0]] = _verange[idx];
613     }
614     }
615    
616     //sanity check - exclude all 4 substituted atoms and sp centers
617     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
618     {
619     if (atom->GetImplicitValence() > 3)
620     {
621     _vpa[atom->GetIdx()] = false;
622     continue;
623     }
624    
625     switch(atom->GetAtomicNum())
626     {
627     //phosphorus and sulfur may be initially typed as sp3
628     case 6:
629     case 7:
630     case 8:
631     if (atom->GetHyb() != 2)
632     _vpa[atom->GetIdx()] = false;
633     break;
634     }
635     }
636    
637     //propagate potentially aromatic atoms
638     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
639     if (_vpa[atom->GetIdx()])
640     PropagatePotentialAromatic(atom);
641    
642     //select root atoms
643     SelectRootAtoms(mol);
644    
645     ExcludeSmallRing(mol); //remove 3 membered rings from consideration
646    
647     //loop over root atoms and look for 5-6 membered aromatic rings
648     _visit.clear();
649     _visit.resize(mol.NumAtoms()+1);
650     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
651     if (_root[atom->GetIdx()])
652     CheckAromaticity(atom,6);
653    
654     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
655     if (_root[atom->GetIdx()])
656     CheckAromaticity(atom,20);
657    
658 gezelter 3057 // cleanup -- check for ring bonds where both atoms are aromatic
659     // but for some reason the bond is not (e.g., between a fused system)
660     for (bond = mol.BeginBond(j);bond;bond = mol.NextBond(j))
661     {
662     if (bond->IsInRing() && (bond->GetBeginAtom())->IsAromatic()
663     && (bond->GetEndAtom())->IsAromatic())
664     bond->SetAromatic();
665     }
666    
667 tim 2440 //for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
668     // if (atom->IsAromatic())
669     // cerr << "aro = " <<atom->GetIdx() << endl;
670    
671     //for (bond = mol.BeginBond(j);bond;bond = mol.NextBond(j))
672     //if (bond->IsAromatic())
673     //cerr << bond->GetIdx() << ' ' << bond->IsAromatic() << endl;
674     }
675    
676     bool OBAromaticTyper::TraverseCycle(OBAtom *root, OBAtom *atom, OBBond *prev,
677     std::pair<int,int> &er,int depth)
678     {
679     if (atom == root)
680     {
681     int i;
682     for (i = er.first;i <= er.second;i++)
683     if (i%4 == 2 && i > 2)
684     return(true);
685    
686     return(false);
687     }
688    
689     if (!depth || !_vpa[atom->GetIdx()] || _visit[atom->GetIdx()])
690     return(false);
691    
692     bool result = false;
693    
694     depth--;
695     er.first += _velec[atom->GetIdx()].first;
696     er.second += _velec[atom->GetIdx()].second;
697    
698     _visit[atom->GetIdx()] = true;
699     OBAtom *nbr;
700     vector<OBEdgeBase*>::iterator i;
701     for (nbr = atom->BeginNbrAtom(i);nbr;nbr = atom->NextNbrAtom(i))
702     if (*i != prev && (*i)->IsInRing() && _vpa[nbr->GetIdx()])
703     {
704     if (TraverseCycle(root,nbr,(OBBond*)(*i),er,depth))
705     {
706     result = true;
707     ((OBBond*) *i)->SetAromatic();
708     }
709     }
710    
711     _visit[atom->GetIdx()] = false;
712     if (result)
713     atom->SetAromatic();
714    
715     er.first -= _velec[atom->GetIdx()].first;
716     er.second -= _velec[atom->GetIdx()].second;
717    
718     return(result);
719     }
720    
721     void OBAromaticTyper::CheckAromaticity(OBAtom *atom,int depth)
722     {
723     OBAtom *nbr;
724     vector<OBEdgeBase*>::iterator i;
725    
726     pair<int,int> erange;
727     for (nbr = atom->BeginNbrAtom(i);nbr;nbr = atom->NextNbrAtom(i))
728     if ((*i)->IsInRing()) // check all rings, regardless of assumed aromaticity
729     {
730     erange = _velec[atom->GetIdx()];
731    
732     if (TraverseCycle(atom,nbr,(OBBond *)*i,erange,depth-1))
733     {
734     atom->SetAromatic();
735     ((OBBond*) *i)->SetAromatic();
736     }
737     }
738     }
739    
740     void OBAromaticTyper::PropagatePotentialAromatic(OBAtom *atom)
741     {
742     int count = 0;
743     OBAtom *nbr;
744     vector<OBEdgeBase*>::iterator i;
745    
746     for (nbr = atom->BeginNbrAtom(i);nbr;nbr = atom->NextNbrAtom(i))
747     if ((*i)->IsInRing() && _vpa[nbr->GetIdx()])
748     count++;
749    
750     if (count < 2)
751     {
752     _vpa[atom->GetIdx()] = false;
753     if (count == 1)
754     for (nbr = atom->BeginNbrAtom(i);nbr;nbr = atom->NextNbrAtom(i))
755     if ((*i)->IsInRing() && _vpa[nbr->GetIdx()])
756     PropagatePotentialAromatic(nbr);
757     }
758     }
759    
760     /**
761     * Select the root atoms for traversing atoms in rings.
762     *
763     * Picking only the begin atom of a closure bond can cause
764     * difficulties when the selected atom is an inner atom
765     * with three neighbour ring atoms. Why ? Because this atom
766     * can get trapped by the other atoms when determining aromaticity,
767     * because a simple visited flag is used in the
768     * OBAromaticTyper::TraverseCycle() method.
769     *
770     * Ported from JOELib, copyright Joerg Wegner, 2003 under the GPL version 2
771     *
772     * @param mol the molecule
773     * @param avoidInnerRingAtoms inner closure ring atoms with more than 2 neighbours will be avoided
774     *
775     */
776     void OBAromaticTyper::SelectRootAtoms(OBMol &mol, bool avoidInnerRingAtoms)
777     {
778     OBBond *bond;
779     OBAtom *atom, *nbr, *nbr2;
780     OBRing *ring;
781     // vector<OBNodeBase*>::iterator i;
782     vector<OBEdgeBase*>::iterator j, l, nbr2Iter;
783     vector<OBRing*> sssRings = mol.GetSSSR();
784     vector<OBRing*>::iterator k;
785    
786     int rootAtom;
787     int ringNbrs;
788     int heavyNbrs;
789     int newRoot = -1;
790     vector<int> tmpRootAtoms;
791     vector<int> tmp;
792    
793     for (bond = mol.BeginBond(j);bond;bond = mol.NextBond(j))
794     if (bond->IsClosure())
795     tmpRootAtoms.push_back(bond->GetBeginAtomIdx());
796    
797     for (bond = mol.BeginBond(j);bond;bond = mol.NextBond(j))
798     if (bond->IsClosure())
799     {
800     // BASIC APPROACH
801     // pick beginning atom at closure bond
802     // this is really ready, isn't it ! ;-)
803     rootAtom = bond->GetBeginAtomIdx();
804     _root[rootAtom] = true;
805    
806     // EXTENDED APPROACH
807     if (avoidInnerRingAtoms)
808     {
809     // count the number of neighbor ring atoms
810     atom = mol.GetAtom(rootAtom);
811     ringNbrs = heavyNbrs = 0;
812    
813     for (nbr = atom->BeginNbrAtom(l);nbr;nbr = atom->NextNbrAtom(l))
814     {
815     // we can get this from atom->GetHvyValence()
816     // but we need to find neighbors in rings too
817     // so let's save some time
818     if (!nbr->IsHydrogen())
819     {
820     heavyNbrs++;
821     if (nbr->IsInRing())
822     ringNbrs++;
823     }
824    
825     // if this atom has more than 2 neighbor ring atoms
826     // we could get trapped later when traversing cycles
827     // which can cause aromaticity false detection
828     newRoot = -1;
829    
830     if (ringNbrs > 2)
831     {
832     // try to find another root atom
833     for (k = sssRings.begin();k != sssRings.end();k++)
834     {
835     ring = (*k);
836     tmp = ring->_path;
837    
838     bool checkThisRing = false;
839     int rootAtomNumber=0;
840     int idx=0;
841     // avoiding two root atoms in one ring !
842     for (unsigned int j = 0; j < tmpRootAtoms.size(); j++)
843     {
844     idx= tmpRootAtoms[j];
845     if(ring->IsInRing(idx))
846     {
847     rootAtomNumber++;
848     if(rootAtomNumber>=2)
849     break;
850     }
851     }
852     if(rootAtomNumber<2)
853     {
854     for (unsigned int j = 0; j < tmp.size(); j++)
855     {
856     // find critical ring
857     if (tmp[j] == rootAtom)
858     {
859     checkThisRing = true;
860     }
861     else
862     {
863     // second root atom in this ring ?
864     if (_root[tmp[j]] == true)
865     {
866     // when there is a second root
867     // atom this ring can not be
868     // used for getting an other
869     // root atom
870     checkThisRing = false;
871    
872     break;
873     }
874     }
875     }
876     }
877    
878     // check ring for getting another
879     // root atom to avoid aromaticity typer problems
880     if (checkThisRing)
881     {
882     // check if we can find another root atom
883     for (unsigned int m = 0; m < tmp.size(); m++)
884     {
885     ringNbrs = heavyNbrs = 0;
886     for (nbr2 = (mol.GetAtom(tmp[m]))->BeginNbrAtom(nbr2Iter);
887     nbr2;nbr2 = (mol.GetAtom(tmp[m]))->NextNbrAtom(nbr2Iter))
888     {
889     if (!nbr2->IsHydrogen())
890     {
891     heavyNbrs++;
892    
893     if (nbr2->IsInRing())
894     ringNbrs++;
895     }
896     }
897    
898     // if the number of neighboured heavy atoms is also
899     // the number of neighboured ring atoms, the aromaticity
900     // typer could be stuck in a local traversing trap
901     if (ringNbrs <= 2 && ring->IsInRing((mol.GetAtom(tmp[m])->GetIdx())))
902     {
903     newRoot = tmp[m];
904     }
905     }
906     }
907     }
908    
909     if ((newRoot != -1) && (rootAtom != newRoot))
910     {
911     // unset root atom
912     _root[rootAtom] = false;
913    
914     // pick new root atom
915     _root[newRoot] = true;
916     }
917     } // if (ringNbrs > 2)
918    
919     } // end for
920     } // if (avoid)
921     } // if (bond.IsClosure())
922     }
923    
924     void OBAromaticTyper::ExcludeSmallRing(OBMol &mol)
925     {
926     OBAtom *atom,*nbr1,*nbr2;
927     vector<OBNodeBase*>::iterator i;
928     vector<OBEdgeBase*>::iterator j,k;
929    
930     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
931     if (_root[atom->GetIdx()])
932     for (nbr1 = atom->BeginNbrAtom(j);nbr1;nbr1 = atom->NextNbrAtom(j))
933     if ((*j)->IsInRing() && _vpa[nbr1->GetIdx()])
934     for (nbr2 = nbr1->BeginNbrAtom(k);nbr2;nbr2 = nbr1->NextNbrAtom(k))
935     if (nbr2 != atom && (*k)->IsInRing() && _vpa[nbr2->GetIdx()])
936     if (atom->IsConnected(nbr2))
937     _root[atom->GetIdx()] = false;
938     }
939    
940     } //namespace OpenBabel;
941    
942     //! \file typer.cpp
943     //! \brief Open Babel atom and aromaticity typer.