diff --git a/CMakeLists.txt b/CMakeLists.txt index aedbd93..b9228e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,6 @@ option (WriteMoreInfo "Write statistic files with more info" OFF) add_subdirectory(${PROJECT_SOURCE_DIR}/external/fmt EXCLUDE_FROM_ALL) -add_subdirectory(${PROJECT_SOURCE_DIR}/external/finite-diff/ EXCLUDE_FROM_ALL) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) @@ -31,21 +30,13 @@ set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) # set(CMAKE_BUILD_TYPE RelWithDebInfo) #endif() -#include(FetchContent) -#FetchContent_Declare( -# finite-diff -# GIT_REPOSITORY https://github.com/zfergus/finite-diff.git -# GIT_TAG "v1.0.1" -# GIT_SHALLOW TRUE -#) -#FetchContent_MakeAvailable(finite-diff) + include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/external/fmt/include ${PROJECT_SOURCE_DIR}/external/LBFGSpp/include ${PROJECT_SOURCE_DIR}/external/ripser ${PROJECT_SOURCE_DIR}/external/eigen-3.4.0 ${PROJECT_SOURCE_DIR}/external/cpp-d4/include - ${PROJECT_SOURCE_DIR}/external/finite-diff/src ${PROJECT_SOURCE_DIR}/external/) include_directories(${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR} ) @@ -329,7 +320,8 @@ if(USE_D4) target_link_libraries(curcuma_core curcuma_d4) endif() -target_link_libraries(curcuma_core pthread fmt::fmt-header-only finitediff::finitediff) +#target_link_libraries(curcuma_core pthread fmt::fmt-header-only finitediff::finitediff) +target_link_libraries(curcuma_core pthread fmt::fmt-header-only ) if(WIN32) # Check if we are on Windows else() diff --git a/README.md b/README.md index 3c40669..2189f1d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ [![CodeFactor](https://www.codefactor.io/repository/github/conradhuebler/curcuma/badge)](https://www.codefactor.io/repository/github/conradhuebler/curcuma) [![Build](https://github.com/conradhuebler/curcuma/workflows/AutomaticBuild/badge.svg)](https://github.com/conradhuebler/curcuma/actions) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.4302722.svg)](https://doi.org/10.5281/zenodo.4302722) +![curcuma Logo](https://github.com/conradhuebler/curcuma/raw/master/misc/curcuma_II.png) # Curcuma diff --git a/external/CxxThreadPool b/external/CxxThreadPool index 7e85a79..e0f2fec 160000 --- a/external/CxxThreadPool +++ b/external/CxxThreadPool @@ -1 +1 @@ -Subproject commit 7e85a79a8ce57c2af93d63c437b600f29a6a444b +Subproject commit e0f2fec388d0fb1f58f6da1894729a04aa407cbb diff --git a/external/LBFGSpp b/external/LBFGSpp index 7e38486..d16c89d 160000 --- a/external/LBFGSpp +++ b/external/LBFGSpp @@ -1 +1 @@ -Subproject commit 7e3848617795ddd0e25f4b772e679adfee583229 +Subproject commit d16c89da2a490307791764aeb2778fa0c1458960 diff --git a/external/tblite b/external/tblite index 4ad71e5..f6df2e4 160000 --- a/external/tblite +++ b/external/tblite @@ -1 +1 @@ -Subproject commit 4ad71e5d821625ada9a468bb368d7cc5e3ce30c4 +Subproject commit f6df2e43a5811fda6027dd9ba2f200c2486d5684 diff --git a/misc/curcuma_I.png b/misc/curcuma_I.png new file mode 100644 index 0000000..41810f6 Binary files /dev/null and b/misc/curcuma_I.png differ diff --git a/misc/curcuma_II.png b/misc/curcuma_II.png new file mode 100644 index 0000000..b4abf53 Binary files /dev/null and b/misc/curcuma_II.png differ diff --git a/misc/curcuma_III.png b/misc/curcuma_III.png new file mode 100644 index 0000000..188afc7 Binary files /dev/null and b/misc/curcuma_III.png differ diff --git a/misc/curcuma_WH.svg b/misc/curcuma_WH.svg new file mode 100644 index 0000000..40440f1 --- /dev/null +++ b/misc/curcuma_WH.svg @@ -0,0 +1,2263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + curcuma + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + curcuma + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + curcuma + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + curcuma + + + + diff --git a/src/capabilities/confscan.cpp b/src/capabilities/confscan.cpp index 8a9f633..5f10226 100644 --- a/src/capabilities/confscan.cpp +++ b/src/capabilities/confscan.cpp @@ -275,6 +275,16 @@ bool ConfScan::openFile() int molecule = 0; PersistentDiagram diagram(m_defaults); FileIterator file(m_filename); + int calcH = 0; + int calcI = 0; + // std::cout << m_looseThresh <<" "<Energy(); @@ -293,9 +303,16 @@ bool ConfScan::openFile() if (m_noname) mol->setName(NamePattern(molecule)); - mol->CalculateRotationalConstants(); - diagram.setDistanceMatrix(mol->LowerDistanceVector()); - mol->setPersisentImage(diagram.generateImage(diagram.generatePairs())); + auto rot = std::chrono::system_clock::now(); + if ((m_looseThresh & 1) == 1) + mol->CalculateRotationalConstants(); + auto ripser = std::chrono::system_clock::now(); + if ((m_looseThresh & 2) == 2) { + diagram.setDistanceMatrix(mol->LowerDistanceVector()); + mol->setPersisentImage(diagram.generateImage(diagram.generatePairs())); + } + calcH += std::chrono::duration_cast(std::chrono::system_clock::now() - ripser).count(); + calcI += std::chrono::duration_cast(ripser - rot).count(); std::pair pair(mol->Name(), mol); m_molecules.push_back(pair); @@ -325,16 +342,30 @@ bool ConfScan::openFile() energy = interface.CalculateEnergy(false); } min_energy = std::min(min_energy, energy); - mol->CalculateRotationalConstants(); + auto rot = std::chrono::system_clock::now(); + if ((m_looseThresh & 1) == 1) + mol->CalculateRotationalConstants(); + auto ripser = std::chrono::system_clock::now(); + + // diagram.setDimension(2); + if ((m_looseThresh & 2) == 2) { + diagram.setDistanceMatrix(mol->LowerDistanceVector()); + mol->setPersisentImage(diagram.generateImage(diagram.generatePairs())); + } + calcH += std::chrono::duration_cast(std::chrono::system_clock::now() - ripser).count(); + calcI += std::chrono::duration_cast(ripser - rot).count(); - diagram.setDimension(2); - diagram.setDistanceMatrix(mol->LowerDistanceVector()); - mol->setPersisentImage(diagram.generateImage(diagram.generatePairs())); m_previously_accepted.push_back(mol); } m_lowest_energy = min_energy; m_result = m_previously_accepted; } + m_timing_rot = calcI; + m_timing_ripser = calcH; + std::cout << "time for calculating descriptors:" << std::endl; + std::cout << "Rotational constants " << m_timing_rot / 1000.0 << " seconds." << std::endl; + std::cout << "Ripser bar code " << m_timing_ripser / 1000.0 << " seconds." << std::endl; + return true; } @@ -1224,6 +1255,10 @@ void ConfScan::Finalise() { TriggerWriteRestart(); + std::cout << "time for calculating descriptors:" << std::endl; + std::cout << "Rotational constants " << m_timing_rot << std::endl; + std::cout << "Ripser bar code difference" << m_timing_ripser << std::endl; + int i = 0; for (const auto molecule : m_stored_structures) { double difference = abs(molecule->Energy() - m_lowest_energy) * 2625.5; diff --git a/src/capabilities/confscan.h b/src/capabilities/confscan.h index eda9a2d..8ca387e 100644 --- a/src/capabilities/confscan.h +++ b/src/capabilities/confscan.h @@ -357,7 +357,7 @@ class ConfScan : public CurcumaMethod { int m_threads = 1; int m_RMSDElement = 7; int m_molaligntol = 10; - + int m_timing_rot = 0, m_timing_ripser = 0; bool m_writeXYZ = false; bool m_check_connections = false; bool m_force_reorder = false, m_prevent_reorder = false; diff --git a/src/capabilities/confsearch.cpp b/src/capabilities/confsearch.cpp index 116bb15..0c00c56 100644 --- a/src/capabilities/confsearch.cpp +++ b/src/capabilities/confsearch.cpp @@ -48,11 +48,8 @@ ConfSearch::~ConfSearch() void ConfSearch::setFile(const std::string& filename) { + getBasename(filename); m_filename = filename; - std::string name = std::string(filename); - for (int i = 0; i < 4; ++i) - name.pop_back(); - m_basename = name; FileIterator file(m_filename); while (!file.AtEnd()) { @@ -69,17 +66,21 @@ bool ConfSearch::Initialise() void ConfSearch::start() { nlohmann::json md = CurcumaMDJson; - md["gfn"] = m_gfn; + md["method"] = m_method; md["impuls_scaling"] = 0.75; - md["dt"] = 0.5; - md["velo"] = 4; - md["thermostat_steps"] = 400; + md["dt"] = 0.25; + md["velo"] = 1.5; + md["coupling"] = 10; md["hmass"] = 1; - md["berendson"] = 200; + md["thermostat"] = "csvr"; md["maxtime"] = m_time; md["rmsd"] = m_rmsd; md["resuce"] = true; - md["print"] = m_time / 3.0; + md["print"] = 1000; + md["rattle"] = true; + md["rattle_tolerance"] = 1e-9; + md["unique"] = true; + for (m_currentT = m_startT; m_currentT >= m_endT; m_currentT -= m_deltaT) { std::cout << std::endl << std::endl @@ -94,7 +95,7 @@ void ConfSearch::start() // uniques.push_back(r); nlohmann::json opt = CurcumaOptJson; - opt["gfn"] = m_gfn; + opt["method"] = m_method; opt["threads"] = m_threads; PerformOptimisation(std::string("fff"), opt); @@ -102,7 +103,7 @@ void ConfSearch::start() scan["rmsdmethod"] = "hybrid"; scan["fewerFile"] = true; scan["threads"] = m_threads; - scan["gfn"] = m_gfn; + scan["method"] = m_method; PerformFilter(std::string("fff"), scan); /* @@ -110,9 +111,9 @@ void ConfSearch::start() delete m_in_stack[i]; m_in_stack.clear(); */ - std::string name = m_basename; - setFile(m_basename + "." + std::to_string(m_currentT) + ".opt.accepted.xyz"); - m_basename = name; + std::string name = Basename(); + setFile(Basename() + "." + std::to_string(m_currentT) + ".opt.accepted.xyz"); + // m_basename = name; /* for (int i = 0; i < uniques.size(); ++i) @@ -134,18 +135,20 @@ std::string ConfSearch::PerformMolecularDynamics(const std::vector& m int index = 0; for (int repeat = 0; repeat < m_repeat; ++repeat) { for (int i = 0; i < molecules.size(); ++i) { - MDThread* thread = new MDThread(index, parameter); + MDThread* thread = new MDThread(parameter); + thread->setThreadId(index); + thread->setBasename(Basename() + ".confsearch"); thread->setMolecule(molecules[i * repeat]); index++; pool->addThread(thread); } } - if (m_gfn == 66) + if (m_method.compare("gfnff") == 0) pool->setActiveThreadCount(1); else pool->setActiveThreadCount(m_threads); pool->StartAndWait(); - std::string file = m_basename + "." + std::to_string(m_currentT) + ".unqiues.xyz"; + std::string file = Basename() + "." + std::to_string(m_currentT) + ".unqiues.xyz"; for (const auto& thread : pool->Finished()) { auto structures = static_cast(thread)->MDDriver()->UniqueMolecules(); @@ -159,11 +162,11 @@ std::string ConfSearch::PerformMolecularDynamics(const std::vector& m std::string ConfSearch::PerformOptimisation(const std::string& f, const nlohmann::json& parameter) { - std::string file = m_basename + "." + std::to_string(m_currentT) + ".unqiues.xyz"; + std::string file = Basename() + "." + std::to_string(m_currentT) + ".unqiues.xyz"; CurcumaOpt optimise(parameter, false); optimise.setFileName(file); - optimise.setBaseName(m_basename + "." + std::to_string(m_currentT)); + optimise.overrideBasename(Basename() + "." + std::to_string(m_currentT)); optimise.start(); return file; @@ -180,7 +183,7 @@ std::string ConfSearch::PerformOptimisation(const std::string& f, const nlohmann auto mols = opt.Molecules(); for (const Molecule& mol : *mols) { optimised.push_back(new Molecule(mol)); - mol.appendXYZFile(m_basename + std::to_string(m_currentT) + ".optimised.xyz"); + mol.appendXYZFile(Basename() + std::to_string(m_currentT) + ".optimised.xyz"); } return optimised; */ @@ -189,7 +192,7 @@ std::string ConfSearch::PerformOptimisation(const std::string& f, const nlohmann std::string ConfSearch::PerformFilter(const std::string& f, const nlohmann::json& parameter) { ConfScan* scan = new ConfScan(parameter, false); - scan->setFileName(m_basename + "." + std::to_string(m_currentT) + ".opt.xyz"); + scan->setFileName(Basename() + "." + std::to_string(m_currentT) + ".opt.xyz"); scan->start(); return std::string("fff"); } @@ -211,7 +214,7 @@ void ConfSearch::ReadControlFile() void ConfSearch::LoadControlJson() { - m_gfn = Json2KeyWord(m_defaults, "GFN"); + m_method = Json2KeyWord(m_defaults, "method"); m_spin = Json2KeyWord(m_defaults, "spin"); m_charge = Json2KeyWord(m_defaults, "charge"); // m_single_step = Json2KeyWord(m_defaults, "dT"); // * fs2amu; diff --git a/src/capabilities/confsearch.h b/src/capabilities/confsearch.h index 4414bbb..c24c3de 100644 --- a/src/capabilities/confsearch.h +++ b/src/capabilities/confsearch.h @@ -33,7 +33,7 @@ #include "src/capabilities/curcumamethod.h" static const nlohmann::json ConfSearchJson{ - { "gfn", 44 }, + { "method", "uff" }, { "charge", 0 }, { "Spin", 0 }, { "startT", 500 }, @@ -82,10 +82,10 @@ class ConfSearch : public CurcumaMethod { virtual void LoadControlJson() override; StringList m_error_list; - std::string m_filename, m_basename; + std::string m_filename, m_method; bool m_silent = true; std::vector m_in_stack, m_final_stack; - int m_gfn = 44, m_spin = 0, m_charge = 0, m_repeat = 5, m_threads = 1; + int m_spin = 0, m_charge = 0, m_repeat = 5, m_threads = 1; double m_time = 1e4, m_startT = 500, m_endT = 300, m_deltaT = 50, m_currentT = 0, m_rmsd = 1.25; }; diff --git a/src/capabilities/curcumamethod.cpp b/src/capabilities/curcumamethod.cpp index 3668bc6..18d791e 100644 --- a/src/capabilities/curcumamethod.cpp +++ b/src/capabilities/curcumamethod.cpp @@ -128,3 +128,11 @@ bool CurcumaMethod::CheckStop() const return result; #endif } + +void CurcumaMethod::getBasename(const std::string& filename) +{ + std::string name = std::string(filename); + for (int i = 0; i < 4; ++i) + name.pop_back(); + m_basename = name; +} diff --git a/src/capabilities/curcumamethod.h b/src/capabilities/curcumamethod.h index b24b636..41c15b0 100644 --- a/src/capabilities/curcumamethod.h +++ b/src/capabilities/curcumamethod.h @@ -51,6 +51,10 @@ class CurcumaMethod { bool CheckStop() const; + std::string Basename() const { return m_basename; } + void getBasename(const std::string& filename); + void overrideBasename(const std::string& basename) { m_basename = basename; } + protected: void TriggerWriteRestart(); @@ -81,4 +85,5 @@ class CurcumaMethod { StringList m_error_list; bool m_silent = true; + std::string m_basename; }; diff --git a/src/capabilities/curcumaopt.cpp b/src/capabilities/curcumaopt.cpp index 8e63008..4ab1ebb 100644 --- a/src/capabilities/curcumaopt.cpp +++ b/src/capabilities/curcumaopt.cpp @@ -49,7 +49,7 @@ using namespace LBFGSpp; int OptThread::execute() { - m_final = CurcumaOpt::LBFGSOptimise(&m_molecule, m_controller, m_result, &m_intermediate); + m_final = CurcumaOpt::LBFGSOptimise(&m_molecule, m_controller, m_result, &m_intermediate, ThreadId(), Basename() + ".opt.trj"); return 0; } @@ -90,6 +90,7 @@ void CurcumaOpt::LoadControlJson() void CurcumaOpt::start() { + getBasename(m_filename); if (m_file_set) { FileIterator file(m_filename); std::multimap results; @@ -123,15 +124,15 @@ void CurcumaOpt::ProcessMoleculesSerial(const std::vector& molecules) auto start = std::chrono::system_clock::now(); interface.updateGeometry(iter->Coords()); double energy = interface.CalculateEnergy(true, true); - std::cout << "dipole?" << std::endl; #ifdef USE_TBLITE if (method.compare("gfn2") == 0) { std::vector dipole = interface.Dipole(); std::cout << std::endl << std::endl - << "Dipole momement " << dipole[0] << " " << dipole[1] << " " << dipole[2] << " : " << sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]) << std::endl; + << "Dipole momement (GFN2)" << dipole[0] << " " << dipole[1] << " " << dipole[2] << " : " << sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]) << std::endl; } #endif + if (m_hessian) { Hessian hess(m_method, m_defaults, m_threads); hess.setMolecule(*iter); @@ -166,8 +167,11 @@ void CurcumaOpt::ProcessMolecules(const std::vector& molecules) else th = new SPThread; + th->setBaseName(Basename()); + th->setMolecule(*iter); th->setController(m_defaults); + th->setThreadId(i); thread_block.push_back(th); pool->addThread(th); @@ -226,30 +230,29 @@ double CurcumaOpt::SinglePoint(const Molecule* initial, const json& controller, interface.setMolecule(*initial); double energy = interface.CalculateEnergy(true, true); - std::cout << "dipole1" << std::endl; - + double store = 0; #ifdef USE_TBLITE if (method.compare("gfn2") == 0) { std::vector dipole = interface.Dipole(); std::cout << std::endl << std::endl - << "Dipole momement " << dipole[0] << " " << dipole[1] << " " << dipole[2] << " : " << sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]) * 2.5418 << std::endl; + << "Dipole momement (GNF2)" << dipole[0] << " " << dipole[1] << " " << dipole[2] << " : " << sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]) * 2.5418 << std::endl; + store = sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]); } #endif return energy; } -Molecule CurcumaOpt::LBFGSOptimise(const Molecule* initial, const json& controller, std::string& output, std::vector* intermediate) + +Molecule CurcumaOpt::LBFGSOptimise(Molecule* initial, const json& controller, std::string& output, std::vector* intermediate, int thread, const std::string& basename) { bool printOutput = Json2KeyWord(controller, "printOutput"); double dE = Json2KeyWord(controller, "dE"); double dRMSD = Json2KeyWord(controller, "dRMSD"); - double LBFGS_eps = Json2KeyWord(controller, "LBFGS_eps"); double GradNorm = Json2KeyWord(controller, "GradNorm"); std::string method = Json2KeyWord(controller, "method"); int MaxIter = Json2KeyWord(controller, "MaxIter"); - int StoreIntermediate = Json2KeyWord(controller, "StoreIntermediate"); int ConvCount = Json2KeyWord(controller, "ConvCount"); int SingleStep = Json2KeyWord(controller, "SingleStep"); @@ -261,6 +264,7 @@ Molecule CurcumaOpt::LBFGSOptimise(const Molecule* initial, const json& controll std::vector constrain; Geometry geometry = initial->getGeometry(); intermediate->push_back(initial); + Molecule previous(initial); Molecule next(initial); Vector parameter(3 * initial->AtomCount()), old_parameter(3 * initial->AtomCount()); @@ -276,20 +280,26 @@ Molecule CurcumaOpt::LBFGSOptimise(const Molecule* initial, const json& controll interface.setMolecule(*initial); double final_energy = interface.CalculateEnergy(true); - + initial->setEnergy(final_energy); + initial->writeXYZFile(basename + ".t" + std::to_string(thread) + ".xyz"); + std::cout << "Initial energy " << final_energy << "Eh" << std::endl; LBFGSParam param; - param.epsilon = LBFGS_eps; - param.m = StoreIntermediate; - // param.max_linesearch = 10000; - // param.ftol = 1e-10; - // param.max_step = 10; - /* - param.linesearch = 3; - param.ftol = 1e-6; - param.wolfe = 0.1; - */ - - LBFGSSolver solver(param); + param.m = Json2KeyWord(controller, "LBFGS_m");; + param.epsilon = Json2KeyWord(controller, "LBFGS_eps_abs"); + param.epsilon_rel = Json2KeyWord(controller, "LBFGS_eps_rel"); + param.past = Json2KeyWord(controller, "LBFGS_past"); + param.delta = Json2KeyWord(controller, "LBFGS_delta"); + param.linesearch = Json2KeyWord(controller, "LBFGS_LST"); + param.max_linesearch = Json2KeyWord(controller, "LBFGS_ls_iter"); + param.min_step = Json2KeyWord(controller, "LBFGS_min_step"); + param.ftol = Json2KeyWord(controller, "LBFGS_ftol"); + param.wolfe = Json2KeyWord(controller, "LBFGS_wolfe"); + + + + //param.linesearch = Json2KeyWord(controller, "LBFGS_LS"); + + LBFGSSolver solver(param); LBFGSInterface fun(3 * initial->AtomCount()); fun.setMolecule(initial); fun.setInterface(&interface); @@ -334,7 +344,7 @@ Molecule CurcumaOpt::LBFGSOptimise(const Molecule* initial, const json& controll if (converged) perform_optimisation = false; - for (iteration = 0; iteration <= MaxIter && perform_optimisation; ++iteration) { + for (iteration = 1; iteration <= MaxIter && perform_optimisation; ++iteration) { old_parameter = parameter; try { solver.SingleStep(fun, parameter, fx); @@ -380,6 +390,23 @@ Molecule CurcumaOpt::LBFGSOptimise(const Molecule* initial, const json& controll } next.setGeometry(geometry); } + if((fun.m_energy - final_energy)*2625.5 > 10 && iteration > 10) + { + if (printOutput) { + output += fmt::format("Energy rises too much!\n"); + output += fmt::format("{0: ^75}\n\n", "*** Geometry Optimisation sufficiantly converged ***"); + std::cout << output; + output.clear(); + } + error = true; + perform_optimisation = false; + for (int i = 0; i < atoms_count; ++i) { + geometry(i, 0) = old_parameter(3 * i); + geometry(i, 1) = old_parameter(3 * i + 1); + geometry(i, 2) = old_parameter(3 * i + 2); + } + } + if ((iteration % SingleStep == 0 && perform_optimisation) || fun.isError()) { parameter = fun.Parameter(); @@ -417,6 +444,13 @@ Molecule CurcumaOpt::LBFGSOptimise(const Molecule* initial, const json& controll + 4 * (solver.isConverged()) + 8 * (solver.final_grad_norm() < GradNorm); perform_optimisation = ((converged & ConvCount) != ConvCount) && (fun.isError() == 0); + std::ifstream test_file("stop"); + bool result = test_file.is_open(); + test_file.close(); + if (result) { + perform_optimisation = false; + error = true; + } /* std::cout << (abs(fun.m_energy - final_energy) * 2625.5 < 0.05) << " " << (int(driver->RMSD() < 0.01)) @@ -435,8 +469,10 @@ Molecule CurcumaOpt::LBFGSOptimise(const Molecule* initial, const json& controll final_energy = fun.m_energy; if (next.Check() == 0) { previous = next; - previous.setEnergy(final_energy); + next.setEnergy(final_energy); intermediate->push_back(next); + next.appendXYZFile(basename + ".t" + std::to_string(thread) + ".xyz"); + } else { perform_optimisation = false; error = true; diff --git a/src/capabilities/curcumaopt.h b/src/capabilities/curcumaopt.h index 6947489..cc25938 100644 --- a/src/capabilities/curcumaopt.h +++ b/src/capabilities/curcumaopt.h @@ -21,6 +21,8 @@ #include "external/CxxThreadPool/include/CxxThreadPool.h" +#include "optimiser/LBFGSppInterface.h" + #include "curcumamethod.h" static json CurcumaOptJson{ @@ -30,11 +32,20 @@ static json CurcumaOptJson{ { "dRMSD", 0.01 }, { "method", "uff" }, { "MaxIter", 5000 }, - { "LBFGS_eps", 1e-5 }, - { "StoreIntermediate", 2000 }, - { "SingleStep", 20 }, + // { "LBFGS_LS", 3}, + { "LBFGS_m", 2000 }, + { "LBFGS_past", 0}, + { "LBFGS_eps_abs", 1e-5 }, + { "LBFGS_eps_rel", 1e-5 }, + { "LBFGS_delta", 0 }, + { "LBFGS_LST", 3}, + { "LBFGS_ls_iter", 2 }, + { "LBFGS_min_step", 1e-4 }, + { "LBFGS_ftol", 1e-4 }, + { "LBFGS_wolfe", 0.9 }, + { "SingleStep", 1 }, { "ConvCount", 11 }, - { "GradNorm", 0.001 }, + { "GradNorm", 1e-4 }, { "Threads", 1 }, { "Charge", 0 }, { "Spin", 0 }, @@ -66,12 +77,15 @@ class SPThread : public CxxThread { inline void setController(const json& controller) { m_controller = controller; } std::string Output() const { return m_result; } const std::vector* Intermediates() const { return &m_intermediate; } + void setBaseName(const std::string& basename) { m_basename = basename; } + std::string Basename() const { return m_basename; } protected: std::string m_result; Molecule m_molecule, m_final; json m_controller = OptJsonPrivate; std::vector m_intermediate; + std::string m_basename; }; class OptThread : public SPThread { @@ -89,9 +103,15 @@ class CurcumaOpt : public CurcumaMethod { void setFileName(const std::string& filename) { m_filename = filename; - m_basename = std::string(m_filename); - for (int i = 0; i < 4; ++i) - m_basename.pop_back(); + + getBasename(filename); + std::ofstream tfile1; + tfile1.open(Optfile()); + tfile1.close(); + + std::ofstream tfile2; + tfile2.open(Trjfile()); + tfile2.close(); m_file_set = true; m_mol_set = false; @@ -105,10 +125,10 @@ class CurcumaOpt : public CurcumaMethod { m_mol_set = false; m_mols_set = true; } - - inline void setBaseName(const std::string& basename) + /* + inline void setBaseName() { - m_basename = basename; + //m_basename = basename; std::ofstream tfile1; tfile1.open(Optfile()); @@ -118,16 +138,16 @@ class CurcumaOpt : public CurcumaMethod { tfile2.open(Trjfile()); tfile2.close(); } +*/ - inline std::string Optfile() const { return std::string(m_basename + ".opt.xyz"); } - inline std::string Trjfile() const { return std::string(m_basename + ".trj.xyz"); } - + inline std::string Optfile() const { return std::string(Basename() + ".opt.xyz"); } + inline std::string Trjfile() const { return std::string(Basename() + ".trj.xyz"); } void start() override; // TODO make pure virtual and move all main action here void setSinglePoint(bool sp) { m_singlepoint = sp; } inline const std::vector* Molecules() const { return &m_molecules; } - static Molecule LBFGSOptimise(const Molecule* host, const json& controller, std::string& output, std::vector* intermediate); + static Molecule LBFGSOptimise(Molecule* host, const json& controller, std::string& output, std::vector* intermediate, int thread = -1, const std::string& basename = "base"); static double SinglePoint(const Molecule* initial, const json& controller, std::string& output); void clear(); @@ -150,7 +170,7 @@ class CurcumaOpt : public CurcumaMethod { void ProcessMolecules(const std::vector& molecule); void ProcessMoleculesSerial(const std::vector& molecule); - std::string m_filename, m_basename = "curcuma_job"; + std::string m_filename; std::string m_method = "UFF"; Molecule m_molecule; std::vector m_molecules; diff --git a/src/capabilities/docking.cpp b/src/capabilities/docking.cpp index 0264013..d663c02 100644 --- a/src/capabilities/docking.cpp +++ b/src/capabilities/docking.cpp @@ -360,13 +360,13 @@ void Docking::OptimiseBatch() /* Optimisation */ std::cout << "** Crude preoptimisation of " << m_optimise->Molecules()->size() << " complexes **" << std::endl; - m_optimise->setBaseName("Optimise_Crude_F2"); + m_optimise->overrideBasename("Optimise_Crude_F2"); m_optimise->start(); /* Optimisation of the structures with incorrect fragments */ std::cout << "** Crude preoptimisation of " << m_singlepoint->Molecules()->size() << " structures with wrong connectivity **" << std::endl; - m_singlepoint->setBaseName("Optimise_Crude_FX"); + m_singlepoint->overrideBasename("Optimise_Crude_FX"); m_singlepoint->start(); /* Update Optimiser to perform single point calculation (GFN2) of the at GFN-FF level optimised structures */ diff --git a/src/capabilities/optimiser/OptimiseDipoleScaling.h b/src/capabilities/optimiser/OptimiseDipoleScaling.h new file mode 100644 index 0000000..d7929bd --- /dev/null +++ b/src/capabilities/optimiser/OptimiseDipoleScaling.h @@ -0,0 +1,128 @@ +/* + * + * Copyright (C) 2023 Conrad Hübler + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + * + */ + +#pragma once + +#include +#include +#include + +#include + +#include "src/core/elements.h" +#include "src/core/global.h" +#include "src/core/molecule.h" +#include "src/core/pseudoff.h" + +#include "src/tools/geometry.h" + +#include "json.hpp" +#include +#include + +using json = nlohmann::json; + +using Eigen::VectorXd; +using namespace LBFGSpp; + +class LBFGSDipoleInterface { +public: + LBFGSDipoleInterface(int n_) + { + } + double operator()(const VectorXd& x, VectorXd& grad) + { + double fx = 0.0; + double dx = 5e-1; + std::vector scaling(x.size()); + for (int i = 0; i < scaling.size(); ++i) { + scaling[i] = x(i); + } + + auto dipole_vector = m_molecule->CalculateDipoleMoment(scaling); + double dipole = sqrt(dipole_vector[0] * dipole_vector[0] + dipole_vector[1] * dipole_vector[1] + dipole_vector[2] * dipole_vector[2]) * 2.5418; + fx = std::abs(m_dipole - dipole); + // std::cout << dipole << " " << m_dipole << " "<< fx<< std::endl; + for (int i = 0; i < scaling.size(); ++i) { + // if(std::abs(fx) < std::abs(m_smaller)) + // std::cout << scaling[i] << " "; + scaling[i] += dx; + dipole_vector = m_molecule->CalculateDipoleMoment(scaling); + double dipolep = sqrt(dipole_vector[0] * dipole_vector[0] + dipole_vector[1] * dipole_vector[1] + dipole_vector[2] * dipole_vector[2]) * 2.5418; + scaling[i] -= 2 * dx; + dipole_vector = m_molecule->CalculateDipoleMoment(scaling); + double dipolem = sqrt(dipole_vector[0] * dipole_vector[0] + dipole_vector[1] * dipole_vector[1] + dipole_vector[2] * dipole_vector[2]) * 2.5418; + + grad[i] = (std::abs(dipolep - dipole) - std::abs(dipolem - dipole)) / (2.0 * dx); + scaling[i] += dx; + } + // if(std::abs(fx) < std::abs(m_smaller)) + // std::cout << std::endl; + m_smaller = std::abs(fx); + m_parameter = x; + return fx; + } + + Vector Parameter() const { return m_parameter; } + void setMolecule(const Molecule* molecule) + { + m_molecule = molecule; + } + + double m_dipole; + double m_smaller = 1; + +private: + int m_atoms = 0; + Vector m_parameter; + const Molecule* m_molecule; +}; + +inline std::pair> OptimiseScaling(const Molecule* molecule, double dipole, double initial, double threshold, int maxiter) +{ + Vector parameter(molecule->AtomCount()); + for (int i = 0; i < molecule->AtomCount(); ++i) + parameter(i) = initial; + + Vector old_param = parameter; + // std::cout << parameter.transpose() << std::endl; + + LBFGSParam param; + param.epsilon = 1e-5; + LBFGSSolver solver(param); + LBFGSDipoleInterface fun(parameter.size()); + fun.m_dipole = dipole * 2.5418; + fun.setMolecule(molecule); + int iteration = 0; + // std::cout << parameter.transpose() << std::endl; + double fx; + int converged = solver.InitializeSingleSteps(fun, parameter, fx); + + for (iteration = 1; iteration <= maxiter && fx > threshold; ++iteration) { + solver.SingleStep(fun, parameter, fx); + std::cout << "Iteration: " << iteration << " Difference: " << fx << std::endl; + } + std::vector scaling(parameter.size()); + for (int i = 0; i < scaling.size(); ++i) + scaling[i] = parameter(i); + auto dipole_vector = molecule->CalculateDipoleMoment(scaling); + dipole = sqrt(dipole_vector[0] * dipole_vector[0] + dipole_vector[1] * dipole_vector[1] + dipole_vector[2] * dipole_vector[2]); + + return std::pair>(dipole, scaling); +} diff --git a/src/capabilities/rmsdtraj.cpp b/src/capabilities/rmsdtraj.cpp index e48dcf7..4979781 100644 --- a/src/capabilities/rmsdtraj.cpp +++ b/src/capabilities/rmsdtraj.cpp @@ -378,7 +378,7 @@ void RMSDTraj::Optimise() { CurcumaOpt optimise(m_controller["rmsdtraj"], true); optimise.setFileName(m_outfile + ".unique.xyz"); - optimise.setBaseName(m_outfile); + // optimise.setBaseName(m_outfile); optimise.start(); } diff --git a/src/capabilities/simplemd.cpp b/src/capabilities/simplemd.cpp index 3cdf402..bd3e494 100644 --- a/src/capabilities/simplemd.cpp +++ b/src/capabilities/simplemd.cpp @@ -67,10 +67,12 @@ void SimpleMD::LoadControlJson() m_timestep = Json2KeyWord(m_defaults, "dT"); m_maxtime = Json2KeyWord(m_defaults, "MaxTime"); m_T0 = Json2KeyWord(m_defaults, "T"); - m_centered = Json2KeyWord(m_defaults, "centered"); - m_dumb = Json2KeyWord(m_defaults, "dump"); + m_rmrottrans = Json2KeyWord(m_defaults, "rmrottrans"); + m_nocenter = Json2KeyWord(m_defaults, "nocenter"); + m_dump = Json2KeyWord(m_defaults, "dump"); m_print = Json2KeyWord(m_defaults, "print"); m_max_top_diff = Json2KeyWord(m_defaults, "MaxTopoDiff"); + m_seed = Json2KeyWord(m_defaults, "seed"); m_rmsd = Json2KeyWord(m_defaults, "rmsd"); m_impuls = Json2KeyWord(m_defaults, "impuls"); @@ -92,24 +94,88 @@ void SimpleMD::LoadControlJson() m_initfile = Json2KeyWord(m_defaults, "initfile"); m_norestart = Json2KeyWord(m_defaults, "norestart"); m_dt2 = m_timestep * m_timestep; - if (Json2KeyWord(m_defaults, "rattle")) { - m_integrator = [=](double* coord, double* grad) { + if (Json2KeyWord(m_defaults, "rattle")) { + Integrator = [=](double* coord, double* grad) { this->Rattle(coord, grad); }; - m_rattle_tolerance = Json2KeyWord(m_defaults, "rattle_tolerance"); - + m_rattle_tolerance_a = Json2KeyWord(m_defaults, "rattle_tolerance_a") / m_timestep; + m_rattle_tolerance_b = Json2KeyWord(m_defaults, "rattle_tolerance_b") / m_timestep; + m_coupling = m_timestep; + m_rattle = Json2KeyWord(m_defaults, "rattle"); std::cout << "Using rattle to constrained bonds!" << std::endl; } else { - m_integrator = [=](double* coord, double* grad) { + Integrator = [=](double* coord, double* grad) { this->Verlet(coord, grad); }; } + + if (Json2KeyWord(m_defaults, "cleanenergy")) { + Energy = [=](double* coord, double* grad) -> double { + return this->CleanEnergy(coord, grad); + }; + std::cout << "Energy Calculator will be set up for each step! Single steps are slower, but more reliable. Recommended for the combination of GFN2 and solvation." << std::endl; + } else { + Energy = [=](double* coord, double* grad) -> double { + return this->FastEnergy(coord, grad); + }; + std::cout << "Energy Calculator will NOT be set up for each step! Fast energy calculation! This is the default way and should not be changed unless the energy and gradient calculation are unstable (happens with GFN2 and solvation)." << std::endl; + } + + if (Json2KeyWord(m_defaults, "wall").compare("spheric") == 0) { + if (Json2KeyWord(m_defaults, "wall_type").compare("logfermi") == 0) { + WallPotential = [=](double* grad) -> double { + this->m_wall_potential = this->ApplySphericLogFermiWalls(grad); + return m_wall_potential; + }; + } else if (Json2KeyWord(m_defaults, "wall_type").compare("harmonic") == 0) { + WallPotential = [=](double* grad) -> double { + this->m_wall_potential = this->ApplySphericHarmonicWalls(grad); + return m_wall_potential; + }; + } else { + std::cout << "Did not understand wall potential input. Exit now!" << std::endl; + exit(1); + } + std::cout << "Setting up spherical potential" << std::endl; + + InitialiseWalls(); + } else if (Json2KeyWord(m_defaults, "wall").compare("rect") == 0) { + if (Json2KeyWord(m_defaults, "wall_type").compare("logfermi") == 0) { + WallPotential = [=](double* grad) -> double { + this->m_wall_potential = this->ApplyRectLogFermiWalls(grad); + return m_wall_potential; + }; + } else if (Json2KeyWord(m_defaults, "wall_type").compare("harmonic") == 0) { + WallPotential = [=](double* grad) -> double { + this->m_wall_potential = this->ApplyRectHarmonicWalls(grad); + return m_wall_potential; + }; + + } else { + std::cout << "Did not understand wall potential input. Exit now!" << std::endl; + exit(1); + } + std::cout << "Setting up rectangular potential" << std::endl; + + InitialiseWalls(); + } else + WallPotential = [=](double* grad) -> double { + return 0; + }; } bool SimpleMD::Initialise() { - if (!m_norestart) - LoadRestartInformation(); + static std::random_device rd{}; + static std::mt19937 gen{ rd() }; + if (m_seed == -1) { + const auto start = std::chrono::high_resolution_clock::now(); + m_seed = std::chrono::duration_cast(start.time_since_epoch()).count(); + } else if (m_seed == 0) + m_seed = m_T0 * m_mass.size(); + std::cout << "Random seed is " << m_seed << std::endl; + gen.seed(m_seed); + if (m_initfile.compare("none") != 0) { json md; std::ifstream restart_file(m_initfile); @@ -122,17 +188,24 @@ bool SimpleMD::Initialise() } LoadRestartInformation(md); m_restart = true; - } + } else if (!m_norestart) + LoadRestartInformation(); + if (m_molecule.AtomCount() == 0) return false; if (!m_restart) { std::ofstream result_file; - result_file.open(m_basename + ".trj.xyz"); + result_file.open(Basename() + ".trj.xyz"); result_file.close(); } m_natoms = m_molecule.AtomCount(); m_molecule.setCharge(0); + if (!m_nocenter) { + std::cout << "Move stucture to the origin ... " << std::endl; + m_molecule.setGeometry(GeometryTools::TranslateGeometry(m_molecule.getGeometry(), GeometryTools::Centroid(m_molecule.getGeometry()), Position{ 0, 0, 0 })); + } else + std::cout << "Move stucture NOT to the origin ... " << std::endl; m_mass = std::vector(3 * m_natoms, 0); m_rmass = std::vector(3 * m_natoms, 0); @@ -208,14 +281,16 @@ bool SimpleMD::Initialise() rmsdtraj["rmsd"] = m_rmsd; rmsdtraj["writeRMSD"] = false; m_unqiue = new RMSDTraj(rmsdtraj, true); - m_unqiue->setBaseName(m_basename + ".xyz"); + m_unqiue->setBaseName(Basename() + ".xyz"); m_unqiue->Initialise(); } + m_dof = 3 * m_natoms; + InitConstrainedBonds(); if (m_writeinit) { json init = WriteRestartInformation(); std::ofstream result_file; - result_file.open(m_basename + ".init.json"); + result_file.open(Basename() + ".init.json"); result_file << init; result_file.close(); } @@ -225,32 +300,39 @@ bool SimpleMD::Initialise() void SimpleMD::InitConstrainedBonds() { - // current just all bonds - if (m_rattle_tolerance > 1) { - std::cout << "Removing contstraints" << std::endl; - return; - } - auto m = m_molecule.DistanceMatrix(); - m_topo_initial = m.second; - for (int i = 0; i < m_molecule.AtomCount(); ++i) { - for (int j = 0; j < i; ++j) { - if (m.second(i, j)) { - std::pair indicies(i, j); - std::pair, double> bond(indicies, m_molecule.CalculateDistance(i, j) * m_molecule.CalculateDistance(i, j)); - m_bond_constrained.push_back(std::pair, double>(bond)); + if (m_rattle) { + auto m = m_molecule.DistanceMatrix(); + m_topo_initial = m.second; + for (int i = 0; i < m_molecule.AtomCount(); ++i) { + for (int j = 0; j < i; ++j) { + if (m.second(i, j)) { + if (m_rattle == 2) { + if (m_molecule.Atom(i).first != 1 && m_molecule.Atom(j).first != 1) + continue; + } + std::pair indicies(i, j); + std::pair, double> bond(indicies, m_molecule.CalculateDistance(i, j) * m_molecule.CalculateDistance(i, j)); + m_bond_constrained.push_back(std::pair, double>(bond)); + // std::cout << i << " " << j << " " << bond.second << std::endl; + } } } } + std::cout << m_dof << " initial degrees of freedom " << std::endl; + std::cout << m_bond_constrained.size() << " constrains active" << std::endl; + m_dof -= m_bond_constrained.size(); + std::cout << m_dof << " degrees of freedom remaining ..." << std::endl; } void SimpleMD::InitVelocities(double scaling) { - std::random_device rd{}; - std::mt19937 gen{ rd() }; + static std::random_device rd{}; + static std::mt19937 gen{ rd() }; std::normal_distribution<> d{ 0, 1 }; double Px = 0.0, Py = 0.0, Pz = 0.0; for (int i = 0; i < m_natoms; ++i) { - double v0 = sqrt(kb * m_T0 * amu2au / (m_mass[i])) * scaling / fs2amu; + double v0 = sqrt(kb_Eh * m_T0 * amu2au / (m_mass[i])) * scaling / fs2amu; + // double v0 = sqrt(kb_SI * m_T0 / (m_mass[i] * atomic_mass)) * scaling; m_velocities[3 * i + 0] = v0 * d(gen); m_velocities[3 * i + 1] = v0 * d(gen); m_velocities[3 * i + 2] = v0 * d(gen); @@ -265,6 +347,30 @@ void SimpleMD::InitVelocities(double scaling) } } +void SimpleMD::InitialiseWalls() +{ + /* + { "wall_xl", 0}, + { "wall_yl", 0}, + { "wall_zl", 0}, + { "wall_x_min", 0}, + { "wall_x_max", 0}, + { "wall_y_min", 0}, + { "wall_y_max", 0}, + { "wall_z_min", 0}, + { "wall_z_max", 0},*/ + m_wall_spheric_radius = Json2KeyWord(m_defaults, "wall_spheric_radius"); + m_wall_temp = Json2KeyWord(m_defaults, "wall_temp"); + m_wall_beta = Json2KeyWord(m_defaults, "wall_beta"); + + m_wall_x_min = Json2KeyWord(m_defaults, "wall_x_min"); + m_wall_x_max = Json2KeyWord(m_defaults, "wall_x_max"); + m_wall_y_min = Json2KeyWord(m_defaults, "wall_y_min"); + m_wall_y_max = Json2KeyWord(m_defaults, "wall_y_max"); + m_wall_z_min = Json2KeyWord(m_defaults, "wall_z_min"); + m_wall_z_max = Json2KeyWord(m_defaults, "wall_z_max"); +} + nlohmann::json SimpleMD::WriteRestartInformation() { nlohmann::json restart; @@ -277,7 +383,8 @@ nlohmann::json SimpleMD::WriteRestartInformation() restart["velocities"] = Tools::DoubleVector2String(m_velocities); restart["geometry"] = Tools::DoubleVector2String(m_current_geometry); restart["gradient"] = Tools::DoubleVector2String(m_gradient); - restart["centered"] = m_centered; + restart["rmrottrans"] = m_rmrottrans; + restart["nocenter"] = m_nocenter; restart["average_T"] = m_aver_Temp; restart["average_Epot"] = m_aver_Epot; restart["average_Ekin"] = m_aver_Ekin; @@ -338,7 +445,11 @@ bool SimpleMD::LoadRestartInformation(const json& state) } catch (json::type_error& e) { } try { - m_centered = state["centered"]; + m_rmrottrans = state["rmrottrans"]; + } catch (json::type_error& e) { + } + try { + m_nocenter = state["nocenter"]; } catch (json::type_error& e) { } try { @@ -394,10 +505,10 @@ bool SimpleMD::LoadRestartInformation(const json& state) } catch (json::type_error& e) { } if (geometry.size()) { - m_current_geometry = Tools::String2DoubleVec(geometry); + m_current_geometry = Tools::String2DoubleVec(geometry, "|"); } if (velocities.size()) { - m_velocities = Tools::String2DoubleVec(velocities); + m_velocities = Tools::String2DoubleVec(velocities, "|"); } m_restart = geometry.size() && velocities.size(); @@ -452,20 +563,21 @@ void SimpleMD::start() << "\t" << "T" << std::endl; #endif - m_Epot = Gradient(coord, gradient); + m_Epot = Energy(coord, gradient); m_Ekin = EKin(); m_Etot = m_Epot + m_Ekin; int m_step = 0; PrintStatus(); + for (; m_currentStep < m_maxtime;) { + auto step0 = std::chrono::system_clock::now(); - for (; m_currentStep <= m_maxtime;) { if (CheckStop() == true) { TriggerWriteRestart(); return; } - if (m_step % m_dumb == 0) { + if (m_step % m_dump == 0) { bool write = WriteGeometry(); if (write) { states.push_back(WriteRestartInformation()); @@ -485,41 +597,54 @@ void SimpleMD::start() for (int i = 0; i < 3 * m_natoms; ++i) { coord[i] = m_current_geometry[i]; } - Gradient(coord, gradient); + Energy(coord, gradient); m_Ekin = EKin(); m_Etot = m_Epot + m_Ekin; m_current_rescue++; PrintStatus(); + m_time_step = 0; } } - if (m_centered && m_step % m_centered == 0 && m_step >= m_centered) { + + if (m_rmrottrans == 1) + RemoveRotation(m_velocities); + else if (m_rmrottrans == 2) + RemoveRotations(m_velocities); + else if (m_rmrottrans == 3) { + RemoveRotations(m_velocities); RemoveRotation(m_velocities); } - m_integrator(coord, gradient); + + WallPotential(gradient); + Integrator(coord, gradient); + if (m_unstable) { PrintStatus(); fmt::print(fg(fmt::color::salmon) | fmt::emphasis::bold, "Simulation got unstable, exiting!\n"); std::ofstream restart_file("unstable_curcuma.json"); restart_file << WriteRestartInformation() << std::endl; + m_time_step = 0; return; } ThermostatFunction(); m_Ekin = EKin(); if (m_writerestart > -1 && m_step % m_writerestart == 0) { - std::ofstream restart_file("curcuma_step_" + std::to_string(m_step) + ".json"); + std::ofstream restart_file("curcuma_step_" + std::to_string(int(m_step * m_timestep)) + ".json"); nlohmann::json restart; restart_file << WriteRestartInformation() << std::endl; } - if ((m_step && m_step % m_print == 0)) { + if ((m_step && int(m_step * m_timestep) % m_print == 0)) { m_Etot = m_Epot + m_Ekin; PrintStatus(); + m_time_step = 0; } if (m_impuls > m_T) { InitVelocities(m_scale_velo * m_impuls_scaling); m_Ekin = EKin(); - PrintStatus(); + // PrintStatus(); + m_time_step = 0; } if (m_current_rescue >= m_max_rescue) { @@ -528,7 +653,9 @@ void SimpleMD::start() } m_step++; m_currentStep += m_timestep; + m_time_step += std::chrono::duration_cast(std::chrono::system_clock::now() - step0).count(); } + PrintStatus(); if (m_thermostat.compare("csvr") == 0) std::cout << "Exchange with heat bath " << m_Ekin_exchange << "Eh" << std::endl; if (m_dipole) { @@ -567,7 +694,7 @@ void SimpleMD::Verlet(double* coord, double* grad) m_current_geometry[3 * i + 1] = coord[3 * i + 1]; m_current_geometry[3 * i + 2] = coord[3 * i + 2]; } - m_Epot = Gradient(coord, grad); + m_Epot = Energy(coord, grad); double ekin = 0.0; for (int i = 0; i < m_natoms; ++i) { m_velocities[3 * i + 0] -= 0.5 * m_timestep * grad[3 * i + 0] * m_rmass[3 * i + 0]; @@ -580,7 +707,7 @@ void SimpleMD::Verlet(double* coord, double* grad) m_gradient[3 * i + 2] = grad[3 * i + 2]; } ekin *= 0.5; - double T = 2.0 * ekin / (kb * 3 * m_natoms); + double T = 2.0 * ekin / (kb_Eh * m_dof); m_unstable = T > 100 * m_T; m_T = T; } @@ -596,6 +723,7 @@ void SimpleMD::Rattle(double* coord, double* grad) */ double m_timestep_inverse = 1 / m_timestep; + std::vector velo(m_natoms, 0); for (int i = 0; i < m_natoms; ++i) { coord[3 * i + 0] = m_current_geometry[3 * i + 0] + m_timestep * m_velocities[3 * i + 0] - 0.5 * grad[3 * i + 0] * m_rmass[3 * i + 0] * m_dt2; coord[3 * i + 1] = m_current_geometry[3 * i + 1] + m_timestep * m_velocities[3 * i + 1] - 0.5 * grad[3 * i + 1] * m_rmass[3 * i + 1] * m_dt2; @@ -604,46 +732,60 @@ void SimpleMD::Rattle(double* coord, double* grad) m_velocities[3 * i + 0] -= 0.5 * m_timestep * grad[3 * i + 0] * m_rmass[3 * i + 0]; m_velocities[3 * i + 1] -= 0.5 * m_timestep * grad[3 * i + 1] * m_rmass[3 * i + 1]; m_velocities[3 * i + 2] -= 0.5 * m_timestep * grad[3 * i + 2] * m_rmass[3 * i + 2]; + velo[i] = m_velocities[3 * i + 0] * m_velocities[3 * i + 0] + m_velocities[3 * i + 1] * m_velocities[3 * i + 1] + m_velocities[3 * i + 2] * m_velocities[3 * i + 2]; } - double epsilon = 0; - while (epsilon > m_rattle_tolerance) { + double epsilon = 2 * m_rattle_tolerance_a; + double iter = 0; + while (epsilon > m_rattle_tolerance_a && iter < 100) { + iter++; epsilon = 0; for (auto bond : m_bond_constrained) { int i = bond.first.first, j = bond.first.second; double distance = bond.second; - double r = distance - - (coord[3 * i + 0] - coord[3 * j + 0]) * (coord[3 * i + 0] - coord[3 * j + 0]) + double distance_current = ((coord[3 * i + 0] - coord[3 * j + 0]) * (coord[3 * i + 0] - coord[3 * j + 0]) + (coord[3 * i + 1] - coord[3 * j + 1]) * (coord[3 * i + 1] - coord[3 * j + 1]) - + (coord[3 * i + 2] - coord[3 * j + 2]) * (coord[3 * i + 2] - coord[3 * j + 2]); + + (coord[3 * i + 2] - coord[3 * j + 2]) * (coord[3 * i + 2] - coord[3 * j + 2])); + double r = distance - distance_current; epsilon += std::abs(r); - double dx = coord[3 * i + 0] - coord[3 * j + 0]; - double dy = coord[3 * i + 1] - coord[3 * j + 1]; - double dz = coord[3 * i + 2] - coord[3 * j + 2]; - - double scalarproduct = (dx) * (m_current_geometry[3 * i + 0] - m_current_geometry[3 * j + 0]) - + (dy) * (m_current_geometry[3 * i + 1] - m_current_geometry[3 * j + 1]) - + (dz) * (m_current_geometry[3 * i + 2] - m_current_geometry[3 * j + 2]); - double lambda = r / ((m_rmass[i] + m_rmass[j]) * scalarproduct); - coord[3 * i + 0] += dx * lambda * 0.5 * m_rmass[i]; - coord[3 * i + 1] += dy * lambda * 0.5 * m_rmass[i]; - coord[3 * i + 2] += dz * lambda * 0.5 * m_rmass[i]; - - coord[3 * j + 0] -= dx * lambda * 0.5 * m_rmass[j]; - coord[3 * j + 1] -= dy * lambda * 0.5 * m_rmass[j]; - coord[3 * j + 2] -= dz * lambda * 0.5 * m_rmass[j]; - - m_velocities[3 * i + 0] += dx * lambda * 0.5 * m_rmass[i] * m_timestep_inverse; - m_velocities[3 * i + 1] += dy * lambda * 0.5 * m_rmass[i] * m_timestep_inverse; - m_velocities[3 * i + 2] += dz * lambda * 0.5 * m_rmass[i] * m_timestep_inverse; - - m_velocities[3 * j + 0] -= dx * lambda * 0.5 * m_rmass[j] * m_timestep_inverse; - m_velocities[3 * j + 1] -= dy * lambda * 0.5 * m_rmass[j] * m_timestep_inverse; - m_velocities[3 * j + 2] -= dz * lambda * 0.5 * m_rmass[j] * m_timestep_inverse; + } + if (epsilon > m_rattle_tolerance_a) { + for (auto bond : m_bond_constrained) { + int i = bond.first.first, j = bond.first.second; + double distance = bond.second; + double distance_current = ((coord[3 * i + 0] - coord[3 * j + 0]) * (coord[3 * i + 0] - coord[3 * j + 0]) + + (coord[3 * i + 1] - coord[3 * j + 1]) * (coord[3 * i + 1] - coord[3 * j + 1]) + + (coord[3 * i + 2] - coord[3 * j + 2]) * (coord[3 * i + 2] - coord[3 * j + 2])); + double r = distance - distance_current; + + double dx = m_current_geometry[3 * i + 0] - m_current_geometry[3 * j + 0]; + double dy = m_current_geometry[3 * i + 1] - m_current_geometry[3 * j + 1]; + double dz = m_current_geometry[3 * i + 2] - m_current_geometry[3 * j + 2]; + + double scalarproduct = (dx) * (coord[3 * i + 0] - coord[3 * j + 0]) + + (dy) * (coord[3 * i + 1] - coord[3 * j + 1]) + + (dz) * (coord[3 * i + 2] - coord[3 * j + 2]); + double lambda = r / (1 * (m_rmass[i] + m_rmass[j]) * scalarproduct); + coord[3 * i + 0] += dx * lambda * 0.5 * m_rmass[i]; + coord[3 * i + 1] += dy * lambda * 0.5 * m_rmass[i]; + coord[3 * i + 2] += dz * lambda * 0.5 * m_rmass[i]; + + coord[3 * j + 0] -= dx * lambda * 0.5 * m_rmass[j]; + coord[3 * j + 1] -= dy * lambda * 0.5 * m_rmass[j]; + coord[3 * j + 2] -= dz * lambda * 0.5 * m_rmass[j]; + + m_velocities[3 * i + 0] += dx * lambda * 0.5 * m_rmass[i] * m_timestep_inverse; + m_velocities[3 * i + 1] += dy * lambda * 0.5 * m_rmass[i] * m_timestep_inverse; + m_velocities[3 * i + 2] += dz * lambda * 0.5 * m_rmass[i] * m_timestep_inverse; + + m_velocities[3 * j + 0] -= dx * lambda * 0.5 * m_rmass[j] * m_timestep_inverse; + m_velocities[3 * j + 1] -= dy * lambda * 0.5 * m_rmass[j] * m_timestep_inverse; + m_velocities[3 * j + 2] -= dz * lambda * 0.5 * m_rmass[j] * m_timestep_inverse; + } } } - m_Epot = Gradient(coord, grad); + m_Epot = Energy(coord, grad); double ekin = 0.0; for (int i = 0; i < m_natoms; ++i) { m_velocities[3 * i + 0] -= 0.5 * m_timestep * grad[3 * i + 0] * m_rmass[3 * i + 0]; @@ -658,42 +800,284 @@ void SimpleMD::Rattle(double* coord, double* grad) m_gradient[3 * i + 1] = grad[3 * i + 1]; m_gradient[3 * i + 2] = grad[3 * i + 2]; } + { + double epsilon = 2 * m_rattle_tolerance_b; + double iter = 0; - while (epsilon > m_rattle_tolerance) { - epsilon = 0; - for (auto bond : m_bond_constrained) { - int i = bond.first.first, j = bond.first.second; - double distance = bond.second; - double r = sqrt((coord[3 * i + 0] - coord[3 * j + 0]) * (m_velocities[3 * i + 0] - m_velocities[3 * j + 0]) - + (coord[3 * i + 1] - coord[3 * j + 1]) * (m_velocities[3 * i + 1] - m_velocities[3 * j + 1]) - + (coord[3 * i + 2] - coord[3 * j + 2]) * (m_velocities[3 * i + 2] - m_velocities[3 * j + 2])); - epsilon += std::abs(r); + while (epsilon > m_rattle_tolerance_b && iter < 100) { + epsilon = 0; - double dx = coord[3 * i + 0] - coord[3 * j + 0]; - double dy = coord[3 * i + 1] - coord[3 * j + 1]; - double dz = coord[3 * i + 2] - coord[3 * j + 2]; + iter++; - double mu = r / ((m_rmass[i] + m_rmass[j]) * distance); + for (auto bond : m_bond_constrained) { + int i = bond.first.first, j = bond.first.second; - m_velocities[3 * i + 0] += dx * mu * m_rmass[i]; - m_velocities[3 * i + 1] += dy * mu * m_rmass[i]; - m_velocities[3 * i + 2] += dz * mu * m_rmass[i]; + double dx = coord[3 * i + 0] - coord[3 * j + 0]; + double dy = coord[3 * i + 1] - coord[3 * j + 1]; + double dz = coord[3 * i + 2] - coord[3 * j + 2]; + double dvx = m_velocities[3 * i + 0] - m_velocities[3 * j + 0]; + double dvy = m_velocities[3 * i + 1] - m_velocities[3 * j + 1]; + double dvz = m_velocities[3 * i + 2] - m_velocities[3 * j + 2]; - m_velocities[3 * j + 0] -= dx * mu * m_rmass[j]; - m_velocities[3 * j + 1] -= dy * mu * m_rmass[j]; - m_velocities[3 * j + 2] -= dz * mu * m_rmass[j]; + double r = (dx) * (dvx) + (dy) * (dvy) + (dz) * (dvz); + epsilon += std::abs(r); + } + if (epsilon > m_rattle_tolerance_b) { + for (auto bond : m_bond_constrained) { + int i = bond.first.first, j = bond.first.second; + double distance = bond.second; + + double dx = coord[3 * i + 0] - coord[3 * j + 0]; + double dy = coord[3 * i + 1] - coord[3 * j + 1]; + double dz = coord[3 * i + 2] - coord[3 * j + 2]; + double dvx = m_velocities[3 * i + 0] - m_velocities[3 * j + 0]; + double dvy = m_velocities[3 * i + 1] - m_velocities[3 * j + 1]; + double dvz = m_velocities[3 * i + 2] - m_velocities[3 * j + 2]; + + double r = (dx) * (dvx) + (dy) * (dvy) + (dz) * (dvz); + double mu = -1 * r / ((m_rmass[i] + m_rmass[j]) * distance); + m_velocities[3 * i + 0] += dx * mu * m_rmass[i]; + m_velocities[3 * i + 1] += dy * mu * m_rmass[i]; + m_velocities[3 * i + 2] += dz * mu * m_rmass[i]; + + m_velocities[3 * j + 0] -= dx * mu * m_rmass[j]; + m_velocities[3 * j + 1] -= dy * mu * m_rmass[j]; + m_velocities[3 * j + 2] -= dz * mu * m_rmass[j]; + } + } } } - for (int i = 0; i < m_natoms; ++i) { ekin += m_mass[i] * (m_velocities[3 * i] * m_velocities[3 * i] + m_velocities[3 * i + 1] * m_velocities[3 * i + 1] + m_velocities[3 * i + 2] * m_velocities[3 * i + 2]); } ekin *= 0.5; - double T = 2.0 * ekin / (kb * 3 * m_natoms); - m_unstable = T > 100 * m_T; + double T = 2.0 * ekin / (kb_Eh * m_dof); + m_unstable = T > 1000 * m_T; m_T = T; } +double SimpleMD::ApplySphericLogFermiWalls(double* grad) +{ + double potential = 0; + double kbT = m_wall_temp * kb_Eh; + // int counter = 0; + for (int i = 0; i < m_natoms; ++i) { + double distance = sqrt(m_current_geometry[3 * i + 0] * m_current_geometry[3 * i + 0] + m_current_geometry[3 * i + 1] * m_current_geometry[3 * i + 1] + m_current_geometry[3 * i + 2] * m_current_geometry[3 * i + 2]); + double exp_expr = exp(m_wall_beta * (distance - m_wall_spheric_radius)); + double curr_pot = kbT * log(1 + exp_expr); + // counter += distance > m_wall_radius; + // std::cout << m_wall_beta*m_current_geometry[3 * i + 0]*exp_expr/(distance*(1-exp_expr)) << " "; + grad[3 * i + 0] -= kbT * m_wall_beta * m_current_geometry[3 * i + 0] * exp_expr / (distance * (1 - exp_expr)); + grad[3 * i + 1] -= kbT * m_wall_beta * m_current_geometry[3 * i + 1] * exp_expr / (distance * (1 - exp_expr)); + grad[3 * i + 2] -= kbT * m_wall_beta * m_current_geometry[3 * i + 2] * exp_expr / (distance * (1 - exp_expr)); + + // std::cout << distance << " "; + potential += curr_pot; + } + // std::cout << counter << " "; + return potential; + // std::cout << potential*kbT << std::endl; +} + +double SimpleMD::ApplyRectLogFermiWalls(double* grad) +{ + double potential = 0; + double kbT = m_wall_temp * kb_Eh; + int counter = 0; + double b = m_wall_beta; + double sum_grad = 0; + for (int i = 0; i < m_natoms; ++i) { + double exp_expr_xl = exp(b * (m_wall_x_min - m_current_geometry[3 * i + 0])); + double exp_expr_xu = exp(b * (m_current_geometry[3 * i + 0] - m_wall_x_max)); + + double exp_expr_yl = exp(b * (m_wall_y_min - m_current_geometry[3 * i + 1])); + double exp_expr_yu = exp(b * (m_current_geometry[3 * i + 1] - m_wall_y_max)); + + double exp_expr_zl = exp(b * (m_wall_z_min - m_current_geometry[3 * i + 2])); + double exp_expr_zu = exp(b * (m_current_geometry[3 * i + 2] - m_wall_z_max)); + + double curr_pot = kbT * (log(1 + exp_expr_xl) + log(1 + exp_expr_xu) + log(1 + exp_expr_yl) + log(1 + exp_expr_yu) + log(1 + exp_expr_zl) + log(1 + exp_expr_zu)); + counter += (m_current_geometry[3 * i + 0] - m_wall_x_min) < 0 || (m_wall_x_max - m_current_geometry[3 * i + 0]) < 0 || (m_current_geometry[3 * i + 1] - m_wall_y_min) < 0 || (m_wall_y_max - m_current_geometry[3 * i + 1]) < 0 || (m_current_geometry[3 * i + 2] - m_wall_z_min) < 0 || (m_wall_z_max - m_current_geometry[3 * i + 2]) < 0; + // std::cout << i << " " << counter << std::endl; + + // std::cout << m_wall_beta*m_current_geometry[3 * i + 0]*exp_expr/(distance*(1-exp_expr)) << " "; + if (i == 81) { + // std::cout << std::endl; + // std::cout << m_current_geometry[3 * i + 0] << " " << m_current_geometry[3 * i + 1] << " " << m_current_geometry[3 * i + 2] << std::endl; + // std::cout << grad[3 * i + 0] << " " << grad[3 * i + 1] << " " < m_wall_spheric_radius); + double out = distance > m_wall_spheric_radius; + counter += out; + + double diff = k * (m_wall_spheric_radius - distance) * (distance > m_wall_spheric_radius); + + double dx = diff * m_current_geometry[3 * i + 0] / distance; + double dy = diff * m_current_geometry[3 * i + 1] / distance; + double dz = diff * m_current_geometry[3 * i + 2] / distance; + + grad[3 * i + 0] -= dx; + grad[3 * i + 1] -= dy; + grad[3 * i + 2] -= dz; + /* + if(out) + { + std::cout << m_current_geometry[3 * i + 0] << " " << m_current_geometry[3 * i + 1] << " " << m_current_geometry[3 * i + 2] << std::endl; + std::cout << dx << " " << dy << " " << dz << std::endl; + }*/ + // std::cout << distance << " "; + potential += curr_pot; + } + // std::cout << counter << " "; + return potential; + // std::cout << potential*kbT << std::endl; +} + +double SimpleMD::ApplyRectHarmonicWalls(double* grad) +{ + double potential = 0; + double k = m_wall_temp * kb_Eh; + int counter = 0; + double b = m_wall_beta; + double sum_grad = 0; + for (int i = 0; i < m_natoms; ++i) { + double Vx = (m_current_geometry[3 * i + 0] - m_wall_x_min) * (m_current_geometry[3 * i + 0] - m_wall_x_min) * (m_current_geometry[3 * i + 0] < m_wall_x_min) + + (m_current_geometry[3 * i + 0] - m_wall_x_max) * (m_current_geometry[3 * i + 0] - m_wall_x_max) * (m_current_geometry[3 * i + 0] > m_wall_x_max); + + double Vy = (m_current_geometry[3 * i + 1] - m_wall_y_min) * (m_current_geometry[3 * i + 1] - m_wall_y_min) * (m_current_geometry[3 * i + 1] < m_wall_y_min) + + (m_current_geometry[3 * i + 1] - m_wall_y_max) * (m_current_geometry[3 * i + 1] - m_wall_y_max) * (m_current_geometry[3 * i + 1] > m_wall_y_max); + + double Vz = (m_current_geometry[3 * i + 2] - m_wall_z_min) * (m_current_geometry[3 * i + 2] - m_wall_z_min) * (m_current_geometry[3 * i + 2] < m_wall_z_min) + + (m_current_geometry[3 * i + 2] - m_wall_z_max) * (m_current_geometry[3 * i + 2] - m_wall_z_max) * (m_current_geometry[3 * i + 2] > m_wall_z_max); + + double curr_pot = 0.5 * k * (Vx + Vy + Vz); + int out = (m_current_geometry[3 * i + 0] - m_wall_x_min) < 0 || (m_wall_x_max - m_current_geometry[3 * i + 0]) < 0 || (m_current_geometry[3 * i + 1] - m_wall_y_min) < 0 || (m_wall_y_max - m_current_geometry[3 * i + 1]) < 0 || (m_current_geometry[3 * i + 2] - m_wall_z_min) < 0 || (m_wall_z_max - m_current_geometry[3 * i + 2]) < 0; + counter += out; + + // std::cout << i << " " << counter << std::endl; + + double dx = k * (std::abs(m_current_geometry[3 * i + 0] - m_wall_x_min) * (m_current_geometry[3 * i + 0] < m_wall_x_min) - (m_current_geometry[3 * i + 0] - m_wall_x_max) * (m_current_geometry[3 * i + 0] > m_wall_x_max)); + + double dy = k * (std::abs(m_current_geometry[3 * i + 1] - m_wall_y_min) * (m_current_geometry[3 * i + 1] < m_wall_y_min) - (m_current_geometry[3 * i + 1] - m_wall_y_max) * (m_current_geometry[3 * i + 1] > m_wall_y_max)); + + double dz = k * (std::abs(m_current_geometry[3 * i + 2] - m_wall_z_min) * (m_current_geometry[3 * i + 2] < m_wall_z_min) - (m_current_geometry[3 * i + 2] - m_wall_z_max) * (m_current_geometry[3 * i + 2] > m_wall_z_max)); + grad[3 * i + 0] -= dx; + grad[3 * i + 1] -= dy; + grad[3 * i + 2] -= dz; + /* if(out) + { + std::cout << m_current_geometry[3 * i + 0] << " " << m_current_geometry[3 * i + 1] << " " << m_current_geometry[3 * i + 2] << std::endl; + std::cout << dx << " " << dy << " " << dz << std::endl; + }*/ + sum_grad += dx + dy + dz; + + potential += curr_pot; + } + // std::cout << counter << " " << sum_grad; + return potential; + // std::cout << potential*kbT << std::endl; +} + +void SimpleMD::RemoveRotations(std::vector& velo) +{ + /* + * This code was taken and adopted from the xtb sources + * https://github.com/grimme-lab/xtb/blob/main/src/rmrottr.f90 + * Special thanks to the developers + */ + double mass = 0; + Position pos = { 0, 0, 0 }, angom{ 0, 0, 0 }; + Geometry geom(m_natoms, 3); + + std::vector> fragments = m_molecule.GetFragments(); + // std::cout << fragments.size() << std::endl; + for (int f = 0; f < fragments.size(); ++f) { + for (int i : fragments[f]) { + double m = m_mass[i]; + mass += m; + pos(0) += m * m_current_geometry[3 * i + 0]; + pos(1) += m * m_current_geometry[3 * i + 1]; + pos(2) += m * m_current_geometry[3 * i + 2]; + + geom(i, 0) = m_current_geometry[3 * i + 0]; + geom(i, 1) = m_current_geometry[3 * i + 1]; + geom(i, 2) = m_current_geometry[3 * i + 2]; + } + pos(0) /= mass; + pos(1) /= mass; + pos(2) /= mass; + + Geometry matrix = Geometry::Zero(3, 3); + for (int i : fragments[f]) { + double m = m_mass[i]; + geom(i, 0) -= pos(0); + geom(i, 1) -= pos(1); + geom(i, 2) -= pos(2); + + double x = geom(i, 0); + double y = geom(i, 1); + double z = geom(i, 2); + angom(0) += m_mass[i] * (geom(i, 1) * velo[3 * i + 2] - geom(i, 2) * velo[3 * i + 1]); + angom(1) += m_mass[i] * (geom(i, 2) * velo[3 * i + 0] - geom(i, 0) * velo[3 * i + 2]); + angom(2) += m_mass[i] * (geom(i, 0) * velo[3 * i + 1] - geom(i, 1) * velo[3 * i + 0]); + double x2 = x * x; + double y2 = y * y; + double z2 = z * z; + matrix(0, 0) += m * (y2 + z2); + matrix(1, 1) += m * (x2 + z2); + matrix(2, 2) += m * (x2 + y2); + matrix(0, 1) -= m * x * y; + matrix(0, 2) -= m * x * z; + matrix(1, 2) -= m * y * z; + } + matrix(1, 0) = matrix(0, 1); + matrix(2, 0) = matrix(0, 2); + matrix(2, 1) = matrix(1, 2); + + Position omega = matrix.inverse() * angom; + + Position rlm = { 0, 0, 0 }, ram = { 0, 0, 0 }; + for (int i : fragments[f]) { + rlm(0) = rlm(0) + m_mass[i] * velo[3 * i + 0]; + rlm(1) = rlm(1) + m_mass[i] * velo[3 * i + 1]; + rlm(2) = rlm(2) + m_mass[i] * velo[3 * i + 2]; + } + + for (int i : fragments[f]) { + ram(0) = (omega(1) * geom(i, 2) - omega(2) * geom(i, 1)); + ram(1) = (omega(2) * geom(i, 0) - omega(0) * geom(i, 2)); + ram(2) = (omega(0) * geom(i, 1) - omega(1) * geom(i, 0)); + + velo[3 * i + 0] = velo[3 * i + 0] - rlm(0) / mass - ram(0); + velo[3 * i + 1] = velo[3 * i + 1] - rlm(1) / mass - ram(1); + velo[3 * i + 2] = velo[3 * i + 2] - rlm(2) / mass - ram(2); + } + } +} + void SimpleMD::RemoveRotation(std::vector& velo) { /* @@ -739,9 +1123,9 @@ void SimpleMD::RemoveRotation(std::vector& velo) matrix(0, 0) += m * (y2 + z2); matrix(1, 1) += m * (x2 + z2); matrix(2, 2) += m * (x2 + y2); - matrix(0, 1) += m * x * y; - matrix(0, 2) += m * x * z; - matrix(1, 2) += m * y * z; + matrix(0, 1) -= m * x * y; + matrix(0, 2) -= m * x * z; + matrix(1, 2) -= m * y * z; } matrix(1, 0) = matrix(0, 1); matrix(2, 0) = matrix(0, 2); @@ -782,7 +1166,7 @@ void SimpleMD::PrintStatus() const #pragma message("awfull, fix it ") if (m_writeUnique) { #ifdef GCC - std::cout << fmt::format("{1: ^{0}f} {2: ^{0}f} {3: ^{0}f} {4: ^{0}f} {5: ^{0}f} {6: ^{0}f} {7: ^{0}f} {8: ^{0}f} {9: ^{0}f} {10: ^{0}f} {11: ^{0}}\n", 15, m_currentStep / 1000, m_Epot, m_aver_Epot, m_Ekin, m_aver_Ekin, m_Etot, m_aver_Etot, m_T, m_aver_Temp, remaining, m_unqiue->StoredStructures()); + std::cout << fmt::format("{1: ^{0}f} {2: ^{0}f} {3: ^{0}f} {4: ^{0}f} {5: ^{0}f} {6: ^{0}f} {7: ^{0}f} {8: ^{0}f} {9: ^{0}f} {10: ^{0}f} {11: ^{0}f} {12: ^{0}ff} {13: ^{0}} {14: ^{0}}\n", 15, m_currentStep / 1000, m_Epot, m_aver_Epot, m_Ekin, m_aver_Ekin, m_Etot, m_aver_Etot, m_T, m_aver_Temp, m_wall_potential, m_average_wall_potential, remaining, m_time_step / 1000.0, m_unqiue->StoredStructures()); #else std::cout << m_currentStep * m_timestep / fs2amu / 1000 << " " << m_Epot << " " << m_Ekin << " " << m_Epot + m_Ekin << m_T << std::endl; @@ -790,9 +1174,9 @@ void SimpleMD::PrintStatus() const } else { #ifdef GCC if (m_dipole) - std::cout << fmt::format("{1: ^{0}f} {2: ^{0}f} {3: ^{0}f} {4: ^{0}f} {5: ^{0}f} {6: ^{0}f} {7: ^{0}f} {8: ^{0}f} {9: ^{0}f} {10: ^{0}f} {11: ^{0}f}\n", 15, m_currentStep / 1000, m_Epot, m_aver_Epot, m_Ekin, m_aver_Ekin, m_Etot, m_aver_Etot, m_T, m_aver_Temp, m_aver_dipol * 2.5418 * 3.3356, remaining); + std::cout << fmt::format("{1: ^{0}f} {2: ^{0}f} {3: ^{0}f} {4: ^{0}f} {5: ^{0}f} {6: ^{0}f} {7: ^{0}f} {8: ^{0}f} {9: ^{0}f} {10: ^{0}f} {11: ^{0}f} {12: ^{0}f} {13: ^{0}f} {14: ^{0}f}\n", 15, m_currentStep / 1000, m_Epot, m_aver_Epot, m_Ekin, m_aver_Ekin, m_Etot, m_aver_Etot, m_T, m_aver_Temp, m_wall_potential, m_average_wall_potential, m_aver_dipol * 2.5418 * 3.3356, m_time_step / 1000.0, remaining); else - std::cout << fmt::format("{1: ^{0}f} {2: ^{0}f} {3: ^{0}f} {4: ^{0}f} {5: ^{0}f} {6: ^{0}f} {7: ^{0}f} {8: ^{0}f} {9: ^{0}f} {10: ^{0}f} \n", 15, m_currentStep / 1000, m_Epot, m_aver_Epot, m_Ekin, m_aver_Ekin, m_Etot, m_aver_Etot, m_T, m_aver_Temp, remaining); + std::cout << fmt::format("{1: ^{0}f} {2: ^{0}f} {3: ^{0}f} {4: ^{0}f} {5: ^{0}f} {6: ^{0}f} {7: ^{0}f} {8: ^{0}f} {9: ^{0}f} {10: ^{0}f} {11: ^{0}f} {12: ^{0}f} {13: ^{0}f}\n", 15, m_currentStep / 1000, m_Epot, m_aver_Epot, m_Ekin, m_aver_Ekin, m_Etot, m_aver_Etot, m_T, m_aver_Temp, m_wall_potential, m_average_wall_potential, remaining, m_time_step / 1000.0); #else std::cout << m_currentStep * m_timestep / fs2amu / 1000 << " " << m_Epot << " " << m_Ekin << " " << m_Epot + m_Ekin << m_T << std::endl; @@ -809,7 +1193,23 @@ void SimpleMD::PrintMatrix(const double* matrix) std::cout << std::endl; } -double SimpleMD::Gradient(const double* coord, double* grad) +double SimpleMD::CleanEnergy(const double* coord, double* grad) +{ + EnergyCalculator interface(m_method, m_defaults); + interface.setMolecule(m_molecule); + interface.updateGeometry(coord); + + double Energy = interface.CalculateEnergy(true); + interface.getGradient(grad); + if (m_dipole) { + std::vector dipole = interface.Dipole(); + m_curr_dipole = sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]); + m_collected_dipole.push_back(sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2])); + } + return Energy; +} + +double SimpleMD::FastEnergy(const double* coord, double* grad) { m_interface->updateGeometry(coord); @@ -831,7 +1231,7 @@ double SimpleMD::EKin() ekin += m_mass[i] * (m_velocities[3 * i] * m_velocities[3 * i] + m_velocities[3 * i + 1] * m_velocities[3 * i + 1] + m_velocities[3 * i + 2] * m_velocities[3 * i + 2]); } ekin *= 0.5; - m_T = 2.0 * ekin / (kb * 3 * m_natoms); + m_T = 2.0 * ekin / (kb_Eh * m_dof); m_aver_Temp = (m_T + (m_currentStep)*m_aver_Temp) / (m_currentStep + 1); m_aver_Epot = (m_Epot + (m_currentStep)*m_aver_Epot) / (m_currentStep + 1); @@ -840,6 +1240,8 @@ double SimpleMD::EKin() if (m_dipole) { m_aver_dipol = (m_curr_dipole + (m_currentStep)*m_aver_dipol) / (m_currentStep + 1); } + m_average_wall_potential = (m_wall_potential + (m_currentStep)*m_average_wall_potential) / (m_currentStep + 1); + return ekin; } @@ -870,12 +1272,13 @@ bool SimpleMD::WriteGeometry() if (m_writeXYZ) { m_molecule.setEnergy(m_Epot); m_molecule.setName(std::to_string(m_currentStep)); - m_molecule.appendXYZFile(m_basename + ".trj.xyz"); + m_molecule.appendXYZFile(Basename() + ".trj.xyz"); } if (m_writeUnique) { if (m_unqiue->CheckMolecule(new Molecule(m_molecule))) { std::cout << " ** new structure was added **" << std::endl; PrintStatus(); + m_time_step = 0; m_unique_structures.push_back(new Molecule(m_molecule)); } } @@ -892,17 +1295,16 @@ void SimpleMD::Berendson() void SimpleMD::CSVR() { - double Ekin_target = 1.5 * kb * m_T0 * m_natoms; - double dof = 3 * m_natoms; + double Ekin_target = 0.5 * kb_Eh * m_T0 * m_dof; double c = exp(-(m_timestep * m_respa) / m_coupling); - std::random_device rd{}; - std::mt19937 gen{ rd() }; - std::normal_distribution<> d{ 0, 1 }; - std::chi_squared_distribution dchi{ dof }; + static std::random_device rd{}; + static std::mt19937 gen{ rd() }; + static std::normal_distribution<> d{ 0, 1 }; + static std::chi_squared_distribution dchi{ m_dof }; double R = d(gen); double SNf = dchi(gen); - double alpha2 = c + (1 - c) * (SNf + R * R) * Ekin_target / (dof * m_Ekin) + 2 * R * sqrt(c * (1 - c) * Ekin_target / (dof * m_Ekin)); + double alpha2 = c + (1 - c) * (SNf + R * R) * Ekin_target / (m_dof * m_Ekin) + 2 * R * sqrt(c * (1 - c) * Ekin_target / (m_dof * m_Ekin)); m_Ekin_exchange += m_Ekin * (alpha2 - 1); double alpha = sqrt(alpha2); diff --git a/src/capabilities/simplemd.h b/src/capabilities/simplemd.h index 85f7ade..8e5c73c 100644 --- a/src/capabilities/simplemd.h +++ b/src/capabilities/simplemd.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "src/capabilities/rmsdtraj.h" @@ -41,7 +42,8 @@ static json CurcumaMDJson{ { "dt", 1 }, { "charge", 0 }, { "Spin", 0 }, - { "centered", 0 }, + { "rmrottrans", 0 }, + { "nocenter", false }, { "dump", 50 }, { "print", 1000 }, { "unique", false }, @@ -60,10 +62,27 @@ static json CurcumaMDJson{ { "norestart", false }, { "writerestart", 1000 }, { "rattle", false }, - { "rattle_tolerance", 1e-5 }, + { "rattle_tolerance_a", 1 }, + { "rattle_tolerance_b", 0.1 }, { "thermostat", "csvr" }, { "respa", 1 }, - { "dipole", false } + { "dipole", false }, + { "seed", -1 }, + { "cleanenergy", false }, + { "wall", "none" }, // can be spheric or rect + { "wall_type", "logfermi" }, // can be logfermi or harmonic + { "wall_spheric_radius", 0 }, + { "wall_xl", 0 }, + { "wall_yl", 0 }, + { "wall_zl", 0 }, + { "wall_x_min", 0 }, + { "wall_x_max", 0 }, + { "wall_y_min", 0 }, + { "wall_y_max", 0 }, + { "wall_z_min", 0 }, + { "wall_z_max", 0 }, + { "wall_temp", 298.15 }, + { "wall_beta", 6 } }; class SimpleMD : public CurcumaMethod { @@ -75,12 +94,12 @@ class SimpleMD : public CurcumaMethod { { m_molecule = molecule; } - + /* void setBaseName(const std::string& name) { m_basename = name; } - + */ bool Initialise() override; void start() override; @@ -114,7 +133,8 @@ class SimpleMD : public CurcumaMethod { void InitVelocities(double scaling = 1.0); - double Gradient(const double* coord, double* grad); + double FastEnergy(const double* coord, double* grad); + double CleanEnergy(const double* coord, double* grad); void PrintMatrix(const double* matrix); @@ -123,19 +143,29 @@ class SimpleMD : public CurcumaMethod { void Rattle(double* coord, double* grad); void RemoveRotation(std::vector& velo); + void RemoveRotations(std::vector& velo); double EKin(); void Berendson(); void CSVR(); + void InitialiseWalls(); + + double ApplySphericLogFermiWalls(double* grad); + double ApplyRectLogFermiWalls(double* grad); + + double ApplySphericHarmonicWalls(double* grad); + double ApplyRectHarmonicWalls(double* grad); + void InitConstrainedBonds(); - std::function m_integrator; + std::function Integrator; + std::function Energy; + std::function WallPotential; std::vector, double>> m_bond_constrained; - std::string m_basename; int m_natoms = 0; - int m_dumb = 1; + int m_dump = 1; double m_T = 0, m_Epot = 0, m_aver_Epot = 0, m_Ekin = 0, m_aver_Ekin = 0, m_Etot = 0, m_aver_Etot = 0, m_aver_dipol = 0, m_curr_dipole = 0; int m_hmass = 4; double m_single_step = 1; @@ -148,7 +178,8 @@ class SimpleMD : public CurcumaMethod { std::vector m_atomtype; Molecule m_molecule; bool m_initialised = false, m_restart = false, m_writeUnique = true, m_opt = false, m_rescue = false, m_writeXYZ = true, m_writeinit = false, m_norestart = false; - int m_centered = 0; + int m_rmrottrans = 0; + bool m_nocenter = false; EnergyCalculator* m_interface; RMSDTraj* m_unqiue; const std::vector m_used_mass; @@ -157,41 +188,50 @@ class SimpleMD : public CurcumaMethod { int m_respa = 1; double m_pos_conv = 0, m_scale_velo = 1.0, m_coupling = 10; double m_impuls = 0, m_impuls_scaling = 0.75, m_dt2 = 0; - double m_rattle_tolerance = 1e-4; + double m_rattle_tolerance_a = 1, m_rattle_tolerance_b = 0.1; + double m_wall_spheric_radius = 6, m_wall_temp = 298.15, m_wall_beta = 6; + double m_wall_x_min = 0, m_wall_x_max = 0, m_wall_y_min = 0, m_wall_y_max = 0, m_wall_z_min = 0, m_wall_z_max = 0; + double m_wall_potential = 0, m_average_wall_potential = 0; + int m_rattle = 0; std::vector m_collected_dipole; Matrix m_topo_initial; std::vector m_unique_structures; std::string m_method = "UFF", m_initfile = "none", m_thermostat = "csvr"; bool m_unstable = false; bool m_dipole = false; + bool m_clean_energy = false; + int m_seed = -1; + int m_time_step = 0; + int m_dof = 0; }; class MDThread : public CxxThread { public: - MDThread(int thread, const json& controller) - : m_thread(thread) - , m_controller(controller) + MDThread(const json& controller) + : m_controller(controller) { setAutoDelete(true); } ~MDThread() = default; - + void setBasename(const std::string& basename) { m_basename = basename; } inline void setMolecule(const Molecule& molecule) { m_molecule = molecule; } SimpleMD* MDDriver() const { return m_mddriver; } virtual int execute() override { - m_mddriver = new SimpleMD(m_controller, false); + json controller; + controller["md"] = m_controller; + m_mddriver = new SimpleMD(controller, false); m_mddriver->setMolecule(m_molecule); - m_mddriver->setBaseName("thread" + std::to_string(m_thread)); + m_mddriver->overrideBasename(m_basename + ".t" + std::to_string(ThreadId())); m_mddriver->Initialise(); m_mddriver->start(); return 0; } protected: - int m_thread; Molecule m_molecule; + std::string m_basename; std::string m_result; json m_controller; SimpleMD* m_mddriver; diff --git a/src/core/global.h b/src/core/global.h index 1bd00d9..aa21311 100644 --- a/src/core/global.h +++ b/src/core/global.h @@ -34,9 +34,13 @@ using json = nlohmann::json; const double pi = 3.14159265359; const double au = 0.52917721092; const double amu2au = 1822.8884850; -const double kb = 3.166811e-6; // Hartree +const double kb_Eh = 3.166811e-6; // Hartree +const double kb_SI = 1.380649e-23; // SI +const double kb_eV = 8.617333262e-5; // eV const double fs2amu = 41.34137314; const double R = 8.31446261815324; +const double atomic_mass = 1.66053906660e-27; +const double T_Eh = 3.1577464e5; typedef Eigen::MatrixXd Geometry; typedef Eigen::MatrixXd Matrix; diff --git a/src/core/hessian.cpp b/src/core/hessian.cpp index 5533d05..9b1a762 100644 --- a/src/core/hessian.cpp +++ b/src/core/hessian.cpp @@ -19,11 +19,7 @@ #include -// #include -// #include -#include -// #include #include "external/CxxThreadPool/include/CxxThreadPool.h" @@ -31,7 +27,6 @@ #include "hessian.h" -using namespace fd; HessianThread::HessianThread(const std::string& method, const json& controller, int i, int j, int xi, int xj, bool fullnumerical) : m_method(method) @@ -153,14 +148,13 @@ void Hessian::CalculateHessian(int type) { if (type == 1) CalculateHessianSemiNumerical(); - else if (type == 2) + else CalculateHessianNumerical(); - else if (type == 3) { - std::cout << "external hessian approach not implemented" << std::endl; - std::cout << "use -hessian 1 or -hessian 2" << std::endl; - FiniteDiffHess(); - } + auto eigenvalues = ConvertHessian(m_hessian); + + // PrintVibrationsPlain(eigenvalues); + auto hessian2 = ProjectHessian(m_hessian); auto projected = ConvertHessian(hessian2); @@ -228,6 +222,21 @@ Vector Hessian::ConvertHessian(Matrix& hessian) return diag_I.eigenvalues(); } +void Hessian::PrintVibrationsPlain(const Vector& eigenvalues) +{ + Vector eigval = eigenvalues.cwiseSqrt(); + std::cout << std::endl + << " Frequencies: " << std::endl; + + for (int i = 0; i < m_molecule.AtomCount() * 3; ++i) { + if (i % 6 == 0) + std::cout << std::endl; + std::cout << m_scale_functions(eigval(i)) << " "; + + } + std::cout << std::endl; +} + void Hessian::PrintVibrations(Vector& eigenvalues, const Vector& projected) { Vector eigval = eigenvalues.cwiseSqrt(); @@ -328,32 +337,3 @@ void Hessian::CalculateHessianSemiNumerical() } delete pool; } - -void Hessian::FiniteDiffHess() -{ - - m_hessian = Eigen::MatrixXd::Ones(3 * m_molecule.AtomCount(), 3 * m_molecule.AtomCount()); - - EnergyCalculator energy(m_method, m_controller); - energy.setMolecule(m_molecule); - - const auto f = [&energy](const Eigen::VectorXd& x) -> double { - energy.updateGeometry(x); - return energy.CalculateEnergy(false, false); - }; - - Eigen::VectorXd x = Eigen::VectorXd::Zero(3*m_molecule.AtomCount()); - for(int i = 0; i < m_molecule.AtomCount(); ++i) - { - x(3*i + 0) = m_molecule.Atom(i).second[0]; - x(3*i + 1) = m_molecule.Atom(i).second[1]; - x(3*i + 2) = m_molecule.Atom(i).second[2]; - - } - // Eigen::MatrixXd hess = Eigen::MatrixXd::Zero(3 * m_molecule.AtomCount(), 3 * m_molecule.AtomCount()); - - Eigen::MatrixXd fhess; - fd::finite_hessian(x, f, fhess, fd::SECOND); - - m_hessian = fhess / au / au; -} diff --git a/src/core/hessian.h b/src/core/hessian.h index ab5f869..3f24b9e 100644 --- a/src/core/hessian.h +++ b/src/core/hessian.h @@ -69,6 +69,7 @@ class Hessian { void CalculateHessian(int type = 1); void PrintVibrations(Vector& eigenvalues, const Vector& projected); + void PrintVibrationsPlain(const Vector& eigenvalues); private: void CalculateHessianNumerical(); diff --git a/src/core/molecule.cpp b/src/core/molecule.cpp index d028126..df0467e 100644 --- a/src/core/molecule.cpp +++ b/src/core/molecule.cpp @@ -156,6 +156,18 @@ Molecule::~Molecule() { } +void Molecule::Initialise(const int* attyp, const double* coord, const int natoms, const double charge, const int spin) +{ + m_charge = charge; + m_spin = spin; + Molecule mol; + for (int i = 0; i < natoms; ++i) { + m_geometry.push_back({ coord[3 * i], coord[3 * i + 1], coord[3 * i + 2] }); + m_atoms.push_back(attyp[i]); + m_mass += Elements::AtomicMass[attyp[i]]; + } +} + void Molecule::ApplyReorderRule(const std::vector& rule) { Molecule mol; @@ -793,6 +805,7 @@ Position Molecule::Centroid(bool protons, int fragment) const return GeometryTools::Centroid(getGeometryByFragment(fragment, protons)); } + Position Molecule::MassCentroid(bool protons, int fragment) const { Position pos = { 0, 0, 0 }; @@ -805,6 +818,105 @@ Position Molecule::MassCentroid(bool protons, int fragment) const return pos; } +Eigen::Vector3d Molecule::COM(bool protons, int fragment) +{ + // todo implement heavy and fragements ... + if (m_mass < 1) + CalculateMass(); + Eigen::Vector3d com = { 0, 0, 0 }; + for (int i = 0; i < m_geometry.size(); ++i) { + double mass = Elements::AtomicMass[m_atoms[i]]; + com(0) += mass * m_geometry[i][0]; + com(1) += mass * m_geometry[i][1]; + com(2) += mass * m_geometry[i][2]; + } + com(0) /= m_mass; + com(1) /= m_mass; + com(2) /= m_mass; + return com; +} + +std::vector Molecule::CalculateDipoleMoments(const std::vector& scaling) const +{ + std::vector dipole_moments; + + if (m_charges.size() != m_geometry.size()) { + std::cout << "No partial charges available" << std::endl; + return dipole_moments; + } + + for (int f = 0; f < GetFragments().size(); ++f) { + Position pos = { 0, 0, 0 }, dipole = { 0, 0, 0 }; + double mass = 0; + for (int i : m_fragments[f]) { + double m = Elements::AtomicMass[m_atoms[i]]; + mass += m; + pos(0) += m * m_geometry[i][0]; + pos(1) += m * m_geometry[i][1]; + pos(2) += m * m_geometry[i][2]; + } + pos(0) /= mass; + pos(1) /= mass; + pos(2) /= mass; + for (int i : m_fragments[f]) { + double scale = 3; + if (scaling.size() > i) + scale = scaling[i]; + dipole(0) += m_charges[i] * (m_geometry[i][0] - pos(0)) * scale; + dipole(1) += m_charges[i] * (m_geometry[i][1] - pos(1)) * scale; + dipole(2) += m_charges[i] * (m_geometry[i][2] - pos(2)) * scale; + // std::cout << scale << " "; + } + // std::cout << std::endl; + dipole_moments.push_back(dipole); + } + return dipole_moments; +} + +Position Molecule::CalculateDipoleMoment(const std::vector& scaling) const +{ + double mass = 0; + + Position pos = { 0, 0, 0 }, dipole = { 0, 0, 0 }; + if (m_charges.size() != m_geometry.size()) { + std::cout << "No partial charges available" << std::endl; + return dipole; + } + for (int i = 0; i < m_geometry.size(); ++i) { + double m = Elements::AtomicMass[m_atoms[i]]; + mass += m; + pos(0) += m * m_geometry[i][0]; + pos(1) += m * m_geometry[i][1]; + pos(2) += m * m_geometry[i][2]; + } + pos(0) /= mass; + pos(1) /= mass; + pos(2) /= mass; + for (int i = 0; i < m_geometry.size(); ++i) { + double scale = 3; + if (scaling.size() > i) + scale = scaling[i]; + dipole(0) += m_charges[i] * (m_geometry[i][0] - pos(0)) * scale; + dipole(1) += m_charges[i] * (m_geometry[i][1] - pos(1)) * scale; + dipole(2) += m_charges[i] * (m_geometry[i][2] - pos(2)) * scale; + // std::cout << scale << " "; + } + // std::cout << std::endl; + return dipole; +} + +double Molecule::GyrationRadius(bool protons, int fragment) +{ + Eigen::Vector3d com = COM(protons, fragment); + double gyr = 0; + for (int i = 0; i < m_geometry.size(); ++i) { + gyr += ((com(0) - m_geometry[i][0]) * (com(0) - m_geometry[i][0]) + (com(0) - m_geometry[i][0]) * (com(0) - m_geometry[i][0]) + (com(0) - m_geometry[i][0]) * (com(0) - m_geometry[i][0])); + } + gyr /= double(m_geometry.size()); + return gyr; + +} + std::pair Molecule::Atom(int i) const { return std::pair(m_atoms[i], { m_geometry[i][0], m_geometry[i][1], m_geometry[i][2] }); diff --git a/src/core/molecule.h b/src/core/molecule.h index a3eacd4..c1ccacc 100644 --- a/src/core/molecule.h +++ b/src/core/molecule.h @@ -60,6 +60,7 @@ class Molecule /* Molecule& operator=(const Molecule& molecule); Molecule& operator=(const Molecule* molecule); */ + void Initialise(const int* attyp, const double* coord, const int natoms, const double charge, const int spin); void ApplyReorderRule(const std::vector& rule); void print_geom(bool moreinfo = true) const; @@ -96,6 +97,8 @@ class Molecule Molecule getFragmentMolecule(int fragment) const; double CalculateDistance(int i, int j) const; + double GyrationRadius(bool hydrogen = true, int fragment = -1); + Geometry getGeometry(const IntPair& pair, bool protons = true) const; Geometry getGeometry(std::vector atoms, bool protons = true) const; Geometry getGeometryByFragment(int fragment, bool protons = true) const; @@ -110,6 +113,7 @@ class Molecule Position Centroid(bool hydrogen = true, int fragment = -1) const; Position MassCentroid(bool hydrogen = true, int fragment = -1) const; + Eigen::Vector3d COM(bool hydrogen = true, int fragment = -1); inline std::size_t AtomCount() const { return m_atoms.size(); } std::vector Atoms() const { return m_atoms; } @@ -126,6 +130,8 @@ class Molecule std::string XYZString() const; std::string XYZString(const std::vector &order) const; + std::vector CalculateDipoleMoments(const std::vector& scaling = std::vector()) const; + Position CalculateDipoleMoment(const std::vector& scaling = std::vector()) const; std::vector BoundHydrogens(int atom, double scaling = 1.5) const; std::map> getConnectivtiy(double scaling = 1.5, int latest = -1) const; @@ -194,6 +200,8 @@ class Molecule Matrix AlignmentAxes() const { return m_alignmentAxes; } + void setPartialCharges(const std::vector& charges) { m_charges = charges; } + private: void ParseString(const std::string& internal, std::vector& elements); @@ -215,6 +223,7 @@ class Molecule int m_charge = 0, m_spin = 0; std::vector> m_geometry; std::vector m_atoms; + std::vector m_charges; std::vector m_connect_mass; Matrix m_HydrogenBondMap; diff --git a/src/core/tbliteinterface.cpp b/src/core/tbliteinterface.cpp index efbdbbf..000e53e 100644 --- a/src/core/tbliteinterface.cpp +++ b/src/core/tbliteinterface.cpp @@ -19,6 +19,10 @@ #ifndef tblite_delete #include "tblite.h" +#include "tblite/container.h" +#include "tblite/context.h" +#include "tblite/error.h" + #endif #include "src/core/global.h" @@ -36,12 +40,27 @@ TBLiteInterface::TBLiteInterface(const json& tblitesettings) { m_tblitesettings = MergeJson(TBLiteSettings, tblitesettings); - m_acc = m_tblitesettings["tb_ac"]; + m_acc = m_tblitesettings["tb_acc"]; m_maxiter = m_tblitesettings["tb_max_iter"]; m_damping = m_tblitesettings["tb_damping"]; m_temp = m_tblitesettings["tb_temp"]; m_verbose = m_tblitesettings["tb_verbose"]; std::string guess = m_tblitesettings["tb_guess"]; + + m_cpcm_eps = m_tblitesettings["cpcm_eps"]; + m_alpb_eps = m_tblitesettings["alpb_eps"]; + + std::string tmp = m_tblitesettings["cpcm_solv"]; + m_cpcm = tmp.compare("none") != 0; + m_cpcm_solv = new char[tmp.length() + 1]; + strcpy(m_cpcm_solv, tmp.c_str()); + + tmp = m_tblitesettings["alpb_solv"]; + m_alpb = tmp.compare("none") != 0; + + m_alpb_solv = new char[tmp.length() + 1]; + strcpy(m_alpb_solv, tmp.c_str()); + if (guess.compare("SAD") == 0) m_guess = 0; else if (guess.compare("EEQ") == 0) @@ -58,11 +77,12 @@ TBLiteInterface::TBLiteInterface(const json& tblitesettings) TBLiteInterface::~TBLiteInterface() { - delete m_error; - delete m_ctx; - delete m_tblite_res; - delete m_tblite_mol; - delete m_tblite_calc; + tblite_delete_error(&m_error); + tblite_delete_context(&m_ctx); + tblite_delete_result(&m_tblite_res); + tblite_delete_structure(&m_tblite_mol); + tblite_delete_calculator(&m_tblite_calc); + tblite_delete_container(&m_tb_cont); delete[] m_coord; delete[] m_attyp; @@ -86,7 +106,6 @@ bool TBLiteInterface::InitialiseMolecule(const Molecule& molecule) m_attyp[i] = atoms[i]; } bool init = InitialiseMolecule(m_attyp, m_coord, m_atomcount, molecule.Charge(), molecule.Spin()); - return init; } @@ -118,6 +137,8 @@ bool TBLiteInterface::InitialiseMolecule(const int* attyp, const double* coord, m_tblite_mol = tblite_new_structure(m_error, natoms, attyp, coord, &charge, &spin, NULL, NULL); m_initialised = true; + m_molecule.Initialise(attyp, coord, natoms, charge, spin); + return true; } @@ -139,20 +160,97 @@ bool TBLiteInterface::UpdateMolecule(const double* coord) return true; } +void TBLiteInterface::ApplySolvation() +{ + int count = ((m_cpcm + m_cpcm_eps) != -1) + ((m_alpb + m_alpb_eps) != -1); + + if (count == 1) { + if (m_cpcm && m_cpcm_eps == -1) { + m_tb_cont = tblite_new_cpcm_solvation_solvent(m_ctx, m_tblite_mol, m_tblite_calc, m_cpcm_solv); + if (tblite_check_context(m_ctx)) { + tbliteError(); + tbliteContextError(); + std::cout << "Error during CPCM calculation, ... Retry with increased verbosity" << std::endl; + tblite_set_context_verbosity(m_ctx, m_verbose + 1); + tblite_delete_container(&m_tb_cont); + m_tb_cont = tblite_new_cpcm_solvation_solvent(m_ctx, m_tblite_mol, m_tblite_calc, m_cpcm_solv); + + // return 0; + } + tblite_calculator_push_back(m_ctx, m_tblite_calc, &m_tb_cont); + } else if (!m_cpcm && m_cpcm_eps != -1) { + m_tb_cont = tblite_new_cpcm_solvation_epsilon(m_ctx, m_tblite_mol, m_tblite_calc, m_cpcm_eps); + if (tblite_check_context(m_ctx)) { + tbliteError(); + tbliteContextError(); + std::cout << "Error during CPCM calculation, ... Retry with increased verbosity" << std::endl; + tblite_set_context_verbosity(m_ctx, m_verbose + 1); + tblite_delete_container(&m_tb_cont); + m_tb_cont = tblite_new_cpcm_solvation_epsilon(m_ctx, m_tblite_mol, m_tblite_calc, m_cpcm_eps); + + // return 0; + } + tblite_calculator_push_back(m_ctx, m_tblite_calc, &m_tb_cont); + } + + if (m_alpb && m_alpb_eps == -1) { + m_tb_cont = tblite_new_alpb_solvation_solvent(m_ctx, m_tblite_mol, m_tblite_calc, m_alpb_solv); + if (tblite_check_context(m_ctx)) { + tbliteError(); + tbliteContextError(); + std::cout << "Error during ALPB calculation, ... Retry with increased verbosity" << std::endl; + tblite_set_context_verbosity(m_ctx, m_verbose + 1); + tblite_delete_container(&m_tb_cont); + m_tb_cont = tblite_new_alpb_solvation_solvent(m_ctx, m_tblite_mol, m_tblite_calc, m_alpb_solv); + + // return 0; + } + tblite_calculator_push_back(m_ctx, m_tblite_calc, &m_tb_cont); + } else if (!m_alpb && m_alpb_eps != -1) { + m_tb_cont = tblite_new_alpb_solvation_epsilon(m_ctx, m_tblite_mol, m_tblite_calc, m_alpb_eps); + if (tblite_check_context(m_ctx)) { + tbliteError(); + tbliteContextError(); + std::cout << "Error during ALPB calculation, ... Retry with increased verbosity" << std::endl; + + tblite_set_context_verbosity(m_ctx, m_verbose + 1); + tblite_delete_container(&m_tb_cont); + m_tb_cont = tblite_new_alpb_solvation_epsilon(m_ctx, m_tblite_mol, m_tblite_calc, m_alpb_eps); + // return 0; + } + tblite_calculator_push_back(m_ctx, m_tblite_calc, &m_tb_cont); + } + } else if (count != 0) { + std::cout << count << std::endl + << std::endl + << "If three witches had three watches, which witch would watch which watch?\n Ignoring epsilon and solvent or two solvation models given simultaneously" << std::endl + << std::endl + << std::endl; + } +} + double TBLiteInterface::GFNCalculation(int parameter, double* grad) { double energy = 0; - if (parameter == 0) { - m_tblite_calc = tblite_new_ipea1_calculator(m_ctx, m_tblite_mol); - } else if (parameter == 1) { - m_tblite_calc = tblite_new_gfn1_calculator(m_ctx, m_tblite_mol); - } else if (parameter == 2) { - m_tblite_calc = tblite_new_gfn2_calculator(m_ctx, m_tblite_mol); + int count = ((m_cpcm + m_cpcm_eps) != -1) + ((m_alpb + m_alpb_eps) != -1); + + + if (!m_calculator) { + if (parameter == 0) { + m_tblite_calc = tblite_new_ipea1_calculator(m_ctx, m_tblite_mol); + } else if (parameter == 1) { + m_tblite_calc = tblite_new_gfn1_calculator(m_ctx, m_tblite_mol); + } else if (parameter == 2) { + m_tblite_calc = tblite_new_gfn2_calculator(m_ctx, m_tblite_mol); + } + if (m_guess == 0) + tblite_set_calculator_guess(m_ctx, m_tblite_calc, TBLITE_GUESS_SAD); + else + tblite_set_calculator_guess(m_ctx, m_tblite_calc, TBLITE_GUESS_EEQ); + m_calculator = true; } - if (m_guess == 0) - tblite_set_calculator_guess(m_ctx, m_tblite_calc, TBLITE_GUESS_SAD); - else - tblite_set_calculator_guess(m_ctx, m_tblite_calc, TBLITE_GUESS_EEQ); + + ApplySolvation(); tblite_set_calculator_accuracy(m_ctx, m_tblite_calc, m_acc); tblite_set_calculator_max_iter(m_ctx, m_tblite_calc, m_maxiter); @@ -161,10 +259,55 @@ double TBLiteInterface::GFNCalculation(int parameter, double* grad) tblite_set_calculator_save_integrals(m_ctx, m_tblite_calc, 0); tblite_get_singlepoint(m_ctx, m_tblite_mol, m_tblite_calc, m_tblite_res); tblite_get_result_energy(m_error, m_tblite_res, &energy); + if (tblite_check_context(m_ctx)) { + std::cerr << "Error during SCF Calculation, reset wavefunction ..." << std::endl; + tbliteContextError(); + tbliteError(); - if (grad != NULL) - tblite_get_result_gradient(m_error, m_tblite_res, grad); + tblite_set_context_verbosity(m_ctx, m_verbose + 1); + if (count == 1) { + tblite_delete_container(&m_tb_cont); + } + tblite_delete_error(&m_error); + tblite_delete_context(&m_ctx); + tblite_delete_result(&m_tblite_res); + // tblite_delete_structure( &m_tblite_mol ); + tblite_delete_calculator(&m_tblite_calc); + tblite_delete_container(&m_tb_cont); + m_error = tblite_new_error(); + m_ctx = tblite_new_context(); + m_tblite_res = tblite_new_result(); + + tblite_set_context_verbosity(m_ctx, m_verbose); + + if (parameter == 0) { + m_tblite_calc = tblite_new_ipea1_calculator(m_ctx, m_tblite_mol); + } else if (parameter == 1) { + m_tblite_calc = tblite_new_gfn1_calculator(m_ctx, m_tblite_mol); + } else if (parameter == 2) { + m_tblite_calc = tblite_new_gfn2_calculator(m_ctx, m_tblite_mol); + } + if (m_guess == 0) + tblite_set_calculator_guess(m_ctx, m_tblite_calc, TBLITE_GUESS_SAD); + else + tblite_set_calculator_guess(m_ctx, m_tblite_calc, TBLITE_GUESS_EEQ); + ApplySolvation(); + tblite_get_singlepoint(m_ctx, m_tblite_mol, m_tblite_calc, m_tblite_res); + tblite_set_context_verbosity(m_ctx, m_verbose); + } + // double* virial = 0; + // tblite_get_result_virial(m_error, m_tblite_res, &virial); + // std::cout << virial << std::endl; + if (grad != NULL) { + tblite_get_result_gradient(m_error, m_tblite_res, grad); + if (tblite_check_context(m_ctx)) { + tbliteContextError(); + // return 0; + } + } + if (count == 1) + tblite_delete_container(&m_tb_cont); return energy; } @@ -208,3 +351,22 @@ std::vector> TBLiteInterface::BondOrders() const delete[] bonds; return bond_orders; } + +void TBLiteInterface::tbliteError() +{ + char message[512]; + tblite_get_error(m_error, message, NULL); + std::cerr << "[Message] " << message << std::endl; + + // printf("[Message] %s\n", message); + tblite_clear_error(m_error); +} + +void TBLiteInterface::tbliteContextError() +{ + char message[512]; + tblite_get_context_error(m_ctx, message, NULL); + std::cerr << "[Message] " << message << std::endl; + // printf("[Message] %s\n", message); + tblite_clear_error(m_error); +} diff --git a/src/core/tbliteinterface.h b/src/core/tbliteinterface.h index 55276ad..bb97544 100644 --- a/src/core/tbliteinterface.h +++ b/src/core/tbliteinterface.h @@ -28,12 +28,16 @@ #include "src/core/molecule.h" static json TBLiteSettings{ - { "tb_ac", 1 }, + { "tb_acc", 1 }, { "tb_max_iter", 250 }, { "tb_damping", 0.4 }, { "tb_temp", 9.500e-4 }, { "tb_verbose", 0 }, - { "tb_guess", "SAD" } + { "tb_guess", "SAD" }, + { "cpcm_solv", "none" }, + { "alpb_solv", "none" }, + { "cpcm_eps", -1 }, + { "alpb_eps", -1 } }; class UFF; @@ -50,12 +54,6 @@ class TBLiteInterface { bool UpdateMolecule(const Molecule& molecule); bool UpdateMolecule(const double* coord); - /* int parameter - * 66 = xtb GFN FF - * 0 = xtb GFN 0 - * 1 = xtb GFN 1 - * 2 = xtb GFN 2 - * */ double GFNCalculation(int parameter = 2, double* grad = 0); void clear(); @@ -66,6 +64,11 @@ class TBLiteInterface { std::vector> BondOrders() const; private: + void ApplySolvation(); + + void tbliteError(); + void tbliteContextError(); + Molecule m_molecule; double* m_coord; int* m_attyp; @@ -77,13 +80,16 @@ class TBLiteInterface { int m_guess = 0; double m_damping = 0.5; double m_temp = 1000; - + double m_cpcm_eps = -1, m_alpb_eps = -1; + char *m_cpcm_solv = "none", *m_alpb_solv = "none"; + bool m_cpcm = false, m_alpb = false; tblite_error m_error = NULL; tblite_structure m_tblite_mol = NULL; tblite_result m_tblite_res = NULL; tblite_context m_ctx = NULL; tblite_calculator m_tblite_calc = NULL; + tblite_container m_tb_cont = NULL; - bool m_initialised = false; + bool m_initialised = false, m_calculator = false; json m_tblitesettings; }; diff --git a/src/helpers/tblite_helper.cpp b/src/helpers/tblite_helper.cpp index 99a66f0..35ff5bf 100644 --- a/src/helpers/tblite_helper.cpp +++ b/src/helpers/tblite_helper.cpp @@ -22,8 +22,7 @@ int main(int argc, char** argv) double gradient[3 * 7]; auto error = tblite_new_error(); auto ctx = tblite_new_context(); - auto res = tblite_new_result(); - + auto cont = tblite_container(); auto mol = tblite_new_structure( error, natoms, @@ -36,6 +35,9 @@ int main(int argc, char** argv) tblite_set_context_logger(ctx, NULL, NULL); tblite_set_context_color(ctx, 5); auto calc = tblite_new_gfn2_calculator(ctx, mol); + cont = tblite_new_cpcm_solvation_solvent(ctx, mol, calc, "ethanol"); + auto res = tblite_new_result(); + tblite_get_singlepoint(ctx, mol, calc, res); tblite_get_result_gradient(error, res, gradient); for (int i = 0; i < 3 * 7; ++i) @@ -44,5 +46,6 @@ int main(int argc, char** argv) delete res; delete ctx; delete error; + delete cont; return 0; } diff --git a/src/main.cpp b/src/main.cpp index 31a1a78..276c884 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,6 +37,8 @@ #include "src/tools/general.h" #include "src/tools/info.h" +#include "src/capabilities/optimiser/OptimiseDipoleScaling.h" + #include #include #include @@ -411,11 +413,13 @@ int main(int argc, char **argv) { Molecule mol1 = Files::LoadFile(argv[2]); SimpleMD md(controller, false); md.setMolecule(mol1); + md.getBasename(argv[2]); + /* std::string name = std::string(argv[2]); for (int i = 0; i < 4; ++i) name.pop_back(); md.setBaseName(name); - + */ md.Initialise(); md.start(); } else if (strcmp(argv[1], "-confsearch") == 0) { @@ -819,6 +823,68 @@ int main(int argc, char **argv) { Molecule mol = file.Next(); mol.writeXYZFile(outfile, Tools::RandomVector(0, mol.AtomCount())); } + } else if (strcmp(argv[1], "-gyration") == 0) { + FileIterator file(argv[2]); + int count = 1; + double sum = 0; + + while (!file.AtEnd()) { + Molecule mol = file.Next(); + double gyr = mol.GyrationRadius(); + sum += gyr; + std::cout << ":: " << gyr << " " << sum / double(count) << std::endl; + count++; + } + } else if (strcmp(argv[1], "-dipole") == 0) { + FileIterator file(argv[2]); + + double target = 0, threshold = 1e-1, initial = 3; + bool target_set = false; + int maxiter = 10; + json blob = controller["dipole"]; + if (blob.contains("target")) { + target = blob["target"]; + target_set = true; + } + if (blob.contains("threshold")) { + threshold = blob["threshold"]; + } + if (blob.contains("initial")) { + initial = blob["initial"]; + } + if (blob.contains("maxiter")) { + maxiter = blob["maxiter"]; + } + + while (!file.AtEnd()) { + Molecule mol = file.Next(); + EnergyCalculator interface("gfn2", blob); + interface.setMolecule(mol); + interface.CalculateEnergy(false, true); + mol.setPartialCharges(interface.Charges()); + std::vector dipole_moment = interface.Dipole(); + if (!target_set) + target = sqrt(dipole_moment[0] * dipole_moment[0] + dipole_moment[1] * dipole_moment[1] + dipole_moment[2] * dipole_moment[2]); + std::cout << "Target dipole moment: " << target << std::endl; + auto dipoles = mol.CalculateDipoleMoments(); + for (const auto& dipole : dipoles) { + std::cout << std::endl + << std::endl + << "Dipole momement for single molecule " << dipole[0] << " " << dipole[1] << " " << dipole[2] << " : " << sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]) * 2.5418 << std::endl; + } + auto dipole = mol.CalculateDipoleMoment(); + std::cout << std::endl + << std::endl + << "Dipole momement for whole structure " << dipole[0] << " " << dipole[1] << " " << dipole[2] << " : " << sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]) * 2.5418 << std::endl; + + auto result = OptimiseScaling(&mol, target, initial, threshold, maxiter); + std::cout << "Final dipole moment " << result.first * 2.5418 << std::endl; + std::cout << "Final dipole moment " << result.first << std::endl; + + for (auto i : result.second) + std::cout << i << " "; + std::cout << std::endl; + } } else { bool centered = false; for (std::size_t i = 2; i < argc; ++i) { @@ -838,31 +904,8 @@ int main(int argc, char **argv) { mol.AnalyseIntermoleculeDistance(); std::cout << mol.Check() << std::endl << std::endl; - /* - Hessian hess("gfn2", UFFParameterJson); - hess.setMolecule(mol); - hess.CalculateHessian(); - */ - /* - UFF forcefield(UFFParameterJson); - forcefield.setMolecule(mol.Atoms(), mol.Coords()); - // forcefield.readParameterFile("parameter.json"); - - forcefield.Initialise(); - std::cout << forcefield.Calculate(true) << std::endl; - forcefield.writeParameterFile("parameter.json"); - forcefield.readParameterFile("parameter.json"); - std::cout << forcefield.Calculate(true) << std::endl; - */ - /* double grad[3 * mol.AtomCount()]; - forcefield.Gradient(grad); - forcefield.NumGrad(grad); - */ - // CompactTopo(mol.HydrogenBondMatrix(-1,-1)); - // std::cout << CompareTopoMatrix(mol.HydrogenBondMatrix(-1,-1),mol.HydrogenBondMatrix(-1,-1) ) << std::endl; - // auto m = mol.DistanceMatrix(); - // std::cout << m.first << std::endl; - // std::cout << m.second << std::endl; + std::cout << mol.COM().transpose() << std::endl; + std::cout << mol.GyrationRadius() << std::endl; } } }