/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2011 University of Bonn. All rights reserved. * Please see the LICENSE file or "Copyright notice" in builder.cpp for details. */ /* * \file controller.cpp * * This file strongly follows the Serialization example from the boost::asio * library (see client.cpp) * * Created on: Nov 27, 2011 * Author: heber */ // include config.h #ifdef HAVE_CONFIG_H #include #endif // boost asio needs specific operator new #include #include "CodePatterns/MemDebug.hpp" #include #include #include #include #include #include #include #include #include #include "atexit.hpp" #include "CodePatterns/Info.hpp" #include "CodePatterns/Log.hpp" #include "Fragmentation/EnergyMatrix.hpp" #include "Fragmentation/ForceMatrix.hpp" #include "Fragmentation/KeySetsContainer.hpp" #include "FragmentController.hpp" #include "Helpers/defs.hpp" #include "Jobs/MPQCCommandJob.hpp" #include "Jobs/MPQCCommandJob_MPQCData.hpp" #include "Jobs/SystemCommandJob.hpp" #include "Results/FragmentResult.hpp" enum CommandIndices { UnknownCommandIndex = 0, AddJobsIndex = 1, CreateJobsIndex = 2, CheckResultsIndex = 3, ReceiveResultsIndex = 4, ReceiveMPQCIndex = 5, RemoveAllIndex = 6, ShutdownIndex = 7, }; /** Creates a SystemCommandJob out of give \a command with \a argument. * * @param jobs created job is added to this vector * @param command command to execute for SystemCommandJob * @param argument argument for command to execute * @param nextid id for this job */ void createjobs( std::vector &jobs, const std::string &command, const std::string &argument, const JobId_t nextid) { FragmentJob::ptr testJob( new SystemCommandJob(command, argument, nextid) ); jobs.push_back(testJob); LOG(1, "INFO: Added one SystemCommandJob."); } /** Creates a MPQCCommandJob with argument \a filename. * * @param jobs created job is added to this vector * @param command mpqc command to execute * @param filename filename being argument to job * @param nextid id for this job */ void parsejob( std::vector &jobs, const std::string &command, const std::string &filename, const JobId_t nextid) { std::ifstream file; file.open(filename.c_str()); ASSERT( file.good(), "parsejob() - file "+filename+" does not exist."); std::string output((std::istreambuf_iterator(file)), std::istreambuf_iterator()); FragmentJob::ptr testJob( new MPQCCommandJob(output, nextid, command) ); jobs.push_back(testJob); file.close(); LOG(1, "INFO: Added MPQCCommandJob from file "+filename+"."); } /** Print received results. * * @param results received results to print */ void printReceivedResults(std::vector &results) { for (std::vector::const_iterator iter = results.begin(); iter != results.end(); ++iter) LOG(1, "RESULT: job #"+toString((*iter)->getId())+": "+toString((*iter)->result)); } /** Print MPQCData from received results. * * @param results received results to extract MPQCData from * @param KeySetFilename filename with keysets to associate forces correctly * @param NoAtoms total number of atoms */ bool printReceivedMPQCResults( const std::vector &results, const std::string &KeySetFilename, size_t NoAtoms) { EnergyMatrix Energy; EnergyMatrix EnergyFragments; ForceMatrix Force; ForceMatrix ForceFragments; KeySetsContainer KeySet; // align fragments std::map< JobId_t, size_t > MatrixNrLookup; size_t FragmentCounter = 0; { // bring ids in order ... typedef std::map< JobId_t, FragmentResult::ptr> IdResultMap_t; IdResultMap_t IdResultMap; for (std::vector::const_iterator iter = results.begin(); iter != results.end(); ++iter) { #ifndef NDEBUG std::pair< IdResultMap_t::iterator, bool> inserter = #endif IdResultMap.insert( make_pair((*iter)->getId(), *iter) ); ASSERT( inserter.second, "printReceivedMPQCResults() - two results have same id " +toString((*iter)->getId())+"."); } // ... and fill lookup for(IdResultMap_t::const_iterator iter = IdResultMap.begin(); iter != IdResultMap.end(); ++iter) MatrixNrLookup.insert( make_pair(iter->first, FragmentCounter++) ); } LOG(1, "INFO: There are " << FragmentCounter << " fragments."); // extract results std::vector fragmentData(results.size()); MPQCData combinedData; LOG(2, "DEBUG: Parsing now through " << results.size() << " results."); for (std::vector::const_iterator iter = results.begin(); iter != results.end(); ++iter) { LOG(1, "RESULT: job #"+toString((*iter)->getId())+": "+toString((*iter)->result)); MPQCData extractedData; std::stringstream inputstream((*iter)->result); LOG(2, "DEBUG: First 50 characters FragmentResult's string: "+(*iter)->result.substr(0, 50)); boost::archive::text_iarchive ia(inputstream); ia >> extractedData; LOG(1, "INFO: extracted data is " << extractedData << "."); // place results into EnergyMatrix ... { MatrixContainer::MatrixArray matrix; matrix.resize(1); matrix[0].resize(1, extractedData.energy); if (!Energy.AddMatrix( std::string("MPQCJob ")+toString((*iter)->getId()), matrix, MatrixNrLookup[(*iter)->getId()])) { 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;igetId()), matrix, MatrixNrLookup[(*iter)->getId()])) { 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; // combine all found data if (!Energy.InitialiseIndices()) return false; if (!Force.ParseIndices(KeySetFilename.c_str())) return false; if (!KeySet.ParseKeySets(KeySetFilename.c_str(), Force.RowCounter, Force.MatrixCounter)) return false; if (!KeySet.ParseManyBodyTerms()) return false; 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 &commandmap, const std::string &cmd) { std::map::const_iterator iter = commandmap.find(cmd); if (iter != commandmap.end()) return iter->second; else return UnknownCommandIndex; } int main(int argc, char* argv[]) { // from this moment on, we need to be sure to deeinitialize in the correct order // this is handled by the cleanup function atexit(cleanUp); setVerbosity(3); size_t Exitflag = 0; typedef std::map CommandsMap_t; CommandsMap_t CommandsMap; CommandsMap.insert( std::make_pair("addjobs", AddJobsIndex) ); CommandsMap.insert( std::make_pair("createjobs", CreateJobsIndex) ); CommandsMap.insert( std::make_pair("checkresults", CheckResultsIndex) ); CommandsMap.insert( std::make_pair("receiveresults", ReceiveResultsIndex) ); CommandsMap.insert( std::make_pair("receivempqc", ReceiveMPQCIndex) ); CommandsMap.insert( std::make_pair("removeall", RemoveAllIndex) ); CommandsMap.insert( std::make_pair("shutdown", ShutdownIndex) ); std::vector Commands; for (CommandsMap_t::const_iterator iter = CommandsMap.begin(); iter != CommandsMap.end(); ++iter) Commands.push_back(iter->first); // Declare the supported options. boost::program_options::options_description desc("Allowed options"); desc.add_options() ("help,h", "produce help message") ("verbosity,v", boost::program_options::value(), "set verbosity level") ("server", boost::program_options::value< std::string >(), "connect to server at this address (host:port)") ("command", boost::program_options::value< std::string >(), (std::string("command to send to server: ")+toString(Commands)).c_str()) ("executable", boost::program_options::value< std::string >(), "executable for commands 'addjobs' and 'createjobs'") ("fragment-path", boost::program_options::value< std::string >(), "path to fragment files for 'receivempqcresults'") ("jobcommand", boost::program_options::value< std::string >(), "command argument for executable for 'createjobs'") ("jobfiles", boost::program_options::value< std::vector< std::string > >()->multitoken(), "list of files as single argument to executable for 'addjobs'") ; boost::program_options::variables_map vm; boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); boost::program_options::notify(vm); if (vm.count("help")) { std::cout << desc << "\n"; return 1; } if (vm.count("verbosity")) { LOG(0, "STATUS: Verbosity level was set to " << vm["verbosity"].as() << "."); setVerbosity(vm["verbosity"].as()); } else { LOG(0, "STATUS: Verbosity level was not set, defaulting to 5."); setVerbosity(5); } std::string server; std::string serverport; if (vm.count("server")) { server = vm["server"].as< std::string >(); serverport = server.substr(server.find_last_of(':')+1, std::string::npos); server = server.substr(0, server.find_last_of(':')); try { boost::lexical_cast(serverport); } catch (boost::bad_lexical_cast) { ELOG(1, "Could not interpret " << serverport << " as server:port."); return 255; } LOG(1, "INFO: Using " << server << ":" << serverport << " as server's address."); } else { ELOG(1, "Requiring server's address (host:port) to connect to."); return 255; } if (!vm.count("command")) { ELOG(1, "Controller requires one of the following commands: "+toString(Commands)); return 255; } const std::string command = vm["command"].as< std::string >(); const CommandIndices commandIndex = getCommandIndex(CommandsMap, command); // check arguments switch(commandIndex) { case AddJobsIndex: if (!vm.count("executable") && !vm.count("jobfiles")) { ELOG(1, "'"+command+"' requires at least two options: [executable] [list of input files ...]."); return 255; } break; case CreateJobsIndex: if (!vm.count("executable") && !vm.count("jobcommand")) { ELOG(1, "'"+command+"' requires two options: [executable] [jobcommand]."); return 255; } break; case CheckResultsIndex: break; case ReceiveResultsIndex: break; case ReceiveMPQCIndex: if (!vm.count("fragment-path")) { ELOG(1, "'"+command+"' require one option: [path to fragment files]."); return 255; } break; case RemoveAllIndex: break; case ShutdownIndex: break; case UnknownCommandIndex: default: ELOG(1, "Unrecognized command '"+toString(command)+"'."); return 255; break; } try { boost::asio::io_service io_service; FragmentController controller(io_service); // Initial phase: information gathering from server switch(commandIndex) { case AddJobsIndex: controller.requestIds(server, serverport, vm["jobfiles"].as< std::vector >().size()); break; case CreateJobsIndex: controller.requestIds(server, serverport, 1); break; case CheckResultsIndex: break; case ReceiveResultsIndex: break; case ReceiveMPQCIndex: break; case RemoveAllIndex: break; case ShutdownIndex: break; case UnknownCommandIndex: default: ELOG(0, "Unrecognized command '"+toString(command)+"'."); return 255; break; } { io_service.reset(); Info info("io_service: Phase One"); io_service.run(); } // Second phase: Building jobs and sending information to server switch(commandIndex) { case AddJobsIndex: { std::vector jobs; const std::string executable(vm["executable"].as< std::string >()); const std::vector< std::string > jobfiles = vm["jobfiles"].as< std::vector< std::string > >(); for (std::vector< std::string >::const_iterator iter = jobfiles.begin(); iter != jobfiles.end(); ++iter) { const JobId_t next_id = controller.getAvailableId(); const std::string &filename = *iter; LOG(1, "INFO: Creating MPQCCommandJob with filename'" +filename+"', and id "+toString(next_id)+"."); parsejob(jobs, executable, filename, next_id); } controller.addJobs(jobs); controller.sendJobs(server, serverport); break; } case CreateJobsIndex: { const JobId_t next_id = controller.getAvailableId(); std::vector jobs; const std::string executable = vm["executable"].as< std::string >(); const std::string jobcommand = vm["jobcommand"].as< std::string >(); createjobs(jobs, executable, jobcommand, next_id); controller.addJobs(jobs); controller.sendJobs(server, serverport); break; } case CheckResultsIndex: controller.checkResults(server, serverport); break; case ReceiveResultsIndex: case ReceiveMPQCIndex: controller.receiveResults(server, serverport); break; case RemoveAllIndex: controller.removeall(server, serverport); break; case ShutdownIndex: controller.shutdown(server, serverport); break; case UnknownCommandIndex: default: ELOG(0, "Unrecognized command '"+toString(command)+"'."); return 255; break; } { io_service.reset(); Info info("io_service: Phase Two"); io_service.run(); } // Final phase: Print result of command switch(commandIndex) { case AddJobsIndex: case CreateJobsIndex: break; case CheckResultsIndex: controller.printDoneJobs(); break; case ReceiveResultsIndex: { std::vector results = controller.getReceivedResults(); printReceivedResults(results); break; } case ReceiveMPQCIndex: { const std::string path = vm["fragment-path"].as< std::string >(); LOG(1, "INFO: Parsing fragment files from " << path << "."); std::vector results = controller.getReceivedResults(); printReceivedMPQCResults( results, path, getNoAtomsFromAdjacencyFile(path)); break; } case RemoveAllIndex: break; case ShutdownIndex: break; case UnknownCommandIndex: default: ELOG(0, "Unrecognized command '"+toString(command)+"'."); return 255; break; } Exitflag = controller.getExitflag(); } catch (std::exception& e) { std::cerr << e.what() << std::endl; } return Exitflag; }