source: src/molecule_graph.cpp@ bf3817

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

Added ifdef HAVE_CONFIG and config.h include to each and every cpp file.

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