ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-2.0/src/openbabel/typer.cpp
Revision: 2518
Committed: Fri Dec 16 21:52:50 2005 UTC (18 years, 6 months ago) by tim
File size: 30847 byte(s)
Log Message:
changed __FUNCTION__ to __func__ to match C99 standard, and then added
an autoconf test to check for __func__ usability.  Changed some default
compile flags for the Sun architecture

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     Some portions Copyright (C) 2001-2005 by Geoffrey R. Hutchison
6    
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 2450 STR_DEFINE(_dir, FRC_PATH);
56     _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 2450 STR_DEFINE(_dir, FRC_PATH);
519     _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     if (!vs.empty() && vs.size() == 3)
535     {
536     strcpy(temp_buffer,vs[0].c_str());
537     sp = new OBSmartsPattern();
538     if (sp->Init(temp_buffer))
539     {
540     _vsp.push_back(sp);
541     _verange.push_back(pair<int,int>
542     (atoi((char*)vs[1].c_str()),
543     atoi((char*)vs[2].c_str())));
544     }
545     else
546     {
547 tim 2518 obErrorLog.ThrowError(__func__, " Could not parse line in aromatic typer from aromatic.txt", obInfo);
548 tim 2440 delete sp;
549     sp = NULL;
550     return;
551     }
552     }
553     else
554 tim 2518 obErrorLog.ThrowError(__func__, " Could not parse line in aromatic typer from aromatic.txt", obInfo);
555 tim 2440 }
556    
557     OBAromaticTyper::~OBAromaticTyper()
558     {
559     vector<OBSmartsPattern*>::iterator i;
560     for (i = _vsp.begin();i != _vsp.end();i++)
561     {
562     delete *i;
563     *i = NULL;
564     }
565     }
566    
567     void OBAromaticTyper::AssignAromaticFlags(OBMol &mol)
568     {
569     if (!_init)
570     Init();
571    
572     if (mol.HasAromaticPerceived())
573     return;
574     mol.SetAromaticPerceived();
575 tim 2518 obErrorLog.ThrowError(__func__,
576 tim 2440 "Ran OpenBabel::AssignAromaticFlags", obAuditMsg);
577    
578     _vpa.clear();
579     _vpa.resize(mol.NumAtoms()+1);
580     _velec.clear();
581     _velec.resize(mol.NumAtoms()+1);
582     _root.clear();
583     _root.resize(mol.NumAtoms()+1);
584    
585     OBBond *bond;
586     OBAtom *atom;
587     vector<OBNodeBase*>::iterator i;
588     vector<OBEdgeBase*>::iterator j;
589    
590     //unset all aromatic flags
591     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
592     atom->UnsetAromatic();
593     for (bond = mol.BeginBond(j);bond;bond = mol.NextBond(j))
594     bond->UnsetAromatic();
595    
596     int idx;
597     vector<vector<int> >::iterator m;
598     vector<OBSmartsPattern*>::iterator k;
599    
600     //mark atoms as potentially aromatic
601     for (idx=0,k = _vsp.begin();k != _vsp.end();k++,idx++)
602     if ((*k)->Match(mol))
603     {
604     _mlist = (*k)->GetMapList();
605     for (m = _mlist.begin();m != _mlist.end();m++)
606     {
607     _vpa[(*m)[0]] = true;
608     _velec[(*m)[0]] = _verange[idx];
609     }
610     }
611    
612     //sanity check - exclude all 4 substituted atoms and sp centers
613     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
614     {
615     if (atom->GetImplicitValence() > 3)
616     {
617     _vpa[atom->GetIdx()] = false;
618     continue;
619     }
620    
621     switch(atom->GetAtomicNum())
622     {
623     //phosphorus and sulfur may be initially typed as sp3
624     case 6:
625     case 7:
626     case 8:
627     if (atom->GetHyb() != 2)
628     _vpa[atom->GetIdx()] = false;
629     break;
630     }
631     }
632    
633     //propagate potentially aromatic atoms
634     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
635     if (_vpa[atom->GetIdx()])
636     PropagatePotentialAromatic(atom);
637    
638     //select root atoms
639     SelectRootAtoms(mol);
640    
641     ExcludeSmallRing(mol); //remove 3 membered rings from consideration
642    
643     //loop over root atoms and look for 5-6 membered aromatic rings
644     _visit.clear();
645     _visit.resize(mol.NumAtoms()+1);
646     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
647     if (_root[atom->GetIdx()])
648     CheckAromaticity(atom,6);
649    
650     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
651     if (_root[atom->GetIdx()])
652     CheckAromaticity(atom,20);
653    
654     //for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
655     // if (atom->IsAromatic())
656     // cerr << "aro = " <<atom->GetIdx() << endl;
657    
658     //for (bond = mol.BeginBond(j);bond;bond = mol.NextBond(j))
659     //if (bond->IsAromatic())
660     //cerr << bond->GetIdx() << ' ' << bond->IsAromatic() << endl;
661     }
662    
663     bool OBAromaticTyper::TraverseCycle(OBAtom *root, OBAtom *atom, OBBond *prev,
664     std::pair<int,int> &er,int depth)
665     {
666     if (atom == root)
667     {
668     int i;
669     for (i = er.first;i <= er.second;i++)
670     if (i%4 == 2 && i > 2)
671     return(true);
672    
673     return(false);
674     }
675    
676     if (!depth || !_vpa[atom->GetIdx()] || _visit[atom->GetIdx()])
677     return(false);
678    
679     bool result = false;
680    
681     depth--;
682     er.first += _velec[atom->GetIdx()].first;
683     er.second += _velec[atom->GetIdx()].second;
684    
685     _visit[atom->GetIdx()] = true;
686     OBAtom *nbr;
687     vector<OBEdgeBase*>::iterator i;
688     for (nbr = atom->BeginNbrAtom(i);nbr;nbr = atom->NextNbrAtom(i))
689     if (*i != prev && (*i)->IsInRing() && _vpa[nbr->GetIdx()])
690     {
691     if (TraverseCycle(root,nbr,(OBBond*)(*i),er,depth))
692     {
693     result = true;
694     ((OBBond*) *i)->SetAromatic();
695     }
696     }
697    
698     _visit[atom->GetIdx()] = false;
699     if (result)
700     atom->SetAromatic();
701    
702     er.first -= _velec[atom->GetIdx()].first;
703     er.second -= _velec[atom->GetIdx()].second;
704    
705     return(result);
706     }
707    
708     void OBAromaticTyper::CheckAromaticity(OBAtom *atom,int depth)
709     {
710     OBAtom *nbr;
711     vector<OBEdgeBase*>::iterator i;
712    
713     pair<int,int> erange;
714     for (nbr = atom->BeginNbrAtom(i);nbr;nbr = atom->NextNbrAtom(i))
715     if ((*i)->IsInRing()) // check all rings, regardless of assumed aromaticity
716     {
717     erange = _velec[atom->GetIdx()];
718    
719     if (TraverseCycle(atom,nbr,(OBBond *)*i,erange,depth-1))
720     {
721     atom->SetAromatic();
722     ((OBBond*) *i)->SetAromatic();
723     }
724     }
725     }
726    
727     void OBAromaticTyper::PropagatePotentialAromatic(OBAtom *atom)
728     {
729     int count = 0;
730     OBAtom *nbr;
731     vector<OBEdgeBase*>::iterator i;
732    
733     for (nbr = atom->BeginNbrAtom(i);nbr;nbr = atom->NextNbrAtom(i))
734     if ((*i)->IsInRing() && _vpa[nbr->GetIdx()])
735     count++;
736    
737     if (count < 2)
738     {
739     _vpa[atom->GetIdx()] = false;
740     if (count == 1)
741     for (nbr = atom->BeginNbrAtom(i);nbr;nbr = atom->NextNbrAtom(i))
742     if ((*i)->IsInRing() && _vpa[nbr->GetIdx()])
743     PropagatePotentialAromatic(nbr);
744     }
745     }
746    
747     /**
748     * Select the root atoms for traversing atoms in rings.
749     *
750     * Picking only the begin atom of a closure bond can cause
751     * difficulties when the selected atom is an inner atom
752     * with three neighbour ring atoms. Why ? Because this atom
753     * can get trapped by the other atoms when determining aromaticity,
754     * because a simple visited flag is used in the
755     * OBAromaticTyper::TraverseCycle() method.
756     *
757     * Ported from JOELib, copyright Joerg Wegner, 2003 under the GPL version 2
758     *
759     * @param mol the molecule
760     * @param avoidInnerRingAtoms inner closure ring atoms with more than 2 neighbours will be avoided
761     *
762     */
763     void OBAromaticTyper::SelectRootAtoms(OBMol &mol, bool avoidInnerRingAtoms)
764     {
765     OBBond *bond;
766     OBAtom *atom, *nbr, *nbr2;
767     OBRing *ring;
768     // vector<OBNodeBase*>::iterator i;
769     vector<OBEdgeBase*>::iterator j, l, nbr2Iter;
770     vector<OBRing*> sssRings = mol.GetSSSR();
771     vector<OBRing*>::iterator k;
772    
773     int rootAtom;
774     int ringNbrs;
775     int heavyNbrs;
776     int newRoot = -1;
777     vector<int> tmpRootAtoms;
778     vector<int> tmp;
779    
780     for (bond = mol.BeginBond(j);bond;bond = mol.NextBond(j))
781     if (bond->IsClosure())
782     tmpRootAtoms.push_back(bond->GetBeginAtomIdx());
783    
784     for (bond = mol.BeginBond(j);bond;bond = mol.NextBond(j))
785     if (bond->IsClosure())
786     {
787     // BASIC APPROACH
788     // pick beginning atom at closure bond
789     // this is really ready, isn't it ! ;-)
790     rootAtom = bond->GetBeginAtomIdx();
791     _root[rootAtom] = true;
792    
793     // EXTENDED APPROACH
794     if (avoidInnerRingAtoms)
795     {
796     // count the number of neighbor ring atoms
797     atom = mol.GetAtom(rootAtom);
798     ringNbrs = heavyNbrs = 0;
799    
800     for (nbr = atom->BeginNbrAtom(l);nbr;nbr = atom->NextNbrAtom(l))
801     {
802     // we can get this from atom->GetHvyValence()
803     // but we need to find neighbors in rings too
804     // so let's save some time
805     if (!nbr->IsHydrogen())
806     {
807     heavyNbrs++;
808     if (nbr->IsInRing())
809     ringNbrs++;
810     }
811    
812     // if this atom has more than 2 neighbor ring atoms
813     // we could get trapped later when traversing cycles
814     // which can cause aromaticity false detection
815     newRoot = -1;
816    
817     if (ringNbrs > 2)
818     {
819     // try to find another root atom
820     for (k = sssRings.begin();k != sssRings.end();k++)
821     {
822     ring = (*k);
823     tmp = ring->_path;
824    
825     bool checkThisRing = false;
826     int rootAtomNumber=0;
827     int idx=0;
828     // avoiding two root atoms in one ring !
829     for (unsigned int j = 0; j < tmpRootAtoms.size(); j++)
830     {
831     idx= tmpRootAtoms[j];
832     if(ring->IsInRing(idx))
833     {
834     rootAtomNumber++;
835     if(rootAtomNumber>=2)
836     break;
837     }
838     }
839     if(rootAtomNumber<2)
840     {
841     for (unsigned int j = 0; j < tmp.size(); j++)
842     {
843     // find critical ring
844     if (tmp[j] == rootAtom)
845     {
846     checkThisRing = true;
847     }
848     else
849     {
850     // second root atom in this ring ?
851     if (_root[tmp[j]] == true)
852     {
853     // when there is a second root
854     // atom this ring can not be
855     // used for getting an other
856     // root atom
857     checkThisRing = false;
858    
859     break;
860     }
861     }
862     }
863     }
864    
865     // check ring for getting another
866     // root atom to avoid aromaticity typer problems
867     if (checkThisRing)
868     {
869     // check if we can find another root atom
870     for (unsigned int m = 0; m < tmp.size(); m++)
871     {
872     ringNbrs = heavyNbrs = 0;
873     for (nbr2 = (mol.GetAtom(tmp[m]))->BeginNbrAtom(nbr2Iter);
874     nbr2;nbr2 = (mol.GetAtom(tmp[m]))->NextNbrAtom(nbr2Iter))
875     {
876     if (!nbr2->IsHydrogen())
877     {
878     heavyNbrs++;
879    
880     if (nbr2->IsInRing())
881     ringNbrs++;
882     }
883     }
884    
885     // if the number of neighboured heavy atoms is also
886     // the number of neighboured ring atoms, the aromaticity
887     // typer could be stuck in a local traversing trap
888     if (ringNbrs <= 2 && ring->IsInRing((mol.GetAtom(tmp[m])->GetIdx())))
889     {
890     newRoot = tmp[m];
891     }
892     }
893     }
894     }
895    
896     if ((newRoot != -1) && (rootAtom != newRoot))
897     {
898     // unset root atom
899     _root[rootAtom] = false;
900    
901     // pick new root atom
902     _root[newRoot] = true;
903     }
904     } // if (ringNbrs > 2)
905    
906     } // end for
907     } // if (avoid)
908     } // if (bond.IsClosure())
909     }
910    
911     void OBAromaticTyper::ExcludeSmallRing(OBMol &mol)
912     {
913     OBAtom *atom,*nbr1,*nbr2;
914     vector<OBNodeBase*>::iterator i;
915     vector<OBEdgeBase*>::iterator j,k;
916    
917     for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
918     if (_root[atom->GetIdx()])
919     for (nbr1 = atom->BeginNbrAtom(j);nbr1;nbr1 = atom->NextNbrAtom(j))
920     if ((*j)->IsInRing() && _vpa[nbr1->GetIdx()])
921     for (nbr2 = nbr1->BeginNbrAtom(k);nbr2;nbr2 = nbr1->NextNbrAtom(k))
922     if (nbr2 != atom && (*k)->IsInRing() && _vpa[nbr2->GetIdx()])
923     if (atom->IsConnected(nbr2))
924     _root[atom->GetIdx()] = false;
925     }
926    
927     } //namespace OpenBabel;
928    
929     //! \file typer.cpp
930     //! \brief Open Babel atom and aromaticity typer.