source: src/molecule_graph.cpp@ e08c46

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

Removed molecule::first, molecule::last.

  • molecule does not have a chained list of bonds.
  • we have to go through atoms and its bonds, by checking (*BondRunner)->leftatom against (*AtomRunner) we exclude the other half.
  • first,last were present in only a few structures.
  • new functions:
    • molecule::hasBondStructure() - replaces first->next != last construct
    • molecule::CountBonds() - replaces first->next->next == last (i.e. one bond present) and alikes.

Signed-off-by: Frederik Heber <heber@…>

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