source: src/Graph/BondGraph.hpp@ e4b9d5

Candidate_v1.7.1 stable
Last change on this file since e4b9d5 was d2be22, checked in by Frederik Heber <frederik.heber@…>, 3 weeks ago

Actions relying on BondGraph fail if not bond table is loaded.

  • this is to ensure to not stumble over missing optimal bond lengths from the table, like with StretchBondAction.
  • TESTFIX: All regression tests that use these actions need to load the bond-table now.
  • Property mode set to 100644
File size: 20.4 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
197 // count atoms in molecule = dimension of matrix (also give each unique name and continuous numbering)
198 const unsigned int counter = Set.size();
199 if (counter > 1) {
200 LOG(1, "STATUS: Setting max bond distance.");
201 LOG(1, "STATUS: Creating LinkedCell structure for given " << counter << " atoms.");
202 LinkedCell::LinkedCell_View LC = getLinkedCell(getMaxPossibleBondDistance(PresentElements));
203
204 LOG(1, "STATUS: Evaluating distance criterion for each atom.");
205
206 const Box &domain = getDomain();
207 const unsigned int CurrentTime = getTime();
208
209 unsigned int BondCount = 0;
210 // go through every atom in the set (observed cause we change its bonds)
211 for(typename AtomSetMixin<container_type,iterator_type,const_iterator_type>::iterator iter = Set.begin();
212 iter != Set.end(); ++iter) {
213 const atom * const Walker = dynamic_cast<const atom *>(*iter);
214 ASSERT(Walker != NULL,
215 "BondGraph::CreateAdjacency() - TesselPoint "
216 +(*iter)->getName()+" that was not an atom retrieved from given set");
217 LOG(2, "INFO: Current Atom is " << *Walker << ".");
218
219 // obtain all possible neighbors
220 LinkedCell::LinkedList ListOfNeighbors = LC.getAllNeighbors(
221 getMaxPossibleBondDistance(Walker->getType(), PresentElements),
222 Walker->getPosition());
223 if (!ListOfNeighbors.empty()) {
224 // we have some possible candidates, go through each
225 for (LinkedCell::LinkedList::const_iterator neighboriter = ListOfNeighbors.begin();
226 neighboriter != ListOfNeighbors.end();
227 ++neighboriter) {
228 const atom * const OtherWalker = dynamic_cast<const atom *>(*neighboriter);
229 ASSERT(OtherWalker != NULL,
230 "BondGraph::CreateAdjacency() - TesselPoint "
231 +(*neighboriter)->getName()+" that was not an atom retrieved from LinkedList");
232 if (OtherWalker->getId() > Walker->getId()) { // just to not add bonds from both sides
233 LOG(4, "INFO: Current other atom is " << *OtherWalker << ".");
234
235 const range<double> MinMaxDistanceSquared(
236 getMinMaxDistanceSquared(Walker, OtherWalker));
237 const double distance = domain.periodicDistanceSquared(OtherWalker->getPosition(),Walker->getPosition());
238 LOG(3, "INFO: Checking squared distance " << distance << " against typical bond length of " << MinMaxDistanceSquared << ".");
239 const bool status = MinMaxDistanceSquared.isInRange(distance);
240 if (status) { // create bond if distance is smaller
241 LOG(2, "ACCEPT: Adding Bond between " << *Walker << " and " << *OtherWalker << " in distance " << sqrt(distance) << ".");
242 // directly use iter to avoid const_cast'ing Walker, too
243 //const bond::ptr Binder =
244 const_cast<atom *>(Walker)->addBond(CurrentTime, const_cast<atom *>(OtherWalker));
245 ++BondCount;
246 } else {
247 LOG(3, "REJECT: Squared distance "
248 << distance << " is out of squared covalent bounds "
249 << MinMaxDistanceSquared << ".");
250 }
251 } else {
252 LOG(4, "REJECT: Not Adding: Wrong order.");
253 }
254 }
255 }
256 }
257 LOG(1, "I detected " << BondCount << " bonds in the molecule.");
258
259 // correct bond degree by comparing valence and bond degree
260 LOG(1, "STATUS: Correcting bond degree.");
261 CorrectBondDegree(Set);
262
263 // output bonds for debugging (if bond chain list was correctly installed)
264 LOG(3, "STATUS: Printing list of created bonds.");
265 if (DoLog(3)) {
266 std::stringstream output;
267 for(const_iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner) {
268 (*AtomRunner)->OutputBondOfAtom(output);
269 output << std::endl << "\t\t";
270 }
271 LOG(3, output.str());
272 }
273 } else {
274 LOG(1, "REJECT: AtomCount is " << counter << ", thus no bonds, no connections.");
275 }
276 }
277
278 /** Creates an adjacency list of the given \a Set of atoms.
279 *
280 * Note that the input stream is required to refer to the same number of
281 * atoms also contained in \a Set.
282 *
283 * \param &Set container with atoms
284 * \param *input input stream to parse
285 * \param skiplines how many header lines to skip
286 * \param id_offset is base id compared to World startin at 0
287 */
288 template <class container_type,
289 class iterator_type,
290 class const_iterator_type>
291 void CreateAdjacencyListFromDbondFile(
292 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set,
293 ifstream *input,
294 unsigned int skiplines,
295 int id_offset) const
296 {
297 char line[MAXSTRINGSIZE];
298
299 // check input stream
300 if (input->fail()) {
301 ELOG(0, "Opening of bond file failed \n");
302 return;
303 };
304 // skip headers
305 for (unsigned int i=0;i<skiplines;i++)
306 input->getline(line,MAXSTRINGSIZE);
307
308 // create lookup map
309 LOG(1, "STATUS: Creating lookup map.");
310 std::map< unsigned int, atom *> AtomLookup;
311 unsigned int counter = id_offset; // if ids do not start at 0
312 for (iterator_type iter = Set.begin(); iter != Set.end(); ++iter) {
313 AtomLookup.insert( make_pair( counter++, *iter) );
314 }
315 LOG(2, "INFO: There are " << counter << " atoms in the given set.");
316
317 LOG(1, "STATUS: Scanning file.");
318 unsigned int atom1, atom2;
319 unsigned int bondcounter = 0;
320 while (!input->eof()) // Check whether we read everything already
321 {
322 input->getline(line,MAXSTRINGSIZE);
323 stringstream zeile(line);
324 if (zeile.str().empty())
325 continue;
326 zeile >> atom1;
327 zeile >> atom2;
328
329 LOG(4, "INFO: Looking for atoms " << atom1 << " and " << atom2 << ".");
330 if (atom2 < atom1) //Sort indices of atoms in order
331 std::swap(atom1, atom2);
332 ASSERT(atom2 < counter,
333 "BondGraph::CreateAdjacencyListFromDbondFile() - ID "
334 +toString(atom2)+" exceeds number of present atoms "+toString(counter)+".");
335 ASSERT(AtomLookup.count(atom1),
336 "BondGraph::CreateAdjacencyListFromDbondFile() - Could not find an atom with the ID given in dbond file");
337 ASSERT(AtomLookup.count(atom2),
338 "BondGraph::CreateAdjacencyListFromDbondFile() - Could not find an atom with the ID given in dbond file");
339 atom * const Walker = AtomLookup[atom1];
340 atom * const OtherWalker = AtomLookup[atom2];
341
342 LOG(3, "INFO: Creating bond between atoms " << atom1 << " and " << atom2 << ".");
343 //const bond::ptr Binder =
344 Walker->addBond(WorldTime::getTime(), OtherWalker);
345 bondcounter++;
346 }
347 LOG(1, "STATUS: "<< bondcounter << " bonds have been parsed.");
348 }
349
350 /** Removes all bonds within the given set of iterable atoms.
351 *
352 * @param Set Range with atoms
353 */
354 template <class container_type,
355 class iterator_type,
356 class const_iterator_type>
357 void cleanAdjacencyList(
358 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
359 {
360 // remove every bond from the list
361 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner) {
362 (*AtomRunner)->removeAllBonds(WorldTime::getTime());
363 }
364 }
365
366 /** Checks whether the bond degree for each atom on the set matches with its valency.
367 *
368 * @param Set Range with atoms
369 * @return true - bond degrees match valency, false - bond degree correction is needed
370 */
371 template <class container_type,
372 class iterator_type,
373 class const_iterator_type>
374 bool checkBondDegree(
375 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
376 {
377 std::set<atom *> allatoms;
378 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner)
379 allatoms.insert(*AtomRunner);
380 return checkBondDegree(allatoms);
381 }
382
383 /** correct bond degree by comparing valence and bond degree.
384 * correct Bond degree of each bond by checking both bond partners for a mismatch between valence and current sum of bond degrees,
385 * iteratively increase the one first where the other bond partner has the fewest number of bonds (i.e. in general bonds oxygene
386 * preferred over carbon bonds). Beforehand, we had picked the first mismatching partner, which lead to oxygenes with single instead of
387 * double bonds as was expected.
388 * @param Set Range with atoms
389 * \return number of bonds that could not be corrected
390 */
391 template <class container_type,
392 class iterator_type,
393 class const_iterator_type>
394 int CorrectBondDegree(
395 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
396 {
397 // reset
398 resetBondDegree(Set);
399 // re-calculate
400 return calculateBondDegree(Set);
401 }
402
403 /** Equality comparator for class BondGraph.
404 *
405 * @param other other instance to compare to
406 * @return true - if equal in every member variable, except static
407 * \a BondGraph::BondThreshold.
408 */
409 bool operator==(const BondGraph &other) const;
410
411 /** Unequality comparator for class BondGraph.
412 *
413 * @param other other instance to compare to
414 * @return false - if equal in every member variable, except static
415 * \a BondGraph::BondThreshold.
416 */
417 bool operator!=(const BondGraph &other) const {
418 return !(*this == other);
419 }
420
421 /** Returns the BondLengthMatrix entry for a given index pair.
422 *
423 * \note This will return a bond length of 1. if we cannot find an entry.
424 *
425 * \param firstelement index/atom number of first element (row index)
426 * \param secondelement index/atom number of second element (column index)
427 * \note matrix is of course symmetric.
428 */
429 double GetBondLength(
430 int firstelement,
431 int secondelement) const;
432
433private:
434 /** Convenience function to place access to World::getLinkedCell() into source module.
435 *
436 * @return ref to LinkedCell_View
437 */
438 LinkedCell::LinkedCell_View getLinkedCell(const double max_distance) const;
439
440 /** Convenience function to place access to World::getDomain() into source module.
441 *
442 * @return ref to Box
443 */
444 Box &getDomain() const;
445
446 /** Convenience function to place access to WorldTime::getTime() into source module.
447 *
448 * @return current time step
449 */
450 unsigned int getTime() const;
451
452 /** Returns bond criterion for given pair based on a bond length matrix.
453 * This calls either the covalent or the bond matrix criterion.
454 * \param *Walker first BondedParticle
455 * \param *OtherWalker second BondedParticle
456 * \return Range with bond interval
457 */
458 range<double> getMinMaxDistance(
459 const element * const Walker,
460 const element * const OtherWalker) const;
461
462 /** Returns bond criterion for given pair of elements based on a bond length matrix.
463 * The matrix should be contained in \a this BondGraph and contain an element-
464 * to-element length.
465 * \param *Walker first element
466 * \param *OtherWalker second element
467 * \return Range with bond interval
468 */
469 range<double> BondLengthMatrixMinMaxDistance(
470 const element * const Walker,
471 const element * const OtherWalker) const;
472
473 /** Returns bond criterion for given pair of elements based on covalent radius.
474 * \param *Walker first element
475 * \param *OtherWalker second element
476 * \return Range with bond interval
477 */
478 range<double> CovalentMinMaxDistance(
479 const element * const Walker,
480 const element * const OtherWalker) const;
481
482
483 /** Resets the bond::BondDegree of all atoms in the set to 1.
484 *
485 * @param Set Range with atoms
486 */
487 template <class container_type,
488 class iterator_type,
489 class const_iterator_type>
490 void resetBondDegree (
491 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
492 {
493 // reset bond degrees
494 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner) {
495 resetBondDegree(*AtomRunner);
496 }
497 }
498
499 bool checkBondDegree(const std::set<atom *> &allatoms) const;
500
501 /** Calculates the bond degree for each atom on the set.
502 *
503 * @param Set Range with atoms
504 * @return number of non-matching bonds
505 */
506 template <class container_type,
507 class iterator_type,
508 class const_iterator_type>
509 int calculateBondDegree(
510 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
511 {
512 std::set<atom *> allatoms;
513 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner)
514 allatoms.insert(*AtomRunner);
515 return calculateBondDegree(allatoms);
516 }
517
518 int calculateBondDegree(const std::set<atom *> &allatoms) const;
519
520 bool operator==(const periodentafel &other) const;
521
522 bool operator!=(const periodentafel &other) const {
523 return !(*this == other);
524 }
525
526 std::set<bond::ptr> getBackEdges() const;
527 std::set<bond::ptr> getTreeEdges() const;
528
529 int CorrectBondDegree(atom *_atom, const std::set<bond::ptr>& skipedges) const;
530
531 void resetBondDegree(atom *_atom) const;
532
533private:
534 // default constructor for serialization
535 BondGraph();
536
537 friend class boost::serialization::access;
538 // serialization
539 template<class Archive>
540 void serialize(Archive & ar, const unsigned int version)
541 {
542 //ar & const_cast<double &>(BondThreshold);
543 ar & BondLengthMatrix;
544 ar & IsAngstroem;
545 }
546
547 //!> half width of the interval for allowed bond distances
548 static const double BondThreshold;
549 //!> Matrix with bond lenth per two elements
550 MatrixContainer *BondLengthMatrix;
551 //!> distance units are angstroem (true), bohr radii (false)
552 bool IsAngstroem;
553};
554
555#endif /* BONDGRAPH_HPP_ */
Note: See TracBrowser for help on using the repository browser.