source: src/Graph/BondGraph.hpp@ 99db9b

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 99db9b was 0763ce, checked in by Frederik Heber <heber@…>, 10 years ago

Added BondGraph::checkBondDegree, FragmentationAction only resets degrees when incorrect.

  • this fixes the bug where the molecular dynamics actions would flip the double bonds in an aromatic ring during the simulation steps because the bond degrees are reset even though the bond graph is present and should be re-used.
  • 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 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");
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(5, "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.