source: src/molecule_graph.cpp@ 43587e

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

Refactored molecule::BuildInducedSubgraph()

  • new functions BuildInducedSubgraph_Init(), BuildInducedSubgraph_FillParentList(), BuildInducedSubgraph_CreateBondsFromParentList(), BuildInducedSubgraph_Finalize()
  • Property mode set to 100644
File size: 54.9 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 SetAtomValueToValue( -1, &atom::GraphNr );
390 ActOnAllAtoms( &atom::InitComponentNr );
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/** Returns next unused bond for this atom \a *vertex or NULL of none exists.
786 * \param *vertex atom to regard
787 * \return bond class or NULL
788 */
789bond * molecule::FindNextUnused(atom *vertex)
790{
791 for (BondList::const_iterator Runner = vertex->ListOfBonds.begin(); Runner != vertex->ListOfBonds.end(); (++Runner))
792 if ((*Runner)->IsUsed() == white)
793 return((*Runner));
794 return NULL;
795};
796
797/** Resets bond::Used flag of all bonds in this molecule.
798 * \return true - success, false - -failure
799 */
800void molecule::ResetAllBondsToUnused()
801{
802 bond *Binder = first;
803 while (Binder->next != last) {
804 Binder = Binder->next;
805 Binder->ResetUsed();
806 }
807};
808
809/** Output a list of flags, stating whether the bond was visited or not.
810 * \param *out output stream for debugging
811 * \param *list
812 */
813void OutputAlreadyVisited(ofstream *out, int *list)
814{
815 *out << Verbose(4) << "Already Visited Bonds:\t";
816 for(int i=1;i<=list[0];i++) *out << Verbose(0) << list[i] << " ";
817 *out << endl;
818};
819
820
821/** Storing the bond structure of a molecule to file.
822 * Simply stores Atom::nr and then the Atom::nr of all bond partners per line.
823 * \param *out output stream for debugging
824 * \param *path path to file
825 * \return true - file written successfully, false - writing failed
826 */
827bool molecule::StoreAdjacencyToFile(ofstream *out, char *path)
828{
829 ofstream AdjacencyFile;
830 stringstream line;
831 bool status = true;
832
833 line << path << "/" << FRAGMENTPREFIX << ADJACENCYFILE;
834 AdjacencyFile.open(line.str().c_str(), ios::out);
835 *out << Verbose(1) << "Saving adjacency list ... ";
836 if (AdjacencyFile != NULL) {
837 ActOnAllAtoms( &atom::OutputAdjacency, &AdjacencyFile );
838 AdjacencyFile.close();
839 *out << Verbose(1) << "done." << endl;
840 } else {
841 *out << Verbose(1) << "failed to open file " << line.str() << "." << endl;
842 status = false;
843 }
844
845 return status;
846};
847
848bool CheckAdjacencyFileAgainstMolecule_Init(ofstream *out, char *path, ifstream &File, int *&CurrentBonds)
849{
850 stringstream filename;
851 filename << path << "/" << FRAGMENTPREFIX << ADJACENCYFILE;
852 File.open(filename.str().c_str(), ios::out);
853 *out << Verbose(1) << "Looking at bond structure stored in adjacency file and comparing to present one ... ";
854 if (File == NULL)
855 return false;
856
857 // allocate storage structure
858 CurrentBonds = Malloc<int>(8, "molecule::CheckAdjacencyFileAgainstMolecule - CurrentBonds"); // contains parsed bonds of current atom
859 return true;
860};
861
862void CheckAdjacencyFileAgainstMolecule_Finalize(ofstream *out, ifstream &File, int *&CurrentBonds)
863{
864 File.close();
865 File.clear();
866 Free(&CurrentBonds);
867};
868
869void CheckAdjacencyFileAgainstMolecule_CompareBonds(ofstream *out, bool &status, int &NonMatchNumber, atom *&Walker, size_t &CurrentBondsOfAtom, int AtomNr, int *&CurrentBonds, atom **ListOfAtoms)
870{
871 size_t j = 0;
872 int id = -1;
873
874 //*out << Verbose(2) << "Walker is " << *Walker << ", bond partners: ";
875 if (CurrentBondsOfAtom == Walker->ListOfBonds.size()) {
876 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
877 id = (*Runner)->GetOtherAtom(Walker)->nr;
878 j = 0;
879 for (;(j<CurrentBondsOfAtom) && (CurrentBonds[j++] != id);)
880 ; // check against all parsed bonds
881 if (CurrentBonds[j-1] != id) { // no match ? Then mark in ListOfAtoms
882 ListOfAtoms[AtomNr] = NULL;
883 NonMatchNumber++;
884 status = false;
885 //*out << "[" << id << "]\t";
886 } else {
887 //*out << id << "\t";
888 }
889 }
890 //*out << endl;
891 } else {
892 *out << "Number of bonds for Atom " << *Walker << " does not match, parsed " << CurrentBondsOfAtom << " against " << Walker->ListOfBonds.size() << "." << endl;
893 status = false;
894 }
895};
896
897/** Checks contents of adjacency file against bond structure in structure molecule.
898 * \param *out output stream for debugging
899 * \param *path path to file
900 * \param **ListOfAtoms allocated (molecule::AtomCount) and filled lookup table for ids (Atom::nr) to *Atom
901 * \return true - structure is equal, false - not equivalence
902 */
903bool molecule::CheckAdjacencyFileAgainstMolecule(ofstream *out, char *path, atom **ListOfAtoms)
904{
905 ifstream File;
906 bool status = true;
907 atom *Walker = NULL;
908 char *buffer = NULL;
909 int *CurrentBonds = NULL;
910 int NonMatchNumber = 0; // will number of atoms with differing bond structure
911 size_t CurrentBondsOfAtom = -1;
912
913 if (!CheckAdjacencyFileAgainstMolecule_Init(out, path, File, CurrentBonds)) {
914 *out << Verbose(1) << "Adjacency file not found." << endl;
915 return true;
916 }
917
918 buffer = Malloc<char>(MAXSTRINGSIZE, "molecule::CheckAdjacencyFileAgainstMolecule: *buffer");
919 // Parse the file line by line and count the bonds
920 while (!File.eof()) {
921 File.getline(buffer, MAXSTRINGSIZE);
922 stringstream line;
923 line.str(buffer);
924 int AtomNr = -1;
925 line >> AtomNr;
926 CurrentBondsOfAtom = -1; // we count one too far due to line end
927 // parse into structure
928 if ((AtomNr >= 0) && (AtomNr < AtomCount)) {
929 Walker = ListOfAtoms[AtomNr];
930 while (!line.eof())
931 line >> CurrentBonds[ ++CurrentBondsOfAtom ];
932 // compare against present bonds
933 CheckAdjacencyFileAgainstMolecule_CompareBonds(out, status, NonMatchNumber, Walker, CurrentBondsOfAtom, AtomNr, CurrentBonds, ListOfAtoms);
934 }
935 }
936 Free(&buffer);
937 CheckAdjacencyFileAgainstMolecule_Finalize(out, File, CurrentBonds);
938
939 if (status) { // if equal we parse the KeySetFile
940 *out << Verbose(1) << "done: Equal." << endl;
941 } else
942 *out << Verbose(1) << "done: Not equal by " << NonMatchNumber << " atoms." << endl;
943 return status;
944};
945
946
947/** Picks from a global stack with all back edges the ones in the fragment.
948 * \param *out output stream for debugging
949 * \param **ListOfLocalAtoms array of father atom::nr to local atom::nr (reverse of atom::father)
950 * \param *ReferenceStack stack with all the back egdes
951 * \param *LocalStack stack to be filled
952 * \return true - everything ok, false - ReferenceStack was empty
953 */
954bool molecule::PickLocalBackEdges(ofstream *out, atom **ListOfLocalAtoms, class StackClass<bond *> *&ReferenceStack, class StackClass<bond *> *&LocalStack)
955{
956 bool status = true;
957 if (ReferenceStack->IsEmpty()) {
958 cerr << "ReferenceStack is empty!" << endl;
959 return false;
960 }
961 bond *Binder = ReferenceStack->PopFirst();
962 bond *FirstBond = Binder; // mark the first bond, so that we don't loop through the stack indefinitely
963 atom *Walker = NULL, *OtherAtom = NULL;
964 ReferenceStack->Push(Binder);
965
966 do { // go through all bonds and push local ones
967 Walker = ListOfLocalAtoms[Binder->leftatom->nr]; // get one atom in the reference molecule
968 if (Walker != NULL) // if this Walker exists in the subgraph ...
969 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
970 OtherAtom = (*Runner)->GetOtherAtom(Walker);
971 if (OtherAtom == ListOfLocalAtoms[(*Runner)->rightatom->nr]) { // found the bond
972 LocalStack->Push((*Runner));
973 *out << Verbose(3) << "Found local edge " << *(*Runner) << "." << endl;
974 break;
975 }
976 }
977 Binder = ReferenceStack->PopFirst(); // loop the stack for next item
978 *out << Verbose(3) << "Current candidate edge " << Binder << "." << endl;
979 ReferenceStack->Push(Binder);
980 } while (FirstBond != Binder);
981
982 return status;
983};
984
985struct BFSAccounting {
986 atom **PredecessorList;
987 int *ShortestPathList;
988 enum Shading *ColorList;
989 class StackClass<atom *> *AtomStack;
990 int AtomCount;
991 int BondOrder;
992 atom *Root;
993};
994
995void BreadthFirstSearchAdd_Init(struct BFSAccounting &BFS, atom *&Root, int AtomCount, int BondOrder, atom **AddedAtomList = NULL)
996{
997 BFS.AtomCount = AtomCount;
998 BFS.BondOrder = BondOrder;
999 BFS.PredecessorList = Malloc<atom*>(AtomCount, "molecule::BreadthFirstSearchAdd_Init: **PredecessorList");
1000 BFS.ShortestPathList = Malloc<int>(AtomCount, "molecule::BreadthFirstSearchAdd_Init: *ShortestPathList");
1001 BFS.ColorList = Malloc<enum Shading>(AtomCount, "molecule::BreadthFirstSearchAdd_Init: *ColorList");
1002 BFS.AtomStack = new StackClass<atom *>(AtomCount);
1003
1004 BFS.Root = Root;
1005 BFS.AtomStack->ClearStack();
1006 BFS.AtomStack->Push(Root);
1007
1008 // initialise each vertex as white with no predecessor, empty queue, color Root lightgray
1009 for (int i=AtomCount;i--;) {
1010 BFS.PredecessorList[i] = NULL;
1011 BFS.ShortestPathList[i] = -1;
1012 if ((AddedAtomList != NULL) && (AddedAtomList[i] != NULL)) // mark already present atoms (i.e. Root and maybe others) as visited
1013 BFS.ColorList[i] = lightgray;
1014 else
1015 BFS.ColorList[i] = white;
1016 }
1017 BFS.ShortestPathList[Root->nr] = 0;
1018};
1019
1020void BreadthFirstSearchAdd_Free(struct BFSAccounting &BFS)
1021{
1022 Free(&BFS.PredecessorList);
1023 Free(&BFS.ShortestPathList);
1024 Free(&BFS.ColorList);
1025 delete(BFS.AtomStack);
1026 BFS.AtomCount = 0;
1027};
1028
1029
1030void BreadthFirstSearchAdd_UnvisitedNode(ofstream *out, molecule *Mol, struct BFSAccounting &BFS, atom *&Walker, atom *&OtherAtom, bond *&Binder, bond *&Bond, atom **&AddedAtomList, bond **&AddedBondList, bool IsAngstroem)
1031{
1032 if (Binder != Bond) // let other atom white if it's via Root bond. In case it's cyclic it has to be reached again (yet Root is from OtherAtom already black, thus no problem)
1033 BFS.ColorList[OtherAtom->nr] = lightgray;
1034 BFS.PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
1035 BFS.ShortestPathList[OtherAtom->nr] = BFS.ShortestPathList[Walker->nr]+1;
1036 *out << Verbose(2) << "Coloring OtherAtom " << OtherAtom->Name << " " << ((BFS.ColorList[OtherAtom->nr] == white) ? "white" : "lightgray") << ", its predecessor is " << Walker->Name << " and its Shortest Path is " << BFS.ShortestPathList[OtherAtom->nr] << " egde(s) long." << endl;
1037 if ((((BFS.ShortestPathList[OtherAtom->nr] < BFS.BondOrder) && (Binder != Bond))) ) { // Check for maximum distance
1038 *out << Verbose(3);
1039 if (AddedAtomList[OtherAtom->nr] == NULL) { // add if it's not been so far
1040 AddedAtomList[OtherAtom->nr] = Mol->AddCopyAtom(OtherAtom);
1041 *out << "Added OtherAtom " << OtherAtom->Name;
1042 AddedBondList[Binder->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], Binder);
1043 *out << " and bond " << *(AddedBondList[Binder->nr]) << ", ";
1044 } 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)
1045 *out << "Not adding OtherAtom " << OtherAtom->Name;
1046 if (AddedBondList[Binder->nr] == NULL) {
1047 AddedBondList[Binder->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], Binder);
1048 *out << ", added Bond " << *(AddedBondList[Binder->nr]);
1049 } else
1050 *out << ", not added Bond ";
1051 }
1052 *out << ", putting OtherAtom into queue." << endl;
1053 BFS.AtomStack->Push(OtherAtom);
1054 } else { // out of bond order, then replace
1055 if ((AddedAtomList[OtherAtom->nr] == NULL) && (Binder->Cyclic))
1056 BFS.ColorList[OtherAtom->nr] = white; // unmark if it has not been queued/added, to make it available via its other bonds (cyclic)
1057 if (Binder == Bond)
1058 *out << Verbose(3) << "Not Queueing, is the Root bond";
1059 else if (BFS.ShortestPathList[OtherAtom->nr] >= BFS.BondOrder)
1060 *out << Verbose(3) << "Not Queueing, is out of Bond Count of " << BFS.BondOrder;
1061 if (!Binder->Cyclic)
1062 *out << ", is not part of a cyclic bond, saturating bond with Hydrogen." << endl;
1063 if (AddedBondList[Binder->nr] == NULL) {
1064 if ((AddedAtomList[OtherAtom->nr] != NULL)) { // .. whether we add or saturate
1065 AddedBondList[Binder->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], Binder);
1066 } else {
1067 #ifdef ADDHYDROGEN
1068 if (!Mol->AddHydrogenReplacementAtom(out, Binder, AddedAtomList[Walker->nr], Walker, OtherAtom, IsAngstroem))
1069 exit(1);
1070 #endif
1071 }
1072 }
1073 }
1074};
1075
1076void BreadthFirstSearchAdd_VisitedNode(ofstream *out, molecule *Mol, struct BFSAccounting &BFS, atom *&Walker, atom *&OtherAtom, bond *&Binder, bond *&Bond, atom **&AddedAtomList, bond **&AddedBondList, bool IsAngstroem)
1077{
1078 *out << Verbose(3) << "Not Adding, has already been visited." << endl;
1079 // This has to be a cyclic bond, check whether it's present ...
1080 if (AddedBondList[Binder->nr] == NULL) {
1081 if ((Binder != Bond) && (Binder->Cyclic) && (((BFS.ShortestPathList[Walker->nr]+1) < BFS.BondOrder))) {
1082 AddedBondList[Binder->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], Binder);
1083 } else { // if it's root bond it has to broken (otherwise we would not create the fragments)
1084 #ifdef ADDHYDROGEN
1085 if(!Mol->AddHydrogenReplacementAtom(out, Binder, AddedAtomList[Walker->nr], Walker, OtherAtom, IsAngstroem))
1086 exit(1);
1087 #endif
1088 }
1089 }
1090};
1091
1092/** Adds atoms up to \a BondCount distance from \a *Root and notes them down in \a **AddedAtomList.
1093 * Gray vertices are always enqueued in an StackClass<atom *> FIFO queue, the rest is usual BFS with adding vertices found was
1094 * white and putting into queue.
1095 * \param *out output stream for debugging
1096 * \param *Mol Molecule class to add atoms to
1097 * \param **AddedAtomList list with added atom pointers, index is atom father's number
1098 * \param **AddedBondList list with added bond pointers, index is bond father's number
1099 * \param *Root root vertex for BFS
1100 * \param *Bond bond not to look beyond
1101 * \param BondOrder maximum distance for vertices to add
1102 * \param IsAngstroem lengths are in angstroem or bohrradii
1103 */
1104void molecule::BreadthFirstSearchAdd(ofstream *out, molecule *Mol, atom **&AddedAtomList, bond **&AddedBondList, atom *Root, bond *Bond, int BondOrder, bool IsAngstroem)
1105{
1106 struct BFSAccounting BFS;
1107 atom *Walker = NULL, *OtherAtom = NULL;
1108 bond *Binder = NULL;
1109
1110 // add Root if not done yet
1111 if (AddedAtomList[Root->nr] == NULL) // add Root if not yet present
1112 AddedAtomList[Root->nr] = Mol->AddCopyAtom(Root);
1113
1114 BreadthFirstSearchAdd_Init(BFS, Root, BondOrder, AtomCount, AddedAtomList);
1115
1116 // and go on ... Queue always contains all lightgray vertices
1117 while (!BFS.AtomStack->IsEmpty()) {
1118 // we have to pop the oldest atom from stack. This keeps the atoms on the stack always of the same ShortestPath distance.
1119 // 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
1120 // append length of 3 (their neighbours). Thus on stack we have always atoms of a certain length n at bottom of stack and
1121 // followed by n+1 till top of stack.
1122 Walker = BFS.AtomStack->PopFirst(); // pop oldest added
1123 *out << Verbose(1) << "Current Walker is: " << Walker->Name << ", and has " << Walker->ListOfBonds.size() << " bonds." << endl;
1124 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1125 if ((*Runner) != NULL) { // don't look at bond equal NULL
1126 Binder = (*Runner);
1127 OtherAtom = (*Runner)->GetOtherAtom(Walker);
1128 *out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *(*Runner) << "." << endl;
1129 if (BFS.ColorList[OtherAtom->nr] == white) {
1130 BreadthFirstSearchAdd_UnvisitedNode(out, Mol, BFS, Walker, OtherAtom, Binder, Bond, AddedAtomList, AddedBondList, IsAngstroem);
1131 } else {
1132 BreadthFirstSearchAdd_VisitedNode(out, Mol, BFS, Walker, OtherAtom, Binder, Bond, AddedAtomList, AddedBondList, IsAngstroem);
1133 }
1134 }
1135 }
1136 BFS.ColorList[Walker->nr] = black;
1137 *out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
1138 }
1139 BreadthFirstSearchAdd_Free(BFS);
1140};
1141
1142/** Adds a bond as a copy to a given one
1143 * \param *left leftatom of new bond
1144 * \param *right rightatom of new bond
1145 * \param *CopyBond rest of fields in bond are copied from this
1146 * \return pointer to new bond
1147 */
1148bond * molecule::CopyBond(atom *left, atom *right, bond *CopyBond)
1149{
1150 bond *Binder = AddBond(left, right, CopyBond->BondDegree);
1151 Binder->Cyclic = CopyBond->Cyclic;
1152 Binder->Type = CopyBond->Type;
1153 return Binder;
1154};
1155
1156void BuildInducedSubgraph_Init(ofstream *out, atom **&ParentList, int AtomCount)
1157{
1158 // reset parent list
1159 ParentList = Malloc<atom*>(AtomCount, "molecule::BuildInducedSubgraph_Init: **ParentList");
1160 *out << Verbose(3) << "Resetting ParentList." << endl;
1161 for (int i=AtomCount;i--;)
1162 ParentList[i] = NULL;
1163};
1164
1165void BuildInducedSubgraph_FillParentList(ofstream *out, const molecule *mol, const molecule *Father, atom **&ParentList)
1166{
1167 // fill parent list with sons
1168 *out << Verbose(3) << "Filling Parent List." << endl;
1169 atom *Walker = mol->start;
1170 while (Walker->next != mol->end) {
1171 Walker = Walker->next;
1172 ParentList[Walker->father->nr] = Walker;
1173 // Outputting List for debugging
1174 *out << Verbose(4) << "Son["<< Walker->father->nr <<"] of " << Walker->father << " is " << ParentList[Walker->father->nr] << "." << endl;
1175 }
1176
1177};
1178
1179void BuildInducedSubgraph_Finalize(ofstream *out, atom **&ParentList)
1180{
1181 Free(&ParentList);
1182};
1183
1184bool BuildInducedSubgraph_CreateBondsFromParent(ofstream *out, molecule *mol, const molecule *Father, atom **&ParentList)
1185{
1186 bool status = true;
1187 atom *Walker = NULL;
1188 atom *OtherAtom = NULL;
1189 // check each entry of parent list and if ok (one-to-and-onto matching) create bonds
1190 *out << Verbose(3) << "Creating bonds." << endl;
1191 Walker = Father->start;
1192 while (Walker->next != Father->end) {
1193 Walker = Walker->next;
1194 if (ParentList[Walker->nr] != NULL) {
1195 if (ParentList[Walker->nr]->father != Walker) {
1196 status = false;
1197 } else {
1198 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1199 OtherAtom = (*Runner)->GetOtherAtom(Walker);
1200 if (ParentList[OtherAtom->nr] != NULL) { // if otheratom is also a father of an atom on this molecule, create the bond
1201 *out << Verbose(4) << "Endpoints of Bond " << (*Runner) << " are both present: " << ParentList[Walker->nr]->Name << " and " << ParentList[OtherAtom->nr]->Name << "." << endl;
1202 mol->AddBond(ParentList[Walker->nr], ParentList[OtherAtom->nr], (*Runner)->BondDegree);
1203 }
1204 }
1205 }
1206 }
1207 }
1208 return status;
1209};
1210
1211/** Adds bond structure to this molecule from \a Father molecule.
1212 * This basically causes this molecule to become an induced subgraph of the \a Father, i.e. for every bond in Father
1213 * with end points present in this molecule, bond is created in this molecule.
1214 * Special care was taken to ensure that this is of complexity O(N), where N is the \a Father's molecule::AtomCount.
1215 * \param *out output stream for debugging
1216 * \param *Father father molecule
1217 * \return true - is induced subgraph, false - there are atoms with fathers not in \a Father
1218 * \todo not checked, not fully working probably
1219 */
1220bool molecule::BuildInducedSubgraph(ofstream *out, const molecule *Father)
1221{
1222 bool status = true;
1223 atom **ParentList = NULL;
1224
1225 *out << Verbose(2) << "Begin of BuildInducedSubgraph." << endl;
1226 BuildInducedSubgraph_Init(out, ParentList, Father->AtomCount);
1227 BuildInducedSubgraph_FillParentList(out, this, Father, ParentList);
1228 status = BuildInducedSubgraph_CreateBondsFromParent(out, this, Father, ParentList);
1229 BuildInducedSubgraph_Finalize(out, ParentList);
1230 *out << Verbose(2) << "End of BuildInducedSubgraph." << endl;
1231 return status;
1232};
1233
1234
1235/** For a given keyset \a *Fragment, checks whether it is connected in the current molecule.
1236 * \param *out output stream for debugging
1237 * \param *Fragment Keyset of fragment's vertices
1238 * \return true - connected, false - disconnected
1239 * \note this is O(n^2) for it's just a bug checker not meant for permanent use!
1240 */
1241bool molecule::CheckForConnectedSubgraph(ofstream *out, KeySet *Fragment)
1242{
1243 atom *Walker = NULL, *Walker2 = NULL;
1244 bool BondStatus = false;
1245 int size;
1246
1247 *out << Verbose(1) << "Begin of CheckForConnectedSubgraph" << endl;
1248 *out << Verbose(2) << "Disconnected atom: ";
1249
1250 // count number of atoms in graph
1251 size = 0;
1252 for(KeySet::iterator runner = Fragment->begin(); runner != Fragment->end(); runner++)
1253 size++;
1254 if (size > 1)
1255 for(KeySet::iterator runner = Fragment->begin(); runner != Fragment->end(); runner++) {
1256 Walker = FindAtom(*runner);
1257 BondStatus = false;
1258 for(KeySet::iterator runners = Fragment->begin(); runners != Fragment->end(); runners++) {
1259 Walker2 = FindAtom(*runners);
1260 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1261 if ((*Runner)->GetOtherAtom(Walker) == Walker2) {
1262 BondStatus = true;
1263 break;
1264 }
1265 if (BondStatus)
1266 break;
1267 }
1268 }
1269 if (!BondStatus) {
1270 *out << (*Walker) << endl;
1271 return false;
1272 }
1273 }
1274 else {
1275 *out << "none." << endl;
1276 return true;
1277 }
1278 *out << "none." << endl;
1279
1280 *out << Verbose(1) << "End of CheckForConnectedSubgraph" << endl;
1281
1282 return true;
1283}
Note: See TracBrowser for help on using the repository browser.