source: src/molecule_graph.cpp@ 12cf773

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

Made the world store the cell_size within a Box object.

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