/* * 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 #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" 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; } void Interfragmenter::combineFragments( const size_t _MaxOrder, const candidates_t &_candidates, const 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::const_iterator finditer = _fragmentmap.find(_atom->getId()); ASSERT( finditer != _fragmentmap.end(), "Interfragmenter::combineFragments() - could not find atom " +toString(_atom->getId())+" in fragment specific lookup."); // copy set to allow erase 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()( Graph &TotalGraph, const size_t MaxOrder, const double Rcut, const enum HydrogenTreatment treatment) { AtomFragmentsMap& atomfragments_container = AtomFragmentsMap::getInstance(); const atomkeyset_t &atomkeyset = atomfragments_container.getMap(); Graph InterFragments; int counter = atomkeyset.size(); /// go through all fragments up to MaxOrder LOG(1, "INFO: Creating inter-fragments."); for (atomkeyset_t::const_iterator atomiter = atomkeyset.begin(); atomiter != atomkeyset.end(); ++atomiter) { const atomId_t &atomid = atomiter->first; LOG(2, "DEBUG: Current atomid is " << atomid); const AtomFragmentsMap::keysets_t &keysets = atomiter->second; for (AtomFragmentsMap::keysets_t::const_iterator keyiter = keysets.begin(); keyiter != keysets.end(); ++keyiter) { const KeySet &keyset = *keyiter; 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 std::vector atomids(candidates.size()); std::transform( candidates.begin(), candidates.end(), atomids.begin(), boost::bind(&atom::getId, _1)); atomkeyset_t fragmentmap = atomfragments_container.getMap(atomids, MaxOrder); /// 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 = atomkeyset.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); // go through each atom and find closest atom not in the same keyset AtomFragmentsMap& atomfragments_container = AtomFragmentsMap::getInstance(); const atomkeyset_t &atomkeyset = atomfragments_container.getMap(); for (atomkeyset_t::const_iterator atomiter = atomkeyset.begin(); atomiter != atomkeyset.end(); ++atomiter) { const atomId_t &atomid = atomiter->first; LOG(2, "DEBUG: Current atomid is " << atomid); const AtomFragmentsMap::keysets_t &keysets = atomiter->second; for (AtomFragmentsMap::keysets_t::const_iterator keyiter = keysets.begin(); keyiter != keysets.end(); ++keyiter) { const KeySet &keyset = *keyiter; 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; }