source: src/molecule_graph.cpp@ b9772a

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

Renamed function pointer in molecule::CreateAdjacencyList to minmaxdistance.

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