source: src/molecule_graph.cpp@ ef9aae

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

Refactored molecule::CyclicStructureAnalysis()

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