source: src/molecule_graph.cpp@ ad37ab

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 Candidate_v1.7.0 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since ad37ab was 9eefda, checked in by Frederik Heber <heber@…>, 16 years ago

Added BFSAccounting and DFSAccounting structures and all functions use these. Added documentation to each of the new functions of previous commits.

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