source: src/Fragmentation/Exporters/SaturatedFragment.cpp@ 0710bf

Last change on this file since 0710bf was e139180, checked in by Frederik Heber <heber@…>, 11 years ago

Fixes to SaturatedFragment::saturateFragment().

  • properly setting up the number of points and the "old" polygon.
  • properly filling in the hydrogen atoms at the calculated places.
  • We have the number of remaining bonds plus the rest. The rest is the valence minus the number of remaining bonds each weighted with its degree. This gives the right number of places to put hydrogens and fill up the valence.
  • TESTS: Removed XFAIL from FragmentMolecule cycles regression test.
  • Property mode set to 100644
File size: 24.6 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2013 University of Bonn. All rights reserved.
5 * Copyright (C) 2013 Frederik Heber. All rights reserved.
6 *
7 *
8 * This file is part of MoleCuilder.
9 *
10 * MoleCuilder is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * MoleCuilder is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24/*
25 * SaturatedFragment.cpp
26 *
27 * Created on: Mar 3, 2013
28 * Author: heber
29 */
30
31// include config.h
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35
36#include "CodePatterns/MemDebug.hpp"
37
38#include "SaturatedFragment.hpp"
39
40#include <algorithm>
41#include <cmath>
42#include <iostream>
43
44#include "CodePatterns/Assert.hpp"
45#include "CodePatterns/Log.hpp"
46
47#include "LinearAlgebra/Exceptions.hpp"
48#include "LinearAlgebra/Plane.hpp"
49#include "LinearAlgebra/RealSpaceMatrix.hpp"
50#include "LinearAlgebra/Vector.hpp"
51
52#include "Atom/atom.hpp"
53#include "Bond/bond.hpp"
54#include "config.hpp"
55#include "Descriptors/AtomIdDescriptor.hpp"
56#include "Fragmentation/Exporters/HydrogenPool.hpp"
57#include "Fragmentation/Exporters/SphericalPointDistribution.hpp"
58#include "Fragmentation/HydrogenSaturation_enum.hpp"
59#include "Graph/BondGraph.hpp"
60#include "World.hpp"
61
62SaturatedFragment::SaturatedFragment(
63 const KeySet &_set,
64 KeySetsInUse_t &_container,
65 HydrogenPool &_hydrogens,
66 const enum HydrogenTreatment _treatment,
67 const enum HydrogenSaturation _saturation) :
68 container(_container),
69 set(_set),
70 hydrogens(_hydrogens),
71 FullMolecule(set),
72 treatment(_treatment),
73 saturation(_saturation)
74{
75 // add to in-use container
76 ASSERT( container.find(set) == container.end(),
77 "SaturatedFragment::SaturatedFragment() - the set "
78 +toString(set)+" is already marked as in use.");
79 container.insert(set);
80
81 // prepare saturation hydrogens
82 saturate();
83}
84
85SaturatedFragment::~SaturatedFragment()
86{
87 // release all saturation hydrogens if present
88 for (KeySet::iterator iter = SaturationHydrogens.begin();
89 !SaturationHydrogens.empty();
90 iter = SaturationHydrogens.begin()) {
91 hydrogens.releaseHydrogen(*iter);
92 SaturationHydrogens.erase(iter);
93 }
94
95 // remove ourselves from in-use container
96 KeySetsInUse_t::iterator iter = container.find(set);
97 ASSERT( container.find(set) != container.end(),
98 "SaturatedFragment::SaturatedFragment() - the set "
99 +toString(set)+" is not marked as in use.");
100 container.erase(iter);
101}
102
103void SaturatedFragment::saturate()
104{
105 // so far, we just have a set of keys. Hence, convert to atom refs
106 // and gather all atoms in a vector
107 std::vector<atom *> atoms;
108 for (KeySet::const_iterator iter = FullMolecule.begin();
109 iter != FullMolecule.end();
110 ++iter) {
111 atom * const Walker = World::getInstance().getAtom(AtomById(*iter));
112 ASSERT( Walker != NULL,
113 "SaturatedFragment::OutputConfig() - id "
114 +toString(*iter)+" is unknown to World.");
115 atoms.push_back(Walker);
116 }
117 LOG(4, "DEBUG: We have gathered the following atoms: " << atoms);
118
119// bool LonelyFlag = false;
120 // go through each atom of the fragment and gather all cut bonds in list
121 typedef std::map<atom *, BondList > CutBonds_t;
122 CutBonds_t CutBonds;
123 for (std::vector<atom *>::const_iterator iter = atoms.begin();
124 iter != atoms.end();
125 ++iter) {
126 atom * const Walker = *iter;
127 // start with an empty list
128 CutBonds.insert( std::make_pair(Walker, BondList() ));
129
130 // go through all bonds
131 const BondList& ListOfBonds = Walker->getListOfBonds();
132 for (BondList::const_iterator BondRunner = ListOfBonds.begin();
133 BondRunner != ListOfBonds.end();
134 ++BondRunner) {
135 atom * const OtherWalker = (*BondRunner)->GetOtherAtom(Walker);
136 // if other atom is in key set
137 if (set.find(OtherWalker->getId()) != set.end()) {
138 LOG(4, "DEBUG: Walker " << *Walker << " is bound to " << *OtherWalker << ".");
139// if (OtherWalker->getId() > Walker->getId()) { // add bond (Nr check is for adding only one of both variants: ab, ba)
140//// std::stringstream output;
141//// output << "ACCEPT: Adding Bond: "
142// output << Leaf->AddBond((*iter), OtherWalker, (*BondRunner)->getDegree());
143//// LOG(3, output.str());
144// //NumBonds[(*iter)->getNr()]++;
145// } else {
146//// LOG(3, "REJECY: Not adding bond, labels in wrong order.");
147// }
148// LonelyFlag = false;
149 } else {
150 LOG(4, "DEBUG: Walker " << *Walker << " is bound to "
151 << *OtherWalker << ", who is not in this fragment molecule.");
152 if ((treatment == ExcludeHydrogen) && (OtherWalker->getElementNo() == (atomicNumber_t)1)) {
153 LOG(4, "REJECT: " << *OtherWalker << " is a hydrogen, that are excluded from the set.");
154 FullMolecule.insert(OtherWalker->getId());
155 } else {
156 LOG(3, "ACCEPT: Adding " << **BondRunner << " as a cut bond.");
157 // there is always at least an empty list
158 CutBonds[Walker].push_back(*BondRunner);
159 }
160 }
161 }
162 }
163 LOG(4, "DEBUG: We have gathered the following CutBonds: " << CutBonds);
164
165 // go through all cut bonds and replace with a hydrogen
166 if (saturation == DoSaturate) {
167 for (CutBonds_t::const_iterator atomiter = CutBonds.begin();
168 atomiter != CutBonds.end(); ++atomiter) {
169 atom * const Walker = atomiter->first;
170 LOG(4, "DEBUG: We are now saturating " << *Walker);
171
172 if (!saturateAtom(Walker, atomiter->second))
173 exit(1);
174 }
175 } else
176 LOG(3, "INFO: We are not saturating cut bonds.");
177}
178
179bool SaturatedFragment::saturateAtom(
180 atom * const _atom,
181 const BondList &_cutbonds)
182{
183 // OLD WAY: use AddHydrogenReplacementAtom() on each cut bond
184// // go through each bond and replace
185// for (BondList::const_iterator bonditer = _cutbonds.begin();
186// bonditer != _cutbonds.end(); ++bonditer) {
187// atom * const OtherWalker = (*bonditer)->GetOtherAtom(_atom);
188// if (!AddHydrogenReplacementAtom(
189// (*bonditer),
190// _atom,
191// OtherWalker,
192// World::getInstance().getConfig()->IsAngstroem == 1))
193// return false;
194// }
195
196 SphericalPointDistribution::Polygon_t Polygon;
197 {
198 // prepare a list of "uncut" bonds via set_difference. For this both lists
199 // have to be sorted.
200 typedef std::vector<bond::ptr> BondVector_t;
201 BondVector_t ListOfBonds(_atom->getListOfBonds().begin(),_atom->getListOfBonds().end());
202 std::sort(ListOfBonds.begin(), ListOfBonds.end());
203 BondVector_t CutBonds(_cutbonds.begin(), _cutbonds.end());
204 std::sort(CutBonds.begin(), CutBonds.end());
205 const BondVector_t::iterator eraseiter = std::set_difference(
206 ListOfBonds.begin(), ListOfBonds.end(),
207 CutBonds.begin(), CutBonds.end(),
208 ListOfBonds.begin());
209 ListOfBonds.erase(eraseiter, ListOfBonds.end());
210
211 // gather the nodes of the shape defined by the current set of bonded atoms
212 for (BondVector_t::const_iterator bonditer = ListOfBonds.begin();
213 bonditer != ListOfBonds.end();
214 ++bonditer) {
215 Vector DistanceVector;
216 if ((*bonditer)->leftatom == _atom)
217 DistanceVector = (*bonditer)->rightatom->getPosition() - (*bonditer)->leftatom->getPosition();
218 else
219 DistanceVector = (*bonditer)->leftatom->getPosition() - (*bonditer)->rightatom->getPosition();
220 // always use unit distances
221 DistanceVector.Normalize();
222 Polygon.push_back( DistanceVector );
223 }
224 LOG(3, "DEBUG: Polygon of atom " << *_atom << " to saturate is " << Polygon);
225 }
226
227 unsigned int NumberOfPoints = 0;
228 {
229 // get the new number of bonds
230 const BondList & ListOfBonds = _atom->getListOfBonds();
231 NumberOfPoints = ListOfBonds.size() - _cutbonds.size(); // number remaining bonds
232 NumberOfPoints += _atom->getElement().getNoValenceOrbitals(); // plus valence
233 // minus remaining bonds weighted by its degree
234 for (BondList::const_iterator BondRunner = ListOfBonds.begin();
235 BondRunner != ListOfBonds.end();
236 ++BondRunner) {
237 // if not one of the cut bonds, reduce by its bond degree -1 (for the one bond itself)
238 const BondList::const_iterator finditer =
239 std::find(_cutbonds.begin(), _cutbonds.end(), *BondRunner);
240 if (finditer == _cutbonds.end())
241 NumberOfPoints -= (*BondRunner)->getDegree();
242 }
243 LOG(3, "DEBUG: There are " << NumberOfPoints
244 << " places to fill in in total for this atom " << *_atom << ".");
245 }
246
247 // get perfect node distribution for the given remaining atoms with respect
248 // to valence of the atoms (for a saturated fragment, resembles number of bonds)
249 SphericalPointDistribution polygonizer;
250 SphericalPointDistribution::Polygon_t NewPolygon;
251 switch (NumberOfPoints)
252 {
253 case 0:
254 NewPolygon = polygonizer.get<0>();
255 break;
256 case 1:
257 NewPolygon = polygonizer.get<1>();
258 break;
259 case 2:
260 NewPolygon = polygonizer.get<2>();
261 break;
262 case 3:
263 NewPolygon = polygonizer.get<3>();
264 break;
265 case 4:
266 NewPolygon = polygonizer.get<4>();
267 break;
268 case 5:
269 NewPolygon = polygonizer.get<5>();
270 break;
271 case 6:
272 NewPolygon = polygonizer.get<6>();
273 break;
274 case 7:
275 NewPolygon = polygonizer.get<7>();
276 break;
277 case 8:
278 NewPolygon = polygonizer.get<8>();
279 break;
280 case 9:
281 NewPolygon = polygonizer.get<9>();
282 break;
283 case 10:
284 NewPolygon = polygonizer.get<10>();
285 break;
286 case 11:
287 NewPolygon = polygonizer.get<11>();
288 break;
289 case 12:
290 NewPolygon = polygonizer.get<12>();
291 break;
292 case 14:
293 NewPolygon = polygonizer.get<14>();
294 break;
295 default:
296 ASSERT(0, "SaturatedFragment::saturateAtom() - cannot deal with the case "
297 +toString(NumberOfPoints)+".");
298 }
299 LOG(3, "DEBUG: Possible Polygon is " << NewPolygon);
300
301 // then we need to match the old with the new
302 SphericalPointDistribution::Polygon_t RemainingPoints =
303 SphericalPointDistribution::matchSphericalPointDistributions(Polygon, NewPolygon);
304
305 LOG(3, "INFO: Points identified to fill are " << RemainingPoints);
306
307 // and place hydrogen atoms at each vacant spot in the distance given by the table
308 for(SphericalPointDistribution::Polygon_t::const_iterator iter = RemainingPoints.begin();
309 iter != RemainingPoints.end(); ++iter) {
310 // find nearest atom as father to this point
311 atom * const _father = _atom;
312 LOG(4, "DEBUG: Filling saturation hydrogen for atom " << _atom << " at " << *iter);
313 const atom& hydrogen = setHydrogenReplacement(
314 _atom,
315 *iter,
316 1.,
317 _father);
318 FullMolecule.insert(hydrogen.getId());
319 }
320
321 return true;
322}
323
324
325bool SaturatedFragment::OutputConfig(
326 std::ostream &out,
327 const ParserTypes _type) const
328{
329 // gather all atoms in a vector
330 std::vector<atom *> atoms;
331 for (KeySet::const_iterator iter = FullMolecule.begin();
332 iter != FullMolecule.end();
333 ++iter) {
334 atom * const Walker = World::getInstance().getAtom(AtomById(*iter));
335 ASSERT( Walker != NULL,
336 "SaturatedFragment::OutputConfig() - id "
337 +toString(*iter)+" is unknown to World.");
338 atoms.push_back(Walker);
339 }
340
341 // TODO: ScanForPeriodicCorrection() is missing so far!
342 // note however that this is not straight-forward for the following reasons:
343 // - we do not copy all atoms anymore, hence we are forced to shift the real
344 // atoms hither and back again
345 // - we use a long-range potential that supports periodic boundary conditions.
346 // Hence, there we would like the original configuration (split through the
347 // the periodic boundaries). Otherwise, we would have to shift (and probably
348 // interpolate) the potential with OBCs applying.
349
350 // list atoms in fragment for debugging
351 {
352 std::stringstream output;
353 output << "INFO: Contained atoms: ";
354 for (std::vector<atom *>::const_iterator iter = atoms.begin();
355 iter != atoms.end(); ++iter) {
356 output << (*iter)->getName() << " ";
357 }
358 LOG(3, output.str());
359 }
360
361 // store to stream via FragmentParser
362 const bool intermediateResult =
363 FormatParserStorage::getInstance().save(
364 out,
365 FormatParserStorage::getInstance().getSuffixFromType(_type),
366 atoms);
367
368 return intermediateResult;
369}
370
371atom * const SaturatedFragment::getHydrogenReplacement(atom * const replacement)
372{
373 atom * const _atom = hydrogens.leaseHydrogen(); // new atom
374 _atom->setAtomicVelocity(replacement->getAtomicVelocity()); // copy velocity
375 _atom->setFixedIon(replacement->getFixedIon());
376 // if we replace hydrogen, we mark it as our father, otherwise we are just an added hydrogen with no father
377 _atom->father = replacement;
378 SaturationHydrogens.insert(_atom->getId());
379 return _atom;
380}
381
382const atom& SaturatedFragment::setHydrogenReplacement(
383 const atom * const _OwnerAtom,
384 const Vector &_position,
385 const double _distance,
386 atom * const _father)
387{
388 atom * const _atom = hydrogens.leaseHydrogen(); // new atom
389 _atom->setPosition( _OwnerAtom->getPosition() + _distance * _position );
390 // always set as fixed ion (not moving during molecular dynamics simulation)
391 _atom->setFixedIon(true);
392 // if we replace hydrogen, we mark it as our father, otherwise we are just an added hydrogen with no father
393 _atom->father = _father;
394 SaturationHydrogens.insert(_atom->getId());
395 return *_atom;
396}
397
398bool SaturatedFragment::AddHydrogenReplacementAtom(
399 bond::ptr TopBond,
400 atom *Origin,
401 atom *Replacement,
402 bool IsAngstroem)
403{
404// Info info(__func__);
405 bool AllWentWell = true; // flag gathering the boolean return value of molecule::AddAtom and other functions, as return value on exit
406 double bondlength; // bond length of the bond to be replaced/cut
407 double bondangle; // bond angle of the bond to be replaced/cut
408 double BondRescale; // rescale value for the hydrogen bond length
409 bond::ptr FirstBond;
410 bond::ptr SecondBond; // Other bonds in double bond case to determine "other" plane
411 atom *FirstOtherAtom = NULL, *SecondOtherAtom = NULL, *ThirdOtherAtom = NULL; // pointer to hydrogen atoms to be added
412 double b,l,d,f,g, alpha, factors[NDIM]; // hold temporary values in triple bond case for coordination determination
413 Vector Orthovector1, Orthovector2; // temporary vectors in coordination construction
414 Vector InBondvector; // vector in direction of *Bond
415 const RealSpaceMatrix &matrix = World::getInstance().getDomain().getM();
416 bond::ptr Binder;
417
418 // create vector in direction of bond
419 InBondvector = Replacement->getPosition() - Origin->getPosition();
420 bondlength = InBondvector.Norm();
421
422 // is greater than typical bond distance? Then we have to correct periodically
423 // the problem is not the H being out of the box, but InBondvector have the wrong direction
424 // due to Replacement or Origin being on the wrong side!
425 const BondGraph * const BG = World::getInstance().getBondGraph();
426 const range<double> MinMaxBondDistance(
427 BG->getMinMaxDistance(Origin,Replacement));
428 if (!MinMaxBondDistance.isInRange(bondlength)) {
429// LOG(4, "InBondvector is: " << InBondvector << ".");
430 Orthovector1.Zero();
431 for (int i=NDIM;i--;) {
432 l = Replacement->at(i) - Origin->at(i);
433 if (fabs(l) > MinMaxBondDistance.last) { // is component greater than bond distance (check against min not useful here)
434 Orthovector1[i] = (l < 0) ? -1. : +1.;
435 } // (signs are correct, was tested!)
436 }
437 Orthovector1 *= matrix;
438 InBondvector -= Orthovector1; // subtract just the additional translation
439 bondlength = InBondvector.Norm();
440// LOG(4, "INFO: Corrected InBondvector is now: " << InBondvector << ".");
441 } // periodic correction finished
442
443 InBondvector.Normalize();
444 // get typical bond length and store as scale factor for later
445 ASSERT(Origin->getType() != NULL,
446 "SaturatedFragment::AddHydrogenReplacementAtom() - element of Origin is not given.");
447 BondRescale = Origin->getType()->getHBondDistance(TopBond->getDegree()-1);
448 if (BondRescale == -1) {
449 ELOG(1, "There is no typical hydrogen bond distance in replacing bond (" << Origin->getName() << "<->" << Replacement->getName() << ") of degree " << TopBond->getDegree() << "!");
450 BondRescale = Origin->getType()->getHBondDistance(TopBond->getDegree());
451 if (BondRescale == -1) {
452 ELOG(1, "There is no typical hydrogen bond distance in replacing bond (" << Origin->getName() << "<->" << Replacement->getName() << ") of any degree!");
453 return false;
454 BondRescale = bondlength;
455 }
456 } else {
457 if (!IsAngstroem)
458 BondRescale /= (1.*AtomicLengthToAngstroem);
459 }
460
461 // discern single, double and triple bonds
462 switch(TopBond->getDegree()) {
463 case 1:
464 // check whether replacement has been an excluded hydrogen
465 if (Replacement->getType()->getAtomicNumber() == HydrogenPool::HYDROGEN) { // neither rescale nor replace if it's already hydrogen
466 FirstOtherAtom = Replacement;
467 BondRescale = bondlength;
468 } else {
469 FirstOtherAtom = getHydrogenReplacement(Replacement);
470 InBondvector *= BondRescale; // rescale the distance vector to Hydrogen bond length
471 FirstOtherAtom->setPosition(Origin->getPosition() + InBondvector); // set coordination to origin and add distance vector to replacement atom
472 }
473 FullMolecule.insert(FirstOtherAtom->getId());
474// LOG(4, "INFO: Added " << *FirstOtherAtom << " at: " << FirstOtherAtom->x << ".");
475 break;
476 case 2:
477 {
478 // determine two other bonds (warning if there are more than two other) plus valence sanity check
479 const BondList& ListOfBonds = Origin->getListOfBonds();
480 for (BondList::const_iterator Runner = ListOfBonds.begin();
481 Runner != ListOfBonds.end();
482 ++Runner) {
483 if ((*Runner) != TopBond) {
484 if (FirstBond == NULL) {
485 FirstBond = (*Runner);
486 FirstOtherAtom = (*Runner)->GetOtherAtom(Origin);
487 } else if (SecondBond == NULL) {
488 SecondBond = (*Runner);
489 SecondOtherAtom = (*Runner)->GetOtherAtom(Origin);
490 } else {
491 ELOG(2, "Detected more than four bonds for atom " << Origin->getName());
492 }
493 }
494 }
495 }
496 if (SecondOtherAtom == NULL) { // then we have an atom with valence four, but only 3 bonds: one to replace and one which is TopBond (third is FirstBond)
497 SecondBond = TopBond;
498 SecondOtherAtom = Replacement;
499 }
500 if (FirstOtherAtom != NULL) { // then we just have this double bond and the plane does not matter at all
501// LOG(3, "Regarding the double bond (" << Origin->Name << "<->" << Replacement->Name << ") to be constructed: Taking " << FirstOtherAtom->Name << " and " << SecondOtherAtom->Name << " along with " << Origin->Name << " to determine orthogonal plane.");
502
503 // determine the plane of these two with the *origin
504 try {
505 Orthovector1 = Plane(Origin->getPosition(), FirstOtherAtom->getPosition(), SecondOtherAtom->getPosition()).getNormal();
506 }
507 catch(LinearDependenceException &excp){
508 LOG(0, boost::diagnostic_information(excp));
509 // TODO: figure out what to do with the Orthovector in this case
510 AllWentWell = false;
511 }
512 } else {
513 Orthovector1.GetOneNormalVector(InBondvector);
514 }
515 //LOG(3, "INFO: Orthovector1: " << Orthovector1 << ".");
516 // orthogonal vector and bond vector between origin and replacement form the new plane
517 Orthovector1.MakeNormalTo(InBondvector);
518 Orthovector1.Normalize();
519 //LOG(3, "ReScaleCheck: " << Orthovector1.Norm() << " and " << InBondvector.Norm() << ".");
520
521 // create the two Hydrogens ...
522 FirstOtherAtom = getHydrogenReplacement(Replacement);
523 SecondOtherAtom = getHydrogenReplacement(Replacement);
524 FullMolecule.insert(FirstOtherAtom->getId());
525 FullMolecule.insert(SecondOtherAtom->getId());
526 bondangle = Origin->getType()->getHBondAngle(1);
527 if (bondangle == -1) {
528 ELOG(1, "There is no typical hydrogen bond angle in replacing bond (" << Origin->getName() << "<->" << Replacement->getName() << ") of degree " << TopBond->getDegree() << "!");
529 return false;
530 bondangle = 0;
531 }
532 bondangle *= M_PI/180./2.;
533// LOG(3, "INFO: ReScaleCheck: InBondvector " << InBondvector << ", " << Orthovector1 << ".");
534// LOG(3, "Half the bond angle is " << bondangle << ", sin and cos of it: " << sin(bondangle) << ", " << cos(bondangle));
535 FirstOtherAtom->Zero();
536 SecondOtherAtom->Zero();
537 for(int i=NDIM;i--;) { // rotate by half the bond angle in both directions (InBondvector is bondangle = 0 direction)
538 FirstOtherAtom->set(i, InBondvector[i] * cos(bondangle) + Orthovector1[i] * (sin(bondangle)));
539 SecondOtherAtom->set(i, InBondvector[i] * cos(bondangle) + Orthovector1[i] * (-sin(bondangle)));
540 }
541 FirstOtherAtom->Scale(BondRescale); // rescale by correct BondDistance
542 SecondOtherAtom->Scale(BondRescale);
543 //LOG(3, "ReScaleCheck: " << FirstOtherAtom->x.Norm() << " and " << SecondOtherAtom->x.Norm() << ".");
544 *FirstOtherAtom += Origin->getPosition();
545 *SecondOtherAtom += Origin->getPosition();
546 // ... and add to molecule
547// LOG(4, "INFO: Added " << *FirstOtherAtom << " at: " << FirstOtherAtom->x << ".");
548// LOG(4, "INFO: Added " << *SecondOtherAtom << " at: " << SecondOtherAtom->x << ".");
549 break;
550 case 3:
551 // take the "usual" tetraoidal angle and add the three Hydrogen in direction of the bond (height of the tetraoid)
552 FirstOtherAtom = getHydrogenReplacement(Replacement);
553 SecondOtherAtom = getHydrogenReplacement(Replacement);
554 ThirdOtherAtom = getHydrogenReplacement(Replacement);
555 FullMolecule.insert(FirstOtherAtom->getId());
556 FullMolecule.insert(SecondOtherAtom->getId());
557 FullMolecule.insert(ThirdOtherAtom->getId());
558
559 // we need to vectors orthonormal the InBondvector
560 AllWentWell = AllWentWell && Orthovector1.GetOneNormalVector(InBondvector);
561// LOG(3, "INFO: Orthovector1: " << Orthovector1 << ".");
562 try{
563 Orthovector2 = Plane(InBondvector, Orthovector1,0).getNormal();
564 }
565 catch(LinearDependenceException &excp) {
566 LOG(0, boost::diagnostic_information(excp));
567 AllWentWell = false;
568 }
569// LOG(3, "INFO: Orthovector2: " << Orthovector2 << ".")
570
571 // create correct coordination for the three atoms
572 alpha = (Origin->getType()->getHBondAngle(2))/180.*M_PI/2.; // retrieve triple bond angle from database
573 l = BondRescale; // desired bond length
574 b = 2.*l*sin(alpha); // base length of isosceles triangle
575 d = l*sqrt(cos(alpha)*cos(alpha) - sin(alpha)*sin(alpha)/3.); // length for InBondvector
576 f = b/sqrt(3.); // length for Orthvector1
577 g = b/2.; // length for Orthvector2
578// LOG(3, "Bond length and half-angle: " << l << ", " << alpha << "\t (b,d,f,g) = " << b << ", " << d << ", " << f << ", " << g << ", ");
579// LOG(3, "The three Bond lengths: " << sqrt(d*d+f*f) << ", " << sqrt(d*d+(-0.5*f)*(-0.5*f)+g*g) << ", " << sqrt(d*d+(-0.5*f)*(-0.5*f)+g*g));
580 factors[0] = d;
581 factors[1] = f;
582 factors[2] = 0.;
583 FirstOtherAtom->LinearCombinationOfVectors(InBondvector, Orthovector1, Orthovector2, factors);
584 factors[1] = -0.5*f;
585 factors[2] = g;
586 SecondOtherAtom->LinearCombinationOfVectors(InBondvector, Orthovector1, Orthovector2, factors);
587 factors[2] = -g;
588 ThirdOtherAtom->LinearCombinationOfVectors(InBondvector, Orthovector1, Orthovector2, factors);
589
590 // rescale each to correct BondDistance
591// FirstOtherAtom->x.Scale(&BondRescale);
592// SecondOtherAtom->x.Scale(&BondRescale);
593// ThirdOtherAtom->x.Scale(&BondRescale);
594
595 // and relative to *origin atom
596 *FirstOtherAtom += Origin->getPosition();
597 *SecondOtherAtom += Origin->getPosition();
598 *ThirdOtherAtom += Origin->getPosition();
599
600 // ... and add to molecule
601// LOG(4, "INFO: Added " << *FirstOtherAtom << " at: " << FirstOtherAtom->x << ".");
602// LOG(4, "INFO: Added " << *SecondOtherAtom << " at: " << SecondOtherAtom->x << ".");
603// LOG(4, "INFO: Added " << *ThirdOtherAtom << " at: " << ThirdOtherAtom->x << ".");
604 break;
605 default:
606 ELOG(1, "BondDegree does not state single, double or triple bond!");
607 AllWentWell = false;
608 break;
609 }
610
611 return AllWentWell;
612};
Note: See TracBrowser for help on using the repository browser.