source: src/molecule_graph.cpp@ b453f9

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since b453f9 was 9eefda, checked in by Frederik Heber <heber@…>, 16 years ago

Added BFSAccounting and DFSAccounting structures and all functions use these. Added documentation to each of the new functions of previous commits.

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