source: src/molecule_graph.cpp@ 673c7f

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 673c7f was 8cbb97, checked in by Tillmann Crueger <crueger@…>, 15 years ago

Merge branch 'VectorRefactoring' into StructureRefactoring

Conflicts:

molecuilder/src/Legacy/oldmenu.cpp
molecuilder/src/Makefile.am
molecuilder/src/analysis_correlation.cpp
molecuilder/src/boundary.cpp
molecuilder/src/builder.cpp
molecuilder/src/config.cpp
molecuilder/src/ellipsoid.cpp
molecuilder/src/linkedcell.cpp
molecuilder/src/molecule.cpp
molecuilder/src/molecule_fragmentation.cpp
molecuilder/src/molecule_geometry.cpp
molecuilder/src/molecule_graph.cpp
molecuilder/src/moleculelist.cpp
molecuilder/src/tesselation.cpp
molecuilder/src/tesselationhelpers.cpp
molecuilder/src/unittests/AnalysisCorrelationToSurfaceUnitTest.cpp
molecuilder/src/unittests/bondgraphunittest.cpp
molecuilder/src/vector.cpp
molecuilder/src/vector.hpp

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