/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2010-2012 University of Bonn. All rights reserved. * 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 . */ /* * FragmentationAction.cpp * * Created on: May 9, 2010 * Author: heber */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "CodePatterns/MemDebug.hpp" #include "Atom/atom.hpp" #include "CodePatterns/IteratorAdaptors.hpp" #include "CodePatterns/Log.hpp" #include "Descriptors/AtomSelectionDescriptor.hpp" #include "Descriptors/MoleculeIdDescriptor.hpp" #include "Fragmentation/Exporters/ExportGraph_ToFiles.hpp" #include "Fragmentation/Exporters/ExportGraph_ToJobs.hpp" #include "Fragmentation/Exporters/SaturatedBond.hpp" #include "Fragmentation/Exporters/SaturatedFragment.hpp" #include "Fragmentation/Exporters/SaturationDistanceMaximizer.hpp" #include "Fragmentation/Fragmentation.hpp" #include "Fragmentation/Graph.hpp" #include "Fragmentation/HydrogenSaturation_enum.hpp" #include "Fragmentation/Interfragmenter.hpp" #include "Fragmentation/KeySetsContainer.hpp" #include "Fragmentation/Summation/Containers/FragmentationResultContainer.hpp" #include "Graph/AdjacencyList.hpp" #include "Graph/BondGraph.hpp" #include "Graph/CyclicStructureAnalysis.hpp" #include "Graph/DepthFirstSearchAnalysis.hpp" #include "Helpers/defs.hpp" #include "molecule.hpp" #include "World.hpp" #include #include #include #include #include #include #include #include "Actions/FragmentationAction/FragmentationAction.hpp" using namespace MoleCuilder; // and construct the stuff #include "FragmentationAction.def" #include "Action_impl_pre.hpp" /** =========== define the function ====================== */ ActionState::ptr FragmentationFragmentationAction::performCall() { clock_t start,end; int ExitFlag = -1; World &world = World::getInstance(); // inform about used parameters LOG(0, "STATUS: Fragmenting molecular system with current connection matrix up to " << params.order.get() << " order. "); if (params.types.get().size() != 0) LOG(0, "STATUS: Fragment files begin with " << params.prefix.get() << " and are stored as: " << params.types.get() << "." << std::endl); // check for selected atoms if (world.beginAtomSelection() == world.endAtomSelection()) { STATUS("There are no atoms selected for fragmentation."); return Action::failure; } // go through all atoms, note down their molecules and group them typedef std::multimap clusters_t; typedef std::vector atomids_t; atomids_t atomids; clusters_t clusters; for (World::AtomSelectionConstIterator iter = world.beginAtomSelection(); iter != world.endAtomSelection(); ++iter) { clusters.insert( std::make_pair(iter->second->getMolecule(), iter->second) ); atomids.push_back(iter->second->getId()); } { std::vector molecules; molecules.insert( molecules.end(), MapKeyIterator(clusters.begin()), MapKeyIterator(clusters.end()) ); molecules.erase( std::unique(molecules.begin(), molecules.end()), molecules.end() ); LOG(1, "INFO: There are " << molecules.size() << " molecules to consider."); } // parse in Adjacency file boost::shared_ptr FileChecker; boost::filesystem::path filename(params.prefix.get() + std::string(ADJACENCYFILE)); if (boost::filesystem::exists(filename) && boost::filesystem::is_regular_file(filename)) { std::ifstream File; File.open(filename.string().c_str(), ios::out); FileChecker.reset(new AdjacencyList(File)); File.close(); } else { LOG(1, "INFO: Could not open default adjacency file " << filename.string() << "."); FileChecker.reset(new AdjacencyList); } // make sure bond degree is correct { BondGraph *BG = World::getInstance().getBondGraph(); World::AtomComposite Set = World::getInstance().getAllAtoms(AtomsBySelection()); // check whether bond graph is correct if (!BG->checkBondDegree(Set)) BG->CorrectBondDegree(Set); else LOG(1, "INFO: Bond degrees all valid, not correcting."); } // we parse in the keysets from last time if present Graph StoredGraph; StoredGraph.ParseKeySetFile(params.prefix.get()); start = clock(); // go through all keys (i.e. all molecules) clusters_t::const_iterator advanceiter; Graph TotalGraph; int keysetcounter = 0; for (clusters_t::const_iterator iter = clusters.begin(); iter != clusters.end(); iter = advanceiter) { // get iterator to past last atom in this molecule const molecule * mol = iter->first; advanceiter = clusters.upper_bound(mol); // copy molecule's atoms' ids as parameters to Fragmentation's AtomMask std::vector mols_atomids; std::transform(iter, advanceiter, std::back_inserter(mols_atomids), boost::bind( &atom::getNr, boost::bind( &clusters_t::value_type::second, _1 )) ); LOG(2, "INFO: Fragmenting in molecule " << mol->getName() << " in " << clusters.count(mol) << " atoms, out of " << mol->getAtomCount() << "."); const enum HydrogenTreatment treatment = params.HowtoTreatHydrogen.get() ? ExcludeHydrogen : IncludeHydrogen; molecule * non_const_mol = World::getInstance().getMolecule(MoleculeById(mol->getId())); Fragmentation Fragmenter(non_const_mol, *FileChecker, treatment); // perform fragmentation LOG(0, std::endl << " ========== Fragmentation of molecule " << mol->getName() << " ========================= "); { Graph StoredLocalGraph(StoredGraph.getLocalGraph(mol)); const int tempFlag = Fragmenter.FragmentMolecule(mols_atomids, params.order.get(), params.prefix.get(), StoredLocalGraph); if ((ExitFlag == 2) && (tempFlag != 2)) ExitFlag = tempFlag; // if there is one molecule that needs further fragmentation, it overrides others if (ExitFlag == -1) ExitFlag = tempFlag; // if we are the first, we set the standard } if (TotalGraph.empty()) { TotalGraph = Fragmenter.getGraph(); keysetcounter = TotalGraph.size(); } else TotalGraph.InsertGraph(Fragmenter.getGraph(), keysetcounter); } // add full cycles if desired if (params.DoCyclesFull.get()) { // get the BackEdgeStack from somewhere DepthFirstSearchAnalysis DFS; DFS(); std::deque BackEdgeStack = DFS.getBackEdgeStack(); // then we analyse the cycles and get them CyclicStructureAnalysis CycleAnalysis(params.HowtoTreatHydrogen.get() ? ExcludeHydrogen : IncludeHydrogen); CycleAnalysis(&BackEdgeStack); CyclicStructureAnalysis::cycles_t cycles = CycleAnalysis.getAllCycles(); // sort them according to KeySet::operator<() std::sort(cycles.begin(), cycles.end()); // store all found cycles to file { boost::filesystem::path filename(params.prefix.get() + std::string(CYCLEKEYSETFILE)); std::ofstream File; LOG(1, "INFO: Storing cycle keysets to " << filename.string() << "."); File.open(filename.string().c_str(), ios::out); for (CyclicStructureAnalysis::cycles_t::const_iterator iter = cycles.begin(); iter != cycles.end(); ++iter) { for (CyclicStructureAnalysis::cycle_t::const_iterator cycleiter = (*iter).begin(); cycleiter != (*iter).end(); ++cycleiter) { File << *cycleiter << "\t"; } File << "\n"; } File.close(); } // ... and to result container { KeySetsContainer cyclekeys; for (CyclicStructureAnalysis::cycles_t::const_iterator iter = cycles.begin(); iter != cycles.end(); ++iter) { const CyclicStructureAnalysis::cycle_t &cycle = *iter; const size_t order = cycle.size(); KeySetsContainer::IntVector temp_cycle(cycle.begin(), cycle.end()); cyclekeys.insert(temp_cycle, order); } FragmentationResultContainer::getInstance().addCycles(cyclekeys); } // Create graph and insert into TotalGraph LOG(0, "STATUS: Adding " << cycles.size() << " cycles."); { Graph CycleGraph; for (CyclicStructureAnalysis::cycles_t::const_iterator iter = cycles.begin(); iter != cycles.end(); ++iter) { const CyclicStructureAnalysis::cycle_t ¤tcycle = *iter; LOG(2, "INFO: Inserting cycle " << currentcycle << "."); #ifndef NDEBUG std::pair< Graph::iterator, bool > inserter = #endif CycleGraph.insert( std::make_pair(currentcycle, NumberValuePair(1,1.)) ); ASSERT( inserter.second, "FragmentationFragmentationAction::performCall() - keyset " +toString(currentcycle)+" inserted twice into CycleGraph."); } TotalGraph.InsertGraph(CycleGraph, keysetcounter); } } LOG(0, "STATUS: There are " << TotalGraph.size() << " fragments."); { // remove OrderAtSite file std::string line; std::ofstream file; line = params.prefix.get() + ORDERATSITEFILE; file.open(line.c_str(), std::ofstream::out | std::ofstream::trunc); file << ""; file.close(); } // now add interfragments if (params.InterOrder.get() != 0) { LOG(0, "STATUS: Putting fragments together up to order " << params.InterOrder.get() << " and distance of " << params.distance.get() << "."); const enum HydrogenTreatment treatment = params.HowtoTreatHydrogen.get() ? ExcludeHydrogen : IncludeHydrogen; const double UpperBound = std::max(10., params.distance.get()); Interfragmenter fragmenter(TotalGraph); // check the largest Rcut that causes no additional inter-fragments const double Min_Rcut = fragmenter.findLargestCutoff(params.InterOrder.get(), UpperBound, treatment); // if we smear out electronic charges, warn when non-overlapping criterion does not hold if (params.InterOrder.get() < Min_Rcut) ELOG(2, "Inter-order is too low to cause any additional fragments."); // then add fragments fragmenter(params.InterOrder.get(), params.distance.get(), treatment); LOG(0, "STATUS: There are now " << TotalGraph.size() << " fragments after interfragmenting."); } // store keysets to file { TotalGraph.StoreKeySetFile(params.prefix.get()); } // create global saturation positions map SaturatedFragment::GlobalSaturationPositions_t globalsaturationpositions; { // go through each atom for (World::AtomSelectionConstIterator iter = world.beginAtomSelection(); iter != world.endAtomSelection(); ++iter) { const atom * const _atom = iter->second; // skip hydrogens if treated special const enum HydrogenTreatment treatment = params.HowtoTreatHydrogen.get() ? ExcludeHydrogen : IncludeHydrogen; if ((treatment == ExcludeHydrogen) && (_atom->getType()->getAtomicNumber() == 1)) { LOG(4, "DEBUG: Skipping hydrogen atom " << *_atom); continue; } // get the valence unsigned int NumberOfPoints = _atom->getElement().getNoValenceOrbitals(); LOG(3, "DEBUG: There are " << NumberOfPoints << " places to fill in in total for this atom " << *_atom << "."); // check whether there are any bonds with degree larger than 1 unsigned int SumOfDegrees = 0; bool PresentHigherBonds = false; const BondList &bondlist = _atom->getListOfBonds(); for (BondList::const_iterator bonditer = bondlist.begin(); bonditer != bondlist.end(); ++bonditer) { SumOfDegrees += (*bonditer)->getDegree(); PresentHigherBonds |= (*bonditer)->getDegree() > 1; } // check whether there are alphas to maximize the hydrogens distances SaturationDistanceMaximizer::position_bins_t position_bins; { // gather all bonds and convert to SaturatedBonds SaturationDistanceMaximizer::PositionContainers_t CutBonds; for (BondList::const_iterator bonditer = bondlist.begin(); bonditer != bondlist.end(); ++bonditer) { CutBonds.push_back( SaturatedBond::ptr(new SaturatedBond(*(bonditer->get()), *_atom) ) ); } SaturationDistanceMaximizer maximizer(CutBonds); if (PresentHigherBonds) { // then find best alphas maximizer(); } else { // if no higher order bonds, we simply gather the scaled positions } position_bins = maximizer.getAllPositionBins(); LOG(4, "DEBUG: Positions for atom " << *_atom << " are " << position_bins); } // convert into the desired entry in the map SaturatedFragment::SaturationsPositionsPerNeighbor_t positions_per_neighbor; { BondList::const_iterator bonditer = bondlist.begin(); SaturationDistanceMaximizer::position_bins_t::const_iterator biniter = position_bins.begin(); for (;bonditer != bondlist.end(); ++bonditer, ++biniter) { const atom * const OtherAtom = (*bonditer)->GetOtherAtom(_atom); std::pair< SaturatedFragment::SaturationsPositionsPerNeighbor_t::iterator, bool > inserter; // check whether we treat hydrogen special if ((treatment == ExcludeHydrogen) && (OtherAtom->getType()->getAtomicNumber() == 1)) { // if hydrogen, forget rescaled position and use original one inserter = positions_per_neighbor.insert( std::make_pair( OtherAtom->getId(), SaturatedFragment::SaturationsPositions_t( 1, OtherAtom->getPosition() - _atom->getPosition()) ) ); } else { inserter = positions_per_neighbor.insert( std::make_pair( OtherAtom->getId(), SaturatedFragment::SaturationsPositions_t( biniter->begin(), biniter->end()) ) ); } // if already pressent, add to this present list ASSERT (inserter.second, "FragmentationAction::performCall() - other atom " +toString(*OtherAtom)+" already present?"); } // bonditer follows nicely ASSERT( biniter == position_bins.end(), "FragmentationAction::performCall() - biniter is out of step, it still points at bond " +toString(*biniter)+"."); } // and insert globalsaturationpositions.insert( std::make_pair( _atom->getId(), positions_per_neighbor )); } } { const enum HydrogenSaturation saturation = params.DoSaturation.get() ? DoSaturate : DontSaturate; const enum HydrogenTreatment treatment = params.HowtoTreatHydrogen.get() ? ExcludeHydrogen : IncludeHydrogen; if (params.types.get().size() != 0) { // store molecule's fragment to file ExportGraph_ToFiles exporter(TotalGraph, treatment, saturation, globalsaturationpositions); exporter.setPrefix(params.prefix.get()); exporter.setOutputTypes(params.types.get()); exporter(); } else { // store molecule's fragment in FragmentJobQueue ExportGraph_ToJobs exporter(TotalGraph, treatment, saturation, globalsaturationpositions); exporter.setLevel(params.level.get()); exporter(); } } // store Adjacency to file { std::string filename = params.prefix.get() + ADJACENCYFILE; std::ofstream AdjacencyFile; AdjacencyFile.open(filename.c_str(), ios::out); AdjacencyList adjacency(atomids); adjacency.StoreToFile(AdjacencyFile); AdjacencyFile.close(); } World::getInstance().setExitFlag(ExitFlag); end = clock(); LOG(0, "STATUS: Clocks for this operation: " << (end-start) << ", time: " << ((double)(end-start)/CLOCKS_PER_SEC) << "s."); return Action::success; } ActionState::ptr FragmentationFragmentationAction::performUndo(ActionState::ptr _state) { return Action::success; } ActionState::ptr FragmentationFragmentationAction::performRedo(ActionState::ptr _state){ return Action::success; } bool FragmentationFragmentationAction::canUndo() { return true; } bool FragmentationFragmentationAction::shouldUndo() { return true; } /** =========== end of function ====================== */