/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2010-2012 University of Bonn. 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 . */ /* * FragmentationAutomationAction.cpp * * Created on: May 18, 2012 * Author: heber */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include // boost asio needs specific operator new #include #include "CodePatterns/MemDebug.hpp" #include #include #include "CodePatterns/Assert.hpp" #include "CodePatterns/Info.hpp" #include "CodePatterns/Log.hpp" #include "JobMarket/Jobs/FragmentJob.hpp" #include "Fragmentation/Automation/MPQCFragmentController.hpp" #include "Fragmentation/Automation/VMGFragmentController.hpp" #include "Fragmentation/EnergyMatrix.hpp" #include "Fragmentation/ForceMatrix.hpp" #include "Fragmentation/Fragmentation.hpp" #include "Fragmentation/SetValues/Fragment.hpp" #include "Fragmentation/SetValues/Histogram.hpp" #include "Fragmentation/SetValues/IndexedVectors.hpp" #include "Fragmentation/HydrogenSaturation_enum.hpp" #include "Fragmentation/KeySet.hpp" #include "Fragmentation/KeySetsContainer.hpp" #include "Fragmentation/Summation/OrthogonalSumUpPerLevel.hpp" #include "Fragmentation/Summation/SumUpPerLevel.hpp" #include "Fragmentation/Summation/OrthogonalFullSummator.hpp" #include "Fragmentation/Summation/writeTable.hpp" #include "Graph/DepthFirstSearchAnalysis.hpp" #include "Helpers/defs.hpp" #include "Jobs/MPQCJob.hpp" #include "Jobs/MPQCData.hpp" #include "Jobs/MPQCData_printKeyNames.hpp" #include "Jobs/Grid/SamplingGrid.hpp" #ifdef HAVE_VMG #include "Jobs/VMGJob.hpp" #include "Jobs/VMGData.hpp" #include "Jobs/VMGDataFused.hpp" #include "Jobs/VMGDataMap.hpp" #include "Jobs/VMGData_printKeyNames.hpp" #endif #include "World.hpp" #include #include #include #include #include "Actions/FragmentationAction/FragmentationAutomationAction.hpp" using namespace MoleCuilder; // and construct the stuff #include "FragmentationAutomationAction.def" #include "Action_impl_pre.hpp" /** =========== define the function ====================== */ class controller_AddOn; // needs to be defined for using the FragmentController controller_AddOn *getAddOn() { return NULL; } /** Helper function to get number of atoms somehow. * * Here, we just parse the number of lines in the adjacency file as * it should correspond to the number of atoms, except when some atoms * are not bonded, but then fragmentation makes no sense. * * @param path path to the adjacency file */ size_t getNoAtomsFromAdjacencyFile(const std::string &path) { size_t NoAtoms = 0; // parse in special file to get atom count (from line count) std::string filename(path); filename += FRAGMENTPREFIX; filename += ADJACENCYFILE; std::ifstream adjacency(filename.c_str()); if (adjacency.fail()) { LOG(0, endl << "getNoAtomsFromAdjacencyFile() - Unable to open " << filename << ", is the directory correct?"); return false; } std::string buffer; while (getline(adjacency, buffer)) NoAtoms++; LOG(1, "INFO: There are " << NoAtoms << " atoms."); return NoAtoms; } /** Creates a lookup from FragmentJob::id to the true fragment number. * * @param jobids vector with job ids * @param FragmentCounter total number of fragments on return * @return Lookup up-map */ std::map< JobId_t, size_t > createMatrixNrLookup( const std::vector &jobids, size_t &FragmentCounter) { // align fragments std::map< JobId_t, size_t > MatrixNrLookup; FragmentCounter = 0; for (std::vector::const_iterator iter = jobids.begin(); iter != jobids.end(); ++iter) { LOG(3, "DEBUG: Inserting (" << *iter << "," << FragmentCounter << ")."); #ifndef NDEBUG std::pair< std::map< JobId_t, size_t >::iterator, bool> inserter = #endif MatrixNrLookup.insert( std::make_pair(*iter, FragmentCounter++) ); ASSERT( inserter.second, "createMatrixNrLookup() - two results have same id " +toString(*iter)+"."); } LOG(1, "INFO: There are " << FragmentCounter << " fragments."); return MatrixNrLookup; } /** Place results from FragmentResult into EnergyMatrix and ForceMatrix. * * @param fragmentData MPQCData resulting from the jobs * @param MatrixNrLookup Lookup up-map from job id to fragment number * @param FragmentCounter total number of fragments * @param NoAtoms total number of atoms * @param Energy energy matrix to be filled on return * @param Force force matrix to be filled on return * @return true - everything ok, false - else */ bool putResultsintoMatrices( const std::map &fragmentData, const std::map< JobId_t, size_t > &MatrixNrLookup, const size_t FragmentCounter, const size_t NoAtoms, EnergyMatrix &Energy, ForceMatrix &Force) { for (std::map::const_iterator dataiter = fragmentData.begin(); dataiter != fragmentData.end(); ++dataiter) { const MPQCData &extractedData = dataiter->second; const JobId_t &jobid = dataiter->first; std::map< JobId_t, size_t >::const_iterator nriter = MatrixNrLookup.find(jobid); ASSERT( nriter != MatrixNrLookup.end(), "putResultsintoMatrices() - MatrixNrLookup does not contain id " +toString(jobid)+"."); // place results into EnergyMatrix ... { MatrixContainer::MatrixArray matrix; matrix.resize(1); matrix[0].resize(1, extractedData.energies.total); if (!Energy.AddMatrix( std::string("MPQCJob ")+toString(jobid), matrix, nriter->second)) { ELOG(1, "Adding energy matrix failed."); return false; } } // ... and ForceMatrix (with two empty columns in front) { MatrixContainer::MatrixArray matrix; const size_t rows = extractedData.forces.size(); matrix.resize(rows); for (size_t i=0;isecond)) { ELOG(1, "Adding force matrix failed."); return false; } } } // add one more matrix (not required for energy) MatrixContainer::MatrixArray matrix; matrix.resize(1); matrix[0].resize(1, 0.); if (!Energy.AddMatrix(std::string("MPQCJob total"), matrix, FragmentCounter)) return false; // but for energy because we need to know total number of atoms matrix.resize(NoAtoms); for (size_t i = 0; i< NoAtoms; ++i) matrix[i].resize(2+NDIM, 0.); if (!Force.AddMatrix(std::string("MPQCJob total"), matrix, FragmentCounter)) return false; return true; } /** Print MPQCData from received results. * * @param fragmentData MPQCData resulting from the jobs, each associated to a job * @param KeySetFilename filename with keysets to associate forces correctly * @param NoAtoms total number of atoms * @param full_sample summed up charge density of electrons from fragments on return * @param full_fragment summed up positions and charges of nuclei from fragments on return */ bool sumUpChargeDensity( const std::map &fragmentData, const std::string &KeySetFilename, SamplingGrid &full_sample, Fragment &full_fragment) { // create a vector of all job ids std::vector jobids; std::transform(fragmentData.begin(),fragmentData.end(), std::back_inserter(jobids), boost::bind( &std::map::value_type::first, boost::lambda::_1 ) ); // create lookup from job nr to fragment number size_t FragmentCounter = 0; const std::map< JobId_t, size_t > MatrixNrLookup = createMatrixNrLookup(jobids, FragmentCounter); // initialise keysets KeySetsContainer KeySet; { // else needs keysets without hydrogens std::stringstream filename; filename << FRAGMENTPREFIX << KEYSETFILE; if (!KeySet.ParseKeySets(KeySetFilename, filename.str(), FragmentCounter)) return false; } /// prepare for OrthogonalSummation // convert KeySetContainer to IndexSetContainer IndexSetContainer::ptr container(new IndexSetContainer(KeySet)); // create the map of all keysets SubsetMap::ptr subsetmap(new SubsetMap(*container)); /// convert all MPQCData to MPQCDataMap_t std::vector Result_Grid_fused( OrthogonalSumUpPerLevel( fragmentData, MatrixNrLookup, container, subsetmap)); std::vector Result_Fragment_fused( OrthogonalSumUpPerLevel( fragmentData, MatrixNrLookup, container, subsetmap)); // obtain full grid full_sample = boost::fusion::at_key(Result_Grid_fused.back()); full_fragment = boost::fusion::at_key(Result_Fragment_fused.back()); return true; } /** Print MPQCData from received results. * * @param fragmentData MPQCData resulting from the jobs, associated to job id * @param KeySetFilename filename with keysets to associate forces correctly * @param NoAtoms total number of atoms * @param full_sample summed up charge from fragments on return */ bool printReceivedMPQCResults( const std::map &fragmentData, const std::string &KeySetFilename, size_t NoAtoms, SamplingGrid &full_sample) { // create a vector of all job ids std::vector jobids; std::transform(fragmentData.begin(),fragmentData.end(), std::back_inserter(jobids), boost::bind( &std::map::value_type::first, boost::lambda::_1 ) ); // create lookup from job nr to fragment number size_t FragmentCounter = 0; const std::map< JobId_t, size_t > MatrixNrLookup= createMatrixNrLookup(jobids, FragmentCounter); // place results into maps EnergyMatrix Energy; ForceMatrix Force; if (!putResultsintoMatrices(fragmentData, MatrixNrLookup, FragmentCounter, NoAtoms, Energy, Force)) return false; // initialise keysets KeySetsContainer KeySet; KeySetsContainer ForceKeySet; if (!Energy.InitialiseIndices()) return false; if (!Force.ParseIndices(KeySetFilename.c_str())) return false; { // else needs keysets without hydrogens std::stringstream filename; filename << FRAGMENTPREFIX << KEYSETFILE; if (!KeySet.ParseKeySets(KeySetFilename, filename.str(), FragmentCounter)) return false; } { // forces need keysets including hydrogens std::stringstream filename; filename << FRAGMENTPREFIX << FORCESFILE; if (!ForceKeySet.ParseKeySets(KeySetFilename, filename.str(), FragmentCounter)) return false; } /// prepare for OrthogonalSummation // convert KeySetContainer to IndexSetContainer IndexSetContainer::ptr container(new IndexSetContainer(KeySet)); // create the map of all keysets SubsetMap::ptr subsetmap(new SubsetMap(*container)); /// convert all MPQCData to MPQCDataMap_t { ASSERT( ForceKeySet.KeySets.size() == fragmentData.size(), "FragmentationAutomationAction::performCall() - ForceKeySet's KeySets and fragmentData differ in size."); typedef boost::mpl::remove::type MPQCDataEnergyVector_noeigenvalues_t; std::vector Result_Energy_fused( OrthogonalSumUpPerLevel( fragmentData, MatrixNrLookup, container, subsetmap)); std::vector Result_Grid_fused( OrthogonalSumUpPerLevel( fragmentData, MatrixNrLookup, container, subsetmap)); std::vector Result_Time_fused( SumUpPerLevel( fragmentData, MatrixNrLookup, container, subsetmap)); // force has extra converter std::map MPQCData_Force_fused; convertMPQCDatatoForceMap(fragmentData, ForceKeySet, MPQCData_Force_fused); std::vector Result_Force_fused(subsetmap->getMaximumSubsetLevel()); AllLevelOrthogonalSummator forceSummer( subsetmap, MPQCData_Force_fused, container->getContainer(), MatrixNrLookup, Result_Force_fused); boost::mpl::for_each(boost::ref(forceSummer)); // obtain full grid full_sample = boost::fusion::at_key(Result_Grid_fused.back()); // print tables (without eigenvalues, they go extra) const size_t MaxLevel = subsetmap->getMaximumSubsetLevel(); const std::string energyresult = writeTable()( Result_Energy_fused, MaxLevel); LOG(0, "Energy table is \n" << energyresult); const std::string eigenvalueresult; LOG(0, "Eigenvalue table is \n" << eigenvalueresult); const std::string forceresult = writeTable()( Result_Force_fused, MaxLevel); LOG(0, "Force table is \n" << forceresult); // we don't want to print grid to a table // print times (without flops for now) typedef boost::mpl::remove::type MPQCDataTimeVector_noflops_t; const std::string timesresult = writeTable()( Result_Time_fused, MaxLevel); LOG(0, "Times table is \n" << timesresult); } // combine all found data if (!KeySet.ParseManyBodyTerms()) return false; EnergyMatrix EnergyFragments; ForceMatrix ForceFragments; if (!EnergyFragments.AllocateMatrix(Energy.Header, Energy.MatrixCounter, Energy.RowCounter, Energy.ColumnCounter)) return false; if (!ForceFragments.AllocateMatrix(Force.Header, Force.MatrixCounter, Force.RowCounter, Force.ColumnCounter)) return false; if(!Energy.SetLastMatrix(0., 0)) return false; if(!Force.SetLastMatrix(0., 2)) return false; for (int BondOrder=0;BondOrder std::vector extractJobIds(const std::map &iddata) { // create a vector of all job ids std::vector jobids; std::transform(iddata.begin(),iddata.end(), std::back_inserter(jobids), boost::bind(&std::map::value_type::first, boost::lambda::_1 ) ); return jobids; } /** Print MPQCData from received results. * * @param fragmentData MPQCData resulting from the jobs * @param longrangeData VMGData resulting from long-range jobs * @param fullsolutionData VMGData resulting from long-range of full problem * @param KeySetFilename filename with keysets to associate forces correctly * @param NoAtoms total number of atoms * @param full_sample summed up charge from fragments on return */ bool printReceivedFullResults( const std::map &fragmentData, const std::map &longrangeData, const VMGData &fullsolutionData, const std::string &KeySetFilename, size_t NoAtoms, SamplingGrid &full_sample) { // create lookup from job nr to fragment number size_t MPQCFragmentCounter = 0; const std::vector mpqcjobids = extractJobIds(fragmentData); const std::map< JobId_t, size_t > MPQCMatrixNrLookup = createMatrixNrLookup(mpqcjobids, MPQCFragmentCounter); size_t VMGFragmentCounter = 0; const std::vector vmgjobids = extractJobIds(longrangeData); const std::map< JobId_t, size_t > VMGMatrixNrLookup = createMatrixNrLookup(vmgjobids, VMGFragmentCounter); // initialise keysets KeySetsContainer KeySet; KeySetsContainer ForceKeySet; { // else needs keysets without hydrogens std::stringstream filename; filename << FRAGMENTPREFIX << KEYSETFILE; if (!KeySet.ParseKeySets(KeySetFilename, filename.str(), MPQCFragmentCounter)) return false; } { // forces need keysets including hydrogens std::stringstream filename; filename << FRAGMENTPREFIX << FORCESFILE; if (!ForceKeySet.ParseKeySets(KeySetFilename, filename.str(), MPQCFragmentCounter)) return false; } /// prepare for OrthogonalSummation // convert KeySetContainer to IndexSetContainer IndexSetContainer::ptr container(new IndexSetContainer(KeySet)); // create the map of all keysets SubsetMap::ptr subsetmap(new SubsetMap(*container)); /// convert all MPQCData to MPQCDataMap_t { typedef boost::mpl::remove::type MPQCDataEnergyVector_noeigenvalues_t; std::vector Result_Energy_fused( OrthogonalSumUpPerLevel( fragmentData, MPQCMatrixNrLookup, container, subsetmap)); std::vector Result_Grid_fused( OrthogonalSumUpPerLevel( fragmentData, MPQCMatrixNrLookup, container, subsetmap)); std::vector Result_Time_fused( SumUpPerLevel( fragmentData, MPQCMatrixNrLookup, container, subsetmap)); std::vector Result_Fragment_fused( OrthogonalSumUpPerLevel( fragmentData, MPQCMatrixNrLookup, container, subsetmap)); // force has extra converter std::map MPQCData_Force_fused; convertMPQCDatatoForceMap(fragmentData, ForceKeySet, MPQCData_Force_fused); std::vector Result_Force_fused(subsetmap->getMaximumSubsetLevel()); AllLevelOrthogonalSummator forceSummer( subsetmap, MPQCData_Force_fused, container->getContainer(), MPQCMatrixNrLookup, Result_Force_fused); boost::mpl::for_each(boost::ref(forceSummer)); // obtain full grid std::map VMGData_Potential_fused; convertDataTo(longrangeData, VMGData_Potential_fused); OrthogonalFullSummator potentialSummer( subsetmap, VMGData_Potential_fused, container->getContainer(), VMGMatrixNrLookup); potentialSummer(subsetmap->getMaximumSubsetLevel()); OrthogonalFullSummator epotentialSummer( subsetmap, VMGData_Potential_fused, container->getContainer(), VMGMatrixNrLookup); epotentialSummer(subsetmap->getMaximumSubsetLevel()); SamplingGrid full_sample_solution = fullsolutionData.sampled_potential; // LOG(0, "Remaining long-range energy from energy_potential is " << full_sample_solution.integral()-epotentialSummer.getFullContribution() << "."); full_sample_solution -= potentialSummer.getFullContribution(); // multiply element-wise with charge distribution LOG(0, "Remaining long-range potential integral is " << full_sample_solution.integral() << "."); full_sample_solution *= full_sample; LOG(0, "Remaining long-range energy from potential integral is " << full_sample_solution.integral() << "."); OrthogonalFullSummator elongSummer( subsetmap, VMGData_Potential_fused, container->getContainer(), VMGMatrixNrLookup); elongSummer(subsetmap->getMaximumSubsetLevel()); double e_long = fullsolutionData.e_long; e_long -= elongSummer.getFullContribution(); LOG(0, "Remaining long-range energy is " << e_long << "."); // print tables (without eigenvalues, they go extra) const size_t MaxLevel = subsetmap->getMaximumSubsetLevel(); const std::string energyresult = writeTable()( Result_Energy_fused, MaxLevel); LOG(0, "Energy table is \n" << energyresult); const std::string eigenvalueresult; LOG(0, "Eigenvalue table is \n" << eigenvalueresult); const std::string forceresult = writeTable()( Result_Force_fused, MaxLevel); LOG(0, "Force table is \n" << forceresult); // we don't want to print grid to a table // print times (without flops for now) typedef boost::mpl::remove::type MPQCDataTimeVector_noflops_t; const std::string timesresult = writeTable()( Result_Time_fused, MaxLevel); LOG(0, "Times table is \n" << timesresult); } return true; } Action::state_ptr FragmentationFragmentationAutomationAction::performCall() { boost::asio::io_service io_service; MPQCFragmentController mpqccontroller(io_service); mpqccontroller.setHost(params.host.get()); mpqccontroller.setPort(params.port.get()); mpqccontroller.setLevel(params.level.get()); VMGFragmentController vmgcontroller(io_service); vmgcontroller.setHost(params.host.get()); vmgcontroller.setPort(params.port.get()); // TODO: Have io_service run in second thread and merge with current again eventually // Phase One: obtain ids std::vector< boost::filesystem::path > jobfiles = params.jobfiles.get(); mpqccontroller.requestIds(jobfiles.size()); // Phase Two: create and add MPQCJobs if (!mpqccontroller.addJobsFromFiles(params.executable.get().string(), jobfiles)) return Action::failure; // Phase Three: calculate result mpqccontroller.waitforResults(jobfiles.size()); std::map fragmentData; mpqccontroller.getResults(fragmentData); #ifdef HAVE_VMG if (params.DoLongrange.get()) { ASSERT( World::getInstance().getAllAtoms().size() != 0, "FragmentationFragmentationAutomationAction::performCall() - please load the full molecule into the World before."); // obtain combined charge density LOG(1, "INFO: Parsing fragment files from " << params.path.get() << "."); SamplingGrid full_sample; Fragment full_fragment; sumUpChargeDensity( fragmentData, params.path.get(), full_sample, full_fragment); // Phase Four: obtain more ids vmgcontroller.requestIds(fragmentData.size()+1); // Phase Five: create VMGJobs const size_t near_field_cells = params.near_field_cells.get(); if (!vmgcontroller.createLongRangeJobs(fragmentData, full_sample, full_fragment, near_field_cells)) return Action::failure; // Phase Six: calculate result vmgcontroller.waitforResults(fragmentData.size()+1); std::map longrangeData; vmgcontroller.getResults(longrangeData); ASSERT( fragmentData.size()+1 == longrangeData.size(), "FragmentationFragmentationAutomationAction::performCall() - number of MPQCresults+1 " +toString(fragmentData.size()+1)+" and VMGresults "+toString(longrangeData.size())+" don't match."); // remove full solution from map (must be highest id), has to be treated extra VMGData fullsolutionData = (--longrangeData.end())->second; longrangeData.erase(--longrangeData.end()); // Final phase: print result { LOG(1, "INFO: Parsing fragment files from " << params.path.get() << "."); printReceivedFullResults( fragmentData, longrangeData, fullsolutionData, params.path.get(), getNoAtomsFromAdjacencyFile(params.path.get()), full_sample); } } #else // Final phase: print result { LOG(1, "INFO: Parsing fragment files from " << params.path.get() << "."); printReceivedMPQCResults( fragmentData, params.path.get(), getNoAtomsFromAdjacencyFile(params.path.get()), full_sample); } #endif size_t Exitflag = vmgcontroller.getExitflag() + mpqccontroller.getExitflag(); return (Exitflag == 0) ? Action::success : Action::failure; } Action::state_ptr FragmentationFragmentationAutomationAction::performUndo(Action::state_ptr _state) { return Action::success; } Action::state_ptr FragmentationFragmentationAutomationAction::performRedo(Action::state_ptr _state){ return Action::success; } bool FragmentationFragmentationAutomationAction::canUndo() { return false; } bool FragmentationFragmentationAutomationAction::shouldUndo() { return false; } /** =========== end of function ====================== */