source: src/molecule_graph.cpp@ 174e0e

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

Refactored molecule::DepthFirstSearchAnalysis()

  • Property mode set to 100644
File size: 51.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/** Analyses the cycles found and returns minimum of all cycle lengths.
497 * We begin with a list of Back edges found during DepthFirstSearchAnalysis(). We go through this list - one end is the Root,
498 * the other our initial Walker - and do a Breadth First Search for the Root. We mark down each Predecessor and as soon as
499 * we have found the Root via BFS, we may climb back the closed cycle via the Predecessors. Thereby we mark atoms and bonds
500 * as cyclic and print out the cycles.
501 * \param *out output stream for debugging
502 * \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!
503 * \param *&MinimumRingSize contains smallest ring size in molecular structure on return or -1 if no rings were found, if set is maximum search distance
504 * \todo BFS from the not-same-LP to find back to starting point of tributary cycle over more than one bond
505 */
506void molecule::CyclicStructureAnalysis(ofstream *out, class StackClass<bond *> * BackEdgeStack, int *&MinimumRingSize)
507{
508 atom **PredecessorList = Malloc<atom*>(AtomCount, "molecule::CyclicStructureAnalysis: **PredecessorList");
509 int *ShortestPathList = Malloc<int>(AtomCount, "molecule::CyclicStructureAnalysis: *ShortestPathList");
510 enum Shading *ColorList = Malloc<enum Shading>(AtomCount, "molecule::CyclicStructureAnalysis: *ColorList");
511 class StackClass<atom *> *BFSStack = new StackClass<atom *> (AtomCount); // will hold the current ring
512 class StackClass<atom *> *TouchedStack = new StackClass<atom *> (AtomCount); // contains all "touched" atoms (that need to be reset after BFS loop)
513 atom *Walker = NULL, *OtherAtom = NULL, *Root = NULL;
514 bond *Binder = NULL, *BackEdge = NULL;
515 int RingSize, NumCycles, MinRingSize = -1;
516
517 // initialise each vertex as white with no predecessor, empty queue, color Root lightgray
518 for (int i=AtomCount;i--;) {
519 PredecessorList[i] = NULL;
520 ShortestPathList[i] = -1;
521 ColorList[i] = white;
522 }
523
524 *out << Verbose(1) << "Back edge list - ";
525 BackEdgeStack->Output(out);
526
527 *out << Verbose(1) << "Analysing cycles ... " << endl;
528 NumCycles = 0;
529 while (!BackEdgeStack->IsEmpty()) {
530 BackEdge = BackEdgeStack->PopFirst();
531 // this is the target
532 Root = BackEdge->leftatom;
533 // this is the source point
534 Walker = BackEdge->rightatom;
535 ShortestPathList[Walker->nr] = 0;
536 BFSStack->ClearStack(); // start with empty BFS stack
537 BFSStack->Push(Walker);
538 TouchedStack->Push(Walker);
539 *out << Verbose(1) << "---------------------------------------------------------------------------------------------------------" << endl;
540 OtherAtom = NULL;
541 do { // look for Root
542 Walker = BFSStack->PopFirst();
543 *out << Verbose(2) << "Current Walker is " << *Walker << ", we look for SP to Root " << *Root << "." << endl;
544 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
545 if ((*Runner) != BackEdge) { // only walk along DFS spanning tree (otherwise we always find SP of one being backedge Binder)
546 OtherAtom = (*Runner)->GetOtherAtom(Walker);
547#ifdef ADDHYDROGEN
548 if (OtherAtom->type->Z != 1) {
549#endif
550 *out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *Binder << "." << endl;
551 if (ColorList[OtherAtom->nr] == white) {
552 TouchedStack->Push(OtherAtom);
553 ColorList[OtherAtom->nr] = lightgray;
554 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
555 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
556 *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;
557 //if (ShortestPathList[OtherAtom->nr] < MinimumRingSize[Walker->GetTrueFather()->nr]) { // Check for maximum distance
558 *out << Verbose(3) << "Putting OtherAtom into queue." << endl;
559 BFSStack->Push(OtherAtom);
560 //}
561 } else {
562 *out << Verbose(3) << "Not Adding, has already been visited." << endl;
563 }
564 if (OtherAtom == Root)
565 break;
566#ifdef ADDHYDROGEN
567 } else {
568 *out << Verbose(2) << "Skipping hydrogen atom " << *OtherAtom << "." << endl;
569 ColorList[OtherAtom->nr] = black;
570 }
571#endif
572 } else {
573 *out << Verbose(2) << "Bond " << *Binder << " not Visiting, is the back edge." << endl;
574 }
575 }
576 ColorList[Walker->nr] = black;
577 *out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
578 if (OtherAtom == Root) { // if we have found the root, check whether this cycle wasn't already found beforehand
579 // step through predecessor list
580 while (OtherAtom != BackEdge->rightatom) {
581 if (!OtherAtom->GetTrueFather()->IsCyclic) // if one bond in the loop is not marked as cyclic, we haven't found this cycle yet
582 break;
583 else
584 OtherAtom = PredecessorList[OtherAtom->nr];
585 }
586 if (OtherAtom == BackEdge->rightatom) { // if each atom in found cycle is cyclic, loop's been found before already
587 *out << Verbose(3) << "This cycle was already found before, skipping and removing seeker from search." << endl;\
588 do {
589 OtherAtom = TouchedStack->PopLast();
590 if (PredecessorList[OtherAtom->nr] == Walker) {
591 *out << Verbose(4) << "Removing " << *OtherAtom << " from lists and stacks." << endl;
592 PredecessorList[OtherAtom->nr] = NULL;
593 ShortestPathList[OtherAtom->nr] = -1;
594 ColorList[OtherAtom->nr] = white;
595 BFSStack->RemoveItem(OtherAtom);
596 }
597 } while ((!TouchedStack->IsEmpty()) && (PredecessorList[OtherAtom->nr] == NULL));
598 TouchedStack->Push(OtherAtom); // last was wrongly popped
599 OtherAtom = BackEdge->rightatom; // set to not Root
600 } else
601 OtherAtom = Root;
602 }
603 } while ((!BFSStack->IsEmpty()) && (OtherAtom != Root) && (OtherAtom != NULL)); // || (ShortestPathList[OtherAtom->nr] < MinimumRingSize[Walker->GetTrueFather()->nr])));
604
605 if (OtherAtom == Root) {
606 // now climb back the predecessor list and thus find the cycle members
607 NumCycles++;
608 RingSize = 1;
609 Root->GetTrueFather()->IsCyclic = true;
610 *out << Verbose(1) << "Found ring contains: ";
611 Walker = Root;
612 while (Walker != BackEdge->rightatom) {
613 *out << Walker->Name << " <-> ";
614 Walker = PredecessorList[Walker->nr];
615 Walker->GetTrueFather()->IsCyclic = true;
616 RingSize++;
617 }
618 *out << Walker->Name << " with a length of " << RingSize << "." << endl << endl;
619 // walk through all and set MinimumRingSize
620 Walker = Root;
621 MinimumRingSize[Walker->GetTrueFather()->nr] = RingSize;
622 while (Walker != BackEdge->rightatom) {
623 Walker = PredecessorList[Walker->nr];
624 if (RingSize < MinimumRingSize[Walker->GetTrueFather()->nr])
625 MinimumRingSize[Walker->GetTrueFather()->nr] = RingSize;
626 }
627 if ((RingSize < MinRingSize) || (MinRingSize == -1))
628 MinRingSize = RingSize;
629 } else {
630 *out << Verbose(1) << "No ring containing " << *Root << " with length equal to or smaller than " << MinimumRingSize[Walker->GetTrueFather()->nr] << " found." << endl;
631 }
632
633 // now clean the lists
634 while (!TouchedStack->IsEmpty()){
635 Walker = TouchedStack->PopFirst();
636 PredecessorList[Walker->nr] = NULL;
637 ShortestPathList[Walker->nr] = -1;
638 ColorList[Walker->nr] = white;
639 }
640 }
641 if (MinRingSize != -1) {
642 // go over all atoms
643 Root = start;
644 while(Root->next != end) {
645 Root = Root->next;
646
647 if (MinimumRingSize[Root->GetTrueFather()->nr] == AtomCount) { // check whether MinimumRingSize is set, if not BFS to next where it is
648 Walker = Root;
649 ShortestPathList[Walker->nr] = 0;
650 BFSStack->ClearStack(); // start with empty BFS stack
651 BFSStack->Push(Walker);
652 TouchedStack->Push(Walker);
653 //*out << Verbose(1) << "---------------------------------------------------------------------------------------------------------" << endl;
654 OtherAtom = Walker;
655 while (OtherAtom != NULL) { // look for Root
656 Walker = BFSStack->PopFirst();
657 //*out << Verbose(2) << "Current Walker is " << *Walker << ", we look for SP to Root " << *Root << "." << endl;
658 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
659 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
660 OtherAtom = (*Runner)->GetOtherAtom(Walker);
661 //*out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *Binder << "." << endl;
662 if (ColorList[OtherAtom->nr] == white) {
663 TouchedStack->Push(OtherAtom);
664 ColorList[OtherAtom->nr] = lightgray;
665 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
666 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
667 //*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;
668 if (OtherAtom->GetTrueFather()->IsCyclic) { // if the other atom is connected to a ring
669 MinimumRingSize[Root->GetTrueFather()->nr] = ShortestPathList[OtherAtom->nr]+MinimumRingSize[OtherAtom->GetTrueFather()->nr];
670 OtherAtom = NULL; //break;
671 break;
672 } else
673 BFSStack->Push(OtherAtom);
674 } else {
675 //*out << Verbose(3) << "Not Adding, has already been visited." << endl;
676 }
677 } else {
678 //*out << Verbose(3) << "Not Visiting, is a back edge." << endl;
679 }
680 }
681 ColorList[Walker->nr] = black;
682 //*out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
683 }
684
685 // now clean the lists
686 while (!TouchedStack->IsEmpty()){
687 Walker = TouchedStack->PopFirst();
688 PredecessorList[Walker->nr] = NULL;
689 ShortestPathList[Walker->nr] = -1;
690 ColorList[Walker->nr] = white;
691 }
692 }
693 *out << Verbose(1) << "Minimum ring size of " << *Root << " is " << MinimumRingSize[Root->GetTrueFather()->nr] << "." << endl;
694 }
695 *out << Verbose(1) << "Minimum ring size is " << MinRingSize << ", over " << NumCycles << " cycles total." << endl;
696 } else
697 *out << Verbose(1) << "No rings were detected in the molecular structure." << endl;
698
699 Free(&PredecessorList);
700 Free(&ShortestPathList);
701 Free(&ColorList);
702 delete(BFSStack);
703};
704
705/** Sets the next component number.
706 * This is O(N) as the number of bonds per atom is bound.
707 * \param *vertex atom whose next atom::*ComponentNr is to be set
708 * \param nr number to use
709 */
710void molecule::SetNextComponentNumber(atom *vertex, int nr)
711{
712 size_t i=0;
713 if (vertex != NULL) {
714 for(;i<vertex->ListOfBonds.size();i++) {
715 if (vertex->ComponentNr[i] == -1) { // check if not yet used
716 vertex->ComponentNr[i] = nr;
717 break;
718 }
719 else if (vertex->ComponentNr[i] == nr) // if number is already present, don't add another time
720 break; // breaking here will not cause error!
721 }
722 if (i == vertex->ListOfBonds.size())
723 cerr << "Error: All Component entries are already occupied!" << endl;
724 } else
725 cerr << "Error: Given vertex is NULL!" << endl;
726};
727
728/** Allocates memory for all atom::*ComponentNr in this molecule and sets each entry to -1.
729 */
730void molecule::InitComponentNumbers()
731{
732 atom *Walker = start;
733 while(Walker->next != end) {
734 Walker = Walker->next;
735 if (Walker->ComponentNr != NULL)
736 Free(&Walker->ComponentNr);
737 Walker->ComponentNr = Malloc<int>(Walker->ListOfBonds.size()+1, "molecule::InitComponentNumbers: *Walker->ComponentNr");
738 for (int i=Walker->ListOfBonds.size()+1;i--;)
739 Walker->ComponentNr[i] = -1;
740 }
741};
742
743/** Returns next unused bond for this atom \a *vertex or NULL of none exists.
744 * \param *vertex atom to regard
745 * \return bond class or NULL
746 */
747bond * molecule::FindNextUnused(atom *vertex)
748{
749 for (BondList::const_iterator Runner = vertex->ListOfBonds.begin(); Runner != vertex->ListOfBonds.end(); (++Runner))
750 if ((*Runner)->IsUsed() == white)
751 return((*Runner));
752 return NULL;
753};
754
755/** Resets bond::Used flag of all bonds in this molecule.
756 * \return true - success, false - -failure
757 */
758void molecule::ResetAllBondsToUnused()
759{
760 bond *Binder = first;
761 while (Binder->next != last) {
762 Binder = Binder->next;
763 Binder->ResetUsed();
764 }
765};
766
767/** Resets atom::nr to -1 of all atoms in this molecule.
768 */
769void molecule::ResetAllAtomNumbers()
770{
771 atom *Walker = start;
772 while (Walker->next != end) {
773 Walker = Walker->next;
774 Walker->GraphNr = -1;
775 }
776};
777
778/** Output a list of flags, stating whether the bond was visited or not.
779 * \param *out output stream for debugging
780 * \param *list
781 */
782void OutputAlreadyVisited(ofstream *out, int *list)
783{
784 *out << Verbose(4) << "Already Visited Bonds:\t";
785 for(int i=1;i<=list[0];i++) *out << Verbose(0) << list[i] << " ";
786 *out << endl;
787};
788
789
790/** Storing the bond structure of a molecule to file.
791 * Simply stores Atom::nr and then the Atom::nr of all bond partners per line.
792 * \param *out output stream for debugging
793 * \param *path path to file
794 * \return true - file written successfully, false - writing failed
795 */
796bool molecule::StoreAdjacencyToFile(ofstream *out, char *path)
797{
798 ofstream AdjacencyFile;
799 stringstream line;
800 bool status = true;
801
802 line << path << "/" << FRAGMENTPREFIX << ADJACENCYFILE;
803 AdjacencyFile.open(line.str().c_str(), ios::out);
804 *out << Verbose(1) << "Saving adjacency list ... ";
805 if (AdjacencyFile != NULL) {
806 ActOnAllAtoms( &atom::OutputAdjacency, &AdjacencyFile );
807 AdjacencyFile.close();
808 *out << Verbose(1) << "done." << endl;
809 } else {
810 *out << Verbose(1) << "failed to open file " << line.str() << "." << endl;
811 status = false;
812 }
813
814 return status;
815};
816
817/** Checks contents of adjacency file against bond structure in structure molecule.
818 * \param *out output stream for debugging
819 * \param *path path to file
820 * \param **ListOfAtoms allocated (molecule::AtomCount) and filled lookup table for ids (Atom::nr) to *Atom
821 * \return true - structure is equal, false - not equivalence
822 */
823bool molecule::CheckAdjacencyFileAgainstMolecule(ofstream *out, char *path, atom **ListOfAtoms)
824{
825 ifstream File;
826 stringstream filename;
827 bool status = true;
828 atom *Walker = NULL;
829 char *buffer = Malloc<char>(MAXSTRINGSIZE, "molecule::CheckAdjacencyFileAgainstMolecule: *buffer");
830
831 filename << path << "/" << FRAGMENTPREFIX << ADJACENCYFILE;
832 File.open(filename.str().c_str(), ios::out);
833 *out << Verbose(1) << "Looking at bond structure stored in adjacency file and comparing to present one ... ";
834 if (File != NULL) {
835 // allocate storage structure
836 int NonMatchNumber = 0; // will number of atoms with differing bond structure
837 int *CurrentBonds = Malloc<int>(8, "molecule::CheckAdjacencyFileAgainstMolecule - CurrentBonds"); // contains parsed bonds of current atom
838 size_t CurrentBondsOfAtom;
839
840 // Parse the file line by line and count the bonds
841 while (!File.eof()) {
842 File.getline(buffer, MAXSTRINGSIZE);
843 stringstream line;
844 line.str(buffer);
845 int AtomNr = -1;
846 line >> AtomNr;
847 CurrentBondsOfAtom = -1; // we count one too far due to line end
848 // parse into structure
849 if ((AtomNr >= 0) && (AtomNr < AtomCount)) {
850 Walker = ListOfAtoms[AtomNr];
851 while (!line.eof())
852 line >> CurrentBonds[ ++CurrentBondsOfAtom ];
853 // compare against present bonds
854 //cout << Verbose(2) << "Walker is " << *Walker << ", bond partners: ";
855 if (CurrentBondsOfAtom == Walker->ListOfBonds.size()) {
856 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
857 int id = (*Runner)->GetOtherAtom(Walker)->nr;
858 size_t j = 0;
859 for (;(j<CurrentBondsOfAtom) && (CurrentBonds[j++] != id);); // check against all parsed bonds
860 if (CurrentBonds[j-1] != id) { // no match ? Then mark in ListOfAtoms
861 ListOfAtoms[AtomNr] = NULL;
862 NonMatchNumber++;
863 status = false;
864 //out << "[" << id << "]\t";
865 } else {
866 //out << id << "\t";
867 }
868 }
869 //out << endl;
870 } else {
871 *out << "Number of bonds for Atom " << *Walker << " does not match, parsed " << CurrentBondsOfAtom << " against " << Walker->ListOfBonds.size() << "." << endl;
872 status = false;
873 }
874 }
875 }
876 File.close();
877 File.clear();
878 if (status) { // if equal we parse the KeySetFile
879 *out << Verbose(1) << "done: Equal." << endl;
880 status = true;
881 } else
882 *out << Verbose(1) << "done: Not equal by " << NonMatchNumber << " atoms." << endl;
883 Free(&CurrentBonds);
884 } else {
885 *out << Verbose(1) << "Adjacency file not found." << endl;
886 status = false;
887 }
888 *out << endl;
889 Free(&buffer);
890
891 return status;
892};
893
894
895/** Picks from a global stack with all back edges the ones in the fragment.
896 * \param *out output stream for debugging
897 * \param **ListOfLocalAtoms array of father atom::nr to local atom::nr (reverse of atom::father)
898 * \param *ReferenceStack stack with all the back egdes
899 * \param *LocalStack stack to be filled
900 * \return true - everything ok, false - ReferenceStack was empty
901 */
902bool molecule::PickLocalBackEdges(ofstream *out, atom **ListOfLocalAtoms, class StackClass<bond *> *&ReferenceStack, class StackClass<bond *> *&LocalStack)
903{
904 bool status = true;
905 if (ReferenceStack->IsEmpty()) {
906 cerr << "ReferenceStack is empty!" << endl;
907 return false;
908 }
909 bond *Binder = ReferenceStack->PopFirst();
910 bond *FirstBond = Binder; // mark the first bond, so that we don't loop through the stack indefinitely
911 atom *Walker = NULL, *OtherAtom = NULL;
912 ReferenceStack->Push(Binder);
913
914 do { // go through all bonds and push local ones
915 Walker = ListOfLocalAtoms[Binder->leftatom->nr]; // get one atom in the reference molecule
916 if (Walker != NULL) // if this Walker exists in the subgraph ...
917 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
918 OtherAtom = (*Runner)->GetOtherAtom(Walker);
919 if (OtherAtom == ListOfLocalAtoms[(*Runner)->rightatom->nr]) { // found the bond
920 LocalStack->Push((*Runner));
921 *out << Verbose(3) << "Found local edge " << *(*Runner) << "." << endl;
922 break;
923 }
924 }
925 Binder = ReferenceStack->PopFirst(); // loop the stack for next item
926 *out << Verbose(3) << "Current candidate edge " << Binder << "." << endl;
927 ReferenceStack->Push(Binder);
928 } while (FirstBond != Binder);
929
930 return status;
931};
932
933
934/** Adds atoms up to \a BondCount distance from \a *Root and notes them down in \a **AddedAtomList.
935 * Gray vertices are always enqueued in an StackClass<atom *> FIFO queue, the rest is usual BFS with adding vertices found was
936 * white and putting into queue.
937 * \param *out output stream for debugging
938 * \param *Mol Molecule class to add atoms to
939 * \param **AddedAtomList list with added atom pointers, index is atom father's number
940 * \param **AddedBondList list with added bond pointers, index is bond father's number
941 * \param *Root root vertex for BFS
942 * \param *Bond bond not to look beyond
943 * \param BondOrder maximum distance for vertices to add
944 * \param IsAngstroem lengths are in angstroem or bohrradii
945 */
946void molecule::BreadthFirstSearchAdd(ofstream *out, molecule *Mol, atom **&AddedAtomList, bond **&AddedBondList, atom *Root, bond *Bond, int BondOrder, bool IsAngstroem)
947{
948 atom **PredecessorList = Malloc<atom*>(AtomCount, "molecule::BreadthFirstSearchAdd: **PredecessorList");
949 int *ShortestPathList = Malloc<int>(AtomCount, "molecule::BreadthFirstSearchAdd: *ShortestPathList");
950 enum Shading *ColorList = Malloc<enum Shading>(AtomCount, "molecule::BreadthFirstSearchAdd: *ColorList");
951 class StackClass<atom *> *AtomStack = new StackClass<atom *>(AtomCount);
952 atom *Walker = NULL, *OtherAtom = NULL;
953
954 // add Root if not done yet
955 AtomStack->ClearStack();
956 if (AddedAtomList[Root->nr] == NULL) // add Root if not yet present
957 AddedAtomList[Root->nr] = Mol->AddCopyAtom(Root);
958 AtomStack->Push(Root);
959
960 // initialise each vertex as white with no predecessor, empty queue, color Root lightgray
961 for (int i=AtomCount;i--;) {
962 PredecessorList[i] = NULL;
963 ShortestPathList[i] = -1;
964 if (AddedAtomList[i] != NULL) // mark already present atoms (i.e. Root and maybe others) as visited
965 ColorList[i] = lightgray;
966 else
967 ColorList[i] = white;
968 }
969 ShortestPathList[Root->nr] = 0;
970
971 // and go on ... Queue always contains all lightgray vertices
972 while (!AtomStack->IsEmpty()) {
973 // we have to pop the oldest atom from stack. This keeps the atoms on the stack always of the same ShortestPath distance.
974 // 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
975 // append length of 3 (their neighbours). Thus on stack we have always atoms of a certain length n at bottom of stack and
976 // followed by n+1 till top of stack.
977 Walker = AtomStack->PopFirst(); // pop oldest added
978 *out << Verbose(1) << "Current Walker is: " << Walker->Name << ", and has " << Walker->ListOfBonds.size() << " bonds." << endl;
979 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
980 if ((*Runner) != NULL) { // don't look at bond equal NULL
981 OtherAtom = (*Runner)->GetOtherAtom(Walker);
982 *out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *(*Runner) << "." << endl;
983 if (ColorList[OtherAtom->nr] == white) {
984 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)
985 ColorList[OtherAtom->nr] = lightgray;
986 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
987 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
988 *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;
989 if ((((ShortestPathList[OtherAtom->nr] < BondOrder) && ((*Runner) != Bond))) ) { // Check for maximum distance
990 *out << Verbose(3);
991 if (AddedAtomList[OtherAtom->nr] == NULL) { // add if it's not been so far
992 AddedAtomList[OtherAtom->nr] = Mol->AddCopyAtom(OtherAtom);
993 *out << "Added OtherAtom " << OtherAtom->Name;
994 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
995 *out << " and bond " << *(AddedBondList[(*Runner)->nr]) << ", ";
996 } 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)
997 *out << "Not adding OtherAtom " << OtherAtom->Name;
998 if (AddedBondList[(*Runner)->nr] == NULL) {
999 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
1000 *out << ", added Bond " << *(AddedBondList[(*Runner)->nr]);
1001 } else
1002 *out << ", not added Bond ";
1003 }
1004 *out << ", putting OtherAtom into queue." << endl;
1005 AtomStack->Push(OtherAtom);
1006 } else { // out of bond order, then replace
1007 if ((AddedAtomList[OtherAtom->nr] == NULL) && ((*Runner)->Cyclic))
1008 ColorList[OtherAtom->nr] = white; // unmark if it has not been queued/added, to make it available via its other bonds (cyclic)
1009 if ((*Runner) == Bond)
1010 *out << Verbose(3) << "Not Queueing, is the Root bond";
1011 else if (ShortestPathList[OtherAtom->nr] >= BondOrder)
1012 *out << Verbose(3) << "Not Queueing, is out of Bond Count of " << BondOrder;
1013 if (!(*Runner)->Cyclic)
1014 *out << ", is not part of a cyclic bond, saturating bond with Hydrogen." << endl;
1015 if (AddedBondList[(*Runner)->nr] == NULL) {
1016 if ((AddedAtomList[OtherAtom->nr] != NULL)) { // .. whether we add or saturate
1017 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
1018 } else {
1019#ifdef ADDHYDROGEN
1020 if (!Mol->AddHydrogenReplacementAtom(out, (*Runner), AddedAtomList[Walker->nr], Walker, OtherAtom, IsAngstroem))
1021 exit(1);
1022#endif
1023 }
1024 }
1025 }
1026 } else {
1027 *out << Verbose(3) << "Not Adding, has already been visited." << endl;
1028 // This has to be a cyclic bond, check whether it's present ...
1029 if (AddedBondList[(*Runner)->nr] == NULL) {
1030 if (((*Runner) != Bond) && ((*Runner)->Cyclic) && (((ShortestPathList[Walker->nr]+1) < BondOrder))) {
1031 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
1032 } else { // if it's root bond it has to broken (otherwise we would not create the fragments)
1033#ifdef ADDHYDROGEN
1034 if(!Mol->AddHydrogenReplacementAtom(out, (*Runner), AddedAtomList[Walker->nr], Walker, OtherAtom, IsAngstroem))
1035 exit(1);
1036#endif
1037 }
1038 }
1039 }
1040 }
1041 }
1042 ColorList[Walker->nr] = black;
1043 *out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
1044 }
1045 Free(&PredecessorList);
1046 Free(&ShortestPathList);
1047 Free(&ColorList);
1048 delete(AtomStack);
1049};
1050
1051/** Adds a bond as a copy to a given one
1052 * \param *left leftatom of new bond
1053 * \param *right rightatom of new bond
1054 * \param *CopyBond rest of fields in bond are copied from this
1055 * \return pointer to new bond
1056 */
1057bond * molecule::CopyBond(atom *left, atom *right, bond *CopyBond)
1058{
1059 bond *Binder = AddBond(left, right, CopyBond->BondDegree);
1060 Binder->Cyclic = CopyBond->Cyclic;
1061 Binder->Type = CopyBond->Type;
1062 return Binder;
1063};
1064
1065
1066/** Adds bond structure to this molecule from \a Father molecule.
1067 * This basically causes this molecule to become an induced subgraph of the \a Father, i.e. for every bond in Father
1068 * with end points present in this molecule, bond is created in this molecule.
1069 * Special care was taken to ensure that this is of complexity O(N), where N is the \a Father's molecule::AtomCount.
1070 * \param *out output stream for debugging
1071 * \param *Father father molecule
1072 * \return true - is induced subgraph, false - there are atoms with fathers not in \a Father
1073 * \todo not checked, not fully working probably
1074 */
1075bool molecule::BuildInducedSubgraph(ofstream *out, const molecule *Father)
1076{
1077 atom *Walker = NULL, *OtherAtom = NULL;
1078 bool status = true;
1079 atom **ParentList = Malloc<atom*>(Father->AtomCount, "molecule::BuildInducedSubgraph: **ParentList");
1080
1081 *out << Verbose(2) << "Begin of BuildInducedSubgraph." << endl;
1082
1083 // reset parent list
1084 *out << Verbose(3) << "Resetting ParentList." << endl;
1085 for (int i=Father->AtomCount;i--;)
1086 ParentList[i] = NULL;
1087
1088 // fill parent list with sons
1089 *out << Verbose(3) << "Filling Parent List." << endl;
1090 Walker = start;
1091 while (Walker->next != end) {
1092 Walker = Walker->next;
1093 ParentList[Walker->father->nr] = Walker;
1094 // Outputting List for debugging
1095 *out << Verbose(4) << "Son["<< Walker->father->nr <<"] of " << Walker->father << " is " << ParentList[Walker->father->nr] << "." << endl;
1096 }
1097
1098 // check each entry of parent list and if ok (one-to-and-onto matching) create bonds
1099 *out << Verbose(3) << "Creating bonds." << endl;
1100 Walker = Father->start;
1101 while (Walker->next != Father->end) {
1102 Walker = Walker->next;
1103 if (ParentList[Walker->nr] != NULL) {
1104 if (ParentList[Walker->nr]->father != Walker) {
1105 status = false;
1106 } else {
1107 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1108 OtherAtom = (*Runner)->GetOtherAtom(Walker);
1109 if (ParentList[OtherAtom->nr] != NULL) { // if otheratom is also a father of an atom on this molecule, create the bond
1110 *out << Verbose(4) << "Endpoints of Bond " << (*Runner) << " are both present: " << ParentList[Walker->nr]->Name << " and " << ParentList[OtherAtom->nr]->Name << "." << endl;
1111 AddBond(ParentList[Walker->nr], ParentList[OtherAtom->nr], (*Runner)->BondDegree);
1112 }
1113 }
1114 }
1115 }
1116 }
1117
1118 Free(&ParentList);
1119 *out << Verbose(2) << "End of BuildInducedSubgraph." << endl;
1120 return status;
1121};
1122
1123
1124/** For a given keyset \a *Fragment, checks whether it is connected in the current molecule.
1125 * \param *out output stream for debugging
1126 * \param *Fragment Keyset of fragment's vertices
1127 * \return true - connected, false - disconnected
1128 * \note this is O(n^2) for it's just a bug checker not meant for permanent use!
1129 */
1130bool molecule::CheckForConnectedSubgraph(ofstream *out, KeySet *Fragment)
1131{
1132 atom *Walker = NULL, *Walker2 = NULL;
1133 bool BondStatus = false;
1134 int size;
1135
1136 *out << Verbose(1) << "Begin of CheckForConnectedSubgraph" << endl;
1137 *out << Verbose(2) << "Disconnected atom: ";
1138
1139 // count number of atoms in graph
1140 size = 0;
1141 for(KeySet::iterator runner = Fragment->begin(); runner != Fragment->end(); runner++)
1142 size++;
1143 if (size > 1)
1144 for(KeySet::iterator runner = Fragment->begin(); runner != Fragment->end(); runner++) {
1145 Walker = FindAtom(*runner);
1146 BondStatus = false;
1147 for(KeySet::iterator runners = Fragment->begin(); runners != Fragment->end(); runners++) {
1148 Walker2 = FindAtom(*runners);
1149 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1150 if ((*Runner)->GetOtherAtom(Walker) == Walker2) {
1151 BondStatus = true;
1152 break;
1153 }
1154 if (BondStatus)
1155 break;
1156 }
1157 }
1158 if (!BondStatus) {
1159 *out << (*Walker) << endl;
1160 return false;
1161 }
1162 }
1163 else {
1164 *out << "none." << endl;
1165 return true;
1166 }
1167 *out << "none." << endl;
1168
1169 *out << Verbose(1) << "End of CheckForConnectedSubgraph" << endl;
1170
1171 return true;
1172}
Note: See TracBrowser for help on using the repository browser.