source: src/molecule_graph.cpp@ 1a6bda

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

BUGFIX: CheckAdjacencyFileAgainstMolecule() hiccup'ed on parsed files.

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