source: src/Graph/BondGraph.hpp

Candidate_v1.6.1
Last change on this file was e3e52a, checked in by Frederik Heber <heber@…>, 9 years ago

FIX: BondGraph::Createdjacency compare atoms on position in memory.

  • this caused every and again failures in tests as an atom with an earlier id might end up at a "later" memory position resulting in flipped positions in e.g. PDB CONECT entries (regression tests molecules remove failed).
  • Property mode set to 100644
File size: 20.1 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 /** Removes allocated bond length table.
74 *
75 */
76 void CleanupBondLengthTable();
77
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
85 /** Determines the maximum of all element::CovalentRadius for elements present in \a &Set.
86 *
87 * I.e. the function returns a sensible cutoff criteria for bond recognition,
88 * e.g. to be used for LinkedCell_deprecated or others.
89 *
90 * \param &PresentElements set of elements whose maximal pair to find
91 */
92 double getMaxPossibleBondDistance(
93 const std::set< const element *> &PresentElements) const
94 {
95 double max_distance = 0.;
96
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) {
104 const range<double> MinMaxDistance(getMinMaxDistance((*iter),(*otheriter)));
105 if (MinMaxDistance.last > max_distance)
106 max_distance = MinMaxDistance.last;
107 }
108 }
109 return max_distance;
110 }
111
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 *
117 * \param Walker element first element in the pair
118 * \param &PresentElements set of elements whose maximal pair to find
119 */
120 double getMaxPossibleBondDistance(
121 const element * const Walker,
122 const std::set< const element *> &PresentElements) const
123 {
124 double max_distance = 0.;
125
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
137 /** Returns bond criterion for given pair based on a bond length matrix.
138 * This calls element-version of getMinMaxDistance().
139 * \param *Walker first BondedParticle
140 * \param *OtherWalker second BondedParticle
141 * \return Range with bond interval
142 */
143 range<double> getMinMaxDistance(
144 const BondedParticle * const Walker,
145 const BondedParticle * const OtherWalker) const;
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
152 * \return Range with bond interval
153 */
154 range<double> getMinMaxDistanceSquared(
155 const BondedParticle * const Walker,
156 const BondedParticle * const OtherWalker) const;
157
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
169 */
170 template <class container_type,
171 class iterator_type,
172 class const_iterator_type>
173 void CreateAdjacency(
174 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
175 {
176 LOG(1, "STATUS: Removing all present bonds.");
177 cleanAdjacencyList(Set);
178
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
190 const std::set< const element *> PresentElements = getElementSetFromNumbers(elements);
191
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.");
196 LOG(1, "STATUS: Creating LinkedCell structure for given " << counter << " atoms.");
197 LinkedCell::LinkedCell_View LC = getLinkedCell(getMaxPossibleBondDistance(PresentElements));
198
199 LOG(1, "STATUS: Evaluating distance criterion for each atom.");
200
201 const Box &domain = getDomain();
202 const unsigned int CurrentTime = getTime();
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(
216 getMaxPossibleBondDistance(Walker->getType(), PresentElements),
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 const atom * const OtherWalker = dynamic_cast<const atom *>(*neighboriter);
224 ASSERT(OtherWalker != NULL,
225 "BondGraph::CreateAdjacency() - TesselPoint "
226 +(*neighboriter)->getName()+" that was not an atom retrieved from LinkedList");
227 if (OtherWalker->getId() > Walker->getId()) { // just to not add bonds from both sides
228 LOG(4, "INFO: Current other atom is " << *OtherWalker << ".");
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
236 LOG(2, "ACCEPT: Adding Bond between " << *Walker << " and " << *OtherWalker << " in distance " << sqrt(distance) << ".");
237 // directly use iter to avoid const_cast'ing Walker, too
238 //const bond::ptr Binder =
239 const_cast<atom *>(Walker)->addBond(CurrentTime, const_cast<atom *>(OtherWalker));
240 ++BondCount;
241 } else {
242 LOG(3, "REJECT: Squared distance "
243 << distance << " is out of squared covalent bounds "
244 << MinMaxDistanceSquared << ".");
245 }
246 } else {
247 LOG(4, "REJECT: Not Adding: Wrong order.");
248 }
249 }
250 }
251 }
252 LOG(1, "I detected " << BondCount << " bonds in the molecule.");
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)
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());
267 }
268 } else {
269 LOG(1, "REJECT: AtomCount is " << counter << ", thus no bonds, no connections.");
270 }
271 }
272
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 << ".");
338 //const bond::ptr Binder =
339 Walker->addBond(WorldTime::getTime(), OtherWalker);
340 bondcounter++;
341 }
342 LOG(1, "STATUS: "<< bondcounter << " bonds have been parsed.");
343 }
344
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>
352 void cleanAdjacencyList(
353 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
354 {
355 // remove every bond from the list
356 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner) {
357 (*AtomRunner)->removeAllBonds(WorldTime::getTime());
358 }
359 }
360
361 /** Checks whether the bond degree for each atom on the set matches with its valency.
362 *
363 * @param Set Range with atoms
364 * @return true - bond degrees match valency, false - bond degree correction is needed
365 */
366 template <class container_type,
367 class iterator_type,
368 class const_iterator_type>
369 bool checkBondDegree(
370 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
371 {
372 std::set<atom *> allatoms;
373 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner)
374 allatoms.insert(*AtomRunner);
375 return checkBondDegree(allatoms);
376 }
377
378 /** correct bond degree by comparing valence and bond degree.
379 * correct Bond degree of each bond by checking both bond partners for a mismatch between valence and current sum of bond degrees,
380 * iteratively increase the one first where the other bond partner has the fewest number of bonds (i.e. in general bonds oxygene
381 * preferred over carbon bonds). Beforehand, we had picked the first mismatching partner, which lead to oxygenes with single instead of
382 * double bonds as was expected.
383 * @param Set Range with atoms
384 * \return number of bonds that could not be corrected
385 */
386 template <class container_type,
387 class iterator_type,
388 class const_iterator_type>
389 int CorrectBondDegree(
390 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
391 {
392 // reset
393 resetBondDegree(Set);
394 // re-calculate
395 return calculateBondDegree(Set);
396 }
397
398 /** Equality comparator for class BondGraph.
399 *
400 * @param other other instance to compare to
401 * @return true - if equal in every member variable, except static
402 * \a BondGraph::BondThreshold.
403 */
404 bool operator==(const BondGraph &other) const;
405
406 /** Unequality comparator for class BondGraph.
407 *
408 * @param other other instance to compare to
409 * @return false - if equal in every member variable, except static
410 * \a BondGraph::BondThreshold.
411 */
412 bool operator!=(const BondGraph &other) const {
413 return !(*this == other);
414 }
415
416private:
417 /** Convenience function to place access to World::getLinkedCell() into source module.
418 *
419 * @return ref to LinkedCell_View
420 */
421 LinkedCell::LinkedCell_View getLinkedCell(const double max_distance) const;
422
423 /** Convenience function to place access to World::getDomain() into source module.
424 *
425 * @return ref to Box
426 */
427 Box &getDomain() const;
428
429 /** Convenience function to place access to WorldTime::getTime() into source module.
430 *
431 * @return current time step
432 */
433 unsigned int getTime() const;
434
435 /** Returns the BondLengthMatrix entry for a given index pair.
436 * \param firstelement index/atom number of first element (row index)
437 * \param secondelement index/atom number of second element (column index)
438 * \note matrix is of course symmetric.
439 */
440 double GetBondLength(
441 int firstelement,
442 int secondelement) const;
443
444 /** Returns bond criterion for given pair based on a bond length matrix.
445 * This calls either the covalent or the bond matrix criterion.
446 * \param *Walker first BondedParticle
447 * \param *OtherWalker second BondedParticle
448 * \return Range with bond interval
449 */
450 range<double> getMinMaxDistance(
451 const element * const Walker,
452 const element * const OtherWalker) const;
453
454 /** Returns bond criterion for given pair of elements based on a bond length matrix.
455 * The matrix should be contained in \a this BondGraph and contain an element-
456 * to-element length.
457 * \param *Walker first element
458 * \param *OtherWalker second element
459 * \return Range with bond interval
460 */
461 range<double> BondLengthMatrixMinMaxDistance(
462 const element * const Walker,
463 const element * const OtherWalker) const;
464
465 /** Returns bond criterion for given pair of elements based on covalent radius.
466 * \param *Walker first element
467 * \param *OtherWalker second element
468 * \return Range with bond interval
469 */
470 range<double> CovalentMinMaxDistance(
471 const element * const Walker,
472 const element * const OtherWalker) const;
473
474
475 /** Resets the bond::BondDegree of all atoms in the set to 1.
476 *
477 * @param Set Range with atoms
478 */
479 template <class container_type,
480 class iterator_type,
481 class const_iterator_type>
482 void resetBondDegree (
483 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
484 {
485 // reset bond degrees
486 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner) {
487 resetBondDegree(*AtomRunner);
488 }
489 }
490
491 bool checkBondDegree(const std::set<atom *> &allatoms) const;
492
493 /** Calculates the bond degree for each atom on the set.
494 *
495 * @param Set Range with atoms
496 * @return number of non-matching bonds
497 */
498 template <class container_type,
499 class iterator_type,
500 class const_iterator_type>
501 int calculateBondDegree(
502 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
503 {
504 std::set<atom *> allatoms;
505 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner)
506 allatoms.insert(*AtomRunner);
507 return calculateBondDegree(allatoms);
508 }
509
510 int calculateBondDegree(const std::set<atom *> &allatoms) const;
511
512 bool operator==(const periodentafel &other) const;
513
514 bool operator!=(const periodentafel &other) const {
515 return !(*this == other);
516 }
517
518 std::set<bond::ptr> getBackEdges() const;
519 std::set<bond::ptr> getTreeEdges() const;
520
521 int CorrectBondDegree(atom *_atom, const std::set<bond::ptr>& skipedges) const;
522
523 void resetBondDegree(atom *_atom) const;
524
525private:
526 // default constructor for serialization
527 BondGraph();
528
529 friend class boost::serialization::access;
530 // serialization
531 template<class Archive>
532 void serialize(Archive & ar, const unsigned int version)
533 {
534 //ar & const_cast<double &>(BondThreshold);
535 ar & BondLengthMatrix;
536 ar & IsAngstroem;
537 }
538
539 //!> half width of the interval for allowed bond distances
540 static const double BondThreshold;
541 //!> Matrix with bond lenth per two elements
542 MatrixContainer *BondLengthMatrix;
543 //!> distance units are angstroem (true), bohr radii (false)
544 bool IsAngstroem;
545};
546
547#endif /* BONDGRAPH_HPP_ */
Note: See TracBrowser for help on using the repository browser.