/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2013 Frederik Heber. All rights reserved. * * * This file is part of MoleCuilder. * * MoleCuilder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * MoleCuilder is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MoleCuilder. If not, see . */ /* * Interfragmenter.cpp * * Created on: Jul 5, 2013 * Author: heber */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "CodePatterns/MemDebug.hpp" #include "Interfragmenter.hpp" #include "CodePatterns/Assert.hpp" #include "CodePatterns/Log.hpp" #include "LinearAlgebra/Vector.hpp" #include "Descriptors/AtomDescriptor.hpp" #include "Element/element.hpp" #include "Fragmentation/Graph.hpp" #include "Fragmentation/KeySet.hpp" #include "LinkedCell/LinkedCell_View.hpp" #include "LinkedCell/types.hpp" #include "World.hpp" Interfragmenter::atomkeyset_t Interfragmenter::getAtomKeySetMap( size_t _MaxOrder ) const { /// create a map of atom to keyset (below equal MaxOrder) atomkeyset_t atomkeyset; LOG(1, "INFO: Placing all atoms and their keysets into a map."); for (Graph::const_iterator keysetiter = TotalGraph.begin(); keysetiter != TotalGraph.end(); ++keysetiter) { const KeySet &keyset = keysetiter->first; LOG(2, "DEBUG: Current keyset is " << keyset); const AtomIdSet atoms(keyset); const size_t atoms_size = atoms.getAtomIds().size(); if ((atoms_size > _MaxOrder) || (atoms_size == 0)) continue; for (AtomIdSet::const_iterator atomiter = atoms.begin(); atomiter != atoms.end(); ++atomiter) { // either create new list ... std::pair inserter = atomkeyset.insert( std::make_pair(*atomiter, keysets_t(1, &keyset) )); // ... or push to end if (inserter.second) { LOG(3, "DEBUG: Created new entry in map."); } else { LOG(3, "DEBUG: Added keyset to present entry."); inserter.first->second.push_back(&keyset); } } } LOG(2, "DEBUG: There are " << atomkeyset.size() << " entries in lookup."); return atomkeyset; } static Vector getAtomIdSetCenter( const AtomIdSet &_atoms) { const molecule * const _mol = (*_atoms.begin())->getMolecule(); const size_t atoms_size = _atoms.getAtomIds().size(); Vector center; for (AtomIdSet::const_iterator iter = _atoms.begin(); iter != _atoms.end(); ++iter) { center += (*iter)->getPosition(); ASSERT ( _mol == (*iter)->getMolecule(), "getAtomIdSetCenter() - ids in same keyset belong to different molecule."); } center *= 1./(double)atoms_size; return center; } Interfragmenter::candidates_t Interfragmenter::getNeighborsOutsideMolecule( const AtomIdSet &_atoms, double _Rcut, const enum HydrogenTreatment _treatment) const { /// go through linked cell and get all neighboring atoms up to Rcut const LinkedCell::LinkedCell_View view = World::getInstance().getLinkedCell(_Rcut); const Vector center = getAtomIdSetCenter(_atoms); const LinkedCell::LinkedList neighbors = view.getAllNeighbors(_Rcut, center); LOG(4, "DEBUG: Obtained " << neighbors.size() << " neighbors in distance of " << _Rcut << " around " << center << "."); /// remove all atoms that belong to same molecule as does one of the /// fragment's atoms const molecule * const _mol = (*_atoms.begin())->getMolecule(); candidates_t candidates; candidates.reserve(neighbors.size()); for (LinkedCell::LinkedList::const_iterator iter = neighbors.begin(); iter != neighbors.end(); ++iter) { const atom * const _atom = static_cast(*iter); ASSERT( _atom != NULL, "Interfragmenter::getNeighborsOutsideMolecule() - a neighbor is not actually an atom?"); if ((_atom->getMolecule() != _mol) && (_atom->getPosition().DistanceSquared(center) < _Rcut*_Rcut) && ((_treatment == IncludeHydrogen) || (_atom->getType()->getAtomicNumber() != 1))) { candidates.push_back(_atom); } } LOG(3, "DEBUG: There remain " << candidates.size() << " candidates."); return candidates; } Interfragmenter::atomkeyset_t Interfragmenter::getCandidatesSpecificKeySetMap( const candidates_t &_candidates, const atomkeyset_t &_atomkeyset) const { atomkeyset_t fragmentmap; for (candidates_t::const_iterator candidateiter = _candidates.begin(); candidateiter != _candidates.end(); ++candidateiter) { const atom * _atom = *candidateiter; atomkeyset_t::const_iterator iter = _atomkeyset.find(_atom); ASSERT( iter != _atomkeyset.end(), "Interfragmenter::getAtomSpecificKeySetMap() - could not find atom " +toString(_atom->getId())+" in lookup."); fragmentmap.insert( std::make_pair( _atom, iter->second) ); } LOG(4, "DEBUG: Copied part of lookup map contains " << fragmentmap.size() << " keys."); return fragmentmap; } void Interfragmenter::combineFragments( const size_t _MaxOrder, const candidates_t &_candidates, atomkeyset_t &_fragmentmap, const KeySet &_keyset, Graph &_InterFragments, int &_counter) { for (candidates_t::const_iterator candidateiter = _candidates.begin(); candidateiter != _candidates.end(); ++candidateiter) { const atom *_atom = *candidateiter; LOG(3, "DEBUG: Current candidate is " << *_atom << "."); atomkeyset_t::iterator finditer = _fragmentmap.find(_atom); ASSERT( finditer != _fragmentmap.end(), "Interfragmenter::combineFragments() - could not find atom " +toString(_atom->getId())+" in fragment specific lookup."); keysets_t &othersets = finditer->second; ASSERT( !othersets.empty(), "Interfragmenter::combineFragments() - keysets to "+toString(_atom->getId())+ "is empty."); keysets_t::iterator otheriter = othersets.begin(); while (otheriter != othersets.end()) { const KeySet &otherset = **otheriter; LOG(3, "DEBUG: Current keyset is " << otherset << "."); // only add them one way round and not the other if (otherset < _keyset) { ++otheriter; continue; } // only add if combined they don't exceed the desired maxorder if (otherset.size() + _keyset.size() > _MaxOrder) { LOG(3, "INFO: Rejecting " << otherset << " as in sum their orders exceed " << _MaxOrder); ++otheriter; continue; } KeySet newset(otherset); newset.insert(_keyset.begin(), _keyset.end()); LOG(3, "DEBUG: Inserting new combined set " << newset << "."); _InterFragments.insert( std::make_pair(newset, std::make_pair(++_counter, 1.))); // finally, remove the set such that no other combination exists otheriter = othersets.erase(otheriter); } } } void Interfragmenter::operator()( const size_t MaxOrder, const double Rcut, const enum HydrogenTreatment treatment) { atomkeyset_t atomkeyset = getAtomKeySetMap(MaxOrder); Graph InterFragments; int counter = TotalGraph.size(); /// go through all fragments up to MaxOrder LOG(1, "INFO: Creating inter-fragments."); for (Graph::const_iterator keysetiter = TotalGraph.begin(); keysetiter != TotalGraph.end(); ++keysetiter) { const KeySet &keyset = keysetiter->first; LOG(2, "DEBUG: Current keyset is " << keyset); const AtomIdSet atoms(keyset); const size_t atoms_size = atoms.getAtomIds().size(); if ((atoms_size > MaxOrder) || (atoms_size == 0)) continue; // get neighboring atoms outside the current molecule candidates_t candidates = getNeighborsOutsideMolecule(atoms, Rcut, treatment); // create a lookup specific to this fragment atomkeyset_t fragmentmap = getCandidatesSpecificKeySetMap(candidates, atomkeyset); /// combine each remaining fragment with current fragment to a new fragment /// if keyset is less (to prevent addding same inter-fragment twice) combineFragments(MaxOrder, candidates, fragmentmap, keyset, InterFragments, counter); } /// eventually, add all new fragments to the Graph counter = TotalGraph.size(); TotalGraph.InsertGraph(InterFragments, counter); } double Interfragmenter::findLargestCutoff( const size_t _MaxOrder, const double _upperbound, const enum HydrogenTreatment _treatment) const { double Rcut = _upperbound*_upperbound; std::pair ClosestPair; // place all atoms into LC grid with some upper bound const LinkedCell::LinkedCell_View view = World::getInstance().getLinkedCell(_upperbound); atomkeyset_t atomkeyset = getAtomKeySetMap(_MaxOrder); // go through each atom and find closest atom not in the same keyset for (Graph::const_iterator keysetiter = TotalGraph.begin(); keysetiter != TotalGraph.end(); ++keysetiter) { const KeySet &keyset = keysetiter->first; LOG(2, "DEBUG: Current keyset is " << keyset); const AtomIdSet atoms(keyset); // get neighboring atoms outside the current molecule const candidates_t candidates = getNeighborsOutsideMolecule(atoms, _upperbound, _treatment); const Vector center = getAtomIdSetCenter(atoms); for (candidates_t::const_iterator candidateiter = candidates.begin(); candidateiter != candidates.end(); ++candidateiter) { atom const * const Walker = *candidateiter; // go through each atom in set and pick minimal distance for (AtomIdSet::const_iterator setiter = atoms.begin(); setiter != atoms.end(); ++setiter) { const double distanceSquared = Walker->getPosition().DistanceSquared(center); // pick the smallest compared to current Rcut if smaller if (distanceSquared < Rcut) { Rcut = distanceSquared; ClosestPair.first = (*setiter)->getId(); ClosestPair.second = Walker->getId(); LOG(2, "DEBUG: Found new pair " << ClosestPair << " with distance " << sqrt(Rcut)); } } } } const double largest_distance = sqrt(Rcut); LOG(1, "INFO: Largest inter-fragment distance to cause no additional fragments: " << largest_distance); return largest_distance; }