source: molecuilder/src/molecule_graph.cpp@ 872b51

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

Huge refactoring: molecule::ListOfBondsPerAtom and molecule::NumberOfBondsPerAtom removed, atom::ListOfBonds introduced. Unit Test for ListOfBonds manipulation introduced.

  • changes to builder.cpp: removed CreateListOfBondsPerAtom() calls, as the creation of the global arrays is not necessary anymore
  • changes to LinkedCell: LinkedCell::CheckBounds(int[NDIM]) does not admonish out of bonds as this is not desired for the local offset which may become out of bounds.
  • changes to lists.hpp templates: BUGFIX: unlink() now sets ->next and ->previous to NULL, cleanup() uses removedwithoutcheck()
  • new templates for molecule.hpp: SumPerAtom() allows for summation of the return value of atom:...() member fiunctions. This is needed e.g. for atom::CorrectBondDegree()

Signed-off-by: Frederik Heber <heber@…>

  • Property mode set to 100644
File size: 50.4 KB
RevLine 
[f92d00]1/*
2 * molecule_graph.cpp
3 *
4 * Created on: Oct 5, 2009
5 * Author: heber
6 */
7
[17b3a5c]8#include "atom.hpp"
9#include "bond.hpp"
[f92d00]10#include "config.hpp"
[17b3a5c]11#include "element.hpp"
12#include "helpers.hpp"
[b0ee98]13#include "linkedcell.hpp"
[17b3a5c]14#include "lists.hpp"
[f92d00]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 */
[bee48d]24void molecule::CreateAdjacencyListFromDbondFile(ofstream *out, ifstream *input)
[f92d00]25{
26
27 // 1 We will parse bonds out of the dbond file created by tremolo.
[bee48d]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 }
[f92d00]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
[b0ee98]73 atom *Walker = NULL;
74 atom *OtherWalker = NULL;
75 atom **AtomMap = NULL;
76 int n[NDIM];
[f92d00]77 double distance, MinDistance, MaxDistance;
[b0ee98]78 LinkedCell *LC = NULL;
79 LinkedNodes *List = NULL;
80 LinkedNodes *OtherList = NULL;
[f92d00]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) {
[b0ee98]94 LC = new LinkedCell(this, bonddistance);
[f92d00]95
[b0ee98]96 // create a list to map Tesselpoint::nr to atom *
97 AtomMap = Malloc<atom *>(AtomCount, "molecule::CreateAdjacencyList - **AtomCount");
[f92d00]98 Walker = start;
[b0ee98]99 while (Walker->next != end) {
[f92d00]100 Walker = Walker->next;
[b0ee98]101 AtomMap[Walker->nr] = Walker;
[f92d00]102 }
103
104 // 3a. go through every cell
[b0ee98]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];
[f92d00]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]++) {
[b0ee98]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 }
[f92d00]136 }
137 }
138 }
139 }
140 }
141 }
142 }
[b0ee98]143 Free(&AtomMap);
144 delete(LC);
145 *out << Verbose(1) << "I detected " << BondCount << " bonds in the molecule with distance " << BondDistance << "." << endl;
[f92d00]146
[b0ee98]147 // correct bond degree by comparing valence and bond degree
148 CorrectBondDegree(out);
[f92d00]149
[b0ee98]150 // output bonds for debugging (if bond chain list was correctly installed)
[872b51]151 ActOnAllAtoms( &atom::OutputBondOfAtom, out );
[b0ee98]152 } else
153 *out << Verbose(1) << "AtomCount is " << AtomCount << ", thus no bonds, no connections!." << endl;
154 *out << Verbose(0) << "End of CreateAdjacencyList." << endl;
155};
[f92d00]156
[b0ee98]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};
[f92d00]170
[b0ee98]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) {
[872b51]184 *out << Verbose(1) << "Correcting Bond degree of each bond ... " << endl;
[b0ee98]185 do {
[872b51]186 No = SumPerAtom( &atom::CorrectBondDegree, out );
[b0ee98]187 } while (No);
[f92d00]188 *out << " done." << endl;
[b0ee98]189 } else {
190 *out << Verbose(1) << "BondCount is " << BondCount << ", no bonds between any of the " << AtomCount << " atoms." << endl;
191 }
[872b51]192 *out << No << " bonds could not be corrected." << endl;
[f92d00]193
[872b51]194 return (No);
[f92d00]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{
[872b51]204 NoCyclicBonds = 0;
[f92d00]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)
[872b51]222 NoCyclicBonds++;
[f92d00]223 }
224 delete(BackEdgeStack);
[872b51]225 return NoCyclicBonds;
[f92d00]226};
[b0ee98]227
228
[f92d00]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
254/** Performs a Depth-First search on this molecule.
255 * Marks bonds in molecule as cyclic, bridge, ... and atoms as
256 * articulations points, ...
257 * We use the algorithm from [Even, Graph Algorithms, p.62].
258 * \param *out output stream for debugging
259 * \param *&BackEdgeStack NULL pointer to StackClass with all the found back edges, allocated and filled on return
260 * \return list of each disconnected subgraph as an individual molecule class structure
261 */
262MoleculeLeafClass * molecule::DepthFirstSearchAnalysis(ofstream *out, class StackClass<bond *> *&BackEdgeStack)
263{
264 class StackClass<atom *> *AtomStack = new StackClass<atom *>(AtomCount);
265 BackEdgeStack = new StackClass<bond *> (BondCount);
266 MoleculeLeafClass *SubGraphs = new MoleculeLeafClass(NULL);
267 MoleculeLeafClass *LeafWalker = SubGraphs;
268 int CurrentGraphNr = 0, OldGraphNr;
269 int ComponentNumber = 0;
270 atom *Walker = NULL, *OtherAtom = NULL, *Root = start->next;
271 bond *Binder = NULL;
272 bool BackStepping = false;
273
274 *out << Verbose(0) << "Begin of DepthFirstSearchAnalysis" << endl;
275
276 ResetAllBondsToUnused();
277 ResetAllAtomNumbers();
278 InitComponentNumbers();
279 BackEdgeStack->ClearStack();
280 while (Root != end) { // if there any atoms at all
281 // (1) mark all edges unused, empty stack, set atom->GraphNr = 0 for all
282 AtomStack->ClearStack();
283
284 // put into new subgraph molecule and add this to list of subgraphs
285 LeafWalker = new MoleculeLeafClass(LeafWalker);
286 LeafWalker->Leaf = new molecule(elemente);
287 LeafWalker->Leaf->AddCopyAtom(Root);
288
289 OldGraphNr = CurrentGraphNr;
290 Walker = Root;
291 do { // (10)
292 do { // (2) set number and Lowpoint of Atom to i, increase i, push current atom
293 if (!BackStepping) { // if we don't just return from (8)
294 Walker->GraphNr = CurrentGraphNr;
295 Walker->LowpointNr = CurrentGraphNr;
296 *out << Verbose(1) << "Setting Walker[" << Walker->Name << "]'s number to " << Walker->GraphNr << " with Lowpoint " << Walker->LowpointNr << "." << endl;
297 AtomStack->Push(Walker);
298 CurrentGraphNr++;
299 }
300 do { // (3) if Walker has no unused egdes, go to (5)
301 BackStepping = false; // reset backstepping flag for (8)
302 if (Binder == NULL) // if we don't just return from (11), Binder is already set to next unused
303 Binder = FindNextUnused(Walker);
304 if (Binder == NULL)
305 break;
306 *out << Verbose(2) << "Current Unused Bond is " << *Binder << "." << endl;
307 // (4) Mark Binder used, ...
308 Binder->MarkUsed(black);
309 OtherAtom = Binder->GetOtherAtom(Walker);
310 *out << Verbose(2) << "(4) OtherAtom is " << OtherAtom->Name << "." << endl;
311 if (OtherAtom->GraphNr != -1) {
312 // (4a) ... if "other" atom has been visited (GraphNr != 0), set lowpoint to minimum of both, go to (3)
313 Binder->Type = BackEdge;
314 BackEdgeStack->Push(Binder);
315 Walker->LowpointNr = ( Walker->LowpointNr < OtherAtom->GraphNr ) ? Walker->LowpointNr : OtherAtom->GraphNr;
316 *out << Verbose(3) << "(4a) Visited: Setting Lowpoint of Walker[" << Walker->Name << "] to " << Walker->LowpointNr << "." << endl;
317 } else {
318 // (4b) ... otherwise set OtherAtom as Ancestor of Walker and Walker as OtherAtom, go to (2)
319 Binder->Type = TreeEdge;
320 OtherAtom->Ancestor = Walker;
321 Walker = OtherAtom;
322 *out << Verbose(3) << "(4b) Not Visited: OtherAtom[" << OtherAtom->Name << "]'s Ancestor is now " << OtherAtom->Ancestor->Name << ", Walker is OtherAtom " << OtherAtom->Name << "." << endl;
323 break;
324 }
325 Binder = NULL;
326 } while (1); // (3)
327 if (Binder == NULL) {
328 *out << Verbose(2) << "No more Unused Bonds." << endl;
329 break;
330 } else
331 Binder = NULL;
332 } while (1); // (2)
333
334 // 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!
335 if ((Walker == Root) && (Binder == NULL))
336 break;
337
338 // (5) if Ancestor of Walker is ...
339 *out << Verbose(1) << "(5) Number of Walker[" << Walker->Name << "]'s Ancestor[" << Walker->Ancestor->Name << "] is " << Walker->Ancestor->GraphNr << "." << endl;
340 if (Walker->Ancestor->GraphNr != Root->GraphNr) {
341 // (6) (Ancestor of Walker is not Root)
342 if (Walker->LowpointNr < Walker->Ancestor->GraphNr) {
343 // (6a) set Ancestor's Lowpoint number to minimum of of its Ancestor and itself, go to Step(8)
344 Walker->Ancestor->LowpointNr = (Walker->Ancestor->LowpointNr < Walker->LowpointNr) ? Walker->Ancestor->LowpointNr : Walker->LowpointNr;
345 *out << Verbose(2) << "(6) Setting Walker[" << Walker->Name << "]'s Ancestor[" << Walker->Ancestor->Name << "]'s Lowpoint to " << Walker->Ancestor->LowpointNr << "." << endl;
346 } else {
347 // (7) (Ancestor of Walker is a separating vertex, remove all from stack till Walker (including), these and Ancestor form a component
348 Walker->Ancestor->SeparationVertex = true;
349 *out << Verbose(2) << "(7) Walker[" << Walker->Name << "]'s Ancestor[" << Walker->Ancestor->Name << "]'s is a separating vertex, creating component." << endl;
350 SetNextComponentNumber(Walker->Ancestor, ComponentNumber);
351 *out << Verbose(3) << "(7) Walker[" << Walker->Name << "]'s Ancestor's Compont is " << ComponentNumber << "." << endl;
352 SetNextComponentNumber(Walker, ComponentNumber);
353 *out << Verbose(3) << "(7) Walker[" << Walker->Name << "]'s Compont is " << ComponentNumber << "." << endl;
354 do {
355 OtherAtom = AtomStack->PopLast();
356 LeafWalker->Leaf->AddCopyAtom(OtherAtom);
357 SetNextComponentNumber(OtherAtom, ComponentNumber);
358 *out << Verbose(3) << "(7) Other[" << OtherAtom->Name << "]'s Compont is " << ComponentNumber << "." << endl;
359 } while (OtherAtom != Walker);
360 ComponentNumber++;
361 }
362 // (8) Walker becomes its Ancestor, go to (3)
363 *out << Verbose(2) << "(8) Walker[" << Walker->Name << "] is now its Ancestor " << Walker->Ancestor->Name << ", backstepping. " << endl;
364 Walker = Walker->Ancestor;
365 BackStepping = true;
366 }
367 if (!BackStepping) { // coming from (8) want to go to (3)
368 // (9) remove all from stack till Walker (including), these and Root form a component
369 AtomStack->Output(out);
370 SetNextComponentNumber(Root, ComponentNumber);
371 *out << Verbose(3) << "(9) Root[" << Root->Name << "]'s Component is " << ComponentNumber << "." << endl;
372 SetNextComponentNumber(Walker, ComponentNumber);
373 *out << Verbose(3) << "(9) Walker[" << Walker->Name << "]'s Component is " << ComponentNumber << "." << endl;
374 do {
375 OtherAtom = AtomStack->PopLast();
376 LeafWalker->Leaf->AddCopyAtom(OtherAtom);
377 SetNextComponentNumber(OtherAtom, ComponentNumber);
378 *out << Verbose(3) << "(7) Other[" << OtherAtom->Name << "]'s Compont is " << ComponentNumber << "." << endl;
379 } while (OtherAtom != Walker);
380 ComponentNumber++;
381
382 // (11) Root is separation vertex, set Walker to Root and go to (4)
383 Walker = Root;
384 Binder = FindNextUnused(Walker);
385 *out << Verbose(1) << "(10) Walker is Root[" << Root->Name << "], next Unused Bond is " << Binder << "." << endl;
386 if (Binder != NULL) { // Root is separation vertex
387 *out << Verbose(1) << "(11) Root is a separation vertex." << endl;
388 Walker->SeparationVertex = true;
389 }
390 }
391 } while ((BackStepping) || (Binder != NULL)); // (10) halt only if Root has no unused edges
392
393 // From OldGraphNr to CurrentGraphNr ranges an disconnected subgraph
394 *out << Verbose(0) << "Disconnected subgraph ranges from " << OldGraphNr << " to " << CurrentGraphNr << "." << endl;
395 LeafWalker->Leaf->Output(out);
396 *out << endl;
397
398 // step on to next root
399 while ((Root != end) && (Root->GraphNr != -1)) {
400 //*out << Verbose(1) << "Current next subgraph root candidate is " << Root->Name << "." << endl;
401 if (Root->GraphNr != -1) // if already discovered, step on
402 Root = Root->next;
403 }
404 }
405 // set cyclic bond criterium on "same LP" basis
[872b51]406 CyclicBondAnalysis();
407
408 OutputGraphInfoPerAtom(out);
409
410 OutputGraphInfoPerBond(out);
411
412 // free all and exit
413 delete(AtomStack);
414 *out << Verbose(0) << "End of DepthFirstSearchAnalysis" << endl;
415 return SubGraphs;
416};
417
418/** Scans through all bonds and set bond::Cyclic to true where atom::LowpointNr of both ends is equal: LP criterion.
419 */
420void molecule::CyclicBondAnalysis()
421{
422 NoCyclicBonds = 0;
423 bond *Binder = first;
[f92d00]424 while(Binder->next != last) {
425 Binder = Binder->next;
426 if (Binder->rightatom->LowpointNr == Binder->leftatom->LowpointNr) { // cyclic ??
427 Binder->Cyclic = true;
428 NoCyclicBonds++;
429 }
430 }
[872b51]431};
[f92d00]432
[872b51]433/** Output graph information per atom.
434 * \param *out output stream
435 */
436void molecule::OutputGraphInfoPerAtom(ofstream *out)
437{
[f92d00]438 *out << Verbose(1) << "Final graph info for each atom is:" << endl;
[872b51]439 ActOnAllAtoms( &atom::OutputGraphInfo, out );
440};
[f92d00]441
[872b51]442/** Output graph information per bond.
443 * \param *out output stream
444 */
445void molecule::OutputGraphInfoPerBond(ofstream *out)
446{
[f92d00]447 *out << Verbose(1) << "Final graph info for each bond is:" << endl;
[872b51]448 bond *Binder = first;
[f92d00]449 while(Binder->next != last) {
450 Binder = Binder->next;
451 *out << Verbose(2) << ((Binder->Type == TreeEdge) ? "TreeEdge " : "BackEdge ") << *Binder << ": <";
452 *out << ((Binder->leftatom->SeparationVertex) ? "SP," : "") << "L" << Binder->leftatom->LowpointNr << " G" << Binder->leftatom->GraphNr << " Comp.";
[872b51]453 Binder->leftatom->OutputComponentNumber(out);
[f92d00]454 *out << " === ";
455 *out << ((Binder->rightatom->SeparationVertex) ? "SP," : "") << "L" << Binder->rightatom->LowpointNr << " G" << Binder->rightatom->GraphNr << " Comp.";
[872b51]456 Binder->rightatom->OutputComponentNumber(out);
[f92d00]457 *out << ">." << endl;
458 if (Binder->Cyclic) // cyclic ??
459 *out << Verbose(3) << "Lowpoint at each side are equal: CYCLIC!" << endl;
460 }
461};
462
463/** Analyses the cycles found and returns minimum of all cycle lengths.
464 * We begin with a list of Back edges found during DepthFirstSearchAnalysis(). We go through this list - one end is the Root,
465 * the other our initial Walker - and do a Breadth First Search for the Root. We mark down each Predecessor and as soon as
466 * we have found the Root via BFS, we may climb back the closed cycle via the Predecessors. Thereby we mark atoms and bonds
467 * as cyclic and print out the cycles.
468 * \param *out output stream for debugging
469 * \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!
470 * \param *&MinimumRingSize contains smallest ring size in molecular structure on return or -1 if no rings were found, if set is maximum search distance
471 * \todo BFS from the not-same-LP to find back to starting point of tributary cycle over more than one bond
472 */
473void molecule::CyclicStructureAnalysis(ofstream *out, class StackClass<bond *> * BackEdgeStack, int *&MinimumRingSize)
474{
475 atom **PredecessorList = Malloc<atom*>(AtomCount, "molecule::CyclicStructureAnalysis: **PredecessorList");
476 int *ShortestPathList = Malloc<int>(AtomCount, "molecule::CyclicStructureAnalysis: *ShortestPathList");
477 enum Shading *ColorList = Malloc<enum Shading>(AtomCount, "molecule::CyclicStructureAnalysis: *ColorList");
478 class StackClass<atom *> *BFSStack = new StackClass<atom *> (AtomCount); // will hold the current ring
479 class StackClass<atom *> *TouchedStack = new StackClass<atom *> (AtomCount); // contains all "touched" atoms (that need to be reset after BFS loop)
480 atom *Walker = NULL, *OtherAtom = NULL, *Root = NULL;
481 bond *Binder = NULL, *BackEdge = NULL;
482 int RingSize, NumCycles, MinRingSize = -1;
483
484 // initialise each vertex as white with no predecessor, empty queue, color Root lightgray
485 for (int i=AtomCount;i--;) {
486 PredecessorList[i] = NULL;
487 ShortestPathList[i] = -1;
488 ColorList[i] = white;
489 }
490
491 *out << Verbose(1) << "Back edge list - ";
492 BackEdgeStack->Output(out);
493
494 *out << Verbose(1) << "Analysing cycles ... " << endl;
495 NumCycles = 0;
496 while (!BackEdgeStack->IsEmpty()) {
497 BackEdge = BackEdgeStack->PopFirst();
498 // this is the target
499 Root = BackEdge->leftatom;
500 // this is the source point
501 Walker = BackEdge->rightatom;
502 ShortestPathList[Walker->nr] = 0;
503 BFSStack->ClearStack(); // start with empty BFS stack
504 BFSStack->Push(Walker);
505 TouchedStack->Push(Walker);
506 *out << Verbose(1) << "---------------------------------------------------------------------------------------------------------" << endl;
507 OtherAtom = NULL;
508 do { // look for Root
509 Walker = BFSStack->PopFirst();
510 *out << Verbose(2) << "Current Walker is " << *Walker << ", we look for SP to Root " << *Root << "." << endl;
[872b51]511 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
512 if ((*Runner) != BackEdge) { // only walk along DFS spanning tree (otherwise we always find SP of one being backedge Binder)
513 OtherAtom = (*Runner)->GetOtherAtom(Walker);
[f92d00]514#ifdef ADDHYDROGEN
515 if (OtherAtom->type->Z != 1) {
516#endif
517 *out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *Binder << "." << endl;
518 if (ColorList[OtherAtom->nr] == white) {
519 TouchedStack->Push(OtherAtom);
520 ColorList[OtherAtom->nr] = lightgray;
521 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
522 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
523 *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;
524 //if (ShortestPathList[OtherAtom->nr] < MinimumRingSize[Walker->GetTrueFather()->nr]) { // Check for maximum distance
525 *out << Verbose(3) << "Putting OtherAtom into queue." << endl;
526 BFSStack->Push(OtherAtom);
527 //}
528 } else {
529 *out << Verbose(3) << "Not Adding, has already been visited." << endl;
530 }
531 if (OtherAtom == Root)
532 break;
533#ifdef ADDHYDROGEN
534 } else {
535 *out << Verbose(2) << "Skipping hydrogen atom " << *OtherAtom << "." << endl;
536 ColorList[OtherAtom->nr] = black;
537 }
538#endif
539 } else {
540 *out << Verbose(2) << "Bond " << *Binder << " not Visiting, is the back edge." << endl;
541 }
542 }
543 ColorList[Walker->nr] = black;
544 *out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
545 if (OtherAtom == Root) { // if we have found the root, check whether this cycle wasn't already found beforehand
546 // step through predecessor list
547 while (OtherAtom != BackEdge->rightatom) {
548 if (!OtherAtom->GetTrueFather()->IsCyclic) // if one bond in the loop is not marked as cyclic, we haven't found this cycle yet
549 break;
550 else
551 OtherAtom = PredecessorList[OtherAtom->nr];
552 }
553 if (OtherAtom == BackEdge->rightatom) { // if each atom in found cycle is cyclic, loop's been found before already
554 *out << Verbose(3) << "This cycle was already found before, skipping and removing seeker from search." << endl;\
555 do {
556 OtherAtom = TouchedStack->PopLast();
557 if (PredecessorList[OtherAtom->nr] == Walker) {
558 *out << Verbose(4) << "Removing " << *OtherAtom << " from lists and stacks." << endl;
559 PredecessorList[OtherAtom->nr] = NULL;
560 ShortestPathList[OtherAtom->nr] = -1;
561 ColorList[OtherAtom->nr] = white;
562 BFSStack->RemoveItem(OtherAtom);
563 }
564 } while ((!TouchedStack->IsEmpty()) && (PredecessorList[OtherAtom->nr] == NULL));
565 TouchedStack->Push(OtherAtom); // last was wrongly popped
566 OtherAtom = BackEdge->rightatom; // set to not Root
567 } else
568 OtherAtom = Root;
569 }
570 } while ((!BFSStack->IsEmpty()) && (OtherAtom != Root) && (OtherAtom != NULL)); // || (ShortestPathList[OtherAtom->nr] < MinimumRingSize[Walker->GetTrueFather()->nr])));
571
572 if (OtherAtom == Root) {
573 // now climb back the predecessor list and thus find the cycle members
574 NumCycles++;
575 RingSize = 1;
576 Root->GetTrueFather()->IsCyclic = true;
577 *out << Verbose(1) << "Found ring contains: ";
578 Walker = Root;
579 while (Walker != BackEdge->rightatom) {
580 *out << Walker->Name << " <-> ";
581 Walker = PredecessorList[Walker->nr];
582 Walker->GetTrueFather()->IsCyclic = true;
583 RingSize++;
584 }
585 *out << Walker->Name << " with a length of " << RingSize << "." << endl << endl;
586 // walk through all and set MinimumRingSize
587 Walker = Root;
588 MinimumRingSize[Walker->GetTrueFather()->nr] = RingSize;
589 while (Walker != BackEdge->rightatom) {
590 Walker = PredecessorList[Walker->nr];
591 if (RingSize < MinimumRingSize[Walker->GetTrueFather()->nr])
592 MinimumRingSize[Walker->GetTrueFather()->nr] = RingSize;
593 }
594 if ((RingSize < MinRingSize) || (MinRingSize == -1))
595 MinRingSize = RingSize;
596 } else {
597 *out << Verbose(1) << "No ring containing " << *Root << " with length equal to or smaller than " << MinimumRingSize[Walker->GetTrueFather()->nr] << " found." << endl;
598 }
599
600 // now clean the lists
601 while (!TouchedStack->IsEmpty()){
602 Walker = TouchedStack->PopFirst();
603 PredecessorList[Walker->nr] = NULL;
604 ShortestPathList[Walker->nr] = -1;
605 ColorList[Walker->nr] = white;
606 }
607 }
608 if (MinRingSize != -1) {
609 // go over all atoms
610 Root = start;
611 while(Root->next != end) {
612 Root = Root->next;
613
614 if (MinimumRingSize[Root->GetTrueFather()->nr] == AtomCount) { // check whether MinimumRingSize is set, if not BFS to next where it is
615 Walker = Root;
616 ShortestPathList[Walker->nr] = 0;
617 BFSStack->ClearStack(); // start with empty BFS stack
618 BFSStack->Push(Walker);
619 TouchedStack->Push(Walker);
620 //*out << Verbose(1) << "---------------------------------------------------------------------------------------------------------" << endl;
621 OtherAtom = Walker;
622 while (OtherAtom != NULL) { // look for Root
623 Walker = BFSStack->PopFirst();
624 //*out << Verbose(2) << "Current Walker is " << *Walker << ", we look for SP to Root " << *Root << "." << endl;
[872b51]625 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
626 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
627 OtherAtom = (*Runner)->GetOtherAtom(Walker);
[f92d00]628 //*out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *Binder << "." << endl;
629 if (ColorList[OtherAtom->nr] == white) {
630 TouchedStack->Push(OtherAtom);
631 ColorList[OtherAtom->nr] = lightgray;
632 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
633 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
634 //*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;
635 if (OtherAtom->GetTrueFather()->IsCyclic) { // if the other atom is connected to a ring
636 MinimumRingSize[Root->GetTrueFather()->nr] = ShortestPathList[OtherAtom->nr]+MinimumRingSize[OtherAtom->GetTrueFather()->nr];
637 OtherAtom = NULL; //break;
638 break;
639 } else
640 BFSStack->Push(OtherAtom);
641 } else {
642 //*out << Verbose(3) << "Not Adding, has already been visited." << endl;
643 }
644 } else {
645 //*out << Verbose(3) << "Not Visiting, is a back edge." << endl;
646 }
647 }
648 ColorList[Walker->nr] = black;
649 //*out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
650 }
651
652 // now clean the lists
653 while (!TouchedStack->IsEmpty()){
654 Walker = TouchedStack->PopFirst();
655 PredecessorList[Walker->nr] = NULL;
656 ShortestPathList[Walker->nr] = -1;
657 ColorList[Walker->nr] = white;
658 }
659 }
660 *out << Verbose(1) << "Minimum ring size of " << *Root << " is " << MinimumRingSize[Root->GetTrueFather()->nr] << "." << endl;
661 }
662 *out << Verbose(1) << "Minimum ring size is " << MinRingSize << ", over " << NumCycles << " cycles total." << endl;
663 } else
664 *out << Verbose(1) << "No rings were detected in the molecular structure." << endl;
665
666 Free(&PredecessorList);
667 Free(&ShortestPathList);
668 Free(&ColorList);
669 delete(BFSStack);
670};
671
672/** Sets the next component number.
673 * This is O(N) as the number of bonds per atom is bound.
674 * \param *vertex atom whose next atom::*ComponentNr is to be set
675 * \param nr number to use
676 */
677void molecule::SetNextComponentNumber(atom *vertex, int nr)
678{
[872b51]679 size_t i=0;
[f92d00]680 if (vertex != NULL) {
[872b51]681 for(;i<vertex->ListOfBonds.size();i++) {
[f92d00]682 if (vertex->ComponentNr[i] == -1) { // check if not yet used
683 vertex->ComponentNr[i] = nr;
684 break;
685 }
686 else if (vertex->ComponentNr[i] == nr) // if number is already present, don't add another time
687 break; // breaking here will not cause error!
688 }
[872b51]689 if (i == vertex->ListOfBonds.size())
[f92d00]690 cerr << "Error: All Component entries are already occupied!" << endl;
691 } else
692 cerr << "Error: Given vertex is NULL!" << endl;
693};
694
695/** Allocates memory for all atom::*ComponentNr in this molecule and sets each entry to -1.
696 */
697void molecule::InitComponentNumbers()
698{
699 atom *Walker = start;
700 while(Walker->next != end) {
701 Walker = Walker->next;
702 if (Walker->ComponentNr != NULL)
703 Free(&Walker->ComponentNr);
[872b51]704 Walker->ComponentNr = Malloc<int>(Walker->ListOfBonds.size()+1, "molecule::InitComponentNumbers: *Walker->ComponentNr");
705 for (int i=Walker->ListOfBonds.size()+1;i--;)
[f92d00]706 Walker->ComponentNr[i] = -1;
707 }
708};
709
710/** Returns next unused bond for this atom \a *vertex or NULL of none exists.
711 * \param *vertex atom to regard
712 * \return bond class or NULL
713 */
714bond * molecule::FindNextUnused(atom *vertex)
715{
[872b51]716 for (BondList::const_iterator Runner = vertex->ListOfBonds.begin(); Runner != vertex->ListOfBonds.end(); (++Runner))
717 if ((*Runner)->IsUsed() == white)
718 return((*Runner));
[f92d00]719 return NULL;
720};
721
722/** Resets bond::Used flag of all bonds in this molecule.
723 * \return true - success, false - -failure
724 */
725void molecule::ResetAllBondsToUnused()
726{
727 bond *Binder = first;
728 while (Binder->next != last) {
729 Binder = Binder->next;
730 Binder->ResetUsed();
731 }
732};
733
734/** Resets atom::nr to -1 of all atoms in this molecule.
735 */
736void molecule::ResetAllAtomNumbers()
737{
738 atom *Walker = start;
739 while (Walker->next != end) {
740 Walker = Walker->next;
741 Walker->GraphNr = -1;
742 }
743};
744
745/** Output a list of flags, stating whether the bond was visited or not.
746 * \param *out output stream for debugging
747 * \param *list
748 */
749void OutputAlreadyVisited(ofstream *out, int *list)
750{
751 *out << Verbose(4) << "Already Visited Bonds:\t";
752 for(int i=1;i<=list[0];i++) *out << Verbose(0) << list[i] << " ";
753 *out << endl;
754};
755
756
757/** Storing the bond structure of a molecule to file.
758 * Simply stores Atom::nr and then the Atom::nr of all bond partners per line.
759 * \param *out output stream for debugging
760 * \param *path path to file
761 * \return true - file written successfully, false - writing failed
762 */
763bool molecule::StoreAdjacencyToFile(ofstream *out, char *path)
764{
765 ofstream AdjacencyFile;
766 stringstream line;
767 bool status = true;
768
769 line << path << "/" << FRAGMENTPREFIX << ADJACENCYFILE;
770 AdjacencyFile.open(line.str().c_str(), ios::out);
771 *out << Verbose(1) << "Saving adjacency list ... ";
772 if (AdjacencyFile != NULL) {
[872b51]773 ActOnAllAtoms( &atom::OutputAdjacency, &AdjacencyFile );
[f92d00]774 AdjacencyFile.close();
775 *out << Verbose(1) << "done." << endl;
776 } else {
777 *out << Verbose(1) << "failed to open file " << line.str() << "." << endl;
778 status = false;
779 }
780
781 return status;
782};
783
784/** Checks contents of adjacency file against bond structure in structure molecule.
785 * \param *out output stream for debugging
786 * \param *path path to file
787 * \param **ListOfAtoms allocated (molecule::AtomCount) and filled lookup table for ids (Atom::nr) to *Atom
788 * \return true - structure is equal, false - not equivalence
789 */
790bool molecule::CheckAdjacencyFileAgainstMolecule(ofstream *out, char *path, atom **ListOfAtoms)
791{
792 ifstream File;
793 stringstream filename;
794 bool status = true;
[872b51]795 atom *Walker = NULL;
[f92d00]796 char *buffer = Malloc<char>(MAXSTRINGSIZE, "molecule::CheckAdjacencyFileAgainstMolecule: *buffer");
797
798 filename << path << "/" << FRAGMENTPREFIX << ADJACENCYFILE;
799 File.open(filename.str().c_str(), ios::out);
800 *out << Verbose(1) << "Looking at bond structure stored in adjacency file and comparing to present one ... ";
801 if (File != NULL) {
802 // allocate storage structure
803 int NonMatchNumber = 0; // will number of atoms with differing bond structure
804 int *CurrentBonds = Malloc<int>(8, "molecule::CheckAdjacencyFileAgainstMolecule - CurrentBonds"); // contains parsed bonds of current atom
[872b51]805 size_t CurrentBondsOfAtom;
[f92d00]806
807 // Parse the file line by line and count the bonds
808 while (!File.eof()) {
809 File.getline(buffer, MAXSTRINGSIZE);
810 stringstream line;
811 line.str(buffer);
812 int AtomNr = -1;
813 line >> AtomNr;
814 CurrentBondsOfAtom = -1; // we count one too far due to line end
815 // parse into structure
816 if ((AtomNr >= 0) && (AtomNr < AtomCount)) {
[872b51]817 Walker = ListOfAtoms[AtomNr];
[f92d00]818 while (!line.eof())
819 line >> CurrentBonds[ ++CurrentBondsOfAtom ];
820 // compare against present bonds
821 //cout << Verbose(2) << "Walker is " << *Walker << ", bond partners: ";
[872b51]822 if (CurrentBondsOfAtom == Walker->ListOfBonds.size()) {
823 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
824 int id = (*Runner)->GetOtherAtom(Walker)->nr;
825 size_t j = 0;
[f92d00]826 for (;(j<CurrentBondsOfAtom) && (CurrentBonds[j++] != id);); // check against all parsed bonds
827 if (CurrentBonds[j-1] != id) { // no match ? Then mark in ListOfAtoms
828 ListOfAtoms[AtomNr] = NULL;
829 NonMatchNumber++;
830 status = false;
831 //out << "[" << id << "]\t";
832 } else {
833 //out << id << "\t";
834 }
835 }
836 //out << endl;
837 } else {
[872b51]838 *out << "Number of bonds for Atom " << *Walker << " does not match, parsed " << CurrentBondsOfAtom << " against " << Walker->ListOfBonds.size() << "." << endl;
[f92d00]839 status = false;
840 }
841 }
842 }
843 File.close();
844 File.clear();
845 if (status) { // if equal we parse the KeySetFile
846 *out << Verbose(1) << "done: Equal." << endl;
847 status = true;
848 } else
849 *out << Verbose(1) << "done: Not equal by " << NonMatchNumber << " atoms." << endl;
850 Free(&CurrentBonds);
851 } else {
852 *out << Verbose(1) << "Adjacency file not found." << endl;
853 status = false;
854 }
855 *out << endl;
856 Free(&buffer);
857
858 return status;
859};
860
861
862/** Picks from a global stack with all back edges the ones in the fragment.
863 * \param *out output stream for debugging
864 * \param **ListOfLocalAtoms array of father atom::nr to local atom::nr (reverse of atom::father)
865 * \param *ReferenceStack stack with all the back egdes
866 * \param *LocalStack stack to be filled
867 * \return true - everything ok, false - ReferenceStack was empty
868 */
869bool molecule::PickLocalBackEdges(ofstream *out, atom **ListOfLocalAtoms, class StackClass<bond *> *&ReferenceStack, class StackClass<bond *> *&LocalStack)
870{
871 bool status = true;
872 if (ReferenceStack->IsEmpty()) {
873 cerr << "ReferenceStack is empty!" << endl;
874 return false;
875 }
876 bond *Binder = ReferenceStack->PopFirst();
877 bond *FirstBond = Binder; // mark the first bond, so that we don't loop through the stack indefinitely
878 atom *Walker = NULL, *OtherAtom = NULL;
879 ReferenceStack->Push(Binder);
880
881 do { // go through all bonds and push local ones
882 Walker = ListOfLocalAtoms[Binder->leftatom->nr]; // get one atom in the reference molecule
883 if (Walker != NULL) // if this Walker exists in the subgraph ...
[872b51]884 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
885 OtherAtom = (*Runner)->GetOtherAtom(Walker);
886 if (OtherAtom == ListOfLocalAtoms[(*Runner)->rightatom->nr]) { // found the bond
887 LocalStack->Push((*Runner));
888 *out << Verbose(3) << "Found local edge " << *(*Runner) << "." << endl;
[f92d00]889 break;
890 }
891 }
892 Binder = ReferenceStack->PopFirst(); // loop the stack for next item
893 *out << Verbose(3) << "Current candidate edge " << Binder << "." << endl;
894 ReferenceStack->Push(Binder);
895 } while (FirstBond != Binder);
896
897 return status;
898};
899
900
901/** Adds atoms up to \a BondCount distance from \a *Root and notes them down in \a **AddedAtomList.
902 * Gray vertices are always enqueued in an StackClass<atom *> FIFO queue, the rest is usual BFS with adding vertices found was
903 * white and putting into queue.
904 * \param *out output stream for debugging
905 * \param *Mol Molecule class to add atoms to
906 * \param **AddedAtomList list with added atom pointers, index is atom father's number
907 * \param **AddedBondList list with added bond pointers, index is bond father's number
908 * \param *Root root vertex for BFS
909 * \param *Bond bond not to look beyond
910 * \param BondOrder maximum distance for vertices to add
911 * \param IsAngstroem lengths are in angstroem or bohrradii
912 */
913void molecule::BreadthFirstSearchAdd(ofstream *out, molecule *Mol, atom **&AddedAtomList, bond **&AddedBondList, atom *Root, bond *Bond, int BondOrder, bool IsAngstroem)
914{
915 atom **PredecessorList = Malloc<atom*>(AtomCount, "molecule::BreadthFirstSearchAdd: **PredecessorList");
916 int *ShortestPathList = Malloc<int>(AtomCount, "molecule::BreadthFirstSearchAdd: *ShortestPathList");
917 enum Shading *ColorList = Malloc<enum Shading>(AtomCount, "molecule::BreadthFirstSearchAdd: *ColorList");
918 class StackClass<atom *> *AtomStack = new StackClass<atom *>(AtomCount);
919 atom *Walker = NULL, *OtherAtom = NULL;
920
921 // add Root if not done yet
922 AtomStack->ClearStack();
923 if (AddedAtomList[Root->nr] == NULL) // add Root if not yet present
924 AddedAtomList[Root->nr] = Mol->AddCopyAtom(Root);
925 AtomStack->Push(Root);
926
927 // initialise each vertex as white with no predecessor, empty queue, color Root lightgray
928 for (int i=AtomCount;i--;) {
929 PredecessorList[i] = NULL;
930 ShortestPathList[i] = -1;
931 if (AddedAtomList[i] != NULL) // mark already present atoms (i.e. Root and maybe others) as visited
932 ColorList[i] = lightgray;
933 else
934 ColorList[i] = white;
935 }
936 ShortestPathList[Root->nr] = 0;
937
938 // and go on ... Queue always contains all lightgray vertices
939 while (!AtomStack->IsEmpty()) {
940 // we have to pop the oldest atom from stack. This keeps the atoms on the stack always of the same ShortestPath distance.
941 // 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
942 // append length of 3 (their neighbours). Thus on stack we have always atoms of a certain length n at bottom of stack and
943 // followed by n+1 till top of stack.
944 Walker = AtomStack->PopFirst(); // pop oldest added
[872b51]945 *out << Verbose(1) << "Current Walker is: " << Walker->Name << ", and has " << Walker->ListOfBonds.size() << " bonds." << endl;
946 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
947 if ((*Runner) != NULL) { // don't look at bond equal NULL
948 OtherAtom = (*Runner)->GetOtherAtom(Walker);
949 *out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *(*Runner) << "." << endl;
[f92d00]950 if (ColorList[OtherAtom->nr] == white) {
[872b51]951 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)
[f92d00]952 ColorList[OtherAtom->nr] = lightgray;
953 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
954 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
955 *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;
[872b51]956 if ((((ShortestPathList[OtherAtom->nr] < BondOrder) && ((*Runner) != Bond))) ) { // Check for maximum distance
[f92d00]957 *out << Verbose(3);
958 if (AddedAtomList[OtherAtom->nr] == NULL) { // add if it's not been so far
959 AddedAtomList[OtherAtom->nr] = Mol->AddCopyAtom(OtherAtom);
960 *out << "Added OtherAtom " << OtherAtom->Name;
[872b51]961 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
962 *out << " and bond " << *(AddedBondList[(*Runner)->nr]) << ", ";
[f92d00]963 } 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)
964 *out << "Not adding OtherAtom " << OtherAtom->Name;
[872b51]965 if (AddedBondList[(*Runner)->nr] == NULL) {
966 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
967 *out << ", added Bond " << *(AddedBondList[(*Runner)->nr]);
[f92d00]968 } else
969 *out << ", not added Bond ";
970 }
971 *out << ", putting OtherAtom into queue." << endl;
972 AtomStack->Push(OtherAtom);
973 } else { // out of bond order, then replace
[872b51]974 if ((AddedAtomList[OtherAtom->nr] == NULL) && ((*Runner)->Cyclic))
[f92d00]975 ColorList[OtherAtom->nr] = white; // unmark if it has not been queued/added, to make it available via its other bonds (cyclic)
[872b51]976 if ((*Runner) == Bond)
[f92d00]977 *out << Verbose(3) << "Not Queueing, is the Root bond";
978 else if (ShortestPathList[OtherAtom->nr] >= BondOrder)
979 *out << Verbose(3) << "Not Queueing, is out of Bond Count of " << BondOrder;
[872b51]980 if (!(*Runner)->Cyclic)
[f92d00]981 *out << ", is not part of a cyclic bond, saturating bond with Hydrogen." << endl;
[872b51]982 if (AddedBondList[(*Runner)->nr] == NULL) {
[f92d00]983 if ((AddedAtomList[OtherAtom->nr] != NULL)) { // .. whether we add or saturate
[872b51]984 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
[f92d00]985 } else {
986#ifdef ADDHYDROGEN
[872b51]987 if (!Mol->AddHydrogenReplacementAtom(out, (*Runner), AddedAtomList[Walker->nr], Walker, OtherAtom, IsAngstroem))
[f92d00]988 exit(1);
989#endif
990 }
991 }
992 }
993 } else {
994 *out << Verbose(3) << "Not Adding, has already been visited." << endl;
995 // This has to be a cyclic bond, check whether it's present ...
[872b51]996 if (AddedBondList[(*Runner)->nr] == NULL) {
997 if (((*Runner) != Bond) && ((*Runner)->Cyclic) && (((ShortestPathList[Walker->nr]+1) < BondOrder))) {
998 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
[f92d00]999 } else { // if it's root bond it has to broken (otherwise we would not create the fragments)
1000#ifdef ADDHYDROGEN
[872b51]1001 if(!Mol->AddHydrogenReplacementAtom(out, (*Runner), AddedAtomList[Walker->nr], Walker, OtherAtom, IsAngstroem))
[f92d00]1002 exit(1);
1003#endif
1004 }
1005 }
1006 }
1007 }
1008 }
1009 ColorList[Walker->nr] = black;
1010 *out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
1011 }
1012 Free(&PredecessorList);
1013 Free(&ShortestPathList);
1014 Free(&ColorList);
1015 delete(AtomStack);
1016};
1017
[872b51]1018/** Adds a bond as a copy to a given one
1019 * \param *left leftatom of new bond
1020 * \param *right rightatom of new bond
1021 * \param *CopyBond rest of fields in bond are copied from this
1022 * \return pointer to new bond
1023 */
1024bond * molecule::CopyBond(atom *left, atom *right, bond *CopyBond)
1025{
1026 bond *Binder = AddBond(left, right, CopyBond->BondDegree);
1027 Binder->Cyclic = CopyBond->Cyclic;
1028 Binder->Type = CopyBond->Type;
1029 return Binder;
1030};
1031
1032
[f92d00]1033/** Adds bond structure to this molecule from \a Father molecule.
1034 * This basically causes this molecule to become an induced subgraph of the \a Father, i.e. for every bond in Father
1035 * with end points present in this molecule, bond is created in this molecule.
1036 * Special care was taken to ensure that this is of complexity O(N), where N is the \a Father's molecule::AtomCount.
1037 * \param *out output stream for debugging
1038 * \param *Father father molecule
1039 * \return true - is induced subgraph, false - there are atoms with fathers not in \a Father
1040 * \todo not checked, not fully working probably
1041 */
1042bool molecule::BuildInducedSubgraph(ofstream *out, const molecule *Father)
1043{
1044 atom *Walker = NULL, *OtherAtom = NULL;
1045 bool status = true;
1046 atom **ParentList = Malloc<atom*>(Father->AtomCount, "molecule::BuildInducedSubgraph: **ParentList");
1047
1048 *out << Verbose(2) << "Begin of BuildInducedSubgraph." << endl;
1049
1050 // reset parent list
1051 *out << Verbose(3) << "Resetting ParentList." << endl;
1052 for (int i=Father->AtomCount;i--;)
1053 ParentList[i] = NULL;
1054
1055 // fill parent list with sons
1056 *out << Verbose(3) << "Filling Parent List." << endl;
1057 Walker = start;
1058 while (Walker->next != end) {
1059 Walker = Walker->next;
1060 ParentList[Walker->father->nr] = Walker;
1061 // Outputting List for debugging
1062 *out << Verbose(4) << "Son["<< Walker->father->nr <<"] of " << Walker->father << " is " << ParentList[Walker->father->nr] << "." << endl;
1063 }
1064
1065 // check each entry of parent list and if ok (one-to-and-onto matching) create bonds
1066 *out << Verbose(3) << "Creating bonds." << endl;
1067 Walker = Father->start;
1068 while (Walker->next != Father->end) {
1069 Walker = Walker->next;
1070 if (ParentList[Walker->nr] != NULL) {
1071 if (ParentList[Walker->nr]->father != Walker) {
1072 status = false;
1073 } else {
[872b51]1074 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1075 OtherAtom = (*Runner)->GetOtherAtom(Walker);
[f92d00]1076 if (ParentList[OtherAtom->nr] != NULL) { // if otheratom is also a father of an atom on this molecule, create the bond
[872b51]1077 *out << Verbose(4) << "Endpoints of Bond " << (*Runner) << " are both present: " << ParentList[Walker->nr]->Name << " and " << ParentList[OtherAtom->nr]->Name << "." << endl;
1078 AddBond(ParentList[Walker->nr], ParentList[OtherAtom->nr], (*Runner)->BondDegree);
[f92d00]1079 }
1080 }
1081 }
1082 }
1083 }
1084
1085 Free(&ParentList);
1086 *out << Verbose(2) << "End of BuildInducedSubgraph." << endl;
1087 return status;
1088};
1089
1090
1091/** For a given keyset \a *Fragment, checks whether it is connected in the current molecule.
1092 * \param *out output stream for debugging
1093 * \param *Fragment Keyset of fragment's vertices
1094 * \return true - connected, false - disconnected
1095 * \note this is O(n^2) for it's just a bug checker not meant for permanent use!
1096 */
1097bool molecule::CheckForConnectedSubgraph(ofstream *out, KeySet *Fragment)
1098{
1099 atom *Walker = NULL, *Walker2 = NULL;
1100 bool BondStatus = false;
1101 int size;
1102
1103 *out << Verbose(1) << "Begin of CheckForConnectedSubgraph" << endl;
1104 *out << Verbose(2) << "Disconnected atom: ";
1105
1106 // count number of atoms in graph
1107 size = 0;
1108 for(KeySet::iterator runner = Fragment->begin(); runner != Fragment->end(); runner++)
1109 size++;
1110 if (size > 1)
1111 for(KeySet::iterator runner = Fragment->begin(); runner != Fragment->end(); runner++) {
1112 Walker = FindAtom(*runner);
1113 BondStatus = false;
1114 for(KeySet::iterator runners = Fragment->begin(); runners != Fragment->end(); runners++) {
1115 Walker2 = FindAtom(*runners);
[872b51]1116 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1117 if ((*Runner)->GetOtherAtom(Walker) == Walker2) {
[f92d00]1118 BondStatus = true;
1119 break;
1120 }
1121 if (BondStatus)
1122 break;
1123 }
1124 }
1125 if (!BondStatus) {
1126 *out << (*Walker) << endl;
1127 return false;
1128 }
1129 }
1130 else {
1131 *out << "none." << endl;
1132 return true;
1133 }
1134 *out << "none." << endl;
1135
1136 *out << Verbose(1) << "End of CheckForConnectedSubgraph" << endl;
1137
1138 return true;
1139}
Note: See TracBrowser for help on using the repository browser.