source: src/Graph/CyclicStructureAnalysis.cpp@ b4f72c

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 b4f72c was 94d5ac6, checked in by Frederik Heber <heber@…>, 12 years ago

FIX: As we use GSL internally, we are as of now required to use GPL v2 license.

  • GNU Scientific Library is used at every place in the code, especially the sub-package LinearAlgebra is based on it which in turn is used really everywhere in the remainder of MoleCuilder. Hence, we have to use the GPL license for the whole of MoleCuilder. In effect, GPL's COPYING was present all along and stated the terms of the GPL v2 license.
  • Hence, I added the default GPL v2 disclaimer to every source file and removed the note about a (actually missing) LICENSE file.
  • also, I added a help-redistribute action which again gives the disclaimer of the GPL v2.
  • also, I changed in the disclaimer that is printed at every program start in builder_init.cpp.
  • TEST: Added check on GPL statement present in every module to test CodeChecks project-disclaimer.
  • Property mode set to 100644
File size: 16.3 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010-2012 University of Bonn. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * CyclicStructureAnalysis.cpp
25 *
26 * Created on: Feb 16, 2011
27 * Author: heber
28 */
29
30// include config.h
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35#include "CodePatterns/MemDebug.hpp"
36
37#include "CyclicStructureAnalysis.hpp"
38
39#include "Atom/atom.hpp"
40#include "Bond/bond.hpp"
41#include "CodePatterns/Assert.hpp"
42#include "CodePatterns/Info.hpp"
43#include "CodePatterns/Log.hpp"
44#include "CodePatterns/Verbose.hpp"
45#include "Element/element.hpp"
46#include "molecule.hpp"
47
48CyclicStructureAnalysis::CyclicStructureAnalysis(const enum HydrogenSaturation _saturation) :
49 saturation(_saturation)
50{}
51
52CyclicStructureAnalysis::~CyclicStructureAnalysis()
53{}
54
55/** Initialise vertex as white with no predecessor, no shortest path(-1), color white.
56 * \param atom_id id of atom whose node we address
57 */
58void CyclicStructureAnalysis::InitNode(atomId_t atom_id)
59{
60 ShortestPathList[atom_id] = -1;
61 PredecessorList[atom_id] = 0;
62 ColorList[atom_id] = GraphEdge::white;
63}
64
65void CyclicStructureAnalysis::Reset()
66{
67 // clear what's present
68 ShortestPathList.clear();
69 PredecessorList.clear();
70 ColorList.clear();
71 BFSStack.clear();
72 TouchedStack.clear();
73}
74
75/** Clean the accounting structure for all nodes touched so far.
76 */
77void CyclicStructureAnalysis::CleanAllTouched()
78{
79 atom *Walker = NULL;
80 while (!TouchedStack.empty()) {
81 Walker = TouchedStack.front();
82 TouchedStack.pop_front();
83 PredecessorList[Walker->getNr()] = NULL;
84 ShortestPathList[Walker->getNr()] = -1;
85 ColorList[Walker->getNr()] = GraphEdge::white;
86 }
87}
88
89/** Resets shortest path list and BFSStack.
90 * \param *&Walker current node, pushed onto BFSStack and TouchedStack
91 */
92void CyclicStructureAnalysis::InitializeToRoot(atom *&Root)
93{
94 ShortestPathList[Root->getNr()] = 0;
95 BFSStack.clear(); // start with empty BFS stack
96 BFSStack.push_front(Root);
97 TouchedStack.push_front(Root);
98}
99
100/** Performs a BFS from \a *Root, trying to find the same node and hence a cycle.
101 * \param *&BackEdge the edge from root that we don't want to move along
102 * \param &BFS accounting structure
103 */
104void CyclicStructureAnalysis::CyclicBFSFromRootToRoot(bond *&BackEdge)
105{
106 atom *Walker = NULL;
107 atom *OtherAtom = NULL;
108 do { // look for Root
109 ASSERT(!BFSStack.empty(), "CyclicStructureAnalysis_CyclicBFSFromRootToRoot() - BFSStack is empty!");
110 Walker = BFSStack.front();
111 BFSStack.pop_front();
112 LOG(2, "INFO: Current Walker is " << *Walker << ", we look for SP to Root " << *Root << "." << endl);
113 const BondList& ListOfBonds = Walker->getListOfBonds();
114 for (BondList::const_iterator Runner = ListOfBonds.begin();
115 Runner != ListOfBonds.end();
116 ++Runner) {
117 if ((*Runner) != BackEdge) { // only walk along DFS spanning tree (otherwise we always find SP of one being backedge Binder)
118 OtherAtom = (*Runner)->GetOtherAtom(Walker);
119 if ((saturation == DontSaturate) || (OtherAtom->getType()->getAtomicNumber() != 1)) {
120 LOG(2, "INFO: Current OtherAtom is: " << OtherAtom->getName() << " for bond " << *(*Runner) << "." << endl);
121 if (ColorList[OtherAtom->getNr()] == GraphEdge::white) {
122 TouchedStack.push_front(OtherAtom);
123 ColorList[OtherAtom->getNr()] = GraphEdge::lightgray;
124 PredecessorList[OtherAtom->getNr()] = Walker; // Walker is the predecessor
125 ShortestPathList[OtherAtom->getNr()] = ShortestPathList[Walker->getNr()] + 1;
126 LOG(2, "INFO: Coloring OtherAtom " << OtherAtom->getName() << " lightgray, its predecessor is " << Walker->getName() << " and its Shortest Path is " << ShortestPathList[OtherAtom->getNr()] << " egde(s) long." << endl);
127 //if (ShortestPathList[OtherAtom->getNr()] < MinimumRingSize[Walker->GetTrueFather()->getNr()]) { // Check for maximum distance
128 LOG(3, "ACCEPT: Putting OtherAtom into queue." << endl);
129 BFSStack.push_front(OtherAtom);
130 //}
131 } else {
132 LOG(3, "REJECT: Not Adding, has already been visited." << endl);
133 }
134 if (OtherAtom == Root)
135 break;
136 } else {
137 LOG(2, "INFO: Skipping hydrogen atom " << *OtherAtom << "." << endl);
138 ColorList[OtherAtom->getNr()] = GraphEdge::black;
139 }
140 } else {
141 LOG(2, "REJECT: Bond " << *(*Runner) << " not Visiting, is the back edge." << endl);
142 }
143 }
144 ColorList[Walker->getNr()] = GraphEdge::black;
145 LOG(1, "INFO: Coloring Walker " << Walker->getName() << " " << GraphEdge::getColorName(ColorList[Walker->getNr()]) << "." << endl);
146 if (OtherAtom == Root) { // if we have found the root, check whether this cycle wasn't already found beforehand
147 // step through predecessor list
148 while (OtherAtom != BackEdge->rightatom) {
149 if (!OtherAtom->GetTrueFather()->IsCyclic) // if one bond in the loop is not marked as cyclic, we haven't found this cycle yet
150 break;
151 else
152 OtherAtom = PredecessorList[OtherAtom->getNr()];
153 }
154 if (OtherAtom == BackEdge->rightatom) { // if each atom in found cycle is cyclic, loop's been found before already
155 LOG(3, "INFO This cycle was already found before, skipping and removing seeker from search." << endl);
156 do {
157 ASSERT(!TouchedStack.empty(), "CyclicStructureAnalysis_CyclicBFSFromRootToRoot() - TouchedStack is empty!");
158 OtherAtom = TouchedStack.front();
159 TouchedStack.pop_front();
160 if (PredecessorList[OtherAtom->getNr()] == Walker) {
161 LOG(4, "INFO: Removing " << *OtherAtom << " from lists and stacks." << endl);
162 PredecessorList[OtherAtom->getNr()] = NULL;
163 ShortestPathList[OtherAtom->getNr()] = -1;
164 ColorList[OtherAtom->getNr()] = GraphEdge::white;
165 // rats ... deque has no find()
166 std::deque<atom *>::iterator iter = find(
167 BFSStack.begin(),
168 BFSStack.end(),
169 OtherAtom);
170 ASSERT(iter != BFSStack.end(),
171 "CyclicStructureAnalysis_CyclicBFSFromRootToRoot() - can't find "+toString(*OtherAtom)+" on stack!");
172 BFSStack.erase(iter);
173 }
174 } while ((!TouchedStack.empty()) && (PredecessorList[OtherAtom->getNr()] == NULL));
175 TouchedStack.push_front(OtherAtom); // last was wrongly popped
176 OtherAtom = BackEdge->rightatom; // set to not Root
177 } else
178 OtherAtom = Root;
179 }
180 } while ((!BFSStack.empty()) && (OtherAtom != Root) && (OtherAtom != NULL)); // || (ShortestPathList[OtherAtom->getNr()] < MinimumRingSize[Walker->GetTrueFather()->getNr()])));
181}
182
183/** Climb back the BFSAccounting::PredecessorList and find cycle members.
184 * \param *&OtherAtom
185 * \param *&BackEdge denotes the edge we did not want to travel along when doing CyclicBFSFromRootToRoot()
186 * \param &BFS accounting structure
187 * \param &MinRingSize global minimum distance from one node without encountering oneself, set on return
188 */
189void CyclicStructureAnalysis::RetrieveCycleMembers(atom *&OtherAtom, bond *&BackEdge, int &MinRingSize)
190{
191 atom *Walker = NULL;
192 int NumCycles = 0;
193 int RingSize = -1;
194
195 if (OtherAtom == Root) {
196 // now climb back the predecessor list and thus find the cycle members
197 NumCycles++;
198 RingSize = 1;
199 Root->GetTrueFather()->IsCyclic = true;
200
201 std::stringstream output;
202 output << "Found ring contains: ";
203 Walker = Root;
204 while (Walker != BackEdge->rightatom) {
205 output << Walker->getName() << " <-> ";
206 Walker = PredecessorList[Walker->getNr()];
207 Walker->GetTrueFather()->IsCyclic = true;
208 RingSize++;
209 }
210 output << Walker->getName() << " with a length of " << RingSize << ".";
211 LOG(0, "INFO: " << output.str());
212
213 // walk through all and set MinimumRingSize
214 Walker = Root;
215 ASSERT(!MinimumRingSize.count(Walker->GetTrueFather()->getNr()),
216 "CyclicStructureAnalysis::RetrieveCycleMembers() - setting MinimumRingSize of "
217 +toString(*(Walker->GetTrueFather()))+" to "
218 +toString(RingSize)+" which is already set to "
219 +toString(MinimumRingSize[Walker->GetTrueFather()->getNr()])+".");
220 MinimumRingSize[Walker->GetTrueFather()->getNr()] = RingSize;
221 while (Walker != BackEdge->rightatom) {
222 Walker = PredecessorList[Walker->getNr()];
223 if (RingSize < MinimumRingSize[Walker->GetTrueFather()->getNr()])
224 MinimumRingSize[Walker->GetTrueFather()->getNr()] = RingSize;
225 }
226 if ((RingSize < MinRingSize) || (MinRingSize == -1))
227 MinRingSize = RingSize;
228 } else {
229 LOG(1, "INFO: No ring containing " << *Root << " with length equal to or smaller than " << MinimumRingSize[Root->GetTrueFather()->getNr()] << " found." << endl);
230 }
231}
232
233/** From a given node performs a BFS to touch the next cycle, for whose nodes \a MinimumRingSize is set and set it accordingly.
234 * \param *&Root node to look for closest cycle from, i.e. \a MinimumRingSize is set for this node
235 * \param AtomCount number of nodes in graph
236 */
237void CyclicStructureAnalysis::BFSToNextCycle(atom *&Root, atom *&Walker)
238{
239 atom *OtherAtom = Walker;
240
241 Reset();
242
243 InitializeToRoot(Walker);
244 while (OtherAtom != NULL) { // look for Root
245 ASSERT(!BFSStack.empty(), "CyclicStructureAnalysis_BFSToNextCycle() - BFSStack is empty!");
246 Walker = BFSStack.front();
247 BFSStack.pop_front();
248 LOG(2, "INFO: Current Walker is " << *Walker << ", we look for SP to Root " << *Root << ".");
249 const BondList& ListOfBonds = Walker->getListOfBonds();
250 for (BondList::const_iterator Runner = ListOfBonds.begin();
251 Runner != ListOfBonds.end();
252 ++Runner) {
253 // "removed (*Runner) != BackEdge) || " from next if, is u
254 if ((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
255 OtherAtom = (*Runner)->GetOtherAtom(Walker);
256 LOG(2, "INFO: Current OtherAtom is: " << OtherAtom->getName() << " for bond " << *(*Runner) << ".");
257 if (ColorList[OtherAtom->getNr()] == GraphEdge::white) {
258 TouchedStack.push_front(OtherAtom);
259 ColorList[OtherAtom->getNr()] = GraphEdge::lightgray;
260 PredecessorList[OtherAtom->getNr()] = Walker; // Walker is the predecessor
261 ShortestPathList[OtherAtom->getNr()] = ShortestPathList[Walker->getNr()] + 1;
262 LOG(2, "ACCEPT: Coloring OtherAtom " << OtherAtom->getName() << " lightgray, its predecessor is " << Walker->getName() << " and its Shortest Path is " << ShortestPathList[OtherAtom->getNr()] << " egde(s) long.");
263 if (OtherAtom->GetTrueFather()->IsCyclic) { // if the other atom is connected to a ring
264 ASSERT(!MinimumRingSize.count(Root->GetTrueFather()->getNr()),
265 "CyclicStructureAnalysis::BFSToNextCycle() - setting MinimumRingSize of "
266 +toString(*(Root->GetTrueFather()))+" to "+
267 toString(ShortestPathList[OtherAtom->getNr()] + MinimumRingSize[OtherAtom->GetTrueFather()->getNr()])
268 +" which is already set to "
269 +toString(MinimumRingSize[Root->GetTrueFather()->getNr()])+".");
270 MinimumRingSize[Root->GetTrueFather()->getNr()] = ShortestPathList[OtherAtom->getNr()] + MinimumRingSize[OtherAtom->GetTrueFather()->getNr()];
271 OtherAtom = NULL; //break;
272 break;
273 } else
274 BFSStack.push_front(OtherAtom);
275 } else {
276 LOG(3, "REJECT: Not Adding, has already been visited.");
277 }
278 } else {
279 LOG(3, "REJECT: Not Visiting, is a back edge.");
280 }
281 }
282 ColorList[Walker->getNr()] = GraphEdge::black;
283 LOG(1, "INFO: Coloring Walker " << Walker->getName() << " " << GraphEdge::getColorName(ColorList[Walker->getNr()]) << ".");
284 }
285}
286
287/** All nodes that are not in cycles get assigned a \a *&MinimumRingSizeby BFS to next cycle.
288 * \param *&MinimumRingSize array with minimum distance without encountering onself for each atom
289 * \param &MinRingSize global minium distance
290 * \param &NumCyles number of cycles in graph
291 */
292void CyclicStructureAnalysis::AssignRingSizetoNonCycleMembers(int &MinRingSize, int &NumCycles)
293{
294 atom *Root = NULL;
295 atom *Walker = NULL;
296 if (MinRingSize != -1) { // if rings are present
297 // go over all atoms
298 World::AtomComposite allatoms = World::getInstance().getAllAtoms();
299 for (World::AtomComposite::const_iterator iter = allatoms.begin();
300 iter != allatoms.end();
301 ++iter) {
302 Root = *iter;
303
304 if (MinimumRingSize.find(Root->GetTrueFather()->getNr()) != MinimumRingSize.end()) { // check whether MinimumRingSize is set, if not BFS to next where it is
305 Walker = Root;
306
307 LOG(1, "---------------------------------------------------------------------------------------------------------");
308 BFSToNextCycle(Root, Walker);
309
310 }
311 ASSERT(MinimumRingSize.find(Root->GetTrueFather()->getNr()) != MinimumRingSize.end(),
312 "CyclicStructureAnalysis::AssignRingSizetoNonCycleMembers() - BFSToNextCycle did not set MinimumRingSize of "
313 +toString(*(Root->GetTrueFather()))+".");
314 LOG(1, "INFO: Minimum ring size of " << *Root << " is " << MinimumRingSize[Root->GetTrueFather()->getNr()] << "." << endl);
315 }
316 LOG(1, "INFO: Minimum ring size is " << MinRingSize << ", over " << NumCycles << " cycles total." << endl);
317 } else
318 LOG(1, "INFO: No rings were detected in the molecular structure." << endl);
319}
320
321/** Analyses the cycles found and returns minimum of all cycle lengths.
322 * We begin with a list of Back edges found during DepthFirstSearchAnalysis(). We go through this list - one end is the Root,
323 * the other our initial Walker - and do a Breadth First Search for the Root. We mark down each Predecessor and as soon as
324 * we have found the Root via BFS, we may climb back the closed cycle via the Predecessors. Thereby we mark atoms and bonds
325 * as cyclic and print out the cycles.
326 * \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!
327 * \todo BFS from the not-same-LP to find back to starting point of tributary cycle over more than one bond
328 */
329void CyclicStructureAnalysis::operator()(std::deque<bond *> * BackEdgeStack)
330{
331 Info FunctionInfo("CyclicStructureAnalysis");
332 atom *Walker = NULL;
333 atom *OtherAtom = NULL;
334 bond *BackEdge = NULL;
335 int NumCycles = 0;
336 int MinRingSize = -1;
337
338 //std::stringstream output;
339 //output << "Back edge list - ";
340 //BackEdgeStack->Output(output);
341 //LOG(0, output.str());
342
343 LOG(1, "STATUS: Analysing cycles ... " << endl);
344 NumCycles = 0;
345 while (!BackEdgeStack->empty()) {
346 BackEdge = BackEdgeStack->front();
347 BackEdgeStack->pop_front();
348 // this is the target
349 Root = BackEdge->leftatom;
350 // this is the source point
351 Walker = BackEdge->rightatom;
352
353 InitializeToRoot(Walker);
354
355 LOG(1, "---------------------------------------------------------------------------------------------------------" << endl);
356 OtherAtom = NULL;
357 // go to next cycle via BFS
358 CyclicBFSFromRootToRoot(BackEdge);
359 // get all member nodes of this cycle
360 RetrieveCycleMembers(OtherAtom, BackEdge, MinRingSize);
361
362 CleanAllTouched();
363 }
364 AssignRingSizetoNonCycleMembers(MinRingSize, NumCycles);
365}
366
367/** Output a list of flags, stating whether the bond was visited or not.
368 * \param *list list to print
369 */
370void CyclicStructureAnalysis::OutputAlreadyVisited(int *list)
371{
372 std::stringstream output;
373 output << "Already Visited Bonds:\t";
374 for (int i = 1; i <= list[0]; i++)
375 output << list[i] << " ";
376 LOG(0, output.str());
377}
378
379const std::map<atomId_t, int >& CyclicStructureAnalysis::getMinimumRingSize() const
380{
381 return MinimumRingSize;
382}
Note: See TracBrowser for help on using the repository browser.