source: src/molecule_graph.cpp@ a0064e

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

Moved stuff in src/Helpers and src/Patterns out to stand-alone project CodePatterns.

Makefile.am's:

  • CodePatterns added to AM_LDFLAGS and AM_CFLAGS
  • libMoleCuilderHelpers removed

Helpers/...

  • defs.hpp include prefixed with Helpers/
  • helpers.?pp removed lots of old, unused functions: bound, ask_value, ...
  • fast_functions.hpp has lots of functions removed as well.
  • all other files and unit tests moved to project CodePatterns.

Patterns/...

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