source: src/molecule_graph.cpp@ bd6bfa

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

Fixes after first,last removal.

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