source: src/molecule.cpp@ 24edfe

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 Candidate_v1.7.0 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 24edfe was 24edfe, checked in by Frederik Heber <heber@…>, 11 years ago

Added AboutToBeRemoved channel to molecule.

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