1
/**********************************************************************
2
Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation version 2 of the License.
8
This program is distributed in the hope that it will be useful,
9
but WITHOUT ANY WARRANTY; without even the implied warranty of
10
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
GNU General Public License for more details.
12
***********************************************************************/
22
bool WriteSmiles(ostream &ofs,OBMol &mol,char *title)
24
char buffer[BUFF_SIZE],tmp[BUFF_SIZE];
29
m2s.CorrectAromaticAmineCharge(mol);
30
m2s.CreateSmiString(mol,buffer);
32
strcpy(tmp,(title) ? title:mol.GetTitle());
33
ofs << buffer << ' ' << tmp << endl;
37
void OBMol2Smi::CreateSmiString(OBMol &mol,char *buffer)
42
vector<OBNodeBase*>::iterator i;
44
for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
45
// if ((!atom->IsHydrogen() || atom->GetValence() == 0) && !_uatoms[atom->GetIdx()])
46
if (!atom->IsHydrogen() && !_uatoms[atom->GetIdx()])
47
if (!atom->IsChiral()) //don't use chiral atoms as root node
49
//clear out closures in case structure is dot disconnected
54
//dot disconnected structure
55
if (strlen(buffer) > 0) strcat(buffer,".");
56
root = new OBSmiNode (atom);
58
FindClosureBonds(mol);
59
if (mol.Has2D()) AssignCisTrans(root);
60
ToSmilesString(root,buffer);
65
bool OBMol2Smi::BuildTree(OBSmiNode *node)
67
vector<OBEdgeBase*>::iterator i;
68
OBAtom *nbr,*atom = node->GetAtom();
70
_uatoms.SetBitOn(atom->GetIdx()); //mark the atom as visited
71
_atmorder.push_back(atom->GetIdx()); //store the atom ordering
72
_storder.push_back(atom->GetIdx()); //store the atom ordering for stereo
74
for (nbr = atom->BeginNbrAtom(i);nbr;nbr = atom->NextNbrAtom(i))
75
if (!nbr->IsHydrogen() && !_uatoms[nbr->GetIdx()])
77
_ubonds.SetBitOn((*i)->GetIdx());
78
OBSmiNode *next = new OBSmiNode (nbr);
79
next->SetParent(atom);
80
node->SetNextNode(next,(OBBond*)*i);
87
void OBMol2Smi::ToSmilesString(OBSmiNode *node,char *buffer)
90
OBAtom *atom = node->GetAtom();
92
//write the current atom to the string
93
GetSmilesElement(node,tmpbuf);
94
strcat(buffer,tmpbuf);
96
//handle ring closures here
97
vector<pair<int,OBBond*> > vc = GetClosureDigits(atom);
100
vector<pair<int,OBBond*> >::iterator i;
101
for (i = vc.begin();i != vc.end();i++)
105
if (i->second->IsUp()) strcat(buffer,"\\");
106
if (i->second->IsDown()) strcat(buffer,"/");
108
if (i->second->GetBO() == 2 && !i->second->IsAromatic()) strcat(buffer,"=");
110
if (i->second->GetBO() == 2) strcat(buffer,"=");
112
if (i->second->GetBO() == 3) strcat(buffer,"#");
115
if (i->first > 9) strcat(buffer,"%");
116
sprintf(tmpbuf,"%d",i->first);
117
strcat(buffer,tmpbuf);
121
//follow path to child atoms
123
for (int i = 0;i < node->Size();i++)
125
bond = node->GetNextBond(i);
126
if (i+1 < node->Size()) strcat(buffer,"(");
127
if (bond->IsUp()) strcat(buffer,"\\");
128
if (bond->IsDown()) strcat(buffer,"/");
130
if (bond->GetBO() == 2 && !bond->IsAromatic()) strcat(buffer,"=");
132
if (bond->GetBO() == 2) strcat(buffer,"=");
134
if (bond->GetBO() == 3) strcat(buffer,"#");
136
ToSmilesString(node->GetNextNode(i),buffer);
137
if (i+1 < node->Size()) strcat(buffer,")");
141
void OBMol2Smi::GetClosureAtoms(OBAtom *atom,vector<OBNodeBase*> &va)
144
//look through closure list for start atom
145
vector<OBEdgeBase*>::iterator i;
146
for (i = _vclose.begin();i != _vclose.end();i++)
149
if (((OBBond*)*i)->GetBeginAtom() == atom)
150
va.push_back(((OBBond*)*i)->GetEndAtom());
151
if (((OBBond*)*i)->GetEndAtom() == atom)
152
va.push_back(((OBBond*)*i)->GetBeginAtom());
156
vector<pair<OBAtom*,pair<int,int> > >::iterator j;
157
for (j = _vopen.begin();j != _vopen.end();j++)
158
for (nbr = atom->BeginNbrAtom(i);nbr;nbr = atom->NextNbrAtom(i))
163
vector<pair<int,OBBond*> > OBMol2Smi::GetClosureDigits(OBAtom *atom)
165
vector<pair<int,OBBond*> > vc; vc.clear();
167
//look through closure list for start atom
170
vector<OBEdgeBase*>::iterator i;
171
for (i = _vclose.begin();i != _vclose.end();i++)
172
if ((bond=(OBBond*)*i))
173
if (bond->GetBeginAtom() == atom || bond->GetEndAtom() == atom)
175
idx = GetUnusedIndex();
176
vc.push_back(pair<int,OBBond*> (idx,bond));
177
bo = (bond->IsAromatic())? 1 : bond->GetBO();
178
_vopen.push_back(pair<OBAtom*,pair<int,int> >
179
(bond->GetNbrAtom(atom),pair<int,int>(idx,bo)));
180
*i = NULL;//remove bond from closure list
183
//try to complete closures
186
vector<pair<OBAtom*,pair<int,int> > >::iterator j;
187
for (j = _vopen.begin();j != _vopen.end();)
188
if (j->first == atom)
190
vc.push_back(pair<int,OBBond*> (j->second.first,(OBBond*)NULL));
200
void OBMol2Smi::FindClosureBonds(OBMol &mol)
205
vector<OBEdgeBase*>::iterator i;
207
bv.FromVecInt(_storder);
209
for (bond = mol.BeginBond(i);bond;bond = mol.NextBond(i))
210
if (!_ubonds[bond->GetIdx()] && bv[bond->GetBeginAtomIdx()])
212
a1 = bond->GetBeginAtom();
213
a2 = bond->GetEndAtom();
214
if (!a1->IsHydrogen() && !a2->IsHydrogen())
215
_vclose.push_back(bond);
218
vector<OBEdgeBase*>::reverse_iterator j;
219
vector<int>::iterator k;
221
//modify _order to reflect ring closures
222
for (j = _vclose.rbegin();j != _vclose.rend();j++)
227
for (k = _storder.begin();k != _storder.end();k++)
228
if (bond->GetBeginAtomIdx() == *k ||
229
bond->GetEndAtomIdx() == *k)
230
if (!a1) a1 = mol.GetAtom(*k);
233
a2 = mol.GetAtom(*k);
238
for (k = _storder.begin();k != _storder.end();k++)
239
if (a1->GetIdx() == *k)
242
if (k != _storder.end()) _storder.insert(k,a2->GetIdx());
243
else _storder.push_back(a2->GetIdx());
249
int OBMol2Smi::GetUnusedIndex()
253
vector<pair<OBAtom*,pair<int,int> > >::iterator j;
254
for (j = _vopen.begin();j != _vopen.end();)
255
if (j->second.first == idx)
257
idx++; //increment idx and start over if digit is already used
265
void OBMol2Smi::CorrectAromaticAmineCharge(OBMol &mol)
268
vector<OBNodeBase*>::iterator i;
271
_aromNH.resize(mol.NumAtoms()+1);
273
for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
274
if (atom->IsNitrogen() && atom->IsAromatic())
275
if (atom->GetHvyValence() == 2)
277
if (atom->GetValence() == 3 || atom->GetImplicitValence() == 3)
278
_aromNH[atom->GetIdx()] = true;
282
void OBMol2Smi::AssignCisTrans(OBSmiNode *node)
284
//traverse the tree searching for acyclic olefins - if it
285
//has at least one heavy atom attachment on each end assign stereochem
288
for (int i = 0;i < node->Size();i++)
290
bond = node->GetNextBond(i);
291
if (bond->GetBO() == 2 && !bond->IsInRing())
293
OBAtom *b = node->GetAtom();
294
OBAtom *c = bond->GetNbrAtom(b);
297
if (b->GetHyb() == 1 || c->GetHyb() == 1) continue;
299
if (b->GetHvyValence() > 1 && c->GetHvyValence() > 1)
302
vector<OBEdgeBase*>::iterator j,k;
304
//look for bond with assigned stereo as in poly-ene
305
for (a = b->BeginNbrAtom(j);a;a = b->NextNbrAtom(j))
306
if (((OBBond*)*j)->IsUp() ||((OBBond*)*j)->IsDown())
310
for (a = b->BeginNbrAtom(j);a;a = b->NextNbrAtom(j))
311
if (a != c && !a->IsHydrogen())
313
for (d = c->BeginNbrAtom(k);d;d = c->NextNbrAtom(k))
314
if (d != b && !d->IsHydrogen())
316
obAssert(a); obAssert(d);
318
if (((OBBond*)*j)->IsUp() || ((OBBond*)*j)->IsDown()) //stereo already assigned
320
if (fabs(CalcTorsionAngle(a->GetVector(),b->GetVector(),
321
c->GetVector(),d->GetVector())) > 10.0)
322
if (((OBBond*)*j)->IsUp()) ((OBBond*)*k)->SetUp();
323
else ((OBBond*)*k)->SetDown();
325
if (((OBBond*)*j)->IsUp()) ((OBBond*)*k)->SetDown();
326
else ((OBBond*)*k)->SetUp();
328
else //assign stereo to both ends
330
((OBBond*)*j)->SetUp();
331
if (fabs(CalcTorsionAngle(a->GetVector(),b->GetVector(),
332
c->GetVector(),d->GetVector())) > 10.0)
333
((OBBond*)*k)->SetUp();
335
((OBBond*)*k)->SetDown();
339
AssignCisTrans(node->GetNextNode(i));
343
void OBMol2Smi::Init()
354
bool OBMol2Smi::GetSmilesElement(OBSmiNode *node,char *element)
356
//***handle reference atom stuff here and return***
358
bool bracketElement = false;
359
bool normalValence = true;
361
OBAtom *atom = node->GetAtom();
363
int bosum = atom->KBOSum();
365
switch (atom->GetAtomicNum())
368
case 5: /*bracketElement = !(normalValence = (bosum == 3)); break;*/ break;
371
if (atom->IsAromatic() && atom->GetHvyValence() == 2 && atom->GetImplicitValence() == 3)
373
bracketElement = !(normalValence = false); break;
376
bracketElement = !(normalValence = (bosum == 3 || bosum == 5)); break;
381
bracketElement = !(normalValence = (bosum == 2 || bosum == 4 || bosum == 6)); break;
387
bracketElement = true;
390
if (atom->GetHvyValence() > 2 && atom->IsChiral())
391
if (((OBMol*)atom->GetParent())->HasNonZeroCoords() || atom->HasChiralitySpecified())
392
bracketElement = true;
394
if (atom->GetFormalCharge() != 0) //bracket charged elements
395
bracketElement = true;
399
if (!atom->GetAtomicNum())
401
bool external = false;
402
vector<pair<int,pair<OBAtom *,OBBond *> > > *externalBonds =
403
(vector<pair<int,pair<OBAtom *,OBBond *> > > *)((OBMol*)atom->GetParent())->GetData("extBonds");
404
vector<pair<int,pair<OBAtom *,OBBond *> > >::iterator externalBond;
407
for(externalBond = externalBonds->begin();externalBond != externalBonds->end();externalBond++)
409
if (externalBond->second.first == atom)
413
OBBond *bond = externalBond->second.second;
414
if (bond->IsUp()) strcat(symbol,"\\");
415
if (bond->IsDown()) strcat(symbol,"/");
417
if (bond->GetBO() == 2 && !bond->IsAromatic()) strcat(symbol,"=");
418
if (bond->GetBO() == 2 && bond->IsAromatic()) strcat(symbol,";");
420
if (bond->GetBO() == 2) strcat(symbol,"=");
422
if (bond->GetBO() == 3) strcat(symbol,"#");
423
sprintf(symbol,"%s%d",symbol,externalBond->first);
428
if(!external) strcpy(symbol,"*");
432
strcpy(symbol,etab.GetSymbol(atom->GetAtomicNum()));
434
if (atom->IsAromatic()) symbol[0] = tolower(symbol[0]);
437
strcpy(element,symbol);
443
if (!atom->GetAtomicNum()) strcpy(symbol,"*");
446
strcpy(symbol,etab.GetSymbol(atom->GetAtomicNum()));
448
if (atom->IsAromatic()) symbol[0] = tolower(symbol[0]);
451
strcat(element,symbol);
453
//if (atom->IsCarbon() && atom->GetHvyValence() > 2 && atom->IsChiral())
454
if (atom->GetHvyValence() > 2 && atom->IsChiral())
457
if (GetChiralStereo(node,stereo))
458
strcat(element,stereo);
461
//add extra hydrogens
462
// if (!normalValence && atom->ImplicitHydrogenCount())
463
if (atom->ImplicitHydrogenCount())
466
if (atom->ImplicitHydrogenCount() > 1)
469
sprintf(tcount,"%d",atom->ImplicitHydrogenCount());
470
strcat(element,tcount);
474
//cat charge on the end
475
if (atom->GetFormalCharge() != 0)
479
if (atom->ImplicitHydrogenCount())
481
cerr << "imp = " << atom->GetAtomicNum() << ' ' << atom->GetImplicitValence() << endl;
483
if (atom->ImplicitHydrogenCount() > 1)
486
sprintf(tcount,"%d",atom->ImplicitHydrogenCount());
487
strcat(element,tcount);
491
if (atom->GetFormalCharge() > 0) strcat(element,"+");
492
else strcat(element,"-");
494
if (abs(atom->GetFormalCharge()) > 1)
497
sprintf(tcharge,"%d",abs(atom->GetFormalCharge()));
498
strcat(element,tcharge);
507
bool OBMol2Smi::GetChiralStereo(OBSmiNode *node,char *stereo)
511
OBAtom *a,*b,*c,*d,hydrogen;
514
OBMol *mol = (OBMol*)b->GetParent();
516
if (!mol->HasNonZeroCoords()) //must have come in from smiles string
518
if (!b->HasChiralitySpecified()) return(false);
519
if (b->IsClockwise()) strcpy(stereo,"@@");
520
else if (b->IsAntiClockwise()) strcpy(stereo,"@");
522
//if (b->GetHvyValence() == 3) strcat(stereo,"H");
526
//give peudo Z coords if mol is 2D
529
Vector v,vz(0.0,0.0,1.0);
533
vector<OBEdgeBase*>::iterator i;
534
for (bond = b->BeginBond(i);bond;bond = b->NextBond(i))
536
nbr = bond->GetEndAtom();
539
v = nbr->GetVector();
540
if (bond->IsWedge()) v += vz;
542
if (bond->IsHash()) v -= vz;
548
nbr = bond->GetBeginAtom();
549
v = nbr->GetVector();
550
if (bond->IsWedge()) v -= vz;
552
if (bond->IsHash()) v += vz;
560
a = node->GetParent();
561
obAssert(a); //chiral atom can't be used as root node - must have parent
563
if (b->GetHvyValence() == 3) //must have attached hydrogen
565
if (b->GetValence() == 4)//has explicit hydrogen
567
vector<OBEdgeBase*>::iterator i;
568
for (c = b->BeginNbrAtom(i);c;c = b->NextNbrAtom(i))
573
else //implicit hydrogen
576
b->GetNewBondVector(v,1.0);
577
hydrogen.SetVector(v);
582
//get connected atoms in order
584
vector<int>::iterator j;
586
//try to get neighbors that are closure atoms in the order they appear in the string
587
vector<OBNodeBase*> va;
588
GetClosureAtoms(b,va);
591
vector<OBNodeBase*>::iterator k;
592
for (k = va.begin();k != va.end();k++)
595
if (!c) c = (OBAtom*)*k;
596
else if (!d) d = (OBAtom*)*k;
600
for (j = _storder.begin();j != _storder.end();j++)
602
nbr = mol->GetAtom(*j);
603
if (!b->IsConnected(nbr)) continue;
604
if (nbr == a || nbr == b || nbr == c) continue;
606
else if (!d) d = nbr;
609
torsion = CalcTorsionAngle(a->GetVector(),b->GetVector(),
610
c->GetVector(),d->GetVector());
612
strcpy(stereo,(torsion<0.0)?"@":"@@");
613
//if (b->GetHvyValence() == 3) strcat(stereo,"H");
615
//re-zero psuedo-coords
620
vector<OBNodeBase*>::iterator k;
621
for (atom = mol->BeginAtom(k);atom;atom = mol->NextAtom(k))
623
v = atom->GetVector();
632
bool WriteFixFile(ostream &ofs,OBMol &mol)
634
char buffer[BUFF_SIZE];
638
//m2s.AssignCisTrans(mol);
639
m2s.CorrectAromaticAmineCharge(mol);
640
m2s.CreateSmiString(mol,buffer);
643
vector<int>::iterator i;
644
vector<int> order = m2s.GetOutputOrder();
645
ofs << buffer << endl;
648
for (j = 0;j < mol.NumConformers();j++)
651
for (i = order.begin();i != order.end();i++)
653
atom = mol.GetAtom(*i);
654
sprintf(buffer,"%9.3f %9.3f %9.3f",atom->GetX(),atom->GetY(),atom->GetZ());
655
ofs << buffer<< endl;
661
OBSmiNode::OBSmiNode(OBAtom *atom)
669
void OBSmiNode::SetNextNode(OBSmiNode *node,OBBond *bond)
671
_nextnode.push_back(node);
672
_nextbond.push_back(bond);
675
OBSmiNode::~OBSmiNode()
677
vector<OBSmiNode*>::iterator i;
678
for (i = _nextnode.begin();i != _nextnode.end();i++)
683
bool WriteTheSmiles(OBMol & mol,char *out)
685
char buffer[2*BUFF_SIZE];
690
m2s.CorrectAromaticAmineCharge(mol);
691
m2s.CreateSmiString(mol,buffer);