source: src/molecule_graph.cpp@ 28c351

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 28c351 was 5f612ee, checked in by Frederik Heber <heber@…>, 15 years ago

Merge branch 'Analysis_PairCorrelation' into StructureRefactoring

Conflicts:

molecuilder/src/Makefile.am
molecuilder/src/World.cpp
molecuilder/src/World.hpp
molecuilder/src/boundary.cpp
molecuilder/src/builder.cpp
molecuilder/src/log.cpp
molecuilder/src/moleculelist.cpp
molecuilder/src/periodentafel.cpp
molecuilder/src/tesselation.cpp
molecuilder/src/unittests/AnalysisCorrelationToSurfaceUnitTest.cpp
molecuilder/src/unittests/Makefile.am
molecuilder/src/unittests/bondgraphunittest.cpp
molecuilder/src/unittests/gslvectorunittest.cpp
molecuilder/src/unittests/logunittest.cpp
molecuilder/src/unittests/tesselation_boundarytriangleunittest.hpp
molecuilder/src/vector.cpp
molecuilder/tests/Tesselations/defs.in

Conflicts have been many and too numerous to listen here, just the few general cases

  • new molecule() replaced by World::getInstance().createMolecule()
  • new atom() replaced by World::getInstance().createAtom() where appropriate.
  • Some DoLog()s added interfered with changes to the message produced by Log() << Verbose(.) << ...
  • DoLog() has been erroneously added to TestRunner.cpp as well, there cout is appropriate
  • ...

Additionally, there was a bug in atom::clone(), sort was set to atom::nr of the atom to clone not of the clone itself. This caused a failure of the fragmentation.

This merge has been fully checked from a clean build directory with subsequent configure,make all install and make check.
It configures, compiles and runs all test cases and the test suite without errors.

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

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