source: src/molecule_graph.cpp@ 1024cb

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

Merge commit 'jupiter/MoleculeStartEndSwitch' into CommandLineActionMapping

Conflicts:

molecuilder/src/Makefile.am
molecuilder/src/builder.cpp
molecuilder/src/config.cpp
molecuilder/src/helpers.hpp
molecuilder/src/molecule.cpp
molecuilder/src/molecule_dynamics.cpp
molecuilder/src/molecule_fragmentation.cpp
molecuilder/src/molecule_geometry.cpp
molecuilder/src/molecule_graph.cpp
molecuilder/src/moleculelist.cpp
molecuilder/src/unittests/AnalysisCorrelationToPointUnitTest.cpp
molecuilder/src/unittests/listofbondsunittest.cpp

Integration of MoleculeStartEndSwitch had the following consequences:

  • no more AtomCount -> getAtomCount()
  • no more start/end -> begin(), end() and iterator
  • no more decent ordering in atomic ids (hence, Simple_configuration/8 and Domain/5, Domain/6 now check by comparing sorted xyz, not confs)

There is still a huge problem with bonds. One test runs into an endless loop.

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

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