source: src/molecule_graph.cpp@ 18eecf

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

Merge branch 'VectorRefactoring' into StructureRefactoring

Conflicts:

molecuilder/src/Legacy/oldmenu.cpp
molecuilder/src/Makefile.am
molecuilder/src/analysis_correlation.cpp
molecuilder/src/boundary.cpp
molecuilder/src/builder.cpp
molecuilder/src/config.cpp
molecuilder/src/ellipsoid.cpp
molecuilder/src/linkedcell.cpp
molecuilder/src/molecule.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/tesselationhelpers.cpp
molecuilder/src/unittests/AnalysisCorrelationToSurfaceUnitTest.cpp
molecuilder/src/unittests/bondgraphunittest.cpp
molecuilder/src/vector.cpp
molecuilder/src/vector.hpp

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