/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 | 18.0k | { |
179 | 18.0k | _title = title; |
180 | 18.0k | Trim(_title); |
181 | 18.0k | } |
182 | | |
183 | | const char *OBMol::GetTitle(bool replaceNewlines) const |
184 | 2.33k | { |
185 | 2.33k | if (!replaceNewlines || _title.find('\n')== string::npos ) |
186 | 2.33k | 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 | | // Single pass; previous implementation was O(n^2) on titles with many |
192 | | // newlines because find_first_of always restarted from position 0. |
193 | 0 | for (char& c : title) { |
194 | 0 | if (c == '\n' || c == '\r') |
195 | 0 | c = ' '; |
196 | 0 | } |
197 | |
|
198 | 0 | return(title.c_str()); |
199 | 2.33k | } |
200 | | |
201 | | bool SortVVInt(const vector<int> &a,const vector<int> &b) |
202 | 0 | { |
203 | 0 | return(a.size() > b.size()); |
204 | 0 | } |
205 | | |
206 | | bool SortAtomZ(const pair<OBAtom*,double> &a, const pair<OBAtom*,double> &b) |
207 | 0 | { |
208 | 0 | return (a.second < b.second); |
209 | 0 | } |
210 | | |
211 | | double OBMol::GetAngle( OBAtom* a, OBAtom* b, OBAtom* c) |
212 | 0 | { |
213 | 0 | return a->GetAngle( b, c ); |
214 | 0 | } |
215 | | |
216 | | double OBMol::GetTorsion(int a,int b,int c,int d) |
217 | 0 | { |
218 | 0 | return(GetTorsion((OBAtom*)_vatom[a-1], |
219 | 0 | (OBAtom*)_vatom[b-1], |
220 | 0 | (OBAtom*)_vatom[c-1], |
221 | 0 | (OBAtom*)_vatom[d-1])); |
222 | 0 | } |
223 | | |
224 | | void OBMol::SetTorsion(OBAtom *a,OBAtom *b,OBAtom *c, OBAtom *d, double ang) |
225 | 0 | { |
226 | 0 | vector<int> tor; |
227 | 0 | vector<int> atoms; |
228 | |
|
229 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
230 | 0 | "Ran OpenBabel::SetTorsion", obAuditMsg); |
231 | |
|
232 | 0 | tor.push_back(a->GetCoordinateIdx()); |
233 | 0 | tor.push_back(b->GetCoordinateIdx()); |
234 | 0 | tor.push_back(c->GetCoordinateIdx()); |
235 | 0 | tor.push_back(d->GetCoordinateIdx()); |
236 | |
|
237 | 0 | FindChildren(atoms, b->GetIdx(), c->GetIdx()); |
238 | 0 | int j; |
239 | 0 | for (j = 0 ; (unsigned)j < atoms.size() ; j++ ) |
240 | 0 | atoms[j] = (atoms[j] - 1) * 3; |
241 | |
|
242 | 0 | double v2x,v2y,v2z; |
243 | 0 | double radang,m[9]; |
244 | 0 | double x,y,z,mag,rotang,sn,cs,t,tx,ty,tz; |
245 | | |
246 | | //calculate the torsion angle |
247 | | // TODO: fix this calculation for periodic systems |
248 | 0 | radang = CalcTorsionAngle(a->GetVector(), |
249 | 0 | b->GetVector(), |
250 | 0 | c->GetVector(), |
251 | 0 | d->GetVector()) / RAD_TO_DEG; |
252 | | // |
253 | | // now we have the torsion angle (radang) - set up the rot matrix |
254 | | // |
255 | | |
256 | | //find the difference between current and requested |
257 | 0 | rotang = ang - radang; |
258 | |
|
259 | 0 | sn = sin(rotang); |
260 | 0 | cs = cos(rotang); |
261 | 0 | t = 1 - cs; |
262 | |
|
263 | 0 | v2x = _c[tor[1]] - _c[tor[2]]; |
264 | 0 | v2y = _c[tor[1]+1] - _c[tor[2]+1]; |
265 | 0 | v2z = _c[tor[1]+2] - _c[tor[2]+2]; |
266 | | |
267 | | //normalize the rotation vector |
268 | 0 | mag = sqrt(SQUARE(v2x)+SQUARE(v2y)+SQUARE(v2z)); |
269 | 0 | x = v2x/mag; |
270 | 0 | y = v2y/mag; |
271 | 0 | z = v2z/mag; |
272 | | |
273 | | //set up the rotation matrix |
274 | 0 | m[0]= t*x*x + cs; |
275 | 0 | m[1] = t*x*y + sn*z; |
276 | 0 | m[2] = t*x*z - sn*y; |
277 | 0 | m[3] = t*x*y - sn*z; |
278 | 0 | m[4] = t*y*y + cs; |
279 | 0 | m[5] = t*y*z + sn*x; |
280 | 0 | m[6] = t*x*z + sn*y; |
281 | 0 | m[7] = t*y*z - sn*x; |
282 | 0 | m[8] = t*z*z + cs; |
283 | | |
284 | | // |
285 | | //now the matrix is set - time to rotate the atoms |
286 | | // |
287 | 0 | tx = _c[tor[1]]; |
288 | 0 | ty = _c[tor[1]+1]; |
289 | 0 | tz = _c[tor[1]+2]; |
290 | 0 | vector<int>::iterator i; |
291 | 0 | for (i = atoms.begin(); i != atoms.end(); ++i) |
292 | 0 | { |
293 | 0 | j = *i; |
294 | |
|
295 | 0 | _c[j] -= tx; |
296 | 0 | _c[j+1] -= ty; |
297 | 0 | _c[j+2]-= tz; |
298 | 0 | x = _c[j]*m[0] + _c[j+1]*m[1] + _c[j+2]*m[2]; |
299 | 0 | y = _c[j]*m[3] + _c[j+1]*m[4] + _c[j+2]*m[5]; |
300 | 0 | z = _c[j]*m[6] + _c[j+1]*m[7] + _c[j+2]*m[8]; |
301 | 0 | _c[j] = x; |
302 | 0 | _c[j+1] = y; |
303 | 0 | _c[j+2] = z; |
304 | 0 | _c[j] += tx; |
305 | 0 | _c[j+1] += ty; |
306 | 0 | _c[j+2] += tz; |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | | |
311 | | double OBMol::GetTorsion(OBAtom *a,OBAtom *b,OBAtom *c,OBAtom *d) |
312 | 0 | { |
313 | 0 | if (!IsPeriodic()) |
314 | 0 | { |
315 | 0 | return(CalcTorsionAngle(a->GetVector(), |
316 | 0 | b->GetVector(), |
317 | 0 | c->GetVector(), |
318 | 0 | d->GetVector())); |
319 | 0 | } |
320 | 0 | else |
321 | 0 | { |
322 | 0 | vector3 v1, v2, v3, v4; |
323 | | // Wrap the atomic positions in a continuous chain that makes sense based on the unit cell |
324 | | // Start by extracting the absolute Cartesian coordinates |
325 | 0 | v1 = a->GetVector(); |
326 | 0 | v2 = b->GetVector(); |
327 | 0 | v3 = c->GetVector(); |
328 | 0 | v4 = d->GetVector(); |
329 | | // Then redefine the positions based on proximity to the previous atom |
330 | | // to build a continuous chain of expanded Cartesian coordinates |
331 | 0 | OBUnitCell *unitCell = (OBUnitCell * ) GetData(OBGenericDataType::UnitCell); |
332 | 0 | v2 = unitCell->UnwrapCartesianNear(v2, v1); |
333 | 0 | v3 = unitCell->UnwrapCartesianNear(v3, v2); |
334 | 0 | v4 = unitCell->UnwrapCartesianNear(v4, v3); |
335 | 0 | return(CalcTorsionAngle(v1, v2, v3, v4)); |
336 | 0 | } |
337 | 0 | } |
338 | | |
339 | | void OBMol::ContigFragList(std::vector<std::vector<int> >&cfl) |
340 | 0 | { |
341 | 0 | int j; |
342 | 0 | OBAtom *atom; |
343 | 0 | OBBond *bond; |
344 | 0 | vector<OBAtom*>::iterator i; |
345 | 0 | vector<OBBond*>::iterator k; |
346 | 0 | OBBitVec used,curr,next,frag; |
347 | 0 | vector<int> tmp; |
348 | |
|
349 | 0 | used.Resize(NumAtoms()+1); |
350 | 0 | curr.Resize(NumAtoms()+1); |
351 | 0 | next.Resize(NumAtoms()+1); |
352 | 0 | frag.Resize(NumAtoms()+1); |
353 | |
|
354 | 0 | while ((unsigned)used.CountBits() < NumAtoms()) |
355 | 0 | { |
356 | 0 | curr.Clear(); |
357 | 0 | frag.Clear(); |
358 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
359 | 0 | if (!used.BitIsSet(atom->GetIdx())) |
360 | 0 | { |
361 | 0 | curr.SetBitOn(atom->GetIdx()); |
362 | 0 | break; |
363 | 0 | } |
364 | |
|
365 | 0 | frag |= curr; |
366 | 0 | while (!curr.IsEmpty()) |
367 | 0 | { |
368 | 0 | next.Clear(); |
369 | 0 | for (j = curr.NextBit(-1);j != curr.EndBit();j = curr.NextBit(j)) |
370 | 0 | { |
371 | 0 | atom = GetAtom(j); |
372 | 0 | for (bond = atom->BeginBond(k);bond;bond = atom->NextBond(k)) |
373 | 0 | if (!used.BitIsSet(bond->GetNbrAtomIdx(atom))) |
374 | 0 | next.SetBitOn(bond->GetNbrAtomIdx(atom)); |
375 | 0 | } |
376 | |
|
377 | 0 | used |= curr; |
378 | 0 | used |= next; |
379 | 0 | frag |= next; |
380 | 0 | curr = next; |
381 | 0 | } |
382 | |
|
383 | 0 | tmp.clear(); |
384 | 0 | frag.ToVecInt(tmp); |
385 | 0 | cfl.push_back(tmp); |
386 | 0 | } |
387 | |
|
388 | 0 | sort(cfl.begin(),cfl.end(),SortVVInt); |
389 | 0 | } |
390 | | |
391 | | void OBMol::FindAngles() |
392 | 0 | { |
393 | | //if already has data return |
394 | 0 | if(HasData(OBGenericDataType::AngleData)) |
395 | 0 | return; |
396 | | |
397 | | //get new data and attach it to molecule |
398 | 0 | OBAngleData *angles = new OBAngleData; |
399 | 0 | angles->SetOrigin(perceived); |
400 | 0 | SetData(angles); |
401 | |
|
402 | 0 | OBAngle angle; |
403 | 0 | OBAtom *b; |
404 | 0 | int unique_angle; |
405 | |
|
406 | 0 | unique_angle = 0; |
407 | |
|
408 | 0 | FOR_ATOMS_OF_MOL(atom, this) { |
409 | 0 | if(atom->GetAtomicNum() == OBElements::Hydrogen) |
410 | 0 | continue; |
411 | | |
412 | 0 | b = (OBAtom*) &*atom; |
413 | |
|
414 | 0 | FOR_NBORS_OF_ATOM(a, b) { |
415 | 0 | FOR_NBORS_OF_ATOM(c, b) { |
416 | 0 | if(&*a == &*c) { |
417 | 0 | unique_angle = 1; |
418 | 0 | continue; |
419 | 0 | } |
420 | | |
421 | 0 | if (unique_angle) { |
422 | 0 | angle.SetAtoms((OBAtom*)b, (OBAtom*)&*a, (OBAtom*)&*c); |
423 | 0 | angles->SetData(angle); |
424 | 0 | angle.Clear(); |
425 | 0 | } |
426 | 0 | } |
427 | 0 | unique_angle = 0; |
428 | 0 | } |
429 | 0 | } |
430 | |
|
431 | 0 | return; |
432 | 0 | } |
433 | | |
434 | | void OBMol::FindTorsions() |
435 | 0 | { |
436 | | //if already has data return |
437 | 0 | if(HasData(OBGenericDataType::TorsionData)) |
438 | 0 | return; |
439 | | |
440 | | //get new data and attach it to molecule |
441 | 0 | OBTorsionData *torsions = new OBTorsionData; |
442 | 0 | torsions->SetOrigin(perceived); |
443 | 0 | SetData(torsions); |
444 | |
|
445 | 0 | OBTorsion torsion; |
446 | 0 | vector<OBBond*>::iterator bi1,bi2,bi3; |
447 | 0 | OBBond* bond; |
448 | 0 | OBAtom *a,*b,*c,*d; |
449 | | |
450 | | //loop through all bonds generating torsions |
451 | 0 | for(bond = BeginBond(bi1);bond;bond = NextBond(bi1)) |
452 | 0 | { |
453 | 0 | b = bond->GetBeginAtom(); |
454 | 0 | c = bond->GetEndAtom(); |
455 | 0 | if(b->GetAtomicNum() == OBElements::Hydrogen || c->GetAtomicNum() == OBElements::Hydrogen) |
456 | 0 | continue; |
457 | | |
458 | 0 | for(a = b->BeginNbrAtom(bi2);a;a = b->NextNbrAtom(bi2)) |
459 | 0 | { |
460 | 0 | if(a == c) |
461 | 0 | continue; |
462 | | |
463 | 0 | for(d = c->BeginNbrAtom(bi3);d;d = c->NextNbrAtom(bi3)) |
464 | 0 | { |
465 | 0 | if ((d == b) || (d == a)) |
466 | 0 | continue; |
467 | 0 | torsion.AddTorsion(a,b,c,d); |
468 | 0 | } |
469 | 0 | } |
470 | | //add torsion to torsionData |
471 | 0 | if(torsion.GetSize()) |
472 | 0 | torsions->SetData(torsion); |
473 | 0 | torsion.Clear(); |
474 | 0 | } |
475 | |
|
476 | 0 | return; |
477 | 0 | } |
478 | | |
479 | | void OBMol::FindLargestFragment(OBBitVec &lf) |
480 | 0 | { |
481 | 0 | int j; |
482 | 0 | OBAtom *atom; |
483 | 0 | OBBond *bond; |
484 | 0 | vector<OBAtom*>::iterator i; |
485 | 0 | vector<OBBond*>::iterator k; |
486 | 0 | OBBitVec used,curr,next,frag; |
487 | |
|
488 | 0 | lf.Clear(); |
489 | 0 | while ((unsigned)used.CountBits() < NumAtoms()) |
490 | 0 | { |
491 | 0 | curr.Clear(); |
492 | 0 | frag.Clear(); |
493 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
494 | 0 | if (!used.BitIsSet(atom->GetIdx())) |
495 | 0 | { |
496 | 0 | curr.SetBitOn(atom->GetIdx()); |
497 | 0 | break; |
498 | 0 | } |
499 | |
|
500 | 0 | frag |= curr; |
501 | 0 | while (!curr.IsEmpty()) |
502 | 0 | { |
503 | 0 | next.Clear(); |
504 | 0 | for (j = curr.NextBit(-1);j != curr.EndBit();j = curr.NextBit(j)) |
505 | 0 | { |
506 | 0 | atom = GetAtom(j); |
507 | 0 | for (bond = atom->BeginBond(k);bond;bond = atom->NextBond(k)) |
508 | 0 | if (!used.BitIsSet(bond->GetNbrAtomIdx(atom))) |
509 | 0 | next.SetBitOn(bond->GetNbrAtomIdx(atom)); |
510 | 0 | } |
511 | |
|
512 | 0 | used |= curr; |
513 | 0 | used |= next; |
514 | 0 | frag |= next; |
515 | 0 | curr = next; |
516 | 0 | } |
517 | |
|
518 | 0 | if (lf.IsEmpty() || lf.CountBits() < frag.CountBits()) |
519 | 0 | lf = frag; |
520 | 0 | } |
521 | 0 | } |
522 | | |
523 | | //! locates all atoms for which there exists a path to 'end' |
524 | | //! without going through 'bgn' |
525 | | //! children must not include 'end' |
526 | | void OBMol::FindChildren(vector<OBAtom*> &children,OBAtom *bgn,OBAtom *end) |
527 | 0 | { |
528 | 0 | OBBitVec used,curr,next; |
529 | |
|
530 | 0 | used |= bgn->GetIdx(); |
531 | 0 | used |= end->GetIdx(); |
532 | 0 | curr |= end->GetIdx(); |
533 | 0 | children.clear(); |
534 | |
|
535 | 0 | int i; |
536 | 0 | OBAtom *atom,*nbr; |
537 | 0 | vector<OBBond*>::iterator j; |
538 | |
|
539 | 0 | for (;;) |
540 | 0 | { |
541 | 0 | next.Clear(); |
542 | 0 | for (i = curr.NextBit(-1);i != curr.EndBit();i = curr.NextBit(i)) |
543 | 0 | { |
544 | 0 | atom = GetAtom(i); |
545 | 0 | for (nbr = atom->BeginNbrAtom(j);nbr;nbr = atom->NextNbrAtom(j)) |
546 | 0 | if (!used[nbr->GetIdx()]) |
547 | 0 | { |
548 | 0 | children.push_back(nbr); |
549 | 0 | next |= nbr->GetIdx(); |
550 | 0 | used |= nbr->GetIdx(); |
551 | 0 | } |
552 | 0 | } |
553 | 0 | if (next.IsEmpty()) |
554 | 0 | break; |
555 | 0 | curr = next; |
556 | 0 | } |
557 | 0 | } |
558 | | |
559 | | //! locates all atoms for which there exists a path to 'second' |
560 | | //! without going through 'first' |
561 | | //! children must not include 'second' |
562 | | void OBMol::FindChildren(vector<int> &children,int first,int second) |
563 | 0 | { |
564 | 0 | int i; |
565 | 0 | OBBitVec used,curr,next; |
566 | |
|
567 | 0 | used.SetBitOn(first); |
568 | 0 | used.SetBitOn(second); |
569 | 0 | curr.SetBitOn(second); |
570 | |
|
571 | 0 | OBAtom *atom; |
572 | 0 | while (!curr.IsEmpty()) |
573 | 0 | { |
574 | 0 | next.Clear(); |
575 | 0 | for (i = curr.NextBit(-1);i != curr.EndBit();i = curr.NextBit(i)) |
576 | 0 | { |
577 | 0 | atom = GetAtom(i); |
578 | 0 | FOR_BONDS_OF_ATOM (bond, atom) |
579 | 0 | if (!used.BitIsSet(bond->GetNbrAtomIdx(atom))) |
580 | 0 | next.SetBitOn(bond->GetNbrAtomIdx(atom)); |
581 | 0 | } |
582 | |
|
583 | 0 | used |= next; |
584 | 0 | curr = next; |
585 | 0 | } |
586 | |
|
587 | 0 | used.SetBitOff(first); |
588 | 0 | used.SetBitOff(second); |
589 | 0 | used.ToVecInt(children); |
590 | 0 | } |
591 | | |
592 | | /*! \brief Calculates the graph theoretical distance (GTD) of each atom. |
593 | | * |
594 | | * Creates a vector (indexed from zero) containing, for each atom |
595 | | * in the molecule, the number of bonds plus one to the most |
596 | | * distant non-H atom. |
597 | | * |
598 | | * For example, for the molecule H3CC(=O)Cl the GTD value for C1 |
599 | | * would be 3, as the most distant non-H atom (either Cl or O) is |
600 | | * 2 bonds away. |
601 | | * |
602 | | * Since the GTD measures the distance to non-H atoms, the GTD values |
603 | | * for terminal H atoms tend to be larger than for non-H terminal atoms. |
604 | | * In the example above, the GTD values for the H atoms are all 4. |
605 | | */ |
606 | | bool OBMol::GetGTDVector(vector<int> >d) |
607 | | //calculates the graph theoretical distance for every atom |
608 | | //and puts it into gtd |
609 | 0 | { |
610 | 0 | gtd.clear(); |
611 | 0 | gtd.resize(NumAtoms()); |
612 | |
|
613 | 0 | int gtdcount,natom; |
614 | 0 | OBBitVec used,curr,next; |
615 | 0 | OBAtom *atom,*atom1; |
616 | 0 | OBBond *bond; |
617 | 0 | vector<OBAtom*>::iterator i; |
618 | 0 | vector<OBBond*>::iterator j; |
619 | |
|
620 | 0 | next.Clear(); |
621 | |
|
622 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
623 | 0 | { |
624 | 0 | gtdcount = 0; |
625 | 0 | used.Clear(); |
626 | 0 | curr.Clear(); |
627 | 0 | used.SetBitOn(atom->GetIdx()); |
628 | 0 | curr.SetBitOn(atom->GetIdx()); |
629 | |
|
630 | 0 | while (!curr.IsEmpty()) |
631 | 0 | { |
632 | 0 | next.Clear(); |
633 | 0 | for (natom = curr.NextBit(-1);natom != curr.EndBit();natom = curr.NextBit(natom)) |
634 | 0 | { |
635 | 0 | atom1 = GetAtom(natom); |
636 | 0 | for (bond = atom1->BeginBond(j);bond;bond = atom1->NextBond(j)) |
637 | 0 | if (!used.BitIsSet(bond->GetNbrAtomIdx(atom1)) && !curr.BitIsSet(bond->GetNbrAtomIdx(atom1))) |
638 | 0 | if (bond->GetNbrAtom(atom1)->GetAtomicNum() != OBElements::Hydrogen) |
639 | 0 | next.SetBitOn(bond->GetNbrAtomIdx(atom1)); |
640 | 0 | } |
641 | |
|
642 | 0 | used |= next; |
643 | 0 | curr = next; |
644 | 0 | gtdcount++; |
645 | 0 | } |
646 | 0 | gtd[atom->GetIdx()-1] = gtdcount; |
647 | 0 | } |
648 | 0 | return(true); |
649 | 0 | } |
650 | | |
651 | | /*! |
652 | | **\brief Calculates a set of graph invariant indexes using |
653 | | ** the graph theoretical distance, number of connected heavy atoms, |
654 | | ** aromatic boolean, ring boolean, atomic number, and |
655 | | ** summation of bond orders connected to the atom. |
656 | | ** Vector is indexed from zero |
657 | | */ |
658 | | void OBMol::GetGIVector(vector<unsigned int> &vid) |
659 | 0 | { |
660 | 0 | vid.clear(); |
661 | 0 | vid.resize(NumAtoms()+1); |
662 | |
|
663 | 0 | vector<int> v; |
664 | 0 | GetGTDVector(v); |
665 | |
|
666 | 0 | int i; |
667 | 0 | OBAtom *atom; |
668 | 0 | vector<OBAtom*>::iterator j; |
669 | 0 | for (i=0,atom = BeginAtom(j);atom;atom = NextAtom(j),++i) |
670 | 0 | { |
671 | 0 | vid[i] = (unsigned int)v[i]; |
672 | 0 | vid[i] += (unsigned int)(atom->GetHvyDegree()*100); |
673 | 0 | vid[i] += (unsigned int)(((atom->IsAromatic()) ? 1 : 0)*1000); |
674 | 0 | vid[i] += (unsigned int)(((atom->IsInRing()) ? 1 : 0)*10000); |
675 | 0 | vid[i] += (unsigned int)(atom->GetAtomicNum()*100000); |
676 | 0 | vid[i] += (unsigned int)(atom->GetImplicitHCount()*10000000); |
677 | 0 | } |
678 | 0 | } |
679 | | |
680 | | static bool OBComparePairSecond(const pair<OBAtom*,unsigned int> &a,const pair<OBAtom*,unsigned int> &b) |
681 | 0 | { |
682 | 0 | return(a.second < b.second); |
683 | 0 | } |
684 | | |
685 | | static bool OBComparePairFirst(const pair<OBAtom*,unsigned int> &a,const pair<OBAtom*,unsigned int> &b) |
686 | 0 | { |
687 | 0 | return(a.first->GetIdx() < b.first->GetIdx()); |
688 | 0 | } |
689 | | |
690 | | //! counts the number of unique symmetry classes in a list |
691 | | static void ClassCount(vector<pair<OBAtom*,unsigned int> > &vp,unsigned int &count) |
692 | 0 | { |
693 | 0 | count = 0; |
694 | 0 | vector<pair<OBAtom*,unsigned int> >::iterator k; |
695 | 0 | sort(vp.begin(),vp.end(),OBComparePairSecond); |
696 | | #if 0 // original version |
697 | | |
698 | | unsigned int id=0; // [ejk] appease gcc's bogus "might be undef'd" warning |
699 | | for (k = vp.begin();k != vp.end();++k) |
700 | | { |
701 | | if (k == vp.begin()) |
702 | | { |
703 | | id = k->second; |
704 | | k->second = count = 0; |
705 | | } |
706 | | else |
707 | | if (k->second != id) |
708 | | { |
709 | | id = k->second; |
710 | | k->second = ++count; |
711 | | } |
712 | | else |
713 | | k->second = count; |
714 | | } |
715 | | count++; |
716 | | #else // get rid of warning, moves test out of loop, returns 0 for empty input |
717 | |
|
718 | 0 | k = vp.begin(); |
719 | 0 | if (k != vp.end()) |
720 | 0 | { |
721 | 0 | unsigned int id = k->second; |
722 | 0 | k->second = 0; |
723 | 0 | ++k; |
724 | 0 | for (;k != vp.end(); ++k) |
725 | 0 | { |
726 | 0 | if (k->second != id) |
727 | 0 | { |
728 | 0 | id = k->second; |
729 | 0 | k->second = ++count; |
730 | 0 | } |
731 | 0 | else |
732 | 0 | k->second = count; |
733 | 0 | } |
734 | 0 | ++count; |
735 | 0 | } |
736 | 0 | else |
737 | 0 | { |
738 | | // [ejk] thinks count=0 might be OK for an empty list, but orig code did |
739 | | //++count; |
740 | 0 | } |
741 | 0 | #endif |
742 | 0 | } |
743 | | |
744 | | //! creates a new vector of symmetry classes base on an existing vector |
745 | | //! helper routine to GetGIDVector |
746 | | static void CreateNewClassVector(vector<pair<OBAtom*,unsigned int> > &vp1,vector<pair<OBAtom*,unsigned int> > &vp2) |
747 | 0 | { |
748 | 0 | unsigned int m,id; |
749 | 0 | OBAtom *nbr; |
750 | 0 | vector<OBBond*>::iterator j; |
751 | 0 | vector<unsigned int>::iterator k; |
752 | 0 | vector<pair<OBAtom*,unsigned int> >::iterator i; |
753 | 0 | sort(vp1.begin(),vp1.end(),OBComparePairFirst); |
754 | 0 | vp2.clear(); |
755 | 0 | for (i = vp1.begin();i != vp1.end();++i) |
756 | 0 | { |
757 | 0 | vector<unsigned int> vtmp; |
758 | 0 | for (nbr = i->first->BeginNbrAtom(j);nbr;nbr = i->first->NextNbrAtom(j)) |
759 | 0 | vtmp.push_back(vp1[nbr->GetIdx()-1].second); |
760 | 0 | sort(vtmp.begin(),vtmp.end(),OBCompareUnsigned); |
761 | | // Base-100 positional sum of the neighbour classes, computed modulo |
762 | | // 2^32 (bit-identical to the previous int overflow, but done in 64-bit |
763 | | // intermediates so it does not invoke overflow). See the matching |
764 | | // routine in graphsym.cpp for details. |
765 | 0 | id = i->second; |
766 | 0 | for (m=100,k = vtmp.begin();k != vtmp.end();++k) |
767 | 0 | { |
768 | 0 | id = static_cast<unsigned int>(id + static_cast<unsigned long long>(*k) * m); |
769 | 0 | m = static_cast<unsigned int>(static_cast<unsigned long long>(m) * 100); |
770 | 0 | } |
771 | |
|
772 | 0 | vp2.push_back(pair<OBAtom*,unsigned int> (i->first,id)); |
773 | 0 | } |
774 | 0 | } |
775 | | |
776 | | /*! |
777 | | **\brief Calculates a set of symmetry identifiers for a molecule. |
778 | | ** Atoms with the same symmetry ID are symmetrically equivalent. |
779 | | ** Vector is indexed from zero |
780 | | */ |
781 | | void OBMol::GetGIDVector(vector<unsigned int> &vgid) |
782 | 0 | { |
783 | 0 | vector<unsigned int> vgi; |
784 | 0 | GetGIVector(vgi); //get vector of graph invariants |
785 | |
|
786 | 0 | int i; |
787 | 0 | OBAtom *atom; |
788 | 0 | vector<OBAtom*>::iterator j; |
789 | 0 | vector<pair<OBAtom*,unsigned int> > vp1,vp2; |
790 | 0 | for (i=0,atom = BeginAtom(j);atom;atom = NextAtom(j),++i) |
791 | 0 | vp1.push_back(pair<OBAtom*,unsigned int> (atom,vgi[i])); |
792 | |
|
793 | 0 | unsigned int nclass1,nclass2; //number of classes |
794 | 0 | ClassCount(vp1,nclass1); |
795 | |
|
796 | 0 | if (nclass1 < NumAtoms()) |
797 | 0 | { |
798 | 0 | for (i = 0;i < 100;++i) //sanity check - shouldn't ever hit this number |
799 | 0 | { |
800 | 0 | CreateNewClassVector(vp1,vp2); |
801 | 0 | ClassCount(vp2,nclass2); |
802 | 0 | vp1 = vp2; |
803 | 0 | if (nclass1 == nclass2) |
804 | 0 | break; |
805 | 0 | nclass1 = nclass2; |
806 | 0 | } |
807 | 0 | } |
808 | |
|
809 | 0 | vgid.clear(); |
810 | 0 | sort(vp1.begin(),vp1.end(),OBComparePairFirst); |
811 | 0 | vector<pair<OBAtom*,unsigned int> >::iterator k; |
812 | 0 | for (k = vp1.begin();k != vp1.end();++k) |
813 | 0 | vgid.push_back(k->second); |
814 | 0 | } |
815 | | |
816 | | unsigned int OBMol::NumHvyAtoms() const |
817 | 0 | { |
818 | 0 | const OBAtom *atom; |
819 | 0 | vector<OBAtom*>::const_iterator(i); |
820 | 0 | unsigned int count = 0; |
821 | |
|
822 | 0 | for(atom = this->BeginAtom(i); atom; atom = this->NextAtom(i)) |
823 | 0 | { |
824 | 0 | if (atom->GetAtomicNum() != OBElements::Hydrogen) |
825 | 0 | count++; |
826 | 0 | } |
827 | |
|
828 | 0 | return(count); |
829 | 0 | } |
830 | | |
831 | | unsigned int OBMol::NumRotors(bool sampleRingBonds) |
832 | 0 | { |
833 | 0 | OBRotorList rl; |
834 | 0 | rl.FindRotors(*this, sampleRingBonds); |
835 | 0 | return rl.Size(); |
836 | 0 | } |
837 | | |
838 | | //! Returns a pointer to the atom after a safety check |
839 | | //! 0 < idx <= NumAtoms |
840 | | OBAtom *OBMol::GetAtom(int idx) const |
841 | 4.64M | { |
842 | 4.64M | if ((unsigned)idx < 1 || (unsigned)idx > NumAtoms()) |
843 | 15.2k | { |
844 | 15.2k | obErrorLog.ThrowError(__FUNCTION__, "Requested Atom Out of Range", obDebug); |
845 | 15.2k | return nullptr; |
846 | 15.2k | } |
847 | | |
848 | 4.62M | return((OBAtom*)_vatom[idx-1]); |
849 | 4.64M | } |
850 | | |
851 | | OBAtom *OBMol::GetAtomById(unsigned long id) const |
852 | 9.51k | { |
853 | 9.51k | if (id >= _atomIds.size()) { |
854 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Requested atom with invalid id.", obDebug); |
855 | 0 | return nullptr; |
856 | 0 | } |
857 | | |
858 | 9.51k | return((OBAtom*)_atomIds[id]); |
859 | 9.51k | } |
860 | | |
861 | | OBAtom *OBMol::GetFirstAtom() const |
862 | 0 | { |
863 | 0 | return _vatom.empty() ? nullptr : (OBAtom*)_vatom[0]; |
864 | 0 | } |
865 | | |
866 | | //! Returns a pointer to the bond after a safety check |
867 | | //! 0 <= idx < NumBonds |
868 | | OBBond *OBMol::GetBond(int idx) const |
869 | 6.56k | { |
870 | 6.56k | if (idx < 0 || (unsigned)idx >= NumBonds()) |
871 | 28 | { |
872 | 28 | obErrorLog.ThrowError(__FUNCTION__, "Requested Bond Out of Range", obDebug); |
873 | 28 | return nullptr; |
874 | 28 | } |
875 | | |
876 | 6.53k | return((OBBond*)_vbond[idx]); |
877 | 6.56k | } |
878 | | |
879 | | OBBond *OBMol::GetBondById(unsigned long id) const |
880 | 5.11k | { |
881 | 5.11k | if (id >= _bondIds.size()) { |
882 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Requested bond with invalid id.", obDebug); |
883 | 0 | return nullptr; |
884 | 0 | } |
885 | | |
886 | 5.11k | return((OBBond*)_bondIds[id]); |
887 | 5.11k | } |
888 | | |
889 | | OBBond *OBMol::GetBond(int bgn, int end) const |
890 | 112k | { |
891 | 112k | return(GetBond(GetAtom(bgn),GetAtom(end))); |
892 | 112k | } |
893 | | |
894 | | OBBond *OBMol::GetBond(OBAtom *bgn,OBAtom *end) const |
895 | 118k | { |
896 | 118k | OBAtom *nbr; |
897 | 118k | vector<OBBond*>::iterator i; |
898 | | |
899 | 118k | if (!bgn || !end) return nullptr; |
900 | | |
901 | 227k | for (nbr = bgn->BeginNbrAtom(i);nbr;nbr = bgn->NextNbrAtom(i)) |
902 | 210k | if (nbr == end) |
903 | 101k | return((OBBond *)*i); |
904 | | |
905 | 16.6k | return nullptr; //just to keep the SGI compiler happy |
906 | 118k | } |
907 | | |
908 | | OBResidue *OBMol::GetResidue(int idx) const |
909 | 0 | { |
910 | 0 | if (idx < 0 || (unsigned)idx >= NumResidues()) |
911 | 0 | { |
912 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Requested Residue Out of Range", obDebug); |
913 | 0 | return nullptr; |
914 | 0 | } |
915 | | |
916 | 0 | return (_residue[idx]); |
917 | 0 | } |
918 | | |
919 | | std::vector<OBInternalCoord*> OBMol::GetInternalCoord() |
920 | 0 | { |
921 | 0 | if (_internals.empty()) |
922 | 0 | { |
923 | 0 | _internals.push_back(nullptr); |
924 | 0 | for(unsigned int i = 1; i <= NumAtoms(); ++i) |
925 | 0 | { |
926 | 0 | _internals.push_back(new OBInternalCoord); |
927 | 0 | } |
928 | 0 | CartesianToInternal(_internals, *this); |
929 | 0 | } |
930 | 0 | return _internals; |
931 | 0 | } |
932 | | |
933 | | //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#findSmallestSetOfSmallestRings">blue-obelisk:findSmallestSetOfSmallestRings</a>. |
934 | | vector<OBRing*> &OBMol::GetSSSR() |
935 | 30.1k | { |
936 | 30.1k | if (!HasSSSRPerceived()) |
937 | 14.8k | FindSSSR(); |
938 | | |
939 | | // SDF / MDL files can inject "<SSSR>" as a property field, which |
940 | | // ends up stored as an OBPairData under that attribute. The legacy |
941 | | // C-style cast misinterpreted it as OBRingData (UBSAN catches the |
942 | | // vptr mismatch). Validate the type and replace if wrong. |
943 | 30.1k | OBGenericData *existing = GetData("SSSR"); |
944 | 30.1k | OBRingData *rd = dynamic_cast<OBRingData *>(existing); |
945 | 30.1k | if (rd == nullptr) { |
946 | 12.5k | if (existing) DeleteData(existing); |
947 | 12.5k | rd = new OBRingData(); |
948 | 12.5k | rd->SetAttribute("SSSR"); |
949 | 12.5k | SetData(rd); |
950 | 12.5k | } |
951 | 30.1k | rd->SetOrigin(perceived); |
952 | 30.1k | return(rd->GetData()); |
953 | 30.1k | } |
954 | | |
955 | | vector<OBRing*> &OBMol::GetLSSR() |
956 | 2.34k | { |
957 | 2.34k | if (!HasLSSRPerceived()) |
958 | 2.34k | FindLSSR(); |
959 | | |
960 | | // Same type-confusion guard as GetSSSR(). |
961 | 2.34k | OBGenericData *existing = GetData("LSSR"); |
962 | 2.34k | OBRingData *rd = dynamic_cast<OBRingData *>(existing); |
963 | 2.34k | if (rd == nullptr) { |
964 | 319 | if (existing) DeleteData(existing); |
965 | 319 | rd = new OBRingData(); |
966 | 319 | rd->SetAttribute("LSSR"); |
967 | 319 | SetData(rd); |
968 | 319 | } |
969 | 2.34k | rd->SetOrigin(perceived); |
970 | 2.34k | return(rd->GetData()); |
971 | 2.34k | } |
972 | | |
973 | | double OBMol::GetMolWt(bool implicitH) |
974 | 0 | { |
975 | 0 | double molwt=0.0; |
976 | 0 | OBAtom *atom; |
977 | 0 | vector<OBAtom*>::iterator i; |
978 | |
|
979 | 0 | double hmass = OBElements::GetMass(1); |
980 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) { |
981 | 0 | molwt += atom->GetAtomicMass(); |
982 | 0 | if (implicitH) |
983 | 0 | molwt += atom->GetImplicitHCount() * hmass; |
984 | 0 | } |
985 | 0 | return(molwt); |
986 | 0 | } |
987 | | |
988 | | double OBMol::GetExactMass(bool implicitH) |
989 | 0 | { |
990 | 0 | double mass=0.0; |
991 | 0 | OBAtom *atom; |
992 | 0 | vector<OBAtom*>::iterator i; |
993 | |
|
994 | 0 | double hmass = OBElements::GetExactMass(1, 1); |
995 | 0 | for (atom = BeginAtom(i); atom; atom = NextAtom(i)) { |
996 | 0 | mass += atom->GetExactMass(); |
997 | 0 | if (implicitH) |
998 | 0 | mass += atom->GetImplicitHCount() * hmass; |
999 | 0 | } |
1000 | |
|
1001 | 0 | return(mass); |
1002 | 0 | } |
1003 | | |
1004 | | //! Stochoimetric formula in spaced format e.g. C 4 H 6 O 1 |
1005 | | //! No pair data is stored. Normally use without parameters: GetSpacedFormula() |
1006 | | //! \since version 2.1 |
1007 | | string OBMol::GetSpacedFormula(int ones, const char* sp, bool implicitH) |
1008 | 0 | { |
1009 | | //Default ones=0, sp=" ". |
1010 | | //Using ones=1 and sp="" will give unspaced formula (and no pair data entry) |
1011 | | // These are the atomic numbers of the elements in alphabetical order, plus |
1012 | | // pseudo atomic numbers for D, T isotopes. |
1013 | 0 | const int NumElements = 118 + 2; |
1014 | 0 | const int alphabetical[NumElements] = { |
1015 | 0 | 89, 47, 13, 95, 18, 33, 85, 79, 5, 56, 4, 107, 83, 97, 35, 6, 20, 48, |
1016 | 0 | 58, 98, 17, 96, 112, 27, 24, 55, 29, NumElements-1, |
1017 | 0 | 105, 110, 66, 68, 99, 63, 9, 26, 114, 100, 87, 31, |
1018 | 0 | 64, 32, 1, 2, 72, 80, 67, 108, 53, 49, 77, 19, 36, 57, 3, 103, 71, 116, 115, 101, |
1019 | 0 | 12, 25, 42, 109, 7, 11, 41, 60, 10, 113, 28, 102, 93, 8, 118, 76, 15, 91, 82, 46, |
1020 | 0 | 61, 84, 59, 78, 94, 88, 37, 75, 104, 111, 45, 86, 44, 16, 51, 21, 34, 106, 14, |
1021 | 0 | 62, 50, 38, NumElements, 73, 65, 43, 52, 90, 22, 81, 69, 117, 92, 23, 74, 54, 39, 70, |
1022 | 0 | 30, 40 }; |
1023 | |
|
1024 | 0 | int atomicCount[NumElements]; |
1025 | 0 | stringstream formula; |
1026 | |
|
1027 | 0 | for (int i = 0; i < NumElements; ++i) |
1028 | 0 | atomicCount[i] = 0; |
1029 | |
|
1030 | 0 | bool UseImplicitH = (NumBonds()!=0 || NumAtoms()==1); |
1031 | | // Do not use implicit hydrogens if explicitly required not to |
1032 | 0 | if (!implicitH) UseImplicitH = false; |
1033 | 0 | bool HasHvyAtoms = NumHvyAtoms()>0; |
1034 | 0 | FOR_ATOMS_OF_MOL(a, *this) |
1035 | 0 | { |
1036 | 0 | int anum = a->GetAtomicNum(); |
1037 | 0 | if(anum==0) |
1038 | 0 | continue; |
1039 | 0 | if(anum > (NumElements-2)) { |
1040 | 0 | char buffer[BUFF_SIZE]; // error buffer |
1041 | 0 | snprintf(buffer, BUFF_SIZE, "Skipping unknown element with atomic number %d", anum); |
1042 | 0 | obErrorLog.ThrowError(__FUNCTION__, buffer, obWarning); |
1043 | 0 | continue; |
1044 | 0 | } |
1045 | 0 | unsigned int iso = a->GetIsotope(); |
1046 | 0 | bool IsHiso = anum == 1 && (iso == 2 || iso == 3); |
1047 | 0 | if(UseImplicitH) |
1048 | 0 | { |
1049 | 0 | if (anum == 1 && !IsHiso && HasHvyAtoms) |
1050 | 0 | continue; // skip explicit hydrogens except D,T |
1051 | 0 | if(anum==1) |
1052 | 0 | { |
1053 | 0 | if (IsHiso && HasHvyAtoms) |
1054 | 0 | --atomicCount[0]; //one of the implicit hydrogens is now explicit |
1055 | 0 | } |
1056 | 0 | else |
1057 | 0 | atomicCount[0] += a->GetImplicitHCount() + a->ExplicitHydrogenCount(); |
1058 | 0 | } |
1059 | 0 | if (IsHiso) |
1060 | 0 | anum = NumElements + iso - 3; //pseudo AtNo for D, T |
1061 | 0 | atomicCount[anum - 1]++; |
1062 | 0 | } |
1063 | |
|
1064 | 0 | if (atomicCount[5] != 0) // Carbon (i.e. 6 - 1 = 5) |
1065 | 0 | { |
1066 | 0 | if (atomicCount[5] > ones) |
1067 | 0 | formula << "C" << sp << atomicCount[5] << sp; |
1068 | 0 | else if (atomicCount[5] == 1) |
1069 | 0 | formula << "C"; |
1070 | |
|
1071 | 0 | atomicCount[5] = 0; // So we don't output C twice |
1072 | | |
1073 | | // only output H if there's also carbon -- otherwise do it alphabetical |
1074 | 0 | if (atomicCount[0] != 0) // Hydrogen (i.e., 1 - 1 = 0) |
1075 | 0 | { |
1076 | 0 | if (atomicCount[0] > ones) |
1077 | 0 | formula << "H" << sp << atomicCount[0] << sp; |
1078 | 0 | else if (atomicCount[0] == 1) |
1079 | 0 | formula << "H"; |
1080 | |
|
1081 | 0 | atomicCount[0] = 0; |
1082 | 0 | } |
1083 | 0 | } |
1084 | |
|
1085 | 0 | for (int j = 0; j < NumElements; ++j) |
1086 | 0 | { |
1087 | 0 | char DT[4] = {'D',0,'T',0}; |
1088 | 0 | const char* symb; |
1089 | 0 | int alph = alphabetical[j]-1; |
1090 | 0 | if (atomicCount[ alph ]) |
1091 | 0 | { |
1092 | 0 | if(alph==NumElements-1) |
1093 | 0 | symb = DT + 2;//T |
1094 | 0 | else if (alph==NumElements-2) |
1095 | 0 | symb = DT; //D |
1096 | 0 | else |
1097 | 0 | symb = OBElements::GetSymbol(alphabetical[j]); |
1098 | |
|
1099 | 0 | formula << symb << sp; |
1100 | 0 | if(atomicCount[alph] > ones) |
1101 | 0 | formula << atomicCount[alph] << sp; |
1102 | 0 | } |
1103 | 0 | } |
1104 | |
|
1105 | 0 | int chg = GetTotalCharge(); |
1106 | 0 | char ch = chg>0 ? '+' : '-' ; |
1107 | 0 | chg = abs(chg); |
1108 | 0 | while(chg--) |
1109 | 0 | formula << ch << sp; |
1110 | |
|
1111 | 0 | string f_str = formula.str(); |
1112 | 0 | return (Trim(f_str)); |
1113 | 0 | } |
1114 | | |
1115 | | //! Stochoimetric formula (e.g., C4H6O). |
1116 | | //! This is either set by OBMol::SetFormula() or generated on-the-fly |
1117 | | //! using the "Hill order" -- i.e., C first if present, then H if present |
1118 | | //! all other elements in alphabetical order. |
1119 | | string OBMol::GetFormula() |
1120 | 0 | { |
1121 | 0 | string attr = "Formula"; |
1122 | 0 | OBPairData *dp = (OBPairData *) GetData(attr); |
1123 | |
|
1124 | 0 | if (dp != nullptr) // we already set the formula (or it was read from a file) |
1125 | 0 | return dp->GetValue(); |
1126 | | |
1127 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
1128 | 0 | "Ran OpenBabel::SetFormula -- Hill order formula", |
1129 | 0 | obAuditMsg); |
1130 | |
|
1131 | 0 | string sformula = GetSpacedFormula(1, ""); |
1132 | |
|
1133 | 0 | dp = new OBPairData; |
1134 | 0 | dp->SetAttribute(attr); |
1135 | 0 | dp->SetValue( sformula ); |
1136 | 0 | dp->SetOrigin( perceived ); // internal generation |
1137 | 0 | SetData(dp); |
1138 | 0 | return sformula; |
1139 | 0 | } |
1140 | | |
1141 | | void OBMol::SetFormula(string molFormula) |
1142 | 0 | { |
1143 | 0 | string attr = "Formula"; |
1144 | 0 | OBPairData *dp = (OBPairData *) GetData(attr); |
1145 | 0 | if (dp == nullptr) |
1146 | 0 | { |
1147 | 0 | dp = new OBPairData; |
1148 | 0 | dp->SetAttribute(attr); |
1149 | 0 | SetData(dp); |
1150 | 0 | } |
1151 | 0 | dp->SetValue(molFormula); |
1152 | | // typically file input, but this needs to be revisited |
1153 | 0 | dp->SetOrigin(fileformatInput); |
1154 | 0 | } |
1155 | | |
1156 | | void OBMol::SetTotalCharge(int charge) |
1157 | 0 | { |
1158 | 0 | SetFlag(OB_TCHARGE_MOL); |
1159 | 0 | _totalCharge = charge; |
1160 | 0 | } |
1161 | | |
1162 | | //! Returns the total molecular charge -- if it has not previously been set |
1163 | | //! it is calculated from the atomic formal charge information. |
1164 | | //! (This may or may not be correct!) |
1165 | | //! If you set atomic charges with OBAtom::SetFormalCharge() |
1166 | | //! you really should set the molecular charge with OBMol::SetTotalCharge() |
1167 | | int OBMol::GetTotalCharge() |
1168 | 0 | { |
1169 | 0 | if(HasFlag(OB_TCHARGE_MOL)) |
1170 | 0 | return(_totalCharge); |
1171 | 0 | else // calculate from atomic formal charges (seems the best default) |
1172 | 0 | { |
1173 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
1174 | 0 | "Ran OpenBabel::GetTotalCharge -- calculated from formal charges", |
1175 | 0 | obAuditMsg); |
1176 | |
|
1177 | 0 | OBAtom *atom; |
1178 | 0 | vector<OBAtom*>::iterator i; |
1179 | 0 | int chg = 0; |
1180 | |
|
1181 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
1182 | 0 | chg += atom->GetFormalCharge(); |
1183 | 0 | return (chg); |
1184 | 0 | } |
1185 | 0 | } |
1186 | | |
1187 | | void OBMol::SetTotalSpinMultiplicity(unsigned int spin) |
1188 | 0 | { |
1189 | 0 | SetFlag(OB_TSPIN_MOL); |
1190 | 0 | _totalSpin = spin; |
1191 | 0 | } |
1192 | | |
1193 | 0 | void OBMol::SetInternalCoord(std::vector<OBInternalCoord*> int_coord) { |
1194 | 0 | if (int_coord[0] != nullptr) { |
1195 | 0 | std::vector<OBInternalCoord*>::iterator it = int_coord.begin(); |
1196 | 0 | int_coord.insert(it, nullptr); |
1197 | 0 | } |
1198 | |
|
1199 | 0 | if (int_coord.size() != _natoms + 1) { |
1200 | 0 | string error = "Number of internal coordinates is not the same as"; |
1201 | 0 | error += " the number of atoms in molecule"; |
1202 | 0 | obErrorLog.ThrowError(__FUNCTION__, error, obError); |
1203 | 0 | return; |
1204 | 0 | } |
1205 | | |
1206 | 0 | _internals = int_coord; |
1207 | |
|
1208 | 0 | return; |
1209 | 0 | } |
1210 | | |
1211 | | //! Returns the total spin multiplicity -- if it has not previously been set |
1212 | | //! It is calculated from the atomic spin multiplicity information |
1213 | | //! assuming the high-spin case (i.e. it simply sums the number of unpaired |
1214 | | //! electrons assuming no further pairing of spins. |
1215 | | //! if it fails (gives singlet for odd number of electronic systems), |
1216 | | //! then assign wrt parity of the total electrons. |
1217 | | unsigned int OBMol::GetTotalSpinMultiplicity() |
1218 | 0 | { |
1219 | 0 | if (HasFlag(OB_TSPIN_MOL)) |
1220 | 0 | return(_totalSpin); |
1221 | 0 | else // calculate from atomic spin information (assuming high-spin case) |
1222 | 0 | { |
1223 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
1224 | 0 | "Ran OpenBabel::GetTotalSpinMultiplicity -- calculating from atomic spins assuming high spin case", |
1225 | 0 | obAuditMsg); |
1226 | |
|
1227 | 0 | OBAtom *atom; |
1228 | 0 | vector<OBAtom*>::iterator i; |
1229 | 0 | unsigned int unpaired_electrons = 0; |
1230 | 0 | int chg = GetTotalCharge(); |
1231 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
1232 | 0 | { |
1233 | 0 | if (atom->GetSpinMultiplicity() > 1) |
1234 | 0 | unpaired_electrons += (atom->GetSpinMultiplicity() - 1); |
1235 | 0 | chg += static_cast<int>(atom->GetAtomicNum()); |
1236 | 0 | } |
1237 | 0 | if (chg % 2 != unpaired_electrons %2) |
1238 | 0 | return ((abs(chg) % 2) + 1); |
1239 | 0 | else |
1240 | 0 | return (unpaired_electrons + 1); |
1241 | 0 | } |
1242 | 0 | } |
1243 | | |
1244 | | OBMol &OBMol::operator=(const OBMol &source) |
1245 | | //atom and bond info is copied from src to dest |
1246 | | //Conformers are now copied also, MM 2/7/01 |
1247 | | //Residue information are copied, MM 4-27-01 |
1248 | | //All OBGenericData incl OBRotameterList is copied, CM 2006 |
1249 | | //Zeros all flags except OB_TCHARGE_MOL, OB_PCHARGE_MOL, OB_HYBRID_MOL |
1250 | | //OB_TSPIN_MOL, OB_AROMATIC_MOL, OB_PERIODIC_MOL, OB_CHAINS_MOL and OB_PATTERN_STRUCTURE which are copied |
1251 | 0 | { |
1252 | 0 | if (this == &source) |
1253 | 0 | return *this; |
1254 | | |
1255 | 0 | OBMol &src = (OBMol &)source; |
1256 | 0 | vector<OBAtom*>::iterator i; |
1257 | 0 | vector<OBBond*>::iterator j; |
1258 | 0 | OBAtom *atom; |
1259 | 0 | OBBond *bond; |
1260 | |
|
1261 | 0 | Clear(); |
1262 | 0 | BeginModify(); |
1263 | |
|
1264 | 0 | _vatom.reserve(src.NumAtoms()); |
1265 | 0 | _atomIds.reserve(src.NumAtoms()); |
1266 | 0 | _vbond.reserve(src.NumBonds()); |
1267 | 0 | _bondIds.reserve(src.NumBonds()); |
1268 | |
|
1269 | 0 | for (atom = src.BeginAtom(i);atom;atom = src.NextAtom(i)) |
1270 | 0 | AddAtom(*atom); |
1271 | 0 | for (bond = src.BeginBond(j);bond;bond = src.NextBond(j)) |
1272 | 0 | AddBond(*bond); |
1273 | |
|
1274 | 0 | this->_title = src.GetTitle(); |
1275 | 0 | this->_energy = src.GetEnergy(); |
1276 | 0 | this->_dimension = src.GetDimension(); |
1277 | 0 | this->SetTotalCharge(src.GetTotalCharge()); //also sets a flag |
1278 | 0 | this->SetTotalSpinMultiplicity(src.GetTotalSpinMultiplicity()); //also sets a flag |
1279 | |
|
1280 | 0 | EndModify(); //zeros flags! |
1281 | |
|
1282 | 0 | if (src.HasFlag(OB_PATTERN_STRUCTURE)) |
1283 | 0 | this->SetFlag(OB_PATTERN_STRUCTURE); |
1284 | 0 | if (src.HasFlag(OB_TSPIN_MOL)) |
1285 | 0 | this->SetFlag(OB_TSPIN_MOL); |
1286 | 0 | if (src.HasFlag(OB_TCHARGE_MOL)) |
1287 | 0 | this->SetFlag(OB_TCHARGE_MOL); |
1288 | 0 | if (src.HasFlag(OB_PCHARGE_MOL)) |
1289 | 0 | this->SetFlag(OB_PCHARGE_MOL); |
1290 | 0 | if (src.HasFlag(OB_PERIODIC_MOL)) |
1291 | 0 | this->SetFlag(OB_PERIODIC_MOL); |
1292 | 0 | if (src.HasFlag(OB_HYBRID_MOL)) |
1293 | 0 | this->SetFlag(OB_HYBRID_MOL); |
1294 | 0 | if (src.HasFlag(OB_AROMATIC_MOL)) |
1295 | 0 | this->SetFlag(OB_AROMATIC_MOL); |
1296 | 0 | if (src.HasFlag(OB_CHAINS_MOL)) |
1297 | 0 | this->SetFlag(OB_CHAINS_MOL); |
1298 | | //this->_flags = src.GetFlags(); //Copy all flags. Perhaps too drastic a change |
1299 | | |
1300 | | |
1301 | | //Copy Residue information |
1302 | 0 | unsigned int NumRes = src.NumResidues(); |
1303 | 0 | if (NumRes) |
1304 | 0 | { |
1305 | 0 | unsigned int k; |
1306 | 0 | OBResidue *src_res = nullptr; |
1307 | 0 | OBResidue *res = nullptr; |
1308 | 0 | OBAtom *src_atom = nullptr; |
1309 | 0 | OBAtom *atom = nullptr; |
1310 | 0 | vector<OBAtom*>::iterator ii; |
1311 | 0 | for (k=0 ; k<NumRes ; ++k) |
1312 | 0 | { |
1313 | 0 | res = NewResidue(); |
1314 | 0 | src_res = src.GetResidue(k); |
1315 | 0 | *res = *src_res; //does not copy atoms |
1316 | 0 | for (src_atom=src_res->BeginAtom(ii) ; src_atom ; src_atom=src_res->NextAtom(ii)) |
1317 | 0 | { |
1318 | 0 | atom = GetAtom(src_atom->GetIdx()); |
1319 | 0 | res->AddAtom(atom); |
1320 | 0 | res->SetAtomID(atom,src_res->GetAtomID(src_atom)); |
1321 | 0 | res->SetHetAtom(atom,src_res->IsHetAtom(src_atom)); |
1322 | 0 | res->SetSerialNum(atom,src_res->GetSerialNum(src_atom)); |
1323 | 0 | } |
1324 | 0 | } |
1325 | 0 | } |
1326 | | |
1327 | | //Copy conformer information |
1328 | 0 | if (src.NumConformers() > 1) { |
1329 | 0 | int k;//,l; |
1330 | 0 | vector<double*> conf; |
1331 | 0 | int currConf = -1; |
1332 | 0 | double* xyz = nullptr; |
1333 | 0 | for (k=0 ; k<src.NumConformers() ; ++k) { |
1334 | 0 | xyz = new double [3*src.NumAtoms()]; |
1335 | 0 | memcpy( xyz, src.GetConformer(k), sizeof( double )*3*src.NumAtoms() ); |
1336 | 0 | conf.push_back(xyz); |
1337 | |
|
1338 | 0 | if( src.GetConformer(k) == src._c ) { |
1339 | 0 | currConf = k; |
1340 | 0 | } |
1341 | 0 | } |
1342 | |
|
1343 | 0 | SetConformers(conf); |
1344 | 0 | if( currConf >= 0 && _vconf.size() ) { |
1345 | 0 | _c = _vconf[currConf]; |
1346 | 0 | } |
1347 | 0 | } |
1348 | | |
1349 | | //Copy all the OBGenericData, providing the new molecule, this, |
1350 | | //for those classes like OBRotameterList which contain Atom pointers |
1351 | | //OBGenericData classes can choose not to be cloned by returning NULL |
1352 | 0 | vector<OBGenericData*>::iterator itr; |
1353 | 0 | for(itr=src.BeginData();itr!=src.EndData();++itr) |
1354 | 0 | { |
1355 | 0 | OBGenericData* pCopiedData = (*itr)->Clone(this); |
1356 | 0 | SetData(pCopiedData); |
1357 | 0 | } |
1358 | |
|
1359 | 0 | if (src.HasChiralityPerceived()) |
1360 | 0 | SetChiralityPerceived(); |
1361 | |
|
1362 | 0 | return(*this); |
1363 | 0 | } |
1364 | | |
1365 | | OBMol &OBMol::operator+=(const OBMol &source) |
1366 | 0 | { |
1367 | 0 | OBMol &src = (OBMol &)source; |
1368 | 0 | vector<OBAtom*>::iterator i; |
1369 | 0 | vector<OBBond*>::iterator j; |
1370 | 0 | vector<OBResidue*>::iterator k; |
1371 | 0 | OBAtom *atom; |
1372 | 0 | OBBond *bond; |
1373 | 0 | OBResidue *residue; |
1374 | |
|
1375 | 0 | BeginModify(); |
1376 | |
|
1377 | 0 | int prevatms = NumAtoms(); |
1378 | |
|
1379 | 0 | string extitle(src.GetTitle()); |
1380 | 0 | if(!extitle.empty()) |
1381 | 0 | _title += "_" + extitle; |
1382 | | |
1383 | | // First, handle atoms and bonds |
1384 | 0 | map<unsigned long int, unsigned long int> correspondingId; |
1385 | 0 | for (atom = src.BeginAtom(i) ; atom ; atom = src.NextAtom(i)) { |
1386 | 0 | AddAtom(*atom, true); // forceNewId=true (don't reuse the original Id) |
1387 | 0 | OBAtom *addedAtom = GetAtom(NumAtoms()); |
1388 | 0 | correspondingId[atom->GetId()] = addedAtom->GetId(); |
1389 | 0 | } |
1390 | 0 | correspondingId[OBStereo::ImplicitRef] = OBStereo::ImplicitRef; |
1391 | |
|
1392 | 0 | for (bond = src.BeginBond(j) ; bond ; bond = src.NextBond(j)) { |
1393 | 0 | bond->SetId(NoId);//Need to remove ID which relates to source mol rather than this mol |
1394 | 0 | AddBond(bond->GetBeginAtomIdx() + prevatms, |
1395 | 0 | bond->GetEndAtomIdx() + prevatms, |
1396 | 0 | bond->GetBondOrder(), bond->GetFlags()); |
1397 | 0 | } |
1398 | | |
1399 | | // Now update all copied residues too |
1400 | 0 | for (residue = src.BeginResidue(k); residue; residue = src.NextResidue(k)) { |
1401 | 0 | AddResidue(*residue); |
1402 | |
|
1403 | 0 | FOR_ATOMS_OF_RESIDUE(resAtom, residue) |
1404 | 0 | { |
1405 | | // This is the equivalent atom in our combined molecule |
1406 | 0 | atom = GetAtom(resAtom->GetIdx() + prevatms); |
1407 | | // So we add this to the last-added residue |
1408 | | // (i.e., what we just copied) |
1409 | 0 | (_residue[_residue.size() - 1])->AddAtom(atom); |
1410 | 0 | } |
1411 | 0 | } |
1412 | | |
1413 | | // Copy the stereo |
1414 | 0 | std::vector<OBGenericData*> vdata = src.GetAllData(OBGenericDataType::StereoData); |
1415 | 0 | for (std::vector<OBGenericData*>::iterator data = vdata.begin(); data != vdata.end(); ++data) { |
1416 | 0 | OBStereo::Type datatype = ((OBStereoBase*)*data)->GetType(); |
1417 | 0 | if (datatype == OBStereo::CisTrans) { |
1418 | 0 | OBCisTransStereo *ct = dynamic_cast<OBCisTransStereo*>(*data); |
1419 | 0 | OBCisTransStereo *nct = new OBCisTransStereo(this); |
1420 | 0 | OBCisTransStereo::Config ct_cfg = ct->GetConfig(); |
1421 | 0 | ct_cfg.begin = correspondingId[ct_cfg.begin]; |
1422 | 0 | ct_cfg.end = correspondingId[ct_cfg.end]; |
1423 | 0 | for(OBStereo::RefIter ri = ct_cfg.refs.begin(); ri != ct_cfg.refs.end(); ++ri) |
1424 | 0 | *ri = correspondingId[*ri]; |
1425 | 0 | nct->SetConfig(ct_cfg); |
1426 | 0 | SetData(nct); |
1427 | 0 | } |
1428 | 0 | else if (datatype == OBStereo::Tetrahedral) { |
1429 | 0 | OBTetrahedralStereo *ts = dynamic_cast<OBTetrahedralStereo*>(*data); |
1430 | 0 | OBTetrahedralStereo *nts = new OBTetrahedralStereo(this); |
1431 | 0 | OBTetrahedralStereo::Config ts_cfg = ts->GetConfig(); |
1432 | 0 | ts_cfg.center = correspondingId[ts_cfg.center]; |
1433 | 0 | ts_cfg.from = correspondingId[ts_cfg.from]; |
1434 | 0 | for(OBStereo::RefIter ri = ts_cfg.refs.begin(); ri != ts_cfg.refs.end(); ++ri) |
1435 | 0 | *ri = correspondingId[*ri]; |
1436 | 0 | nts->SetConfig(ts_cfg); |
1437 | 0 | SetData(nts); |
1438 | 0 | } |
1439 | 0 | } |
1440 | | |
1441 | | // TODO: This is actually a weird situation (e.g., adding a 2D mol to 3D one) |
1442 | | // We should do something to update the src coordinates if they're not 3D |
1443 | 0 | if(src.GetDimension()<_dimension) |
1444 | 0 | _dimension = src.GetDimension(); |
1445 | | // TODO: Periodicity is similarly weird (e.g., adding nonperiodic data to |
1446 | | // a crystal, or two incompatible lattice parameters). For now, just assume |
1447 | | // we intend to keep the lattice of the source (no updates necessary) |
1448 | |
|
1449 | 0 | EndModify(); |
1450 | |
|
1451 | 0 | return(*this); |
1452 | 0 | } |
1453 | | |
1454 | | bool OBMol::Clear() |
1455 | 17.5k | { |
1456 | 17.5k | if (obErrorLog.GetOutputLevel() >= obAuditMsg) |
1457 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
1458 | 0 | "Ran OpenBabel::Clear Molecule", obAuditMsg); |
1459 | | |
1460 | | // Destroy residues first: ~OBResidue() walks its atom list to clear |
1461 | | // back-pointers via SetResidue(nullptr). If atoms were destroyed |
1462 | | // first, those calls would dereference dead pointers (UBSAN trips |
1463 | | // in residue.cpp:853). The symmetric path in ~OBAtom() already |
1464 | | // handles the reverse direction by checking _residue != nullptr. |
1465 | 17.5k | unsigned int ii; |
1466 | 17.5k | for (ii=0 ; ii<_residue.size() ; ++ii) |
1467 | 0 | { |
1468 | 0 | DestroyResidue(_residue[ii]); |
1469 | 0 | } |
1470 | 17.5k | _residue.clear(); |
1471 | | |
1472 | 17.5k | vector<OBAtom*>::iterator i; |
1473 | 17.5k | vector<OBBond*>::iterator j; |
1474 | 17.5k | for (i = _vatom.begin();i != _vatom.end();++i) |
1475 | 0 | { |
1476 | 0 | DestroyAtom(*i); |
1477 | 0 | *i = nullptr; |
1478 | 0 | } |
1479 | 17.5k | for (j = _vbond.begin();j != _vbond.end();++j) |
1480 | 0 | { |
1481 | 0 | DestroyBond(*j); |
1482 | 0 | *j = nullptr; |
1483 | 0 | } |
1484 | | |
1485 | 17.5k | _atomIds.clear(); |
1486 | 17.5k | _bondIds.clear(); |
1487 | 17.5k | _natoms = _nbonds = 0; |
1488 | | |
1489 | | //clear out the multiconformer data |
1490 | 17.5k | vector<double*>::iterator k; |
1491 | 17.5k | for (k = _vconf.begin();k != _vconf.end();++k) |
1492 | 0 | delete [] *k; |
1493 | 17.5k | _vconf.clear(); |
1494 | | |
1495 | | //Clear flags except OB_PATTERN_STRUCTURE which is left the same |
1496 | 17.5k | _flags &= OB_PATTERN_STRUCTURE; |
1497 | | |
1498 | 17.5k | _c = nullptr; |
1499 | 17.5k | _mod = 0; |
1500 | | |
1501 | | // Clean up generic data via the base class |
1502 | 17.5k | return(OBBase::Clear()); |
1503 | 17.5k | } |
1504 | | |
1505 | | void OBMol::BeginModify() |
1506 | 17.3k | { |
1507 | | //suck coordinates from _c into _v for each atom |
1508 | 17.3k | if (!_mod && !Empty()) |
1509 | 0 | { |
1510 | 0 | OBAtom *atom; |
1511 | 0 | vector<OBAtom*>::iterator i; |
1512 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
1513 | 0 | { |
1514 | 0 | atom->SetVector(); |
1515 | 0 | atom->ClearCoordPtr(); |
1516 | 0 | } |
1517 | |
|
1518 | 0 | vector<double*>::iterator j; |
1519 | 0 | for (j = _vconf.begin();j != _vconf.end();++j) |
1520 | 0 | delete [] *j; |
1521 | |
|
1522 | 0 | _c = nullptr; |
1523 | 0 | _vconf.clear(); |
1524 | | |
1525 | | //Destroy rotamer list if necessary |
1526 | 0 | if ((OBRotamerList *)GetData(OBGenericDataType::RotamerList)) |
1527 | 0 | { |
1528 | 0 | delete (OBRotamerList *)GetData(OBGenericDataType::RotamerList); |
1529 | 0 | DeleteData(OBGenericDataType::RotamerList); |
1530 | 0 | } |
1531 | 0 | } |
1532 | | |
1533 | 17.3k | _mod++; |
1534 | 17.3k | } |
1535 | | |
1536 | | void OBMol::EndModify(bool nukePerceivedData) |
1537 | 14.9k | { |
1538 | 14.9k | if (_mod == 0) |
1539 | 0 | { |
1540 | 0 | obErrorLog.ThrowError(__FUNCTION__, "_mod is negative - EndModify() called too many times", obDebug); |
1541 | 0 | return; |
1542 | 0 | } |
1543 | | |
1544 | 14.9k | _mod--; |
1545 | | |
1546 | 14.9k | if (_mod) |
1547 | 0 | return; |
1548 | | |
1549 | | // wipe all but whether it has aromaticity perceived, is a reaction, or has periodic boundaries enabled |
1550 | 14.9k | if (nukePerceivedData) |
1551 | 14.9k | _flags = _flags & (OB_AROMATIC_MOL|OB_REACTION_MOL|OB_PERIODIC_MOL); |
1552 | | |
1553 | 14.9k | _c = nullptr; |
1554 | | |
1555 | 14.9k | if (Empty()) |
1556 | 2.45k | return; |
1557 | | |
1558 | | //if atoms present convert coords into array |
1559 | 12.4k | double *c = new double [NumAtoms()*3]; |
1560 | 12.4k | _c = c; |
1561 | | |
1562 | 12.4k | unsigned int idx; |
1563 | 12.4k | OBAtom *atom; |
1564 | 12.4k | vector<OBAtom*>::iterator j; |
1565 | 1.65M | for (idx=0,atom = BeginAtom(j);atom;atom = NextAtom(j),++idx) |
1566 | 1.64M | { |
1567 | 1.64M | atom->SetIdx(idx+1); |
1568 | 1.64M | (atom->GetVector()).Get(&_c[idx*3]); |
1569 | 1.64M | atom->SetCoordPtr(&_c); |
1570 | 1.64M | } |
1571 | 12.4k | _vconf.push_back(c); |
1572 | | |
1573 | | // Always remove angle and torsion data, since they will interfere with the iterators |
1574 | | // PR#2812013 |
1575 | 12.4k | DeleteData(OBGenericDataType::AngleData); |
1576 | 12.4k | DeleteData(OBGenericDataType::TorsionData); |
1577 | 12.4k | } |
1578 | | |
1579 | | void OBMol::DestroyAtom(OBAtom *atom) |
1580 | 1.66M | { |
1581 | 1.66M | if (atom) |
1582 | 1.66M | { |
1583 | 1.66M | delete atom; |
1584 | 1.66M | atom = nullptr; |
1585 | 1.66M | } |
1586 | 1.66M | } |
1587 | | |
1588 | | void OBMol::DestroyBond(OBBond *bond) |
1589 | 16.6k | { |
1590 | 16.6k | if (bond) |
1591 | 16.6k | { |
1592 | 16.6k | delete bond; |
1593 | 16.6k | bond = nullptr; |
1594 | 16.6k | } |
1595 | 16.6k | } |
1596 | | |
1597 | | void OBMol::DestroyResidue(OBResidue *residue) |
1598 | 0 | { |
1599 | 0 | if (residue) |
1600 | 0 | { |
1601 | 0 | delete residue; |
1602 | 0 | residue = nullptr; |
1603 | 0 | } |
1604 | 0 | } |
1605 | | |
1606 | | OBAtom *OBMol::NewAtom() |
1607 | 125k | { |
1608 | 125k | return NewAtom(_atomIds.size()); |
1609 | 125k | } |
1610 | | |
1611 | | //! \brief Instantiate a New Atom and add it to the molecule |
1612 | | //! |
1613 | | //! Checks bond_queue for any bonds that should be made to the new atom |
1614 | | //! and updates atom indexes. |
1615 | | OBAtom *OBMol::NewAtom(unsigned long id) |
1616 | 125k | { |
1617 | | // BeginModify(); |
1618 | | |
1619 | | // resize _atomIds if needed |
1620 | 125k | if (id >= _atomIds.size()) { |
1621 | 125k | unsigned int size = _atomIds.size(); |
1622 | 125k | _atomIds.resize(id+1); |
1623 | 125k | for (unsigned long i = size; i < id; ++i) |
1624 | 0 | _atomIds[i] = nullptr; |
1625 | 125k | } |
1626 | | |
1627 | 125k | if (_atomIds.at(id)) |
1628 | 0 | return nullptr; |
1629 | | |
1630 | 125k | OBAtom *obatom = new OBAtom; |
1631 | 125k | obatom->SetIdx(_natoms+1); |
1632 | 125k | obatom->SetParent(this); |
1633 | | |
1634 | 125k | _atomIds[id] = obatom; |
1635 | 125k | obatom->SetId(id); |
1636 | | |
1637 | 125k | #define OBAtomIncrement 100 |
1638 | | |
1639 | 125k | if (_natoms+1 >= _vatom.size()) |
1640 | 11.6k | { |
1641 | 11.6k | _vatom.resize(_natoms+OBAtomIncrement); |
1642 | 11.6k | vector<OBAtom*>::iterator j; |
1643 | 1.16M | for (j = _vatom.begin(),j+=(_natoms+1);j != _vatom.end();++j) |
1644 | 1.15M | *j = nullptr; |
1645 | 11.6k | } |
1646 | 125k | #undef OBAtomIncrement |
1647 | | |
1648 | | |
1649 | 125k | _vatom[_natoms] = obatom; |
1650 | 125k | _natoms++; |
1651 | | |
1652 | 125k | if (HasData(OBGenericDataType::VirtualBondData)) |
1653 | 0 | { |
1654 | | /*add bonds that have been queued*/ |
1655 | 0 | OBVirtualBond *vb; |
1656 | 0 | vector<OBGenericData*> verase; |
1657 | 0 | vector<OBGenericData*>::iterator i; |
1658 | 0 | for (i = BeginData();i != EndData();++i) |
1659 | 0 | if ((*i)->GetDataType() == OBGenericDataType::VirtualBondData) |
1660 | 0 | { |
1661 | 0 | vb = (OBVirtualBond*)*i; |
1662 | 0 | if (vb->GetBgn() > _natoms || vb->GetEnd() > _natoms) |
1663 | 0 | continue; |
1664 | 0 | if (obatom->GetIdx() == static_cast<unsigned int>(vb->GetBgn()) |
1665 | 0 | || obatom->GetIdx() == static_cast<unsigned int>(vb->GetEnd())) |
1666 | 0 | { |
1667 | 0 | AddBond(vb->GetBgn(),vb->GetEnd(),vb->GetOrder()); |
1668 | 0 | verase.push_back(*i); |
1669 | 0 | } |
1670 | 0 | } |
1671 | |
|
1672 | 0 | if (!verase.empty()) |
1673 | 0 | DeleteData(verase); |
1674 | 0 | } |
1675 | | |
1676 | | // EndModify(); |
1677 | | |
1678 | 125k | return(obatom); |
1679 | 125k | } |
1680 | | |
1681 | | OBResidue *OBMol::NewResidue() |
1682 | 0 | { |
1683 | 0 | OBResidue *obresidue = new OBResidue; |
1684 | 0 | obresidue->SetIdx(_residue.size()); |
1685 | 0 | _residue.push_back(obresidue); |
1686 | 0 | return(obresidue); |
1687 | 0 | } |
1688 | | |
1689 | | OBBond *OBMol::NewBond() |
1690 | 0 | { |
1691 | 0 | return NewBond(_bondIds.size()); |
1692 | 0 | } |
1693 | | |
1694 | | //! \since version 2.1 |
1695 | | //! \brief Instantiate a New Bond and add it to the molecule |
1696 | | //! |
1697 | | //! Sets the proper Bond index and insures this molecule is set as the parent. |
1698 | | OBBond *OBMol::NewBond(unsigned long id) |
1699 | 0 | { |
1700 | | // resize _bondIds if needed |
1701 | 0 | if (id >= _bondIds.size()) { |
1702 | 0 | unsigned int size = _bondIds.size(); |
1703 | 0 | _bondIds.resize(id+1); |
1704 | 0 | for (unsigned long i = size; i < id; ++i) |
1705 | 0 | _bondIds[i] = nullptr; |
1706 | 0 | } |
1707 | |
|
1708 | 0 | if (_bondIds.at(id)) |
1709 | 0 | return nullptr; |
1710 | | |
1711 | 0 | OBBond *pBond = new OBBond; |
1712 | 0 | pBond->SetParent(this); |
1713 | 0 | pBond->SetIdx(_nbonds); |
1714 | |
|
1715 | 0 | _bondIds[id] = pBond; |
1716 | 0 | pBond->SetId(id); |
1717 | |
|
1718 | 0 | #define OBBondIncrement 100 |
1719 | 0 | if (_nbonds+1 >= _vbond.size()) |
1720 | 0 | { |
1721 | 0 | _vbond.resize(_nbonds+OBBondIncrement); |
1722 | 0 | vector<OBBond*>::iterator i; |
1723 | 0 | for (i = _vbond.begin(),i+=(_nbonds+1);i != _vbond.end();++i) |
1724 | 0 | *i = nullptr; |
1725 | 0 | } |
1726 | 0 | #undef OBBondIncrement |
1727 | |
|
1728 | 0 | _vbond[_nbonds] = (OBBond*)pBond; |
1729 | 0 | _nbonds++; |
1730 | |
|
1731 | 0 | return(pBond); |
1732 | 0 | } |
1733 | | |
1734 | | //! \brief Add an atom to a molecule |
1735 | | //! |
1736 | | //! Also checks bond_queue for any bonds that should be made to the new atom |
1737 | | bool OBMol::AddAtom(OBAtom &atom, bool forceNewId) |
1738 | 1.53M | { |
1739 | | // BeginModify(); |
1740 | | |
1741 | | // Use the existing atom Id unless either it's invalid or forceNewId has been specified |
1742 | 1.53M | unsigned long id; |
1743 | 1.53M | if (forceNewId) |
1744 | 0 | id = _atomIds.size(); |
1745 | 1.53M | else { |
1746 | 1.53M | id = atom.GetId(); |
1747 | 1.53M | if (id == NoId) |
1748 | 1.53M | id = _atomIds.size(); |
1749 | 1.53M | } |
1750 | | |
1751 | 1.53M | OBAtom *obatom = new OBAtom; |
1752 | 1.53M | *obatom = atom; |
1753 | 1.53M | obatom->SetIdx(_natoms+1); |
1754 | 1.53M | obatom->SetParent(this); |
1755 | | |
1756 | | // resize _atomIds if needed |
1757 | 1.53M | if (id >= _atomIds.size()) { |
1758 | 1.53M | unsigned int size = _atomIds.size(); |
1759 | 1.53M | _atomIds.resize(id+1); |
1760 | 1.53M | for (unsigned long i = size; i < id; ++i) |
1761 | 0 | _atomIds[i] = nullptr; |
1762 | 1.53M | } |
1763 | | |
1764 | 1.53M | obatom->SetId(id); |
1765 | 1.53M | _atomIds[id] = obatom; |
1766 | | |
1767 | 1.53M | #define OBAtomIncrement 100 |
1768 | | |
1769 | 1.53M | if (_natoms+1 >= _vatom.size()) |
1770 | 17.7k | { |
1771 | 17.7k | _vatom.resize(_natoms+OBAtomIncrement); |
1772 | 17.7k | vector<OBAtom*>::iterator j; |
1773 | 1.77M | for (j = _vatom.begin(),j+=(_natoms+1);j != _vatom.end();++j) |
1774 | 1.76M | *j = nullptr; |
1775 | 17.7k | } |
1776 | 1.53M | #undef OBAtomIncrement |
1777 | | |
1778 | 1.53M | _vatom[_natoms] = (OBAtom*)obatom; |
1779 | 1.53M | _natoms++; |
1780 | | |
1781 | 1.53M | if (HasData(OBGenericDataType::VirtualBondData)) |
1782 | 0 | { |
1783 | | /*add bonds that have been queued*/ |
1784 | 0 | OBVirtualBond *vb; |
1785 | 0 | vector<OBGenericData*> verase; |
1786 | 0 | vector<OBGenericData*>::iterator i; |
1787 | 0 | for (i = BeginData();i != EndData();++i) |
1788 | 0 | if ((*i)->GetDataType() == OBGenericDataType::VirtualBondData) |
1789 | 0 | { |
1790 | 0 | vb = (OBVirtualBond*)*i; |
1791 | 0 | if (vb->GetBgn() > _natoms || vb->GetEnd() > _natoms) |
1792 | 0 | continue; |
1793 | 0 | if (obatom->GetIdx() == static_cast<unsigned int>(vb->GetBgn()) |
1794 | 0 | || obatom->GetIdx() == static_cast<unsigned int>(vb->GetEnd())) |
1795 | 0 | { |
1796 | 0 | AddBond(vb->GetBgn(),vb->GetEnd(),vb->GetOrder()); |
1797 | 0 | verase.push_back(*i); |
1798 | 0 | } |
1799 | 0 | } |
1800 | |
|
1801 | 0 | if (!verase.empty()) |
1802 | 0 | DeleteData(verase); |
1803 | 0 | } |
1804 | | |
1805 | | // EndModify(); |
1806 | | |
1807 | 1.53M | return(true); |
1808 | 1.53M | } |
1809 | | |
1810 | | bool OBMol::InsertAtom(OBAtom &atom) |
1811 | 0 | { |
1812 | 0 | BeginModify(); |
1813 | 0 | AddAtom(atom); |
1814 | 0 | EndModify(); |
1815 | |
|
1816 | 0 | return(true); |
1817 | 0 | } |
1818 | | |
1819 | | bool OBMol::AddResidue(OBResidue &residue) |
1820 | 0 | { |
1821 | 0 | BeginModify(); |
1822 | |
|
1823 | 0 | OBResidue *obresidue = new OBResidue; |
1824 | 0 | *obresidue = residue; |
1825 | |
|
1826 | 0 | obresidue->SetIdx(_residue.size()); |
1827 | |
|
1828 | 0 | _residue.push_back(obresidue); |
1829 | |
|
1830 | 0 | EndModify(); |
1831 | |
|
1832 | 0 | return(true); |
1833 | 0 | } |
1834 | | |
1835 | | bool OBMol::StripSalts(unsigned int threshold) |
1836 | 0 | { |
1837 | 0 | vector<vector<int> > cfl; |
1838 | 0 | vector<vector<int> >::iterator i,max; |
1839 | |
|
1840 | 0 | ContigFragList(cfl); |
1841 | 0 | if (cfl.empty() || cfl.size() == 1) |
1842 | 0 | { |
1843 | 0 | return(false); |
1844 | 0 | } |
1845 | | |
1846 | | |
1847 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Ran OpenBabel::StripSalts", obAuditMsg); |
1848 | |
|
1849 | 0 | max = cfl.begin(); |
1850 | 0 | for (i = cfl.begin();i != cfl.end();++i) |
1851 | 0 | { |
1852 | 0 | if ((*max).size() < (*i).size()) |
1853 | 0 | max = i; |
1854 | 0 | } |
1855 | |
|
1856 | 0 | vector<int>::iterator j; |
1857 | 0 | vector<OBAtom*> delatoms; |
1858 | 0 | set<int> atomIndices; |
1859 | 0 | for (i = cfl.begin(); i != cfl.end(); ++i) |
1860 | 0 | { |
1861 | 0 | if (i->size() < threshold || (threshold == 0 && i != max)) |
1862 | 0 | { |
1863 | 0 | for (j = (*i).begin(); j != (*i).end(); ++j) |
1864 | 0 | { |
1865 | 0 | if (atomIndices.find( *j ) == atomIndices.end()) |
1866 | 0 | { |
1867 | 0 | delatoms.push_back(GetAtom(*j)); |
1868 | 0 | atomIndices.insert(*j); |
1869 | 0 | } |
1870 | 0 | } |
1871 | 0 | } |
1872 | 0 | } |
1873 | |
|
1874 | 0 | if (!delatoms.empty()) |
1875 | 0 | { |
1876 | | // int tmpflags = _flags & (~(OB_SSSR_MOL)); |
1877 | 0 | BeginModify(); |
1878 | 0 | vector<OBAtom*>::iterator k; |
1879 | 0 | for (k = delatoms.begin(); k != delatoms.end(); ++k) |
1880 | 0 | DeleteAtom((OBAtom*)*k); |
1881 | 0 | EndModify(); |
1882 | | // _flags = tmpflags; // Gave crash when SmartsPattern::Match() |
1883 | | // was called susequently |
1884 | | // Hans De Winter; 03-nov-2010 |
1885 | 0 | } |
1886 | |
|
1887 | 0 | return (true); |
1888 | 0 | } |
1889 | | |
1890 | | // Convenience function used by the DeleteHydrogens methods |
1891 | | static bool IsSuppressibleHydrogen(OBAtom *atom) |
1892 | 0 | { |
1893 | 0 | if (atom->GetIsotope() == 0 && atom->GetHvyDegree() == 1 && atom->GetFormalCharge() == 0 |
1894 | 0 | && !atom->GetData("Atom Class")) |
1895 | 0 | return true; |
1896 | 0 | else |
1897 | 0 | return false; |
1898 | 0 | } |
1899 | | |
1900 | | bool OBMol::DeletePolarHydrogens() |
1901 | 0 | { |
1902 | 0 | OBAtom *atom; |
1903 | 0 | vector<OBAtom*>::iterator i; |
1904 | 0 | vector<OBAtom*> delatoms; |
1905 | |
|
1906 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
1907 | 0 | "Ran OpenBabel::DeleteHydrogens -- polar", |
1908 | 0 | obAuditMsg); |
1909 | |
|
1910 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
1911 | 0 | if (atom->IsPolarHydrogen() && IsSuppressibleHydrogen(atom)) |
1912 | 0 | delatoms.push_back(atom); |
1913 | |
|
1914 | 0 | if (delatoms.empty()) |
1915 | 0 | return(true); |
1916 | | |
1917 | 0 | IncrementMod(); |
1918 | |
|
1919 | 0 | for (i = delatoms.begin();i != delatoms.end();++i) |
1920 | 0 | DeleteAtom((OBAtom *)*i); |
1921 | |
|
1922 | 0 | DecrementMod(); |
1923 | |
|
1924 | 0 | SetSSSRPerceived(false); |
1925 | 0 | SetLSSRPerceived(false); |
1926 | 0 | return(true); |
1927 | 0 | } |
1928 | | |
1929 | | |
1930 | | bool OBMol::DeleteNonPolarHydrogens() |
1931 | 0 | { |
1932 | 0 | OBAtom *atom; |
1933 | 0 | vector<OBAtom*>::iterator i; |
1934 | 0 | vector<OBAtom*> delatoms; |
1935 | |
|
1936 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
1937 | 0 | "Ran OpenBabel::DeleteHydrogens -- nonpolar", |
1938 | 0 | obAuditMsg); |
1939 | | |
1940 | |
|
1941 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
1942 | 0 | if (atom->IsNonPolarHydrogen() && IsSuppressibleHydrogen(atom)) |
1943 | 0 | delatoms.push_back(atom); |
1944 | |
|
1945 | 0 | if (delatoms.empty()) |
1946 | 0 | return(true); |
1947 | | |
1948 | | /* |
1949 | | int idx1,idx2; |
1950 | | vector<double*>::iterator j; |
1951 | | for (idx1=0,idx2=0,atom = BeginAtom(i);atom;atom = NextAtom(i),++idx1) |
1952 | | if (atom->GetAtomicNum() != OBElements::Hydrogen) |
1953 | | { |
1954 | | for (j = _vconf.begin();j != _vconf.end();++j) |
1955 | | memcpy((char*)&((*j)[idx2*3]),(char*)&((*j)[idx1*3]),sizeof(double)*3); |
1956 | | idx2++; |
1957 | | } |
1958 | | */ |
1959 | | |
1960 | 0 | IncrementMod(); |
1961 | |
|
1962 | 0 | for (i = delatoms.begin();i != delatoms.end();++i) |
1963 | 0 | DeleteAtom((OBAtom *)*i); |
1964 | |
|
1965 | 0 | DecrementMod(); |
1966 | |
|
1967 | 0 | SetSSSRPerceived(false); |
1968 | 0 | SetLSSRPerceived(false); |
1969 | 0 | return(true); |
1970 | 0 | } |
1971 | | |
1972 | | bool OBMol::DeleteHydrogens() |
1973 | 0 | { |
1974 | 0 | OBAtom *atom;//,*nbr; |
1975 | 0 | vector<OBAtom*>::iterator i; |
1976 | 0 | vector<OBAtom*> delatoms,va; |
1977 | |
|
1978 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
1979 | 0 | "Ran OpenBabel::DeleteHydrogens", obAuditMsg); |
1980 | |
|
1981 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
1982 | 0 | if (atom->GetAtomicNum() == OBElements::Hydrogen && IsSuppressibleHydrogen(atom)) |
1983 | 0 | delatoms.push_back(atom); |
1984 | |
|
1985 | 0 | SetHydrogensAdded(false); |
1986 | |
|
1987 | 0 | if (delatoms.empty()) |
1988 | 0 | return(true); |
1989 | | |
1990 | | /* decide whether these flags need to be reset |
1991 | | _flags &= (~(OB_ATOMTYPES_MOL)); |
1992 | | _flags &= (~(OB_HYBRID_MOL)); |
1993 | | _flags &= (~(OB_PCHARGE_MOL)); |
1994 | | _flags &= (~(OB_IMPVAL_MOL)); |
1995 | | */ |
1996 | | |
1997 | 0 | IncrementMod(); |
1998 | | |
1999 | | // This is slow -- we need methods to delete a set of atoms |
2000 | | // and to delete a set of bonds |
2001 | | // Calling this sequentially does result in correct behavior |
2002 | | // (e.g., fixing PR# 1704551) |
2003 | 0 | OBBondIterator bi; |
2004 | 0 | for (i = delatoms.begin(); i != delatoms.end(); ++i) { |
2005 | 0 | OBAtom* nbr = (*i)->BeginNbrAtom(bi); |
2006 | 0 | if (nbr) // defensive |
2007 | 0 | nbr->SetImplicitHCount(nbr->GetImplicitHCount() + 1); |
2008 | 0 | DeleteAtom((OBAtom *)*i); |
2009 | 0 | } |
2010 | |
|
2011 | 0 | DecrementMod(); |
2012 | |
|
2013 | 0 | SetSSSRPerceived(false); |
2014 | 0 | SetLSSRPerceived(false); |
2015 | 0 | return(true); |
2016 | 0 | } |
2017 | | |
2018 | | bool OBMol::DeleteHydrogens(OBAtom *atom) |
2019 | | //deletes all hydrogens attached to the atom passed to the function |
2020 | 0 | { |
2021 | 0 | OBAtom *nbr; |
2022 | 0 | vector<OBAtom*>::iterator i; |
2023 | 0 | vector<OBBond*>::iterator k; |
2024 | 0 | vector<OBAtom*> delatoms; |
2025 | |
|
2026 | 0 | for (nbr = atom->BeginNbrAtom(k);nbr;nbr = atom->NextNbrAtom(k)) |
2027 | 0 | if (nbr->GetAtomicNum() == OBElements::Hydrogen && IsSuppressibleHydrogen(atom)) |
2028 | 0 | delatoms.push_back(nbr); |
2029 | |
|
2030 | 0 | if (delatoms.empty()) |
2031 | 0 | return(true); |
2032 | | |
2033 | 0 | IncrementMod(); |
2034 | 0 | for (i = delatoms.begin();i != delatoms.end();++i) |
2035 | 0 | DeleteHydrogen((OBAtom*)*i); |
2036 | 0 | DecrementMod(); |
2037 | |
|
2038 | 0 | SetHydrogensAdded(false); |
2039 | 0 | SetSSSRPerceived(false); |
2040 | 0 | SetLSSRPerceived(false); |
2041 | 0 | return(true); |
2042 | 0 | } |
2043 | | |
2044 | | bool OBMol::DeleteHydrogen(OBAtom *atom) |
2045 | | //deletes the hydrogen atom passed to the function |
2046 | 0 | { |
2047 | 0 | if (atom->GetAtomicNum() != OBElements::Hydrogen) |
2048 | 0 | return false; |
2049 | | |
2050 | | // OBAngleData/OBTorsionData cache raw OBAtom* pointers; drop them now so |
2051 | | // a later FOR_ANGLES_OF_MOL doesn't read freed memory. |
2052 | 0 | DeleteData(OBGenericDataType::AngleData); |
2053 | 0 | DeleteData(OBGenericDataType::TorsionData); |
2054 | |
|
2055 | 0 | unsigned atomidx = atom->GetIdx(); |
2056 | | |
2057 | | //find bonds to delete |
2058 | 0 | OBAtom *nbr; |
2059 | 0 | vector<OBBond*> vdb; |
2060 | 0 | vector<OBBond*>::iterator j; |
2061 | 0 | for (nbr = atom->BeginNbrAtom(j);nbr;nbr = atom->NextNbrAtom(j)) |
2062 | 0 | vdb.push_back(*j); |
2063 | |
|
2064 | 0 | IncrementMod(); |
2065 | 0 | for (j = vdb.begin();j != vdb.end();++j) |
2066 | 0 | DeleteBond((OBBond*)*j); //delete bonds |
2067 | 0 | DecrementMod(); |
2068 | |
|
2069 | 0 | int idx; |
2070 | 0 | if (atomidx != NumAtoms()) |
2071 | 0 | { |
2072 | 0 | idx = atom->GetCoordinateIdx(); |
2073 | 0 | int size = NumAtoms()-atom->GetIdx(); |
2074 | 0 | vector<double*>::iterator k; |
2075 | 0 | for (k = _vconf.begin();k != _vconf.end();++k) |
2076 | 0 | memmove((char*)&(*k)[idx],(char*)&(*k)[idx+3],sizeof(double)*3*size); |
2077 | |
|
2078 | 0 | } |
2079 | | |
2080 | | // Deleting hydrogens does not invalidate the stereo objects |
2081 | | // - however, any explicit refs to the hydrogen atom must be |
2082 | | // converted to implicit refs |
2083 | 0 | OBStereo::Ref id = atom->GetId(); |
2084 | 0 | StereoRefToImplicit(*this, id); |
2085 | |
|
2086 | 0 | _atomIds[id] = nullptr; |
2087 | 0 | _vatom.erase(_vatom.begin()+(atomidx-1)); |
2088 | 0 | _natoms--; |
2089 | | |
2090 | | //reset all the indices to the atoms |
2091 | 0 | vector<OBAtom*>::iterator i; |
2092 | 0 | OBAtom *atomi; |
2093 | 0 | for (idx=1,atomi = BeginAtom(i);atomi;atomi = NextAtom(i),++idx) |
2094 | 0 | atomi->SetIdx(idx); |
2095 | |
|
2096 | 0 | SetHydrogensAdded(false); |
2097 | |
|
2098 | 0 | DestroyAtom(atom); |
2099 | |
|
2100 | 0 | SetSSSRPerceived(false); |
2101 | 0 | SetLSSRPerceived(false); |
2102 | 0 | return(true); |
2103 | 0 | } |
2104 | | |
2105 | | /* |
2106 | | this has become a wrapper for backward compatibility |
2107 | | */ |
2108 | | bool OBMol::AddHydrogens(bool polaronly, bool correctForPH, double pH) |
2109 | 0 | { |
2110 | 0 | return(AddNewHydrogens(polaronly ? PolarHydrogen : AllHydrogen, correctForPH, pH)); |
2111 | 0 | } |
2112 | | |
2113 | | static bool AtomIsNSOP(OBAtom *atom) |
2114 | 0 | { |
2115 | 0 | switch (atom->GetAtomicNum()) { |
2116 | 0 | case OBElements::Nitrogen: |
2117 | 0 | case OBElements::Sulfur: |
2118 | 0 | case OBElements::Oxygen: |
2119 | 0 | case OBElements::Phosphorus: |
2120 | 0 | return true; |
2121 | 0 | default: |
2122 | 0 | return false; |
2123 | 0 | } |
2124 | 0 | } |
2125 | | |
2126 | | //! \return a "corrected" bonding radius based on the hybridization. |
2127 | | //! Scales the covalent radius by 0.95 for sp2 and 0.90 for sp hybrids |
2128 | | static double CorrectedBondRad(unsigned int elem, unsigned int hyb) |
2129 | 0 | { |
2130 | 0 | double rad = OBElements::GetCovalentRad(elem); |
2131 | 0 | switch (hyb) { |
2132 | 0 | case 2: |
2133 | 0 | return rad * 0.95; |
2134 | 0 | case 1: |
2135 | 0 | return rad * 0.90; |
2136 | 0 | default: |
2137 | 0 | return rad; |
2138 | 0 | } |
2139 | 0 | } |
2140 | | |
2141 | | bool OBMol::AddNewHydrogens(HydrogenType whichHydrogen, bool correctForPH, double pH) |
2142 | 0 | { |
2143 | 0 | if (!IsCorrectedForPH() && correctForPH) |
2144 | 0 | CorrectForPH(pH); |
2145 | |
|
2146 | 0 | if (HasHydrogensAdded()) |
2147 | 0 | return(true); |
2148 | | |
2149 | 0 | bool hasChiralityPerceived = this->HasChiralityPerceived(); // remember |
2150 | | |
2151 | | /* |
2152 | | // |
2153 | | // This was causing bug #1892844 in avogadro. We also want to add hydrogens if the molecule has no bonds. |
2154 | | // |
2155 | | if(NumBonds()==0 && NumAtoms()!=1) |
2156 | | { |
2157 | | obErrorLog.ThrowError(__FUNCTION__, |
2158 | | "Did not run OpenBabel::AddHydrogens on molecule with no bonds", obAuditMsg); |
2159 | | return true; |
2160 | | } |
2161 | | */ |
2162 | 0 | if (whichHydrogen == AllHydrogen) |
2163 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
2164 | 0 | "Ran OpenBabel::AddHydrogens", obAuditMsg); |
2165 | 0 | else if (whichHydrogen == PolarHydrogen) |
2166 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
2167 | 0 | "Ran OpenBabel::AddHydrogens -- polar only", obAuditMsg); |
2168 | 0 | else |
2169 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
2170 | 0 | "Ran OpenBabel::AddHydrogens -- nonpolar only", obAuditMsg); |
2171 | | |
2172 | | // Make sure we have conformers (PR#1665519) |
2173 | 0 | if (!_vconf.empty() && !Empty()) { |
2174 | 0 | OBAtom *atom; |
2175 | 0 | vector<OBAtom*>::iterator i; |
2176 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
2177 | 0 | { |
2178 | 0 | atom->SetVector(); |
2179 | 0 | } |
2180 | 0 | } |
2181 | |
|
2182 | 0 | SetHydrogensAdded(); // This must come after EndModify() as EndModify() wipes the flags |
2183 | | // If chirality was already perceived, remember this (to avoid wiping information |
2184 | 0 | if (hasChiralityPerceived) |
2185 | 0 | this->SetChiralityPerceived(); |
2186 | | |
2187 | | //count up number of hydrogens to add |
2188 | 0 | OBAtom *atom,*h; |
2189 | 0 | int hcount,count=0; |
2190 | 0 | vector<pair<OBAtom*,int> > vhadd; |
2191 | 0 | vector<OBAtom*>::iterator i; |
2192 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
2193 | 0 | { |
2194 | 0 | if (whichHydrogen == PolarHydrogen && !AtomIsNSOP(atom)) |
2195 | 0 | continue; |
2196 | 0 | if (whichHydrogen == NonPolarHydrogen && AtomIsNSOP(atom)) |
2197 | 0 | continue; |
2198 | | |
2199 | 0 | hcount = atom->GetImplicitHCount(); |
2200 | 0 | atom->SetImplicitHCount(0); |
2201 | |
|
2202 | 0 | if (hcount) |
2203 | 0 | { |
2204 | 0 | vhadd.push_back(pair<OBAtom*,int>(atom,hcount)); |
2205 | 0 | count += hcount; |
2206 | 0 | } |
2207 | 0 | } |
2208 | |
|
2209 | 0 | if (count == 0) { |
2210 | | // Make sure to clear SSSR and aromatic flags we may have tripped above |
2211 | 0 | _flags &= (~(OB_SSSR_MOL|OB_AROMATIC_MOL)); |
2212 | 0 | return(true); |
2213 | 0 | } |
2214 | 0 | bool hasCoords = HasNonZeroCoords(); |
2215 | | |
2216 | | //realloc memory in coordinate arrays for new hydrogens |
2217 | 0 | double *tmpf; |
2218 | 0 | vector<double*>::iterator j; |
2219 | 0 | for (j = _vconf.begin();j != _vconf.end();++j) |
2220 | 0 | { |
2221 | 0 | tmpf = new double [(NumAtoms()+count)*3]; |
2222 | 0 | memset(tmpf,'\0',sizeof(double)*(NumAtoms()+count)*3); |
2223 | 0 | if (hasCoords) |
2224 | 0 | memcpy(tmpf,(*j),sizeof(double)*NumAtoms()*3); |
2225 | 0 | delete []*j; |
2226 | 0 | *j = tmpf; |
2227 | 0 | } |
2228 | |
|
2229 | 0 | IncrementMod(); |
2230 | |
|
2231 | 0 | int m,n; |
2232 | 0 | vector3 v; |
2233 | 0 | vector<pair<OBAtom*,int> >::iterator k; |
2234 | 0 | double hbrad = CorrectedBondRad(1, 0); |
2235 | |
|
2236 | 0 | for (k = vhadd.begin();k != vhadd.end();++k) |
2237 | 0 | { |
2238 | 0 | atom = k->first; |
2239 | 0 | double bondlen = hbrad + CorrectedBondRad(atom->GetAtomicNum(), atom->GetHyb()); |
2240 | 0 | for (m = 0;m < k->second;++m) |
2241 | 0 | { |
2242 | 0 | int badh = 0; |
2243 | 0 | for (n = 0;n < NumConformers();++n) |
2244 | 0 | { |
2245 | 0 | SetConformer(n); |
2246 | 0 | if (hasCoords) |
2247 | 0 | { |
2248 | | // Ensure that add hydrogens only returns finite coords |
2249 | | //atom->GetNewBondVector(v,bondlen); |
2250 | 0 | v = OBBuilder::GetNewBondVector(atom,bondlen); |
2251 | 0 | if (isfinite(v.x()) || isfinite(v.y()) || isfinite(v.z())) { |
2252 | 0 | _c[(NumAtoms())*3] = v.x(); |
2253 | 0 | _c[(NumAtoms())*3+1] = v.y(); |
2254 | 0 | _c[(NumAtoms())*3+2] = v.z(); |
2255 | 0 | } |
2256 | 0 | else { |
2257 | 0 | _c[(NumAtoms())*3] = 0.0; |
2258 | 0 | _c[(NumAtoms())*3+1] = 0.0; |
2259 | 0 | _c[(NumAtoms())*3+2] = 0.0; |
2260 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
2261 | 0 | "Ran OpenBabel::AddHydrogens -- no reasonable bond geometry for desired hydrogen.", |
2262 | 0 | obAuditMsg); |
2263 | 0 | badh++; |
2264 | 0 | } |
2265 | 0 | } |
2266 | 0 | else |
2267 | 0 | memset((char*)&_c[NumAtoms()*3],'\0',sizeof(double)*3); |
2268 | 0 | } |
2269 | 0 | if(badh == 0 || badh < NumConformers()) |
2270 | 0 | { |
2271 | | // Add the new H atom to the appropriate residue list |
2272 | | //but avoid doing perception by checking for existence of residue |
2273 | | //just in case perception is trigger, make sure GetResidue is called |
2274 | | //before adding the hydrogen to the molecule |
2275 | 0 | OBResidue *res = atom->HasResidue() ? atom->GetResidue() : nullptr; |
2276 | 0 | h = NewAtom(); |
2277 | 0 | h->SetType("H"); |
2278 | 0 | h->SetAtomicNum(1); |
2279 | 0 | string aname = "H"; |
2280 | |
|
2281 | 0 | if(res) |
2282 | 0 | { |
2283 | 0 | res->AddAtom(h); |
2284 | 0 | res->SetAtomID(h,aname); |
2285 | | |
2286 | | //hydrogen should inherit hetatm status of heteroatom (default is false) |
2287 | 0 | if(res->IsHetAtom(atom)) |
2288 | 0 | { |
2289 | 0 | res->SetHetAtom(h, true); |
2290 | 0 | } |
2291 | 0 | } |
2292 | |
|
2293 | 0 | int bondFlags = 0; |
2294 | 0 | AddBond(atom->GetIdx(),h->GetIdx(),1, bondFlags); |
2295 | 0 | if (_c) { |
2296 | 0 | h->SetCoordPtr(&_c); |
2297 | 0 | } |
2298 | 0 | OpenBabel::ImplicitRefToStereo(*this, atom->GetId(), h->GetId()); |
2299 | 0 | } |
2300 | 0 | } |
2301 | 0 | } |
2302 | |
|
2303 | 0 | DecrementMod(); |
2304 | | |
2305 | | //reset atom type and partial charge flags |
2306 | 0 | _flags &= (~(OB_PCHARGE_MOL|OB_ATOMTYPES_MOL|OB_SSSR_MOL|OB_AROMATIC_MOL|OB_HYBRID_MOL)); |
2307 | |
|
2308 | 0 | return(true); |
2309 | 0 | } |
2310 | | |
2311 | | bool OBMol::AddPolarHydrogens() |
2312 | 0 | { |
2313 | 0 | return(AddNewHydrogens(PolarHydrogen)); |
2314 | 0 | } |
2315 | | |
2316 | | bool OBMol::AddNonPolarHydrogens() |
2317 | 0 | { |
2318 | 0 | return(AddNewHydrogens(NonPolarHydrogen)); |
2319 | 0 | } |
2320 | | |
2321 | | bool OBMol::AddHydrogens(OBAtom *atom) |
2322 | 0 | { |
2323 | 0 | int hcount = atom->GetImplicitHCount(); |
2324 | 0 | if (hcount == 0) |
2325 | 0 | return true; |
2326 | | |
2327 | 0 | atom->SetImplicitHCount(0); |
2328 | |
|
2329 | 0 | vector<pair<OBAtom*, int> > vhadd; |
2330 | 0 | vhadd.push_back(pair<OBAtom*,int>(atom, hcount)); |
2331 | | |
2332 | | //realloc memory in coordinate arrays for new hydroges |
2333 | 0 | double *tmpf; |
2334 | 0 | vector<double*>::iterator j; |
2335 | 0 | for (j = _vconf.begin();j != _vconf.end();++j) |
2336 | 0 | { |
2337 | 0 | tmpf = new double [(NumAtoms()+hcount)*3+10]; |
2338 | 0 | memcpy(tmpf,(*j),sizeof(double)*NumAtoms()*3); |
2339 | 0 | delete []*j; |
2340 | 0 | *j = tmpf; |
2341 | 0 | } |
2342 | |
|
2343 | 0 | IncrementMod(); |
2344 | |
|
2345 | 0 | int m,n; |
2346 | 0 | vector3 v; |
2347 | 0 | vector<pair<OBAtom*,int> >::iterator k; |
2348 | 0 | double hbrad = CorrectedBondRad(1,0); |
2349 | |
|
2350 | 0 | OBAtom *h; |
2351 | 0 | for (k = vhadd.begin();k != vhadd.end();++k) |
2352 | 0 | { |
2353 | 0 | atom = k->first; |
2354 | 0 | double bondlen = hbrad + CorrectedBondRad(atom->GetAtomicNum(),atom->GetHyb()); |
2355 | 0 | for (m = 0;m < k->second;++m) |
2356 | 0 | { |
2357 | 0 | for (n = 0;n < NumConformers();++n) |
2358 | 0 | { |
2359 | 0 | SetConformer(n); |
2360 | | //atom->GetNewBondVector(v,bondlen); |
2361 | 0 | v = OBBuilder::GetNewBondVector(atom,bondlen); |
2362 | 0 | _c[(NumAtoms())*3] = v.x(); |
2363 | 0 | _c[(NumAtoms())*3+1] = v.y(); |
2364 | 0 | _c[(NumAtoms())*3+2] = v.z(); |
2365 | 0 | } |
2366 | 0 | h = NewAtom(); |
2367 | 0 | h->SetType("H"); |
2368 | 0 | h->SetAtomicNum(1); |
2369 | |
|
2370 | 0 | int bondFlags = 0; |
2371 | 0 | AddBond(atom->GetIdx(),h->GetIdx(),1, bondFlags); |
2372 | 0 | h->SetCoordPtr(&_c); |
2373 | 0 | OpenBabel::ImplicitRefToStereo(*this, atom->GetId(), h->GetId()); |
2374 | 0 | } |
2375 | 0 | } |
2376 | |
|
2377 | 0 | DecrementMod(); |
2378 | 0 | SetConformer(0); |
2379 | | |
2380 | | //reset atom type and partial charge flags |
2381 | | //_flags &= (~(OB_PCHARGE_MOL|OB_ATOMTYPES_MOL)); |
2382 | |
|
2383 | 0 | return(true); |
2384 | 0 | } |
2385 | | |
2386 | | bool OBMol::CorrectForPH(double pH) |
2387 | 0 | { |
2388 | 0 | if (IsCorrectedForPH()) |
2389 | 0 | return(true); |
2390 | 0 | phmodel.CorrectForPH(*this, pH); |
2391 | |
|
2392 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
2393 | 0 | "Ran OpenBabel::CorrectForPH", obAuditMsg); |
2394 | |
|
2395 | 0 | return(true); |
2396 | 0 | } |
2397 | | |
2398 | | //! \brief set spin multiplicity for H-deficient atoms |
2399 | | /** |
2400 | | If NoImplicitH is true then the molecule has no implicit hydrogens. Individual atoms |
2401 | | on which ForceNoH() has been called also have no implicit hydrogens. |
2402 | | If NoImplicitH is false (the default), then if there are any explicit hydrogens |
2403 | | on an atom then they constitute all the hydrogen on that atom. However, a hydrogen |
2404 | | atom with its _isotope!=0 is not considered explicit hydrogen for this purpose. |
2405 | | In addition, an atom which has had ForceImplH()called for it is never considered |
2406 | | hydrogen deficient, e.g. unbracketed atoms in SMILES. |
2407 | | Any discrepancy with the expected atom valency is interpreted as the atom being a |
2408 | | radical of some sort and iits _spinMultiplicity is set to 2 when it is one hydrogen short |
2409 | | and 3 when it is two hydrogens short and similarly for greater hydrogen deficiency. |
2410 | | |
2411 | | So SMILES C[CH] is interpreted as methyl carbene, CC[H][H] as ethane, and CC[2H] as CH3CH2D. |
2412 | | **/ |
2413 | | |
2414 | | |
2415 | | |
2416 | | bool OBMol::AssignSpinMultiplicity(bool /*NoImplicitH*/) |
2417 | 0 | { |
2418 | | // TODO: The following functions simply returns true, as it has been made |
2419 | | // redundant by changes to the handling of implicit hydrogens, and spin. |
2420 | | // This needs to be sorted out properly at some point. |
2421 | 0 | return true; |
2422 | 0 | } |
2423 | | |
2424 | | // Used by DeleteAtom below. Code based on StereoRefToImplicit |
2425 | | static void DeleteStereoOnAtom(OBMol& mol, OBStereo::Ref atomId) |
2426 | 0 | { |
2427 | 0 | std::vector<OBGenericData*> vdata = mol.GetAllData(OBGenericDataType::StereoData); |
2428 | 0 | for (std::vector<OBGenericData*>::iterator data = vdata.begin(); data != vdata.end(); ++data) { |
2429 | 0 | OBStereo::Type datatype = ((OBStereoBase*)*data)->GetType(); |
2430 | |
|
2431 | 0 | if (datatype != OBStereo::CisTrans && datatype != OBStereo::Tetrahedral) { |
2432 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
2433 | 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); |
2434 | 0 | continue; |
2435 | 0 | } |
2436 | | |
2437 | 0 | if (datatype == OBStereo::CisTrans) { |
2438 | 0 | OBCisTransStereo *ct = dynamic_cast<OBCisTransStereo*>(*data); |
2439 | 0 | OBCisTransStereo::Config ct_cfg = ct->GetConfig(); |
2440 | 0 | if (ct_cfg.begin == atomId || ct_cfg.end == atomId || |
2441 | 0 | std::find(ct_cfg.refs.begin(), ct_cfg.refs.end(), atomId) != ct_cfg.refs.end()) |
2442 | 0 | mol.DeleteData(ct); |
2443 | 0 | } |
2444 | 0 | else if (datatype == OBStereo::Tetrahedral) { |
2445 | 0 | OBTetrahedralStereo *ts = dynamic_cast<OBTetrahedralStereo*>(*data); |
2446 | 0 | OBTetrahedralStereo::Config ts_cfg = ts->GetConfig(); |
2447 | 0 | if (ts_cfg.from == atomId || |
2448 | 0 | std::find(ts_cfg.refs.begin(), ts_cfg.refs.end(), atomId) != ts_cfg.refs.end()) |
2449 | 0 | mol.DeleteData(ts); |
2450 | 0 | } |
2451 | 0 | } |
2452 | 0 | } |
2453 | | |
2454 | | bool OBMol::DeleteAtom(OBAtom *atom, bool destroyAtom) |
2455 | 0 | { |
2456 | 0 | if (atom->GetAtomicNum() == OBElements::Hydrogen) |
2457 | 0 | return(DeleteHydrogen(atom)); |
2458 | | |
2459 | | // OBAngleData/OBTorsionData cache raw OBAtom* pointers; drop them now so |
2460 | | // a later FOR_ANGLES_OF_MOL doesn't read freed memory. |
2461 | 0 | DeleteData(OBGenericDataType::AngleData); |
2462 | 0 | DeleteData(OBGenericDataType::TorsionData); |
2463 | |
|
2464 | 0 | BeginModify(); |
2465 | | //don't need to do anything with coordinates b/c |
2466 | | //BeginModify() blows away coordinates |
2467 | | |
2468 | | //find bonds to delete |
2469 | 0 | OBAtom *nbr; |
2470 | 0 | vector<OBBond*> vdb; |
2471 | 0 | vector<OBBond*>::iterator j; |
2472 | 0 | for (nbr = atom->BeginNbrAtom(j);nbr;nbr = atom->NextNbrAtom(j)) |
2473 | 0 | vdb.push_back(*j); |
2474 | |
|
2475 | 0 | for (j = vdb.begin();j != vdb.end();++j) |
2476 | 0 | DeleteBond((OBBond *)*j); //delete bonds |
2477 | |
|
2478 | 0 | _atomIds[atom->GetId()] = nullptr; |
2479 | 0 | _vatom.erase(_vatom.begin()+(atom->GetIdx()-1)); |
2480 | 0 | _natoms--; |
2481 | | |
2482 | | //reset all the indices to the atoms |
2483 | 0 | int idx; |
2484 | 0 | vector<OBAtom*>::iterator i; |
2485 | 0 | OBAtom *atomi; |
2486 | 0 | for (idx=1,atomi = BeginAtom(i);atomi;atomi = NextAtom(i),++idx) |
2487 | 0 | atomi->SetIdx(idx); |
2488 | |
|
2489 | 0 | EndModify(); |
2490 | | |
2491 | | // Delete any stereo objects involving this atom |
2492 | 0 | OBStereo::Ref id = atom->GetId(); |
2493 | 0 | DeleteStereoOnAtom(*this, id); |
2494 | |
|
2495 | 0 | if (destroyAtom) |
2496 | 0 | DestroyAtom(atom); |
2497 | |
|
2498 | 0 | SetSSSRPerceived(false); |
2499 | 0 | SetLSSRPerceived(false); |
2500 | 0 | return(true); |
2501 | 0 | } |
2502 | | |
2503 | | bool OBMol::DeleteResidue(OBResidue *residue, bool destroyResidue) |
2504 | 0 | { |
2505 | 0 | unsigned short idx = residue->GetIdx(); |
2506 | 0 | _residue.erase(_residue.begin() + idx); |
2507 | |
|
2508 | 0 | for ( unsigned short i = idx ; i < _residue.size() ; i++ ) |
2509 | 0 | _residue[i]->SetIdx(i); |
2510 | |
|
2511 | 0 | if (destroyResidue) |
2512 | 0 | DestroyResidue(residue); |
2513 | |
|
2514 | 0 | SetSSSRPerceived(false); |
2515 | 0 | SetLSSRPerceived(false); |
2516 | 0 | return(true); |
2517 | 0 | } |
2518 | | |
2519 | | bool OBMol::DeleteBond(OBBond *bond, bool destroyBond) |
2520 | 0 | { |
2521 | | // Cached angles/torsions are derived from bond connectivity. They aren't |
2522 | | // a UAF risk here, but they no longer match the topology — invalidate. |
2523 | 0 | DeleteData(OBGenericDataType::AngleData); |
2524 | 0 | DeleteData(OBGenericDataType::TorsionData); |
2525 | |
|
2526 | 0 | BeginModify(); |
2527 | |
|
2528 | 0 | (bond->GetBeginAtom())->DeleteBond(bond); |
2529 | 0 | (bond->GetEndAtom())->DeleteBond(bond); |
2530 | 0 | _bondIds[bond->GetId()] = nullptr; |
2531 | 0 | _vbond.erase(_vbond.begin() + bond->GetIdx()); // bond index starts at 0!!! |
2532 | 0 | _nbonds--; |
2533 | |
|
2534 | 0 | vector<OBBond*>::iterator i; |
2535 | 0 | int j; |
2536 | 0 | OBBond *bondi; |
2537 | 0 | for (bondi = BeginBond(i),j=0;bondi;bondi = NextBond(i),++j) |
2538 | 0 | bondi->SetIdx(j); |
2539 | |
|
2540 | 0 | EndModify(); |
2541 | |
|
2542 | 0 | if (destroyBond) |
2543 | 0 | DestroyBond(bond); |
2544 | |
|
2545 | 0 | SetSSSRPerceived(false); |
2546 | 0 | SetLSSRPerceived(false); |
2547 | 0 | return(true); |
2548 | 0 | } |
2549 | | |
2550 | | bool OBMol::AddBond(int first,int second,int order,int flags,int insertpos) |
2551 | 17.7k | { |
2552 | | // Don't add the bond if it already exists |
2553 | 17.7k | if (first == second || GetBond(first, second) != nullptr) |
2554 | 1.12k | return(false); |
2555 | | |
2556 | | // BeginModify(); |
2557 | | |
2558 | 16.6k | if ((unsigned)first <= NumAtoms() && (unsigned)second <= NumAtoms()) |
2559 | | //atoms exist and bond doesn't |
2560 | 16.6k | { |
2561 | 16.6k | OBBond *bond = new OBBond; |
2562 | 16.6k | if (!bond) |
2563 | 0 | { |
2564 | | //EndModify(); |
2565 | 0 | return(false); |
2566 | 0 | } |
2567 | | |
2568 | 16.6k | OBAtom *bgn,*end; |
2569 | 16.6k | bgn = GetAtom(first); |
2570 | 16.6k | end = GetAtom(second); |
2571 | 16.6k | if (!bgn || !end) |
2572 | 4 | { |
2573 | 4 | obErrorLog.ThrowError(__FUNCTION__, "Unable to add bond - invalid atom index", obDebug); |
2574 | 4 | delete bond; |
2575 | 4 | return(false); |
2576 | 4 | } |
2577 | 16.6k | bond->Set(_nbonds,bgn,end,order,flags); |
2578 | 16.6k | bond->SetParent(this); |
2579 | | |
2580 | 16.6k | bond->SetId(_bondIds.size()); |
2581 | 16.6k | _bondIds.push_back(bond); |
2582 | | |
2583 | 16.6k | #define OBBondIncrement 100 |
2584 | 16.6k | if (_nbonds+1 >= _vbond.size()) |
2585 | 4.32k | { |
2586 | 4.32k | _vbond.resize(_nbonds+OBBondIncrement); |
2587 | 4.32k | vector<OBBond*>::iterator i; |
2588 | 432k | for (i = _vbond.begin(),i+=(_nbonds+1);i != _vbond.end();++i) |
2589 | 427k | *i = nullptr; |
2590 | 4.32k | } |
2591 | 16.6k | #undef OBBondIncrement |
2592 | | |
2593 | 16.6k | _vbond[_nbonds] = (OBBond*)bond; |
2594 | 16.6k | _nbonds++; |
2595 | | |
2596 | 16.6k | if (insertpos == -1) |
2597 | 16.6k | { |
2598 | 16.6k | bgn->AddBond(bond); |
2599 | 16.6k | end->AddBond(bond); |
2600 | 16.6k | } |
2601 | 0 | else |
2602 | 0 | { |
2603 | 0 | if (insertpos >= static_cast<int>(bgn->GetExplicitDegree())) |
2604 | 0 | bgn->AddBond(bond); |
2605 | 0 | else //need to insert the bond for the connectivity order to be preserved |
2606 | 0 | { //otherwise stereochemistry gets screwed up |
2607 | 0 | vector<OBBond*>::iterator bi; |
2608 | 0 | bgn->BeginNbrAtom(bi); |
2609 | 0 | bi += insertpos; |
2610 | 0 | bgn->InsertBond(bi,bond); |
2611 | 0 | } |
2612 | 0 | end->AddBond(bond); |
2613 | 0 | } |
2614 | 16.6k | } |
2615 | 0 | else //at least one atom doesn't exist yet - add to bond_q |
2616 | 0 | SetData(new OBVirtualBond(first,second,order,flags)); |
2617 | | |
2618 | | // EndModify(); |
2619 | | |
2620 | 16.6k | return(true); |
2621 | 16.6k | } |
2622 | | |
2623 | | bool OBMol::AddBond(OBBond &bond) |
2624 | 0 | { |
2625 | 0 | if(!AddBond(bond.GetBeginAtomIdx(), |
2626 | 0 | bond.GetEndAtomIdx(), |
2627 | 0 | bond.GetBondOrder(), |
2628 | 0 | bond.GetFlags())) |
2629 | 0 | return false; |
2630 | | //copy the bond's generic data |
2631 | 0 | OBDataIterator diter; |
2632 | 0 | for(diter=bond.BeginData(); diter!=bond.EndData();++diter) |
2633 | 0 | GetBond(NumBonds()-1)->CloneData(*diter); |
2634 | 0 | return true; |
2635 | 0 | } |
2636 | | |
2637 | | void OBMol::Align(OBAtom *a1,OBAtom *a2,vector3 &p1,vector3 &p2) |
2638 | 0 | { |
2639 | 0 | vector<int> children; |
2640 | |
|
2641 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
2642 | 0 | "Ran OpenBabel::Align", obAuditMsg); |
2643 | | |
2644 | | //find which atoms to rotate |
2645 | 0 | FindChildren(children,a1->GetIdx(),a2->GetIdx()); |
2646 | 0 | children.push_back(a2->GetIdx()); |
2647 | | |
2648 | | //find the rotation vector and angle |
2649 | 0 | vector3 v1,v2,v3; |
2650 | 0 | v1 = p2 - p1; |
2651 | 0 | v2 = a2->GetVector() - a1->GetVector(); |
2652 | 0 | v3 = cross(v1,v2); |
2653 | 0 | double angle = vectorAngle(v1,v2); |
2654 | | |
2655 | | //find the rotation matrix |
2656 | 0 | matrix3x3 m; |
2657 | 0 | m.RotAboutAxisByAngle(v3,angle); |
2658 | | |
2659 | | //rotate atoms |
2660 | 0 | vector3 v; |
2661 | 0 | OBAtom *atom; |
2662 | 0 | vector<int>::iterator i; |
2663 | 0 | for (i = children.begin();i != children.end();++i) |
2664 | 0 | { |
2665 | 0 | atom = GetAtom(*i); |
2666 | 0 | v = atom->GetVector(); |
2667 | 0 | v -= a1->GetVector(); |
2668 | 0 | v *= m; //rotate the point |
2669 | 0 | v += p1; //translate the vector |
2670 | 0 | atom->SetVector(v); |
2671 | 0 | } |
2672 | | //set a1 = p1 |
2673 | 0 | a1->SetVector(p1); |
2674 | 0 | } |
2675 | | |
2676 | | void OBMol::ToInertialFrame() |
2677 | 0 | { |
2678 | 0 | double m[9]; |
2679 | 0 | for (int i = 0;i < NumConformers();++i) |
2680 | 0 | ToInertialFrame(i,m); |
2681 | 0 | } |
2682 | | |
2683 | | void OBMol::ToInertialFrame(int conf,double *rmat) |
2684 | 0 | { |
2685 | 0 | unsigned int i; |
2686 | 0 | double x,y,z; |
2687 | 0 | double mi; |
2688 | 0 | double mass = 0.0; |
2689 | 0 | double center[3],m[3][3]; |
2690 | |
|
2691 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
2692 | 0 | "Ran OpenBabel::ToInertialFrame", obAuditMsg); |
2693 | |
|
2694 | 0 | for (i = 0;i < 3;++i) |
2695 | 0 | memset(&m[i],'\0',sizeof(double)*3); |
2696 | 0 | memset(center,'\0',sizeof(double)*3); |
2697 | |
|
2698 | 0 | SetConformer(conf); |
2699 | 0 | OBAtom *atom; |
2700 | 0 | vector<OBAtom*>::iterator j; |
2701 | | //find center of mass |
2702 | 0 | for (atom = BeginAtom(j);atom;atom = NextAtom(j)) |
2703 | 0 | { |
2704 | 0 | mi = atom->GetAtomicMass(); |
2705 | 0 | center[0] += mi*atom->x(); |
2706 | 0 | center[1] += mi*atom->y(); |
2707 | 0 | center[2] += mi*atom->z(); |
2708 | 0 | mass += mi; |
2709 | 0 | } |
2710 | |
|
2711 | 0 | center[0] /= mass; |
2712 | 0 | center[1] /= mass; |
2713 | 0 | center[2] /= mass; |
2714 | | |
2715 | | //calculate inertial tensor |
2716 | 0 | for (atom = BeginAtom(j);atom;atom = NextAtom(j)) |
2717 | 0 | { |
2718 | 0 | x = atom->x()-center[0]; |
2719 | 0 | y = atom->y()-center[1]; |
2720 | 0 | z = atom->z()-center[2]; |
2721 | 0 | mi = atom->GetAtomicMass(); |
2722 | |
|
2723 | 0 | m[0][0] += mi*(y*y+z*z); |
2724 | 0 | m[0][1] -= mi*x*y; |
2725 | 0 | m[0][2] -= mi*x*z; |
2726 | | // m[1][0] -= mi*x*y; |
2727 | 0 | m[1][1] += mi*(x*x+z*z); |
2728 | 0 | m[1][2] -= mi*y*z; |
2729 | | // m[2][0] -= mi*x*z; |
2730 | | // m[2][1] -= mi*y*z; |
2731 | 0 | m[2][2] += mi*(x*x+y*y); |
2732 | 0 | } |
2733 | | // Fill in the lower triangle using symmetry across the diagonal |
2734 | 0 | m[1][0] = m[0][1]; |
2735 | 0 | m[2][0] = m[0][2]; |
2736 | 0 | m[2][1] = m[1][2]; |
2737 | | |
2738 | | /* find rotation matrix for moment of inertia */ |
2739 | 0 | ob_make_rmat(m,rmat); |
2740 | | |
2741 | | /* rotate all coordinates */ |
2742 | 0 | double *c = GetConformer(conf); |
2743 | 0 | for(i=0; i < NumAtoms();++i) |
2744 | 0 | { |
2745 | 0 | x = c[i*3]-center[0]; |
2746 | 0 | y = c[i*3+1]-center[1]; |
2747 | 0 | z = c[i*3+2]-center[2]; |
2748 | 0 | c[i*3] = x*rmat[0] + y*rmat[1] + z*rmat[2]; |
2749 | 0 | c[i*3+1] = x*rmat[3] + y*rmat[4] + z*rmat[5]; |
2750 | 0 | c[i*3+2] = x*rmat[6] + y*rmat[7] + z*rmat[8]; |
2751 | 0 | } |
2752 | 0 | } |
2753 | | |
2754 | | OBMol::OBMol() |
2755 | 17.5k | { |
2756 | 17.5k | _natoms = _nbonds = 0; |
2757 | 17.5k | _mod = 0; |
2758 | 17.5k | _totalCharge = 0; |
2759 | 17.5k | _dimension = 3; |
2760 | 17.5k | _vatom.clear(); |
2761 | 17.5k | _atomIds.clear(); |
2762 | 17.5k | _vbond.clear(); |
2763 | 17.5k | _bondIds.clear(); |
2764 | 17.5k | _vdata.clear(); |
2765 | 17.5k | _title = ""; |
2766 | 17.5k | _c = nullptr; |
2767 | 17.5k | _flags = 0; |
2768 | 17.5k | _vconf.clear(); |
2769 | 17.5k | _autoPartialCharge = true; |
2770 | 17.5k | _autoFormalCharge = true; |
2771 | 17.5k | _energy = 0.0; |
2772 | 17.5k | } |
2773 | | |
2774 | 0 | OBMol::OBMol(const OBMol &mol) : OBBase(mol) |
2775 | 0 | { |
2776 | 0 | _natoms = _nbonds = 0; |
2777 | 0 | _mod = 0; |
2778 | 0 | _totalCharge = 0; |
2779 | 0 | _dimension = 3; |
2780 | 0 | _vatom.clear(); |
2781 | 0 | _atomIds.clear(); |
2782 | 0 | _vbond.clear(); |
2783 | 0 | _bondIds.clear(); |
2784 | 0 | _vdata.clear(); |
2785 | 0 | _title = ""; |
2786 | 0 | _c = nullptr; |
2787 | 0 | _flags = 0; |
2788 | 0 | _vconf.clear(); |
2789 | 0 | _autoPartialCharge = true; |
2790 | 0 | _autoFormalCharge = true; |
2791 | | //NF _compressed = false; |
2792 | 0 | _energy = 0.0; |
2793 | 0 | *this = mol; |
2794 | 0 | } |
2795 | | |
2796 | | OBMol::~OBMol() |
2797 | 17.5k | { |
2798 | 17.5k | OBAtom *atom; |
2799 | 17.5k | OBBond *bond; |
2800 | 17.5k | OBResidue *residue; |
2801 | 17.5k | vector<OBAtom*>::iterator i; |
2802 | 17.5k | vector<OBBond*>::iterator j; |
2803 | 17.5k | vector<OBResidue*>::iterator r; |
2804 | | // Destroy residues before atoms so ~OBResidue() can clear back- |
2805 | | // pointers on still-live atoms (see Clear() for the same reason). |
2806 | 17.5k | for (residue = BeginResidue(r);residue;residue = NextResidue(r)) |
2807 | 0 | DestroyResidue(residue); |
2808 | 1.68M | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
2809 | 1.66M | DestroyAtom(atom); |
2810 | 34.1k | for (bond = BeginBond(j);bond;bond = NextBond(j)) |
2811 | 16.6k | DestroyBond(bond); |
2812 | | |
2813 | | //clear out the multiconformer data |
2814 | 17.5k | vector<double*>::iterator k; |
2815 | 30.0k | for (k = _vconf.begin();k != _vconf.end();++k) |
2816 | 12.4k | delete [] *k; |
2817 | 17.5k | _vconf.clear(); |
2818 | 17.5k | } |
2819 | | |
2820 | | bool OBMol::HasNonZeroCoords() |
2821 | 0 | { |
2822 | 0 | OBAtom *atom; |
2823 | 0 | vector<OBAtom*>::iterator i; |
2824 | |
|
2825 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
2826 | 0 | if (atom->GetVector().length_2() != 0.0) |
2827 | 0 | return(true); |
2828 | | |
2829 | 0 | return(false); |
2830 | 0 | } |
2831 | | |
2832 | | bool OBMol::Has2D(bool Not3D) |
2833 | 14.2k | { |
2834 | 14.2k | bool hasX,hasY; |
2835 | 14.2k | OBAtom *atom; |
2836 | 14.2k | vector<OBAtom*>::iterator i; |
2837 | | |
2838 | 14.2k | hasX = hasY = false; |
2839 | 1.60M | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
2840 | 1.59M | { |
2841 | 1.59M | if (!hasX && fabs(atom->x()) >= 2e-6) |
2842 | 1.86k | hasX = true; |
2843 | 1.59M | if (!hasY && fabs(atom->y()) >= 2e-6) |
2844 | 1.59k | hasY = true; |
2845 | 1.59M | if(Not3D && atom->z()) |
2846 | 0 | return false; |
2847 | 1.59M | } |
2848 | 14.2k | if (hasX || hasY) //was && but this excluded vertically or horizontally aligned linear mols |
2849 | 3.06k | return(true); |
2850 | 11.2k | return(false); |
2851 | 14.2k | } |
2852 | | |
2853 | | bool OBMol::Has3D() |
2854 | 14.8k | { |
2855 | 14.8k | bool hasX,hasY,hasZ; |
2856 | 14.8k | OBAtom *atom; |
2857 | 14.8k | vector<OBAtom*>::iterator i; |
2858 | | |
2859 | 14.8k | hasX = hasY = hasZ = false; |
2860 | | // if (this->_c == NULL) **Test removed** Prevented function use during molecule construction |
2861 | | // return(false); |
2862 | 1.61M | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
2863 | 1.60M | { |
2864 | 1.60M | if (!hasX && fabs(atom->x()) >= 2e-6) |
2865 | 2.46k | hasX = true; |
2866 | 1.60M | if (!hasY && fabs(atom->y()) >= 2e-6) |
2867 | 2.20k | hasY = true; |
2868 | 1.60M | if (!hasZ && fabs(atom->z()) >= 2e-6) |
2869 | 2.17k | hasZ = true; |
2870 | | |
2871 | 1.60M | if (hasX && hasY && hasZ) |
2872 | 605 | return(true); |
2873 | 1.60M | } |
2874 | 14.2k | return(false); |
2875 | 14.8k | } |
2876 | | |
2877 | | void OBMol::SetCoordinates(double *newCoords) |
2878 | 0 | { |
2879 | 0 | bool noCptr = (_c == nullptr); // did we previously have a coordinate ptr |
2880 | 0 | if (noCptr) { |
2881 | 0 | _c = new double [NumAtoms()*3]; |
2882 | 0 | } |
2883 | | |
2884 | | // copy from external to internal |
2885 | 0 | memcpy((char*)_c, (char*)newCoords, sizeof(double)*3*NumAtoms()); |
2886 | |
|
2887 | 0 | if (noCptr) { |
2888 | 0 | OBAtom *atom; |
2889 | 0 | vector<OBAtom*>::iterator i; |
2890 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
2891 | 0 | atom->SetCoordPtr(&_c); |
2892 | 0 | _vconf.push_back(newCoords); |
2893 | 0 | } |
2894 | 0 | } |
2895 | | |
2896 | | //! Renumber the atoms according to the order of indexes in the supplied vector |
2897 | | //! This with assemble an atom vector and call RenumberAtoms(vector<OBAtom*>) |
2898 | | //! It will return without action if the supplied vector is empty or does not |
2899 | | //! have the same number of atoms as the molecule. |
2900 | | //! |
2901 | | //! \since version 2.3 |
2902 | | void OBMol::RenumberAtoms(vector<int> v) |
2903 | 0 | { |
2904 | 0 | if (Empty() || v.size() != NumAtoms()) |
2905 | 0 | return; |
2906 | | |
2907 | 0 | vector <OBAtom*> va; |
2908 | 0 | va.reserve(NumAtoms()); |
2909 | |
|
2910 | 0 | vector<int>::iterator i; |
2911 | 0 | for (i = v.begin(); i != v.end(); ++i) |
2912 | 0 | va.push_back( GetAtom(*i) ); |
2913 | |
|
2914 | 0 | this->RenumberAtoms(va); |
2915 | 0 | } |
2916 | | |
2917 | | //! Renumber the atoms in this molecule according to the order in the supplied |
2918 | | //! vector. This will return without action if the supplied vector is empty or |
2919 | | //! does not have the same number of atoms as the molecule. |
2920 | | void OBMol::RenumberAtoms(vector<OBAtom*> &v) |
2921 | 0 | { |
2922 | 0 | if (Empty()) |
2923 | 0 | return; |
2924 | | |
2925 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
2926 | 0 | "Ran OpenBabel::RenumberAtoms", obAuditMsg); |
2927 | |
|
2928 | 0 | OBAtom *atom; |
2929 | 0 | vector<OBAtom*> va; |
2930 | 0 | vector<OBAtom*>::iterator i; |
2931 | |
|
2932 | 0 | va = v; |
2933 | | |
2934 | | //make sure all atoms are represented in the vector |
2935 | 0 | if (va.empty() || va.size() != NumAtoms()) |
2936 | 0 | return; |
2937 | | |
2938 | 0 | OBBitVec bv; |
2939 | 0 | for (i = va.begin();i != va.end();++i) |
2940 | 0 | bv |= (*i)->GetIdx(); |
2941 | |
|
2942 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
2943 | 0 | if (!bv[atom->GetIdx()]) |
2944 | 0 | va.push_back(atom); |
2945 | |
|
2946 | 0 | int j,k; |
2947 | 0 | double *c; |
2948 | 0 | double *ctmp = new double [NumAtoms()*3]; |
2949 | |
|
2950 | 0 | for (j = 0;j < NumConformers();++j) |
2951 | 0 | { |
2952 | 0 | c = GetConformer(j); |
2953 | 0 | for (k=0,i = va.begin();i != va.end(); ++i,++k) |
2954 | 0 | memcpy((char*)&ctmp[k*3],(char*)&c[((OBAtom*)*i)->GetCoordinateIdx()],sizeof(double)*3); |
2955 | 0 | memcpy((char*)c,(char*)ctmp,sizeof(double)*3*NumAtoms()); |
2956 | 0 | } |
2957 | |
|
2958 | 0 | for (k=1,i = va.begin();i != va.end(); ++i,++k) |
2959 | 0 | (*i)->SetIdx(k); |
2960 | |
|
2961 | 0 | delete [] ctmp; |
2962 | |
|
2963 | 0 | _vatom.clear(); |
2964 | 0 | for (i = va.begin();i != va.end();++i) |
2965 | 0 | _vatom.push_back(*i); |
2966 | |
|
2967 | 0 | DeleteData(OBGenericDataType::RingData); |
2968 | 0 | DeleteData("OpenBabel Symmetry Classes"); |
2969 | 0 | DeleteData("LSSR"); |
2970 | 0 | DeleteData("SSSR"); |
2971 | 0 | UnsetFlag(OB_LSSR_MOL); |
2972 | 0 | UnsetFlag(OB_SSSR_MOL); |
2973 | 0 | } |
2974 | | |
2975 | | bool WriteTitles(ostream &ofs, OBMol &mol) |
2976 | 0 | { |
2977 | 0 | ofs << mol.GetTitle() << endl; |
2978 | 0 | return true; |
2979 | 0 | } |
2980 | | |
2981 | | //check that unreasonable bonds aren't being added |
2982 | | static bool validAdditionalBond(OBAtom *a, OBAtom *n) |
2983 | 0 | { |
2984 | 0 | if(a->GetExplicitValence() == 5 && a->GetAtomicNum() == 15) |
2985 | 0 | { |
2986 | | //only allow octhedral bonding for F and Cl |
2987 | 0 | if(n->GetAtomicNum() == 9 || n->GetAtomicNum() == 17) |
2988 | 0 | return true; |
2989 | 0 | else |
2990 | 0 | return false; |
2991 | 0 | } |
2992 | | //other things to check? |
2993 | 0 | return true; |
2994 | 0 | } |
2995 | | |
2996 | | /*! This method adds single bonds between all atoms |
2997 | | closer than their combined atomic covalent radii, |
2998 | | then "cleans up" making sure bonded atoms are not |
2999 | | closer than 0.4A and the atom does not exceed its valence. |
3000 | | It implements blue-obelisk:rebondFrom3DCoordinates. |
3001 | | |
3002 | | */ |
3003 | | void OBMol::ConnectTheDots(void) |
3004 | 0 | { |
3005 | 0 | if (Empty()) |
3006 | 0 | return; |
3007 | 0 | if (_dimension != 3) return; // not useful on non-3D structures |
3008 | | |
3009 | 0 | if (IsPeriodic()) |
3010 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
3011 | 0 | "Ran OpenBabel::ConnectTheDots -- using periodic boundary conditions", |
3012 | 0 | obAuditMsg); |
3013 | 0 | else |
3014 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
3015 | 0 | "Ran OpenBabel::ConnectTheDots", obAuditMsg); |
3016 | | |
3017 | |
|
3018 | 0 | int j,k,max; |
3019 | 0 | double maxrad = 0; |
3020 | 0 | bool unset = false; |
3021 | 0 | OBAtom *atom,*nbr; |
3022 | 0 | vector<OBAtom*>::iterator i; |
3023 | 0 | vector<pair<OBAtom*,double> > zsortedAtoms; |
3024 | 0 | vector<double> rad; |
3025 | 0 | vector<int> zsorted; |
3026 | 0 | vector<int> bondCount; // existing bonds (e.g., from residues in PDB) |
3027 | |
|
3028 | 0 | double *c = new double [NumAtoms()*3]; |
3029 | 0 | rad.resize(_natoms); |
3030 | |
|
3031 | 0 | for (j = 0, atom = BeginAtom(i) ; atom ; atom = NextAtom(i), ++j) |
3032 | 0 | { |
3033 | 0 | bondCount.push_back(atom->GetExplicitDegree()); |
3034 | | //don't consider atoms with a full valance already |
3035 | | //this is both for correctness (trust existing bonds) and performance |
3036 | 0 | if(atom->GetExplicitValence() >= OBElements::GetMaxBonds(atom->GetAtomicNum())) |
3037 | 0 | continue; |
3038 | 0 | if(atom->GetAtomicNum() == 7 && atom->GetFormalCharge() == 0 && atom->GetExplicitValence() >= 3) |
3039 | 0 | continue; |
3040 | 0 | (atom->GetVector()).Get(&c[j*3]); |
3041 | 0 | pair<OBAtom*,double> entry(atom, atom->GetVector().z()); |
3042 | 0 | zsortedAtoms.push_back(entry); |
3043 | 0 | } |
3044 | 0 | sort(zsortedAtoms.begin(), zsortedAtoms.end(), SortAtomZ); |
3045 | |
|
3046 | 0 | max = zsortedAtoms.size(); |
3047 | |
|
3048 | 0 | for ( j = 0 ; j < max ; j++ ) |
3049 | 0 | { |
3050 | 0 | atom = zsortedAtoms[j].first; |
3051 | 0 | rad[j] = OBElements::GetCovalentRad(atom->GetAtomicNum()); |
3052 | 0 | maxrad = std::max(rad[j],maxrad); |
3053 | 0 | zsorted.push_back(atom->GetIdx()-1); |
3054 | 0 | } |
3055 | |
|
3056 | 0 | int idx1, idx2; |
3057 | 0 | double d2,cutoff,zd; |
3058 | 0 | vector3 atom1, atom2, wrapped_coords; // Only used for periodic coords |
3059 | 0 | for (j = 0 ; j < max ; ++j) |
3060 | 0 | { |
3061 | 0 | double maxcutoff = SQUARE(rad[j]+maxrad+0.45); |
3062 | 0 | idx1 = zsorted[j]; |
3063 | 0 | for (k = j + 1 ; k < max ; k++ ) |
3064 | 0 | { |
3065 | 0 | idx2 = zsorted[k]; |
3066 | | |
3067 | | // bonded if closer than elemental Rcov + tolerance |
3068 | 0 | cutoff = SQUARE(rad[j] + rad[k] + 0.45); |
3069 | | |
3070 | | // Use minimum image convention if the unit cell is periodic |
3071 | | // Otherwise, use a simpler (faster) distance calculation based on raw coordinates |
3072 | 0 | if (IsPeriodic()) |
3073 | 0 | { |
3074 | 0 | atom1 = vector3(c[idx1*3], c[idx1*3+1], c[idx1*3+2]); |
3075 | 0 | atom2 = vector3(c[idx2*3], c[idx2*3+1], c[idx2*3+2]); |
3076 | 0 | OBUnitCell *unitCell = (OBUnitCell * ) GetData(OBGenericDataType::UnitCell); |
3077 | 0 | wrapped_coords = unitCell->MinimumImageCartesian(atom1 - atom2); |
3078 | 0 | d2 = wrapped_coords.length_2(); |
3079 | 0 | } |
3080 | 0 | else |
3081 | 0 | { |
3082 | 0 | zd = SQUARE(c[idx1*3+2] - c[idx2*3+2]); |
3083 | | // bigger than max cutoff, which is determined using largest radius, |
3084 | | // not the radius of k (which might be small, ie H, and cause an early termination) |
3085 | | // since we sort by z, anything beyond k will also fail |
3086 | 0 | if (zd > maxcutoff ) |
3087 | 0 | break; |
3088 | | |
3089 | 0 | d2 = SQUARE(c[idx1*3] - c[idx2*3]); |
3090 | 0 | if (d2 > cutoff) |
3091 | 0 | continue; // x's bigger than cutoff |
3092 | 0 | d2 += SQUARE(c[idx1*3+1] - c[idx2*3+1]); |
3093 | 0 | if (d2 > cutoff) |
3094 | 0 | continue; // x^2 + y^2 bigger than cutoff |
3095 | 0 | d2 += zd; |
3096 | 0 | } |
3097 | | |
3098 | 0 | if (d2 > cutoff) |
3099 | 0 | continue; |
3100 | 0 | if (d2 < 0.16) // 0.4 * 0.4 = 0.16 |
3101 | 0 | continue; |
3102 | | |
3103 | 0 | atom = GetAtom(idx1+1); |
3104 | 0 | nbr = GetAtom(idx2+1); |
3105 | |
|
3106 | 0 | if (atom->IsConnected(nbr)) |
3107 | 0 | continue; |
3108 | | |
3109 | 0 | if (!validAdditionalBond(atom,nbr) || !validAdditionalBond(nbr, atom)) |
3110 | 0 | continue; |
3111 | | |
3112 | 0 | AddBond(idx1+1,idx2+1,1); |
3113 | 0 | } |
3114 | 0 | } |
3115 | | |
3116 | | // If between BeginModify and EndModify, coord pointers are NULL |
3117 | | // setup molecule to handle current coordinates |
3118 | |
|
3119 | 0 | if (_c == nullptr) |
3120 | 0 | { |
3121 | 0 | _c = c; |
3122 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
3123 | 0 | atom->SetCoordPtr(&_c); |
3124 | 0 | _vconf.push_back(c); |
3125 | 0 | unset = true; |
3126 | 0 | } |
3127 | | |
3128 | | // Cleanup -- delete long bonds that exceed max valence |
3129 | 0 | OBBond *maxbond, *bond; |
3130 | 0 | double maxlength; |
3131 | 0 | vector<OBBond*>::iterator l, m; |
3132 | 0 | int valCount; |
3133 | 0 | bool changed; |
3134 | 0 | BeginModify(); //prevent needless re-perception in DeleteBond |
3135 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
3136 | 0 | { |
3137 | 0 | while (atom->GetExplicitValence() > static_cast<unsigned int>(OBElements::GetMaxBonds(atom->GetAtomicNum())) |
3138 | 0 | || atom->SmallestBondAngle() < 45.0) |
3139 | 0 | { |
3140 | 0 | bond = atom->BeginBond(l); |
3141 | 0 | maxbond = bond; |
3142 | | // Fix from Liu Zhiguo 2008-01-26 |
3143 | | // loop past any bonds |
3144 | | // which existed before ConnectTheDots was called |
3145 | | // (e.g., from PDB resdata.txt) |
3146 | 0 | valCount = 0; |
3147 | 0 | while (valCount < bondCount[atom->GetIdx() - 1]) { |
3148 | 0 | bond = atom->NextBond(l); |
3149 | | // timvdm: 2008-03-05 |
3150 | | // NextBond only returns NULL if the iterator l == _bonds.end(). |
3151 | | // This was casuing problems as follows: |
3152 | | // NextBond = 0x???????? |
3153 | | // NextBond = 0x???????? |
3154 | | // NextBond = 0x???????? |
3155 | | // NextBond = 0x???????? |
3156 | | // NextBond = NULL <-- this NULL was not detected |
3157 | | // NextBond = 0x???????? |
3158 | 0 | if (!bond) // so we add an additional check |
3159 | 0 | break; |
3160 | 0 | maxbond = bond; |
3161 | 0 | valCount++; |
3162 | 0 | } |
3163 | 0 | if (!bond) // no new bonds added for this atom, just skip it |
3164 | 0 | break; |
3165 | | |
3166 | | // delete bonds between hydrogens when over max valence |
3167 | 0 | if (atom->GetAtomicNum() == OBElements::Hydrogen) |
3168 | 0 | { |
3169 | 0 | m = l; |
3170 | 0 | changed = false; |
3171 | 0 | for (;bond;bond = atom->NextBond(m)) |
3172 | 0 | { |
3173 | 0 | if (bond->GetNbrAtom(atom)->GetAtomicNum() == OBElements::Hydrogen) |
3174 | 0 | { |
3175 | 0 | DeleteBond(bond); |
3176 | 0 | changed = true; |
3177 | 0 | break; |
3178 | 0 | } |
3179 | 0 | } |
3180 | 0 | if (changed) |
3181 | 0 | { |
3182 | | // bond deleted, reevaluate BOSum |
3183 | 0 | continue; |
3184 | 0 | } |
3185 | 0 | else |
3186 | 0 | { |
3187 | | // reset to first new bond |
3188 | 0 | bond = maxbond; |
3189 | 0 | } |
3190 | 0 | } |
3191 | | |
3192 | 0 | maxlength = maxbond->GetLength(); |
3193 | 0 | for (bond = atom->NextBond(l);bond;bond = atom->NextBond(l)) |
3194 | 0 | { |
3195 | 0 | if (bond->GetLength() > maxlength) |
3196 | 0 | { |
3197 | 0 | maxbond = bond; |
3198 | 0 | maxlength = bond->GetLength(); |
3199 | 0 | } |
3200 | 0 | } |
3201 | 0 | DeleteBond(maxbond); // delete the new bond with the longest length |
3202 | 0 | } |
3203 | 0 | } |
3204 | 0 | EndModify(); |
3205 | 0 | if (unset) |
3206 | 0 | { |
3207 | 0 | if (_c != nullptr){ |
3208 | 0 | delete [] _c; |
3209 | | |
3210 | | // Note that the above delete doesn't set _c value to nullptr |
3211 | 0 | _c = nullptr; |
3212 | 0 | } |
3213 | |
|
3214 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
3215 | 0 | atom->ClearCoordPtr(); |
3216 | 0 | if (_vconf.size() > 0) |
3217 | 0 | _vconf.resize(_vconf.size()-1); |
3218 | 0 | } |
3219 | |
|
3220 | 0 | if (_c != nullptr) |
3221 | 0 | delete [] c; |
3222 | 0 | } |
3223 | | |
3224 | | /*! This method uses bond angles and geometries from current |
3225 | | connectivity to guess atom types and then filling empty valences |
3226 | | with multiple bonds. It currently has a pass to detect some |
3227 | | frequent functional groups. It still needs a pass to detect aromatic |
3228 | | rings to "clean up." |
3229 | | AssignSpinMultiplicity(true) is called at the end of the function. The true |
3230 | | states that there are no implict hydrogens in the molecule. |
3231 | | */ |
3232 | | void OBMol::PerceiveBondOrders() |
3233 | 0 | { |
3234 | 0 | if (Empty()) |
3235 | 0 | return; |
3236 | 0 | if (_dimension != 3) return; // not useful on non-3D structures |
3237 | | |
3238 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
3239 | 0 | "Ran OpenBabel::PerceiveBondOrders", obAuditMsg); |
3240 | |
|
3241 | 0 | OBAtom *atom, *b, *c; |
3242 | 0 | vector3 v1, v2; |
3243 | 0 | double angle;//, dist1, dist2; |
3244 | 0 | vector<OBAtom*>::iterator i; |
3245 | 0 | vector<OBBond*>::iterator j;//,k; |
3246 | | |
3247 | | // BeginModify(); |
3248 | | |
3249 | | // Pass 1: Assign estimated hybridization based on avg. angles |
3250 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
3251 | 0 | { |
3252 | 0 | angle = atom->AverageBondAngle(); |
3253 | | |
3254 | | // cout << atom->GetAtomicNum() << " " << angle << endl; |
3255 | |
|
3256 | 0 | if (angle > 155.0) |
3257 | 0 | atom->SetHyb(1); |
3258 | 0 | else if (angle <= 155.0 && angle > 115.0) |
3259 | 0 | atom->SetHyb(2); |
3260 | | |
3261 | | // special case for imines |
3262 | 0 | if (atom->GetAtomicNum() == OBElements::Nitrogen |
3263 | 0 | && atom->ExplicitHydrogenCount() == 1 |
3264 | 0 | && atom->GetExplicitDegree() == 2 |
3265 | 0 | && angle > 109.5) |
3266 | 0 | atom->SetHyb(2); |
3267 | 0 | else if(atom->GetAtomicNum() == OBElements::Nitrogen |
3268 | 0 | && atom->GetExplicitDegree() == 2 |
3269 | 0 | && atom->IsInRing()) //azete |
3270 | 0 | atom->SetHyb(2); |
3271 | 0 | } // pass 1 |
3272 | | |
3273 | | // Make sure upcoming calls to GetHyb() don't kill these temporary values |
3274 | 0 | SetHybridizationPerceived(); |
3275 | | |
3276 | | // Pass 2: look for 5-member rings with torsions <= 7.5 degrees |
3277 | | // and 6-member rings with torsions <= 12 degrees |
3278 | | // (set all atoms with at least two bonds to sp2) |
3279 | |
|
3280 | 0 | vector<OBRing*> rlist; |
3281 | 0 | vector<OBRing*>::iterator ringit; |
3282 | 0 | vector<int> path; |
3283 | 0 | double torsions = 0.0; |
3284 | |
|
3285 | 0 | if (!HasSSSRPerceived()) |
3286 | 0 | FindSSSR(); |
3287 | 0 | rlist = GetSSSR(); |
3288 | 0 | for (ringit = rlist.begin(); ringit != rlist.end(); ++ringit) |
3289 | 0 | { |
3290 | 0 | if ((*ringit)->Size() == 5) |
3291 | 0 | { |
3292 | 0 | path = (*ringit)->_path; |
3293 | 0 | torsions = |
3294 | 0 | ( fabs(GetTorsion(path[0], path[1], path[2], path[3])) + |
3295 | 0 | fabs(GetTorsion(path[1], path[2], path[3], path[4])) + |
3296 | 0 | fabs(GetTorsion(path[2], path[3], path[4], path[0])) + |
3297 | 0 | fabs(GetTorsion(path[3], path[4], path[0], path[1])) + |
3298 | 0 | fabs(GetTorsion(path[4], path[0], path[1], path[2])) ) / 5.0; |
3299 | 0 | if (torsions <= 7.5) |
3300 | 0 | { |
3301 | 0 | for (unsigned int ringAtom = 0; ringAtom != path.size(); ++ringAtom) |
3302 | 0 | { |
3303 | 0 | b = GetAtom(path[ringAtom]); |
3304 | | // if an aromatic ring atom has valence 3, it is already set |
3305 | | // to sp2 because the average angles should be 120 anyway |
3306 | | // so only look for valence 2 |
3307 | 0 | if (b->GetExplicitDegree() == 2) |
3308 | 0 | b->SetHyb(2); |
3309 | 0 | } |
3310 | 0 | } |
3311 | 0 | } |
3312 | 0 | else if ((*ringit)->Size() == 6) |
3313 | 0 | { |
3314 | 0 | path = (*ringit)->_path; |
3315 | 0 | torsions = |
3316 | 0 | ( fabs(GetTorsion(path[0], path[1], path[2], path[3])) + |
3317 | 0 | fabs(GetTorsion(path[1], path[2], path[3], path[4])) + |
3318 | 0 | fabs(GetTorsion(path[2], path[3], path[4], path[5])) + |
3319 | 0 | fabs(GetTorsion(path[3], path[4], path[5], path[0])) + |
3320 | 0 | fabs(GetTorsion(path[4], path[5], path[0], path[1])) + |
3321 | 0 | fabs(GetTorsion(path[5], path[0], path[1], path[2])) ) / 6.0; |
3322 | 0 | if (torsions <= 12.0) |
3323 | 0 | { |
3324 | 0 | for (unsigned int ringAtom = 0; ringAtom != path.size(); ++ringAtom) |
3325 | 0 | { |
3326 | 0 | b = GetAtom(path[ringAtom]); |
3327 | 0 | if (b->GetExplicitDegree() == 2 || b->GetExplicitDegree() == 3) |
3328 | 0 | b->SetHyb(2); |
3329 | 0 | } |
3330 | 0 | } |
3331 | 0 | } |
3332 | 0 | } |
3333 | | |
3334 | | // Pass 3: "Antialiasing" If an atom marked as sp hybrid isn't |
3335 | | // bonded to another or an sp2 hybrid isn't bonded |
3336 | | // to another (or terminal atoms in both cases) |
3337 | | // mark them to a lower hybridization for now |
3338 | 0 | bool openNbr; |
3339 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
3340 | 0 | { |
3341 | 0 | if (atom->GetHyb() == 2 || atom->GetHyb() == 1) |
3342 | 0 | { |
3343 | 0 | openNbr = false; |
3344 | 0 | for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j)) |
3345 | 0 | { |
3346 | 0 | if (b->GetHyb() < 3 || b->GetExplicitDegree() == 1) |
3347 | 0 | { |
3348 | 0 | openNbr = true; |
3349 | 0 | break; |
3350 | 0 | } |
3351 | 0 | } |
3352 | 0 | if (!openNbr && atom->GetHyb() == 2) |
3353 | 0 | atom->SetHyb(3); |
3354 | 0 | else if (!openNbr && atom->GetHyb() == 1) |
3355 | 0 | atom->SetHyb(2); |
3356 | 0 | } |
3357 | 0 | } // pass 3 |
3358 | | |
3359 | | // Pass 4: Check for known functional group patterns and assign bonds |
3360 | | // to the canonical form |
3361 | | // Currently we have explicit code to do this, but a "bond typer" |
3362 | | // is in progress to make it simpler to test and debug. |
3363 | 0 | bondtyper.AssignFunctionalGroupBonds(*this); |
3364 | | |
3365 | | // Pass 5: Check for aromatic rings and assign bonds as appropriate |
3366 | | // This is just a quick and dirty approximation that marks everything |
3367 | | // as potentially aromatic |
3368 | | |
3369 | | // This doesn't work perfectly, but it's pretty decent. |
3370 | | // Need to have a list of SMARTS patterns for common rings |
3371 | | // which would "break ties" on complicated multi-ring systems |
3372 | | // (Most of the current problems lie in the interface with the |
3373 | | // Kekulize code anyway, not in marking everything as potentially aromatic) |
3374 | |
|
3375 | 0 | bool needs_kekulization = false; // are there any aromatic bonds? |
3376 | 0 | bool typed; // has this ring been typed? |
3377 | 0 | unsigned int loop, loopSize; |
3378 | 0 | for (ringit = rlist.begin(); ringit != rlist.end(); ++ringit) |
3379 | 0 | { |
3380 | 0 | typed = false; |
3381 | 0 | loopSize = (*ringit)->Size(); |
3382 | 0 | if (loopSize == 5 || loopSize == 6 || loopSize == 7) |
3383 | 0 | { |
3384 | 0 | path = (*ringit)->_path; |
3385 | 0 | for(loop = 0; loop < loopSize; ++loop) |
3386 | 0 | { |
3387 | 0 | atom = GetAtom(path[loop]); |
3388 | 0 | if(atom->HasBondOfOrder(2) || atom->HasBondOfOrder(3) |
3389 | 0 | || atom->GetHyb() != 2) |
3390 | 0 | { |
3391 | 0 | typed = true; |
3392 | 0 | break; |
3393 | 0 | } |
3394 | 0 | } |
3395 | |
|
3396 | 0 | if (!typed) |
3397 | 0 | for(loop = 0; loop < loopSize; ++loop) |
3398 | 0 | { |
3399 | | // cout << " set aromatic " << path[loop] << endl; |
3400 | 0 | (GetBond(path[loop], path[(loop+1) % loopSize]))->SetAromatic(); |
3401 | 0 | needs_kekulization = true; |
3402 | 0 | } |
3403 | 0 | } |
3404 | 0 | } |
3405 | | |
3406 | | // Kekulization is necessary if an aromatic bond is present |
3407 | 0 | if (needs_kekulization) { |
3408 | 0 | this->SetAromaticPerceived(); |
3409 | | // First of all, set the atoms at the ends of the aromatic bonds to also |
3410 | | // be aromatic. This information is required for OBKekulize. |
3411 | 0 | FOR_BONDS_OF_MOL(bond, this) { |
3412 | 0 | if (bond->IsAromatic()) { |
3413 | 0 | bond->GetBeginAtom()->SetAromatic(); |
3414 | 0 | bond->GetEndAtom()->SetAromatic(); |
3415 | 0 | } |
3416 | 0 | } |
3417 | 0 | bool ok = OBKekulize(this); |
3418 | 0 | if (!ok) { |
3419 | 0 | stringstream errorMsg; |
3420 | 0 | errorMsg << "Failed to kekulize aromatic bonds in OBMol::PerceiveBondOrders"; |
3421 | 0 | std::string title = this->GetTitle(); |
3422 | 0 | if (!title.empty()) |
3423 | 0 | errorMsg << " (title is " << title << ")"; |
3424 | 0 | errorMsg << endl; |
3425 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
3426 | | // return false; Should we return false for a kekulization failure? |
3427 | 0 | } |
3428 | 0 | this->SetAromaticPerceived(false); |
3429 | 0 | } |
3430 | | |
3431 | | // Quick pass.. eliminate inter-ring sulfur atom multiple bonds |
3432 | | // dkoes - I have removed this code - if double bonds are set, |
3433 | | // we should trust them. See pdb_ligands_sdf/4iph_1fj.sdf for |
3434 | | // a case where the charge isn't set, but we break the molecule |
3435 | | // if we remove the double bond. Also, the previous code was |
3436 | | // fragile - relying on the total mol charge being set. If we |
3437 | | // are going to do anything, we should "perceive" a formal charge |
3438 | | // in the case of a ring sulfur with a double bond (thiopyrylium) |
3439 | | |
3440 | | // Pass 6: Assign remaining bond types, ordered by atom electronegativity |
3441 | 0 | vector<pair<OBAtom*,double> > sortedAtoms; |
3442 | 0 | vector<double> rad; |
3443 | 0 | vector<int> sorted; |
3444 | 0 | int iter, max; |
3445 | 0 | double maxElNeg, shortestBond, currentElNeg; |
3446 | 0 | double bondLength, testLength; |
3447 | |
|
3448 | 0 | for (atom = BeginAtom(i) ; atom ; atom = NextAtom(i)) |
3449 | 0 | { |
3450 | | // if atoms have the same electronegativity, make sure those with shorter bonds |
3451 | | // are handled first (helps with assignment of conjugated single/double bonds) |
3452 | 0 | shortestBond = 1.0e5; |
3453 | 0 | for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j)) |
3454 | 0 | { |
3455 | 0 | if (b->GetAtomicNum()!=1) shortestBond = |
3456 | 0 | std::min(shortestBond,(atom->GetBond(b))->GetLength()); |
3457 | 0 | } |
3458 | 0 | pair<OBAtom*,double> entry(atom, |
3459 | 0 | OBElements::GetElectroNeg(atom->GetAtomicNum())*1e6+shortestBond); |
3460 | |
|
3461 | 0 | sortedAtoms.push_back(entry); |
3462 | 0 | } |
3463 | 0 | sort(sortedAtoms.begin(), sortedAtoms.end(), SortAtomZ); |
3464 | |
|
3465 | 0 | max = sortedAtoms.size(); |
3466 | 0 | for (iter = 0 ; iter < max ; iter++ ) |
3467 | 0 | { |
3468 | 0 | atom = sortedAtoms[iter].first; |
3469 | | // Debugging statement |
3470 | | // cout << " atom->Hyb " << atom->GetAtomicNum() << " " << atom->GetIdx() << " " << atom->GetHyb() |
3471 | | // << " BO: " << atom->GetExplicitValence() << endl; |
3472 | | |
3473 | | // Possible sp-hybrids |
3474 | 0 | if ( (atom->GetHyb() == 1 || atom->GetExplicitDegree() == 1) |
3475 | 0 | && atom->GetExplicitValence() + 2 <= static_cast<unsigned int>(OBElements::GetMaxBonds(atom->GetAtomicNum())) |
3476 | 0 | ) |
3477 | 0 | { |
3478 | | |
3479 | | // loop through the neighbors looking for a hybrid or terminal atom |
3480 | | // (and pick the one with highest electronegativity first) |
3481 | | // *or* pick a neighbor that's a terminal atom |
3482 | 0 | if (atom->HasNonSingleBond() || |
3483 | 0 | (atom->GetAtomicNum() == 7 && atom->GetExplicitValence() + 2 > 3)) |
3484 | 0 | continue; |
3485 | | |
3486 | 0 | maxElNeg = 0.0; |
3487 | 0 | shortestBond = 5000.0; |
3488 | 0 | c = nullptr; |
3489 | 0 | for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j)) |
3490 | 0 | { |
3491 | 0 | currentElNeg = OBElements::GetElectroNeg(b->GetAtomicNum()); |
3492 | 0 | if ( (b->GetHyb() == 1 || b->GetExplicitDegree() == 1) |
3493 | 0 | && b->GetExplicitValence() + 2 <= static_cast<unsigned int>(OBElements::GetMaxBonds(b->GetAtomicNum())) |
3494 | 0 | && (currentElNeg > maxElNeg || |
3495 | 0 | (IsApprox(currentElNeg,maxElNeg, 1.0e-6) |
3496 | 0 | && (atom->GetBond(b))->GetLength() < shortestBond)) ) |
3497 | 0 | { |
3498 | 0 | if (b->HasNonSingleBond() || |
3499 | 0 | (b->GetAtomicNum() == 7 && b->GetExplicitValence() + 2 > 3)) |
3500 | 0 | continue; |
3501 | | |
3502 | | // Test terminal bonds against expected triple bond lengths |
3503 | 0 | bondLength = (atom->GetBond(b))->GetLength(); |
3504 | 0 | if (atom->GetExplicitDegree() == 1 || b->GetExplicitDegree() == 1) { |
3505 | 0 | testLength = CorrectedBondRad(atom->GetAtomicNum(), atom->GetHyb()) |
3506 | 0 | + CorrectedBondRad(b->GetAtomicNum(), b->GetHyb()); |
3507 | 0 | if (bondLength > 0.9 * testLength) |
3508 | 0 | continue; // too long, ignore it |
3509 | 0 | } |
3510 | | |
3511 | 0 | shortestBond = bondLength; |
3512 | 0 | maxElNeg = OBElements::GetElectroNeg(b->GetAtomicNum()); |
3513 | 0 | c = b; // save this atom for later use |
3514 | 0 | } |
3515 | 0 | } |
3516 | 0 | if (c) |
3517 | 0 | (atom->GetBond(c))->SetBondOrder(3); |
3518 | 0 | } |
3519 | | // Possible sp2-hybrid atoms |
3520 | 0 | else if ( (atom->GetHyb() == 2 || atom->GetExplicitDegree() == 1) |
3521 | 0 | && atom->GetExplicitValence() + 1 <= static_cast<unsigned int>(OBElements::GetMaxBonds(atom->GetAtomicNum())) ) |
3522 | 0 | { |
3523 | | // as above |
3524 | 0 | if (atom->HasNonSingleBond() || |
3525 | 0 | (atom->GetAtomicNum() == 7 && atom->GetExplicitValence() + 1 > 3)) |
3526 | 0 | continue; |
3527 | | |
3528 | | // Don't build multiple bonds to ring sulfurs |
3529 | | // except thiopyrylium |
3530 | 0 | if (atom->IsInRing() && atom->GetAtomicNum() == 16) { |
3531 | 0 | if (_totalCharge > 1 && atom->GetFormalCharge() == 0) |
3532 | 0 | atom->SetFormalCharge(+1); |
3533 | 0 | else |
3534 | 0 | continue; |
3535 | 0 | } |
3536 | | |
3537 | 0 | maxElNeg = 0.0; |
3538 | 0 | shortestBond = 5000.0; |
3539 | 0 | c = nullptr; |
3540 | 0 | for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j)) |
3541 | 0 | { |
3542 | 0 | currentElNeg = OBElements::GetElectroNeg(b->GetAtomicNum()); |
3543 | 0 | if ( (b->GetHyb() == 2 || b->GetExplicitDegree() == 1) |
3544 | 0 | && b->GetExplicitValence() + 1 <= static_cast<unsigned int>(OBElements::GetMaxBonds(b->GetAtomicNum())) |
3545 | 0 | && (GetBond(atom, b))->IsDoubleBondGeometry() |
3546 | 0 | && (currentElNeg > maxElNeg || (IsApprox(currentElNeg,maxElNeg, 1.0e-6)) ) ) |
3547 | 0 | { |
3548 | 0 | if (b->HasNonSingleBond() || |
3549 | 0 | (b->GetAtomicNum() == 7 && b->GetExplicitValence() + 1 > 3)) |
3550 | 0 | continue; |
3551 | | |
3552 | 0 | if (b->IsInRing() && b->GetAtomicNum() == 16) { |
3553 | 0 | if (_totalCharge > 1 && b->GetFormalCharge() == 0) |
3554 | 0 | b->SetFormalCharge(+1); |
3555 | 0 | else |
3556 | 0 | continue; |
3557 | 0 | } |
3558 | | |
3559 | | // Test terminal bonds against expected double bond lengths |
3560 | 0 | bondLength = (atom->GetBond(b))->GetLength(); |
3561 | 0 | if (atom->GetExplicitDegree() == 1 || b->GetExplicitDegree() == 1) { |
3562 | 0 | testLength = CorrectedBondRad(atom->GetAtomicNum(), atom->GetHyb()) |
3563 | 0 | + CorrectedBondRad(b->GetAtomicNum(), b->GetHyb()); |
3564 | 0 | if (bondLength > 0.93 * testLength) |
3565 | 0 | continue; // too long, ignore it |
3566 | 0 | } |
3567 | | |
3568 | | // OK, see if this is better than the previous choice |
3569 | | // If it's much shorter, pick it (e.g., fulvene) |
3570 | | // If they're close (0.1A) then prefer the bond in the ring |
3571 | 0 | double difference = shortestBond - (atom->GetBond(b))->GetLength(); |
3572 | 0 | if ( (difference > 0.1) |
3573 | 0 | || ( (difference > -0.01) && |
3574 | 0 | ( (!atom->IsInRing() || !c || !c->IsInRing() || b->IsInRing()) |
3575 | 0 | || (atom->IsInRing() && c && !c->IsInRing() && b->IsInRing()) ) ) ) { |
3576 | 0 | shortestBond = (atom->GetBond(b))->GetLength(); |
3577 | 0 | maxElNeg = OBElements::GetElectroNeg(b->GetAtomicNum()); |
3578 | 0 | c = b; // save this atom for later use |
3579 | 0 | } // is this bond better than previous choices |
3580 | 0 | } |
3581 | 0 | } // loop through neighbors |
3582 | 0 | if (c) |
3583 | 0 | (atom->GetBond(c))->SetBondOrder(2); |
3584 | 0 | } |
3585 | 0 | } // pass 6 |
3586 | | |
3587 | | // Now let the atom typer go to work again |
3588 | 0 | _flags &= (~(OB_HYBRID_MOL)); |
3589 | 0 | _flags &= (~(OB_AROMATIC_MOL)); |
3590 | 0 | _flags &= (~(OB_ATOMTYPES_MOL)); |
3591 | | // EndModify(true); // "nuke" perceived data |
3592 | | |
3593 | | //Set _spinMultiplicity other than zero for atoms which are hydrogen |
3594 | | //deficient and which have implicit valency definitions (essentially the |
3595 | | //organic subset in SMILES). There are assumed to no implicit hydrogens. |
3596 | | //AssignSpinMultiplicity(true); // TODO: sort out radicals |
3597 | 0 | } |
3598 | | |
3599 | | void OBMol::Center() |
3600 | 0 | { |
3601 | 0 | for (int i = 0;i < NumConformers();++i) |
3602 | 0 | Center(i); |
3603 | 0 | } |
3604 | | |
3605 | | vector3 OBMol::Center(int nconf) |
3606 | 0 | { |
3607 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
3608 | 0 | "Ran OpenBabel::Center", obAuditMsg); |
3609 | |
|
3610 | 0 | SetConformer(nconf); |
3611 | |
|
3612 | 0 | OBAtom *atom; |
3613 | 0 | vector<OBAtom*>::iterator i; |
3614 | |
|
3615 | 0 | double x=0.0,y=0.0,z=0.0; |
3616 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
3617 | 0 | { |
3618 | 0 | x += atom->x(); |
3619 | 0 | y += atom->y(); |
3620 | 0 | z += atom->z(); |
3621 | 0 | } |
3622 | |
|
3623 | 0 | x /= (double)NumAtoms(); |
3624 | 0 | y /= (double)NumAtoms(); |
3625 | 0 | z /= (double)NumAtoms(); |
3626 | |
|
3627 | 0 | vector3 vtmp; |
3628 | 0 | vector3 v(x,y,z); |
3629 | |
|
3630 | 0 | for (atom = BeginAtom(i);atom;atom = NextAtom(i)) |
3631 | 0 | { |
3632 | 0 | vtmp = atom->GetVector() - v; |
3633 | 0 | atom->SetVector(vtmp); |
3634 | 0 | } |
3635 | |
|
3636 | 0 | return(v); |
3637 | 0 | } |
3638 | | |
3639 | | |
3640 | | /*! this method adds the vector v to all atom positions in all conformers */ |
3641 | | void OBMol::Translate(const vector3 &v) |
3642 | 0 | { |
3643 | 0 | for (int i = 0;i < NumConformers();++i) |
3644 | 0 | Translate(v,i); |
3645 | 0 | } |
3646 | | |
3647 | | /*! this method adds the vector v to all atom positions in the |
3648 | | conformer nconf. If nconf == OB_CURRENT_CONFORMER, then the atom |
3649 | | positions in the current conformer are translated. */ |
3650 | | void OBMol::Translate(const vector3 &v, int nconf) |
3651 | 0 | { |
3652 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
3653 | 0 | "Ran OpenBabel::Translate", obAuditMsg); |
3654 | |
|
3655 | 0 | int i,size; |
3656 | 0 | double x,y,z; |
3657 | 0 | double *c = (nconf == OB_CURRENT_CONFORMER)? _c : GetConformer(nconf); |
3658 | |
|
3659 | 0 | x = v.x(); |
3660 | 0 | y = v.y(); |
3661 | 0 | z = v.z(); |
3662 | 0 | size = NumAtoms(); |
3663 | 0 | for (i = 0;i < size;++i) |
3664 | 0 | { |
3665 | 0 | c[i*3 ] += x; |
3666 | 0 | c[i*3+1] += y; |
3667 | 0 | c[i*3+2] += z; |
3668 | 0 | } |
3669 | 0 | } |
3670 | | |
3671 | | void OBMol::Rotate(const double u[3][3]) |
3672 | 0 | { |
3673 | 0 | int i,j,k; |
3674 | 0 | double m[9]; |
3675 | 0 | for (k=0,i = 0;i < 3;++i) |
3676 | 0 | for (j = 0;j < 3;++j) |
3677 | 0 | m[k++] = u[i][j]; |
3678 | |
|
3679 | 0 | for (i = 0;i < NumConformers();++i) |
3680 | 0 | Rotate(m,i); |
3681 | 0 | } |
3682 | | |
3683 | | void OBMol::Rotate(const double m[9]) |
3684 | 0 | { |
3685 | 0 | for (int i = 0;i < NumConformers();++i) |
3686 | 0 | Rotate(m,i); |
3687 | 0 | } |
3688 | | |
3689 | | void OBMol::Rotate(const double m[9],int nconf) |
3690 | 0 | { |
3691 | 0 | int i,size; |
3692 | 0 | double x,y,z; |
3693 | 0 | double *c = (nconf == OB_CURRENT_CONFORMER)? _c : GetConformer(nconf); |
3694 | |
|
3695 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
3696 | 0 | "Ran OpenBabel::Rotate", obAuditMsg); |
3697 | |
|
3698 | 0 | size = NumAtoms(); |
3699 | 0 | for (i = 0;i < size;++i) |
3700 | 0 | { |
3701 | 0 | x = c[i*3 ]; |
3702 | 0 | y = c[i*3+1]; |
3703 | 0 | z = c[i*3+2]; |
3704 | 0 | c[i*3 ] = m[0]*x + m[1]*y + m[2]*z; |
3705 | 0 | c[i*3+1] = m[3]*x + m[4]*y + m[5]*z; |
3706 | 0 | c[i*3+2] = m[6]*x + m[7]*y + m[8]*z; |
3707 | 0 | } |
3708 | 0 | } |
3709 | | |
3710 | | void OBMol::SetEnergies(std::vector<double> &energies) |
3711 | 0 | { |
3712 | 0 | if (!HasData(OBGenericDataType::ConformerData)) |
3713 | 0 | SetData(new OBConformerData); |
3714 | 0 | OBConformerData *cd = (OBConformerData*) GetData(OBGenericDataType::ConformerData); |
3715 | 0 | cd->SetEnergies(energies); |
3716 | 0 | } |
3717 | | |
3718 | | vector<double> OBMol::GetEnergies() |
3719 | 0 | { |
3720 | 0 | if (!HasData(OBGenericDataType::ConformerData)) |
3721 | 0 | SetData(new OBConformerData); |
3722 | 0 | OBConformerData *cd = (OBConformerData*) GetData(OBGenericDataType::ConformerData); |
3723 | 0 | vector<double> energies = cd->GetEnergies(); |
3724 | |
|
3725 | 0 | return energies; |
3726 | 0 | } |
3727 | | |
3728 | | double OBMol::GetEnergy(int ci) |
3729 | 0 | { |
3730 | 0 | if (!HasData(OBGenericDataType::ConformerData)) |
3731 | 0 | SetData(new OBConformerData); |
3732 | 0 | OBConformerData *cd = (OBConformerData*) GetData(OBGenericDataType::ConformerData); |
3733 | 0 | vector<double> energies = cd->GetEnergies(); |
3734 | |
|
3735 | 0 | if (((unsigned int)ci >= energies.size()) || (ci < 0)) |
3736 | 0 | return 0.0; |
3737 | | |
3738 | 0 | return energies[ci]; |
3739 | 0 | } |
3740 | | |
3741 | | void OBMol::SetConformers(vector<double*> &v) |
3742 | 0 | { |
3743 | 0 | vector<double*>::iterator i; |
3744 | 0 | for (i = _vconf.begin();i != _vconf.end();++i) |
3745 | 0 | delete [] *i; |
3746 | |
|
3747 | 0 | _vconf = v; |
3748 | 0 | _c = _vconf.empty() ? nullptr : _vconf[0]; |
3749 | |
|
3750 | 0 | } |
3751 | | |
3752 | | void OBMol::SetConformer(unsigned int i) |
3753 | 0 | { |
3754 | 0 | if (i < _vconf.size()) |
3755 | 0 | _c = _vconf[i]; |
3756 | 0 | } |
3757 | | |
3758 | | void OBMol::CopyConformer(double *c,int idx) |
3759 | 0 | { |
3760 | | // obAssert(!_vconf.empty() && (unsigned)idx < _vconf.size()); |
3761 | 0 | memcpy((char*)c, (char*)_vconf[idx], sizeof(double)*3*NumAtoms()); |
3762 | 0 | } |
3763 | | |
3764 | | // void OBMol::CopyConformer(double *c,int idx) |
3765 | | // { |
3766 | | // obAssert(!_vconf.empty() && (unsigned)idx < _vconf.size()); |
3767 | | |
3768 | | // unsigned int i; |
3769 | | // for (i = 0;i < NumAtoms();++i) |
3770 | | // { |
3771 | | // _vconf[idx][i*3 ] = (double)c[i*3 ]; |
3772 | | // _vconf[idx][i*3+1] = (double)c[i*3+1]; |
3773 | | // _vconf[idx][i*3+2] = (double)c[i*3+2]; |
3774 | | // } |
3775 | | // } |
3776 | | |
3777 | | void OBMol::DeleteConformer(int idx) |
3778 | 0 | { |
3779 | 0 | if (idx < 0 || idx >= (signed)_vconf.size()) |
3780 | 0 | return; |
3781 | | |
3782 | 0 | delete [] _vconf[idx]; |
3783 | 0 | _vconf.erase((_vconf.begin()+idx)); |
3784 | 0 | } |
3785 | | |
3786 | | ///Converts for instance [N+]([O-])=O to N(=O)=O |
3787 | | bool OBMol::ConvertDativeBonds() |
3788 | 0 | { |
3789 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
3790 | 0 | "Ran OpenBabel::ConvertDativeBonds", obAuditMsg); |
3791 | | |
3792 | | //Look for + and - charges on adjacent atoms |
3793 | 0 | OBAtom* patom; |
3794 | 0 | vector<OBAtom*>::iterator i; |
3795 | 0 | bool converted = false; |
3796 | 0 | for (patom = BeginAtom(i);patom;patom = NextAtom(i)) |
3797 | 0 | { |
3798 | 0 | vector<OBBond*>::iterator itr; |
3799 | 0 | OBBond *pbond; |
3800 | 0 | for (pbond = patom->BeginBond(itr);patom->GetFormalCharge() && pbond;pbond = patom->NextBond(itr)) |
3801 | 0 | { |
3802 | 0 | OBAtom* pNbratom = pbond->GetNbrAtom(patom); |
3803 | 0 | int chg1 = patom->GetFormalCharge(); |
3804 | 0 | int chg2 = pNbratom->GetFormalCharge(); |
3805 | 0 | if((chg1>0 && chg2<0)|| (chg1<0 && chg2>0)) |
3806 | 0 | { |
3807 | | //dative bond. Reduce charges and increase bond order |
3808 | 0 | converted =true; |
3809 | 0 | if(chg1>0) |
3810 | 0 | --chg1; |
3811 | 0 | else |
3812 | 0 | ++chg1; |
3813 | 0 | patom->SetFormalCharge(chg1); |
3814 | 0 | if(chg2>0) |
3815 | 0 | --chg2; |
3816 | 0 | else |
3817 | 0 | ++chg2; |
3818 | 0 | pNbratom->SetFormalCharge(chg2); |
3819 | 0 | pbond->SetBondOrder(pbond->GetBondOrder()+1); |
3820 | 0 | } |
3821 | 0 | } |
3822 | 0 | } |
3823 | 0 | return converted; //false if no changes made |
3824 | 0 | } |
3825 | | |
3826 | | static bool IsNotCorH(OBAtom* atom) |
3827 | 0 | { |
3828 | 0 | switch (atom->GetAtomicNum()) |
3829 | 0 | { |
3830 | 0 | case OBElements::Hydrogen: |
3831 | 0 | case OBElements::Carbon: |
3832 | 0 | return false; |
3833 | 0 | } |
3834 | 0 | return true; |
3835 | 0 | } |
3836 | | |
3837 | | //This maybe would be better using smirks from a datafile |
3838 | | bool OBMol::MakeDativeBonds() |
3839 | 0 | { |
3840 | | //! Converts 5-valent N to charged form of dative bonds, |
3841 | | //! e.g. -N(=O)=O converted to -[N+]([O-])=O. Returns true if conversion occurs. |
3842 | 0 | BeginModify(); |
3843 | | //AddHydrogens(); |
3844 | 0 | bool converted = false; |
3845 | 0 | OBAtom* patom; |
3846 | 0 | vector<OBAtom*>::iterator ai; |
3847 | 0 | for (patom = BeginAtom(ai);patom;patom = NextAtom(ai)) //all atoms |
3848 | 0 | { |
3849 | 0 | if(patom->GetAtomicNum() == OBElements::Nitrogen // || patom->GetAtomicNum() == OBElements::Phosphorus) not phosphorus! |
3850 | 0 | && (patom->GetExplicitValence()==5 || (patom->GetExplicitValence()==4 && patom->GetFormalCharge()==0))) |
3851 | 0 | { |
3852 | | // Find the bond to be modified. Prefer a bond to a hetero-atom, |
3853 | | // and the highest order bond if there is a choice. |
3854 | 0 | OBBond *bond, *bestbond; |
3855 | 0 | OBBondIterator bi; |
3856 | 0 | for (bestbond = bond = patom->BeginBond(bi); bond; bond = patom->NextBond(bi)) |
3857 | 0 | { |
3858 | 0 | unsigned int bo = bond->GetBondOrder(); |
3859 | 0 | if(bo>=2 && bo<=4) |
3860 | 0 | { |
3861 | 0 | bool het = IsNotCorH(bond->GetNbrAtom(patom)); |
3862 | 0 | bool oldhet = IsNotCorH(bestbond->GetNbrAtom(patom)); |
3863 | 0 | bool higherorder = bo > bestbond->GetBondOrder(); |
3864 | 0 | if((het && !oldhet) || (((het && oldhet) || (!het && !oldhet)) && higherorder)) |
3865 | 0 | bestbond = bond; |
3866 | 0 | } |
3867 | 0 | } |
3868 | | //Make the charged form |
3869 | 0 | bestbond->SetBondOrder(bestbond->GetBondOrder()-1); |
3870 | 0 | patom->SetFormalCharge(+1); |
3871 | 0 | OBAtom* at = bestbond->GetNbrAtom(patom); |
3872 | 0 | at->SetFormalCharge(-1); |
3873 | 0 | converted=true; |
3874 | 0 | } |
3875 | 0 | } |
3876 | 0 | EndModify(); |
3877 | 0 | return converted; |
3878 | 0 | } |
3879 | | |
3880 | | /** |
3881 | | * This function is useful when writing to legacy formats (such as MDL MOL) that do |
3882 | | * not support zero-order bonds. It is worth noting that some compounds cannot be |
3883 | | * well represented using just single, double and triple bonds, even with adjustments |
3884 | | * to adjacent charges. In these cases, simply converting zero-order bonds to single |
3885 | | * bonds is all that can be done. |
3886 | | * |
3887 | | @verbatim |
3888 | | Algorithm from: |
3889 | | Clark, A. M. Accurate Specification of Molecular Structures: The Case for |
3890 | | Zero-Order Bonds and Explicit Hydrogen Counting. Journal of Chemical Information |
3891 | | and Modeling, 51, 3149-3157 (2011). http://pubs.acs.org/doi/abs/10.1021/ci200488k |
3892 | | @endverbatim |
3893 | | */ |
3894 | | bool OBMol::ConvertZeroBonds() |
3895 | 0 | { |
3896 | | // TODO: Option to just remove zero-order bonds entirely |
3897 | | |
3898 | | // TODO: Is it OK to not wrap this in BeginModify() and EndModify()? |
3899 | | // If we must, I think we need to manually remember HasImplicitValencePerceived and |
3900 | | // re-set it after EndModify() |
3901 | | |
3902 | | // Periodic table block for element (1=s, 2=p, 3=d, 4=f) |
3903 | 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, |
3904 | 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, |
3905 | 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, |
3906 | 0 | 4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3}; |
3907 | |
|
3908 | 0 | bool converted = false; |
3909 | | // Get contiguous fragments of molecule |
3910 | 0 | vector<vector<int> > cfl; |
3911 | 0 | ContigFragList(cfl); |
3912 | | // Iterate over contiguous fragments |
3913 | 0 | for (vector< vector<int> >::iterator i = cfl.begin(); i != cfl.end(); ++i) { |
3914 | | // Get all zero-order bonds in contiguous fragment |
3915 | 0 | vector<OBBond*> bonds; |
3916 | 0 | for(vector<int>::const_iterator j = i->begin(); j != i->end(); ++j) { |
3917 | 0 | FOR_BONDS_OF_ATOM(b, GetAtom(*j)) { |
3918 | 0 | if (b->GetBondOrder() == 0 && !(find(bonds.begin(), bonds.end(), &*b) != bonds.end())) { |
3919 | 0 | bonds.push_back(&*b); |
3920 | 0 | } |
3921 | 0 | } |
3922 | 0 | } |
3923 | | // Convert zero-order bonds |
3924 | 0 | while (bonds.size() > 0) { |
3925 | | // Pick a bond using scoring system |
3926 | 0 | int bi = 0; |
3927 | 0 | if (bonds.size() > 1) { |
3928 | 0 | vector<int> scores(bonds.size()); |
3929 | 0 | for (unsigned int n = 0; n < bonds.size(); n++) { |
3930 | 0 | OBAtom *bgn = bonds[n]->GetBeginAtom(); |
3931 | 0 | OBAtom *end = bonds[n]->GetEndAtom(); |
3932 | 0 | int score = 0; |
3933 | 0 | score += bgn->GetAtomicNum() + end->GetAtomicNum(); |
3934 | 0 | score += abs(bgn->GetFormalCharge()) + abs(end->GetFormalCharge()); |
3935 | 0 | pair<int, int> lb = bgn->LewisAcidBaseCounts(); |
3936 | 0 | pair<int, int> le = end->LewisAcidBaseCounts(); |
3937 | 0 | if (lb.first > 0 && lb.second > 0 && le.first > 0 && le.second > 0) { |
3938 | 0 | score += 100; // Both atoms are Lewis acids *and* Lewis bases |
3939 | 0 | } else if ((lb.first > 0 && le.second > 0) && (lb.second > 0 && le.first > 0)) { |
3940 | 0 | score -= 1000; // Lewis acid/base direction is mono-directional |
3941 | 0 | } |
3942 | 0 | int bcount = bgn->GetImplicitHCount(); |
3943 | 0 | FOR_BONDS_OF_ATOM(b, bgn) { bcount += 1; } |
3944 | 0 | int ecount = end->GetImplicitHCount(); |
3945 | 0 | FOR_BONDS_OF_ATOM(b, end) { ecount += 1; } |
3946 | 0 | if (bcount == 1 || ecount == 1) { |
3947 | 0 | score -= 10; // If the start or end atoms have only 1 neighbour |
3948 | 0 | } |
3949 | 0 | scores[n] = score; |
3950 | 0 | } |
3951 | 0 | for (unsigned int n = 1; n < scores.size(); n++) { |
3952 | 0 | if (scores[n] < scores[bi]) { |
3953 | 0 | bi = n; |
3954 | 0 | } |
3955 | 0 | } |
3956 | 0 | } |
3957 | 0 | OBBond *bond = bonds[bi]; |
3958 | 0 | bonds.erase(bonds.begin() + bi); |
3959 | 0 | OBAtom *bgn = bond->GetBeginAtom(); |
3960 | 0 | OBAtom *end = bond->GetEndAtom(); |
3961 | | // _ele is an unsigned char (0..255), but BLOCKS only covers known |
3962 | | // elements (Z<=112). Treat anything outside the table as block 0 |
3963 | | // so the heuristics below leave the bond as a plain single bond. |
3964 | 0 | unsigned int zb = bgn->GetAtomicNum(); |
3965 | 0 | unsigned int ze = end->GetAtomicNum(); |
3966 | 0 | int blockb = (zb < sizeof(BLOCKS)/sizeof(BLOCKS[0])) ? BLOCKS[zb] : 0; |
3967 | 0 | int blocke = (ze < sizeof(BLOCKS)/sizeof(BLOCKS[0])) ? BLOCKS[ze] : 0; |
3968 | 0 | pair<int, int> lb = bgn->LewisAcidBaseCounts(); |
3969 | 0 | pair<int, int> le = end->LewisAcidBaseCounts(); |
3970 | 0 | int chg = 0; // Amount to adjust atom charges |
3971 | 0 | int ord = 1; // New bond order |
3972 | 0 | if (lb.first > 0 && lb.second > 0 && le.first > 0 && le.second > 0) { |
3973 | 0 | ord = 2; // both atoms are amphoteric, so turn it into a double bond |
3974 | 0 | } else if (lb.first > 0 && blockb == 2 && blocke >= 3) { |
3975 | 0 | ord = 2; // p-block lewis acid with d/f-block element: make into double bond |
3976 | 0 | } else if (le.first > 0 && blocke == 2 && blockb >= 3) { |
3977 | 0 | ord = 2; // p-block lewis acid with d/f-block element: make into double bond |
3978 | 0 | } else if (lb.first > 0 && le.second > 0) { |
3979 | 0 | chg = -1; // lewis acid/base goes one way only; charge separate it |
3980 | 0 | } else if (lb.second > 0 && le.first > 0) { |
3981 | 0 | chg = 1; // no matching capacity; do not charge separate |
3982 | 0 | } |
3983 | | // adjust bond order and atom charges accordingly |
3984 | 0 | bgn->SetFormalCharge(bgn->GetFormalCharge()+chg); |
3985 | 0 | end->SetFormalCharge(end->GetFormalCharge()-chg); |
3986 | 0 | bond->SetBondOrder(ord); |
3987 | 0 | converted = true; |
3988 | 0 | } |
3989 | 0 | } |
3990 | 0 | return converted; |
3991 | 0 | } |
3992 | | |
3993 | | OBAtom *OBMol::BeginAtom(OBAtomIterator &i) |
3994 | 904k | { |
3995 | 904k | i = _vatom.begin(); |
3996 | 904k | return i == _vatom.end() ? nullptr : (OBAtom*)*i; |
3997 | 904k | } |
3998 | | |
3999 | | const OBAtom* OBMol::BeginAtom(OBAtomConstIterator &i) const |
4000 | 0 | { |
4001 | 0 | i = _vatom.cbegin(); |
4002 | 0 | return i == _vatom.cend() ? nullptr : (OBAtom *)*i; |
4003 | 0 | } |
4004 | | |
4005 | | OBAtom *OBMol::NextAtom(OBAtomIterator &i) |
4006 | 116M | { |
4007 | 116M | ++i; |
4008 | 116M | return i == _vatom.end() ? nullptr : (OBAtom*)*i; |
4009 | 116M | } |
4010 | | |
4011 | | const OBAtom* OBMol::NextAtom(OBAtomConstIterator &i) const |
4012 | 0 | { |
4013 | 0 | ++i; |
4014 | 0 | return i == _vatom.cend() ? nullptr : (OBAtom *)*i; |
4015 | 0 | } |
4016 | | |
4017 | | OBBond *OBMol::BeginBond(OBBondIterator &i) |
4018 | 65.7k | { |
4019 | 65.7k | i = _vbond.begin(); |
4020 | 65.7k | return i == _vbond.end() ? nullptr : (OBBond*)*i; |
4021 | 65.7k | } |
4022 | | |
4023 | | OBBond *OBMol::NextBond(OBBondIterator &i) |
4024 | 109k | { |
4025 | 109k | ++i; |
4026 | 109k | return i == _vbond.end() ? nullptr : (OBBond*)*i; |
4027 | 109k | } |
4028 | | |
4029 | | //! \since version 2.4 |
4030 | | int OBMol::AreInSameRing(OBAtom *a, OBAtom *b) |
4031 | 0 | { |
4032 | 0 | bool a_in, b_in; |
4033 | 0 | vector<OBRing*> vr; |
4034 | 0 | vr = GetLSSR(); |
4035 | |
|
4036 | 0 | vector<OBRing*>::iterator i; |
4037 | 0 | vector<int>::iterator j; |
4038 | |
|
4039 | 0 | for (i = vr.begin();i != vr.end();++i) { |
4040 | 0 | a_in = false; |
4041 | 0 | b_in = false; |
4042 | | // Go through the path of the ring and see if a and/or b match |
4043 | | // each node in the path |
4044 | 0 | for(j = (*i)->_path.begin();j != (*i)->_path.end();++j) { |
4045 | 0 | if ((unsigned)(*j) == a->GetIdx()) |
4046 | 0 | a_in = true; |
4047 | 0 | if ((unsigned)(*j) == b->GetIdx()) |
4048 | 0 | b_in = true; |
4049 | 0 | } |
4050 | |
|
4051 | 0 | if (a_in && b_in) |
4052 | 0 | return (*i)->Size(); |
4053 | 0 | } |
4054 | | |
4055 | 0 | return 0; |
4056 | 0 | } |
4057 | | |
4058 | | vector<OBMol> OBMol::Separate(int StartIndex) |
4059 | 0 | { |
4060 | 0 | vector<OBMol> result; |
4061 | 0 | if( NumAtoms() == 0 ) |
4062 | 0 | return result; // nothing to do, but let's prevent a crash |
4063 | | |
4064 | 0 | OBMolAtomDFSIter iter( this, StartIndex ); |
4065 | 0 | OBMol newMol; |
4066 | 0 | while( GetNextFragment( iter, newMol ) ) { |
4067 | 0 | result.push_back( newMol ); |
4068 | 0 | newMol.Clear(); |
4069 | 0 | } |
4070 | |
|
4071 | 0 | return result; |
4072 | 0 | } |
4073 | | |
4074 | | //! \brief Copy part of a molecule to another molecule |
4075 | | /** |
4076 | | This function copies a substructure of a molecule to another molecule. The key |
4077 | | information needed is an OBBitVec indicating which atoms to include and (optionally) |
4078 | | an OBBitVec indicating which bonds to exclude. By default, only bonds joining |
4079 | | included atoms are copied. |
4080 | | |
4081 | | When an atom is copied, but not all of its bonds are, by default hydrogen counts are |
4082 | | adjusted to account for the missing bonds. That is, given the SMILES "CF", if we |
4083 | | copy the two atoms but exclude the bond, we will end up with "C.F". This behavior |
4084 | | can be changed by specifiying a value other than 1 for the \p correctvalence parameter. |
4085 | | A value of 0 will yield "[C].[F]" while 2 will yield "C*.F*" (see \p correctvalence below |
4086 | | for more information). |
4087 | | |
4088 | | Aromaticity is preserved as present in the original OBMol. If this is not desired, |
4089 | | the user should call OBMol::SetAromaticPerceived(false) on the new OBMol. |
4090 | | |
4091 | | Stereochemistry is only preserved if the corresponding elements are wholly present in |
4092 | | the substructure. For example, all four atoms and bonds of a tetrahedral stereocenter |
4093 | | must be copied. |
4094 | | |
4095 | | Residue information is preserved if the original OBMol is marked as having |
4096 | | its residues perceived. If this is not desired, either call |
4097 | | OBMol::SetChainsPerceived(false) in advance on the original OBMol to avoid copying |
4098 | | the residues (and then reset it afterwards), or else call it on the new OBMol so |
4099 | | that residue information will be reperceived (when requested). |
4100 | | |
4101 | | Here is an example of using this method to copy ring systems to a new molecule. |
4102 | | Given the molecule represented by the SMILES string, "FC1CC1c2ccccc2I", we will |
4103 | | end up with a new molecule represented by the SMILES string, "C1CC1.c2ccccc2". |
4104 | | \code{.cpp} |
4105 | | OBBitVec atoms(mol.NumAtoms() + 1); // the maximum size needed |
4106 | | FOR_ATOMS_OF_MOL(atom, mol) { |
4107 | | if(atom->IsInRing()) |
4108 | | atoms.SetBitOn(atom->Idx()); |
4109 | | } |
4110 | | OBBitVec excludebonds(mol.NumBonds()); // the maximum size needed |
4111 | | FOR_BONDS_OF_MOL(bond, mol) { |
4112 | | if(!bond->IsInRing()) |
4113 | | excludebonds.SetBitOn(bond->Idx()); |
4114 | | } |
4115 | | OBMol newmol; |
4116 | | mol.CopySubstructure(&newmol, &atoms, &excludebonds); |
4117 | | \endcode |
4118 | | |
4119 | | When used from Python, note that "None" may be used to specify an empty value for |
4120 | | the \p excludebonds parameter. |
4121 | | |
4122 | | \remark Some alternatives to using this function, which may be preferred in some |
4123 | | instances due to efficiency or convenience are: |
4124 | | -# Copying the entire OBMol, and then deleting the unwanted parts |
4125 | | -# Modifiying the original OBMol, and then restoring it |
4126 | | -# Using the SMILES writer option -xf to specify fragment atom idxs |
4127 | | |
4128 | | \return A boolean indicating success or failure. Currently failure is only reported |
4129 | | if one of the specified atoms is not present, or \p atoms is a NULL |
4130 | | pointer. |
4131 | | |
4132 | | \param newmol The molecule to which to add the substructure. Note that atoms are |
4133 | | appended to this molecule. |
4134 | | \param atoms An OBBitVec, indexed by atom Idx, specifying which atoms to copy |
4135 | | \param excludebonds An OBBitVec, indexed by bond Idx, specifying a list of bonds |
4136 | | to exclude. By default, all bonds between the specified atoms are |
4137 | | included - this parameter overrides that. |
4138 | | \param correctvalence A value of 0, 1 (default) or 2 that indicates how atoms with missing |
4139 | | bonds are handled: |
4140 | | 0 - Leave the implicit hydrogen count unchanged; |
4141 | | 1 - Adjust the implicit hydrogen count to correct for |
4142 | | the missing bonds; |
4143 | | 2 - Replace the missing bonds with bonds to dummy atoms |
4144 | | \param atomorder Record the Idxs of the original atoms. That is, the first element |
4145 | | in this vector will be the Idx of the atom in the original OBMol |
4146 | | that corresponds to the first atom in the new OBMol. Note that |
4147 | | the information is appended to this vector. |
4148 | | \param bondorder Record the Idxs of the original bonds. See \p atomorder above. |
4149 | | |
4150 | | **/ |
4151 | | |
4152 | | bool OBMol::CopySubstructure(OBMol& newmol, OBBitVec *atoms, OBBitVec *excludebonds, unsigned int correctvalence, |
4153 | | std::vector<unsigned int> *atomorder, std::vector<unsigned int> *bondorder) |
4154 | 0 | { |
4155 | 0 | if (!atoms) |
4156 | 0 | return false; |
4157 | | |
4158 | 0 | bool record_atomorder = atomorder != nullptr; |
4159 | 0 | bool record_bondorder = bondorder != nullptr; |
4160 | 0 | bool bonds_specified = excludebonds != nullptr; |
4161 | |
|
4162 | 0 | newmol.SetDimension(GetDimension()); |
4163 | | |
4164 | | // If the parent is set to periodic, then also apply boundary conditions to the fragments |
4165 | 0 | if (IsPeriodic()) { |
4166 | 0 | OBUnitCell* parent_uc = (OBUnitCell*)GetData(OBGenericDataType::UnitCell); |
4167 | 0 | newmol.SetData(parent_uc->Clone(nullptr)); |
4168 | 0 | newmol.SetPeriodicMol(); |
4169 | 0 | } |
4170 | | // If the parent had aromaticity perceived, then retain that for the fragment |
4171 | 0 | newmol.SetFlag(_flags & OB_AROMATIC_MOL); |
4172 | | // The fragment will preserve the "chains perceived" flag of the parent |
4173 | 0 | newmol.SetFlag(_flags & OB_CHAINS_MOL); |
4174 | | // We will check for residues only if the parent has chains perceived already |
4175 | 0 | bool checkresidues = HasChainsPerceived(); |
4176 | | |
4177 | | // Now add the atoms |
4178 | 0 | map<OBAtom*, OBAtom*> AtomMap;//key is from old mol; value from new mol |
4179 | 0 | for (int bit = atoms->FirstBit(); bit != atoms->EndBit(); bit = atoms->NextBit(bit)) { |
4180 | 0 | OBAtom* atom = this->GetAtom(bit); |
4181 | 0 | if (!atom) |
4182 | 0 | return false; |
4183 | 0 | newmol.AddAtom(*atom); // each subsequent atom |
4184 | 0 | if (record_atomorder) |
4185 | 0 | atomorder->push_back(bit); |
4186 | 0 | AtomMap[&*atom] = newmol.GetAtom(newmol.NumAtoms()); |
4187 | 0 | } |
4188 | | |
4189 | | //Add the residues |
4190 | 0 | if (checkresidues) { |
4191 | 0 | map<OBResidue*, OBResidue*> ResidueMap; // map from old->new |
4192 | 0 | for (int bit = atoms->FirstBit(); bit != atoms->EndBit(); bit = atoms->NextBit(bit)) { |
4193 | 0 | OBAtom* atom = this->GetAtom(bit); |
4194 | 0 | OBResidue* res = atom->GetResidue(); |
4195 | 0 | if (!res) continue; |
4196 | 0 | map<OBResidue*, OBResidue*>::iterator mit = ResidueMap.find(res); |
4197 | 0 | OBResidue *newres; |
4198 | 0 | if (mit == ResidueMap.end()) { |
4199 | 0 | newres = newmol.NewResidue(); |
4200 | 0 | *newres = *res; |
4201 | 0 | ResidueMap[res] = newres; |
4202 | 0 | } else { |
4203 | 0 | newres = mit->second; |
4204 | 0 | } |
4205 | 0 | OBAtom* newatom = AtomMap[&*atom]; |
4206 | 0 | newres->AddAtom(newatom); |
4207 | 0 | newres->SetAtomID(newatom, res->GetAtomID(atom)); |
4208 | 0 | newres->SetHetAtom(newatom, res->IsHetAtom(atom)); |
4209 | 0 | newres->SetSerialNum(newatom, res->GetSerialNum(atom)); |
4210 | 0 | } |
4211 | 0 | } |
4212 | | |
4213 | | // Update Stereo |
4214 | 0 | std::vector<OBGenericData*>::iterator data; |
4215 | 0 | std::vector<OBGenericData*> stereoData = GetAllData(OBGenericDataType::StereoData); |
4216 | 0 | for (data = stereoData.begin(); data != stereoData.end(); ++data) { |
4217 | 0 | if (static_cast<OBStereoBase*>(*data)->GetType() == OBStereo::CisTrans) { |
4218 | 0 | OBCisTransStereo *ct = dynamic_cast<OBCisTransStereo*>(*data); |
4219 | | |
4220 | | // Check that the entirety of this cistrans cfg occurs in this substructure |
4221 | 0 | OBCisTransStereo::Config cfg = ct->GetConfig(); |
4222 | 0 | OBAtom* begin = GetAtomById(cfg.begin); |
4223 | 0 | if (AtomMap.find(begin) == AtomMap.end()) |
4224 | 0 | continue; |
4225 | 0 | OBAtom* end = GetAtomById(cfg.end); |
4226 | 0 | if (AtomMap.find(end) == AtomMap.end()) |
4227 | 0 | continue; |
4228 | 0 | bool skip_cfg = false; |
4229 | 0 | if (bonds_specified) { |
4230 | 0 | FOR_BONDS_OF_ATOM(bond, begin) { |
4231 | 0 | if (excludebonds->BitIsSet(bond->GetIdx())) { |
4232 | 0 | skip_cfg = true; |
4233 | 0 | break; |
4234 | 0 | } |
4235 | 0 | } |
4236 | 0 | if (skip_cfg) |
4237 | 0 | continue; |
4238 | 0 | FOR_BONDS_OF_ATOM(bond, end) { |
4239 | 0 | if (excludebonds->BitIsSet(bond->GetIdx())) { |
4240 | 0 | skip_cfg = true; |
4241 | 0 | break; |
4242 | 0 | } |
4243 | 0 | } |
4244 | 0 | if (skip_cfg) |
4245 | 0 | continue; |
4246 | 0 | } |
4247 | 0 | for (OBStereo::RefIter ri = cfg.refs.begin(); ri != cfg.refs.end(); ++ri) { |
4248 | 0 | if (*ri != OBStereo::ImplicitRef && AtomMap.find(GetAtomById(*ri)) == AtomMap.end()) { |
4249 | 0 | skip_cfg = true; |
4250 | 0 | break; |
4251 | 0 | } |
4252 | 0 | } |
4253 | 0 | if (skip_cfg) |
4254 | 0 | continue; |
4255 | | |
4256 | 0 | OBCisTransStereo::Config newcfg; |
4257 | 0 | newcfg.specified = cfg.specified; |
4258 | 0 | newcfg.begin = cfg.begin == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(cfg.begin)]->GetId(); |
4259 | 0 | newcfg.end = cfg.end == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(cfg.end)]->GetId(); |
4260 | 0 | OBStereo::Refs refs; |
4261 | 0 | for (OBStereo::RefIter ri = cfg.refs.begin(); ri != cfg.refs.end(); ++ri) { |
4262 | 0 | OBStereo::Ref ref = *ri == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(*ri)]->GetId(); |
4263 | 0 | refs.push_back(ref); |
4264 | 0 | } |
4265 | 0 | newcfg.refs = refs; |
4266 | |
|
4267 | 0 | OBCisTransStereo *newct = new OBCisTransStereo(this); |
4268 | 0 | newct->SetConfig(newcfg); |
4269 | 0 | newmol.SetData(newct); |
4270 | 0 | } |
4271 | 0 | else if (static_cast<OBStereoBase*>(*data)->GetType() == OBStereo::Tetrahedral) { |
4272 | 0 | OBTetrahedralStereo *tet = dynamic_cast<OBTetrahedralStereo*>(*data); |
4273 | 0 | OBTetrahedralStereo::Config cfg = tet->GetConfig(); |
4274 | | |
4275 | | // Check that the entirety of this tet cfg occurs in this substructure |
4276 | 0 | OBAtom *center = GetAtomById(cfg.center); |
4277 | 0 | std::map<OBAtom*, OBAtom*>::iterator centerit = AtomMap.find(center); |
4278 | 0 | if (centerit == AtomMap.end()) |
4279 | 0 | continue; |
4280 | 0 | if (cfg.from != OBStereo::ImplicitRef && AtomMap.find(GetAtomById(cfg.from)) == AtomMap.end()) |
4281 | 0 | continue; |
4282 | 0 | bool skip_cfg = false; |
4283 | 0 | if (bonds_specified) { |
4284 | 0 | FOR_BONDS_OF_ATOM(bond, center) { |
4285 | 0 | if (excludebonds->BitIsSet(bond->GetIdx())) { |
4286 | 0 | skip_cfg = true; |
4287 | 0 | break; |
4288 | 0 | } |
4289 | 0 | } |
4290 | 0 | if (skip_cfg) |
4291 | 0 | continue; |
4292 | 0 | } |
4293 | 0 | for (OBStereo::RefIter ri = cfg.refs.begin(); ri != cfg.refs.end(); ++ri) { |
4294 | 0 | if (*ri != OBStereo::ImplicitRef && AtomMap.find(GetAtomById(*ri)) == AtomMap.end()) { |
4295 | 0 | skip_cfg = true; |
4296 | 0 | break; |
4297 | 0 | } |
4298 | 0 | } |
4299 | 0 | if (skip_cfg) |
4300 | 0 | continue; |
4301 | | |
4302 | 0 | OBTetrahedralStereo::Config newcfg; |
4303 | 0 | newcfg.specified = cfg.specified; |
4304 | 0 | newcfg.center = centerit->second->GetId(); |
4305 | 0 | newcfg.from = cfg.from == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(cfg.from)]->GetId(); |
4306 | 0 | OBStereo::Refs refs; |
4307 | 0 | for (OBStereo::RefIter ri = cfg.refs.begin(); ri != cfg.refs.end(); ++ri) { |
4308 | 0 | OBStereo::Ref ref = *ri == OBStereo::ImplicitRef ? OBStereo::ImplicitRef : AtomMap[GetAtomById(*ri)]->GetId(); |
4309 | 0 | refs.push_back(ref); |
4310 | 0 | } |
4311 | 0 | newcfg.refs = refs; |
4312 | |
|
4313 | 0 | OBTetrahedralStereo *newtet = new OBTetrahedralStereo(this); |
4314 | 0 | newtet->SetConfig(newcfg); |
4315 | 0 | newmol.SetData(newtet); |
4316 | 0 | } |
4317 | 0 | } |
4318 | | |
4319 | | // Options: |
4320 | | // 1. Bonds that do not connect atoms in the subset are ignored |
4321 | | // 2. As 1. but implicit Hs are added to replace them |
4322 | | // 3. As 1. but asterisks are added to replace them |
4323 | 0 | FOR_BONDS_OF_MOL(bond, this) { |
4324 | 0 | bool skipping_bond = bonds_specified && excludebonds->BitIsSet(bond->GetIdx()); |
4325 | 0 | map<OBAtom*, OBAtom*>::iterator posB = AtomMap.find(bond->GetBeginAtom()); |
4326 | 0 | map<OBAtom*, OBAtom*>::iterator posE = AtomMap.find(bond->GetEndAtom()); |
4327 | 0 | if (posB == AtomMap.end() && posE == AtomMap.end()) |
4328 | 0 | continue; |
4329 | | |
4330 | 0 | if (posB == AtomMap.end() || posE == AtomMap.end() || skipping_bond) { |
4331 | 0 | switch(correctvalence) { |
4332 | 0 | case 1: |
4333 | 0 | if (posB == AtomMap.end() || (skipping_bond && posE != AtomMap.end())) |
4334 | 0 | posE->second->SetImplicitHCount(posE->second->GetImplicitHCount() + bond->GetBondOrder()); |
4335 | 0 | if (posE == AtomMap.end() || (skipping_bond && posB != AtomMap.end())) |
4336 | 0 | posB->second->SetImplicitHCount(posB->second->GetImplicitHCount() + bond->GetBondOrder()); |
4337 | 0 | break; |
4338 | 0 | case 2: { |
4339 | 0 | OBAtom *atomB, *atomE; |
4340 | 0 | if (skipping_bond) { |
4341 | 0 | for(int N=0; N<2; ++N) { |
4342 | 0 | atomB = nullptr; |
4343 | 0 | atomE = nullptr; |
4344 | 0 | if (N==0) { |
4345 | 0 | if (posB != AtomMap.end()) { |
4346 | 0 | atomB = posB->second; |
4347 | 0 | atomE = newmol.NewAtom(); |
4348 | 0 | if (record_atomorder) |
4349 | 0 | atomorder->push_back(bond->GetEndAtomIdx()); |
4350 | 0 | } |
4351 | 0 | } else if (posE != AtomMap.end()) { |
4352 | 0 | atomE = posE->second; |
4353 | 0 | atomB = newmol.NewAtom(); |
4354 | 0 | if (record_atomorder) |
4355 | 0 | atomorder->push_back(bond->GetBeginAtomIdx()); |
4356 | 0 | } |
4357 | 0 | if (atomB == nullptr || atomE == nullptr) |
4358 | 0 | continue; |
4359 | 0 | newmol.AddBond(atomB->GetIdx(), atomE->GetIdx(), |
4360 | 0 | bond->GetBondOrder(), bond->GetFlags()); |
4361 | 0 | if (record_bondorder) |
4362 | 0 | bondorder->push_back(bond->GetIdx()); |
4363 | 0 | } |
4364 | 0 | } |
4365 | 0 | else { |
4366 | 0 | atomB = (posB == AtomMap.end()) ? newmol.NewAtom() : posB->second; |
4367 | 0 | atomE = (posE == AtomMap.end()) ? newmol.NewAtom() : posE->second; |
4368 | 0 | if (record_atomorder) { |
4369 | 0 | if (posB == AtomMap.end()) |
4370 | 0 | atomorder->push_back(bond->GetBeginAtomIdx()); |
4371 | 0 | else |
4372 | 0 | atomorder->push_back(bond->GetEndAtomIdx()); |
4373 | 0 | } |
4374 | 0 | newmol.AddBond(atomB->GetIdx(), atomE->GetIdx(), |
4375 | 0 | bond->GetBondOrder(), bond->GetFlags()); |
4376 | 0 | if (record_bondorder) |
4377 | 0 | bondorder->push_back(bond->GetIdx()); |
4378 | 0 | } |
4379 | 0 | } |
4380 | 0 | break; |
4381 | 0 | default: |
4382 | 0 | break; |
4383 | 0 | } |
4384 | 0 | } |
4385 | 0 | else { |
4386 | 0 | newmol.AddBond((posB->second)->GetIdx(), posE->second->GetIdx(), |
4387 | 0 | bond->GetBondOrder(), bond->GetFlags()); |
4388 | 0 | if (record_bondorder) |
4389 | 0 | bondorder->push_back(bond->GetIdx()); |
4390 | 0 | } |
4391 | 0 | } |
4392 | | |
4393 | 0 | return true; |
4394 | 0 | } |
4395 | | |
4396 | 0 | bool OBMol::GetNextFragment( OBMolAtomDFSIter& iter, OBMol& newmol ) { |
4397 | 0 | if( ! iter ) return false; |
4398 | | |
4399 | | // We want to keep the atoms in their original order rather than use |
4400 | | // the DFS order so just record the information first |
4401 | 0 | OBBitVec infragment(this->NumAtoms()+1); |
4402 | 0 | do { //for each atom in fragment |
4403 | 0 | infragment.SetBitOn(iter->GetIdx()); |
4404 | 0 | } while ((iter++).next()); |
4405 | |
|
4406 | 0 | bool ok = CopySubstructure(newmol, &infragment); |
4407 | |
|
4408 | 0 | return ok; |
4409 | 0 | } |
4410 | | |
4411 | | // Put the specified molecular charge on a single atom (which is expected for InChIFormat). |
4412 | | // Assumes all the hydrogen is explicitly included in the molecule, |
4413 | | // and that SetTotalCharge() has not been called. (This function is an alternative.) |
4414 | | // Returns false if cannot assign all the charge. |
4415 | | // Not robust in the general case, but see below for the more common simpler cases. |
4416 | | bool OBMol::AssignTotalChargeToAtoms(int charge) |
4417 | 0 | { |
4418 | 0 | int extraCharge = charge - GetTotalCharge(); //GetTotalCharge() gets charge on atoms |
4419 | |
|
4420 | 0 | FOR_ATOMS_OF_MOL (atom, this) |
4421 | 0 | { |
4422 | 0 | unsigned int atomicnum = atom->GetAtomicNum(); |
4423 | 0 | if (atomicnum == 1) |
4424 | 0 | continue; |
4425 | 0 | int charge = atom->GetFormalCharge(); |
4426 | 0 | unsigned bosum = atom->GetExplicitValence(); |
4427 | 0 | unsigned int totalValence = bosum + atom->GetImplicitHCount(); |
4428 | 0 | unsigned int typicalValence = GetTypicalValence(atomicnum, bosum, charge); |
4429 | 0 | int diff = typicalValence - totalValence; |
4430 | 0 | if(diff != 0) |
4431 | 0 | { |
4432 | 0 | int c; |
4433 | 0 | if(extraCharge == 0) |
4434 | 0 | c = diff > 0 ? -1 : +1; //e.g. CH3C(=O)O, NH4 respectively |
4435 | 0 | else |
4436 | 0 | c = extraCharge < 0 ? -1 : 1; |
4437 | 0 | if (totalValence == GetTypicalValence(atomicnum, bosum, charge + c)) { |
4438 | 0 | atom->SetFormalCharge(charge + c); |
4439 | 0 | extraCharge -= c; |
4440 | 0 | } |
4441 | 0 | } |
4442 | 0 | } |
4443 | 0 | if(extraCharge != 0) |
4444 | 0 | { |
4445 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Unable to assign all the charge to atoms", obWarning); |
4446 | 0 | return false; |
4447 | 0 | } |
4448 | 0 | return true; |
4449 | 0 | } |
4450 | | /* These cases work ok |
4451 | | original charge result |
4452 | | [NH4] +1 [NH4+] |
4453 | | -C(=O)[O] -1 -C(=O)[O-] |
4454 | | -[CH2] +1 -C[CH2+] |
4455 | | -[CH2] -1 -C[CH2-] |
4456 | | [NH3]CC(=O)[O] 0 [NH3+]CC(=O)[O-] |
4457 | | S(=O)(=O)([O])[O] -2 S(=O)(=O)([O-])[O-] |
4458 | | [NH4].[Cl] 0 [NH4+].[Cl-] |
4459 | | */ |
4460 | | |
4461 | | } // end namespace OpenBabel |
4462 | | |
4463 | | //! \file mol.cpp |
4464 | | //! \brief Handle molecules. Implementation of OBMol. |