/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> >d) |
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. |