source: src/Graph/BondGraph.hpp@ 97dff0

Last change on this file since 97dff0 was 4f04ab8, checked in by Frederik Heber <heber@…>, 12 years ago

Using boost::graph to obtain maximum matching for bond degree correction.

  • added bimap to boost.m4, added boost_bimap and boost_graph to configure.ac.
  • rewrote createMatching() in BondGraph with boost::graph, code mostly taken from maximum cardinality matching example.
  • Property mode set to 100644
File size: 19.9 KB
RevLine 
[b70721]1/*
2 * bondgraph.hpp
3 *
4 * Created on: Oct 29, 2009
5 * Author: heber
6 */
7
8#ifndef BONDGRAPH_HPP_
9#define BONDGRAPH_HPP_
10
11using namespace std;
12
13/*********************************************** includes ***********************************/
14
15// include config.h
16#ifdef HAVE_CONFIG_H
17#include <config.h>
18#endif
19
[986ed3]20#include <iosfwd>
[108968]21#include <set>
[b70721]22
[f007a1]23#include <boost/serialization/array.hpp>
24
[6f0841]25#include "Atom/AtomSet.hpp"
[129204]26#include "Bond/bond.hpp"
[826e8c]27#include "Box.hpp"
[3738f0]28#include "CodePatterns/Assert.hpp"
29#include "CodePatterns/Log.hpp"
30#include "CodePatterns/Range.hpp"
[3bdb6d]31#include "Element/element.hpp"
[f007a1]32#include "Fragmentation/MatrixContainer.hpp"
[826e8c]33#include "Helpers/defs.hpp"
34#include "LinkedCell/LinkedCell_View.hpp"
[53c7fc]35#include "LinkedCell/IPointCloud.hpp"
36#include "LinkedCell/PointCloudAdaptor.hpp"
[0cbad2]37#include "WorldTime.hpp"
[b48ba6]38
[b70721]39/****************************************** forward declarations *****************************/
40
41class molecule;
[97ebf8]42class BondedParticle;
[b70721]43class MatrixContainer;
44
45/********************************************** definitions *********************************/
46
47/********************************************** declarations *******************************/
48
49
50class BondGraph {
[300220]51 //!> analysis bonds unit test should be friend to access private parts.
52 friend class AnalysisBondsTest;
53 //!> own bond graph unit test should be friend to access private parts.
54 friend class BondGraphTest;
[b70721]55public:
[e7350d4]56 /** Constructor of class BondGraph.
57 * This classes contains typical bond lengths and thus may be used to construct a bond graph for a given molecule.
58 */
[b70721]59 BondGraph(bool IsA);
[e7350d4]60
61 /** Destructor of class BondGraph.
62 */
[b70721]63 ~BondGraph();
[e7350d4]64
65 /** Parses the bond lengths in a given file and puts them int a matrix form.
66 * Allocates \a MatrixContainer for BondGraph::BondLengthMatrix, using MatrixContainer::ParseMatrix(),
67 * but only if parsing is successful. Otherwise variable is left as NULL.
68 * \param &input input stream to parse table from
69 * \return true - success in parsing file, false - failed to parse the file
70 */
[4e855e]71 bool LoadBondLengthTable(std::istream &input);
[e7350d4]72
[829761]73 /** Removes allocated bond length table.
74 *
75 */
76 void CleanupBondLengthTable();
77
[108968]78 /** Internal helper to convert a set of atomicNumber_t to element refs.
79 *
80 * @param Set set of atomicNumber_t
81 * @return set of element refs
82 */
83 std::set< const element *> getElementSetFromNumbers(const std::set<atomicNumber_t> &Set) const;
84
[3738f0]85 /** Determines the maximum of all element::CovalentRadius for elements present in \a &Set.
86 *
[0ec7fe]87 * I.e. the function returns a sensible cutoff criteria for bond recognition,
[6bd7e0]88 * e.g. to be used for LinkedCell_deprecated or others.
[3738f0]89 *
[108968]90 * \param &PresentElements set of elements whose maximal pair to find
[3738f0]91 */
[0ec7fe]92 double getMaxPossibleBondDistance(
[108968]93 const std::set< const element *> &PresentElements) const
[3738f0]94 {
[0ec7fe]95 double max_distance = 0.;
[108968]96
[0ec7fe]97 // create all element combinations
98 for (std::set< const element *>::const_iterator iter = PresentElements.begin();
99 iter != PresentElements.end();
100 ++iter) {
101 for (std::set< const element *>::const_iterator otheriter = iter;
102 otheriter != PresentElements.end();
103 ++otheriter) {
[607eab]104 const range<double> MinMaxDistance(getMinMaxDistance((*iter),(*otheriter)));
[0ec7fe]105 if (MinMaxDistance.last > max_distance)
106 max_distance = MinMaxDistance.last;
107 }
[3738f0]108 }
109 return max_distance;
110 }
111
[826e8c]112 /** Determines the maximum of all element::CovalentRadius for elements present in \a &Set.
113 *
114 * I.e. the function returns a sensible cutoff criteria for bond recognition,
115 * e.g. to be used for LinkedCell_deprecated or others.
116 *
[108968]117 * \param Walker element first element in the pair
118 * \param &PresentElements set of elements whose maximal pair to find
[826e8c]119 */
120 double getMaxPossibleBondDistance(
121 const element * const Walker,
[108968]122 const std::set< const element *> &PresentElements) const
[826e8c]123 {
124 double max_distance = 0.;
[108968]125
[826e8c]126 // create all element combinations
127 for (std::set< const element *>::const_iterator iter = PresentElements.begin();
128 iter != PresentElements.end();
129 ++iter) {
130 const range<double> MinMaxDistance(getMinMaxDistance((*iter),Walker));
131 if (MinMaxDistance.last > max_distance)
132 max_distance = MinMaxDistance.last;
133 }
134 return max_distance;
135 }
136
[72d90e]137 /** Returns bond criterion for given pair based on a bond length matrix.
[300220]138 * This calls element-version of getMinMaxDistance().
[72d90e]139 * \param *Walker first BondedParticle
140 * \param *OtherWalker second BondedParticle
[607eab]141 * \return Range with bond interval
[72d90e]142 */
[607eab]143 range<double> getMinMaxDistance(
[300220]144 const BondedParticle * const Walker,
[607eab]145 const BondedParticle * const OtherWalker) const;
[300220]146
147 /** Returns SQUARED bond criterion for given pair based on a bond length matrix.
148 * This calls element-version of getMinMaxDistance() and squares the values
149 * of either interval end.
150 * \param *Walker first BondedParticle
151 * \param *OtherWalker second BondedParticle
[607eab]152 * \return Range with bond interval
[300220]153 */
[607eab]154 range<double> getMinMaxDistanceSquared(
[300220]155 const BondedParticle * const Walker,
[607eab]156 const BondedParticle * const OtherWalker) const;
[b70721]157
[826e8c]158 /** Creates an adjacency list of the molecule.
159 * Generally, we use the CSD approach to bond recognition, that is the the distance
160 * between two atoms A and B must be within [Rcov(A)+Rcov(B)-t,Rcov(A)+Rcov(B)+t] with
161 * a threshold t = 0.4 Angstroem.
162 * To make it O(N log N) the function uses the linked-cell technique as follows:
163 * The procedure is step-wise:
164 * -# Remove every bond in list
165 * -# go through every atom in given \a set, check the atoms therein against all possible bond partners, add bond if true
166 * -# correct the bond degree iteratively (single->double->triple bond)
167 * -# finally print the bond list to \a *out if desired
168 * \param &set Container with all atoms to create adjacency for
[e7350d4]169 */
[3738f0]170 template <class container_type,
171 class iterator_type,
172 class const_iterator_type>
[111f4a]173 void CreateAdjacency(
174 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
[3738f0]175 {
176 LOG(1, "STATUS: Removing all present bonds.");
177 cleanAdjacencyList(Set);
178
[108968]179 // gather set of all present elements
180 std::set<atomicNumber_t> elements;
181 for(typename AtomSetMixin<container_type,iterator_type,const_iterator_type>::iterator iter = Set.begin();
182 iter != Set.end(); ++iter) {
183 const atom * const Walker = dynamic_cast<const atom *>(*iter);
184 ASSERT(Walker != NULL,
185 "BondGraph::CreateAdjacency() - TesselPoint "
186 +(*iter)->getName()+" that was not an atom retrieved from given set");
187 elements.insert( Walker->getElementNo() );
188 }
189 // get all elements
[31021ab]190 const std::set< const element *> PresentElements = getElementSetFromNumbers(elements);
[108968]191
[3738f0]192 // count atoms in molecule = dimension of matrix (also give each unique name and continuous numbering)
193 const unsigned int counter = Set.size();
194 if (counter > 1) {
195 LOG(1, "STATUS: Setting max bond distance.");
[31021ab]196 LOG(1, "STATUS: Creating LinkedCell structure for given " << counter << " atoms.");
[108968]197 LinkedCell::LinkedCell_View LC = getLinkedCell(getMaxPossibleBondDistance(PresentElements));
[3738f0]198
[31021ab]199 LOG(1, "STATUS: Evaluating distance criterion for each atom.");
[3738f0]200
[31021ab]201 const Box &domain = getDomain();
202 const unsigned int CurrentTime = getTime();
[826e8c]203
204 unsigned int BondCount = 0;
205 // go through every atom in the set (observed cause we change its bonds)
206 for(typename AtomSetMixin<container_type,iterator_type,const_iterator_type>::iterator iter = Set.begin();
207 iter != Set.end(); ++iter) {
208 const atom * const Walker = dynamic_cast<const atom *>(*iter);
209 ASSERT(Walker != NULL,
210 "BondGraph::CreateAdjacency() - TesselPoint "
211 +(*iter)->getName()+" that was not an atom retrieved from given set");
212 LOG(2, "INFO: Current Atom is " << *Walker << ".");
213
214 // obtain all possible neighbors
215 LinkedCell::LinkedList ListOfNeighbors = LC.getAllNeighbors(
[108968]216 getMaxPossibleBondDistance(Walker->getType(), PresentElements),
[826e8c]217 Walker->getPosition());
218 if (!ListOfNeighbors.empty()) {
219 // we have some possible candidates, go through each
220 for (LinkedCell::LinkedList::const_iterator neighboriter = ListOfNeighbors.begin();
221 neighboriter != ListOfNeighbors.end();
222 ++neighboriter) {
223 if ((*neighboriter) > Walker) { // just to not add bonds from both sides
224 const atom * const OtherWalker = dynamic_cast<const atom *>(*neighboriter);
225 ASSERT(OtherWalker != NULL,
226 "BondGraph::CreateAdjacency() - TesselPoint "
227 +(*neighboriter)->getName()+" that was not an atom retrieved from LinkedList");
[335011]228 LOG(4, "INFO: Current other atom is " << *OtherWalker << ".");
[826e8c]229
230 const range<double> MinMaxDistanceSquared(
231 getMinMaxDistanceSquared(Walker, OtherWalker));
232 const double distance = domain.periodicDistanceSquared(OtherWalker->getPosition(),Walker->getPosition());
233 LOG(3, "INFO: Checking squared distance " << distance << " against typical bond length of " << MinMaxDistanceSquared << ".");
234 const bool status = MinMaxDistanceSquared.isInRange(distance);
235 if (status) { // create bond if distance is smaller
[335011]236 LOG(2, "ACCEPT: Adding Bond between " << *Walker << " and " << *OtherWalker << " in distance " << sqrt(distance) << ".");
[826e8c]237 // directly use iter to avoid const_cast'ing Walker, too
[88c8ec]238 //const bond::ptr Binder =
[826e8c]239 const_cast<atom *>(Walker)->addBond(CurrentTime, const_cast<atom *>(OtherWalker));
240 ++BondCount;
241 } else {
[335011]242 LOG(3, "REJECT: Squared distance "
[826e8c]243 << distance << " is out of squared covalent bounds "
244 << MinMaxDistanceSquared << ".");
245 }
246 } else {
247 LOG(5, "REJECT: Not Adding: Wrong order.");
248 }
249 }
250 }
251 }
252 LOG(1, "I detected " << BondCount << " bonds in the molecule.");
[3738f0]253
254 // correct bond degree by comparing valence and bond degree
255 LOG(1, "STATUS: Correcting bond degree.");
256 CorrectBondDegree(Set);
257
258 // output bonds for debugging (if bond chain list was correctly installed)
[335011]259 LOG(3, "STATUS: Printing list of created bonds.");
260 if (DoLog(3)) {
261 std::stringstream output;
262 for(const_iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner) {
263 (*AtomRunner)->OutputBondOfAtom(output);
264 output << std::endl << "\t\t";
265 }
266 LOG(3, output.str());
[3738f0]267 }
268 } else {
269 LOG(1, "REJECT: AtomCount is " << counter << ", thus no bonds, no connections.");
270 }
271 }
272
[0cbad2]273 /** Creates an adjacency list of the given \a Set of atoms.
274 *
275 * Note that the input stream is required to refer to the same number of
276 * atoms also contained in \a Set.
277 *
278 * \param &Set container with atoms
279 * \param *input input stream to parse
280 * \param skiplines how many header lines to skip
281 * \param id_offset is base id compared to World startin at 0
282 */
283 template <class container_type,
284 class iterator_type,
285 class const_iterator_type>
286 void CreateAdjacencyListFromDbondFile(
287 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set,
288 ifstream *input,
289 unsigned int skiplines,
290 int id_offset) const
291 {
292 char line[MAXSTRINGSIZE];
293
294 // check input stream
295 if (input->fail()) {
296 ELOG(0, "Opening of bond file failed \n");
297 return;
298 };
299 // skip headers
300 for (unsigned int i=0;i<skiplines;i++)
301 input->getline(line,MAXSTRINGSIZE);
302
303 // create lookup map
304 LOG(1, "STATUS: Creating lookup map.");
305 std::map< unsigned int, atom *> AtomLookup;
306 unsigned int counter = id_offset; // if ids do not start at 0
307 for (iterator_type iter = Set.begin(); iter != Set.end(); ++iter) {
308 AtomLookup.insert( make_pair( counter++, *iter) );
309 }
310 LOG(2, "INFO: There are " << counter << " atoms in the given set.");
311
312 LOG(1, "STATUS: Scanning file.");
313 unsigned int atom1, atom2;
314 unsigned int bondcounter = 0;
315 while (!input->eof()) // Check whether we read everything already
316 {
317 input->getline(line,MAXSTRINGSIZE);
318 stringstream zeile(line);
319 if (zeile.str().empty())
320 continue;
321 zeile >> atom1;
322 zeile >> atom2;
323
324 LOG(4, "INFO: Looking for atoms " << atom1 << " and " << atom2 << ".");
325 if (atom2 < atom1) //Sort indices of atoms in order
326 std::swap(atom1, atom2);
327 ASSERT(atom2 < counter,
328 "BondGraph::CreateAdjacencyListFromDbondFile() - ID "
329 +toString(atom2)+" exceeds number of present atoms "+toString(counter)+".");
330 ASSERT(AtomLookup.count(atom1),
331 "BondGraph::CreateAdjacencyListFromDbondFile() - Could not find an atom with the ID given in dbond file");
332 ASSERT(AtomLookup.count(atom2),
333 "BondGraph::CreateAdjacencyListFromDbondFile() - Could not find an atom with the ID given in dbond file");
334 atom * const Walker = AtomLookup[atom1];
335 atom * const OtherWalker = AtomLookup[atom2];
336
337 LOG(3, "INFO: Creating bond between atoms " << atom1 << " and " << atom2 << ".");
[88c8ec]338 //const bond::ptr Binder =
[db7e6d]339 Walker->addBond(WorldTime::getTime(), OtherWalker);
[0cbad2]340 bondcounter++;
341 }
342 LOG(1, "STATUS: "<< bondcounter << " bonds have been parsed.");
343 }
344
[3738f0]345 /** Removes all bonds within the given set of iterable atoms.
346 *
347 * @param Set Range with atoms
348 */
349 template <class container_type,
350 class iterator_type,
351 class const_iterator_type>
[111f4a]352 void cleanAdjacencyList(
353 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
[3738f0]354 {
355 // remove every bond from the list
356 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner) {
[5e2f80]357 (*AtomRunner)->removeAllBonds();
358// BondList& ListOfBonds = (*AtomRunner)->getListOfBonds();
359// for(BondList::iterator BondRunner = ListOfBonds.begin();
360// !ListOfBonds.empty();
361// BondRunner = ListOfBonds.begin()) {
362// ASSERT((*BondRunner)->Contains(*AtomRunner),
363// "BondGraph::cleanAdjacencyList() - "+
364// toString(*BondRunner)+" does not contain "+
365// toString(*AtomRunner)+".");
366// delete((*BondRunner));
367// }
[3738f0]368 }
369 }
370
371 /** correct bond degree by comparing valence and bond degree.
372 * correct Bond degree of each bond by checking both bond partners for a mismatch between valence and current sum of bond degrees,
373 * iteratively increase the one first where the other bond partner has the fewest number of bonds (i.e. in general bonds oxygene
374 * preferred over carbon bonds). Beforehand, we had picked the first mismatching partner, which lead to oxygenes with single instead of
375 * double bonds as was expected.
376 * @param Set Range with atoms
377 * \return number of bonds that could not be corrected
378 */
379 template <class container_type,
380 class iterator_type,
381 class const_iterator_type>
[111f4a]382 int CorrectBondDegree(
383 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
[3738f0]384 {
385 // reset
386 resetBondDegree(Set);
387 // re-calculate
388 return calculateBondDegree(Set);
389 }
[af2c424]390
[9b6663]391 /** Equality comparator for class BondGraph.
392 *
393 * @param other other instance to compare to
394 * @return true - if equal in every member variable, except static
395 * \a BondGraph::BondThreshold.
396 */
397 bool operator==(const BondGraph &other) const;
398
399 /** Unequality comparator for class BondGraph.
400 *
401 * @param other other instance to compare to
402 * @return false - if equal in every member variable, except static
403 * \a BondGraph::BondThreshold.
404 */
405 bool operator!=(const BondGraph &other) const {
406 return !(*this == other);
407 }
408
[b70721]409private:
[826e8c]410 /** Convenience function to place access to World::getLinkedCell() into source module.
411 *
412 * @return ref to LinkedCell_View
413 */
414 LinkedCell::LinkedCell_View getLinkedCell(const double max_distance) const;
415
416 /** Convenience function to place access to World::getDomain() into source module.
417 *
418 * @return ref to Box
419 */
420 Box &getDomain() const;
421
422 /** Convenience function to place access to WorldTime::getTime() into source module.
423 *
424 * @return current time step
425 */
426 unsigned int getTime() const;
[88b400]427
[300220]428 /** Returns the BondLengthMatrix entry for a given index pair.
429 * \param firstelement index/atom number of first element (row index)
430 * \param secondelement index/atom number of second element (column index)
431 * \note matrix is of course symmetric.
432 */
433 double GetBondLength(
434 int firstelement,
435 int secondelement) const;
436
[e7350d4]437 /** Returns bond criterion for given pair based on a bond length matrix.
[111f4a]438 * This calls either the covalent or the bond matrix criterion.
[e7350d4]439 * \param *Walker first BondedParticle
440 * \param *OtherWalker second BondedParticle
[607eab]441 * \return Range with bond interval
[e7350d4]442 */
[607eab]443 range<double> getMinMaxDistance(
[300220]444 const element * const Walker,
[607eab]445 const element * const OtherWalker) const;
[72d90e]446
[300220]447 /** Returns bond criterion for given pair of elements based on a bond length matrix.
448 * The matrix should be contained in \a this BondGraph and contain an element-
449 * to-element length.
450 * \param *Walker first element
451 * \param *OtherWalker second element
[607eab]452 * \return Range with bond interval
[300220]453 */
[607eab]454 range<double> BondLengthMatrixMinMaxDistance(
[300220]455 const element * const Walker,
[607eab]456 const element * const OtherWalker) const;
[300220]457
458 /** Returns bond criterion for given pair of elements based on covalent radius.
459 * \param *Walker first element
460 * \param *OtherWalker second element
[607eab]461 * \return Range with bond interval
[e7350d4]462 */
[607eab]463 range<double> CovalentMinMaxDistance(
[300220]464 const element * const Walker,
[607eab]465 const element * const OtherWalker) const;
[72d90e]466
[3738f0]467
468 /** Resets the bond::BondDegree of all atoms in the set to 1.
469 *
470 * @param Set Range with atoms
471 */
472 template <class container_type,
473 class iterator_type,
474 class const_iterator_type>
[326000]475 void resetBondDegree (
[111f4a]476 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
[3738f0]477 {
478 // reset bond degrees
479 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner) {
[378fc6b]480 resetBondDegree(*AtomRunner);
[3738f0]481 }
482 }
483
484 /** Calculates the bond degree for each atom on the set.
485 *
486 * @param Set Range with atoms
487 * @return number of non-matching bonds
488 */
489 template <class container_type,
490 class iterator_type,
491 class const_iterator_type>
[111f4a]492 int calculateBondDegree(
493 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
[3738f0]494 {
[4f04ab8]495 std::set<atom *> allatoms;
496 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner)
497 allatoms.insert(*AtomRunner);
498 return calculateBondDegree(allatoms);
[3738f0]499 }
500
[4f04ab8]501 int calculateBondDegree(const std::set<atom *> &allatoms) const;
502
[f007a1]503 bool operator==(const periodentafel &other) const;
504
505 bool operator!=(const periodentafel &other) const {
506 return !(*this == other);
507 }
508
[326000]509 std::set<bond::ptr> getBackEdges() const;
510 std::set<bond::ptr> getTreeEdges() const;
511
512 int CorrectBondDegree(atom *_atom, const std::set<bond::ptr>& skipedges) const;
[378fc6b]513
514 void resetBondDegree(atom *_atom) const;
515
[f007a1]516private:
517 // default constructor for serialization
518 BondGraph();
519
520 friend class boost::serialization::access;
521 // serialization
522 template<class Archive>
523 void serialize(Archive & ar, const unsigned int version)
524 {
525 //ar & const_cast<double &>(BondThreshold);
526 ar & BondLengthMatrix;
527 ar & IsAngstroem;
528 }
529
530 //!> half width of the interval for allowed bond distances
531 static const double BondThreshold;
[e7350d4]532 //!> Matrix with bond lenth per two elements
[b70721]533 MatrixContainer *BondLengthMatrix;
[e7350d4]534 //!> distance units are angstroem (true), bohr radii (false)
[b70721]535 bool IsAngstroem;
536};
537
538#endif /* BONDGRAPH_HPP_ */
Note: See TracBrowser for help on using the repository browser.