source: src/molecule.cpp@ 70db8f

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 70db8f was 708277, checked in by Frederik Heber <heber@…>, 12 years ago

FIX: Using fixed Observer in CodePatterns 1.2.6.

  • we now require CodePatterns 1.2.6.
  • Notifications now use subjectKilled() when the Observable they are associated with is destroyed. This allows Observers that are just sign on to a single channel safely sign off.
  • also, derived Observable classes must not remove their Channels and Notifications by themselves. This is done by the Observable base class. Otherwise, subjectKilled() is not called.
  • changed AtomObserver: Is directly connected to the atom class, AtomInserted and AtomRemoved called in its cstor and dstor. The World has nothing to do with it. However, we have to make sure AtomObserver is purged after World.
  • Fixed also some faulty uses of ObserverLog and we always insert into NotificationChannels with static_cast to Observable for clarity.
  • Property mode set to 100755
File size: 37.3 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010-2012 University of Bonn. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/** \file molecules.cpp
24 *
25 * Functions for the class molecule.
26 *
27 */
28
29// include config.h
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33
34#include "CodePatterns/MemDebug.hpp"
35
36#include <cstring>
37#include <boost/bind.hpp>
38#include <boost/foreach.hpp>
39
40#include <gsl/gsl_inline.h>
41#include <gsl/gsl_heapsort.h>
42
43#include "molecule.hpp"
44
45#include "Atom/atom.hpp"
46#include "Bond/bond.hpp"
47#include "Box.hpp"
48#include "CodePatterns/enumeration.hpp"
49#include "CodePatterns/Log.hpp"
50#include "config.hpp"
51#include "Descriptors/AtomIdDescriptor.hpp"
52#include "Element/element.hpp"
53#include "Graph/BondGraph.hpp"
54#include "LinearAlgebra/Exceptions.hpp"
55#include "LinearAlgebra/leastsquaremin.hpp"
56#include "LinearAlgebra/Plane.hpp"
57#include "LinearAlgebra/RealSpaceMatrix.hpp"
58#include "LinearAlgebra/Vector.hpp"
59#include "LinkedCell/linkedcell.hpp"
60#include "IdPool_impl.hpp"
61#include "Shapes/BaseShapes.hpp"
62#include "Tesselation/tesselation.hpp"
63#include "World.hpp"
64#include "WorldTime.hpp"
65
66
67/************************************* Functions for class molecule *********************************/
68
69/** Constructor of class molecule.
70 * Initialises molecule list with correctly referenced start and end, and sets molecule::last_atom to zero.
71 */
72molecule::molecule() :
73 Observable("molecule"),
74 MDSteps(0),
75 NoNonBonds(0),
76 NoCyclicBonds(0),
77 ActiveFlag(false),
78 IndexNr(-1),
79 NoNonHydrogen(this,boost::bind(&molecule::doCountNoNonHydrogen,this),"NoNonHydrogen"),
80 BondCount(this,boost::bind(&molecule::doCountBonds,this),"BondCount"),
81 atomIdPool(1, 20, 100),
82 last_atom(0)
83{
84 // add specific channels
85 Channels *OurChannel = new Channels;
86 NotificationChannels.insert( std::make_pair( static_cast<Observable *>(this), OurChannel) );
87 for (size_t type = 0; type < (size_t)NotificationType_MAX; ++type)
88 OurChannel->addChannel(type);
89
90 strcpy(name,World::getInstance().getDefaultName().c_str());
91};
92
93molecule *NewMolecule(){
94 return new molecule();
95}
96
97/** Destructor of class molecule.
98 * Initialises molecule list with correctly referenced start and end, and sets molecule::last_atom to zero.
99 */
100molecule::~molecule()
101{
102 CleanupMolecule();
103};
104
105
106void DeleteMolecule(molecule *mol){
107 delete mol;
108}
109
110// getter and setter
111const std::string molecule::getName() const{
112 return std::string(name);
113}
114
115int molecule::getAtomCount() const{
116 return atomIds.size();
117}
118
119size_t molecule::getNoNonHydrogen() const{
120 return *NoNonHydrogen;
121}
122
123int molecule::getBondCount() const{
124 return *BondCount;
125}
126
127void molecule::setName(const std::string _name){
128 OBSERVE;
129 NOTIFY(MoleculeNameChanged);
130 cout << "Set name of molecule " << getId() << " to " << _name << endl;
131 strncpy(name,_name.c_str(),MAXSTRINGSIZE);
132}
133
134void molecule::InsertLocalToGlobalId(atom * const pointer)
135{
136#ifndef NDEBUG
137 std::pair< LocalToGlobalId_t::iterator, bool > inserter =
138#endif
139 LocalToGlobalId.insert( std::make_pair(pointer->getNr(), pointer) );
140 ASSERT( inserter.second,
141 "molecule::AddAtom() - local number "+toString(pointer->getNr())+" appears twice.");
142}
143
144bool molecule::changeAtomNr(int oldNr, int newNr, atom* target){
145 OBSERVE;
146 if(atomIdPool.reserveId(newNr)){
147 NOTIFY(AtomNrChanged);
148 if (oldNr != -1) // -1 is reserved and indicates no number
149 atomIdPool.releaseId(oldNr);
150 LocalToGlobalId.erase(oldNr);
151 ASSERT (target,
152 "molecule::changeAtomNr() - given target is NULL, cannot set Nr or name.");
153 target->setNr(newNr);
154 InsertLocalToGlobalId(target);
155 setAtomName(target);
156 return true;
157 } else{
158 return false;
159 }
160}
161
162bool molecule::changeId(moleculeId_t newId){
163 // first we move ourselves in the world
164 // the world lets us know if that succeeded
165 if(World::getInstance().changeMoleculeId(id,newId,this)){
166 id = newId;
167 return true;
168 }
169 else{
170 return false;
171 }
172}
173
174
175moleculeId_t molecule::getId() const {
176 return id;
177}
178
179void molecule::setId(moleculeId_t _id){
180 id =_id;
181}
182
183const Formula &molecule::getFormula() const {
184 return formula;
185}
186
187unsigned int molecule::getElementCount() const{
188 return formula.getElementCount();
189}
190
191bool molecule::hasElement(const element *element) const{
192 return formula.hasElement(element);
193}
194
195bool molecule::hasElement(atomicNumber_t Z) const{
196 return formula.hasElement(Z);
197}
198
199bool molecule::hasElement(const string &shorthand) const{
200 return formula.hasElement(shorthand);
201}
202
203/************************** Access to the List of Atoms ****************/
204
205molecule::const_iterator molecule::erase( const_iterator loc )
206{
207 OBSERVE;
208 NOTIFY(AtomRemoved);
209 const_iterator iter = loc;
210 ++iter;
211 atom * const _atom = const_cast<atom *>(*loc);
212 atomIds.erase( _atom->getId() );
213 {
214 NOTIFY(AtomNrChanged);
215 atomIdPool.releaseId(_atom->getNr());
216 LocalToGlobalId.erase(_atom->getNr());
217 _atom->setNr(-1);
218 }
219 formula-=_atom->getType();
220 _atom->removeFromMolecule();
221 return iter;
222}
223
224molecule::const_iterator molecule::erase( atom * key )
225{
226 OBSERVE;
227 NOTIFY(AtomRemoved);
228 const_iterator iter = find(key);
229 if (iter != end()){
230 ++iter;
231 atomIds.erase( key->getId() );
232 {
233 NOTIFY(AtomNrChanged);
234 atomIdPool.releaseId(key->getNr());
235 LocalToGlobalId.erase(key->getNr());
236 key->setNr(-1);
237 }
238 formula-=key->getType();
239 key->removeFromMolecule();
240 }
241 return iter;
242}
243
244pair<molecule::iterator,bool> molecule::insert ( atom * const key )
245{
246 OBSERVE;
247 NOTIFY(AtomInserted);
248 std::pair<iterator,bool> res = atomIds.insert(key->getId());
249 if (res.second) { // push atom if went well
250 NOTIFY(AtomNrChanged);
251 key->setNr(atomIdPool.getNextId());
252 InsertLocalToGlobalId(key);
253 setAtomName(key);
254 formula+=key->getType();
255 return res;
256 } else {
257 return pair<iterator,bool>(end(),res.second);
258 }
259}
260
261void molecule::setAtomName(atom *_atom) const
262{
263 std::stringstream sstr;
264 sstr << _atom->getType()->getSymbol() << _atom->getNr();
265 _atom->setName(sstr.str());
266}
267
268World::AtomComposite molecule::getAtomSet() const
269{
270 World::AtomComposite vector_of_atoms;
271 for (molecule::iterator iter = begin(); iter != end(); ++iter)
272 vector_of_atoms.push_back(*iter);
273 return vector_of_atoms;
274}
275
276/** Adds given atom \a *pointer from molecule list.
277 * Increases molecule::last_atom and gives last number to added atom and names it according to its element::abbrev and molecule::AtomCount
278 * \param *pointer allocated and set atom
279 * \return true - succeeded, false - atom not found in list
280 */
281bool molecule::AddAtom(atom *pointer)
282{
283 if (pointer != NULL) {
284 // molecule::insert() is called by setMolecule()
285 pointer->setMolecule(this);
286 }
287 return true;
288};
289
290/** Adds a copy of the given atom \a *pointer from molecule list.
291 * Increases molecule::last_atom and gives last number to added atom.
292 * \param *pointer allocated and set atom
293 * \return pointer to the newly added atom
294 */
295atom * molecule::AddCopyAtom(atom *pointer)
296{
297 atom *retval = NULL;
298 if (pointer != NULL) {
299 atom *walker = pointer->clone();
300 AddAtom(walker);
301 retval=walker;
302 }
303 return retval;
304};
305
306/** Adds a Hydrogen atom in replacement for the given atom \a *partner in bond with a *origin.
307 * Here, we have to distinguish between single, double or triple bonds as stated by \a BondDegree, that each demand
308 * a different scheme when adding \a *replacement atom for the given one.
309 * -# Single Bond: Simply add new atom with bond distance rescaled to typical hydrogen one
310 * -# Double Bond: Here, we need the **BondList of the \a *origin atom, by scanning for the other bonds instead of
311 * *Bond, we use the through these connected atoms to determine the plane they lie in, vector::MakeNormalvector().
312 * The orthonormal vector to this plane along with the vector in *Bond direction determines the plane the two
313 * replacing hydrogens shall lie in. Now, all remains to do is take the usual hydrogen double bond angle for the
314 * element of *origin and form the sin/cos admixture of both plane vectors for the new coordinates of the two
315 * hydrogens forming this angle with *origin.
316 * -# Triple Bond: The idea is to set up a tetraoid (C1-H1-H2-H3) (however the lengths \f$b\f$ of the sides of the base
317 * triangle formed by the to be added hydrogens are not equal to the typical bond distance \f$l\f$ but have to be
318 * determined from the typical angle \f$\alpha\f$ for a hydrogen triple connected to the element of *origin):
319 * We have the height \f$d\f$ as the vector in *Bond direction (from triangle C1-H1-H2).
320 * \f[ h = l \cdot \cos{\left (\frac{\alpha}{2} \right )} \qquad b = 2l \cdot \sin{\left (\frac{\alpha}{2} \right)} \quad \rightarrow \quad d = l \cdot \sqrt{\cos^2{\left (\frac{\alpha}{2} \right)}-\frac{1}{3}\cdot\sin^2{\left (\frac{\alpha}{2}\right )}}
321 * \f]
322 * vector::GetNormalvector() creates one orthonormal vector from this *Bond vector and vector::MakeNormalvector creates
323 * the third one from the former two vectors. The latter ones form the plane of the base triangle mentioned above.
324 * The lengths for these are \f$f\f$ and \f$g\f$ (from triangle H1-H2-(center of H1-H2-H3)) with knowledge that
325 * the median lines in an isosceles triangle meet in the center point with a ratio 2:1.
326 * \f[ f = \frac{b}{\sqrt{3}} \qquad g = \frac{b}{2}
327 * \f]
328 * as the coordination of all three atoms in the coordinate system of these three vectors:
329 * \f$\pmatrix{d & f & 0}\f$, \f$\pmatrix{d & -0.5 \cdot f & g}\f$ and \f$\pmatrix{d & -0.5 \cdot f & -g}\f$.
330 *
331 * \param *out output stream for debugging
332 * \param *Bond pointer to bond between \a *origin and \a *replacement
333 * \param *TopOrigin son of \a *origin of upper level molecule (the atom added to this molecule as a copy of \a *origin)
334 * \param *origin pointer to atom which acts as the origin for scaling the added hydrogen to correct bond length
335 * \param *replacement pointer to the atom which shall be copied as a hydrogen atom in this molecule
336 * \param isAngstroem whether the coordination of the given atoms is in AtomicLength (false) or Angstrom(true)
337 * \return number of atoms added, if < bond::BondDegree then something went wrong
338 * \todo double and triple bonds splitting (always use the tetraeder angle!)
339 */
340bool molecule::AddHydrogenReplacementAtom(bond::ptr TopBond, atom *BottomOrigin, atom *TopOrigin, atom *TopReplacement, bool IsAngstroem)
341{
342// Info info(__func__);
343 bool AllWentWell = true; // flag gathering the boolean return value of molecule::AddAtom and other functions, as return value on exit
344 double bondlength; // bond length of the bond to be replaced/cut
345 double bondangle; // bond angle of the bond to be replaced/cut
346 double BondRescale; // rescale value for the hydrogen bond length
347 bond::ptr FirstBond;
348 bond::ptr SecondBond; // Other bonds in double bond case to determine "other" plane
349 atom *FirstOtherAtom = NULL, *SecondOtherAtom = NULL, *ThirdOtherAtom = NULL; // pointer to hydrogen atoms to be added
350 double b,l,d,f,g, alpha, factors[NDIM]; // hold temporary values in triple bond case for coordination determination
351 Vector Orthovector1, Orthovector2; // temporary vectors in coordination construction
352 Vector InBondvector; // vector in direction of *Bond
353 const RealSpaceMatrix &matrix = World::getInstance().getDomain().getM();
354 bond::ptr Binder;
355
356 // create vector in direction of bond
357 InBondvector = TopReplacement->getPosition() - TopOrigin->getPosition();
358 bondlength = InBondvector.Norm();
359
360 // is greater than typical bond distance? Then we have to correct periodically
361 // the problem is not the H being out of the box, but InBondvector have the wrong direction
362 // due to TopReplacement or Origin being on the wrong side!
363 const BondGraph * const BG = World::getInstance().getBondGraph();
364 const range<double> MinMaxBondDistance(
365 BG->getMinMaxDistance(TopOrigin,TopReplacement));
366 if (!MinMaxBondDistance.isInRange(bondlength)) {
367// LOG(4, "InBondvector is: " << InBondvector << ".");
368 Orthovector1.Zero();
369 for (int i=NDIM;i--;) {
370 l = TopReplacement->at(i) - TopOrigin->at(i);
371 if (fabs(l) > MinMaxBondDistance.last) { // is component greater than bond distance (check against min not useful here)
372 Orthovector1[i] = (l < 0) ? -1. : +1.;
373 } // (signs are correct, was tested!)
374 }
375 Orthovector1 *= matrix;
376 InBondvector -= Orthovector1; // subtract just the additional translation
377 bondlength = InBondvector.Norm();
378// LOG(4, "INFO: Corrected InBondvector is now: " << InBondvector << ".");
379 } // periodic correction finished
380
381 InBondvector.Normalize();
382 // get typical bond length and store as scale factor for later
383 ASSERT(TopOrigin->getType() != NULL, "AddHydrogenReplacementAtom: element of TopOrigin is not given.");
384 BondRescale = TopOrigin->getType()->getHBondDistance(TopBond->BondDegree-1);
385 if (BondRescale == -1) {
386 ELOG(1, "There is no typical hydrogen bond distance in replacing bond (" << TopOrigin->getName() << "<->" << TopReplacement->getName() << ") of degree " << TopBond->BondDegree << "!");
387 return false;
388 BondRescale = bondlength;
389 } else {
390 if (!IsAngstroem)
391 BondRescale /= (1.*AtomicLengthToAngstroem);
392 }
393
394 // discern single, double and triple bonds
395 switch(TopBond->BondDegree) {
396 case 1:
397 FirstOtherAtom = World::getInstance().createAtom(); // new atom
398 FirstOtherAtom->setType(1); // element is Hydrogen
399 FirstOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
400 FirstOtherAtom->setFixedIon(TopReplacement->getFixedIon());
401 if (TopReplacement->getType()->getAtomicNumber() == 1) { // neither rescale nor replace if it's already hydrogen
402 FirstOtherAtom->father = TopReplacement;
403 BondRescale = bondlength;
404 } else {
405 FirstOtherAtom->father = NULL; // if we replace hydrogen, we mark it as our father, otherwise we are just an added hydrogen with no father
406 }
407 InBondvector *= BondRescale; // rescale the distance vector to Hydrogen bond length
408 FirstOtherAtom->setPosition(TopOrigin->getPosition() + InBondvector); // set coordination to origin and add distance vector to replacement atom
409 AllWentWell = AllWentWell && AddAtom(FirstOtherAtom);
410// LOG(4, "INFO: Added " << *FirstOtherAtom << " at: " << FirstOtherAtom->x << ".");
411 Binder = AddBond(BottomOrigin, FirstOtherAtom, 1);
412 Binder->Cyclic = false;
413 Binder->Type = GraphEdge::TreeEdge;
414 break;
415 case 2:
416 {
417 // determine two other bonds (warning if there are more than two other) plus valence sanity check
418 const BondList& ListOfBonds = TopOrigin->getListOfBonds();
419 for (BondList::const_iterator Runner = ListOfBonds.begin();
420 Runner != ListOfBonds.end();
421 ++Runner) {
422 if ((*Runner) != TopBond) {
423 if (FirstBond == NULL) {
424 FirstBond = (*Runner);
425 FirstOtherAtom = (*Runner)->GetOtherAtom(TopOrigin);
426 } else if (SecondBond == NULL) {
427 SecondBond = (*Runner);
428 SecondOtherAtom = (*Runner)->GetOtherAtom(TopOrigin);
429 } else {
430 ELOG(2, "Detected more than four bonds for atom " << TopOrigin->getName());
431 }
432 }
433 }
434 }
435 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)
436 SecondBond = TopBond;
437 SecondOtherAtom = TopReplacement;
438 }
439 if (FirstOtherAtom != NULL) { // then we just have this double bond and the plane does not matter at all
440// LOG(3, "Regarding the double bond (" << TopOrigin->Name << "<->" << TopReplacement->Name << ") to be constructed: Taking " << FirstOtherAtom->Name << " and " << SecondOtherAtom->Name << " along with " << TopOrigin->Name << " to determine orthogonal plane.");
441
442 // determine the plane of these two with the *origin
443 try {
444 Orthovector1 = Plane(TopOrigin->getPosition(), FirstOtherAtom->getPosition(), SecondOtherAtom->getPosition()).getNormal();
445 }
446 catch(LinearDependenceException &excp){
447 LOG(0, boost::diagnostic_information(excp));
448 // TODO: figure out what to do with the Orthovector in this case
449 AllWentWell = false;
450 }
451 } else {
452 Orthovector1.GetOneNormalVector(InBondvector);
453 }
454 //LOG(3, "INFO: Orthovector1: " << Orthovector1 << ".");
455 // orthogonal vector and bond vector between origin and replacement form the new plane
456 Orthovector1.MakeNormalTo(InBondvector);
457 Orthovector1.Normalize();
458 //LOG(3, "ReScaleCheck: " << Orthovector1.Norm() << " and " << InBondvector.Norm() << ".");
459
460 // create the two Hydrogens ...
461 FirstOtherAtom = World::getInstance().createAtom();
462 SecondOtherAtom = World::getInstance().createAtom();
463 FirstOtherAtom->setType(1);
464 SecondOtherAtom->setType(1);
465 FirstOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
466 FirstOtherAtom->setFixedIon(TopReplacement->getFixedIon());
467 SecondOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
468 SecondOtherAtom->setFixedIon(TopReplacement->getFixedIon());
469 FirstOtherAtom->father = NULL; // we are just an added hydrogen with no father
470 SecondOtherAtom->father = NULL; // we are just an added hydrogen with no father
471 bondangle = TopOrigin->getType()->getHBondAngle(1);
472 if (bondangle == -1) {
473 ELOG(1, "There is no typical hydrogen bond angle in replacing bond (" << TopOrigin->getName() << "<->" << TopReplacement->getName() << ") of degree " << TopBond->BondDegree << "!");
474 return false;
475 bondangle = 0;
476 }
477 bondangle *= M_PI/180./2.;
478// LOG(3, "INFO: ReScaleCheck: InBondvector " << InBondvector << ", " << Orthovector1 << ".");
479// LOG(3, "Half the bond angle is " << bondangle << ", sin and cos of it: " << sin(bondangle) << ", " << cos(bondangle));
480 FirstOtherAtom->Zero();
481 SecondOtherAtom->Zero();
482 for(int i=NDIM;i--;) { // rotate by half the bond angle in both directions (InBondvector is bondangle = 0 direction)
483 FirstOtherAtom->set(i, InBondvector[i] * cos(bondangle) + Orthovector1[i] * (sin(bondangle)));
484 SecondOtherAtom->set(i, InBondvector[i] * cos(bondangle) + Orthovector1[i] * (-sin(bondangle)));
485 }
486 FirstOtherAtom->Scale(BondRescale); // rescale by correct BondDistance
487 SecondOtherAtom->Scale(BondRescale);
488 //LOG(3, "ReScaleCheck: " << FirstOtherAtom->x.Norm() << " and " << SecondOtherAtom->x.Norm() << ".");
489 *FirstOtherAtom += TopOrigin->getPosition();
490 *SecondOtherAtom += TopOrigin->getPosition();
491 // ... and add to molecule
492 AllWentWell = AllWentWell && AddAtom(FirstOtherAtom);
493 AllWentWell = AllWentWell && AddAtom(SecondOtherAtom);
494// LOG(4, "INFO: Added " << *FirstOtherAtom << " at: " << FirstOtherAtom->x << ".");
495// LOG(4, "INFO: Added " << *SecondOtherAtom << " at: " << SecondOtherAtom->x << ".");
496 Binder = AddBond(BottomOrigin, FirstOtherAtom, 1);
497 Binder->Cyclic = false;
498 Binder->Type = GraphEdge::TreeEdge;
499 Binder = AddBond(BottomOrigin, SecondOtherAtom, 1);
500 Binder->Cyclic = false;
501 Binder->Type = GraphEdge::TreeEdge;
502 break;
503 case 3:
504 // take the "usual" tetraoidal angle and add the three Hydrogen in direction of the bond (height of the tetraoid)
505 FirstOtherAtom = World::getInstance().createAtom();
506 SecondOtherAtom = World::getInstance().createAtom();
507 ThirdOtherAtom = World::getInstance().createAtom();
508 FirstOtherAtom->setType(1);
509 SecondOtherAtom->setType(1);
510 ThirdOtherAtom->setType(1);
511 FirstOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
512 FirstOtherAtom->setFixedIon(TopReplacement->getFixedIon());
513 SecondOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
514 SecondOtherAtom->setFixedIon(TopReplacement->getFixedIon());
515 ThirdOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
516 ThirdOtherAtom->setFixedIon(TopReplacement->getFixedIon());
517 FirstOtherAtom->father = NULL; // we are just an added hydrogen with no father
518 SecondOtherAtom->father = NULL; // we are just an added hydrogen with no father
519 ThirdOtherAtom->father = NULL; // we are just an added hydrogen with no father
520
521 // we need to vectors orthonormal the InBondvector
522 AllWentWell = AllWentWell && Orthovector1.GetOneNormalVector(InBondvector);
523// LOG(3, "INFO: Orthovector1: " << Orthovector1 << ".");
524 try{
525 Orthovector2 = Plane(InBondvector, Orthovector1,0).getNormal();
526 }
527 catch(LinearDependenceException &excp) {
528 LOG(0, boost::diagnostic_information(excp));
529 AllWentWell = false;
530 }
531// LOG(3, "INFO: Orthovector2: " << Orthovector2 << ".")
532
533 // create correct coordination for the three atoms
534 alpha = (TopOrigin->getType()->getHBondAngle(2))/180.*M_PI/2.; // retrieve triple bond angle from database
535 l = BondRescale; // desired bond length
536 b = 2.*l*sin(alpha); // base length of isosceles triangle
537 d = l*sqrt(cos(alpha)*cos(alpha) - sin(alpha)*sin(alpha)/3.); // length for InBondvector
538 f = b/sqrt(3.); // length for Orthvector1
539 g = b/2.; // length for Orthvector2
540// LOG(3, "Bond length and half-angle: " << l << ", " << alpha << "\t (b,d,f,g) = " << b << ", " << d << ", " << f << ", " << g << ", ");
541// 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));
542 factors[0] = d;
543 factors[1] = f;
544 factors[2] = 0.;
545 FirstOtherAtom->LinearCombinationOfVectors(InBondvector, Orthovector1, Orthovector2, factors);
546 factors[1] = -0.5*f;
547 factors[2] = g;
548 SecondOtherAtom->LinearCombinationOfVectors(InBondvector, Orthovector1, Orthovector2, factors);
549 factors[2] = -g;
550 ThirdOtherAtom->LinearCombinationOfVectors(InBondvector, Orthovector1, Orthovector2, factors);
551
552 // rescale each to correct BondDistance
553// FirstOtherAtom->x.Scale(&BondRescale);
554// SecondOtherAtom->x.Scale(&BondRescale);
555// ThirdOtherAtom->x.Scale(&BondRescale);
556
557 // and relative to *origin atom
558 *FirstOtherAtom += TopOrigin->getPosition();
559 *SecondOtherAtom += TopOrigin->getPosition();
560 *ThirdOtherAtom += TopOrigin->getPosition();
561
562 // ... and add to molecule
563 AllWentWell = AllWentWell && AddAtom(FirstOtherAtom);
564 AllWentWell = AllWentWell && AddAtom(SecondOtherAtom);
565 AllWentWell = AllWentWell && AddAtom(ThirdOtherAtom);
566// LOG(4, "INFO: Added " << *FirstOtherAtom << " at: " << FirstOtherAtom->x << ".");
567// LOG(4, "INFO: Added " << *SecondOtherAtom << " at: " << SecondOtherAtom->x << ".");
568// LOG(4, "INFO: Added " << *ThirdOtherAtom << " at: " << ThirdOtherAtom->x << ".");
569 Binder = AddBond(BottomOrigin, FirstOtherAtom, 1);
570 Binder->Cyclic = false;
571 Binder->Type = GraphEdge::TreeEdge;
572 Binder = AddBond(BottomOrigin, SecondOtherAtom, 1);
573 Binder->Cyclic = false;
574 Binder->Type = GraphEdge::TreeEdge;
575 Binder = AddBond(BottomOrigin, ThirdOtherAtom, 1);
576 Binder->Cyclic = false;
577 Binder->Type = GraphEdge::TreeEdge;
578 break;
579 default:
580 ELOG(1, "BondDegree does not state single, double or triple bond!");
581 AllWentWell = false;
582 break;
583 }
584
585 return AllWentWell;
586};
587
588/** Creates a copy of this molecule.
589 * \param offset translation Vector for the new molecule relative to old one
590 * \return copy of molecule
591 */
592molecule *molecule::CopyMolecule(const Vector &offset) const
593{
594 molecule *copy = World::getInstance().createMolecule();
595
596 // copy all atoms
597 std::map< const atom *, atom *> FatherFinder;
598 for (iterator iter = begin(); iter != end(); ++iter) {
599 atom * const copy_atom = copy->AddCopyAtom(*iter);
600 copy_atom->setPosition(copy_atom->getPosition() + offset);
601 FatherFinder.insert( std::make_pair( *iter, copy_atom ) );
602 }
603
604 // copy all bonds
605 for(const_iterator AtomRunner = begin(); AtomRunner != end(); ++AtomRunner) {
606 const BondList& ListOfBonds = (*AtomRunner)->getListOfBonds();
607 for(BondList::const_iterator BondRunner = ListOfBonds.begin();
608 BondRunner != ListOfBonds.end();
609 ++BondRunner)
610 if ((*BondRunner)->leftatom == *AtomRunner) {
611 bond::ptr Binder = (*BondRunner);
612 // get the pendant atoms of current bond in the copy molecule
613 ASSERT(FatherFinder.count(Binder->leftatom),
614 "molecule::CopyMolecule() - No copy of original left atom "
615 +toString(Binder->leftatom)+" for bond copy found");
616 ASSERT(FatherFinder.count(Binder->rightatom),
617 "molecule::CopyMolecule() - No copy of original right atom "
618 +toString(Binder->rightatom)+" for bond copy found");
619 atom * const LeftAtom = FatherFinder[Binder->leftatom];
620 atom * const RightAtom = FatherFinder[Binder->rightatom];
621
622 bond::ptr const NewBond = copy->AddBond(LeftAtom, RightAtom, Binder->BondDegree);
623 NewBond->Cyclic = Binder->Cyclic;
624 if (Binder->Cyclic)
625 copy->NoCyclicBonds++;
626 NewBond->Type = Binder->Type;
627 }
628 }
629 // correct fathers
630 //for_each(begin(),end(),mem_fun(&atom::CorrectFather));
631
632 return copy;
633};
634
635
636/** Destroys all atoms inside this molecule.
637 */
638void molecule::removeAtomsinMolecule()
639{
640 // remove each atom from world
641 for(iterator AtomRunner = begin(); !empty(); AtomRunner = begin())
642 World::getInstance().destroyAtom(*AtomRunner);
643};
644
645
646/**
647 * Copies all atoms of a molecule which are within the defined parallelepiped.
648 *
649 * @param offest for the origin of the parallelepiped
650 * @param three vectors forming the matrix that defines the shape of the parallelpiped
651 */
652molecule* molecule::CopyMoleculeFromSubRegion(const Shape &region) const {
653 molecule *copy = World::getInstance().createMolecule();
654
655 // copy all atoms
656 std::map< const atom *, atom *> FatherFinder;
657 for (iterator iter = begin(); iter != end(); ++iter) {
658 if (region.isInside((*iter)->getPosition())) {
659 atom * const copy_atom = copy->AddCopyAtom(*iter);
660 FatherFinder.insert( std::make_pair( *iter, copy_atom ) );
661 }
662 }
663
664 // copy all bonds
665 for(molecule::const_iterator AtomRunner = begin(); AtomRunner != end(); ++AtomRunner) {
666 const BondList& ListOfBonds = (*AtomRunner)->getListOfBonds();
667 for(BondList::const_iterator BondRunner = ListOfBonds.begin();
668 BondRunner != ListOfBonds.end();
669 ++BondRunner)
670 if ((*BondRunner)->leftatom == *AtomRunner) {
671 bond::ptr Binder = (*BondRunner);
672 if ((FatherFinder.count(Binder->leftatom))
673 && (FatherFinder.count(Binder->rightatom))) {
674 // if copy present, then it must be from subregion
675 atom * const LeftAtom = FatherFinder[Binder->leftatom];
676 atom * const RightAtom = FatherFinder[Binder->rightatom];
677
678 bond::ptr const NewBond = copy->AddBond(LeftAtom, RightAtom, Binder->BondDegree);
679 NewBond->Cyclic = Binder->Cyclic;
680 if (Binder->Cyclic)
681 copy->NoCyclicBonds++;
682 NewBond->Type = Binder->Type;
683 }
684 }
685 }
686 // correct fathers
687 //for_each(begin(),end(),mem_fun(&atom::CorrectFather));
688
689 //TODO: copy->BuildInducedSubgraph(this);
690
691 return copy;
692}
693
694/** Adds a bond to a the molecule specified by two atoms, \a *first and \a *second.
695 * Also updates molecule::BondCount and molecule::NoNonBonds.
696 * \param *first first atom in bond
697 * \param *second atom in bond
698 * \return pointer to bond or NULL on failure
699 */
700bond::ptr molecule::AddBond(atom *atom1, atom *atom2, int degree)
701{
702 bond::ptr Binder;
703
704 // some checks to make sure we are able to create the bond
705 ASSERT(atom1,
706 "molecule::AddBond() - First atom "+toString(atom1)
707 +" is not a invalid pointer");
708 ASSERT(atom2,
709 "molecule::AddBond() - Second atom "+toString(atom2)
710 +" is not a invalid pointer");
711 ASSERT(isInMolecule(atom1),
712 "molecule::AddBond() - First atom "+toString(atom1)
713 +" is not part of molecule");
714 ASSERT(isInMolecule(atom2),
715 "molecule::AddBond() - Second atom "+toString(atom2)
716 +" is not part of molecule");
717
718 Binder.reset(new bond(atom1, atom2, degree));
719 atom1->RegisterBond(WorldTime::getTime(), Binder);
720 atom2->RegisterBond(WorldTime::getTime(), Binder);
721 if ((atom1->getType() != NULL)
722 && (atom1->getType()->getAtomicNumber() != 1)
723 && (atom2->getType() != NULL)
724 && (atom2->getType()->getAtomicNumber() != 1))
725 NoNonBonds++;
726
727 return Binder;
728};
729
730/** Set molecule::name from the basename without suffix in the given \a *filename.
731 * \param *filename filename
732 */
733void molecule::SetNameFromFilename(const char *filename)
734{
735 OBSERVE;
736 int length = 0;
737 const char *molname = strrchr(filename, '/');
738 if (molname != NULL)
739 molname += sizeof(char); // search for filename without dirs
740 else
741 molname = filename; // contains no slashes
742 const char *endname = strchr(molname, '.');
743 if ((endname == NULL) || (endname < molname))
744 length = strlen(molname);
745 else
746 length = strlen(molname) - strlen(endname);
747 cout << "Set name of molecule " << getId() << " to " << molname << endl;
748 strncpy(name, molname, length);
749 name[length]='\0';
750};
751
752/** Sets the molecule::cell_size to the components of \a *dim (rectangular box)
753 * \param *dim vector class
754 */
755void molecule::SetBoxDimension(Vector *dim)
756{
757 RealSpaceMatrix domain;
758 for(int i =0; i<NDIM;++i)
759 domain.at(i,i) = dim->at(i);
760 World::getInstance().setDomain(domain);
761};
762
763/** Removes atom from molecule list, but does not delete it.
764 * \param *pointer atom to be removed
765 * \return true - succeeded, false - atom not found in list
766 */
767bool molecule::UnlinkAtom(atom *pointer)
768{
769 if (pointer == NULL)
770 return false;
771 pointer->removeFromMolecule();
772 return true;
773};
774
775/** Removes every atom from molecule list.
776 * \return true - succeeded, false - atom not found in list
777 */
778bool molecule::CleanupMolecule()
779{
780 for (molecule::iterator iter = begin(); !empty(); iter = begin())
781 (*iter)->removeFromMolecule();
782 return empty();
783};
784
785/** Finds an atom specified by its continuous number.
786 * \param Nr number of atom withim molecule
787 * \return pointer to atom or NULL
788 */
789atom * molecule::FindAtom(int Nr) const
790{
791 LocalToGlobalId_t::const_iterator iter = LocalToGlobalId.find(Nr);
792 if (iter != LocalToGlobalId.end()) {
793 //LOG(0, "Found Atom Nr. " << walker->getNr());
794 return iter->second;
795 } else {
796 ELOG(1, "Atom with Nr " << Nr << " not found in molecule " << getName() << "'s list.");
797 return NULL;
798 }
799}
800
801/** Checks whether the given atom is a member of this molecule.
802 *
803 * We make use here of molecule::atomIds to get a result on
804 *
805 * @param _atom atom to check
806 * @return true - is member, false - is not
807 */
808bool molecule::isInMolecule(const atom * const _atom)
809{
810 ASSERT(_atom->getMolecule() == this,
811 "molecule::isInMolecule() - atom is not designated to be in molecule '"
812 +toString(this->getName())+"'.");
813 molecule::const_iterator iter = atomIds.find(_atom->getId());
814 return (iter != atomIds.end());
815}
816
817/** Asks for atom number, and checks whether in list.
818 * \param *text question before entering
819 */
820atom * molecule::AskAtom(std::string text)
821{
822 int No;
823 atom *ion = NULL;
824 do {
825 //std::cout << "============Atom list==========================" << std::endl;
826 //mol->Output((ofstream *)&cout);
827 //std::cout << "===============================================" << std::endl;
828 std::cout << text;
829 cin >> No;
830 ion = this->FindAtom(No);
831 } while (ion == NULL);
832 return ion;
833};
834
835/** Checks if given coordinates are within cell volume.
836 * \param *x array of coordinates
837 * \return true - is within, false - out of cell
838 */
839bool molecule::CheckBounds(const Vector *x) const
840{
841 const RealSpaceMatrix &domain = World::getInstance().getDomain().getM();
842 bool result = true;
843 for (int i=0;i<NDIM;i++) {
844 result = result && ((x->at(i) >= 0) && (x->at(i) < domain.at(i,i)));
845 }
846 //return result;
847 return true; /// probably not gonna use the check no more
848};
849
850/** Prints molecule to *out.
851 * \param *out output stream
852 */
853bool molecule::Output(ostream * const output) const
854{
855 if (output == NULL) {
856 return false;
857 } else {
858 int AtomNo[MAX_ELEMENTS];
859 memset(AtomNo,0,(MAX_ELEMENTS-1)*sizeof(*AtomNo));
860 enumeration<const element*> elementLookup = formula.enumerateElements();
861 *output << "#Ion_TypeNr._Nr.R[0] R[1] R[2] MoveType (0 MoveIon, 1 FixedIon)" << endl;
862 for_each(begin(),end(),boost::bind(&atom::OutputArrayIndexed,_1,output,elementLookup,AtomNo,(const char*)0));
863 return true;
864 }
865};
866
867/** Outputs contents of each atom::ListOfBonds.
868 * \param *out output stream
869 */
870void molecule::OutputListOfBonds() const
871{
872 std::stringstream output;
873 LOG(2, "From Contents of ListOfBonds, all atoms:");
874 for (molecule::const_iterator iter = begin();
875 iter != end();
876 ++iter) {
877 (*iter)->OutputBondOfAtom(output);
878 output << std::endl << "\t\t";
879 }
880 LOG(2, output.str());
881}
882
883/** Brings molecule::AtomCount and atom::*Name up-to-date.
884 * \param *out output stream for debugging
885 */
886size_t molecule::doCountNoNonHydrogen() const
887{
888 int temp = 0;
889 // go through atoms and look for new ones
890 for (molecule::const_iterator iter = begin(); iter != end(); ++iter)
891 if ((*iter)->getType()->getAtomicNumber() != 1) // count non-hydrogen atoms whilst at it
892 ++temp;
893 return temp;
894};
895
896/** Counts the number of present bonds.
897 * \return number of bonds
898 */
899int molecule::doCountBonds() const
900{
901 unsigned int counter = 0;
902 for(molecule::const_iterator AtomRunner = begin(); AtomRunner != end(); ++AtomRunner) {
903 const BondList& ListOfBonds = (*AtomRunner)->getListOfBonds();
904 for(BondList::const_iterator BondRunner = ListOfBonds.begin();
905 BondRunner != ListOfBonds.end();
906 ++BondRunner)
907 if ((*BondRunner)->leftatom == *AtomRunner)
908 counter++;
909 }
910 return counter;
911}
912
913
914/** Returns an index map for two father-son-molecules.
915 * The map tells which atom in this molecule corresponds to which one in the other molecul with their fathers.
916 * \param *out output stream for debugging
917 * \param *OtherMolecule corresponding molecule with fathers
918 * \return allocated map of size molecule::AtomCount with map
919 * \todo make this with a good sort O(n), not O(n^2)
920 */
921int * molecule::GetFatherSonAtomicMap(molecule *OtherMolecule)
922{
923 LOG(3, "Begin of GetFatherAtomicMap.");
924 int *AtomicMap = new int[getAtomCount()];
925 for (int i=getAtomCount();i--;)
926 AtomicMap[i] = -1;
927 if (OtherMolecule == this) { // same molecule
928 for (int i=getAtomCount();i--;) // no need as -1 means already that there is trivial correspondence
929 AtomicMap[i] = i;
930 LOG(4, "Map is trivial.");
931 } else {
932 std::stringstream output;
933 output << "Map is ";
934 for (molecule::const_iterator iter = begin(); iter != end(); ++iter) {
935 if ((*iter)->father == NULL) {
936 AtomicMap[(*iter)->getNr()] = -2;
937 } else {
938 for (molecule::const_iterator runner = OtherMolecule->begin(); runner != OtherMolecule->end(); ++runner) {
939 //for (int i=0;i<AtomCount;i++) { // search atom
940 //for (int j=0;j<OtherMolecule->getAtomCount();j++) {
941 //LOG(4, "Comparing father " << (*iter)->father << " with the other one " << (*runner)->father << ".");
942 if ((*iter)->father == (*runner))
943 AtomicMap[(*iter)->getNr()] = (*runner)->getNr();
944 }
945 }
946 output << AtomicMap[(*iter)->getNr()] << "\t";
947 }
948 LOG(4, output.str());
949 }
950 LOG(3, "End of GetFatherAtomicMap.");
951 return AtomicMap;
952};
953
954
955void molecule::flipActiveFlag(){
956 ActiveFlag = !ActiveFlag;
957}
958
959Shape molecule::getBoundingShape(const double boundary) const
960{
961 // get center and radius
962 Vector center;
963 double radius = 0.;
964 {
965 center.Zero();
966 for(const_iterator iter = begin(); iter != end(); ++iter)
967 center += (*iter)->getPosition();
968 center *= 1./(double)size();
969 for(const_iterator iter = begin(); iter != end(); ++iter) {
970 const Vector &position = (*iter)->getPosition();
971 const double temp_distance = position.DistanceSquared(center);
972 if (temp_distance > radius)
973 radius = temp_distance;
974 }
975 }
976 // convert radius to true value and add some small boundary
977 radius = sqrt(radius) + boundary + 1e+6*std::numeric_limits<double>::epsilon();
978 LOG(1, "INFO: The " << size() << " atoms of the molecule are contained in a sphere at "
979 << center << " with radius " << radius << ".");
980
981 // TODO: When we do not use a Sphere here anymore, then FillRegularGridAction will
982 // will not work as it expects a sphere due to possible random rotations.
983 Shape BoundingShape(Sphere(center, radius));
984 LOG(1, "INFO: Created sphere at " << BoundingShape.getCenter() << " and radius "
985 << BoundingShape.getRadius() << ".");
986 return BoundingShape;
987}
988
989// construct idpool
990CONSTRUCT_IDPOOL(atomId_t, continuousId)
991
Note: See TracBrowser for help on using the repository browser.