source: src/Graph/BondGraph.cpp@ ad0929

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

FIX: BondGraph::GetBondLength() accessed off by one.

  • Property mode set to 100644
File size: 18.2 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 int firstIndex = firstZ-1;
128 int secondIndex = secondZ-1;
129 double return_length;
130 if ((firstIndex < 0) || (firstIndex >= (int)BondLengthMatrix->Matrix[0].size()))
131 return -1.;
132 if ((secondIndex < 0) || (secondIndex >= (int)BondLengthMatrix->Matrix[0][firstIndex].size()))
133 return -1.;
134 if (BondLengthMatrix == NULL) {
135 return_length = -1.;
136 } else {
137 return_length = BondLengthMatrix->Matrix[0][firstIndex][secondIndex];
138 }
139 LOG(4, "INFO: Request for length between " << firstZ << " and "
140 << secondZ << ": " << return_length << ".");
141 return return_length;
142}
143
144range<double> BondGraph::CovalentMinMaxDistance(
145 const element * const Walker,
146 const element * const OtherWalker) const
147{
148 range<double> MinMaxDistance(0.,0.);
149 MinMaxDistance.first = OtherWalker->getCovalentRadius() + Walker->getCovalentRadius();
150 MinMaxDistance.first *= (IsAngstroem) ? 1. : 1. / AtomicLengthToAngstroem;
151 MinMaxDistance.last = MinMaxDistance.first + BondThreshold;
152 MinMaxDistance.first -= BondThreshold;
153 return MinMaxDistance;
154}
155
156range<double> BondGraph::BondLengthMatrixMinMaxDistance(
157 const element * const Walker,
158 const element * const OtherWalker) const
159{
160 ASSERT(BondLengthMatrix, "BondGraph::BondLengthMatrixMinMaxDistance() called without NULL BondLengthMatrix.");
161 ASSERT(Walker, "BondGraph::BondLengthMatrixMinMaxDistance() - illegal element given.");
162 ASSERT(OtherWalker, "BondGraph::BondLengthMatrixMinMaxDistance() - illegal other element given.");
163 range<double> MinMaxDistance(0.,0.);
164 MinMaxDistance.first = GetBondLength(Walker->getAtomicNumber()-1, OtherWalker->getAtomicNumber()-1);
165 MinMaxDistance.first *= (IsAngstroem) ? 1. : 1. / AtomicLengthToAngstroem;
166 MinMaxDistance.last = MinMaxDistance.first + BondThreshold;
167 MinMaxDistance.first -= BondThreshold;
168 return MinMaxDistance;
169}
170
171range<double> BondGraph::getMinMaxDistance(
172 const element * const Walker,
173 const element * const OtherWalker) const
174{
175 range<double> MinMaxDistance(0.,0.);
176 if (BondLengthMatrix == NULL) {// safety measure if no matrix has been parsed yet
177 LOG(5, "DEBUG: Using Covalent radii criterion for [min,max) distances.");
178 MinMaxDistance = CovalentMinMaxDistance(Walker, OtherWalker);
179 } else {
180 LOG(5, "DEBUG: Using tabulated bond table criterion for [min,max) distances.");
181 MinMaxDistance = BondLengthMatrixMinMaxDistance(Walker, OtherWalker);
182 }
183 return MinMaxDistance;
184}
185
186range<double> BondGraph::getMinMaxDistance(
187 const BondedParticle * const Walker,
188 const BondedParticle * const OtherWalker) const
189{
190 return getMinMaxDistance(Walker->getType(), OtherWalker->getType());
191}
192
193range<double> BondGraph::getMinMaxDistanceSquared(
194 const BondedParticle * const Walker,
195 const BondedParticle * const OtherWalker) const
196{
197 // use non-squared version
198 range<double> MinMaxDistance(getMinMaxDistance(Walker, OtherWalker));
199 // and square
200 MinMaxDistance.first *= MinMaxDistance.first;
201 MinMaxDistance.last *= MinMaxDistance.last;
202 return MinMaxDistance;
203}
204
205LinkedCell::LinkedCell_View BondGraph::getLinkedCell(const double max_distance) const
206{
207 return World::getInstance().getLinkedCell(max_distance);
208}
209
210std::set< const element *> BondGraph::getElementSetFromNumbers(const std::set<atomicNumber_t> &Set) const
211{
212 std::set< const element *> PresentElements;
213 for(std::set<atomicNumber_t>::const_iterator iter = Set.begin(); iter != Set.end(); ++iter)
214 PresentElements.insert( World::getInstance().getPeriode()->FindElement(*iter) );
215 return PresentElements;
216}
217
218Box &BondGraph::getDomain() const
219{
220 return World::getInstance().getDomain();
221}
222
223unsigned int BondGraph::getTime() const
224{
225 return WorldTime::getTime();
226}
227
228bool BondGraph::operator==(const BondGraph &other) const
229{
230 if (IsAngstroem != other.IsAngstroem)
231 return false;
232 if (((BondLengthMatrix != NULL) && (other.BondLengthMatrix == NULL))
233 || ((BondLengthMatrix == NULL) && (other.BondLengthMatrix != NULL)))
234 return false;
235 if ((BondLengthMatrix != NULL) && (other.BondLengthMatrix != NULL))
236 if (*BondLengthMatrix != *other.BondLengthMatrix)
237 return false;
238 return true;
239}
240
241/** Corrects the bond degree by one at most if necessary.
242 *
243 * We are constraint to bonds in \a egdes, if the candidate bond is in edges, we
244 * just don't increment its bond degree. We do not choose an alternative for this
245 * atom.
246 *
247 * \param edges only these edges can be updated
248 * \return number of corrections done
249 */
250int BondGraph::CorrectBondDegree(atom *_atom, const std::set<bond::ptr>& edges) const
251{
252 int NoBonds = 0;
253 int OtherNoBonds = 0;
254 int FalseBondDegree = 0;
255 int CandidateDeltaNoBonds = -1;
256 atom *OtherWalker = NULL;
257 bond::ptr CandidateBond;
258
259 NoBonds = _atom->CountBonds();
260 LOG(3, "Walker " << *_atom << ": " << (int)_atom->getType()->getNoValenceOrbitals() << " > " << NoBonds << "?");
261 const int DeltaNoBonds = (int)(_atom->getType()->getNoValenceOrbitals()) - NoBonds;
262 if (DeltaNoBonds > 0) { // we have a mismatch, check all bonding partners for mismatch
263 const BondList& ListOfBonds = _atom->getListOfBonds();
264 for (BondList::const_iterator Runner = ListOfBonds.begin(); Runner != ListOfBonds.end(); (++Runner)) {
265 OtherWalker = (*Runner)->GetOtherAtom(_atom);
266 OtherNoBonds = OtherWalker->CountBonds();
267 const int OtherDeltaNoBonds = (int)(OtherWalker->getType()->getNoValenceOrbitals()) - OtherNoBonds;
268 LOG(3, "OtherWalker " << *OtherWalker << ": " << (int)OtherWalker->getType()->getNoValenceOrbitals() << " > " << OtherNoBonds << "?");
269 if (OtherDeltaNoBonds > 0) { // check if possible candidate
270 const BondList& OtherListOfBonds = OtherWalker->getListOfBonds();
271 if ((CandidateBond == NULL)
272 || (ListOfBonds.size() > OtherListOfBonds.size()) // pick the one with fewer number of bonds first
273 || ((CandidateDeltaNoBonds < 0) || (CandidateDeltaNoBonds > OtherDeltaNoBonds)) ) // pick the one closer to fulfilling its valence first
274 {
275 CandidateDeltaNoBonds = OtherDeltaNoBonds;
276 CandidateBond = (*Runner);
277 LOG(3, "New candidate is " << *CandidateBond << ".");
278 }
279 }
280 }
281 if ((CandidateBond != NULL)) {
282 if (edges.find(CandidateBond) != edges.end()) {
283 CandidateBond->setDegree(CandidateBond->getDegree()+1);
284 LOG(2, "Increased bond degree for bond " << *CandidateBond << ".");
285 } else
286 LOG(2, "Bond " << *CandidateBond << " is not in edges.");
287 } else {
288 ELOG(2, "Could not find correct degree for atom " << *_atom << ".");
289 FalseBondDegree++;
290 }
291 }
292 return FalseBondDegree;
293}
294
295/** Sets the weight of all connected bonds to one.
296 */
297void BondGraph::resetBondDegree(atom *_atom) const
298{
299 const BondList &ListOfBonds = _atom->getListOfBonds();
300 for (BondList::const_iterator BondRunner = ListOfBonds.begin();
301 BondRunner != ListOfBonds.end();
302 ++BondRunner)
303 (*BondRunner)->setDegree(1);
304}
305
306std::set<bond::ptr> BondGraph::getBackEdges() const
307{
308 DepthFirstSearchAnalysis DFS;
309 DFS();
310
311 const std::deque<bond::ptr>& backedgestack = DFS.getBackEdgeStack();
312 std::set<bond::ptr> backedges(backedgestack.begin(), backedgestack.end());
313
314 return backedges;
315}
316
317std::set<bond::ptr> BondGraph::getTreeEdges() const
318{
319 const std::set<bond::ptr> backedges = getBackEdges();
320 std::set<bond::ptr> alledges;
321 const World& world = World::getInstance();
322 for(World::AtomConstIterator iter = world.getAtomIter();
323 iter != world.atomEnd(); ++iter) {
324 const BondList &ListOfBonds = (*iter)->getListOfBonds();
325 alledges.insert(ListOfBonds.begin(), ListOfBonds.end());
326 }
327 std::set<bond::ptr> treeedges;
328 std::set_difference(
329 alledges.begin(), alledges.end(),
330 backedges.begin(), backedges.end(),
331 std::inserter(treeedges, treeedges.begin()));
332 return treeedges;
333}
334
335struct hasDelta {
336 bool operator()(atom *_atom) {
337 const double delta =
338 _atom->getType()->getNoValenceOrbitals() - _atom->CountBonds();
339 return (fabs(delta) > 0);
340 }
341};
342
343typedef std::set<atom *> deltaatoms_t;
344typedef std::set<bond::ptr> deltaedges_t;
345
346static int gatherAllDeltaAtoms(
347 const deltaatoms_t &allatoms,
348 deltaatoms_t &deltaatoms)
349{
350 static hasDelta delta;
351 deltaatoms.clear();
352 for (deltaatoms_t::const_iterator iter = allatoms.begin();
353 iter != allatoms.end(); ++iter) {
354 if (delta(*iter))
355 deltaatoms.insert(*iter);
356 }
357 return deltaatoms.size();
358}
359
360typedef boost::bimap<int,atom*> AtomIndexLookup_t;
361
362static AtomIndexLookup_t createAtomIndexLookup(
363 const deltaedges_t &edges)
364{
365 AtomIndexLookup_t AtomIndexLookup;
366 size_t index = 0;
367 for (deltaedges_t::const_iterator edgeiter = edges.begin();
368 edgeiter != edges.end(); ++edgeiter) {
369 const bond::ptr & _bond = *edgeiter;
370
371 // insert left into lookup
372 std::pair< AtomIndexLookup_t::iterator, bool > inserter =
373 AtomIndexLookup.insert( AtomIndexLookup_t::value_type(index, _bond->leftatom) );
374 if (inserter.second)
375 ++index;
376
377 // insert right into lookup
378 inserter = AtomIndexLookup.insert( AtomIndexLookup_t::value_type(index, _bond->rightatom) );
379 if (inserter.second)
380 ++index;
381 }
382 return AtomIndexLookup;
383}
384
385typedef std::map< std::pair<atom *, atom*>, bond::ptr> BondLookup_t;
386static BondLookup_t createBondLookup(
387 const deltaedges_t &edges)
388{
389 BondLookup_t BondLookup;
390 for (deltaedges_t::const_iterator edgeiter = edges.begin();
391 edgeiter != edges.end(); ++edgeiter) {
392 const bond::ptr & _bond = *edgeiter;
393
394 // insert bond into pair lookup
395 BondLookup.insert(
396 std::make_pair(
397 std::make_pair(_bond->leftatom, _bond->rightatom),
398 _bond)
399 );
400 }
401 return BondLookup;
402}
403
404typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> graph_t;
405typedef std::vector<boost::graph_traits<graph_t>::vertex_descriptor> mate_t;
406
407static void fillEdgesIntoMatching(
408 graph_t &g,
409 mate_t &mate,
410 const AtomIndexLookup_t &AtomIndexLookup,
411 const BondLookup_t &BondLookup,
412 deltaedges_t &matching
413 )
414{
415 matching.clear();
416 boost::graph_traits<graph_t>::vertex_iterator vi, vi_end;
417 for(boost::tuples::tie(vi,vi_end) = boost::vertices(g); vi != vi_end; ++vi)
418 if (mate[*vi] != boost::graph_traits<graph_t>::null_vertex() && *vi < mate[*vi]) {
419 atom * leftatom = AtomIndexLookup.left.at(*vi);
420 atom * rightatom = AtomIndexLookup.left.at(mate[*vi]);
421 std::pair<atom *,atom *> AtomPair(leftatom, rightatom);
422 std::pair<atom *,atom *> reverseAtomPair(rightatom, leftatom);
423 BondLookup_t::const_iterator iter = BondLookup.find(AtomPair);
424 if (iter != BondLookup.end()) {
425 matching.insert(iter->second);
426 } else {
427 iter = BondLookup.find(reverseAtomPair);
428 if (iter != BondLookup.end()) {
429 matching.insert(iter->second);
430 } else
431 ASSERT(0, "fillEdgesIntoMatching() - cannot find ("+toString(*vi)+","
432 +toString(mate[*vi])+") in BondLookup.");
433 }
434 }
435}
436
437static bool createMatching(
438 deltaedges_t &deltaedges,
439 deltaedges_t &matching
440 )
441{
442 // gather all vertices for graph in a lookup structure
443 // to translate the graphs indices to atoms and also to associated bonds
444 AtomIndexLookup_t AtomIndexLookup = createAtomIndexLookup(deltaedges);
445 BondLookup_t BondLookup = createBondLookup(deltaedges);
446 const int n_vertices = AtomIndexLookup.size();
447
448 // construct graph
449 graph_t g(n_vertices);
450
451 // add edges to graph
452 for (deltaedges_t::const_iterator edgeiter = deltaedges.begin();
453 edgeiter != deltaedges.end(); ++edgeiter) {
454 const bond::ptr & _bond = *edgeiter;
455 boost::add_edge(
456 AtomIndexLookup.right.at(_bond->leftatom),
457 AtomIndexLookup.right.at(_bond->rightatom),
458 g);
459 }
460
461 // mate structure contains unique edge partner to every vertex in matching
462 mate_t mate(n_vertices);
463
464 // get maximum matching over given edges
465 bool success = boost::checked_edmonds_maximum_cardinality_matching(g, &mate[0]);
466
467 if (success) {
468 LOG(1, "STATUS: Found a matching of size " << matching_size(g, &mate[0]) << ".");
469 fillEdgesIntoMatching(g, mate, AtomIndexLookup, BondLookup, matching);
470 } else {
471 ELOG(2, "Failed to find maximum matching.");
472 }
473
474 return success;
475}
476
477bool BondGraph::checkBondDegree(const deltaatoms_t &allatoms) const
478{
479 deltaatoms_t deltaatoms;
480 return (gatherAllDeltaAtoms(allatoms, deltaatoms) == 0);
481}
482
483
484int BondGraph::calculateBondDegree(const deltaatoms_t &allatoms) const
485{
486 deltaatoms_t deltaatoms;
487 deltaedges_t deltaedges;
488 deltaedges_t matching;
489
490 LOG(1, "STATUS: Calculating bond degrees.");
491 if (DoLog(2)) {
492 std::stringstream output;
493 output << "INFO: All atoms are: ";
494 BOOST_FOREACH( atom *_atom, allatoms) {
495 output << *_atom << " ";
496 }
497 LOG(2, output.str());
498 }
499
500 size_t IterCount = 0;
501 // 1. gather all atoms with delta > 0
502 while ((gatherAllDeltaAtoms(allatoms, deltaatoms) != 0) && (IterCount < 100)) {
503 if (DoLog(3)) {
504 std::stringstream output;
505 output << "DEBUG: Current delta atoms are: ";
506 BOOST_FOREACH( atom *_atom, deltaatoms) {
507 output << *_atom << " ";
508 }
509 LOG(3, output.str());
510 }
511
512 // 2. gather all edges that connect atoms with both(!) having delta > 0
513 deltaedges.clear();
514 for (deltaatoms_t::const_iterator atomiter = deltaatoms.begin();
515 atomiter != deltaatoms.end(); ++atomiter) {
516 const atom * const _atom = *atomiter;
517 const BondList& ListOfBonds = (*atomiter)->getListOfBonds();
518 for (BondList::const_iterator bonditer = ListOfBonds.begin();
519 bonditer != ListOfBonds.end();++bonditer) {
520 const bond::ptr _bond = *bonditer;
521 if ((_bond->leftatom == _atom) && (deltaatoms.find(_bond->rightatom) != deltaatoms.end()))
522 deltaedges.insert(_bond);
523 else if ((_bond->rightatom == _atom) && (deltaatoms.find(_bond->leftatom) != deltaatoms.end()))
524 deltaedges.insert(_bond);
525 }
526 }
527 if (DoLog(3)) {
528 std::stringstream output;
529 output << "DEBUG: Current delta bonds are: ";
530 BOOST_FOREACH( bond::ptr _bond, deltaedges) {
531 output << *_bond << " ";
532 }
533 LOG(3, output.str());
534 }
535 if (deltaedges.empty())
536 break;
537
538 // 3. build matching over these edges
539 if (!createMatching(deltaedges, matching))
540 break;
541 if (DoLog(2)) {
542 std::stringstream output;
543 output << "DEBUG: Resulting matching is: ";
544 BOOST_FOREACH( bond::ptr _bond, matching) {
545 output << *_bond << " ";
546 }
547 LOG(2, output.str());
548 }
549
550 // 4. increase degree for each edge of the matching
551 LOG(2, "DEBUG: Increasing bond degrees by one.");
552 for (deltaedges_t::iterator edgeiter = matching.begin();
553 edgeiter != matching.end(); ++edgeiter) {
554 (*edgeiter)->setDegree( (*edgeiter)->getDegree()+1 );
555 }
556
557 // 6. stop after a given number of iterations or when all atoms are happy.
558 ++IterCount;
559 };
560
561 // check whether we still have deltaatoms
562 {
563 hasDelta delta;
564 for (deltaatoms_t::const_iterator iter = allatoms.begin();
565 iter != allatoms.end(); ++iter)
566 if (delta(*iter))
567 ELOG(2, "Could not find satisfy charge neutrality for atom " << **iter << ".");
568 }
569
570 return deltaedges.size();
571}
Note: See TracBrowser for help on using the repository browser.