source: src/Graph/BondGraph.cpp@ 19832d

Candidate_v1.7.0 stable
Last change on this file since 19832d was 19832d, checked in by Frederik Heber <frederik.heber@…>, 20 months ago

BondGraph: returns 1. instead of -1. on miss.

  • when the symmetric matrix did not have an entry or the matrix itself was null, then we returned -1.
  • This was often ued in following computations and ended us finding the atom on the "other" side of a bonded atom than expected.
  • Now, we return always 1. as this is a distance than should be always useful and can be brought to correct lengths in an optimization run.
  • Property mode set to 100644
File size: 18.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 * Copyright (C) 2013 Frederik Heber. All rights reserved.
6 *
7 *
8 * This file is part of MoleCuilder.
9 *
10 * MoleCuilder is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * MoleCuilder is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24/*
25 * bondgraph.cpp
26 *
27 * Created on: Oct 29, 2009
28 * Author: heber
29 */
30
31// include config.h
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35
36// boost::graph uses placement new
37#include <boost/graph/adjacency_list.hpp>
38
39//#include "CodePatterns/MemDebug.hpp"
40
41#include <algorithm>
42#include <boost/bimap.hpp>
43#include <boost/bind.hpp>
44#include <boost/foreach.hpp>
45#include <boost/function.hpp>
46#include <boost/graph/max_cardinality_matching.hpp>
47#include <deque>
48#include <iostream>
49
50#include "Atom/atom.hpp"
51#include "Bond/bond.hpp"
52#include "Graph/BondGraph.hpp"
53#include "Box.hpp"
54#include "Element/element.hpp"
55#include "CodePatterns/Info.hpp"
56#include "CodePatterns/Log.hpp"
57#include "CodePatterns/Range.hpp"
58#include "CodePatterns/Verbose.hpp"
59#include "molecule.hpp"
60#include "Element/periodentafel.hpp"
61#include "Fragmentation/MatrixContainer.hpp"
62#include "Graph/DepthFirstSearchAnalysis.hpp"
63#include "LinearAlgebra/Vector.hpp"
64#include "World.hpp"
65#include "WorldTime.hpp"
66
67const double BondGraph::BondThreshold = 0.4; //!< CSD threshold in bond check which is the width of the interval whose center is the sum of the covalent radii
68
69BondGraph::BondGraph() :
70 BondLengthMatrix(NULL),
71 IsAngstroem(true)
72{}
73
74BondGraph::BondGraph(bool IsA) :
75 BondLengthMatrix(NULL),
76 IsAngstroem(IsA)
77{}
78
79BondGraph::~BondGraph()
80{
81 CleanupBondLengthTable();
82}
83
84void BondGraph::CleanupBondLengthTable()
85{
86 if (BondLengthMatrix != NULL) {
87 delete(BondLengthMatrix);
88 }
89}
90
91bool BondGraph::LoadBondLengthTable(
92 std::istream &input)
93{
94 Info FunctionInfo(__func__);
95 bool status = true;
96 MatrixContainer *TempContainer = NULL;
97
98 // allocate MatrixContainer
99 if (BondLengthMatrix != NULL) {
100 LOG(1, "MatrixContainer for Bond length already present, removing.");
101 delete(BondLengthMatrix);
102 BondLengthMatrix = NULL;
103 }
104 TempContainer = new MatrixContainer;
105
106 // parse in matrix
107 if ((input.good()) && (status = TempContainer->ParseMatrix(input, 0, 1, 0))) {
108 LOG(1, "Parsing bond length matrix successful.");
109 } else {
110 ELOG(1, "Parsing bond length matrix failed.");
111 status = false;
112 }
113
114 if (status) // set to not NULL only if matrix was parsed
115 BondLengthMatrix = TempContainer;
116 else {
117 BondLengthMatrix = NULL;
118 delete(TempContainer);
119 }
120 return status;
121}
122
123double BondGraph::GetBondLength(
124 int firstZ,
125 int secondZ) const
126{
127 // returns 1. as an at least useful length
128 int firstIndex = firstZ-1;
129 int secondIndex = secondZ-1;
130 double return_length;
131 if ((firstIndex < 0) || (firstIndex >= (int)BondLengthMatrix->Matrix[0].size()))
132 return 1.;
133 if ((secondIndex < 0) || (secondIndex >= (int)BondLengthMatrix->Matrix[0][firstIndex].size()))
134 return 1.;
135 if (BondLengthMatrix == NULL) {
136 return_length = 1.;
137 } else {
138 return_length = BondLengthMatrix->Matrix[0][firstIndex][secondIndex];
139 }
140 LOG(4, "INFO: Request for length between " << firstZ << " and "
141 << secondZ << ": " << return_length << ".");
142 return return_length;
143}
144
145range<double> BondGraph::CovalentMinMaxDistance(
146 const element * const Walker,
147 const element * const OtherWalker) const
148{
149 range<double> MinMaxDistance(0.,0.);
150 MinMaxDistance.first = OtherWalker->getCovalentRadius() + Walker->getCovalentRadius();
151 MinMaxDistance.first *= (IsAngstroem) ? 1. : 1. / AtomicLengthToAngstroem;
152 MinMaxDistance.last = MinMaxDistance.first + BondThreshold;
153 MinMaxDistance.first -= BondThreshold;
154 return MinMaxDistance;
155}
156
157range<double> BondGraph::BondLengthMatrixMinMaxDistance(
158 const element * const Walker,
159 const element * const OtherWalker) const
160{
161 ASSERT(BondLengthMatrix, "BondGraph::BondLengthMatrixMinMaxDistance() called without NULL BondLengthMatrix.");
162 ASSERT(Walker, "BondGraph::BondLengthMatrixMinMaxDistance() - illegal element given.");
163 ASSERT(OtherWalker, "BondGraph::BondLengthMatrixMinMaxDistance() - illegal other element given.");
164 range<double> MinMaxDistance(0.,0.);
165 MinMaxDistance.first = GetBondLength(Walker->getAtomicNumber()-1, OtherWalker->getAtomicNumber()-1);
166 MinMaxDistance.first *= (IsAngstroem) ? 1. : 1. / AtomicLengthToAngstroem;
167 MinMaxDistance.last = MinMaxDistance.first + BondThreshold;
168 MinMaxDistance.first -= BondThreshold;
169 return MinMaxDistance;
170}
171
172range<double> BondGraph::getMinMaxDistance(
173 const element * const Walker,
174 const element * const OtherWalker) const
175{
176 range<double> MinMaxDistance(0.,0.);
177 if (BondLengthMatrix == NULL) {// safety measure if no matrix has been parsed yet
178 LOG(5, "DEBUG: Using Covalent radii criterion for [min,max) distances.");
179 MinMaxDistance = CovalentMinMaxDistance(Walker, OtherWalker);
180 } else {
181 LOG(5, "DEBUG: Using tabulated bond table criterion for [min,max) distances.");
182 MinMaxDistance = BondLengthMatrixMinMaxDistance(Walker, OtherWalker);
183 }
184 return MinMaxDistance;
185}
186
187range<double> BondGraph::getMinMaxDistance(
188 const BondedParticle * const Walker,
189 const BondedParticle * const OtherWalker) const
190{
191 return getMinMaxDistance(Walker->getType(), OtherWalker->getType());
192}
193
194range<double> BondGraph::getMinMaxDistanceSquared(
195 const BondedParticle * const Walker,
196 const BondedParticle * const OtherWalker) const
197{
198 // use non-squared version
199 range<double> MinMaxDistance(getMinMaxDistance(Walker, OtherWalker));
200 // and square
201 MinMaxDistance.first *= MinMaxDistance.first;
202 MinMaxDistance.last *= MinMaxDistance.last;
203 return MinMaxDistance;
204}
205
206LinkedCell::LinkedCell_View BondGraph::getLinkedCell(const double max_distance) const
207{
208 return World::getInstance().getLinkedCell(max_distance);
209}
210
211std::set< const element *> BondGraph::getElementSetFromNumbers(const std::set<atomicNumber_t> &Set) const
212{
213 std::set< const element *> PresentElements;
214 for(std::set<atomicNumber_t>::const_iterator iter = Set.begin(); iter != Set.end(); ++iter)
215 PresentElements.insert( World::getInstance().getPeriode()->FindElement(*iter) );
216 return PresentElements;
217}
218
219Box &BondGraph::getDomain() const
220{
221 return World::getInstance().getDomain();
222}
223
224unsigned int BondGraph::getTime() const
225{
226 return WorldTime::getTime();
227}
228
229bool BondGraph::operator==(const BondGraph &other) const
230{
231 if (IsAngstroem != other.IsAngstroem)
232 return false;
233 if (((BondLengthMatrix != NULL) && (other.BondLengthMatrix == NULL))
234 || ((BondLengthMatrix == NULL) && (other.BondLengthMatrix != NULL)))
235 return false;
236 if ((BondLengthMatrix != NULL) && (other.BondLengthMatrix != NULL))
237 if (*BondLengthMatrix != *other.BondLengthMatrix)
238 return false;
239 return true;
240}
241
242/** Corrects the bond degree by one at most if necessary.
243 *
244 * We are constraint to bonds in \a egdes, if the candidate bond is in edges, we
245 * just don't increment its bond degree. We do not choose an alternative for this
246 * atom.
247 *
248 * \param edges only these edges can be updated
249 * \return number of corrections done
250 */
251int BondGraph::CorrectBondDegree(atom *_atom, const std::set<bond::ptr>& edges) const
252{
253 int NoBonds = 0;
254 int OtherNoBonds = 0;
255 int FalseBondDegree = 0;
256 int CandidateDeltaNoBonds = -1;
257 atom *OtherWalker = NULL;
258 bond::ptr CandidateBond;
259
260 NoBonds = _atom->CountBonds();
261 LOG(3, "Walker " << *_atom << ": " << (int)_atom->getType()->getNoValenceOrbitals() << " > " << NoBonds << "?");
262 const int DeltaNoBonds = (int)(_atom->getType()->getNoValenceOrbitals()) - NoBonds;
263 if (DeltaNoBonds > 0) { // we have a mismatch, check all bonding partners for mismatch
264 const BondList& ListOfBonds = _atom->getListOfBonds();
265 for (BondList::const_iterator Runner = ListOfBonds.begin(); Runner != ListOfBonds.end(); (++Runner)) {
266 OtherWalker = (*Runner)->GetOtherAtom(_atom);
267 OtherNoBonds = OtherWalker->CountBonds();
268 const int OtherDeltaNoBonds = (int)(OtherWalker->getType()->getNoValenceOrbitals()) - OtherNoBonds;
269 LOG(3, "OtherWalker " << *OtherWalker << ": " << (int)OtherWalker->getType()->getNoValenceOrbitals() << " > " << OtherNoBonds << "?");
270 if (OtherDeltaNoBonds > 0) { // check if possible candidate
271 const BondList& OtherListOfBonds = OtherWalker->getListOfBonds();
272 if ((CandidateBond == NULL)
273 || (ListOfBonds.size() > OtherListOfBonds.size()) // pick the one with fewer number of bonds first
274 || ((CandidateDeltaNoBonds < 0) || (CandidateDeltaNoBonds > OtherDeltaNoBonds)) ) // pick the one closer to fulfilling its valence first
275 {
276 CandidateDeltaNoBonds = OtherDeltaNoBonds;
277 CandidateBond = (*Runner);
278 LOG(3, "New candidate is " << *CandidateBond << ".");
279 }
280 }
281 }
282 if ((CandidateBond != NULL)) {
283 if (edges.find(CandidateBond) != edges.end()) {
284 CandidateBond->setDegree(CandidateBond->getDegree()+1);
285 LOG(2, "Increased bond degree for bond " << *CandidateBond << ".");
286 } else
287 LOG(2, "Bond " << *CandidateBond << " is not in edges.");
288 } else {
289 ELOG(2, "Could not find correct degree for atom " << *_atom << ".");
290 FalseBondDegree++;
291 }
292 }
293 return FalseBondDegree;
294}
295
296/** Sets the weight of all connected bonds to one.
297 */
298void BondGraph::resetBondDegree(atom *_atom) const
299{
300 const BondList &ListOfBonds = _atom->getListOfBonds();
301 for (BondList::const_iterator BondRunner = ListOfBonds.begin();
302 BondRunner != ListOfBonds.end();
303 ++BondRunner)
304 (*BondRunner)->setDegree(1);
305}
306
307std::set<bond::ptr> BondGraph::getBackEdges() const
308{
309 DepthFirstSearchAnalysis DFS;
310 DFS();
311
312 const std::deque<bond::ptr>& backedgestack = DFS.getBackEdgeStack();
313 std::set<bond::ptr> backedges(backedgestack.begin(), backedgestack.end());
314
315 return backedges;
316}
317
318std::set<bond::ptr> BondGraph::getTreeEdges() const
319{
320 const std::set<bond::ptr> backedges = getBackEdges();
321 std::set<bond::ptr> alledges;
322 const World& world = World::getInstance();
323 for(World::AtomConstIterator iter = world.getAtomIter();
324 iter != world.atomEnd(); ++iter) {
325 const BondList &ListOfBonds = (*iter)->getListOfBonds();
326 alledges.insert(ListOfBonds.begin(), ListOfBonds.end());
327 }
328 std::set<bond::ptr> treeedges;
329 std::set_difference(
330 alledges.begin(), alledges.end(),
331 backedges.begin(), backedges.end(),
332 std::inserter(treeedges, treeedges.begin()));
333 return treeedges;
334}
335
336struct hasDelta {
337 bool operator()(atom *_atom) {
338 const double delta =
339 _atom->getType()->getNoValenceOrbitals() - _atom->CountBonds();
340 return (fabs(delta) > 0);
341 }
342};
343
344typedef std::set<atom *> deltaatoms_t;
345typedef std::set<bond::ptr> deltaedges_t;
346
347static int gatherAllDeltaAtoms(
348 const deltaatoms_t &allatoms,
349 deltaatoms_t &deltaatoms)
350{
351 static hasDelta delta;
352 deltaatoms.clear();
353 for (deltaatoms_t::const_iterator iter = allatoms.begin();
354 iter != allatoms.end(); ++iter) {
355 if (delta(*iter))
356 deltaatoms.insert(*iter);
357 }
358 return deltaatoms.size();
359}
360
361typedef boost::bimap<int,atom*> AtomIndexLookup_t;
362
363static AtomIndexLookup_t createAtomIndexLookup(
364 const deltaedges_t &edges)
365{
366 AtomIndexLookup_t AtomIndexLookup;
367 size_t index = 0;
368 for (deltaedges_t::const_iterator edgeiter = edges.begin();
369 edgeiter != edges.end(); ++edgeiter) {
370 const bond::ptr & _bond = *edgeiter;
371
372 // insert left into lookup
373 std::pair< AtomIndexLookup_t::iterator, bool > inserter =
374 AtomIndexLookup.insert( AtomIndexLookup_t::value_type(index, _bond->leftatom) );
375 if (inserter.second)
376 ++index;
377
378 // insert right into lookup
379 inserter = AtomIndexLookup.insert( AtomIndexLookup_t::value_type(index, _bond->rightatom) );
380 if (inserter.second)
381 ++index;
382 }
383 return AtomIndexLookup;
384}
385
386typedef std::map< std::pair<atom *, atom*>, bond::ptr> BondLookup_t;
387static BondLookup_t createBondLookup(
388 const deltaedges_t &edges)
389{
390 BondLookup_t BondLookup;
391 for (deltaedges_t::const_iterator edgeiter = edges.begin();
392 edgeiter != edges.end(); ++edgeiter) {
393 const bond::ptr & _bond = *edgeiter;
394
395 // insert bond into pair lookup
396 BondLookup.insert(
397 std::make_pair(
398 std::make_pair(_bond->leftatom, _bond->rightatom),
399 _bond)
400 );
401 }
402 return BondLookup;
403}
404
405typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> graph_t;
406typedef std::vector<boost::graph_traits<graph_t>::vertex_descriptor> mate_t;
407
408static void fillEdgesIntoMatching(
409 graph_t &g,
410 mate_t &mate,
411 const AtomIndexLookup_t &AtomIndexLookup,
412 const BondLookup_t &BondLookup,
413 deltaedges_t &matching
414 )
415{
416 matching.clear();
417 boost::graph_traits<graph_t>::vertex_iterator vi, vi_end;
418 for(boost::tuples::tie(vi,vi_end) = boost::vertices(g); vi != vi_end; ++vi)
419 if (mate[*vi] != boost::graph_traits<graph_t>::null_vertex() && *vi < mate[*vi]) {
420 atom * leftatom = AtomIndexLookup.left.at(*vi);
421 atom * rightatom = AtomIndexLookup.left.at(mate[*vi]);
422 std::pair<atom *,atom *> AtomPair(leftatom, rightatom);
423 std::pair<atom *,atom *> reverseAtomPair(rightatom, leftatom);
424 BondLookup_t::const_iterator iter = BondLookup.find(AtomPair);
425 if (iter != BondLookup.end()) {
426 matching.insert(iter->second);
427 } else {
428 iter = BondLookup.find(reverseAtomPair);
429 if (iter != BondLookup.end()) {
430 matching.insert(iter->second);
431 } else
432 ASSERT(0, "fillEdgesIntoMatching() - cannot find ("+toString(*vi)+","
433 +toString(mate[*vi])+") in BondLookup.");
434 }
435 }
436}
437
438static bool createMatching(
439 deltaedges_t &deltaedges,
440 deltaedges_t &matching
441 )
442{
443 // gather all vertices for graph in a lookup structure
444 // to translate the graphs indices to atoms and also to associated bonds
445 AtomIndexLookup_t AtomIndexLookup = createAtomIndexLookup(deltaedges);
446 BondLookup_t BondLookup = createBondLookup(deltaedges);
447 const int n_vertices = AtomIndexLookup.size();
448
449 // construct graph
450 graph_t g(n_vertices);
451
452 // add edges to graph
453 for (deltaedges_t::const_iterator edgeiter = deltaedges.begin();
454 edgeiter != deltaedges.end(); ++edgeiter) {
455 const bond::ptr & _bond = *edgeiter;
456 boost::add_edge(
457 AtomIndexLookup.right.at(_bond->leftatom),
458 AtomIndexLookup.right.at(_bond->rightatom),
459 g);
460 }
461
462 // mate structure contains unique edge partner to every vertex in matching
463 mate_t mate(n_vertices);
464
465 // get maximum matching over given edges
466 bool success = boost::checked_edmonds_maximum_cardinality_matching(g, &mate[0]);
467
468 if (success) {
469 LOG(1, "STATUS: Found a matching of size " << matching_size(g, &mate[0]) << ".");
470 fillEdgesIntoMatching(g, mate, AtomIndexLookup, BondLookup, matching);
471 } else {
472 ELOG(2, "Failed to find maximum matching.");
473 }
474
475 return success;
476}
477
478bool BondGraph::checkBondDegree(const deltaatoms_t &allatoms) const
479{
480 deltaatoms_t deltaatoms;
481 return (gatherAllDeltaAtoms(allatoms, deltaatoms) == 0);
482}
483
484
485int BondGraph::calculateBondDegree(const deltaatoms_t &allatoms) const
486{
487 deltaatoms_t deltaatoms;
488 deltaedges_t deltaedges;
489 deltaedges_t matching;
490
491 LOG(1, "STATUS: Calculating bond degrees.");
492 if (DoLog(2)) {
493 std::stringstream output;
494 output << "INFO: All atoms are: ";
495 BOOST_FOREACH( atom *_atom, allatoms) {
496 output << *_atom << " ";
497 }
498 LOG(2, output.str());
499 }
500
501 size_t IterCount = 0;
502 // 1. gather all atoms with delta > 0
503 while ((gatherAllDeltaAtoms(allatoms, deltaatoms) != 0) && (IterCount < 100)) {
504 if (DoLog(3)) {
505 std::stringstream output;
506 output << "DEBUG: Current delta atoms are: ";
507 BOOST_FOREACH( atom *_atom, deltaatoms) {
508 output << *_atom << " ";
509 }
510 LOG(3, output.str());
511 }
512
513 // 2. gather all edges that connect atoms with both(!) having delta > 0
514 deltaedges.clear();
515 for (deltaatoms_t::const_iterator atomiter = deltaatoms.begin();
516 atomiter != deltaatoms.end(); ++atomiter) {
517 const atom * const _atom = *atomiter;
518 const BondList& ListOfBonds = (*atomiter)->getListOfBonds();
519 for (BondList::const_iterator bonditer = ListOfBonds.begin();
520 bonditer != ListOfBonds.end();++bonditer) {
521 const bond::ptr _bond = *bonditer;
522 if ((_bond->leftatom == _atom) && (deltaatoms.find(_bond->rightatom) != deltaatoms.end()))
523 deltaedges.insert(_bond);
524 else if ((_bond->rightatom == _atom) && (deltaatoms.find(_bond->leftatom) != deltaatoms.end()))
525 deltaedges.insert(_bond);
526 }
527 }
528 if (DoLog(3)) {
529 std::stringstream output;
530 output << "DEBUG: Current delta bonds are: ";
531 BOOST_FOREACH( bond::ptr _bond, deltaedges) {
532 output << *_bond << " ";
533 }
534 LOG(3, output.str());
535 }
536 if (deltaedges.empty())
537 break;
538
539 // 3. build matching over these edges
540 if (!createMatching(deltaedges, matching))
541 break;
542 if (DoLog(2)) {
543 std::stringstream output;
544 output << "DEBUG: Resulting matching is: ";
545 BOOST_FOREACH( bond::ptr _bond, matching) {
546 output << *_bond << " ";
547 }
548 LOG(2, output.str());
549 }
550
551 // 4. increase degree for each edge of the matching
552 LOG(2, "DEBUG: Increasing bond degrees by one.");
553 for (deltaedges_t::iterator edgeiter = matching.begin();
554 edgeiter != matching.end(); ++edgeiter) {
555 (*edgeiter)->setDegree( (*edgeiter)->getDegree()+1 );
556 }
557
558 // 6. stop after a given number of iterations or when all atoms are happy.
559 ++IterCount;
560 };
561
562 // check whether we still have deltaatoms
563 {
564 hasDelta delta;
565 for (deltaatoms_t::const_iterator iter = allatoms.begin();
566 iter != allatoms.end(); ++iter)
567 if (delta(*iter))
568 ELOG(2, "Could not find satisfy charge neutrality for atom " << **iter << ".");
569 }
570
571 return deltaedges.size();
572}
Note: See TracBrowser for help on using the repository browser.