source: src/molecule_graph.cpp@ a7b761b

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 a7b761b was a7b761b, checked in by Tillmann Crueger <crueger@…>, 15 years ago

Merge branch 'MoleculeStartEndSwitch' into StructureRefactoring

Conflicts:

molecuilder/src/Helpers/Assert.cpp
molecuilder/src/Helpers/Assert.hpp
molecuilder/src/Legacy/oldmenu.cpp
molecuilder/src/Makefile.am
molecuilder/src/Patterns/Cacheable.hpp
molecuilder/src/Patterns/Observer.cpp
molecuilder/src/Patterns/Observer.hpp
molecuilder/src/analysis_correlation.cpp
molecuilder/src/boundary.cpp
molecuilder/src/builder.cpp
molecuilder/src/config.cpp
molecuilder/src/helpers.hpp
molecuilder/src/molecule.cpp
molecuilder/src/molecule.hpp
molecuilder/src/molecule_dynamics.cpp
molecuilder/src/molecule_fragmentation.cpp
molecuilder/src/molecule_geometry.cpp
molecuilder/src/molecule_graph.cpp
molecuilder/src/moleculelist.cpp
molecuilder/src/tesselation.cpp
molecuilder/src/unittests/AnalysisCorrelationToSurfaceUnitTest.cpp
molecuilder/src/unittests/ObserverTest.cpp
molecuilder/src/unittests/ObserverTest.hpp

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