source: src/molecule_graph.cpp@ 38c44b

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

BondedParticleInfo now has vector<BondList>

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