source: src/Graph/BondGraph.hpp@ f2d5ce

Candidate_v1.7.1 stable
Last change on this file since f2d5ce was 2e7888, checked in by Frederik Heber <frederik.heber@…>, 6 weeks ago

Adds computed bond table.

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