/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2010 University of Bonn. All rights reserved. * Please see the LICENSE file or "Copyright notice" in builder.cpp for details. */ /* * World.cpp * * Created on: Feb 3, 2010 * Author: crueger */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "CodePatterns/MemDebug.hpp" #include "World.hpp" #include #include "Actions/ActionTraits.hpp" #include "Actions/ManipulateAtomsProcess.hpp" #include "atom.hpp" #include "Graph/BondGraph.hpp" #include "Box.hpp" #include "CodePatterns/Assert.hpp" #include "config.hpp" #include "Descriptors/AtomDescriptor.hpp" #include "Descriptors/AtomDescriptor_impl.hpp" #include "Descriptors/MoleculeDescriptor.hpp" #include "Descriptors/MoleculeDescriptor_impl.hpp" #include "Descriptors/SelectiveIterator_impl.hpp" #include "Element/periodentafel.hpp" #include "Graph/DepthFirstSearchAnalysis.hpp" #include "Helpers/defs.hpp" #include "LinearAlgebra/RealSpaceMatrix.hpp" #include "molecule.hpp" #include "MoleculeListClass.hpp" #include "ThermoStatContainer.hpp" #include "WorldTime.hpp" #include "CodePatterns/Singleton_impl.hpp" #include "CodePatterns/ObservedContainer_impl.hpp" using namespace MoleCuilder; const unsigned int MAX_POOL_FRAGMENTATION=20; const unsigned int MAX_FRAGMENTATION_SKIPS=100; /******************************* Notifications ************************/ atom* World::_lastchangedatom = NULL; molecule* World::_lastchangedmol = NULL; /******************************* getter and setter ************************/ periodentafel *&World::getPeriode() { return periode; } BondGraph *&World::getBondGraph() { return BG; } void World::setBondGraph(BondGraph *_BG){ delete (BG); BG = _BG; } config *&World::getConfig(){ return configuration; } // Atoms atom* World::getAtom(AtomDescriptor descriptor){ return descriptor.find(); } World::AtomComposite World::getAllAtoms(AtomDescriptor descriptor){ return descriptor.findAll(); } World::AtomComposite World::getAllAtoms(){ return getAllAtoms(AllAtoms()); } int World::numAtoms(){ return atoms.size(); } // Molecules molecule *World::getMolecule(MoleculeDescriptor descriptor){ return descriptor.find(); } std::vector World::getAllMolecules(MoleculeDescriptor descriptor){ return descriptor.findAll(); } std::vector World::getAllMolecules(){ return getAllMolecules(AllMolecules()); } int World::numMolecules(){ return molecules_deprecated->ListOfMolecules.size(); } // system Box& World::getDomain() { return *cell_size; } void World::setDomain(const RealSpaceMatrix &mat){ OBSERVE; *cell_size = mat; } void World::setDomain(double * matrix) { OBSERVE; RealSpaceMatrix M = ReturnFullMatrixforSymmetric(matrix); cell_size->setM(M); } void World::setTime(const unsigned int _step) { if (_step != WorldTime::getTime()) { // set new time WorldTime::setTime(_step); // TODO: removed when BondGraph creates the adjacency // 1. remove all of World's molecules for (MoleculeIterator iter = getMoleculeIter(); getMoleculeIter() != moleculeEnd(); iter = getMoleculeIter()) { getMolecules()->erase(*iter); destroyMolecule(*iter); } // 2. (re-)create bondgraph AtomComposite Set = getAllAtoms(); BG->CreateAdjacency(Set); // 3. scan for connected subgraphs => molecules DepthFirstSearchAnalysis DFS; DFS(); DFS.UpdateMoleculeStructure(); } } std::string World::getDefaultName() { return defaultName; } void World::setDefaultName(std::string name) { OBSERVE; defaultName = name; }; class ThermoStatContainer * World::getThermostats() { return Thermostats; } int World::getExitFlag() { return ExitFlag; } void World::setExitFlag(int flag) { if (ExitFlag < flag) ExitFlag = flag; } /******************** Methods to change World state *********************/ molecule* World::createMolecule(){ OBSERVE; molecule *mol = NULL; mol = NewMolecule(); moleculeId_t id = getNextMoleculeId(); ASSERT(!molecules.count(id),"proposed id did not specify an unused ID"); mol->setId(id); // store the molecule by ID molecules[mol->getId()] = mol; mol->signOn(this); _lastchangedmol = mol; NOTIFY(MoleculeInserted); return mol; } void World::destroyMolecule(molecule* mol){ OBSERVE; ASSERT(mol,"Molecule that was meant to be destroyed did not exist"); destroyMolecule(mol->getId()); } void World::destroyMolecule(moleculeId_t id){ molecule *mol = molecules[id]; ASSERT(mol,"Molecule id that was meant to be destroyed did not exist"); // give notice about immediate removal { OBSERVE; _lastchangedmol = mol; NOTIFY(MoleculeRemoved); } DeleteMolecule(mol); if (isMoleculeSelected(id)) selectedMolecules.erase(id); molecules.erase(id); releaseMoleculeId(id); } atom *World::createAtom(){ OBSERVE; atomId_t id = getNextAtomId(); ASSERT(!atoms.count(id),"proposed id did not specify an unused ID"); atom *res = NewAtom(id); res->setWorld(this); // store the atom by ID atoms[res->getId()] = res; _lastchangedatom = res; NOTIFY(AtomInserted); return res; } int World::registerAtom(atom *atom){ OBSERVE; atomId_t id = getNextAtomId(); atom->setId(id); atom->setWorld(this); atoms[atom->getId()] = atom; return atom->getId(); } void World::destroyAtom(atom* atom){ int id = atom->getId(); destroyAtom(id); } void World::destroyAtom(atomId_t id) { atom *atom = atoms[id]; ASSERT(atom,"Atom ID that was meant to be destroyed did not exist"); // give notice about immediate removal { OBSERVE; _lastchangedatom = atom; NOTIFY(AtomRemoved); } DeleteAtom(atom); if (isAtomSelected(id)) selectedAtoms.erase(id); atoms.erase(id); releaseAtomId(id); } bool World::changeAtomId(atomId_t oldId, atomId_t newId, atom* target){ OBSERVE; // in case this call did not originate from inside the atom, we redirect it, // to also let it know that it has changed if(!target){ target = atoms[oldId]; ASSERT(target,"Atom with that ID not found"); return target->changeId(newId); } else{ if(reserveAtomId(newId)){ atoms.erase(oldId); atoms.insert(pair(newId,target)); return true; } else{ return false; } } } bool World::changeMoleculeId(moleculeId_t oldId, moleculeId_t newId, molecule* target){ OBSERVE; // in case this call did not originate from inside the atom, we redirect it, // to also let it know that it has changed if(!target){ target = molecules[oldId]; ASSERT(target,"Molecule with that ID not found"); return target->changeId(newId); } else{ if(reserveMoleculeId(newId)){ molecules.erase(oldId); molecules.insert(pair(newId,target)); return true; } else{ return false; } } } ManipulateAtomsProcess* World::manipulateAtoms(boost::function op,std::string name,AtomDescriptor descr){ ActionTraits manipulateTrait(name); return new ManipulateAtomsProcess(op, descr,manipulateTrait,false); } ManipulateAtomsProcess* World::manipulateAtoms(boost::function op,std::string name){ return manipulateAtoms(op,name,AllAtoms()); } /********************* Internal Change methods for double Callback and Observer mechanism ********/ void World::doManipulate(ManipulateAtomsProcess *proc){ proc->signOn(this); { OBSERVE; proc->doManipulate(this); } proc->signOff(this); } /******************************* IDManagement *****************************/ // Atoms atomId_t World::getNextAtomId(){ // try to find an Id in the pool; if(!atomIdPool.empty()){ atomIdPool_t::iterator iter=atomIdPool.begin(); atomId_t id = iter->first; range newRange = makeRange(id+1,iter->last); // we wont use this iterator anymore, so we don't care about invalidating atomIdPool.erase(iter); if(newRange.first=currAtomId ){ range newRange = makeRange(currAtomId,id); if(newRange.firstisBefore(id)){ // we have covered all available ranges... nothing to be found here break; } // no need to check first, since it has to be <=id, since otherwise we would have broken out if(!iter->isBeyond(id)){ // we found a matching range... get the id from this range // split up this range at the point of id range bottomRange = makeRange(iter->first,id); range topRange = makeRange(id+1,iter->last); // remove this range atomIdPool.erase(iter); if(bottomRange.firstfirst==iter->last)){ // merge the two ranges range newRange = makeRange(iter->first,next->last); atomIdPool.erase(iter); atomIdPool.erase(next); pair res = atomIdPool.insert(newRange); ASSERT(res.second,"Id-Pool was confused"); iter=res.first; continue; } ++iter; } if(!atomIdPool.empty()){ // check if the last range is at the border atomIdPool_t::iterator iter = atomIdPool.end(); iter--; if(iter->last==currAtomId){ currAtomId=iter->first; atomIdPool.erase(iter); } } lastAtomPoolSize=atomIdPool.size(); numAtomDefragSkips=0; } // Molecules moleculeId_t World::getNextMoleculeId(){ // try to find an Id in the pool; if(!moleculeIdPool.empty()){ moleculeIdPool_t::iterator iter=moleculeIdPool.begin(); moleculeId_t id = iter->first; range newRange = makeRange(id+1,iter->last); // we wont use this iterator anymore, so we don't care about invalidating moleculeIdPool.erase(iter); if(newRange.first=currMoleculeId ){ range newRange = makeRange(currMoleculeId,id); if(newRange.firstisBefore(id)){ // we have coverd all available ranges... nothing to be found here break; } // no need to check first, since it has to be <=id, since otherwise we would have broken out if(!iter->isBeyond(id)){ // we found a matching range... get the id from this range // split up this range at the point of id range bottomRange = makeRange(iter->first,id); range topRange = makeRange(id+1,iter->last); // remove this range moleculeIdPool.erase(iter); if(bottomRange.firstfirst==iter->last)){ // merge the two ranges range newRange = makeRange(iter->first,next->last); moleculeIdPool.erase(iter); moleculeIdPool.erase(next); pair res = moleculeIdPool.insert(newRange); ASSERT(res.second,"Id-Pool was confused"); iter=res.first; continue; } ++iter; } if(!moleculeIdPool.empty()){ // check if the last range is at the border moleculeIdPool_t::iterator iter = moleculeIdPool.end(); iter--; if(iter->last==currMoleculeId){ currMoleculeId=iter->first; moleculeIdPool.erase(iter); } } lastMoleculePoolSize=moleculeIdPool.size(); numMoleculeDefragSkips=0; } /******************************* Iterators ********************************/ // external parts with observers CONSTRUCT_SELECTIVE_ITERATOR(atom*,World::AtomSet,AtomDescriptor); World::AtomIterator World::getAtomIter(AtomDescriptor descr){ return AtomIterator(descr,atoms); } World::AtomIterator World::getAtomIter(){ return AtomIterator(AllAtoms(),atoms); } World::AtomIterator World::atomEnd(){ return AtomIterator(AllAtoms(),atoms,atoms.end()); } CONSTRUCT_SELECTIVE_ITERATOR(molecule*,World::MoleculeSet,MoleculeDescriptor); World::MoleculeIterator World::getMoleculeIter(MoleculeDescriptor descr){ return MoleculeIterator(descr,molecules); } World::MoleculeIterator World::getMoleculeIter(){ return MoleculeIterator(AllMolecules(),molecules); } World::MoleculeIterator World::moleculeEnd(){ return MoleculeIterator(AllMolecules(),molecules,molecules.end()); } // Internal parts, without observers // Build the AtomIterator from template CONSTRUCT_SELECTIVE_ITERATOR(atom*,World::AtomSet::set_t,AtomDescriptor); World::internal_AtomIterator World::getAtomIter_internal(AtomDescriptor descr){ return internal_AtomIterator(descr,atoms.getContent()); } World::internal_AtomIterator World::atomEnd_internal(){ return internal_AtomIterator(AllAtoms(),atoms.getContent(),atoms.end_internal()); } // build the MoleculeIterator from template CONSTRUCT_SELECTIVE_ITERATOR(molecule*,World::MoleculeSet::set_t,MoleculeDescriptor); World::internal_MoleculeIterator World::getMoleculeIter_internal(MoleculeDescriptor descr){ return internal_MoleculeIterator(descr,molecules.getContent()); } World::internal_MoleculeIterator World::moleculeEnd_internal(){ return internal_MoleculeIterator(AllMolecules(),molecules.getContent(),molecules.end_internal()); } /************************** Selection of Atoms and molecules ******************/ // Atoms void World::clearAtomSelection(){ selectedAtoms.clear(); } void World::selectAtom(const atom *_atom){ // atom * is unchanged in this function, but we do store entity as changeable ASSERT(_atom,"Invalid pointer in selection of atom"); selectedAtoms[_atom->getId()]=const_cast(_atom); } void World::selectAtom(const atomId_t id){ ASSERT(atoms.count(id),"Atom Id selected that was not in the world"); selectedAtoms[id]=atoms[id]; } void World::selectAllAtoms(AtomDescriptor descr){ internal_AtomIterator begin = getAtomIter_internal(descr); internal_AtomIterator end = atomEnd_internal(); void (World::*func)(const atom*) = &World::selectAtom; // needed for type resolution of overloaded function for_each(begin,end,bind1st(mem_fun(func),this)); // func is select... see above } void World::selectAtomsOfMolecule(const molecule *_mol){ ASSERT(_mol,"Invalid pointer to molecule in selection of Atoms of Molecule"); // need to make it const to get the fast iterators const molecule *mol = _mol; void (World::*func)(const atom*) = &World::selectAtom; // needed for type resolution of overloaded function for_each(mol->begin(),mol->end(),bind1st(mem_fun(func),this)); // func is select... see above } void World::selectAtomsOfMolecule(const moleculeId_t id){ ASSERT(molecules.count(id),"No molecule with the given id upon Selection of atoms from molecule"); selectAtomsOfMolecule(molecules[id]); } void World::unselectAtom(const atom *_atom){ ASSERT(_atom,"Invalid pointer in unselection of atom"); unselectAtom(_atom->getId()); } void World::unselectAtom(const atomId_t id){ ASSERT(atoms.count(id),"Atom Id unselected that was not in the world"); selectedAtoms.erase(id); } void World::unselectAllAtoms(AtomDescriptor descr){ internal_AtomIterator begin = getAtomIter_internal(descr); internal_AtomIterator end = atomEnd_internal(); void (World::*func)(const atom*) = &World::unselectAtom; // needed for type resolution of overloaded function for_each(begin,end,bind1st(mem_fun(func),this)); // func is unselect... see above } void World::unselectAtomsOfMolecule(const molecule *_mol){ ASSERT(_mol,"Invalid pointer to molecule in selection of Atoms of Molecule"); // need to make it const to get the fast iterators const molecule *mol = _mol; void (World::*func)(const atom*) = &World::unselectAtom; // needed for type resolution of overloaded function for_each(mol->begin(),mol->end(),bind1st(mem_fun(func),this)); // func is unsselect... see above } void World::unselectAtomsOfMolecule(const moleculeId_t id){ ASSERT(molecules.count(id),"No molecule with the given id upon Selection of atoms from molecule"); unselectAtomsOfMolecule(molecules[id]); } size_t World::countSelectedAtoms() const { size_t count = 0; for (AtomSet::const_iterator iter = selectedAtoms.begin(); iter != selectedAtoms.end(); ++iter) count++; return count; } bool World::isSelected(const atom *_atom) const { return isAtomSelected(_atom->getId()); } bool World::isAtomSelected(const atomId_t no) const { return selectedAtoms.find(no) != selectedAtoms.end(); } const std::vector World::getSelectedAtoms() const { std::vector returnAtoms; returnAtoms.resize(countSelectedAtoms()); int count = 0; for (AtomSet::const_iterator iter = selectedAtoms.begin(); iter != selectedAtoms.end(); ++iter) returnAtoms[count++] = iter->second; return returnAtoms; } // Molecules void World::clearMoleculeSelection(){ selectedMolecules.clear(); } void World::selectMolecule(const molecule *_mol){ // molecule * is unchanged in this function, but we do store entity as changeable ASSERT(_mol,"Invalid pointer to molecule in selection"); selectedMolecules[_mol->getId()]=const_cast(_mol); } void World::selectMolecule(const moleculeId_t id){ ASSERT(molecules.count(id),"Molecule Id selected that was not in the world"); selectedMolecules[id]=molecules[id]; } void World::selectAllMolecules(MoleculeDescriptor descr){ internal_MoleculeIterator begin = getMoleculeIter_internal(descr); internal_MoleculeIterator end = moleculeEnd_internal(); void (World::*func)(const molecule*) = &World::selectMolecule; // needed for type resolution of overloaded function for_each(begin,end,bind1st(mem_fun(func),this)); // func is select... see above } void World::selectMoleculeOfAtom(const atom *_atom){ ASSERT(_atom,"Invalid atom pointer in selection of MoleculeOfAtom"); molecule *mol=_atom->getMolecule(); // the atom might not be part of a molecule if(mol){ selectMolecule(mol); } } void World::selectMoleculeOfAtom(const atomId_t id){ ASSERT(atoms.count(id),"No such atom with given ID in selection of Molecules of Atom");\ selectMoleculeOfAtom(atoms[id]); } void World::unselectMolecule(const molecule *_mol){ ASSERT(_mol,"invalid pointer in unselection of molecule"); unselectMolecule(_mol->getId()); } void World::unselectMolecule(const moleculeId_t id){ ASSERT(molecules.count(id),"No such molecule with ID in unselection"); selectedMolecules.erase(id); } void World::unselectAllMolecules(MoleculeDescriptor descr){ internal_MoleculeIterator begin = getMoleculeIter_internal(descr); internal_MoleculeIterator end = moleculeEnd_internal(); void (World::*func)(const molecule*) = &World::unselectMolecule; // needed for type resolution of overloaded function for_each(begin,end,bind1st(mem_fun(func),this)); // func is unselect... see above } void World::unselectMoleculeOfAtom(const atom *_atom){ ASSERT(_atom,"Invalid atom pointer in selection of MoleculeOfAtom"); molecule *mol=_atom->getMolecule(); // the atom might not be part of a molecule if(mol){ unselectMolecule(mol); } } void World::unselectMoleculeOfAtom(const atomId_t id){ ASSERT(atoms.count(id),"No such atom with given ID in selection of Molecules of Atom");\ unselectMoleculeOfAtom(atoms[id]); } size_t World::countSelectedMolecules() const { size_t count = 0; for (MoleculeSet::const_iterator iter = selectedMolecules.begin(); iter != selectedMolecules.end(); ++iter) count++; return count; } bool World::isSelected(const molecule *_mol) const { return isMoleculeSelected(_mol->getId()); } bool World::isMoleculeSelected(const moleculeId_t no) const { return selectedMolecules.find(no) != selectedMolecules.end(); } const std::vector World::getSelectedMolecules() const { std::vector returnMolecules; returnMolecules.resize(countSelectedMolecules()); int count = 0; for (MoleculeSet::const_iterator iter = selectedMolecules.begin(); iter != selectedMolecules.end(); ++iter) returnMolecules[count++] = iter->second; return returnMolecules; } /******************* Iterators over Selection *****************************/ World::AtomSelectionIterator World::beginAtomSelection(){ return selectedAtoms.begin(); } World::AtomSelectionIterator World::endAtomSelection(){ return selectedAtoms.end(); } World::AtomSelectionConstIterator World::beginAtomSelection() const{ return selectedAtoms.begin(); } World::AtomSelectionConstIterator World::endAtomSelection() const{ return selectedAtoms.end(); } World::MoleculeSelectionIterator World::beginMoleculeSelection(){ return selectedMolecules.begin(); } World::MoleculeSelectionIterator World::endMoleculeSelection(){ return selectedMolecules.end(); } World::MoleculeSelectionConstIterator World::beginMoleculeSelection() const{ return selectedMolecules.begin(); } World::MoleculeSelectionConstIterator World::endMoleculeSelection() const{ return selectedMolecules.end(); } /******************************* Singleton Stuff **************************/ World::World() : Observable("World"), BG(new BondGraph(true)), // assume Angstroem for the moment periode(new periodentafel(true)), configuration(new config), Thermostats(new ThermoStatContainer), ExitFlag(0), atoms(this), selectedAtoms(this), currAtomId(0), lastAtomPoolSize(0), numAtomDefragSkips(0), molecules(this), selectedMolecules(this), currMoleculeId(0), lastMoleculePoolSize(0), numMoleculeDefragSkips(0), molecules_deprecated(new MoleculeListClass(this)) { cell_size = new Box; RealSpaceMatrix domain; domain.at(0,0) = 20; domain.at(1,1) = 20; domain.at(2,2) = 20; cell_size->setM(domain); defaultName = "none"; NotificationChannels = new Channels(this); for (size_t type = 0; type < (size_t)NotificationType_MAX; ++type) NotificationChannels->addChannel(type); molecules_deprecated->signOn(this); } World::~World() { molecules_deprecated->signOff(this); delete cell_size; delete molecules_deprecated; MoleculeSet::iterator molIter; for(molIter=molecules.begin();molIter!=molecules.end();++molIter){ DeleteMolecule((*molIter).second); } molecules.clear(); AtomSet::iterator atIter; for(atIter=atoms.begin();atIter!=atoms.end();++atIter){ DeleteAtom((*atIter).second); } atoms.clear(); // empty notifications delete NotificationChannels; delete BG; delete periode; delete configuration; delete Thermostats; } // Explicit instantiation of the singleton mechanism at this point CONSTRUCT_SINGLETON(World) CONSTRUCT_OBSERVEDCONTAINER(World::AtomSTLSet) CONSTRUCT_OBSERVEDCONTAINER(World::MoleculeSTLSet) /******************************* deprecated Legacy Stuff ***********************/ MoleculeListClass *&World::getMolecules() { return molecules_deprecated; }