/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2025 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 . */ /* * AddSelectedAtomsAsFragmentAction.cpp * * Created on: Nov 20, 2025 * 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/AdaptivityMap.hpp" #include "Fragmentation/Exporters/ExportGraph_ToAtomFragments.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/AddSelectedAtomsAsFragmentAction.hpp" using namespace MoleCuilder; // and construct the stuff #include "AddSelectedAtomsAsFragmentAction.def" #include "Action_impl_pre.hpp" /** =========== define the function ====================== */ ActionState::ptr FragmentationAddSelectedAtomsAsFragmentAction::performCall() { clock_t start,end; int ExitFlag = -1; World &world = World::getInstance(); // inform about used parameters LOG(0, "STATUS: Adding currently selected atoms as one fragment "); 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 storing as a single fragment."); 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 among the selected atoms to consider."); } // 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); /** * The "cluster" sorts the atoms into one set per molecule (from lower_bound to upper_bound * as \a *mol is same key for all these atoms). * Hence, we just need to convert this set of atoms into a KeySet and turn it into a Graph. */ KeySet mols_atomids; std::transform(iter, advanceiter, std::inserter(mols_atomids, mols_atomids.end()), boost::bind( &atom::getId, boost::bind( &clusters_t::value_type::second, _1 )) ); Graph MoleculeGraph; MoleculeGraph.insert( make_pair(mols_atomids, NumberValuePair(1, 1.)) ); if (TotalGraph.empty()) { TotalGraph = MoleculeGraph; keysetcounter = TotalGraph.size(); } else TotalGraph.InsertGraph(MoleculeGraph, 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(); } // store graph internally AtomFragmentsMap &atomfragments = AtomFragmentsMap::getInstance(); atomfragments.clear(); atomfragments.insert(TotalGraph); // store keysets to file { TotalGraph.StoreKeySetFile(params.prefix.get()); } { const enum HydrogenSaturation saturation = DontSaturate; const enum HydrogenTreatment treatment = IncludeHydrogen; const SaturatedFragment::GlobalSaturationPositions_t empty_globalsaturationpositions; if (params.types.get().size() != 0) { // store molecule's fragment to file ExportGraph_ToFiles exporter(TotalGraph, treatment, saturation, empty_globalsaturationpositions); exporter.setPrefix(params.prefix.get()); exporter.setOutputTypes(params.types.get()); if (!exporter()) return Action::failure; } else { // store molecule's fragment in FragmentJobQueue ExportGraph_ToJobs exporter(TotalGraph, treatment, saturation, empty_globalsaturationpositions); exporter.setLevel(params.level.get()); exporter.setMaximumMeshWidth(params.max_meshwidth.get()); if (!exporter()) return Action::failure; } // add full keysets to present keysets in AtomFragmentsMap ExportGraph_ToAtomFragments exporter(TotalGraph, treatment, saturation, empty_globalsaturationpositions); if (!exporter()) return Action::failure; } if (!AtomFragmentsMap::getInstance().checkCompleteness()) { ELOG(0, "Something went wrong with placing keysets in AtomFragmentsMap."); return Action::failure; } // 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(); } return Action::success; } ActionState::ptr FragmentationAddSelectedAtomsAsFragmentAction::performUndo(ActionState::ptr _state) { return Action::success; } ActionState::ptr FragmentationAddSelectedAtomsAsFragmentAction::performRedo(ActionState::ptr _state){ return Action::success; } bool FragmentationAddSelectedAtomsAsFragmentAction::canUndo() { return true; } bool FragmentationAddSelectedAtomsAsFragmentAction::shouldUndo() { return true; } /** =========== end of function ====================== */