source: src/Graph/BondGraph.hpp@ 4f04ab8

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 4f04ab8 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
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();
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// }
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>
382 int CorrectBondDegree(
383 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
384 {
385 // reset
386 resetBondDegree(Set);
387 // re-calculate
388 return calculateBondDegree(Set);
389 }
390
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
409private:
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;
427
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
437 /** Returns bond criterion for given pair based on a bond length matrix.
438 * This calls either the covalent or the bond matrix criterion.
439 * \param *Walker first BondedParticle
440 * \param *OtherWalker second BondedParticle
441 * \return Range with bond interval
442 */
443 range<double> getMinMaxDistance(
444 const element * const Walker,
445 const element * const OtherWalker) const;
446
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
452 * \return Range with bond interval
453 */
454 range<double> BondLengthMatrixMinMaxDistance(
455 const element * const Walker,
456 const element * const OtherWalker) const;
457
458 /** Returns bond criterion for given pair of elements based on covalent radius.
459 * \param *Walker first element
460 * \param *OtherWalker second element
461 * \return Range with bond interval
462 */
463 range<double> CovalentMinMaxDistance(
464 const element * const Walker,
465 const element * const OtherWalker) const;
466
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>
475 void resetBondDegree (
476 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
477 {
478 // reset bond degrees
479 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner) {
480 resetBondDegree(*AtomRunner);
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>
492 int calculateBondDegree(
493 AtomSetMixin<container_type,iterator_type,const_iterator_type> &Set) const
494 {
495 std::set<atom *> allatoms;
496 for(iterator_type AtomRunner = Set.begin(); AtomRunner != Set.end(); ++AtomRunner)
497 allatoms.insert(*AtomRunner);
498 return calculateBondDegree(allatoms);
499 }
500
501 int calculateBondDegree(const std::set<atom *> &allatoms) const;
502
503 bool operator==(const periodentafel &other) const;
504
505 bool operator!=(const periodentafel &other) const {
506 return !(*this == other);
507 }
508
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;
513
514 void resetBondDegree(atom *_atom) const;
515
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;
532 //!> Matrix with bond lenth per two elements
533 MatrixContainer *BondLengthMatrix;
534 //!> distance units are angstroem (true), bohr radii (false)
535 bool IsAngstroem;
536};
537
538#endif /* BONDGRAPH_HPP_ */
Note: See TracBrowser for help on using the repository browser.