source: src/molecule_graph.cpp@ b0a5f1

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

Huge change: Log() << Verbose(.) --> DoLog(.) && (Log() << Verbose(.) << ...);

Most of the files are affected, but this is necessary as if DoLog() says verbosity is not enough, all the stream operators won"t get executed which saves substantial amount of computation time.

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

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