source: src/molecule_graph.cpp@ c68c90

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

Added case '-A' (parsing bonds) to testsuite.

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