source: src/molecule_graph.cpp@ 458c31

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 Candidate_v1.7.0 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 458c31 was 458c31, checked in by Frederik Heber <heber@…>, 15 years ago

molecule::BondCount is now Cachable and private.

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