source: molecuilder/src/molecule_graph.cpp@ f444ce

Last change on this file since f444ce was f444ce, checked in by Frederik Heber <heber@…>, 16 years ago

Shifted molecuke::InitComponentNumbers() to atom::InitComponentNr.

Note that this is still only possible due to ListOfBondsPerAtom() -> atom::ListOfBonds() switch.

  • Property mode set to 100644
File size: 52.7 KB
Line 
1/*
2 * molecule_graph.cpp
3 *
4 * Created on: Oct 5, 2009
5 * Author: heber
6 */
7
8#include "atom.hpp"
9#include "bond.hpp"
10#include "config.hpp"
11#include "element.hpp"
12#include "helpers.hpp"
13#include "linkedcell.hpp"
14#include "lists.hpp"
15#include "memoryallocator.hpp"
16#include "molecule.hpp"
17
18/************************************* Functions for class molecule *********************************/
19
20
21/** Creates an adjacency list of the molecule.
22 * We obtain an outside file with the indices of atoms which are bondmembers.
23 */
24void molecule::CreateAdjacencyListFromDbondFile(ofstream *out, ifstream *input)
25{
26
27 // 1 We will parse bonds out of the dbond file created by tremolo.
28 int atom1, atom2;
29 atom *Walker, *OtherWalker;
30
31 if (!input)
32 {
33 cout << Verbose(1) << "Opening silica failed \n";
34 };
35
36 *input >> ws >> atom1;
37 *input >> ws >> atom2;
38 cout << Verbose(1) << "Scanning file\n";
39 while (!input->eof()) // Check whether we read everything already
40 {
41 *input >> ws >> atom1;
42 *input >> ws >> atom2;
43
44 if(atom2<atom1) //Sort indices of atoms in order
45 flip(atom1, atom2);
46 Walker=FindAtom(atom1);
47 OtherWalker=FindAtom(atom2);
48 AddBond(Walker, OtherWalker); //Add the bond between the two atoms with respective indices.
49 }
50};
51
52
53/** Creates an adjacency list of the molecule.
54 * Generally, we use the CSD approach to bond recognition, that is the the distance
55 * between two atoms A and B must be within [Rcov(A)+Rcov(B)-t,Rcov(A)+Rcov(B)+t] with
56 * a threshold t = 0.4 Angstroem.
57 * To make it O(N log N) the function uses the linked-cell technique as follows:
58 * The procedure is step-wise:
59 * -# Remove every bond in list
60 * -# Count the atoms in the molecule with CountAtoms()
61 * -# partition cell into smaller linked cells of size \a bonddistance
62 * -# put each atom into its corresponding cell
63 * -# go through every cell, check the atoms therein against all possible bond partners in the 27 adjacent cells, add bond if true
64 * -# correct the bond degree iteratively (single->double->triple bond)
65 * -# finally print the bond list to \a *out if desired
66 * \param *out out stream for printing the matrix, NULL if no output
67 * \param bonddistance length of linked cells (i.e. maximum minimal length checked)
68 * \param IsAngstroem whether coordinate system is gauged to Angstroem or Bohr radii
69 */
70void molecule::CreateAdjacencyList(ofstream *out, double bonddistance, bool IsAngstroem)
71{
72
73 atom *Walker = NULL;
74 atom *OtherWalker = NULL;
75 atom **AtomMap = NULL;
76 int n[NDIM];
77 double distance, MinDistance, MaxDistance;
78 LinkedCell *LC = NULL;
79 LinkedNodes *List = NULL;
80 LinkedNodes *OtherList = NULL;
81
82 BondDistance = bonddistance; // * ((IsAngstroem) ? 1. : 1./AtomicLengthToAngstroem);
83 *out << Verbose(0) << "Begin of CreateAdjacencyList." << endl;
84 // remove every bond from the list
85 if ((first->next != last) && (last->previous != first)) { // there are bonds present
86 cleanup(first,last);
87 }
88
89 // count atoms in molecule = dimension of matrix (also give each unique name and continuous numbering)
90 CountAtoms(out);
91 *out << Verbose(1) << "AtomCount " << AtomCount << "." << endl;
92
93 if (AtomCount != 0) {
94 LC = new LinkedCell(this, bonddistance);
95
96 // create a list to map Tesselpoint::nr to atom *
97 AtomMap = Malloc<atom *>(AtomCount, "molecule::CreateAdjacencyList - **AtomCount");
98 Walker = start;
99 while (Walker->next != end) {
100 Walker = Walker->next;
101 AtomMap[Walker->nr] = Walker;
102 }
103
104 // 3a. go through every cell
105 for (LC->n[0] = 0; LC->n[0] < LC->N[0]; LC->n[0]++)
106 for (LC->n[1] = 0; LC->n[1] < LC->N[1]; LC->n[1]++)
107 for (LC->n[2] = 0; LC->n[2] < LC->N[2]; LC->n[2]++) {
108 List = LC->GetCurrentCell();
109 //*out << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << " containing " << List->size() << " points." << endl;
110 if (List != NULL) {
111 for (LinkedNodes::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
112 Walker = AtomMap[(*Runner)->nr];
113 //*out << Verbose(0) << "Current Atom is " << *Walker << "." << endl;
114 // 3c. check for possible bond between each atom in this and every one in the 27 cells
115 for (n[0]=-1;n[0]<=1;n[0]++)
116 for (n[1]=-1;n[1]<=1;n[1]++)
117 for (n[2]=-1;n[2]<=1;n[2]++) {
118 OtherList = LC->GetRelativeToCurrentCell(n);
119 //*out << Verbose(2) << "Current relative cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << " containing " << List->size() << " points." << endl;
120 if (OtherList != NULL) {
121 for (LinkedNodes::iterator OtherRunner = OtherList->begin(); OtherRunner != OtherList->end(); OtherRunner++) {
122 if ((*OtherRunner)->nr > Walker->nr) {
123 OtherWalker = AtomMap[(*OtherRunner)->nr];
124 //*out << Verbose(1) << "Checking distance " << OtherWalker->x.PeriodicDistanceSquared(&(Walker->x), cell_size) << " against typical bond length of " << bonddistance*bonddistance << "." << endl;
125 MinDistance = OtherWalker->type->CovalentRadius + Walker->type->CovalentRadius;
126 MinDistance *= (IsAngstroem) ? 1. : 1./AtomicLengthToAngstroem;
127 MaxDistance = MinDistance + BONDTHRESHOLD;
128 MinDistance -= BONDTHRESHOLD;
129 distance = OtherWalker->x.PeriodicDistanceSquared(&(Walker->x), cell_size);
130 if ((OtherWalker->father->nr > Walker->father->nr) && (distance <= MaxDistance*MaxDistance) && (distance >= MinDistance*MinDistance)) { // create bond if distance is smaller
131 //*out << Verbose(1) << "Adding Bond between " << *Walker << " and " << *OtherWalker << " in distance " << sqrt(distance) << "." << endl;
132 AddBond(Walker->father, OtherWalker->father, 1); // also increases molecule::BondCount
133 } else {
134 //*out << Verbose(1) << "Not Adding: Wrong label order or distance too great." << endl;
135 }
136 }
137 }
138 }
139 }
140 }
141 }
142 }
143 Free(&AtomMap);
144 delete(LC);
145 *out << Verbose(1) << "I detected " << BondCount << " bonds in the molecule with distance " << BondDistance << "." << endl;
146
147 // correct bond degree by comparing valence and bond degree
148 CorrectBondDegree(out);
149
150 // output bonds for debugging (if bond chain list was correctly installed)
151 ActOnAllAtoms( &atom::OutputBondOfAtom, out );
152 } else
153 *out << Verbose(1) << "AtomCount is " << AtomCount << ", thus no bonds, no connections!." << endl;
154 *out << Verbose(0) << "End of CreateAdjacencyList." << endl;
155};
156
157/** Prints a list of all bonds to \a *out.
158 * \param output stream
159 */
160void molecule::OutputBondsList(ofstream *out)
161{
162 *out << Verbose(1) << endl << "From contents of bond chain list:";
163 bond *Binder = first;
164 while(Binder->next != last) {
165 Binder = Binder->next;
166 *out << *Binder << "\t" << endl;
167 }
168 *out << endl;
169};
170
171/** correct bond degree by comparing valence and bond degree.
172 * correct Bond degree of each bond by checking both bond partners for a mismatch between valence and current sum of bond degrees,
173 * iteratively increase the one first where the other bond partner has the fewest number of bonds (i.e. in general bonds oxygene
174 * preferred over carbon bonds). Beforehand, we had picked the first mismatching partner, which lead to oxygenes with single instead of
175 * double bonds as was expected.
176 * \param *out output stream for debugging
177 * \return number of bonds that could not be corrected
178 */
179int molecule::CorrectBondDegree(ofstream *out)
180{
181 int No = 0;
182
183 if (BondCount != 0) {
184 *out << Verbose(1) << "Correcting Bond degree of each bond ... " << endl;
185 do {
186 No = SumPerAtom( &atom::CorrectBondDegree, out );
187 } while (No);
188 *out << " done." << endl;
189 } else {
190 *out << Verbose(1) << "BondCount is " << BondCount << ", no bonds between any of the " << AtomCount << " atoms." << endl;
191 }
192 *out << No << " bonds could not be corrected." << endl;
193
194 return (No);
195};
196
197/** Counts all cyclic bonds and returns their number.
198 * \note Hydrogen bonds can never by cyclic, thus no check for that
199 * \param *out output stream for debugging
200 * \return number opf cyclic bonds
201 */
202int molecule::CountCyclicBonds(ofstream *out)
203{
204 NoCyclicBonds = 0;
205 int *MinimumRingSize = NULL;
206 MoleculeLeafClass *Subgraphs = NULL;
207 class StackClass<bond *> *BackEdgeStack = NULL;
208 bond *Binder = first;
209 if ((Binder->next != last) && (Binder->next->Type == Undetermined)) {
210 *out << Verbose(0) << "No Depth-First-Search analysis performed so far, calling ..." << endl;
211 Subgraphs = DepthFirstSearchAnalysis(out, BackEdgeStack);
212 while (Subgraphs->next != NULL) {
213 Subgraphs = Subgraphs->next;
214 delete(Subgraphs->previous);
215 }
216 delete(Subgraphs);
217 delete[](MinimumRingSize);
218 }
219 while(Binder->next != last) {
220 Binder = Binder->next;
221 if (Binder->Cyclic)
222 NoCyclicBonds++;
223 }
224 delete(BackEdgeStack);
225 return NoCyclicBonds;
226};
227
228
229/** Returns Shading as a char string.
230 * \param color the Shading
231 * \return string of the flag
232 */
233string molecule::GetColor(enum Shading color)
234{
235 switch(color) {
236 case white:
237 return "white";
238 break;
239 case lightgray:
240 return "lightgray";
241 break;
242 case darkgray:
243 return "darkgray";
244 break;
245 case black:
246 return "black";
247 break;
248 default:
249 return "uncolored";
250 break;
251 };
252};
253
254void SetWalkersGraphNr(ofstream *out, bool &BackStepping, atom *&Walker, int &CurrentGraphNr, class StackClass<atom *> *&AtomStack)
255{
256 if (!BackStepping) { // if we don't just return from (8)
257 Walker->GraphNr = CurrentGraphNr;
258 Walker->LowpointNr = CurrentGraphNr;
259 *out << Verbose(1) << "Setting Walker[" << Walker->Name << "]'s number to " << Walker->GraphNr << " with Lowpoint " << Walker->LowpointNr << "." << endl;
260 AtomStack->Push(Walker);
261 CurrentGraphNr++;
262 }
263};
264
265void ProbeAlongUnusedBond(ofstream *out, molecule *mol, bond *&Binder, bool &BackStepping, atom *&Walker, class StackClass<bond *> *&BackEdgeStack)
266{
267 atom *OtherAtom = NULL;
268
269 do { // (3) if Walker has no unused egdes, go to (5)
270 BackStepping = false; // reset backstepping flag for (8)
271 if (Binder == NULL) // if we don't just return from (11), Binder is already set to next unused
272 Binder = mol->FindNextUnused(Walker);
273 if (Binder == NULL)
274 break;
275 *out << Verbose(2) << "Current Unused Bond is " << *Binder << "." << endl;
276 // (4) Mark Binder used, ...
277 Binder->MarkUsed(black);
278 OtherAtom = Binder->GetOtherAtom(Walker);
279 *out << Verbose(2) << "(4) OtherAtom is " << OtherAtom->Name << "." << endl;
280 if (OtherAtom->GraphNr != -1) {
281 // (4a) ... if "other" atom has been visited (GraphNr != 0), set lowpoint to minimum of both, go to (3)
282 Binder->Type = BackEdge;
283 BackEdgeStack->Push(Binder);
284 Walker->LowpointNr = ( Walker->LowpointNr < OtherAtom->GraphNr ) ? Walker->LowpointNr : OtherAtom->GraphNr;
285 *out << Verbose(3) << "(4a) Visited: Setting Lowpoint of Walker[" << Walker->Name << "] to " << Walker->LowpointNr << "." << endl;
286 } else {
287 // (4b) ... otherwise set OtherAtom as Ancestor of Walker and Walker as OtherAtom, go to (2)
288 Binder->Type = TreeEdge;
289 OtherAtom->Ancestor = Walker;
290 Walker = OtherAtom;
291 *out << Verbose(3) << "(4b) Not Visited: OtherAtom[" << OtherAtom->Name << "]'s Ancestor is now " << OtherAtom->Ancestor->Name << ", Walker is OtherAtom " << OtherAtom->Name << "." << endl;
292 break;
293 }
294 Binder = NULL;
295 } while (1); // (3)
296};
297
298void CheckForaNewComponent(ofstream *out, molecule *mol, bool &BackStepping, atom *&Walker, atom *&Root, int &ComponentNumber, class StackClass<atom *> *&AtomStack, MoleculeLeafClass *&LeafWalker )
299{
300 atom *OtherAtom = NULL;
301
302 // (5) if Ancestor of Walker is ...
303 *out << Verbose(1) << "(5) Number of Walker[" << Walker->Name << "]'s Ancestor[" << Walker->Ancestor->Name << "] is " << Walker->Ancestor->GraphNr << "." << endl;
304
305 if (Walker->Ancestor->GraphNr != Root->GraphNr) {
306 // (6) (Ancestor of Walker is not Root)
307 if (Walker->LowpointNr < Walker->Ancestor->GraphNr) {
308 // (6a) set Ancestor's Lowpoint number to minimum of of its Ancestor and itself, go to Step(8)
309 Walker->Ancestor->LowpointNr = (Walker->Ancestor->LowpointNr < Walker->LowpointNr) ? Walker->Ancestor->LowpointNr : Walker->LowpointNr;
310 *out << Verbose(2) << "(6) Setting Walker[" << Walker->Name << "]'s Ancestor[" << Walker->Ancestor->Name << "]'s Lowpoint to " << Walker->Ancestor->LowpointNr << "." << endl;
311 } else {
312 // (7) (Ancestor of Walker is a separating vertex, remove all from stack till Walker (including), these and Ancestor form a component
313 Walker->Ancestor->SeparationVertex = true;
314 *out << Verbose(2) << "(7) Walker[" << Walker->Name << "]'s Ancestor[" << Walker->Ancestor->Name << "]'s is a separating vertex, creating component." << endl;
315 mol->SetNextComponentNumber(Walker->Ancestor, ComponentNumber);
316 *out << Verbose(3) << "(7) Walker[" << Walker->Name << "]'s Ancestor's Compont is " << ComponentNumber << "." << endl;
317 mol->SetNextComponentNumber(Walker, ComponentNumber);
318 *out << Verbose(3) << "(7) Walker[" << Walker->Name << "]'s Compont is " << ComponentNumber << "." << endl;
319 do {
320 OtherAtom = AtomStack->PopLast();
321 LeafWalker->Leaf->AddCopyAtom(OtherAtom);
322 mol->SetNextComponentNumber(OtherAtom, ComponentNumber);
323 *out << Verbose(3) << "(7) Other[" << OtherAtom->Name << "]'s Compont is " << ComponentNumber << "." << endl;
324 } while (OtherAtom != Walker);
325 ComponentNumber++;
326 }
327 // (8) Walker becomes its Ancestor, go to (3)
328 *out << Verbose(2) << "(8) Walker[" << Walker->Name << "] is now its Ancestor " << Walker->Ancestor->Name << ", backstepping. " << endl;
329 Walker = Walker->Ancestor;
330 BackStepping = true;
331 }
332};
333
334void CleanRootStackDownTillWalker(ofstream *out, molecule *mol, bool &BackStepping, atom *&Root, atom *&Walker, bond *&Binder, int &ComponentNumber, class StackClass<atom *> *&AtomStack, MoleculeLeafClass *&LeafWalker)
335{
336 atom *OtherAtom = NULL;
337
338 if (!BackStepping) { // coming from (8) want to go to (3)
339 // (9) remove all from stack till Walker (including), these and Root form a component
340 AtomStack->Output(out);
341 mol->SetNextComponentNumber(Root, ComponentNumber);
342 *out << Verbose(3) << "(9) Root[" << Root->Name << "]'s Component is " << ComponentNumber << "." << endl;
343 mol->SetNextComponentNumber(Walker, ComponentNumber);
344 *out << Verbose(3) << "(9) Walker[" << Walker->Name << "]'s Component is " << ComponentNumber << "." << endl;
345 do {
346 OtherAtom = AtomStack->PopLast();
347 LeafWalker->Leaf->AddCopyAtom(OtherAtom);
348 mol->SetNextComponentNumber(OtherAtom, ComponentNumber);
349 *out << Verbose(3) << "(7) Other[" << OtherAtom->Name << "]'s Compont is " << ComponentNumber << "." << endl;
350 } while (OtherAtom != Walker);
351 ComponentNumber++;
352
353 // (11) Root is separation vertex, set Walker to Root and go to (4)
354 Walker = Root;
355 Binder = mol->FindNextUnused(Walker);
356 *out << Verbose(1) << "(10) Walker is Root[" << Root->Name << "], next Unused Bond is " << Binder << "." << endl;
357 if (Binder != NULL) { // Root is separation vertex
358 *out << Verbose(1) << "(11) Root is a separation vertex." << endl;
359 Walker->SeparationVertex = true;
360 }
361 }
362};
363
364
365/** Performs a Depth-First search on this molecule.
366 * Marks bonds in molecule as cyclic, bridge, ... and atoms as
367 * articulations points, ...
368 * We use the algorithm from [Even, Graph Algorithms, p.62].
369 * \param *out output stream for debugging
370 * \param *&BackEdgeStack NULL pointer to StackClass with all the found back edges, allocated and filled on return
371 * \return list of each disconnected subgraph as an individual molecule class structure
372 */
373MoleculeLeafClass * molecule::DepthFirstSearchAnalysis(ofstream *out, class StackClass<bond *> *&BackEdgeStack)
374{
375 class StackClass<atom *> *AtomStack = new StackClass<atom *>(AtomCount);
376 BackEdgeStack = new StackClass<bond *> (BondCount);
377 MoleculeLeafClass *SubGraphs = new MoleculeLeafClass(NULL);
378 MoleculeLeafClass *LeafWalker = SubGraphs;
379 int CurrentGraphNr = 0, OldGraphNr;
380 int ComponentNumber = 0;
381 atom *Walker = NULL;
382 atom *Root = start->next;
383 bond *Binder = NULL;
384 bool BackStepping = false;
385
386 *out << Verbose(0) << "Begin of DepthFirstSearchAnalysis" << endl;
387
388 ResetAllBondsToUnused();
389 ResetAllAtomNumbers();
390 ActOnAllAtoms( &atom::InitComponentNr );
391 BackEdgeStack->ClearStack();
392 while (Root != end) { // if there any atoms at all
393 // (1) mark all edges unused, empty stack, set atom->GraphNr = 0 for all
394 AtomStack->ClearStack();
395
396 // put into new subgraph molecule and add this to list of subgraphs
397 LeafWalker = new MoleculeLeafClass(LeafWalker);
398 LeafWalker->Leaf = new molecule(elemente);
399 LeafWalker->Leaf->AddCopyAtom(Root);
400
401 OldGraphNr = CurrentGraphNr;
402 Walker = Root;
403 do { // (10)
404 do { // (2) set number and Lowpoint of Atom to i, increase i, push current atom
405 SetWalkersGraphNr(out, BackStepping, Walker, CurrentGraphNr, AtomStack);
406
407 ProbeAlongUnusedBond(out, this, Binder, BackStepping, Walker, BackEdgeStack);
408
409 if (Binder == NULL) {
410 *out << Verbose(2) << "No more Unused Bonds." << endl;
411 break;
412 } else
413 Binder = NULL;
414 } while (1); // (2)
415
416 // if we came from backstepping, yet there were no more unused bonds, we end up here with no Ancestor, because Walker is Root! Then we are finished!
417 if ((Walker == Root) && (Binder == NULL))
418 break;
419
420 CheckForaNewComponent(out, this, BackStepping, Walker, Root,ComponentNumber, AtomStack, LeafWalker );
421
422 CleanRootStackDownTillWalker(out, this, BackStepping, Root, Walker, Binder, ComponentNumber, AtomStack, LeafWalker);
423
424 } while ((BackStepping) || (Binder != NULL)); // (10) halt only if Root has no unused edges
425
426 // From OldGraphNr to CurrentGraphNr ranges an disconnected subgraph
427 *out << Verbose(0) << "Disconnected subgraph ranges from " << OldGraphNr << " to " << CurrentGraphNr << "." << endl;
428 LeafWalker->Leaf->Output(out);
429 *out << endl;
430
431 // step on to next root
432 while ((Root != end) && (Root->GraphNr != -1)) {
433 //*out << Verbose(1) << "Current next subgraph root candidate is " << Root->Name << "." << endl;
434 if (Root->GraphNr != -1) // if already discovered, step on
435 Root = Root->next;
436 }
437 }
438 // set cyclic bond criterium on "same LP" basis
439 CyclicBondAnalysis();
440
441 OutputGraphInfoPerAtom(out);
442
443 OutputGraphInfoPerBond(out);
444
445 // free all and exit
446 delete(AtomStack);
447 *out << Verbose(0) << "End of DepthFirstSearchAnalysis" << endl;
448 return SubGraphs;
449};
450
451/** Scans through all bonds and set bond::Cyclic to true where atom::LowpointNr of both ends is equal: LP criterion.
452 */
453void molecule::CyclicBondAnalysis()
454{
455 NoCyclicBonds = 0;
456 bond *Binder = first;
457 while(Binder->next != last) {
458 Binder = Binder->next;
459 if (Binder->rightatom->LowpointNr == Binder->leftatom->LowpointNr) { // cyclic ??
460 Binder->Cyclic = true;
461 NoCyclicBonds++;
462 }
463 }
464};
465
466/** Output graph information per atom.
467 * \param *out output stream
468 */
469void molecule::OutputGraphInfoPerAtom(ofstream *out)
470{
471 *out << Verbose(1) << "Final graph info for each atom is:" << endl;
472 ActOnAllAtoms( &atom::OutputGraphInfo, out );
473};
474
475/** Output graph information per bond.
476 * \param *out output stream
477 */
478void molecule::OutputGraphInfoPerBond(ofstream *out)
479{
480 *out << Verbose(1) << "Final graph info for each bond is:" << endl;
481 bond *Binder = first;
482 while(Binder->next != last) {
483 Binder = Binder->next;
484 *out << Verbose(2) << ((Binder->Type == TreeEdge) ? "TreeEdge " : "BackEdge ") << *Binder << ": <";
485 *out << ((Binder->leftatom->SeparationVertex) ? "SP," : "") << "L" << Binder->leftatom->LowpointNr << " G" << Binder->leftatom->GraphNr << " Comp.";
486 Binder->leftatom->OutputComponentNumber(out);
487 *out << " === ";
488 *out << ((Binder->rightatom->SeparationVertex) ? "SP," : "") << "L" << Binder->rightatom->LowpointNr << " G" << Binder->rightatom->GraphNr << " Comp.";
489 Binder->rightatom->OutputComponentNumber(out);
490 *out << ">." << endl;
491 if (Binder->Cyclic) // cyclic ??
492 *out << Verbose(3) << "Lowpoint at each side are equal: CYCLIC!" << endl;
493 }
494};
495
496/** initialise each vertex as white with no predecessor, empty queue, color Root lightgray.
497 *
498 */
499void InitializeAccounting(atom **&PredecessorList, int *&ShortestPathList, enum Shading *&ColorList, int AtomCount)
500{
501 for (int i=AtomCount;i--;) {
502 PredecessorList[i] = NULL;
503 ShortestPathList[i] = -1;
504 ColorList[i] = white;
505 }
506};
507
508void ResetAccounting(atom *&Walker, class StackClass<atom *> *&TouchedStack, int *&ShortestPathList, class StackClass<atom *> *&BFSStack)
509{
510 ShortestPathList[Walker->nr] = 0;
511 BFSStack->ClearStack(); // start with empty BFS stack
512 BFSStack->Push(Walker);
513 TouchedStack->Push(Walker);
514};
515
516void CyclicBFSFromRootToRoot(ofstream *out, atom *&Root, bond *&BackEdge, class StackClass<atom *> *&TouchedStack, int *&ShortestPathList, atom **&PredecessorList, class StackClass<atom *> *&BFSStack, enum Shading *&ColorList)
517{
518 atom *Walker = NULL;
519 atom *OtherAtom = NULL;
520 do { // look for Root
521 Walker = BFSStack->PopFirst();
522 *out << Verbose(2) << "Current Walker is " << *Walker << ", we look for SP to Root " << *Root << "." << endl;
523 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
524 if ((*Runner) != BackEdge) { // only walk along DFS spanning tree (otherwise we always find SP of one being backedge Binder)
525 OtherAtom = (*Runner)->GetOtherAtom(Walker);
526 #ifdef ADDHYDROGEN
527 if (OtherAtom->type->Z != 1) {
528 #endif
529 *out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *(*Runner) << "." << endl;
530 if (ColorList[OtherAtom->nr] == white) {
531 TouchedStack->Push(OtherAtom);
532 ColorList[OtherAtom->nr] = lightgray;
533 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
534 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
535 *out << Verbose(2) << "Coloring OtherAtom " << OtherAtom->Name << " lightgray, its predecessor is " << Walker->Name << " and its Shortest Path is " << ShortestPathList[OtherAtom->nr] << " egde(s) long." << endl;
536 //if (ShortestPathList[OtherAtom->nr] < MinimumRingSize[Walker->GetTrueFather()->nr]) { // Check for maximum distance
537 *out << Verbose(3) << "Putting OtherAtom into queue." << endl;
538 BFSStack->Push(OtherAtom);
539 //}
540 } else {
541 *out << Verbose(3) << "Not Adding, has already been visited." << endl;
542 }
543 if (OtherAtom == Root)
544 break;
545 #ifdef ADDHYDROGEN
546 } else {
547 *out << Verbose(2) << "Skipping hydrogen atom " << *OtherAtom << "." << endl;
548 ColorList[OtherAtom->nr] = black;
549 }
550 #endif
551 } else {
552 *out << Verbose(2) << "Bond " << *(*Runner) << " not Visiting, is the back edge." << endl;
553 }
554 }
555 ColorList[Walker->nr] = black;
556 *out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
557 if (OtherAtom == Root) { // if we have found the root, check whether this cycle wasn't already found beforehand
558 // step through predecessor list
559 while (OtherAtom != BackEdge->rightatom) {
560 if (!OtherAtom->GetTrueFather()->IsCyclic) // if one bond in the loop is not marked as cyclic, we haven't found this cycle yet
561 break;
562 else
563 OtherAtom = PredecessorList[OtherAtom->nr];
564 }
565 if (OtherAtom == BackEdge->rightatom) { // if each atom in found cycle is cyclic, loop's been found before already
566 *out << Verbose(3) << "This cycle was already found before, skipping and removing seeker from search." << endl;\
567 do {
568 OtherAtom = TouchedStack->PopLast();
569 if (PredecessorList[OtherAtom->nr] == Walker) {
570 *out << Verbose(4) << "Removing " << *OtherAtom << " from lists and stacks." << endl;
571 PredecessorList[OtherAtom->nr] = NULL;
572 ShortestPathList[OtherAtom->nr] = -1;
573 ColorList[OtherAtom->nr] = white;
574 BFSStack->RemoveItem(OtherAtom);
575 }
576 } while ((!TouchedStack->IsEmpty()) && (PredecessorList[OtherAtom->nr] == NULL));
577 TouchedStack->Push(OtherAtom); // last was wrongly popped
578 OtherAtom = BackEdge->rightatom; // set to not Root
579 } else
580 OtherAtom = Root;
581 }
582 } while ((!BFSStack->IsEmpty()) && (OtherAtom != Root) && (OtherAtom != NULL)); // || (ShortestPathList[OtherAtom->nr] < MinimumRingSize[Walker->GetTrueFather()->nr])));
583};
584
585void RetrieveCycleMembers(ofstream *out, atom *&Root, atom *&OtherAtom, bond *&BackEdge, atom **&PredecessorList, int *&MinimumRingSize, int &MinRingSize)
586{
587 atom *Walker = NULL;
588 int NumCycles = 0;
589 int RingSize = -1;
590
591 if (OtherAtom == Root) {
592 // now climb back the predecessor list and thus find the cycle members
593 NumCycles++;
594 RingSize = 1;
595 Root->GetTrueFather()->IsCyclic = true;
596 *out << Verbose(1) << "Found ring contains: ";
597 Walker = Root;
598 while (Walker != BackEdge->rightatom) {
599 *out << Walker->Name << " <-> ";
600 Walker = PredecessorList[Walker->nr];
601 Walker->GetTrueFather()->IsCyclic = true;
602 RingSize++;
603 }
604 *out << Walker->Name << " with a length of " << RingSize << "." << endl << endl;
605 // walk through all and set MinimumRingSize
606 Walker = Root;
607 MinimumRingSize[Walker->GetTrueFather()->nr] = RingSize;
608 while (Walker != BackEdge->rightatom) {
609 Walker = PredecessorList[Walker->nr];
610 if (RingSize < MinimumRingSize[Walker->GetTrueFather()->nr])
611 MinimumRingSize[Walker->GetTrueFather()->nr] = RingSize;
612 }
613 if ((RingSize < MinRingSize) || (MinRingSize == -1))
614 MinRingSize = RingSize;
615 } else {
616 *out << Verbose(1) << "No ring containing " << *Root << " with length equal to or smaller than " << MinimumRingSize[Walker->GetTrueFather()->nr] << " found." << endl;
617 }
618};
619
620void CleanAccounting(class StackClass<atom *> *&TouchedStack, atom **&PredecessorList, int *&ShortestPathList, enum Shading *&ColorList)
621{
622 atom *Walker = NULL;
623 while (!TouchedStack->IsEmpty()){
624 Walker = TouchedStack->PopFirst();
625 PredecessorList[Walker->nr] = NULL;
626 ShortestPathList[Walker->nr] = -1;
627 ColorList[Walker->nr] = white;
628 }
629};
630
631
632void BFSToNextCycle(ofstream *out, atom *&Root, atom *&Walker, bond *&BackEdge, int *&MinimumRingSize, int AtomCount)
633{
634 atom **PredecessorList = Malloc<atom*>(AtomCount, "molecule::CyclicStructureAnalysis: **PredecessorList");
635 int *ShortestPathList = Malloc<int>(AtomCount, "molecule::CyclicStructureAnalysis: *ShortestPathList");
636 enum Shading *ColorList = Malloc<enum Shading>(AtomCount, "molecule::CyclicStructureAnalysis: *ColorList");
637 class StackClass<atom *> *BFSStack = new StackClass<atom *> (AtomCount); // will hold the current ring
638 class StackClass<atom *> *TouchedStack = new StackClass<atom *> (AtomCount); // contains all "touched" atoms (that need to be reset after BFS loop)
639 atom *OtherAtom = Walker;
640
641 InitializeAccounting(PredecessorList, ShortestPathList, ColorList, AtomCount);
642
643 ResetAccounting(Walker, TouchedStack, ShortestPathList, BFSStack);
644 while (OtherAtom != NULL) { // look for Root
645 Walker = BFSStack->PopFirst();
646 //*out << Verbose(2) << "Current Walker is " << *Walker << ", we look for SP to Root " << *Root << "." << endl;
647 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
648 if (((*Runner) != BackEdge) || (Walker->ListOfBonds.size() == 1)) { // only walk along DFS spanning tree (otherwise we always find SP of 1 being backedge Binder), but terminal hydrogens may be connected via backedge, hence extra check
649 OtherAtom = (*Runner)->GetOtherAtom(Walker);
650 //*out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *Binder << "." << endl;
651 if (ColorList[OtherAtom->nr] == white) {
652 TouchedStack->Push(OtherAtom);
653 ColorList[OtherAtom->nr] = lightgray;
654 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
655 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
656 //*out << Verbose(2) << "Coloring OtherAtom " << OtherAtom->Name << " lightgray, its predecessor is " << Walker->Name << " and its Shortest Path is " << ShortestPathList[OtherAtom->nr] << " egde(s) long." << endl;
657 if (OtherAtom->GetTrueFather()->IsCyclic) { // if the other atom is connected to a ring
658 MinimumRingSize[Root->GetTrueFather()->nr] = ShortestPathList[OtherAtom->nr]+MinimumRingSize[OtherAtom->GetTrueFather()->nr];
659 OtherAtom = NULL; //break;
660 break;
661 } else
662 BFSStack->Push(OtherAtom);
663 } else {
664 //*out << Verbose(3) << "Not Adding, has already been visited." << endl;
665 }
666 } else {
667 //*out << Verbose(3) << "Not Visiting, is a back edge." << endl;
668 }
669 }
670 ColorList[Walker->nr] = black;
671 //*out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
672 }
673 //CleanAccountingLists(TouchedStack, PredecessorList, ShortestPathList, ColorList);
674
675 Free(&PredecessorList);
676 Free(&ShortestPathList);
677 Free(&ColorList);
678 delete(BFSStack);
679};
680
681void AssignRingSizetoNonCycleMembers(ofstream *out, int *&MinimumRingSize, int &MinRingSize, int &NumCycles, molecule *mol, bond *&BackEdge)
682{
683 atom *Root= NULL;
684 atom *Walker = NULL;
685 if (MinRingSize != -1) { // if rings are present
686 // go over all atoms
687 Root = mol->start;
688 while(Root->next != mol->end) {
689 Root = Root->next;
690
691 if (MinimumRingSize[Root->GetTrueFather()->nr] == mol->AtomCount) { // check whether MinimumRingSize is set, if not BFS to next where it is
692 Walker = Root;
693
694 //*out << Verbose(1) << "---------------------------------------------------------------------------------------------------------" << endl;
695 BFSToNextCycle(out, Root, Walker, BackEdge, MinimumRingSize, mol->AtomCount);
696
697 }
698 *out << Verbose(1) << "Minimum ring size of " << *Root << " is " << MinimumRingSize[Root->GetTrueFather()->nr] << "." << endl;
699 }
700 *out << Verbose(1) << "Minimum ring size is " << MinRingSize << ", over " << NumCycles << " cycles total." << endl;
701 } else
702 *out << Verbose(1) << "No rings were detected in the molecular structure." << endl;
703};
704
705/** Analyses the cycles found and returns minimum of all cycle lengths.
706 * We begin with a list of Back edges found during DepthFirstSearchAnalysis(). We go through this list - one end is the Root,
707 * the other our initial Walker - and do a Breadth First Search for the Root. We mark down each Predecessor and as soon as
708 * we have found the Root via BFS, we may climb back the closed cycle via the Predecessors. Thereby we mark atoms and bonds
709 * as cyclic and print out the cycles.
710 * \param *out output stream for debugging
711 * \param *BackEdgeStack stack with all back edges found during DFS scan. Beware: This stack contains the bonds from the total molecule, not from the subgraph!
712 * \param *&MinimumRingSize contains smallest ring size in molecular structure on return or -1 if no rings were found, if set is maximum search distance
713 * \todo BFS from the not-same-LP to find back to starting point of tributary cycle over more than one bond
714 */
715void molecule::CyclicStructureAnalysis(ofstream *out, class StackClass<bond *> * BackEdgeStack, int *&MinimumRingSize)
716{
717 atom **PredecessorList = Malloc<atom*>(AtomCount, "molecule::CyclicStructureAnalysis: **PredecessorList");
718 int *ShortestPathList = Malloc<int>(AtomCount, "molecule::CyclicStructureAnalysis: *ShortestPathList");
719 enum Shading *ColorList = Malloc<enum Shading>(AtomCount, "molecule::CyclicStructureAnalysis: *ColorList");
720 class StackClass<atom *> *BFSStack = new StackClass<atom *> (AtomCount); // will hold the current ring
721 class StackClass<atom *> *TouchedStack = new StackClass<atom *> (AtomCount); // contains all "touched" atoms (that need to be reset after BFS loop)
722 atom *Walker = NULL;
723 atom *OtherAtom = NULL;
724 atom *Root = NULL;
725 bond *BackEdge = NULL;
726 int NumCycles = 0;
727 int MinRingSize = -1;
728
729 InitializeAccounting(PredecessorList, ShortestPathList, ColorList, AtomCount);
730
731 *out << Verbose(1) << "Back edge list - ";
732 BackEdgeStack->Output(out);
733
734 *out << Verbose(1) << "Analysing cycles ... " << endl;
735 NumCycles = 0;
736 while (!BackEdgeStack->IsEmpty()) {
737 BackEdge = BackEdgeStack->PopFirst();
738 // this is the target
739 Root = BackEdge->leftatom;
740 // this is the source point
741 Walker = BackEdge->rightatom;
742
743 ResetAccounting(Walker, TouchedStack, ShortestPathList, BFSStack);
744
745 *out << Verbose(1) << "---------------------------------------------------------------------------------------------------------" << endl;
746 OtherAtom = NULL;
747 CyclicBFSFromRootToRoot(out, Root, BackEdge, TouchedStack, ShortestPathList, PredecessorList, BFSStack, ColorList);
748
749 RetrieveCycleMembers(out, Root, OtherAtom, BackEdge, PredecessorList, MinimumRingSize, MinRingSize);
750
751 CleanAccounting(TouchedStack, PredecessorList, ShortestPathList, ColorList);
752 }
753 Free(&PredecessorList);
754 Free(&ShortestPathList);
755 Free(&ColorList);
756 delete(BFSStack);
757
758 AssignRingSizetoNonCycleMembers(out, MinimumRingSize, MinRingSize, NumCycles, this, BackEdge);
759
760};
761
762/** Sets the next component number.
763 * This is O(N) as the number of bonds per atom is bound.
764 * \param *vertex atom whose next atom::*ComponentNr is to be set
765 * \param nr number to use
766 */
767void molecule::SetNextComponentNumber(atom *vertex, int nr)
768{
769 size_t i=0;
770 if (vertex != NULL) {
771 for(;i<vertex->ListOfBonds.size();i++) {
772 if (vertex->ComponentNr[i] == -1) { // check if not yet used
773 vertex->ComponentNr[i] = nr;
774 break;
775 }
776 else if (vertex->ComponentNr[i] == nr) // if number is already present, don't add another time
777 break; // breaking here will not cause error!
778 }
779 if (i == vertex->ListOfBonds.size())
780 cerr << "Error: All Component entries are already occupied!" << endl;
781 } else
782 cerr << "Error: Given vertex is NULL!" << endl;
783};
784
785/** Returns next unused bond for this atom \a *vertex or NULL of none exists.
786 * \param *vertex atom to regard
787 * \return bond class or NULL
788 */
789bond * molecule::FindNextUnused(atom *vertex)
790{
791 for (BondList::const_iterator Runner = vertex->ListOfBonds.begin(); Runner != vertex->ListOfBonds.end(); (++Runner))
792 if ((*Runner)->IsUsed() == white)
793 return((*Runner));
794 return NULL;
795};
796
797/** Resets bond::Used flag of all bonds in this molecule.
798 * \return true - success, false - -failure
799 */
800void molecule::ResetAllBondsToUnused()
801{
802 bond *Binder = first;
803 while (Binder->next != last) {
804 Binder = Binder->next;
805 Binder->ResetUsed();
806 }
807};
808
809/** Resets atom::nr to -1 of all atoms in this molecule.
810 */
811void molecule::ResetAllAtomNumbers()
812{
813 atom *Walker = start;
814 while (Walker->next != end) {
815 Walker = Walker->next;
816 Walker->GraphNr = -1;
817 }
818};
819
820/** Output a list of flags, stating whether the bond was visited or not.
821 * \param *out output stream for debugging
822 * \param *list
823 */
824void OutputAlreadyVisited(ofstream *out, int *list)
825{
826 *out << Verbose(4) << "Already Visited Bonds:\t";
827 for(int i=1;i<=list[0];i++) *out << Verbose(0) << list[i] << " ";
828 *out << endl;
829};
830
831
832/** Storing the bond structure of a molecule to file.
833 * Simply stores Atom::nr and then the Atom::nr of all bond partners per line.
834 * \param *out output stream for debugging
835 * \param *path path to file
836 * \return true - file written successfully, false - writing failed
837 */
838bool molecule::StoreAdjacencyToFile(ofstream *out, char *path)
839{
840 ofstream AdjacencyFile;
841 stringstream line;
842 bool status = true;
843
844 line << path << "/" << FRAGMENTPREFIX << ADJACENCYFILE;
845 AdjacencyFile.open(line.str().c_str(), ios::out);
846 *out << Verbose(1) << "Saving adjacency list ... ";
847 if (AdjacencyFile != NULL) {
848 ActOnAllAtoms( &atom::OutputAdjacency, &AdjacencyFile );
849 AdjacencyFile.close();
850 *out << Verbose(1) << "done." << endl;
851 } else {
852 *out << Verbose(1) << "failed to open file " << line.str() << "." << endl;
853 status = false;
854 }
855
856 return status;
857};
858
859/** Checks contents of adjacency file against bond structure in structure molecule.
860 * \param *out output stream for debugging
861 * \param *path path to file
862 * \param **ListOfAtoms allocated (molecule::AtomCount) and filled lookup table for ids (Atom::nr) to *Atom
863 * \return true - structure is equal, false - not equivalence
864 */
865bool molecule::CheckAdjacencyFileAgainstMolecule(ofstream *out, char *path, atom **ListOfAtoms)
866{
867 ifstream File;
868 stringstream filename;
869 bool status = true;
870 atom *Walker = NULL;
871 char *buffer = Malloc<char>(MAXSTRINGSIZE, "molecule::CheckAdjacencyFileAgainstMolecule: *buffer");
872
873 filename << path << "/" << FRAGMENTPREFIX << ADJACENCYFILE;
874 File.open(filename.str().c_str(), ios::out);
875 *out << Verbose(1) << "Looking at bond structure stored in adjacency file and comparing to present one ... ";
876 if (File != NULL) {
877 // allocate storage structure
878 int NonMatchNumber = 0; // will number of atoms with differing bond structure
879 int *CurrentBonds = Malloc<int>(8, "molecule::CheckAdjacencyFileAgainstMolecule - CurrentBonds"); // contains parsed bonds of current atom
880 size_t CurrentBondsOfAtom;
881
882 // Parse the file line by line and count the bonds
883 while (!File.eof()) {
884 File.getline(buffer, MAXSTRINGSIZE);
885 stringstream line;
886 line.str(buffer);
887 int AtomNr = -1;
888 line >> AtomNr;
889 CurrentBondsOfAtom = -1; // we count one too far due to line end
890 // parse into structure
891 if ((AtomNr >= 0) && (AtomNr < AtomCount)) {
892 Walker = ListOfAtoms[AtomNr];
893 while (!line.eof())
894 line >> CurrentBonds[ ++CurrentBondsOfAtom ];
895 // compare against present bonds
896 //cout << Verbose(2) << "Walker is " << *Walker << ", bond partners: ";
897 if (CurrentBondsOfAtom == Walker->ListOfBonds.size()) {
898 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
899 int id = (*Runner)->GetOtherAtom(Walker)->nr;
900 size_t j = 0;
901 for (;(j<CurrentBondsOfAtom) && (CurrentBonds[j++] != id);); // check against all parsed bonds
902 if (CurrentBonds[j-1] != id) { // no match ? Then mark in ListOfAtoms
903 ListOfAtoms[AtomNr] = NULL;
904 NonMatchNumber++;
905 status = false;
906 //out << "[" << id << "]\t";
907 } else {
908 //out << id << "\t";
909 }
910 }
911 //out << endl;
912 } else {
913 *out << "Number of bonds for Atom " << *Walker << " does not match, parsed " << CurrentBondsOfAtom << " against " << Walker->ListOfBonds.size() << "." << endl;
914 status = false;
915 }
916 }
917 }
918 File.close();
919 File.clear();
920 if (status) { // if equal we parse the KeySetFile
921 *out << Verbose(1) << "done: Equal." << endl;
922 status = true;
923 } else
924 *out << Verbose(1) << "done: Not equal by " << NonMatchNumber << " atoms." << endl;
925 Free(&CurrentBonds);
926 } else {
927 *out << Verbose(1) << "Adjacency file not found." << endl;
928 status = false;
929 }
930 *out << endl;
931 Free(&buffer);
932
933 return status;
934};
935
936
937/** Picks from a global stack with all back edges the ones in the fragment.
938 * \param *out output stream for debugging
939 * \param **ListOfLocalAtoms array of father atom::nr to local atom::nr (reverse of atom::father)
940 * \param *ReferenceStack stack with all the back egdes
941 * \param *LocalStack stack to be filled
942 * \return true - everything ok, false - ReferenceStack was empty
943 */
944bool molecule::PickLocalBackEdges(ofstream *out, atom **ListOfLocalAtoms, class StackClass<bond *> *&ReferenceStack, class StackClass<bond *> *&LocalStack)
945{
946 bool status = true;
947 if (ReferenceStack->IsEmpty()) {
948 cerr << "ReferenceStack is empty!" << endl;
949 return false;
950 }
951 bond *Binder = ReferenceStack->PopFirst();
952 bond *FirstBond = Binder; // mark the first bond, so that we don't loop through the stack indefinitely
953 atom *Walker = NULL, *OtherAtom = NULL;
954 ReferenceStack->Push(Binder);
955
956 do { // go through all bonds and push local ones
957 Walker = ListOfLocalAtoms[Binder->leftatom->nr]; // get one atom in the reference molecule
958 if (Walker != NULL) // if this Walker exists in the subgraph ...
959 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
960 OtherAtom = (*Runner)->GetOtherAtom(Walker);
961 if (OtherAtom == ListOfLocalAtoms[(*Runner)->rightatom->nr]) { // found the bond
962 LocalStack->Push((*Runner));
963 *out << Verbose(3) << "Found local edge " << *(*Runner) << "." << endl;
964 break;
965 }
966 }
967 Binder = ReferenceStack->PopFirst(); // loop the stack for next item
968 *out << Verbose(3) << "Current candidate edge " << Binder << "." << endl;
969 ReferenceStack->Push(Binder);
970 } while (FirstBond != Binder);
971
972 return status;
973};
974
975
976/** Adds atoms up to \a BondCount distance from \a *Root and notes them down in \a **AddedAtomList.
977 * Gray vertices are always enqueued in an StackClass<atom *> FIFO queue, the rest is usual BFS with adding vertices found was
978 * white and putting into queue.
979 * \param *out output stream for debugging
980 * \param *Mol Molecule class to add atoms to
981 * \param **AddedAtomList list with added atom pointers, index is atom father's number
982 * \param **AddedBondList list with added bond pointers, index is bond father's number
983 * \param *Root root vertex for BFS
984 * \param *Bond bond not to look beyond
985 * \param BondOrder maximum distance for vertices to add
986 * \param IsAngstroem lengths are in angstroem or bohrradii
987 */
988void molecule::BreadthFirstSearchAdd(ofstream *out, molecule *Mol, atom **&AddedAtomList, bond **&AddedBondList, atom *Root, bond *Bond, int BondOrder, bool IsAngstroem)
989{
990 atom **PredecessorList = Malloc<atom*>(AtomCount, "molecule::BreadthFirstSearchAdd: **PredecessorList");
991 int *ShortestPathList = Malloc<int>(AtomCount, "molecule::BreadthFirstSearchAdd: *ShortestPathList");
992 enum Shading *ColorList = Malloc<enum Shading>(AtomCount, "molecule::BreadthFirstSearchAdd: *ColorList");
993 class StackClass<atom *> *AtomStack = new StackClass<atom *>(AtomCount);
994 atom *Walker = NULL, *OtherAtom = NULL;
995
996 // add Root if not done yet
997 AtomStack->ClearStack();
998 if (AddedAtomList[Root->nr] == NULL) // add Root if not yet present
999 AddedAtomList[Root->nr] = Mol->AddCopyAtom(Root);
1000 AtomStack->Push(Root);
1001
1002 // initialise each vertex as white with no predecessor, empty queue, color Root lightgray
1003 for (int i=AtomCount;i--;) {
1004 PredecessorList[i] = NULL;
1005 ShortestPathList[i] = -1;
1006 if (AddedAtomList[i] != NULL) // mark already present atoms (i.e. Root and maybe others) as visited
1007 ColorList[i] = lightgray;
1008 else
1009 ColorList[i] = white;
1010 }
1011 ShortestPathList[Root->nr] = 0;
1012
1013 // and go on ... Queue always contains all lightgray vertices
1014 while (!AtomStack->IsEmpty()) {
1015 // we have to pop the oldest atom from stack. This keeps the atoms on the stack always of the same ShortestPath distance.
1016 // e.g. if current atom is 2, push to end of stack are of length 3, but first all of length 2 would be popped. They again
1017 // append length of 3 (their neighbours). Thus on stack we have always atoms of a certain length n at bottom of stack and
1018 // followed by n+1 till top of stack.
1019 Walker = AtomStack->PopFirst(); // pop oldest added
1020 *out << Verbose(1) << "Current Walker is: " << Walker->Name << ", and has " << Walker->ListOfBonds.size() << " bonds." << endl;
1021 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1022 if ((*Runner) != NULL) { // don't look at bond equal NULL
1023 OtherAtom = (*Runner)->GetOtherAtom(Walker);
1024 *out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *(*Runner) << "." << endl;
1025 if (ColorList[OtherAtom->nr] == white) {
1026 if ((*Runner) != Bond) // let other atom white if it's via Root bond. In case it's cyclic it has to be reached again (yet Root is from OtherAtom already black, thus no problem)
1027 ColorList[OtherAtom->nr] = lightgray;
1028 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
1029 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
1030 *out << Verbose(2) << "Coloring OtherAtom " << OtherAtom->Name << " " << ((ColorList[OtherAtom->nr] == white) ? "white" : "lightgray") << ", its predecessor is " << Walker->Name << " and its Shortest Path is " << ShortestPathList[OtherAtom->nr] << " egde(s) long." << endl;
1031 if ((((ShortestPathList[OtherAtom->nr] < BondOrder) && ((*Runner) != Bond))) ) { // Check for maximum distance
1032 *out << Verbose(3);
1033 if (AddedAtomList[OtherAtom->nr] == NULL) { // add if it's not been so far
1034 AddedAtomList[OtherAtom->nr] = Mol->AddCopyAtom(OtherAtom);
1035 *out << "Added OtherAtom " << OtherAtom->Name;
1036 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
1037 *out << " and bond " << *(AddedBondList[(*Runner)->nr]) << ", ";
1038 } else { // this code should actually never come into play (all white atoms are not yet present in BondMolecule, that's why they are white in the first place)
1039 *out << "Not adding OtherAtom " << OtherAtom->Name;
1040 if (AddedBondList[(*Runner)->nr] == NULL) {
1041 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
1042 *out << ", added Bond " << *(AddedBondList[(*Runner)->nr]);
1043 } else
1044 *out << ", not added Bond ";
1045 }
1046 *out << ", putting OtherAtom into queue." << endl;
1047 AtomStack->Push(OtherAtom);
1048 } else { // out of bond order, then replace
1049 if ((AddedAtomList[OtherAtom->nr] == NULL) && ((*Runner)->Cyclic))
1050 ColorList[OtherAtom->nr] = white; // unmark if it has not been queued/added, to make it available via its other bonds (cyclic)
1051 if ((*Runner) == Bond)
1052 *out << Verbose(3) << "Not Queueing, is the Root bond";
1053 else if (ShortestPathList[OtherAtom->nr] >= BondOrder)
1054 *out << Verbose(3) << "Not Queueing, is out of Bond Count of " << BondOrder;
1055 if (!(*Runner)->Cyclic)
1056 *out << ", is not part of a cyclic bond, saturating bond with Hydrogen." << endl;
1057 if (AddedBondList[(*Runner)->nr] == NULL) {
1058 if ((AddedAtomList[OtherAtom->nr] != NULL)) { // .. whether we add or saturate
1059 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
1060 } else {
1061#ifdef ADDHYDROGEN
1062 if (!Mol->AddHydrogenReplacementAtom(out, (*Runner), AddedAtomList[Walker->nr], Walker, OtherAtom, IsAngstroem))
1063 exit(1);
1064#endif
1065 }
1066 }
1067 }
1068 } else {
1069 *out << Verbose(3) << "Not Adding, has already been visited." << endl;
1070 // This has to be a cyclic bond, check whether it's present ...
1071 if (AddedBondList[(*Runner)->nr] == NULL) {
1072 if (((*Runner) != Bond) && ((*Runner)->Cyclic) && (((ShortestPathList[Walker->nr]+1) < BondOrder))) {
1073 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
1074 } else { // if it's root bond it has to broken (otherwise we would not create the fragments)
1075#ifdef ADDHYDROGEN
1076 if(!Mol->AddHydrogenReplacementAtom(out, (*Runner), AddedAtomList[Walker->nr], Walker, OtherAtom, IsAngstroem))
1077 exit(1);
1078#endif
1079 }
1080 }
1081 }
1082 }
1083 }
1084 ColorList[Walker->nr] = black;
1085 *out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
1086 }
1087 Free(&PredecessorList);
1088 Free(&ShortestPathList);
1089 Free(&ColorList);
1090 delete(AtomStack);
1091};
1092
1093/** Adds a bond as a copy to a given one
1094 * \param *left leftatom of new bond
1095 * \param *right rightatom of new bond
1096 * \param *CopyBond rest of fields in bond are copied from this
1097 * \return pointer to new bond
1098 */
1099bond * molecule::CopyBond(atom *left, atom *right, bond *CopyBond)
1100{
1101 bond *Binder = AddBond(left, right, CopyBond->BondDegree);
1102 Binder->Cyclic = CopyBond->Cyclic;
1103 Binder->Type = CopyBond->Type;
1104 return Binder;
1105};
1106
1107
1108/** Adds bond structure to this molecule from \a Father molecule.
1109 * This basically causes this molecule to become an induced subgraph of the \a Father, i.e. for every bond in Father
1110 * with end points present in this molecule, bond is created in this molecule.
1111 * Special care was taken to ensure that this is of complexity O(N), where N is the \a Father's molecule::AtomCount.
1112 * \param *out output stream for debugging
1113 * \param *Father father molecule
1114 * \return true - is induced subgraph, false - there are atoms with fathers not in \a Father
1115 * \todo not checked, not fully working probably
1116 */
1117bool molecule::BuildInducedSubgraph(ofstream *out, const molecule *Father)
1118{
1119 atom *Walker = NULL, *OtherAtom = NULL;
1120 bool status = true;
1121 atom **ParentList = Malloc<atom*>(Father->AtomCount, "molecule::BuildInducedSubgraph: **ParentList");
1122
1123 *out << Verbose(2) << "Begin of BuildInducedSubgraph." << endl;
1124
1125 // reset parent list
1126 *out << Verbose(3) << "Resetting ParentList." << endl;
1127 for (int i=Father->AtomCount;i--;)
1128 ParentList[i] = NULL;
1129
1130 // fill parent list with sons
1131 *out << Verbose(3) << "Filling Parent List." << endl;
1132 Walker = start;
1133 while (Walker->next != end) {
1134 Walker = Walker->next;
1135 ParentList[Walker->father->nr] = Walker;
1136 // Outputting List for debugging
1137 *out << Verbose(4) << "Son["<< Walker->father->nr <<"] of " << Walker->father << " is " << ParentList[Walker->father->nr] << "." << endl;
1138 }
1139
1140 // check each entry of parent list and if ok (one-to-and-onto matching) create bonds
1141 *out << Verbose(3) << "Creating bonds." << endl;
1142 Walker = Father->start;
1143 while (Walker->next != Father->end) {
1144 Walker = Walker->next;
1145 if (ParentList[Walker->nr] != NULL) {
1146 if (ParentList[Walker->nr]->father != Walker) {
1147 status = false;
1148 } else {
1149 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1150 OtherAtom = (*Runner)->GetOtherAtom(Walker);
1151 if (ParentList[OtherAtom->nr] != NULL) { // if otheratom is also a father of an atom on this molecule, create the bond
1152 *out << Verbose(4) << "Endpoints of Bond " << (*Runner) << " are both present: " << ParentList[Walker->nr]->Name << " and " << ParentList[OtherAtom->nr]->Name << "." << endl;
1153 AddBond(ParentList[Walker->nr], ParentList[OtherAtom->nr], (*Runner)->BondDegree);
1154 }
1155 }
1156 }
1157 }
1158 }
1159
1160 Free(&ParentList);
1161 *out << Verbose(2) << "End of BuildInducedSubgraph." << endl;
1162 return status;
1163};
1164
1165
1166/** For a given keyset \a *Fragment, checks whether it is connected in the current molecule.
1167 * \param *out output stream for debugging
1168 * \param *Fragment Keyset of fragment's vertices
1169 * \return true - connected, false - disconnected
1170 * \note this is O(n^2) for it's just a bug checker not meant for permanent use!
1171 */
1172bool molecule::CheckForConnectedSubgraph(ofstream *out, KeySet *Fragment)
1173{
1174 atom *Walker = NULL, *Walker2 = NULL;
1175 bool BondStatus = false;
1176 int size;
1177
1178 *out << Verbose(1) << "Begin of CheckForConnectedSubgraph" << endl;
1179 *out << Verbose(2) << "Disconnected atom: ";
1180
1181 // count number of atoms in graph
1182 size = 0;
1183 for(KeySet::iterator runner = Fragment->begin(); runner != Fragment->end(); runner++)
1184 size++;
1185 if (size > 1)
1186 for(KeySet::iterator runner = Fragment->begin(); runner != Fragment->end(); runner++) {
1187 Walker = FindAtom(*runner);
1188 BondStatus = false;
1189 for(KeySet::iterator runners = Fragment->begin(); runners != Fragment->end(); runners++) {
1190 Walker2 = FindAtom(*runners);
1191 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1192 if ((*Runner)->GetOtherAtom(Walker) == Walker2) {
1193 BondStatus = true;
1194 break;
1195 }
1196 if (BondStatus)
1197 break;
1198 }
1199 }
1200 if (!BondStatus) {
1201 *out << (*Walker) << endl;
1202 return false;
1203 }
1204 }
1205 else {
1206 *out << "none." << endl;
1207 return true;
1208 }
1209 *out << "none." << endl;
1210
1211 *out << Verbose(1) << "End of CheckForConnectedSubgraph" << endl;
1212
1213 return true;
1214}
Note: See TracBrowser for help on using the repository browser.