source: src/molecule.cpp@ 36bb2d

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 36bb2d was 94d5ac6, checked in by Frederik Heber <heber@…>, 12 years ago

FIX: As we use GSL internally, we are as of now required to use GPL v2 license.

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