source: src/molecule.cpp@ 88c8ec

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 88c8ec was 88c8ec, checked in by Frederik Heber <heber@…>, 13 years ago

REFACTOR: Replaced all "bond *" appearances by bond::ptr.

  • this is preparatory for making bond::ptr a boost::shared_ptr of bond.
  • NOTE: We had to remove a const prefix at four or five places and forward declarations had to be replaced by the true inclusion of bond.hpp at tne or so files. Apart from that, the replacement has been very smooth.
  • Property mode set to 100755
File size: 38.1 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::ptr 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::ptr FirstBond = NULL;
336 bond::ptr SecondBond = NULL; // Other bonds in double bond case to determine "other" plane
337 atom *FirstOtherAtom = NULL, *SecondOtherAtom = NULL, *ThirdOtherAtom = NULL; // pointer to hydrogen atoms to be added
338 double b,l,d,f,g, alpha, factors[NDIM]; // hold temporary values in triple bond case for coordination determination
339 Vector Orthovector1, Orthovector2; // temporary vectors in coordination construction
340 Vector InBondvector; // vector in direction of *Bond
341 const RealSpaceMatrix &matrix = World::getInstance().getDomain().getM();
342 bond::ptr Binder = NULL;
343
344 // create vector in direction of bond
345 InBondvector = TopReplacement->getPosition() - TopOrigin->getPosition();
346 bondlength = InBondvector.Norm();
347
348 // is greater than typical bond distance? Then we have to correct periodically
349 // the problem is not the H being out of the box, but InBondvector have the wrong direction
350 // due to TopReplacement or Origin being on the wrong side!
351 const BondGraph * const BG = World::getInstance().getBondGraph();
352 const range<double> MinMaxBondDistance(
353 BG->getMinMaxDistance(TopOrigin,TopReplacement));
354 if (!MinMaxBondDistance.isInRange(bondlength)) {
355// LOG(4, "InBondvector is: " << InBondvector << ".");
356 Orthovector1.Zero();
357 for (int i=NDIM;i--;) {
358 l = TopReplacement->at(i) - TopOrigin->at(i);
359 if (fabs(l) > MinMaxBondDistance.last) { // is component greater than bond distance (check against min not useful here)
360 Orthovector1[i] = (l < 0) ? -1. : +1.;
361 } // (signs are correct, was tested!)
362 }
363 Orthovector1 *= matrix;
364 InBondvector -= Orthovector1; // subtract just the additional translation
365 bondlength = InBondvector.Norm();
366// LOG(4, "INFO: Corrected InBondvector is now: " << InBondvector << ".");
367 } // periodic correction finished
368
369 InBondvector.Normalize();
370 // get typical bond length and store as scale factor for later
371 ASSERT(TopOrigin->getType() != NULL, "AddHydrogenReplacementAtom: element of TopOrigin is not given.");
372 BondRescale = TopOrigin->getType()->getHBondDistance(TopBond->BondDegree-1);
373 if (BondRescale == -1) {
374 ELOG(1, "There is no typical hydrogen bond distance in replacing bond (" << TopOrigin->getName() << "<->" << TopReplacement->getName() << ") of degree " << TopBond->BondDegree << "!");
375 return false;
376 BondRescale = bondlength;
377 } else {
378 if (!IsAngstroem)
379 BondRescale /= (1.*AtomicLengthToAngstroem);
380 }
381
382 // discern single, double and triple bonds
383 switch(TopBond->BondDegree) {
384 case 1:
385 FirstOtherAtom = World::getInstance().createAtom(); // new atom
386 FirstOtherAtom->setType(1); // element is Hydrogen
387 FirstOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
388 FirstOtherAtom->setFixedIon(TopReplacement->getFixedIon());
389 if (TopReplacement->getType()->getAtomicNumber() == 1) { // neither rescale nor replace if it's already hydrogen
390 FirstOtherAtom->father = TopReplacement;
391 BondRescale = bondlength;
392 } else {
393 FirstOtherAtom->father = NULL; // if we replace hydrogen, we mark it as our father, otherwise we are just an added hydrogen with no father
394 }
395 InBondvector *= BondRescale; // rescale the distance vector to Hydrogen bond length
396 FirstOtherAtom->setPosition(TopOrigin->getPosition() + InBondvector); // set coordination to origin and add distance vector to replacement atom
397 AllWentWell = AllWentWell && AddAtom(FirstOtherAtom);
398// LOG(4, "INFO: Added " << *FirstOtherAtom << " at: " << FirstOtherAtom->x << ".");
399 Binder = AddBond(BottomOrigin, FirstOtherAtom, 1);
400 Binder->Cyclic = false;
401 Binder->Type = GraphEdge::TreeEdge;
402 break;
403 case 2:
404 {
405 // determine two other bonds (warning if there are more than two other) plus valence sanity check
406 const BondList& ListOfBonds = TopOrigin->getListOfBonds();
407 for (BondList::const_iterator Runner = ListOfBonds.begin();
408 Runner != ListOfBonds.end();
409 ++Runner) {
410 if ((*Runner) != TopBond) {
411 if (FirstBond == NULL) {
412 FirstBond = (*Runner);
413 FirstOtherAtom = (*Runner)->GetOtherAtom(TopOrigin);
414 } else if (SecondBond == NULL) {
415 SecondBond = (*Runner);
416 SecondOtherAtom = (*Runner)->GetOtherAtom(TopOrigin);
417 } else {
418 ELOG(2, "Detected more than four bonds for atom " << TopOrigin->getName());
419 }
420 }
421 }
422 }
423 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)
424 SecondBond = TopBond;
425 SecondOtherAtom = TopReplacement;
426 }
427 if (FirstOtherAtom != NULL) { // then we just have this double bond and the plane does not matter at all
428// 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.");
429
430 // determine the plane of these two with the *origin
431 try {
432 Orthovector1 = Plane(TopOrigin->getPosition(), FirstOtherAtom->getPosition(), SecondOtherAtom->getPosition()).getNormal();
433 }
434 catch(LinearDependenceException &excp){
435 LOG(0, boost::diagnostic_information(excp));
436 // TODO: figure out what to do with the Orthovector in this case
437 AllWentWell = false;
438 }
439 } else {
440 Orthovector1.GetOneNormalVector(InBondvector);
441 }
442 //LOG(3, "INFO: Orthovector1: " << Orthovector1 << ".");
443 // orthogonal vector and bond vector between origin and replacement form the new plane
444 Orthovector1.MakeNormalTo(InBondvector);
445 Orthovector1.Normalize();
446 //LOG(3, "ReScaleCheck: " << Orthovector1.Norm() << " and " << InBondvector.Norm() << ".");
447
448 // create the two Hydrogens ...
449 FirstOtherAtom = World::getInstance().createAtom();
450 SecondOtherAtom = World::getInstance().createAtom();
451 FirstOtherAtom->setType(1);
452 SecondOtherAtom->setType(1);
453 FirstOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
454 FirstOtherAtom->setFixedIon(TopReplacement->getFixedIon());
455 SecondOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
456 SecondOtherAtom->setFixedIon(TopReplacement->getFixedIon());
457 FirstOtherAtom->father = NULL; // we are just an added hydrogen with no father
458 SecondOtherAtom->father = NULL; // we are just an added hydrogen with no father
459 bondangle = TopOrigin->getType()->getHBondAngle(1);
460 if (bondangle == -1) {
461 ELOG(1, "There is no typical hydrogen bond angle in replacing bond (" << TopOrigin->getName() << "<->" << TopReplacement->getName() << ") of degree " << TopBond->BondDegree << "!");
462 return false;
463 bondangle = 0;
464 }
465 bondangle *= M_PI/180./2.;
466// LOG(3, "INFO: ReScaleCheck: InBondvector " << InBondvector << ", " << Orthovector1 << ".");
467// LOG(3, "Half the bond angle is " << bondangle << ", sin and cos of it: " << sin(bondangle) << ", " << cos(bondangle));
468 FirstOtherAtom->Zero();
469 SecondOtherAtom->Zero();
470 for(int i=NDIM;i--;) { // rotate by half the bond angle in both directions (InBondvector is bondangle = 0 direction)
471 FirstOtherAtom->set(i, InBondvector[i] * cos(bondangle) + Orthovector1[i] * (sin(bondangle)));
472 SecondOtherAtom->set(i, InBondvector[i] * cos(bondangle) + Orthovector1[i] * (-sin(bondangle)));
473 }
474 FirstOtherAtom->Scale(BondRescale); // rescale by correct BondDistance
475 SecondOtherAtom->Scale(BondRescale);
476 //LOG(3, "ReScaleCheck: " << FirstOtherAtom->x.Norm() << " and " << SecondOtherAtom->x.Norm() << ".");
477 *FirstOtherAtom += TopOrigin->getPosition();
478 *SecondOtherAtom += TopOrigin->getPosition();
479 // ... and add to molecule
480 AllWentWell = AllWentWell && AddAtom(FirstOtherAtom);
481 AllWentWell = AllWentWell && AddAtom(SecondOtherAtom);
482// LOG(4, "INFO: Added " << *FirstOtherAtom << " at: " << FirstOtherAtom->x << ".");
483// LOG(4, "INFO: Added " << *SecondOtherAtom << " at: " << SecondOtherAtom->x << ".");
484 Binder = AddBond(BottomOrigin, FirstOtherAtom, 1);
485 Binder->Cyclic = false;
486 Binder->Type = GraphEdge::TreeEdge;
487 Binder = AddBond(BottomOrigin, SecondOtherAtom, 1);
488 Binder->Cyclic = false;
489 Binder->Type = GraphEdge::TreeEdge;
490 break;
491 case 3:
492 // take the "usual" tetraoidal angle and add the three Hydrogen in direction of the bond (height of the tetraoid)
493 FirstOtherAtom = World::getInstance().createAtom();
494 SecondOtherAtom = World::getInstance().createAtom();
495 ThirdOtherAtom = World::getInstance().createAtom();
496 FirstOtherAtom->setType(1);
497 SecondOtherAtom->setType(1);
498 ThirdOtherAtom->setType(1);
499 FirstOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
500 FirstOtherAtom->setFixedIon(TopReplacement->getFixedIon());
501 SecondOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
502 SecondOtherAtom->setFixedIon(TopReplacement->getFixedIon());
503 ThirdOtherAtom->setAtomicVelocity(TopReplacement->getAtomicVelocity()); // copy velocity
504 ThirdOtherAtom->setFixedIon(TopReplacement->getFixedIon());
505 FirstOtherAtom->father = NULL; // we are just an added hydrogen with no father
506 SecondOtherAtom->father = NULL; // we are just an added hydrogen with no father
507 ThirdOtherAtom->father = NULL; // we are just an added hydrogen with no father
508
509 // we need to vectors orthonormal the InBondvector
510 AllWentWell = AllWentWell && Orthovector1.GetOneNormalVector(InBondvector);
511// LOG(3, "INFO: Orthovector1: " << Orthovector1 << ".");
512 try{
513 Orthovector2 = Plane(InBondvector, Orthovector1,0).getNormal();
514 }
515 catch(LinearDependenceException &excp) {
516 LOG(0, boost::diagnostic_information(excp));
517 AllWentWell = false;
518 }
519// LOG(3, "INFO: Orthovector2: " << Orthovector2 << ".")
520
521 // create correct coordination for the three atoms
522 alpha = (TopOrigin->getType()->getHBondAngle(2))/180.*M_PI/2.; // retrieve triple bond angle from database
523 l = BondRescale; // desired bond length
524 b = 2.*l*sin(alpha); // base length of isosceles triangle
525 d = l*sqrt(cos(alpha)*cos(alpha) - sin(alpha)*sin(alpha)/3.); // length for InBondvector
526 f = b/sqrt(3.); // length for Orthvector1
527 g = b/2.; // length for Orthvector2
528// LOG(3, "Bond length and half-angle: " << l << ", " << alpha << "\t (b,d,f,g) = " << b << ", " << d << ", " << f << ", " << g << ", ");
529// 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));
530 factors[0] = d;
531 factors[1] = f;
532 factors[2] = 0.;
533 FirstOtherAtom->LinearCombinationOfVectors(InBondvector, Orthovector1, Orthovector2, factors);
534 factors[1] = -0.5*f;
535 factors[2] = g;
536 SecondOtherAtom->LinearCombinationOfVectors(InBondvector, Orthovector1, Orthovector2, factors);
537 factors[2] = -g;
538 ThirdOtherAtom->LinearCombinationOfVectors(InBondvector, Orthovector1, Orthovector2, factors);
539
540 // rescale each to correct BondDistance
541// FirstOtherAtom->x.Scale(&BondRescale);
542// SecondOtherAtom->x.Scale(&BondRescale);
543// ThirdOtherAtom->x.Scale(&BondRescale);
544
545 // and relative to *origin atom
546 *FirstOtherAtom += TopOrigin->getPosition();
547 *SecondOtherAtom += TopOrigin->getPosition();
548 *ThirdOtherAtom += TopOrigin->getPosition();
549
550 // ... and add to molecule
551 AllWentWell = AllWentWell && AddAtom(FirstOtherAtom);
552 AllWentWell = AllWentWell && AddAtom(SecondOtherAtom);
553 AllWentWell = AllWentWell && AddAtom(ThirdOtherAtom);
554// LOG(4, "INFO: Added " << *FirstOtherAtom << " at: " << FirstOtherAtom->x << ".");
555// LOG(4, "INFO: Added " << *SecondOtherAtom << " at: " << SecondOtherAtom->x << ".");
556// LOG(4, "INFO: Added " << *ThirdOtherAtom << " at: " << ThirdOtherAtom->x << ".");
557 Binder = AddBond(BottomOrigin, FirstOtherAtom, 1);
558 Binder->Cyclic = false;
559 Binder->Type = GraphEdge::TreeEdge;
560 Binder = AddBond(BottomOrigin, SecondOtherAtom, 1);
561 Binder->Cyclic = false;
562 Binder->Type = GraphEdge::TreeEdge;
563 Binder = AddBond(BottomOrigin, ThirdOtherAtom, 1);
564 Binder->Cyclic = false;
565 Binder->Type = GraphEdge::TreeEdge;
566 break;
567 default:
568 ELOG(1, "BondDegree does not state single, double or triple bond!");
569 AllWentWell = false;
570 break;
571 }
572
573 return AllWentWell;
574};
575
576/** Creates a copy of this molecule.
577 * \param offset translation Vector for the new molecule relative to old one
578 * \return copy of molecule
579 */
580molecule *molecule::CopyMolecule(const Vector &offset) const
581{
582 molecule *copy = World::getInstance().createMolecule();
583
584 // copy all atoms
585 std::map< const atom *, atom *> FatherFinder;
586 for (iterator iter = begin(); iter != end(); ++iter) {
587 atom * const copy_atom = copy->AddCopyAtom(*iter);
588 copy_atom->setPosition(copy_atom->getPosition() + offset);
589 FatherFinder.insert( std::make_pair( *iter, copy_atom ) );
590 }
591
592 // copy all bonds
593 for(const_iterator AtomRunner = begin(); AtomRunner != end(); ++AtomRunner) {
594 const BondList& ListOfBonds = (*AtomRunner)->getListOfBonds();
595 for(BondList::const_iterator BondRunner = ListOfBonds.begin();
596 BondRunner != ListOfBonds.end();
597 ++BondRunner)
598 if ((*BondRunner)->leftatom == *AtomRunner) {
599 bond::ptr Binder = (*BondRunner);
600 // get the pendant atoms of current bond in the copy molecule
601 ASSERT(FatherFinder.count(Binder->leftatom),
602 "molecule::CopyMolecule() - No copy of original left atom "
603 +toString(Binder->leftatom)+" for bond copy found");
604 ASSERT(FatherFinder.count(Binder->rightatom),
605 "molecule::CopyMolecule() - No copy of original right atom "
606 +toString(Binder->rightatom)+" for bond copy found");
607 atom * const LeftAtom = FatherFinder[Binder->leftatom];
608 atom * const RightAtom = FatherFinder[Binder->rightatom];
609
610 bond::ptr const NewBond = copy->AddBond(LeftAtom, RightAtom, Binder->BondDegree);
611 NewBond->Cyclic = Binder->Cyclic;
612 if (Binder->Cyclic)
613 copy->NoCyclicBonds++;
614 NewBond->Type = Binder->Type;
615 }
616 }
617 // correct fathers
618 //for_each(begin(),end(),mem_fun(&atom::CorrectFather));
619
620 return copy;
621};
622
623
624/** Destroys all atoms inside this molecule.
625 */
626void molecule::removeAtomsinMolecule()
627{
628 // remove each atom from world
629 for(iterator AtomRunner = begin(); !empty(); AtomRunner = begin())
630 World::getInstance().destroyAtom(*AtomRunner);
631};
632
633
634/**
635 * Copies all atoms of a molecule which are within the defined parallelepiped.
636 *
637 * @param offest for the origin of the parallelepiped
638 * @param three vectors forming the matrix that defines the shape of the parallelpiped
639 */
640molecule* molecule::CopyMoleculeFromSubRegion(const Shape &region) const {
641 molecule *copy = World::getInstance().createMolecule();
642
643 // copy all atoms
644 std::map< const atom *, atom *> FatherFinder;
645 for (iterator iter = begin(); iter != end(); ++iter) {
646 if((*iter)->IsInShape(region)){
647 atom * const copy_atom = copy->AddCopyAtom(*iter);
648 FatherFinder.insert( std::make_pair( *iter, copy_atom ) );
649 }
650 }
651
652 // copy all bonds
653 for(molecule::const_iterator AtomRunner = begin(); AtomRunner != end(); ++AtomRunner) {
654 const BondList& ListOfBonds = (*AtomRunner)->getListOfBonds();
655 for(BondList::const_iterator BondRunner = ListOfBonds.begin();
656 BondRunner != ListOfBonds.end();
657 ++BondRunner)
658 if ((*BondRunner)->leftatom == *AtomRunner) {
659 bond::ptr Binder = (*BondRunner);
660 if ((FatherFinder.count(Binder->leftatom))
661 && (FatherFinder.count(Binder->rightatom))) {
662 // if copy present, then it must be from subregion
663 atom * const LeftAtom = FatherFinder[Binder->leftatom];
664 atom * const RightAtom = FatherFinder[Binder->rightatom];
665
666 bond::ptr const NewBond = copy->AddBond(LeftAtom, RightAtom, Binder->BondDegree);
667 NewBond->Cyclic = Binder->Cyclic;
668 if (Binder->Cyclic)
669 copy->NoCyclicBonds++;
670 NewBond->Type = Binder->Type;
671 }
672 }
673 }
674 // correct fathers
675 //for_each(begin(),end(),mem_fun(&atom::CorrectFather));
676
677 //TODO: copy->BuildInducedSubgraph(this);
678
679 return copy;
680}
681
682/** Adds a bond to a the molecule specified by two atoms, \a *first and \a *second.
683 * Also updates molecule::BondCount and molecule::NoNonBonds.
684 * \param *first first atom in bond
685 * \param *second atom in bond
686 * \return pointer to bond or NULL on failure
687 */
688bond::ptr molecule::AddBond(atom *atom1, atom *atom2, int degree)
689{
690 bond::ptr Binder = NULL;
691
692 // some checks to make sure we are able to create the bond
693 ASSERT(atom1,
694 "molecule::AddBond() - First atom "+toString(atom1)
695 +" is not a invalid pointer");
696 ASSERT(atom2,
697 "molecule::AddBond() - Second atom "+toString(atom2)
698 +" is not a invalid pointer");
699 ASSERT(isInMolecule(atom1),
700 "molecule::AddBond() - First atom "+toString(atom1)
701 +" is not part of molecule");
702 ASSERT(isInMolecule(atom2),
703 "molecule::AddBond() - Second atom "+toString(atom2)
704 +" is not part of molecule");
705
706 Binder = new bond(atom1, atom2, degree);
707 atom1->RegisterBond(WorldTime::getTime(), Binder);
708 atom2->RegisterBond(WorldTime::getTime(), Binder);
709 if ((atom1->getType() != NULL)
710 && (atom1->getType()->getAtomicNumber() != 1)
711 && (atom2->getType() != NULL)
712 && (atom2->getType()->getAtomicNumber() != 1))
713 NoNonBonds++;
714
715 return Binder;
716};
717
718/** Remove bond from bond chain list and from the both atom::ListOfBonds.
719 * Bond::~Bond takes care of bond removal
720 * \param *pointer bond pointer
721 * \return true - bound found and removed, false - bond not found/removed
722 */
723bool molecule::RemoveBond(bond::ptr pointer)
724{
725 //ELOG(1, "molecule::RemoveBond: Function not implemented yet.");
726 delete(pointer);
727 return true;
728};
729
730/** Remove every bond from bond chain list that atom \a *BondPartner is a constituent of.
731 * \todo Function not implemented yet
732 * \param *BondPartner atom to be removed
733 * \return true - bounds found and removed, false - bonds not found/removed
734 */
735bool molecule::RemoveBonds(atom *BondPartner)
736{
737 //ELOG(1, "molecule::RemoveBond: Function not implemented yet.");
738 BondPartner->removeAllBonds();
739 return false;
740};
741
742/** Set molecule::name from the basename without suffix in the given \a *filename.
743 * \param *filename filename
744 */
745void molecule::SetNameFromFilename(const char *filename)
746{
747 OBSERVE;
748 int length = 0;
749 const char *molname = strrchr(filename, '/');
750 if (molname != NULL)
751 molname += sizeof(char); // search for filename without dirs
752 else
753 molname = filename; // contains no slashes
754 const char *endname = strchr(molname, '.');
755 if ((endname == NULL) || (endname < molname))
756 length = strlen(molname);
757 else
758 length = strlen(molname) - strlen(endname);
759 cout << "Set name of molecule " << getId() << " to " << molname << endl;
760 strncpy(name, molname, length);
761 name[length]='\0';
762};
763
764/** Sets the molecule::cell_size to the components of \a *dim (rectangular box)
765 * \param *dim vector class
766 */
767void molecule::SetBoxDimension(Vector *dim)
768{
769 RealSpaceMatrix domain;
770 for(int i =0; i<NDIM;++i)
771 domain.at(i,i) = dim->at(i);
772 World::getInstance().setDomain(domain);
773};
774
775/** Removes atom from molecule list and removes all of its bonds.
776 * \param *pointer atom to be removed
777 * \return true - succeeded, false - atom not found in list
778 */
779bool molecule::RemoveAtom(atom *pointer)
780{
781 ASSERT(pointer, "Null pointer passed to molecule::RemoveAtom().");
782 RemoveBonds(pointer);
783 pointer->removeFromMolecule();
784 return true;
785};
786
787/** Removes atom from molecule list, but does not delete it.
788 * \param *pointer atom to be removed
789 * \return true - succeeded, false - atom not found in list
790 */
791bool molecule::UnlinkAtom(atom *pointer)
792{
793 if (pointer == NULL)
794 return false;
795 pointer->removeFromMolecule();
796 return true;
797};
798
799/** Removes every atom from molecule list.
800 * \return true - succeeded, false - atom not found in list
801 */
802bool molecule::CleanupMolecule()
803{
804 for (molecule::iterator iter = begin(); !empty(); iter = begin())
805 (*iter)->removeFromMolecule();
806 return empty();
807};
808
809/** Finds an atom specified by its continuous number.
810 * \param Nr number of atom withim molecule
811 * \return pointer to atom or NULL
812 */
813atom * molecule::FindAtom(int Nr) const
814{
815 molecule::iterator iter = begin();
816 for (; iter != end(); ++iter)
817 if ((*iter)->getNr() == Nr)
818 break;
819 if (iter != end()) {
820 //LOG(0, "Found Atom Nr. " << walker->getNr());
821 return (*iter);
822 } else {
823 ELOG(1, "Atom with Nr " << Nr << " not found in molecule " << getName() << "'s list.");
824 return NULL;
825 }
826}
827
828/** Checks whether the given atom is a member of this molecule.
829 *
830 * We make use here of molecule::atomIds to get a result on
831 *
832 * @param _atom atom to check
833 * @return true - is member, false - is not
834 */
835bool molecule::isInMolecule(const atom * const _atom)
836{
837 ASSERT(_atom->getMolecule() == this,
838 "molecule::isInMolecule() - atom is not designated to be in molecule '"
839 +toString(this->getName())+"'.");
840 molecule::const_iterator iter = atomIds.find(_atom->getId());
841 return (iter != atomIds.end());
842}
843
844/** Asks for atom number, and checks whether in list.
845 * \param *text question before entering
846 */
847atom * molecule::AskAtom(std::string text)
848{
849 int No;
850 atom *ion = NULL;
851 do {
852 //std::cout << "============Atom list==========================" << std::endl;
853 //mol->Output((ofstream *)&cout);
854 //std::cout << "===============================================" << std::endl;
855 std::cout << text;
856 cin >> No;
857 ion = this->FindAtom(No);
858 } while (ion == NULL);
859 return ion;
860};
861
862/** Checks if given coordinates are within cell volume.
863 * \param *x array of coordinates
864 * \return true - is within, false - out of cell
865 */
866bool molecule::CheckBounds(const Vector *x) const
867{
868 const RealSpaceMatrix &domain = World::getInstance().getDomain().getM();
869 bool result = true;
870 for (int i=0;i<NDIM;i++) {
871 result = result && ((x->at(i) >= 0) && (x->at(i) < domain.at(i,i)));
872 }
873 //return result;
874 return true; /// probably not gonna use the check no more
875};
876
877/** Prints molecule to *out.
878 * \param *out output stream
879 */
880bool molecule::Output(ostream * const output) const
881{
882 if (output == NULL) {
883 return false;
884 } else {
885 int AtomNo[MAX_ELEMENTS];
886 memset(AtomNo,0,(MAX_ELEMENTS-1)*sizeof(*AtomNo));
887 enumeration<const element*> elementLookup = formula.enumerateElements();
888 *output << "#Ion_TypeNr._Nr.R[0] R[1] R[2] MoveType (0 MoveIon, 1 FixedIon)" << endl;
889 for_each(begin(),end(),boost::bind(&atom::OutputArrayIndexed,_1,output,elementLookup,AtomNo,(const char*)0));
890 return true;
891 }
892};
893
894/** Outputs contents of each atom::ListOfBonds.
895 * \param *out output stream
896 */
897void molecule::OutputListOfBonds() const
898{
899 std::stringstream output;
900 LOG(2, "From Contents of ListOfBonds, all atoms:");
901 for (molecule::const_iterator iter = begin();
902 iter != end();
903 ++iter) {
904 (*iter)->OutputBondOfAtom(output);
905 output << std::endl << "\t\t";
906 }
907 LOG(2, output.str());
908}
909
910/** Brings molecule::AtomCount and atom::*Name up-to-date.
911 * \param *out output stream for debugging
912 */
913size_t molecule::doCountNoNonHydrogen() const
914{
915 int temp = 0;
916 // go through atoms and look for new ones
917 for (molecule::const_iterator iter = begin(); iter != end(); ++iter)
918 if ((*iter)->getType()->getAtomicNumber() != 1) // count non-hydrogen atoms whilst at it
919 ++temp;
920 return temp;
921};
922
923/** Counts the number of present bonds.
924 * \return number of bonds
925 */
926int molecule::doCountBonds() const
927{
928 unsigned int counter = 0;
929 for(molecule::const_iterator AtomRunner = begin(); AtomRunner != end(); ++AtomRunner) {
930 const BondList& ListOfBonds = (*AtomRunner)->getListOfBonds();
931 for(BondList::const_iterator BondRunner = ListOfBonds.begin();
932 BondRunner != ListOfBonds.end();
933 ++BondRunner)
934 if ((*BondRunner)->leftatom == *AtomRunner)
935 counter++;
936 }
937 return counter;
938}
939
940
941/** Returns an index map for two father-son-molecules.
942 * The map tells which atom in this molecule corresponds to which one in the other molecul with their fathers.
943 * \param *out output stream for debugging
944 * \param *OtherMolecule corresponding molecule with fathers
945 * \return allocated map of size molecule::AtomCount with map
946 * \todo make this with a good sort O(n), not O(n^2)
947 */
948int * molecule::GetFatherSonAtomicMap(molecule *OtherMolecule)
949{
950 LOG(3, "Begin of GetFatherAtomicMap.");
951 int *AtomicMap = new int[getAtomCount()];
952 for (int i=getAtomCount();i--;)
953 AtomicMap[i] = -1;
954 if (OtherMolecule == this) { // same molecule
955 for (int i=getAtomCount();i--;) // no need as -1 means already that there is trivial correspondence
956 AtomicMap[i] = i;
957 LOG(4, "Map is trivial.");
958 } else {
959 std::stringstream output;
960 output << "Map is ";
961 for (molecule::const_iterator iter = begin(); iter != end(); ++iter) {
962 if ((*iter)->father == NULL) {
963 AtomicMap[(*iter)->getNr()] = -2;
964 } else {
965 for (molecule::const_iterator runner = OtherMolecule->begin(); runner != OtherMolecule->end(); ++runner) {
966 //for (int i=0;i<AtomCount;i++) { // search atom
967 //for (int j=0;j<OtherMolecule->getAtomCount();j++) {
968 //LOG(4, "Comparing father " << (*iter)->father << " with the other one " << (*runner)->father << ".");
969 if ((*iter)->father == (*runner))
970 AtomicMap[(*iter)->getNr()] = (*runner)->getNr();
971 }
972 }
973 output << AtomicMap[(*iter)->getNr()] << "\t";
974 }
975 LOG(4, output.str());
976 }
977 LOG(3, "End of GetFatherAtomicMap.");
978 return AtomicMap;
979};
980
981
982void molecule::flipActiveFlag(){
983 ActiveFlag = !ActiveFlag;
984}
985
986Shape molecule::getBoundingShape(const double boundary) const
987{
988 // get center and radius
989 Vector center;
990 double radius = 0.;
991 {
992 center.Zero();
993 for(const_iterator iter = begin(); iter != end(); ++iter)
994 center += (*iter)->getPosition();
995 center *= 1./(double)size();
996 for(const_iterator iter = begin(); iter != end(); ++iter) {
997 const Vector &position = (*iter)->getPosition();
998 const double temp_distance = position.DistanceSquared(center);
999 if (temp_distance > radius)
1000 radius = temp_distance;
1001 }
1002 }
1003 // convert radius to true value and add some small boundary
1004 radius = sqrt(radius) + boundary + 1e+6*std::numeric_limits<double>::epsilon();
1005 LOG(1, "INFO: The " << size() << " atoms of the molecule are contained in a sphere at "
1006 << center << " with radius " << radius << ".");
1007
1008 // TODO: When we do not use a Sphere here anymore, then FillRegularGridAction will
1009 // will not work as it expects a sphere due to possible random rotations.
1010 Shape BoundingShape(Sphere(center, radius));
1011 LOG(1, "INFO: Created sphere at " << BoundingShape.getCenter() << " and radius "
1012 << BoundingShape.getRadius() << ".");
1013 return BoundingShape;
1014}
1015
1016// construct idpool
1017CONSTRUCT_IDPOOL(atomId_t, continuousId)
1018
Note: See TracBrowser for help on using the repository browser.