source: src/molecule_graph.cpp@ 266237

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

Huge refactoring: molecule::ListOfBondsPerAtom and molecule::NumberOfBondsPerAtom removed, atom::ListOfBonds introduced. Unit Test for ListOfBonds manipulation introduced.

  • changes to builder.cpp: removed CreateListOfBondsPerAtom() calls, as the creation of the global arrays is not necessary anymore
  • changes to LinkedCell: LinkedCell::CheckBounds(int[NDIM]) does not admonish out of bonds as this is not desired for the local offset which may become out of bounds.
  • changes to lists.hpp templates: BUGFIX: unlink() now sets ->next and ->previous to NULL, cleanup() uses removedwithoutcheck()
  • new templates for molecule.hpp: SumPerAtom() allows for summation of the return value of atom:...() member fiunctions. This is needed e.g. for atom::CorrectBondDegree()

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

  • Property mode set to 100644
File size: 50.4 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
254/** Performs a Depth-First search on this molecule.
255 * Marks bonds in molecule as cyclic, bridge, ... and atoms as
256 * articulations points, ...
257 * We use the algorithm from [Even, Graph Algorithms, p.62].
258 * \param *out output stream for debugging
259 * \param *&BackEdgeStack NULL pointer to StackClass with all the found back edges, allocated and filled on return
260 * \return list of each disconnected subgraph as an individual molecule class structure
261 */
262MoleculeLeafClass * molecule::DepthFirstSearchAnalysis(ofstream *out, class StackClass<bond *> *&BackEdgeStack)
263{
264 class StackClass<atom *> *AtomStack = new StackClass<atom *>(AtomCount);
265 BackEdgeStack = new StackClass<bond *> (BondCount);
266 MoleculeLeafClass *SubGraphs = new MoleculeLeafClass(NULL);
267 MoleculeLeafClass *LeafWalker = SubGraphs;
268 int CurrentGraphNr = 0, OldGraphNr;
269 int ComponentNumber = 0;
270 atom *Walker = NULL, *OtherAtom = NULL, *Root = start->next;
271 bond *Binder = NULL;
272 bool BackStepping = false;
273
274 *out << Verbose(0) << "Begin of DepthFirstSearchAnalysis" << endl;
275
276 ResetAllBondsToUnused();
277 ResetAllAtomNumbers();
278 InitComponentNumbers();
279 BackEdgeStack->ClearStack();
280 while (Root != end) { // if there any atoms at all
281 // (1) mark all edges unused, empty stack, set atom->GraphNr = 0 for all
282 AtomStack->ClearStack();
283
284 // put into new subgraph molecule and add this to list of subgraphs
285 LeafWalker = new MoleculeLeafClass(LeafWalker);
286 LeafWalker->Leaf = new molecule(elemente);
287 LeafWalker->Leaf->AddCopyAtom(Root);
288
289 OldGraphNr = CurrentGraphNr;
290 Walker = Root;
291 do { // (10)
292 do { // (2) set number and Lowpoint of Atom to i, increase i, push current atom
293 if (!BackStepping) { // if we don't just return from (8)
294 Walker->GraphNr = CurrentGraphNr;
295 Walker->LowpointNr = CurrentGraphNr;
296 *out << Verbose(1) << "Setting Walker[" << Walker->Name << "]'s number to " << Walker->GraphNr << " with Lowpoint " << Walker->LowpointNr << "." << endl;
297 AtomStack->Push(Walker);
298 CurrentGraphNr++;
299 }
300 do { // (3) if Walker has no unused egdes, go to (5)
301 BackStepping = false; // reset backstepping flag for (8)
302 if (Binder == NULL) // if we don't just return from (11), Binder is already set to next unused
303 Binder = FindNextUnused(Walker);
304 if (Binder == NULL)
305 break;
306 *out << Verbose(2) << "Current Unused Bond is " << *Binder << "." << endl;
307 // (4) Mark Binder used, ...
308 Binder->MarkUsed(black);
309 OtherAtom = Binder->GetOtherAtom(Walker);
310 *out << Verbose(2) << "(4) OtherAtom is " << OtherAtom->Name << "." << endl;
311 if (OtherAtom->GraphNr != -1) {
312 // (4a) ... if "other" atom has been visited (GraphNr != 0), set lowpoint to minimum of both, go to (3)
313 Binder->Type = BackEdge;
314 BackEdgeStack->Push(Binder);
315 Walker->LowpointNr = ( Walker->LowpointNr < OtherAtom->GraphNr ) ? Walker->LowpointNr : OtherAtom->GraphNr;
316 *out << Verbose(3) << "(4a) Visited: Setting Lowpoint of Walker[" << Walker->Name << "] to " << Walker->LowpointNr << "." << endl;
317 } else {
318 // (4b) ... otherwise set OtherAtom as Ancestor of Walker and Walker as OtherAtom, go to (2)
319 Binder->Type = TreeEdge;
320 OtherAtom->Ancestor = Walker;
321 Walker = OtherAtom;
322 *out << Verbose(3) << "(4b) Not Visited: OtherAtom[" << OtherAtom->Name << "]'s Ancestor is now " << OtherAtom->Ancestor->Name << ", Walker is OtherAtom " << OtherAtom->Name << "." << endl;
323 break;
324 }
325 Binder = NULL;
326 } while (1); // (3)
327 if (Binder == NULL) {
328 *out << Verbose(2) << "No more Unused Bonds." << endl;
329 break;
330 } else
331 Binder = NULL;
332 } while (1); // (2)
333
334 // 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!
335 if ((Walker == Root) && (Binder == NULL))
336 break;
337
338 // (5) if Ancestor of Walker is ...
339 *out << Verbose(1) << "(5) Number of Walker[" << Walker->Name << "]'s Ancestor[" << Walker->Ancestor->Name << "] is " << Walker->Ancestor->GraphNr << "." << endl;
340 if (Walker->Ancestor->GraphNr != Root->GraphNr) {
341 // (6) (Ancestor of Walker is not Root)
342 if (Walker->LowpointNr < Walker->Ancestor->GraphNr) {
343 // (6a) set Ancestor's Lowpoint number to minimum of of its Ancestor and itself, go to Step(8)
344 Walker->Ancestor->LowpointNr = (Walker->Ancestor->LowpointNr < Walker->LowpointNr) ? Walker->Ancestor->LowpointNr : Walker->LowpointNr;
345 *out << Verbose(2) << "(6) Setting Walker[" << Walker->Name << "]'s Ancestor[" << Walker->Ancestor->Name << "]'s Lowpoint to " << Walker->Ancestor->LowpointNr << "." << endl;
346 } else {
347 // (7) (Ancestor of Walker is a separating vertex, remove all from stack till Walker (including), these and Ancestor form a component
348 Walker->Ancestor->SeparationVertex = true;
349 *out << Verbose(2) << "(7) Walker[" << Walker->Name << "]'s Ancestor[" << Walker->Ancestor->Name << "]'s is a separating vertex, creating component." << endl;
350 SetNextComponentNumber(Walker->Ancestor, ComponentNumber);
351 *out << Verbose(3) << "(7) Walker[" << Walker->Name << "]'s Ancestor's Compont is " << ComponentNumber << "." << endl;
352 SetNextComponentNumber(Walker, ComponentNumber);
353 *out << Verbose(3) << "(7) Walker[" << Walker->Name << "]'s Compont is " << ComponentNumber << "." << endl;
354 do {
355 OtherAtom = AtomStack->PopLast();
356 LeafWalker->Leaf->AddCopyAtom(OtherAtom);
357 SetNextComponentNumber(OtherAtom, ComponentNumber);
358 *out << Verbose(3) << "(7) Other[" << OtherAtom->Name << "]'s Compont is " << ComponentNumber << "." << endl;
359 } while (OtherAtom != Walker);
360 ComponentNumber++;
361 }
362 // (8) Walker becomes its Ancestor, go to (3)
363 *out << Verbose(2) << "(8) Walker[" << Walker->Name << "] is now its Ancestor " << Walker->Ancestor->Name << ", backstepping. " << endl;
364 Walker = Walker->Ancestor;
365 BackStepping = true;
366 }
367 if (!BackStepping) { // coming from (8) want to go to (3)
368 // (9) remove all from stack till Walker (including), these and Root form a component
369 AtomStack->Output(out);
370 SetNextComponentNumber(Root, ComponentNumber);
371 *out << Verbose(3) << "(9) Root[" << Root->Name << "]'s Component is " << ComponentNumber << "." << endl;
372 SetNextComponentNumber(Walker, ComponentNumber);
373 *out << Verbose(3) << "(9) Walker[" << Walker->Name << "]'s Component is " << ComponentNumber << "." << endl;
374 do {
375 OtherAtom = AtomStack->PopLast();
376 LeafWalker->Leaf->AddCopyAtom(OtherAtom);
377 SetNextComponentNumber(OtherAtom, ComponentNumber);
378 *out << Verbose(3) << "(7) Other[" << OtherAtom->Name << "]'s Compont is " << ComponentNumber << "." << endl;
379 } while (OtherAtom != Walker);
380 ComponentNumber++;
381
382 // (11) Root is separation vertex, set Walker to Root and go to (4)
383 Walker = Root;
384 Binder = FindNextUnused(Walker);
385 *out << Verbose(1) << "(10) Walker is Root[" << Root->Name << "], next Unused Bond is " << Binder << "." << endl;
386 if (Binder != NULL) { // Root is separation vertex
387 *out << Verbose(1) << "(11) Root is a separation vertex." << endl;
388 Walker->SeparationVertex = true;
389 }
390 }
391 } while ((BackStepping) || (Binder != NULL)); // (10) halt only if Root has no unused edges
392
393 // From OldGraphNr to CurrentGraphNr ranges an disconnected subgraph
394 *out << Verbose(0) << "Disconnected subgraph ranges from " << OldGraphNr << " to " << CurrentGraphNr << "." << endl;
395 LeafWalker->Leaf->Output(out);
396 *out << endl;
397
398 // step on to next root
399 while ((Root != end) && (Root->GraphNr != -1)) {
400 //*out << Verbose(1) << "Current next subgraph root candidate is " << Root->Name << "." << endl;
401 if (Root->GraphNr != -1) // if already discovered, step on
402 Root = Root->next;
403 }
404 }
405 // set cyclic bond criterium on "same LP" basis
406 CyclicBondAnalysis();
407
408 OutputGraphInfoPerAtom(out);
409
410 OutputGraphInfoPerBond(out);
411
412 // free all and exit
413 delete(AtomStack);
414 *out << Verbose(0) << "End of DepthFirstSearchAnalysis" << endl;
415 return SubGraphs;
416};
417
418/** Scans through all bonds and set bond::Cyclic to true where atom::LowpointNr of both ends is equal: LP criterion.
419 */
420void molecule::CyclicBondAnalysis()
421{
422 NoCyclicBonds = 0;
423 bond *Binder = first;
424 while(Binder->next != last) {
425 Binder = Binder->next;
426 if (Binder->rightatom->LowpointNr == Binder->leftatom->LowpointNr) { // cyclic ??
427 Binder->Cyclic = true;
428 NoCyclicBonds++;
429 }
430 }
431};
432
433/** Output graph information per atom.
434 * \param *out output stream
435 */
436void molecule::OutputGraphInfoPerAtom(ofstream *out)
437{
438 *out << Verbose(1) << "Final graph info for each atom is:" << endl;
439 ActOnAllAtoms( &atom::OutputGraphInfo, out );
440};
441
442/** Output graph information per bond.
443 * \param *out output stream
444 */
445void molecule::OutputGraphInfoPerBond(ofstream *out)
446{
447 *out << Verbose(1) << "Final graph info for each bond is:" << endl;
448 bond *Binder = first;
449 while(Binder->next != last) {
450 Binder = Binder->next;
451 *out << Verbose(2) << ((Binder->Type == TreeEdge) ? "TreeEdge " : "BackEdge ") << *Binder << ": <";
452 *out << ((Binder->leftatom->SeparationVertex) ? "SP," : "") << "L" << Binder->leftatom->LowpointNr << " G" << Binder->leftatom->GraphNr << " Comp.";
453 Binder->leftatom->OutputComponentNumber(out);
454 *out << " === ";
455 *out << ((Binder->rightatom->SeparationVertex) ? "SP," : "") << "L" << Binder->rightatom->LowpointNr << " G" << Binder->rightatom->GraphNr << " Comp.";
456 Binder->rightatom->OutputComponentNumber(out);
457 *out << ">." << endl;
458 if (Binder->Cyclic) // cyclic ??
459 *out << Verbose(3) << "Lowpoint at each side are equal: CYCLIC!" << endl;
460 }
461};
462
463/** Analyses the cycles found and returns minimum of all cycle lengths.
464 * We begin with a list of Back edges found during DepthFirstSearchAnalysis(). We go through this list - one end is the Root,
465 * the other our initial Walker - and do a Breadth First Search for the Root. We mark down each Predecessor and as soon as
466 * we have found the Root via BFS, we may climb back the closed cycle via the Predecessors. Thereby we mark atoms and bonds
467 * as cyclic and print out the cycles.
468 * \param *out output stream for debugging
469 * \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!
470 * \param *&MinimumRingSize contains smallest ring size in molecular structure on return or -1 if no rings were found, if set is maximum search distance
471 * \todo BFS from the not-same-LP to find back to starting point of tributary cycle over more than one bond
472 */
473void molecule::CyclicStructureAnalysis(ofstream *out, class StackClass<bond *> * BackEdgeStack, int *&MinimumRingSize)
474{
475 atom **PredecessorList = Malloc<atom*>(AtomCount, "molecule::CyclicStructureAnalysis: **PredecessorList");
476 int *ShortestPathList = Malloc<int>(AtomCount, "molecule::CyclicStructureAnalysis: *ShortestPathList");
477 enum Shading *ColorList = Malloc<enum Shading>(AtomCount, "molecule::CyclicStructureAnalysis: *ColorList");
478 class StackClass<atom *> *BFSStack = new StackClass<atom *> (AtomCount); // will hold the current ring
479 class StackClass<atom *> *TouchedStack = new StackClass<atom *> (AtomCount); // contains all "touched" atoms (that need to be reset after BFS loop)
480 atom *Walker = NULL, *OtherAtom = NULL, *Root = NULL;
481 bond *Binder = NULL, *BackEdge = NULL;
482 int RingSize, NumCycles, MinRingSize = -1;
483
484 // initialise each vertex as white with no predecessor, empty queue, color Root lightgray
485 for (int i=AtomCount;i--;) {
486 PredecessorList[i] = NULL;
487 ShortestPathList[i] = -1;
488 ColorList[i] = white;
489 }
490
491 *out << Verbose(1) << "Back edge list - ";
492 BackEdgeStack->Output(out);
493
494 *out << Verbose(1) << "Analysing cycles ... " << endl;
495 NumCycles = 0;
496 while (!BackEdgeStack->IsEmpty()) {
497 BackEdge = BackEdgeStack->PopFirst();
498 // this is the target
499 Root = BackEdge->leftatom;
500 // this is the source point
501 Walker = BackEdge->rightatom;
502 ShortestPathList[Walker->nr] = 0;
503 BFSStack->ClearStack(); // start with empty BFS stack
504 BFSStack->Push(Walker);
505 TouchedStack->Push(Walker);
506 *out << Verbose(1) << "---------------------------------------------------------------------------------------------------------" << endl;
507 OtherAtom = NULL;
508 do { // look for Root
509 Walker = BFSStack->PopFirst();
510 *out << Verbose(2) << "Current Walker is " << *Walker << ", we look for SP to Root " << *Root << "." << endl;
511 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
512 if ((*Runner) != BackEdge) { // only walk along DFS spanning tree (otherwise we always find SP of one being backedge Binder)
513 OtherAtom = (*Runner)->GetOtherAtom(Walker);
514#ifdef ADDHYDROGEN
515 if (OtherAtom->type->Z != 1) {
516#endif
517 *out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *Binder << "." << endl;
518 if (ColorList[OtherAtom->nr] == white) {
519 TouchedStack->Push(OtherAtom);
520 ColorList[OtherAtom->nr] = lightgray;
521 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
522 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
523 *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;
524 //if (ShortestPathList[OtherAtom->nr] < MinimumRingSize[Walker->GetTrueFather()->nr]) { // Check for maximum distance
525 *out << Verbose(3) << "Putting OtherAtom into queue." << endl;
526 BFSStack->Push(OtherAtom);
527 //}
528 } else {
529 *out << Verbose(3) << "Not Adding, has already been visited." << endl;
530 }
531 if (OtherAtom == Root)
532 break;
533#ifdef ADDHYDROGEN
534 } else {
535 *out << Verbose(2) << "Skipping hydrogen atom " << *OtherAtom << "." << endl;
536 ColorList[OtherAtom->nr] = black;
537 }
538#endif
539 } else {
540 *out << Verbose(2) << "Bond " << *Binder << " not Visiting, is the back edge." << endl;
541 }
542 }
543 ColorList[Walker->nr] = black;
544 *out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
545 if (OtherAtom == Root) { // if we have found the root, check whether this cycle wasn't already found beforehand
546 // step through predecessor list
547 while (OtherAtom != BackEdge->rightatom) {
548 if (!OtherAtom->GetTrueFather()->IsCyclic) // if one bond in the loop is not marked as cyclic, we haven't found this cycle yet
549 break;
550 else
551 OtherAtom = PredecessorList[OtherAtom->nr];
552 }
553 if (OtherAtom == BackEdge->rightatom) { // if each atom in found cycle is cyclic, loop's been found before already
554 *out << Verbose(3) << "This cycle was already found before, skipping and removing seeker from search." << endl;\
555 do {
556 OtherAtom = TouchedStack->PopLast();
557 if (PredecessorList[OtherAtom->nr] == Walker) {
558 *out << Verbose(4) << "Removing " << *OtherAtom << " from lists and stacks." << endl;
559 PredecessorList[OtherAtom->nr] = NULL;
560 ShortestPathList[OtherAtom->nr] = -1;
561 ColorList[OtherAtom->nr] = white;
562 BFSStack->RemoveItem(OtherAtom);
563 }
564 } while ((!TouchedStack->IsEmpty()) && (PredecessorList[OtherAtom->nr] == NULL));
565 TouchedStack->Push(OtherAtom); // last was wrongly popped
566 OtherAtom = BackEdge->rightatom; // set to not Root
567 } else
568 OtherAtom = Root;
569 }
570 } while ((!BFSStack->IsEmpty()) && (OtherAtom != Root) && (OtherAtom != NULL)); // || (ShortestPathList[OtherAtom->nr] < MinimumRingSize[Walker->GetTrueFather()->nr])));
571
572 if (OtherAtom == Root) {
573 // now climb back the predecessor list and thus find the cycle members
574 NumCycles++;
575 RingSize = 1;
576 Root->GetTrueFather()->IsCyclic = true;
577 *out << Verbose(1) << "Found ring contains: ";
578 Walker = Root;
579 while (Walker != BackEdge->rightatom) {
580 *out << Walker->Name << " <-> ";
581 Walker = PredecessorList[Walker->nr];
582 Walker->GetTrueFather()->IsCyclic = true;
583 RingSize++;
584 }
585 *out << Walker->Name << " with a length of " << RingSize << "." << endl << endl;
586 // walk through all and set MinimumRingSize
587 Walker = Root;
588 MinimumRingSize[Walker->GetTrueFather()->nr] = RingSize;
589 while (Walker != BackEdge->rightatom) {
590 Walker = PredecessorList[Walker->nr];
591 if (RingSize < MinimumRingSize[Walker->GetTrueFather()->nr])
592 MinimumRingSize[Walker->GetTrueFather()->nr] = RingSize;
593 }
594 if ((RingSize < MinRingSize) || (MinRingSize == -1))
595 MinRingSize = RingSize;
596 } else {
597 *out << Verbose(1) << "No ring containing " << *Root << " with length equal to or smaller than " << MinimumRingSize[Walker->GetTrueFather()->nr] << " found." << endl;
598 }
599
600 // now clean the lists
601 while (!TouchedStack->IsEmpty()){
602 Walker = TouchedStack->PopFirst();
603 PredecessorList[Walker->nr] = NULL;
604 ShortestPathList[Walker->nr] = -1;
605 ColorList[Walker->nr] = white;
606 }
607 }
608 if (MinRingSize != -1) {
609 // go over all atoms
610 Root = start;
611 while(Root->next != end) {
612 Root = Root->next;
613
614 if (MinimumRingSize[Root->GetTrueFather()->nr] == AtomCount) { // check whether MinimumRingSize is set, if not BFS to next where it is
615 Walker = Root;
616 ShortestPathList[Walker->nr] = 0;
617 BFSStack->ClearStack(); // start with empty BFS stack
618 BFSStack->Push(Walker);
619 TouchedStack->Push(Walker);
620 //*out << Verbose(1) << "---------------------------------------------------------------------------------------------------------" << endl;
621 OtherAtom = Walker;
622 while (OtherAtom != NULL) { // look for Root
623 Walker = BFSStack->PopFirst();
624 //*out << Verbose(2) << "Current Walker is " << *Walker << ", we look for SP to Root " << *Root << "." << endl;
625 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
626 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
627 OtherAtom = (*Runner)->GetOtherAtom(Walker);
628 //*out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *Binder << "." << endl;
629 if (ColorList[OtherAtom->nr] == white) {
630 TouchedStack->Push(OtherAtom);
631 ColorList[OtherAtom->nr] = lightgray;
632 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
633 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
634 //*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;
635 if (OtherAtom->GetTrueFather()->IsCyclic) { // if the other atom is connected to a ring
636 MinimumRingSize[Root->GetTrueFather()->nr] = ShortestPathList[OtherAtom->nr]+MinimumRingSize[OtherAtom->GetTrueFather()->nr];
637 OtherAtom = NULL; //break;
638 break;
639 } else
640 BFSStack->Push(OtherAtom);
641 } else {
642 //*out << Verbose(3) << "Not Adding, has already been visited." << endl;
643 }
644 } else {
645 //*out << Verbose(3) << "Not Visiting, is a back edge." << endl;
646 }
647 }
648 ColorList[Walker->nr] = black;
649 //*out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
650 }
651
652 // now clean the lists
653 while (!TouchedStack->IsEmpty()){
654 Walker = TouchedStack->PopFirst();
655 PredecessorList[Walker->nr] = NULL;
656 ShortestPathList[Walker->nr] = -1;
657 ColorList[Walker->nr] = white;
658 }
659 }
660 *out << Verbose(1) << "Minimum ring size of " << *Root << " is " << MinimumRingSize[Root->GetTrueFather()->nr] << "." << endl;
661 }
662 *out << Verbose(1) << "Minimum ring size is " << MinRingSize << ", over " << NumCycles << " cycles total." << endl;
663 } else
664 *out << Verbose(1) << "No rings were detected in the molecular structure." << endl;
665
666 Free(&PredecessorList);
667 Free(&ShortestPathList);
668 Free(&ColorList);
669 delete(BFSStack);
670};
671
672/** Sets the next component number.
673 * This is O(N) as the number of bonds per atom is bound.
674 * \param *vertex atom whose next atom::*ComponentNr is to be set
675 * \param nr number to use
676 */
677void molecule::SetNextComponentNumber(atom *vertex, int nr)
678{
679 size_t i=0;
680 if (vertex != NULL) {
681 for(;i<vertex->ListOfBonds.size();i++) {
682 if (vertex->ComponentNr[i] == -1) { // check if not yet used
683 vertex->ComponentNr[i] = nr;
684 break;
685 }
686 else if (vertex->ComponentNr[i] == nr) // if number is already present, don't add another time
687 break; // breaking here will not cause error!
688 }
689 if (i == vertex->ListOfBonds.size())
690 cerr << "Error: All Component entries are already occupied!" << endl;
691 } else
692 cerr << "Error: Given vertex is NULL!" << endl;
693};
694
695/** Allocates memory for all atom::*ComponentNr in this molecule and sets each entry to -1.
696 */
697void molecule::InitComponentNumbers()
698{
699 atom *Walker = start;
700 while(Walker->next != end) {
701 Walker = Walker->next;
702 if (Walker->ComponentNr != NULL)
703 Free(&Walker->ComponentNr);
704 Walker->ComponentNr = Malloc<int>(Walker->ListOfBonds.size()+1, "molecule::InitComponentNumbers: *Walker->ComponentNr");
705 for (int i=Walker->ListOfBonds.size()+1;i--;)
706 Walker->ComponentNr[i] = -1;
707 }
708};
709
710/** Returns next unused bond for this atom \a *vertex or NULL of none exists.
711 * \param *vertex atom to regard
712 * \return bond class or NULL
713 */
714bond * molecule::FindNextUnused(atom *vertex)
715{
716 for (BondList::const_iterator Runner = vertex->ListOfBonds.begin(); Runner != vertex->ListOfBonds.end(); (++Runner))
717 if ((*Runner)->IsUsed() == white)
718 return((*Runner));
719 return NULL;
720};
721
722/** Resets bond::Used flag of all bonds in this molecule.
723 * \return true - success, false - -failure
724 */
725void molecule::ResetAllBondsToUnused()
726{
727 bond *Binder = first;
728 while (Binder->next != last) {
729 Binder = Binder->next;
730 Binder->ResetUsed();
731 }
732};
733
734/** Resets atom::nr to -1 of all atoms in this molecule.
735 */
736void molecule::ResetAllAtomNumbers()
737{
738 atom *Walker = start;
739 while (Walker->next != end) {
740 Walker = Walker->next;
741 Walker->GraphNr = -1;
742 }
743};
744
745/** Output a list of flags, stating whether the bond was visited or not.
746 * \param *out output stream for debugging
747 * \param *list
748 */
749void OutputAlreadyVisited(ofstream *out, int *list)
750{
751 *out << Verbose(4) << "Already Visited Bonds:\t";
752 for(int i=1;i<=list[0];i++) *out << Verbose(0) << list[i] << " ";
753 *out << endl;
754};
755
756
757/** Storing the bond structure of a molecule to file.
758 * Simply stores Atom::nr and then the Atom::nr of all bond partners per line.
759 * \param *out output stream for debugging
760 * \param *path path to file
761 * \return true - file written successfully, false - writing failed
762 */
763bool molecule::StoreAdjacencyToFile(ofstream *out, char *path)
764{
765 ofstream AdjacencyFile;
766 stringstream line;
767 bool status = true;
768
769 line << path << "/" << FRAGMENTPREFIX << ADJACENCYFILE;
770 AdjacencyFile.open(line.str().c_str(), ios::out);
771 *out << Verbose(1) << "Saving adjacency list ... ";
772 if (AdjacencyFile != NULL) {
773 ActOnAllAtoms( &atom::OutputAdjacency, &AdjacencyFile );
774 AdjacencyFile.close();
775 *out << Verbose(1) << "done." << endl;
776 } else {
777 *out << Verbose(1) << "failed to open file " << line.str() << "." << endl;
778 status = false;
779 }
780
781 return status;
782};
783
784/** Checks contents of adjacency file against bond structure in structure molecule.
785 * \param *out output stream for debugging
786 * \param *path path to file
787 * \param **ListOfAtoms allocated (molecule::AtomCount) and filled lookup table for ids (Atom::nr) to *Atom
788 * \return true - structure is equal, false - not equivalence
789 */
790bool molecule::CheckAdjacencyFileAgainstMolecule(ofstream *out, char *path, atom **ListOfAtoms)
791{
792 ifstream File;
793 stringstream filename;
794 bool status = true;
795 atom *Walker = NULL;
796 char *buffer = Malloc<char>(MAXSTRINGSIZE, "molecule::CheckAdjacencyFileAgainstMolecule: *buffer");
797
798 filename << path << "/" << FRAGMENTPREFIX << ADJACENCYFILE;
799 File.open(filename.str().c_str(), ios::out);
800 *out << Verbose(1) << "Looking at bond structure stored in adjacency file and comparing to present one ... ";
801 if (File != NULL) {
802 // allocate storage structure
803 int NonMatchNumber = 0; // will number of atoms with differing bond structure
804 int *CurrentBonds = Malloc<int>(8, "molecule::CheckAdjacencyFileAgainstMolecule - CurrentBonds"); // contains parsed bonds of current atom
805 size_t CurrentBondsOfAtom;
806
807 // Parse the file line by line and count the bonds
808 while (!File.eof()) {
809 File.getline(buffer, MAXSTRINGSIZE);
810 stringstream line;
811 line.str(buffer);
812 int AtomNr = -1;
813 line >> AtomNr;
814 CurrentBondsOfAtom = -1; // we count one too far due to line end
815 // parse into structure
816 if ((AtomNr >= 0) && (AtomNr < AtomCount)) {
817 Walker = ListOfAtoms[AtomNr];
818 while (!line.eof())
819 line >> CurrentBonds[ ++CurrentBondsOfAtom ];
820 // compare against present bonds
821 //cout << Verbose(2) << "Walker is " << *Walker << ", bond partners: ";
822 if (CurrentBondsOfAtom == Walker->ListOfBonds.size()) {
823 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
824 int id = (*Runner)->GetOtherAtom(Walker)->nr;
825 size_t j = 0;
826 for (;(j<CurrentBondsOfAtom) && (CurrentBonds[j++] != id);); // check against all parsed bonds
827 if (CurrentBonds[j-1] != id) { // no match ? Then mark in ListOfAtoms
828 ListOfAtoms[AtomNr] = NULL;
829 NonMatchNumber++;
830 status = false;
831 //out << "[" << id << "]\t";
832 } else {
833 //out << id << "\t";
834 }
835 }
836 //out << endl;
837 } else {
838 *out << "Number of bonds for Atom " << *Walker << " does not match, parsed " << CurrentBondsOfAtom << " against " << Walker->ListOfBonds.size() << "." << endl;
839 status = false;
840 }
841 }
842 }
843 File.close();
844 File.clear();
845 if (status) { // if equal we parse the KeySetFile
846 *out << Verbose(1) << "done: Equal." << endl;
847 status = true;
848 } else
849 *out << Verbose(1) << "done: Not equal by " << NonMatchNumber << " atoms." << endl;
850 Free(&CurrentBonds);
851 } else {
852 *out << Verbose(1) << "Adjacency file not found." << endl;
853 status = false;
854 }
855 *out << endl;
856 Free(&buffer);
857
858 return status;
859};
860
861
862/** Picks from a global stack with all back edges the ones in the fragment.
863 * \param *out output stream for debugging
864 * \param **ListOfLocalAtoms array of father atom::nr to local atom::nr (reverse of atom::father)
865 * \param *ReferenceStack stack with all the back egdes
866 * \param *LocalStack stack to be filled
867 * \return true - everything ok, false - ReferenceStack was empty
868 */
869bool molecule::PickLocalBackEdges(ofstream *out, atom **ListOfLocalAtoms, class StackClass<bond *> *&ReferenceStack, class StackClass<bond *> *&LocalStack)
870{
871 bool status = true;
872 if (ReferenceStack->IsEmpty()) {
873 cerr << "ReferenceStack is empty!" << endl;
874 return false;
875 }
876 bond *Binder = ReferenceStack->PopFirst();
877 bond *FirstBond = Binder; // mark the first bond, so that we don't loop through the stack indefinitely
878 atom *Walker = NULL, *OtherAtom = NULL;
879 ReferenceStack->Push(Binder);
880
881 do { // go through all bonds and push local ones
882 Walker = ListOfLocalAtoms[Binder->leftatom->nr]; // get one atom in the reference molecule
883 if (Walker != NULL) // if this Walker exists in the subgraph ...
884 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
885 OtherAtom = (*Runner)->GetOtherAtom(Walker);
886 if (OtherAtom == ListOfLocalAtoms[(*Runner)->rightatom->nr]) { // found the bond
887 LocalStack->Push((*Runner));
888 *out << Verbose(3) << "Found local edge " << *(*Runner) << "." << endl;
889 break;
890 }
891 }
892 Binder = ReferenceStack->PopFirst(); // loop the stack for next item
893 *out << Verbose(3) << "Current candidate edge " << Binder << "." << endl;
894 ReferenceStack->Push(Binder);
895 } while (FirstBond != Binder);
896
897 return status;
898};
899
900
901/** Adds atoms up to \a BondCount distance from \a *Root and notes them down in \a **AddedAtomList.
902 * Gray vertices are always enqueued in an StackClass<atom *> FIFO queue, the rest is usual BFS with adding vertices found was
903 * white and putting into queue.
904 * \param *out output stream for debugging
905 * \param *Mol Molecule class to add atoms to
906 * \param **AddedAtomList list with added atom pointers, index is atom father's number
907 * \param **AddedBondList list with added bond pointers, index is bond father's number
908 * \param *Root root vertex for BFS
909 * \param *Bond bond not to look beyond
910 * \param BondOrder maximum distance for vertices to add
911 * \param IsAngstroem lengths are in angstroem or bohrradii
912 */
913void molecule::BreadthFirstSearchAdd(ofstream *out, molecule *Mol, atom **&AddedAtomList, bond **&AddedBondList, atom *Root, bond *Bond, int BondOrder, bool IsAngstroem)
914{
915 atom **PredecessorList = Malloc<atom*>(AtomCount, "molecule::BreadthFirstSearchAdd: **PredecessorList");
916 int *ShortestPathList = Malloc<int>(AtomCount, "molecule::BreadthFirstSearchAdd: *ShortestPathList");
917 enum Shading *ColorList = Malloc<enum Shading>(AtomCount, "molecule::BreadthFirstSearchAdd: *ColorList");
918 class StackClass<atom *> *AtomStack = new StackClass<atom *>(AtomCount);
919 atom *Walker = NULL, *OtherAtom = NULL;
920
921 // add Root if not done yet
922 AtomStack->ClearStack();
923 if (AddedAtomList[Root->nr] == NULL) // add Root if not yet present
924 AddedAtomList[Root->nr] = Mol->AddCopyAtom(Root);
925 AtomStack->Push(Root);
926
927 // initialise each vertex as white with no predecessor, empty queue, color Root lightgray
928 for (int i=AtomCount;i--;) {
929 PredecessorList[i] = NULL;
930 ShortestPathList[i] = -1;
931 if (AddedAtomList[i] != NULL) // mark already present atoms (i.e. Root and maybe others) as visited
932 ColorList[i] = lightgray;
933 else
934 ColorList[i] = white;
935 }
936 ShortestPathList[Root->nr] = 0;
937
938 // and go on ... Queue always contains all lightgray vertices
939 while (!AtomStack->IsEmpty()) {
940 // we have to pop the oldest atom from stack. This keeps the atoms on the stack always of the same ShortestPath distance.
941 // 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
942 // append length of 3 (their neighbours). Thus on stack we have always atoms of a certain length n at bottom of stack and
943 // followed by n+1 till top of stack.
944 Walker = AtomStack->PopFirst(); // pop oldest added
945 *out << Verbose(1) << "Current Walker is: " << Walker->Name << ", and has " << Walker->ListOfBonds.size() << " bonds." << endl;
946 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
947 if ((*Runner) != NULL) { // don't look at bond equal NULL
948 OtherAtom = (*Runner)->GetOtherAtom(Walker);
949 *out << Verbose(2) << "Current OtherAtom is: " << OtherAtom->Name << " for bond " << *(*Runner) << "." << endl;
950 if (ColorList[OtherAtom->nr] == white) {
951 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)
952 ColorList[OtherAtom->nr] = lightgray;
953 PredecessorList[OtherAtom->nr] = Walker; // Walker is the predecessor
954 ShortestPathList[OtherAtom->nr] = ShortestPathList[Walker->nr]+1;
955 *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;
956 if ((((ShortestPathList[OtherAtom->nr] < BondOrder) && ((*Runner) != Bond))) ) { // Check for maximum distance
957 *out << Verbose(3);
958 if (AddedAtomList[OtherAtom->nr] == NULL) { // add if it's not been so far
959 AddedAtomList[OtherAtom->nr] = Mol->AddCopyAtom(OtherAtom);
960 *out << "Added OtherAtom " << OtherAtom->Name;
961 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
962 *out << " and bond " << *(AddedBondList[(*Runner)->nr]) << ", ";
963 } 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)
964 *out << "Not adding OtherAtom " << OtherAtom->Name;
965 if (AddedBondList[(*Runner)->nr] == NULL) {
966 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
967 *out << ", added Bond " << *(AddedBondList[(*Runner)->nr]);
968 } else
969 *out << ", not added Bond ";
970 }
971 *out << ", putting OtherAtom into queue." << endl;
972 AtomStack->Push(OtherAtom);
973 } else { // out of bond order, then replace
974 if ((AddedAtomList[OtherAtom->nr] == NULL) && ((*Runner)->Cyclic))
975 ColorList[OtherAtom->nr] = white; // unmark if it has not been queued/added, to make it available via its other bonds (cyclic)
976 if ((*Runner) == Bond)
977 *out << Verbose(3) << "Not Queueing, is the Root bond";
978 else if (ShortestPathList[OtherAtom->nr] >= BondOrder)
979 *out << Verbose(3) << "Not Queueing, is out of Bond Count of " << BondOrder;
980 if (!(*Runner)->Cyclic)
981 *out << ", is not part of a cyclic bond, saturating bond with Hydrogen." << endl;
982 if (AddedBondList[(*Runner)->nr] == NULL) {
983 if ((AddedAtomList[OtherAtom->nr] != NULL)) { // .. whether we add or saturate
984 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
985 } else {
986#ifdef ADDHYDROGEN
987 if (!Mol->AddHydrogenReplacementAtom(out, (*Runner), AddedAtomList[Walker->nr], Walker, OtherAtom, IsAngstroem))
988 exit(1);
989#endif
990 }
991 }
992 }
993 } else {
994 *out << Verbose(3) << "Not Adding, has already been visited." << endl;
995 // This has to be a cyclic bond, check whether it's present ...
996 if (AddedBondList[(*Runner)->nr] == NULL) {
997 if (((*Runner) != Bond) && ((*Runner)->Cyclic) && (((ShortestPathList[Walker->nr]+1) < BondOrder))) {
998 AddedBondList[(*Runner)->nr] = Mol->CopyBond(AddedAtomList[Walker->nr], AddedAtomList[OtherAtom->nr], (*Runner));
999 } else { // if it's root bond it has to broken (otherwise we would not create the fragments)
1000#ifdef ADDHYDROGEN
1001 if(!Mol->AddHydrogenReplacementAtom(out, (*Runner), AddedAtomList[Walker->nr], Walker, OtherAtom, IsAngstroem))
1002 exit(1);
1003#endif
1004 }
1005 }
1006 }
1007 }
1008 }
1009 ColorList[Walker->nr] = black;
1010 *out << Verbose(1) << "Coloring Walker " << Walker->Name << " black." << endl;
1011 }
1012 Free(&PredecessorList);
1013 Free(&ShortestPathList);
1014 Free(&ColorList);
1015 delete(AtomStack);
1016};
1017
1018/** Adds a bond as a copy to a given one
1019 * \param *left leftatom of new bond
1020 * \param *right rightatom of new bond
1021 * \param *CopyBond rest of fields in bond are copied from this
1022 * \return pointer to new bond
1023 */
1024bond * molecule::CopyBond(atom *left, atom *right, bond *CopyBond)
1025{
1026 bond *Binder = AddBond(left, right, CopyBond->BondDegree);
1027 Binder->Cyclic = CopyBond->Cyclic;
1028 Binder->Type = CopyBond->Type;
1029 return Binder;
1030};
1031
1032
1033/** Adds bond structure to this molecule from \a Father molecule.
1034 * This basically causes this molecule to become an induced subgraph of the \a Father, i.e. for every bond in Father
1035 * with end points present in this molecule, bond is created in this molecule.
1036 * Special care was taken to ensure that this is of complexity O(N), where N is the \a Father's molecule::AtomCount.
1037 * \param *out output stream for debugging
1038 * \param *Father father molecule
1039 * \return true - is induced subgraph, false - there are atoms with fathers not in \a Father
1040 * \todo not checked, not fully working probably
1041 */
1042bool molecule::BuildInducedSubgraph(ofstream *out, const molecule *Father)
1043{
1044 atom *Walker = NULL, *OtherAtom = NULL;
1045 bool status = true;
1046 atom **ParentList = Malloc<atom*>(Father->AtomCount, "molecule::BuildInducedSubgraph: **ParentList");
1047
1048 *out << Verbose(2) << "Begin of BuildInducedSubgraph." << endl;
1049
1050 // reset parent list
1051 *out << Verbose(3) << "Resetting ParentList." << endl;
1052 for (int i=Father->AtomCount;i--;)
1053 ParentList[i] = NULL;
1054
1055 // fill parent list with sons
1056 *out << Verbose(3) << "Filling Parent List." << endl;
1057 Walker = start;
1058 while (Walker->next != end) {
1059 Walker = Walker->next;
1060 ParentList[Walker->father->nr] = Walker;
1061 // Outputting List for debugging
1062 *out << Verbose(4) << "Son["<< Walker->father->nr <<"] of " << Walker->father << " is " << ParentList[Walker->father->nr] << "." << endl;
1063 }
1064
1065 // check each entry of parent list and if ok (one-to-and-onto matching) create bonds
1066 *out << Verbose(3) << "Creating bonds." << endl;
1067 Walker = Father->start;
1068 while (Walker->next != Father->end) {
1069 Walker = Walker->next;
1070 if (ParentList[Walker->nr] != NULL) {
1071 if (ParentList[Walker->nr]->father != Walker) {
1072 status = false;
1073 } else {
1074 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1075 OtherAtom = (*Runner)->GetOtherAtom(Walker);
1076 if (ParentList[OtherAtom->nr] != NULL) { // if otheratom is also a father of an atom on this molecule, create the bond
1077 *out << Verbose(4) << "Endpoints of Bond " << (*Runner) << " are both present: " << ParentList[Walker->nr]->Name << " and " << ParentList[OtherAtom->nr]->Name << "." << endl;
1078 AddBond(ParentList[Walker->nr], ParentList[OtherAtom->nr], (*Runner)->BondDegree);
1079 }
1080 }
1081 }
1082 }
1083 }
1084
1085 Free(&ParentList);
1086 *out << Verbose(2) << "End of BuildInducedSubgraph." << endl;
1087 return status;
1088};
1089
1090
1091/** For a given keyset \a *Fragment, checks whether it is connected in the current molecule.
1092 * \param *out output stream for debugging
1093 * \param *Fragment Keyset of fragment's vertices
1094 * \return true - connected, false - disconnected
1095 * \note this is O(n^2) for it's just a bug checker not meant for permanent use!
1096 */
1097bool molecule::CheckForConnectedSubgraph(ofstream *out, KeySet *Fragment)
1098{
1099 atom *Walker = NULL, *Walker2 = NULL;
1100 bool BondStatus = false;
1101 int size;
1102
1103 *out << Verbose(1) << "Begin of CheckForConnectedSubgraph" << endl;
1104 *out << Verbose(2) << "Disconnected atom: ";
1105
1106 // count number of atoms in graph
1107 size = 0;
1108 for(KeySet::iterator runner = Fragment->begin(); runner != Fragment->end(); runner++)
1109 size++;
1110 if (size > 1)
1111 for(KeySet::iterator runner = Fragment->begin(); runner != Fragment->end(); runner++) {
1112 Walker = FindAtom(*runner);
1113 BondStatus = false;
1114 for(KeySet::iterator runners = Fragment->begin(); runners != Fragment->end(); runners++) {
1115 Walker2 = FindAtom(*runners);
1116 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1117 if ((*Runner)->GetOtherAtom(Walker) == Walker2) {
1118 BondStatus = true;
1119 break;
1120 }
1121 if (BondStatus)
1122 break;
1123 }
1124 }
1125 if (!BondStatus) {
1126 *out << (*Walker) << endl;
1127 return false;
1128 }
1129 }
1130 else {
1131 *out << "none." << endl;
1132 return true;
1133 }
1134 *out << "none." << endl;
1135
1136 *out << Verbose(1) << "End of CheckForConnectedSubgraph" << endl;
1137
1138 return true;
1139}
Note: See TracBrowser for help on using the repository browser.