diff --git a/plugins/pycv/.gitignore b/plugins/pycv/.gitignore new file mode 100644 index 0000000000..ce1d56103b --- /dev/null +++ b/plugins/pycv/.gitignore @@ -0,0 +1,4 @@ +Make.inc +*.o +*.so +env diff --git a/plugins/pycv/ActionWithPython.cpp b/plugins/pycv/ActionWithPython.cpp new file mode 100644 index 0000000000..097e868e1b --- /dev/null +++ b/plugins/pycv/ActionWithPython.cpp @@ -0,0 +1,142 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +Copyright (c) 2019 of Toni Giorgino + +The pycv module is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The pycv module 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with plumed. If not, see . ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ + +#include "ActionWithPython.h" + +#include "core/ActionWithValue.h" + +#include // everything needed for embedding +#include + +#include + + +namespace py = pybind11; + + +namespace PLMD { +namespace pycv { + + +// We can only have one interpreter globally. This is less than ideal +// because CVs can interfere with each other. The whole purpose of +// this superclass is to make a singleton. Putting it in the +// constructor makes it so that the interpreter is only initialized if +// one of the PYCV actions are used. +// https://pybind11.readthedocs.io/en/stable/reference.html#_CPPv422initialize_interpreterb + +unsigned PlumedScopedPythonInterpreter::use_count=0; +std::unique_ptr PlumedScopedPythonInterpreter::interpreterGuard = + nullptr; +std::mutex PlumedScopedPythonInterpreter::interpreterMutex{}; + +PlumedScopedPythonInterpreter::PlumedScopedPythonInterpreter(::PLMD::Log&outlog) + :log(outlog) { + std::lock_guard lk(interpreterMutex); + if(use_count==0 && Py_IsInitialized()) { + //this addresses the "calling pycv within a python interpreter problem" + ++use_count; + } + + if(use_count==0) { + std::cerr<< "------ initialized Python interpreter\n"; + log<< "------ initialized Python interpreter\n"; + interpreterGuard = std::make_unique(); + } else { + std::cerr << "------ Python interpreter already initializated\n"; + log << "------ Python interpreter already initializated\n"; + } + ++use_count; +} + +PlumedScopedPythonInterpreter::~PlumedScopedPythonInterpreter() { + // Finalization is tricky, because it should happen AFTER the + // destruction of ALL the python declared variables + std::lock_guard lk(interpreterMutex); + --use_count; + if(use_count==0) { + interpreterGuard.reset(nullptr); + std::cerr << "------ Python interpreter finalized\n"; + log << "------ Python interpreter finalized\n"; + } +} + +ActionWithPython::ActionWithPython (const ActionOptions&ao) + :Action(ao),guard(log) {} + +void ActionWithPython::pyParseFlag(const char* key, const ::pybind11::dict &initDict, bool& returnValue) { + parseFlag(key, returnValue); + if(initDict.contains(key)) { + bool defaultRet; + keywords.getLogicalDefault(key,defaultRet); + if (returnValue!=defaultRet) { + error(std::string("you specified the same keyword ").append(key)+ " both in python and in the settings file"); + } + returnValue = initDict[key].cast(); + } +} +/******************************************************************************/ +//Value/components init +void initializeValue(::PLMD::ActionWithValue& action,pybind11::dict &settingsDict) { + action.log << " will have a single component"; + bool withDerivatives=false; + if(settingsDict.contains("derivative")) { + withDerivatives=settingsDict["derivative"].cast(); + } + if(withDerivatives) { + action.addValueWithDerivatives(); + action.log << " WITH derivatives\n"; + } else { + action.addValue(); + action.log << " WITHOUT derivatives\n"; + } +} + +void initializeComponent(::PLMD::ActionWithValue& action,const std::string&name,py::dict &settingsDict) { + bool withDerivatives=false; + if(settingsDict.contains("derivative")) { + withDerivatives=settingsDict["derivative"].cast(); + } + + if(withDerivatives) { + action.addComponentWithDerivatives(name); + action.log << " WITH derivatives\n"; + } else { + action.addComponent(name); + action.log << " WITHOUT derivatives\n"; + } +} + +void valueSettings(py::dict &settings, Value* valPtr) { + if(settings.contains("period")) { + if (settings["period"].is_none()) { + valPtr->setNotPeriodic(); + } else { + py::tuple t = settings["period"]; + if(t.size()!=2) { + plumed_merror("period must have exactly 2 components"); + } + //the ballad py::str(t[0]).cast() is to not care about the type of input of the user + std::string min=py::str(t[0]).cast(); + std::string max=py::str(t[1]).cast(); + valPtr->setDomain(min, max); + } + } +} + +} // namespace pycv +} // namespace PLMD diff --git a/plugins/pycv/ActionWithPython.h b/plugins/pycv/ActionWithPython.h new file mode 100644 index 0000000000..a941eb4bae --- /dev/null +++ b/plugins/pycv/ActionWithPython.h @@ -0,0 +1,81 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +Copyright (c) 2019 of Toni Giorgino + +The pycv module is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The pycv module 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with plumed. If not, see . ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#ifndef __PLUMED_pycv_ActionWithPython_h //{ +#define __PLUMED_pycv_ActionWithPython_h + +#include +#include + +#include "core/Action.h" + +#include // everything needed for embedding + +namespace PLMD { +class Value; +namespace pycv { + +using pycvComm_t = double; +static constexpr auto PYTHONCV_CITATION = "Giorgino, (2019). PYCV: a PLUMED 2 Module Enabling the Rapid Prototyping of Collective Variables in Python. Journal of Open Source Software, 4(42), 1773. doi:10.21105/joss.01773"; +static constexpr auto BIASING_DISABLED = "PYCV: Gradient was expected as a second return value but is missing. Biasing won't work\n"; +static constexpr std::string_view PYCV_COMPONENTPREFIX="py"; + +///This class act both as a guard for the interpreter and a a case container for the python module and functions +class PlumedScopedPythonInterpreter final { +public: + PlumedScopedPythonInterpreter(::PLMD::Log&); + ~PlumedScopedPythonInterpreter(); +private: + ::PLMD::Log& log; + static unsigned use_count; + static std::unique_ptr<::pybind11::scoped_interpreter> interpreterGuard; + static std::mutex interpreterMutex; +}; + +class ActionWithPython: public virtual ::PLMD::Action { + //the guard MUST be set up before the python objects + // (so that it can be destroyed after them) + PlumedScopedPythonInterpreter guard; +public: + explicit ActionWithPython (const ActionOptions&); + ///redefinition of parse to avoid confict between plumed.dat and python options + template + void pyParse(const char* key, const ::pybind11::dict &initDict, T& returnValue); +///redefinition of parseFlag to avoid confict between plumed.dat and python options + void pyParseFlag(const char* key, const ::pybind11::dict &initDict, bool& returnValue); +}; + +template +void ActionWithPython::pyParse( + const char* key, const ::pybind11::dict &initDict, T& returnValue) { + T initVal(returnValue); + parse(key,returnValue); + //this is not robust, but with no access to Action::line we cannot use Tools::findKeyword + if(initDict.contains(key)) { + if (returnValue != initVal) { + error(std::string("you specified the same keyword ").append(key)+ " both in python and in the settings file"); + } + returnValue = initDict[key].cast(); + } +} + +void initializeValue(::PLMD::ActionWithValue&, pybind11::dict &); +void initializeComponent(::PLMD::ActionWithValue&,const std::string&,pybind11::dict &); +void valueSettings( pybind11::dict &r, Value* valPtr); + +} // namespace pycv +} // namespace PLMD +#endif //__PLUMED_pycv_ActionWithPython_h //} diff --git a/plugins/pycv/COPYRIGHT b/plugins/pycv/COPYRIGHT new file mode 100644 index 0000000000..31f6df6c8b --- /dev/null +++ b/plugins/pycv/COPYRIGHT @@ -0,0 +1,14 @@ +Copyright (c) 2019 of Toni Giorgino + +The pycv module is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The pycv module 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with plumed. If not, see . diff --git a/plugins/pycv/Makefile b/plugins/pycv/Makefile new file mode 100644 index 0000000000..6fd8791ea2 --- /dev/null +++ b/plugins/pycv/Makefile @@ -0,0 +1,37 @@ +include Make.inc + +#Dependency tracking based on https://make.mad-scientist.net/papers/advanced-auto-dependency-generation/#tldr +#this assumes gcc +DEPDIR := .deps +DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d + +OBJS = ActionWithPython.o PythonCVInterface.o PythonFunction.o PlumedPythonEmbeddedModule.o + +ADDCPPFLAGS=$(shell python3-config --cflags --embed) $(shell python -m pybind11 --includes) $(PLUMED_INCLUDE) +ADDCLDFLAGS=$(shell python3-config --ldflags --embed) + +ifeq ($(SOEXT),dylib) + SONAME_OPTION:=-Wl,-install_name +else + SONAME_OPTION:=-Wl,-soname +endif + +all: PythonCVInterface.$(SOEXT) + + +%.o: %.cpp $(DEPDIR)/%.d | $(DEPDIR) + @echo Compiling object $@ + @$(CXX) -c $(DEPFLAGS) $(CPPFLAGS) $(ADDCPPFLAGS) $(CXXFLAGS) $< -o $@ + +$(DEPDIR): ; @mkdir -p $@ + +DEPFILES := $(OBJS:%.o=$(DEPDIR)/%.d) +$(DEPFILES): +include $(wildcard $(DEPFILES)) + +PythonCVInterface.$(SOEXT): $(OBJS) + @echo Compiling linking $@ + @$(LDSHARED) $(ADDCLDFLAGS) $(SONAME_OPTION),"$(notdir $@)" $(DYNAMIC_LIBS) $(PLUMED_KERNEL) -o $@ $^ + +clean: + rm -f $(OBJS) PythonCVInterface.$(SOEXT) \ No newline at end of file diff --git a/plugins/pycv/PlumedPythonEmbeddedModule.cpp b/plugins/pycv/PlumedPythonEmbeddedModule.cpp new file mode 100644 index 0000000000..8ffc1959ee --- /dev/null +++ b/plugins/pycv/PlumedPythonEmbeddedModule.cpp @@ -0,0 +1,276 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +Copyright (c) 2023 Daniele Rapetti + +The pycv module is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The pycv module 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with plumed. If not, see . ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ + +#include // everything needed for embedding +#include +#include +#include +#include + +#include "tools/Vector.h" +#include "tools/NeighborList.h" + +#include "PythonCVInterface.h" +#include "PythonFunction.h" + +namespace py=pybind11; + +PYBIND11_MAKE_OPAQUE(std::vector) + +#define defGetter(pyfun,classname, cppfun, type, description) \ + def(pyfun, [](classname* self) -> type{ \ + return self->cppfun(); }, description) + +PYBIND11_EMBEDDED_MODULE(plumedCommunications, m) { + py::module_ defaults = m.def_submodule("defaults", "Submodule with the default definitions"); + defaults.attr("COMPONENT") = py::dict(py::arg("period")=py::none(),py::arg("derivative")=true); + defaults.attr("COMPONENT_NODEV") = py::dict(py::arg("period")=py::none(),py::arg("derivative")=false); + py::bind_vector>(m, "VectorAtomNumber"); + py::class_(m, "PythonCVInterface") + .def_readwrite("data",&PLMD::pycv::PythonCVInterface::dataContainer,"Return an accessible dictionary that persist along all the simulation") + /***************************Start of Action methods***************************/ + //usin "PLMD::pycv::PythonCVInterface::getStep" instead of the lambda gives compilation errors + .defGetter("getStep",PLMD::pycv::PythonCVInterface,getStep, long int,"Returns the current step") + .defGetter("getTime",PLMD::pycv::PythonCVInterface,getTime,double,"Return the present time") + .defGetter("getTimeStep",PLMD::pycv::PythonCVInterface,getTimeStep,double,"Return the timestep") + .defGetter("isRestart",PLMD::pycv::PythonCVInterface,getRestart,bool,"Return true if we are doing a restart") + .defGetter("isExchangeStep",PLMD::pycv::PythonCVInterface,getExchangeStep,bool,"Check if we are on an exchange step") + .def_property_readonly("label", + [](PLMD::pycv::PythonCVInterface* self) -> std::string { + return self->getLabel(); + }, + "returns the label") + .def("log",[](PLMD::pycv::PythonCVInterface* self, py::object data) { + self->log << py::str(data).cast(); + }, + "puts a string in the PLUMED output",py::arg("s")) + .def("lognl",[](PLMD::pycv::PythonCVInterface* self, py::object data) { + self->log << py::str(data).cast()<< "\n"; + }, + "puts a string in the PLUMED output (and appends a newline)",py::arg("s")) + /****************************End of Action methods****************************/ + .def("getPosition", + [](PLMD::pycv::PythonCVInterface* self, int i) -> py::array_t { + py::array_t::ShapeContainer shape({3}); + py::array_t atom(shape,&self->getPosition(i)[0]); + return atom; + }, + "Returns an ndarray with the position of the \"i\"th" + " atom requested by the action",py::arg("i")) + .def_property_readonly("nat", + [](PLMD::pycv::PythonCVInterface* self) -> size_t { + return self->getPositions().size(); + }, + "return the number of atoms" + ) + //here I can use &PLMD::pycv::PythonCVInterface::makeWhole because is not in Action + // that is inherithed both by withValue and Atomistic + .def("makeWhole",&PLMD::pycv::PythonCVInterface::makeWhole,"Make atoms whole, assuming they are in the proper order") + .def("getPositions",[](PLMD::pycv::PythonCVInterface* self) -> py::array_t { + auto nat=self->getPositions().size(); + py::array_t::ShapeContainer shape({nat,3}); + py::array_t atomList(shape, + &self->getPositions()[0][0] + ); + return atomList; + }, + "Returns a numpy.array that contains the atomic positions of the atoms requested by the action") + .def("getPbc",&PLMD::pycv::PythonCVInterface::getPbc, + "returns an interface to the current pbcs") + .def("getNeighbourList",&PLMD::pycv::PythonCVInterface::getNL, + "returns an interface to the current Neighborlist") +//https://pybind11.readthedocs.io/en/stable/advanced/functions.html#return-value-policies + /*.def("getAbsoluteIndexes", &PLMD::pycv::PythonCVInterface::getAbsoluteIndexes , + "Get the vector of absolute indexes.", + py::return_value_policy::reference)*/ + .def_property_readonly("absoluteIndexes", + &PLMD::pycv::PythonCVInterface::getAbsoluteIndexes, + "Get the vector of absolute indexes.", + py::return_value_policy::reference_internal + ) + .def("charge", &PLMD::pycv::PythonCVInterface::getCharge, + "Get charge of i-th atom", py::arg("i") + ) + .def("mass", &PLMD::pycv::PythonCVInterface::getMass, + "Get mass of i-th atom", py::arg("i") + ) + .def("masses", + [](PLMD::pycv::PythonCVInterface* self) -> py::array_t { + auto nat=self->getPositions().size(); + py::array_t::ShapeContainer shape({nat}); + py::array_t masses(shape); + auto retAccessor = masses.mutable_unchecked<1>(); + for (auto i=0u; i < nat; ++i) { + retAccessor(i)=self->getMass(i); + } + return masses; + }, + "Returns and ndarray with the masses of the atoms requested by the action") + .def("charges", + [](PLMD::pycv::PythonCVInterface* self) -> py::array_t { + auto nat=self->getPositions().size(); + py::array_t::ShapeContainer shape({nat}); + py::array_t charges(shape); + auto retAccessor = charges.mutable_unchecked<1>(); + for (auto i=0u; i < nat; ++i) { + retAccessor(i)=self->getCharge(i); + } + return charges; + }, + "Returns and ndarray with the charges of the atoms requested by the action") + ; + py::class_(m, "PLMDPbc") + //.def(py::init<>()) + .def("apply",[](const PLMD::Pbc* self, py::array_t& deltas) -> py::array_t { + //TODO:shape check + //TODO: this may be set up to modify the passed deltas, so no new intialization + // ^this needs to be VERY clear to te user + auto accessor = deltas.unchecked<2>(); + auto nat=deltas.shape(0); + py::array_t toRet({nat,deltas.shape(1)}); + auto retAccessor = toRet.mutable_unchecked<2>(); + for (auto i=0u; i < nat; ++i) { + auto t= self->distance(PLMD::Vector(0.0,0.0,0.0), + //I think this may be slow, but serves as a demonstration as a base for the tests + PLMD::Vector(accessor(i,0),accessor(i,1),accessor(i,2)), + nullptr); + retAccessor(i,0) = t[0]; + retAccessor(i,1) = t[1]; + retAccessor(i,2) = t[2]; + } + return toRet; + }, + //this should be optimized for speed +#define T3x3toArray( boxGetter ) \ +py::array_t toRet({3,3}); \ +auto retAccessor = toRet.mutable_unchecked<2>(); \ +PLMD::Tensor box=boxGetter; \ +for(unsigned i=0;i<3;++i){ \ + for(unsigned j=0;j<3;++j) \ + retAccessor(i,j) = box(i, j); \ +} \ +return toRet; + "Apply PBC to a set of positions or distance vectors") + .def("getBox",[](const PLMD::Pbc* self) -> py::array_t { + T3x3toArray( self->getBox() ) + /* + py::array_t toRet({3,3}); + auto retAccessor = toRet.mutable_unchecked<2>(); + PLMD::Tensor box=self->getBox(); + //TODO: optimize for speed: + for(unsigned i=0;i<3;++i){ + for(unsigned j=0;j<3;++j) + retAccessor(i,j) = box(i,j); + } + return toRet;*/ + }, + "Get a numpy array of shape (3,3) with the box vectors") + .def("getInvBox",[](const PLMD::Pbc* self) -> py::array_t { + T3x3toArray( self->getInvBox() ) + /* + py::array_t toRet({3,3}); + auto retAccessor = toRet.mutable_unchecked<2>(); + PLMD::Tensor box=self->getInvBox(); + //TODO: optimize for speed: + for(unsigned i=0;i<3;++i){ + for(unsigned j=0;j<3;++j) + retAccessor(i,j) = box(i,j); + } + return toRet; + */ + }, + "Get a numpy array of shape (3,3) with the inverted box vectors") +#undef T3x3toArray + ; + py::class_(m, "NeighborList") + //.def(py::init<>()) + //https://numpy.org/doc/stable/user/basics.types.html + //numpy.uint=unsigned long + .def("size",&PLMD::NeighborList::size,"return the number of pairs") + .def("__len__", &PLMD::NeighborList::size) + .def("getClosePairs",[](const PLMD::NeighborList* self)->py::array_t { + auto ncouples = self->size(); + py::array_t::ShapeContainer shape({ncouples,2}); + py::array_t couples(shape); + auto retAccessor = couples.mutable_unchecked<2>(); + for(size_t c=0; c< ncouples; ++c) { + retAccessor(c,0) = self->getClosePair(c).first; + retAccessor(c,1) = self->getClosePair(c).second; + } + return couples; + }, + + "get a (NC,2) nd array with the list of couple indexes") + ; + py::class_(m, "AtomNumber") + .def(py::init<>()) +// + .def_property_readonly("index", + static_cast(&PLMD::AtomNumber::index), + "The index number.") + .def_property_readonly("serial", + static_cast(&PLMD::AtomNumber::serial), + "The index number.") + ; + py::class_(m, "PythonFunction") + /***************************Start of Action methods***************************/ + //usin "PLMD::pycv::PythonCVInterface::getStep" instead of the lambda gives compilation errors + .defGetter("getStep",PLMD::pycv::PythonFunction,getStep, long int,"Returns the current step") + .defGetter("getTime",PLMD::pycv::PythonFunction,getTime,double,"Return the present time") + .defGetter("getTimeStep",PLMD::pycv::PythonFunction,getTimeStep,double,"Return the timestep") + .defGetter("isRestart",PLMD::pycv::PythonFunction,getRestart,bool,"Return true if we are doing a restart") + .defGetter("isExchangeStep",PLMD::pycv::PythonFunction,getExchangeStep,bool,"Check if we are on an exchange step") + .def_property_readonly("label", + [](PLMD::pycv::PythonFunction* self) -> std::string { + return self->getLabel(); + }, + "returns the label") + .def("log",[](PLMD::pycv::PythonFunction* self, py::object data) { + self->log << py::str(data).cast(); + }, + "puts a string in the PLUMED output",py::arg("s")) + .def("lognl",[](PLMD::pycv::PythonFunction* self, py::object data) { + self->log << py::str(data).cast()<< "\n"; + }, + "puts a string in the PLUMED output (and appends a newline)",py::arg("s")) + /****************************End of Action methods****************************/ + .def("argument", &PLMD::pycv::PythonFunction:: + getArgument,"Get value of the of i-th argument", py::arg("i")) + .def("arguments", [](PLMD::pycv::PythonFunction* self) -> py::array_t { + auto nargs=self->getNumberOfArguments(); + py::array_t::ShapeContainer shape({nargs}); + py::array_t arguments(shape); + for(auto i=0u; i < nargs; ++i) + arguments.mutable_at(i) = self->getArgument(i); + return arguments; + } + ,"Retuns a ndarray with the values of the arguments") + .def_property_readonly("nargs", &PLMD::pycv::PythonFunction:: + getNumberOfArguments,"Get the number of arguments") + .def("difference", &PLMD::pycv::PythonFunction:: + difference,"Takes the difference taking into account pbc for argument i", + py::arg("i"),py::arg("x"),py::arg("y")) + .def("bringBackInPbc", &PLMD::pycv::PythonFunction:: + bringBackInPbc,"Takes one value and brings it back into the pbc of argument i", + py::arg("i"),py::arg("x")) + //I cannot find a way of testing: + // .def("getProjection", &PLMD::pycv::PythonFunction:: + // getProjection,"Get the scalar product between the gradients of two variables", + // py::arg("i"),py::arg("j")) + //to be added and tested getNumberOfArguments + ; +} diff --git a/plugins/pycv/PythonCVInterface.cpp b/plugins/pycv/PythonCVInterface.cpp new file mode 100644 index 0000000000..8aa0536577 --- /dev/null +++ b/plugins/pycv/PythonCVInterface.cpp @@ -0,0 +1,772 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +Copyright (c) 2019-2023 of Toni Giorgino + +The pycv module is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The pycv module 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with plumed. If not, see . ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#include "PythonCVInterface.h" + +#include "core/ActionRegister.h" +#include "core/PlumedMain.h" +#include "tools/NeighborList.h" +#include "tools/Pbc.h" + +#include // everything needed for embedding +#include +#include + +#include +#include +#include +#include + +//+PLUMEDOC COLVAR PYCVINTERFACE +/* +Define collective variables in the Python language. + +Plumed will import the module chosen wiht the `IMPORT` keyword. +The module can be a simple py file or a directory with the standard module +configuration (a directory that contains at least an __init__.py, see the +rt-persistentData test for an example). + +In the module there must be a function that will be used in caclulation and the +definition of the component(s) (meaning period and if derivatives will be +returned) of your cv. + +PYCVINTERFACE will call two or more elements from the module. +Each element or function can be selected with its dedicated keyword: + - `CALCULATE` will select the function to be called at each step (defaults to + `"plumedCalculate"`) + - `INIT` will select the function (or the dict, see down) to be called during + the initilization (defaults to `"plumedInit"`) + - `UPDATE` will select the function to be called at each step, during the + update() invocation + - `PREPARE` will select the function to be called at each step, during the + prepare() invocation +All the function called will need a single argument of type + `plumedCommunications.PythonCVInterface` + + +\par Getting started + +The minimal plumed input is: +\plumedfile +cv1: PYTHONCV IMPORT=hello ATOMS=1 +PRINT FILE=colvar.out ARG=* +\endplumedfile + +this should be paired with the `hello.py` file: +@code{.py} +import plumedCommunications as PLMD + +plumedInit={"Value":PLMD.defaults.COMPONENT_NODEV} + +def plumedCompute(action: PLMD.PythonCVInterface): + action.log("Hello, world, from calculate!") + return 0.0 +@endcode + +If `INIT` is not specified, plumed will search for an object "plumedInit", +that can be either a function that returns a dict or a dict +This dict MUST contain at least the informations about the presence of the +derivatives and on the periodicity of the variable. +We will refer to this dict as "the init dict" from now. + +If `CALCULATE` is not specified, plumed will search for a function named +"plumedCalculate" plumed will read the variable returned accordingly to what it +was specified in the initialization dict. + +The init dict will tell plumed how many components the calculate function will +return and how they shall behave. +Along this the dict can contain all the keyword that are compatible with +PYCVINTERFACE. +Mind that if the same keyword is specified both in the init dict and in the +plumed file the calculation will be aborted to avoid unwated settings confict. +In case of flags the dict entry must be a bool, differently from the standard +plumed input. + +The only keyword that can only be specified in python is `COMPONENTS`. +The `COMPONENTS` key must point to a dict that has as keys the names of the +componensts. +Each component dictionary must have two keys: + - `"period"`: `None` of a list of two values, min and max (like `[0,1]` or also + strings like `["0.5*pi","2*pi"]`) + - `"derivative"`: `True` or `False` +If you want to use a single component you can create the `"COMPONENTS"` dict +with as single key, the name will be ignored. +In the previous example the key `"Value"` is used instead of `"COMPONENTS"`: +it is a shorter form for `"COMPONENTS":{"any":{...}}`. +To avoid confusion you cannot specify both `"COMPONENTS"` and `"Value"` in the + same dict. + +To speed up the declarations of the components the `plumedCommunications` module +contains a submodule `defaults` with the default dictionaries already set up: + - plumedCommunications.defaults.COMPONENT={"period":None, "derivative":True} + - plumedCommunications.defaults.COMPONENT_NODEV={"period":None, "derivative":False} + +\par The calculate function + +The calculate funtion must, as all the other functions accept a +PLMD.PythonCVInterface object as only input. + +The calculate function must either return a float or a tuple or, in the case of +multiple components, a dict whose keys are the name of the components, whose +elements are either float or tuple. + +Plumed will assign automatically assign the result to the CV (to the key named +element), if the name of the component is missing the calculation will be +interrupted with an error message. +If derivatives are disabled it will expect a float(or a double). +In case of activated derivatives it will interrupt the calculation if the +return value would not be a tuple. +The tuple should be (float, ndArray(nat,3),ndArray(3,3)) with the first +elements the value, the second the atomic derivatives and the third the box +derivative (that can also have shape(9), with format (x_x, x_y, x_z, y_x, y_y, +y_z, z_x, z_y, z_z)), if the box derivative are not present a WARNING will be +raised, but the calculation won't be interrupted. + +\par The prepare and update functions and the "data" attribute + +If the `PREPARE` keyword is used, the defined function will be called at +prepare time, before calculate. +The prepare dictionary can contain a `"setAtomRequest"` key with a parseable +ATOM string, like in the input (or a list of indexes, 0 based). +@code{.py} +#this , with "PREPARE=changeAtom" in the plumed file will select a new atom at each new step +def changeAtom(plmdAction: plumedCommunications.PythonCVInterface): + toret = {"setAtomRequest": f"1, {int(plmdAction.getStep()) + 2}"} + if plmdAction.getStep() == 3: + toret["setAtomRequest"] = "1,2" + return toret +@endcode + +If the `UPDATE` keyword is used, the defined function will be called at update +time, after calculate. As now plumed will ignore the return of this function +(but it stills need to return a dict) and it is intended to accumulate things +or post process data afer calculate + +In the example `plmdAction.data["pycv"]=0` is intialized in `pyinit` and its +value is updated in calculate. + +\plumedfile +cv1: PYCVINTERFACE ... + ATOMS=@mdatoms + IMPORT=pycvPersistentData + CALCULATE=pydist + INIT=pyinit +... + +PRINT FILE=colvar.out ARG=* +\endplumedfile + +@code{.py} +import plumedCommunications as PLMD +from plumedCommunications.defaults import COMPONENT_NODEV + +def pyinit(plmdAction: PLMD.PythonCVInterface): + plmdAction.data["pycv"]=0 + print(f"{plmdAction.data=}", file=log) + return {"Value":COMPONENT_NODEV} + +def pydist(plmdAction: PLMD.PythonCVInterface): + plmdAction.data["pycv"]+=plmdAction.getStep() + d=plmdAction.data["pycv"] + return d +@endcode + +The plumedCommunications.PythonCVInterface has a `data` attribute that is a +dictionary and can be used to store data during the calculations + + +\par Getting the manual +You can obtain the manual of the module (and of its components) +by running plumed driver with this plumed file +\plumedfile +LOAD GLOBAL FILE=PythonCVInterface.so +cvdist: PYCVINTERFACE IMPORT=pyhelp +PRINT FILE=colvar.out ARG=* +\endplumedfile +and this py module +@code{.py} +import plumedCommunications +import pydoc + +def plumedInit(_): + with open('PythonCVInterface.help.txt', 'w') as f: + h = pydoc.Helper(output=f) + h(plumedCommunications.PythonCVInterface) + with open('plumedCommunications.help.txt', 'w') as f: + h = pydoc.Helper(output=f) + h(plumedCommunications) + with open('plumedCommunications.defaults.help.txt', 'w') as f: + h = pydoc.Helper(output=f) + h(plumedCommunications.defaults) + return {"Value":plumedCommunications.defaults.COMPONENT_NODEV, "ATOMS":"1"} + +def plumedCalculate(_): + return 0 +@endcode + + +\par Tips and tricks and examples + +Automatic differentiation and transparent compilation (including to +GPU) can be performed via Google's [JAX +library](https://github.com/google/jax): see the example below. + + +The following input tells PLUMED to print the distance between atoms 1 +and 4. + +\plumedfile +cv1: PYTHONCV ATOMS=1,4 IMPORT=distcv CALCULATE=cv +PRINT FILE=colvar.out ARG=* +\endplumedfile + +The file `distcv.py` should contain something as follows. + +@code{.py} +import numpy as np +import plumedCommunications as PLMD + +plumedInit={"Value":PLMD.defaults.COMPONENT} +# Define the distance function +def dist_f(x): + r = x[0,:]-x[1,:] + d2 = np.dot(r,r) + return np.sqrt(d2) + +def grad_dist(x): + d = dist_f(x) + r = x[0,:]-x[1,:] + g = r/d + return np.array([g,-g]) + +# The CV function actually called +def cv(action:PLMD.PythonCVInterface): + return dist_f(action.getPositions()), grad_dist(action.getPositions()) + +@endcode + + +\par JAX for automatic differentiation and compilation + +Automatic differentiation and transparent compilation (including to +GPU) can be performed via Google's [JAX +library](https://github.com/google/jax). In a nutshell, it's sufficient +to replace `numpy` with `jax.numpy`. See the following example. + + +\plumedfile +cv1: PYTHONCV ATOMS=1,2,3 IMPORT=jaxcv CALCULATE=angle +PRINT FILE=colvar.out ARG=* + +\endplumedfile + + +And, in `jaxcv.py`... + +@code{.py} +# Import the JAX library +import jax.numpy as np +from jax import grad, jit, vmap +import plumedCommunications as PLMD + +plumedInit={"Value":PLMD.defaults.COMPONENT} + +# Implementation of the angle function +def angle_f(x): + r1 = x[0,:]-x[1,:] + r2 = x[2,:]-x[1,:] + + costheta = np.dot(r1,r2) / np.linalg.norm(r1) / np.linalg.norm(r2) + theta = np.arccos(costheta) + return theta + +# Use JAX to auto-gradient it +angle_grad = grad(angle_f) + +def cv(action:PLMD.PythonCVInterface): + return angle_f(action.getPositions()), angle_grad(action.getPositions()) +@endcode + + +There are however +[limitations](https://github.com/google/jax#current-gotchas), the most +notable of which is that indexed assignments such as `x[i]=y` are not +allowed, and should instead be replaced by functional equivalents such +as `x=jax.ops.index_update(x, jax.ops.index[i], y)`. + + +\par Multiple components + +It is possible to return multiple components at a time. This may be +useful e.g. if they reuse part of the computation. To do so, pass the +declare the `"COMPONENTS"` key in the init dict, and assign to each key a dict +with the same rules of the key `"Value"`. In this case, the function must +return a dict with the component names as keys, see the "calculate" paragraph. +Inside PLUMED, component names +will be prefixed by `py-`. + +Note that you can use JAX's Jacobian function `jax.jacrev()` to +conveniently compute the gradients all at once (see regtests). For +example: + +\plumedfile +cv1: PYTHONCV ATOMS=1,3,4 IMPORT=distcv FUNCTION=cv COMPONENTS=d12,d13 +\endplumedfile + +@code{.py} +import jax.numpy as np +from jax import jacrev, jit +import plumedCommunications as PLMD + +plumedInit = dict( + COMPONENTS=dict(d12=PLMD.defaults.COMPONENT, d13=PLMD.defaults.COMPONENT) +) + +# Define the distance function +@jit +def dist_f(X): + return { + 'd12': np.linalg.norm( X[0,:]-X[1,:] ), + 'd13': np.linalg.norm( X[0,:]-X[2,:] ) + } + +dist_grad=jacrev(dist_f) + +def cv(action: PLMD.PythonCVInterface): + toret = {} + d=dist_f(action.getPositions()) + g=dist_grad(action.getPositions()) + for key in ["d12","d13"]: + toret[key] = (d[key],g[key]) + return toret +@endcode + +\par Installation + +A use of an virtual environment or equivalent is recomended. +To compile pycv you just need numpy and pybind11, jax is not necessary for +compilation and installation. + +To compile the shared object library you need also plumed in your path. +You need to export the following environmental variables: +\verbatim +export PLUMED_MKLIB_CFLAGS="$(python3-config --cflags --embed) $(python -m pybind11 --includes)" +export PLUMED_MKLIB_LDFLAGS="$(python3-config --ldflags --embed)" +\endverbatim +and then compile the shared object: +\verbatim +plumed mklib PythonCVInterface.cpp ActionWithPython.cpp PlumedPythonEmbeddedModule.cpp +\endverbatim + +If you are on linux you can use pycv only with a plumed version that is +compatible with the `GLOBAL` keyword for the action `LOAD` + +*/ +//+ENDPLUMEDOC + + +#define vdbg(...) \ + std::cerr << std::boolalpha<(initFcn)) { + initDict = initFcn; + } else { + initDict = initFcn(this); + } + } else if(initFunName!=PYCV_DEFAULTINIT) { + //If the default INIT is not preset, is not a problem + error("the function "+ initFunName + " is not present in "+ import); + } + + std::string prepareFunName; + parse("PREPARE",prepareFunName); + if (prepareFunName!=PYCV_NOTIMPLEMENTED) { + if (!py::hasattr(pyModule,prepareFunName.c_str())) { + error("the function " + prepareFunName + " is not present in "+ import); + } + hasPrepare=true; + pyPrepare=pyModule.attr(prepareFunName.c_str()); + log.printf(" will use %s while calling prepare() before calculate()\n", prepareFunName.c_str()); + } + + std::string updateFunName; + parse("UPDATE",updateFunName); + if (updateFunName!=PYCV_NOTIMPLEMENTED) { + if (!py::hasattr(pyModule,updateFunName.c_str())) { + error("the function " + updateFunName + " is not present in " + import); + } + pyUpdate=pyModule.attr(updateFunName.c_str()); + hasUpdate=true; + log.printf(" will use %s while calling update() after calculate()\n", updateFunName.c_str()); + } + + { + std::vector components; + parseVector("COMPONENTS", components); + + if (components.size()>1) { + error("Please define multiple COMPONENTS from INIT in python."); + } + } + if(initDict.contains("COMPONENTS")) { + if(initDict.contains("Value")) { + error("The initialize dict cannot contain both \"Value\" and \"COMPONENTS\""); + } + if(!py::isinstance(initDict["COMPONENTS"])) { + error("COMPONENTS must be a dictionary using with the name of the components as keys"); + } + py::dict components=initDict["COMPONENTS"]; + for(auto comp: components) { + auto settings = py::cast(comp.second); + if(components.size()==1) { //a single component + initializeValue(dynamic_cast<::PLMD::ActionWithValue&>(*this), settings); + valueSettings(settings,getPntrToValue()); + } else { + auto name=std::string(PYCV_COMPONENTPREFIX) + +"-"+py::cast(comp.first); + initializeComponent(dynamic_cast<::PLMD::ActionWithValue&>(*this), + name, + settings); + valueSettings(settings,getPntrToComponent(name)); + } + } + + } else if(initDict.contains("Value")) { + py::dict settingsDict=initDict["Value"]; + initializeValue(dynamic_cast<::PLMD::ActionWithValue&>(*this),settingsDict); + valueSettings(settingsDict,getPntrToValue()); + } else { + warning(" WARNING: by defaults components periodicity is not set and component is added without derivatives - see manual\n"); + //this will crash with an error, beacuse periodicity is not explicitly set + addValue(); + } + + std::vector atoms; + pyParseAtomList("ATOMS",initDict,atoms); + std::vector groupA; + pyParseAtomList("GROUPA",initDict,groupA); + std::vector groupB; + pyParseAtomList("GROUPB",initDict,groupB); + + if(atoms.size() !=0 && groupA.size()!=0) + error("you can choose only between using the neigbourlist OR the atoms"); + + if(atoms.size()==0&& groupA.size()==0 && groupB.size()==0) + error("At least one atom is required"); + + if (atoms.size() != 0 && groupA.size() != 0) + error("you can choose only between using the neigbourlist OR the atoms"); + + if (atoms.size() == 0 && groupA.size() == 0 && groupB.size() == 0) + error("At least one atom is required"); + + bool nopbc; + pyParseFlag("NOPBC",initDict, nopbc); + pbc = !nopbc; + + if (groupA.size() > 0) { + // parse the NL things only in the NL case + bool dopair; + pyParseFlag("PAIR",initDict, dopair); + // this is a WIP + + bool serial = false; + bool doneigh; + pyParseFlag("NLIST",initDict,doneigh); + double nl_cut = 0.0; + int nl_st = 0; + if (doneigh) { + //parse("NL_CUTOFF", nl_cut); + pyParse("NL_CUTOFF", initDict, nl_cut); + if (nl_cut <= 0.0) + error("NL_CUTOFF should be explicitly specified and positive"); + pyParse("NL_STRIDE",initDict, nl_st); + if (nl_st <= 0) + error("NL_STRIDE should be explicitly specified and positive"); + } + // endof WIP + if (groupB.size() > 0) { + if (doneigh) + nl = Tools::make_unique( + groupA, groupB, serial, dopair, pbc, getPbc(), comm, nl_cut, nl_st); + else + nl = Tools::make_unique(groupA, groupB, serial, dopair, + pbc, getPbc(), comm); + } else { + if (doneigh) + nl = Tools::make_unique(groupA, serial, pbc, getPbc(), + comm, nl_cut, nl_st); + else + nl = Tools::make_unique(groupA, serial, pbc, getPbc(), + comm); + } + requestAtoms(nl->getFullAtomList()); + } else { + requestAtoms(atoms); + } + + if (getNumberOfComponents()>1) { + log.printf(" it is expected to return dictionaries with %d components\n", + getNumberOfComponents()); + } + + log << " Bibliography " << plumed.cite(PYTHONCV_CITATION) << "\n"; + // NB: the NL kewywords will be counted as error when using ATOMS + checkRead(); +} catch (const py::error_already_set &e) { + plumed_merror(e.what()); + //vdbg(e.what()); +} + +void PythonCVInterface::prepare() try { + if (nl) { + if (nl->getStride() > 0) { + if (firsttime || (getStep() % nl->getStride() == 0)) { + requestAtoms(nl->getFullAtomList()); + invalidateList = true; + firsttime = false; + } else { + requestAtoms(nl->getReducedAtomList()); + invalidateList = false; + if (getExchangeStep()) + error("Neighbor lists should be updated on exchange steps - choose a " + "NL_STRIDE which divides the exchange stride!"); + } + if (getExchangeStep()) + firsttime = true; + } + } + if (hasPrepare) { + py::dict prepareDict = pyPrepare(this); + if (prepareDict.contains("setAtomRequest")) { + //should I use "interpretAtomList"? + std::vector myatoms; + if(py::isinstance(prepareDict["setAtomRequest"])|| + py::isinstance(prepareDict["setAtomRequest"])) { + py::tuple t = prepareDict["setAtomRequest"]; + + for (const auto &i : t) { + auto at = PLMD::AtomNumber::index(i.cast()); + myatoms.push_back(at); + } + } else { + auto atomlist=PLMD::Tools::getWords( + py::str(prepareDict["setAtomRequest"]).cast(), + "\t\n ,"); + interpretAtomList( atomlist, myatoms ); + } + requestAtoms(myatoms); + } + } +} catch (const py::error_already_set &e) { + plumed_merror(e.what()); +} + +void PythonCVInterface::update() try { + if(hasUpdate) { + py::dict updateDict=pyUpdate(this); + //See what to do here + } +} catch (const py::error_already_set &e) { + plumed_merror(e.what()); +} + +// calculator +void PythonCVInterface::calculate() try { + if (nl) { + if (nl->getStride() > 0 && invalidateList) { + nl->update(getPositions()); + } + } + // Call the function + py::object r = pyCalculate(this); + if(getNumberOfComponents()>1) { // MULTIPLE NAMED COMPONENTS + calculateMultiComponent(r); + } else { // SINGLE COMPONENT + readReturn(r, getPntrToValue()); + } + +} catch (const py::error_already_set &e) { + plumed_merror(e.what()); +} + +void PythonCVInterface::readReturn(const py::object &r, Value* valPtr) { + // Is there more than 1 return value? + if (py::isinstance(r)||py::isinstance(r)) { + // 1st return value: CV + py::list rl=r.cast(); + pycvComm_t value = rl[0].cast(); + valPtr->set(value); + auto natoms = getPositions().size(); + if (rl.size() > 1) { + if(!valPtr->hasDerivatives()) + error(valPtr->getName()+" was declared without derivatives, but python returned with derivatives"); + // 2nd return value: gradient: numpy array of (natoms, 3) + py::array_t grad(rl[1]); + // Assert correct gradient shape + if (grad.ndim() != 2 || grad.shape(0) != natoms || grad.shape(1) != 3) { + log.printf("Error: wrong shape for the gradient return argument: should be " + "(natoms=%d,3), received %ld x %ld\n", + natoms, grad.shape(0), grad.shape(1)); + error("Python CV returned wrong gradient shape error"); + } + // To optimize, see "direct access" + // https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html + for (unsigned i = 0; i < natoms; i++) { + Vector3d gi(grad.at(i, 0), grad.at(i, 1), grad.at(i, 2)); + setAtomsDerivatives(valPtr, i, gi); + } + } else if (valPtr->hasDerivatives()) + error(valPtr->getName()+" was declared with derivatives, but python returned none"); + + if (rl.size() > 2) { + if(!valPtr->hasDerivatives()) + plumed_merror(valPtr->getName()+" was declared without derivatives, but python returned with box derivatives"); + py::array_t pyBoxDev(rl[2]); + // expecting the box derivatives + Tensor boxDev; + if (pyBoxDev.ndim() == 2 && + (pyBoxDev.shape(0) == 3 && pyBoxDev.shape(1) == 3)) { // boxDev is 3x3 + boxDev = + Tensor({pyBoxDev.at(0, 0), pyBoxDev.at(0, 1), pyBoxDev.at(0, 2), + pyBoxDev.at(1, 0), pyBoxDev.at(1, 1), pyBoxDev.at(1, 2), + pyBoxDev.at(2, 0), pyBoxDev.at(2, 1), pyBoxDev.at(2, 2)}); + } else if (pyBoxDev.ndim() == 1 && pyBoxDev.shape(0) == 9) { + boxDev = Tensor({pyBoxDev.at(0), pyBoxDev.at(1), pyBoxDev.at(2), + pyBoxDev.at(3), pyBoxDev.at(4), pyBoxDev.at(5), + pyBoxDev.at(6), pyBoxDev.at(7), pyBoxDev.at(8)}); + } else { + log.printf( + "Error: wrong shape for the box derivatives return argument: " + "should be (size 3,3 or 9), received %ld x %ld\n", + natoms, pyBoxDev.shape(0), pyBoxDev.shape(1)); + error("Python CV returned wrong box derivatives shape error"); + } + setBoxDerivatives(valPtr, boxDev); + } else if (valPtr->hasDerivatives()) + warning(valPtr->getName()+" was declared with derivatives, but python returned no box derivatives"); + } else { + // Only value returned. Might be an error as well. + if (valPtr->hasDerivatives()) + warning(BIASING_DISABLED); + pycvComm_t value = r.cast(); + valPtr->set(value); + } + //TODO: is this ok? + if (!pbc) + setBoxDerivativesNoPbc(valPtr); +} + + +void PythonCVInterface::calculateMultiComponent(py::object &r) { + + const auto nc = getNumberOfComponents(); + if (py::isinstance(r)) { + py::dict dataDict = r.cast(); // values + for(int i=0; i < nc; ++i) { + auto component=getPntrToComponent(i); + //get the without "label.prefix-" + std::string key=component->getName().substr( + 2 + getLabel().size() + +PYCV_COMPONENTPREFIX.size()); + if (dataDict.contains(key.c_str())) + readReturn(dataDict[key.c_str()], component); + else + error( "python did not returned " + key ); + } + } else { + // In principle one could handle a "list" return case. + error("Multi-components pyCVs need to return dictionaries"); + } +} + +void PythonCVInterface::pyParseAtomList(const char* key, const ::pybind11::dict &initDict, std::vector &myatoms) { + parseAtomList(key,myatoms); + + if(initDict.contains(key)) { + if (myatoms.size()>0) + error(std::string("you specified the same keyword ").append(key)+ " both in python and in the settings file"); + auto atomlist=PLMD::Tools::getWords( + py::str(initDict[key]).cast(), + "\t\n ,"); + interpretAtomList( atomlist, myatoms ); + } +} + +NeighborList &PythonCVInterface::getNL() { return *nl; } +} // namespace pycvs +} // namespace PLMD diff --git a/plugins/pycv/PythonCVInterface.h b/plugins/pycv/PythonCVInterface.h new file mode 100644 index 0000000000..0a0bd8496f --- /dev/null +++ b/plugins/pycv/PythonCVInterface.h @@ -0,0 +1,67 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +Copyright (c) 2023 Daniele Rapetti + +The pycv module is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The pycv module 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with plumed. If not, see . ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#ifndef __PLUMED_pycv_PythonCVInterface_h +#define __PLUMED_pycv_PythonCVInterface_h +#include "ActionWithPython.h" + +#include "colvar/Colvar.h" + +namespace PLMD { + +class NeighborList; + +namespace pycv { + +///TODO: manual "you have to specify ATOMS=something for default atoms" +///TODO: add interface to pbc +///TODO: the topology can be assumed fixed and done on the go at each run by loading the pdb in the python code +class PythonCVInterface : public Colvar, public ActionWithPython { + static constexpr auto PYCV_NOTIMPLEMENTED="PYCV_NOTIMPLEMENTED"; + static constexpr auto PYCV_DEFAULTINIT="plumedInit"; + static constexpr auto PYCV_DEFAULTCALCULATE="plumedCalculate"; + + std::unique_ptr nl{nullptr}; + + ::pybind11::module_ pyModule {}; + ::pybind11::object pyCalculate{}; + ::pybind11::object pyPrepare; + ::pybind11::object pyUpdate; + + bool pbc=false; + bool hasPrepare = false; + bool hasUpdate = false; + bool invalidateList = true; + bool firsttime = true; + void calculateMultiComponent(pybind11::object &); + void readReturn(const pybind11::object &, Value* ); +public: + ::pybind11::dict dataContainer {}; + explicit PythonCVInterface(const ActionOptions&); + static void registerKeywords( Keywords& keys ); +// active methods: + void calculate() override; + void prepare() override; + void update() override; + + NeighborList& getNL(); + ///redefinition of parseAtomList to avoid confict between plumed.dat and python options + void pyParseAtomList(const char* key, const ::pybind11::dict &initDict, std::vector &); +}; + +} // namespace pycv +} // namespace PLMD +#endif __PLUMED_pycv_PythonCVInterface_h diff --git a/plugins/pycv/PythonFunction.cpp b/plugins/pycv/PythonFunction.cpp new file mode 100644 index 0000000000..cc9167b7f3 --- /dev/null +++ b/plugins/pycv/PythonFunction.cpp @@ -0,0 +1,280 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +Copyright (c) 2019 of Toni Giorgino + +The pycv module is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The pycv module 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with plumed. If not, see . ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#include "PythonFunction.h" + +#include "core/ActionRegister.h" +#include "core/PlumedMain.h" // cite + +#include // everything needed for embedding +#include + +#include +#include + + +using namespace std; +namespace py = pybind11; + + +namespace PLMD { +namespace pycv { + +//+PLUMEDOC FUNCTION PYTHONFUNCTION +/* +Define collective variables in the Python language. + +A Python module named in the `IMPORT` keyword is first imported. The +function passed as the `FUNCTION` keyword is called at each time +step. It is assumed to receive a numpy array of shape `(N,1)`, N being +the number of arguments passed at the `ARG` keyword. See also \ref +CUSTOM. + +The function should return two values: a scalar (the CV value), and +its gradient with respect to each coordinate (an array of the same +shape as the input). Not returning the gradient will prevent biasing +from working (with warnings). + +Automatic differentiation and transparent compilation (including to +GPU) can be performed via Google's [JAX +library](https://github.com/google/jax): see \ref PYTHONCV. + + +\par Examples + +The following example mimics the one in \ref CUSTOM. + +\plumedfile +dAB: DISTANCE ATOMS=10,12 +dAC: DISTANCE ATOMS=10,15 +diff: PYTHONFUNCTION ARG=dAB,dAC IMPORT=pythonfunction FUNCTION=diff PERIODIC=NO +METAD ARG=diff WIDTH=0.1 HEIGHT=0.5 BIASFACTOR=10 PACE=100 + +\endplumedfile + +The file `pythonfunction.py` should contain something as follows. + +@code{.py} +import jax.numpy as np + +def diff_f(X): + x, y = X + return y-x + + +# Just to demonstrate how auto grad is done. +# In this specific case it is just np.array([-1., 1.]) + +diff_grad = grad(diff_f) + + +# The function actually being called +def diff(x): + return diff_f(x), diff_grad(x) + +@endcode + +\par See also + +Use \ref PYTHONCV if you are dealing with atom coordinates directly. + +Please see \ref PYTHONCV for installation and automatic +differentiation. + +See \ref CUSTOM for a non-Python equivalent. + + +*/ +//+ENDPLUMEDOC + +PLUMED_REGISTER_ACTION(PythonFunction,"PYFUNCTION") + +void PythonFunction::registerKeywords( Keywords& keys ) { + Function::registerKeywords( keys ); + keys.use("ARG"); keys.use("PERIODIC"); + keys.add("compulsory","IMPORT","the python file to import, containing the function"); + keys.add("compulsory","CALCULATE",PYCV_DEFAULTCALCULATE,"the function to call"); + keys.add("compulsory","INIT",PYCV_DEFAULTINIT,"the function to call during the construction method of the function"); + keys.add("hidden","COMPONENTS","if provided, the function will return multiple components, with the names given"); + keys.addOutputComponent(PYCV_COMPONENTPREFIX.data(),"COMPONENTS","Each of the components output py the Python code, prefixed by py-"); + // Why is NOPBC not listed here? +} + +// Everything being copied from Custom.cpp +PythonFunction::PythonFunction(const ActionOptions&ao)try: + Action(ao), + Function(ao), + ActionWithPython(ao) { + + auto nargs = getNumberOfArguments(); + //Loading the python module + std::string import; + parse("IMPORT",import); + std::string calculateFunName; + //setting up the calculate function + parse("CALCULATE",calculateFunName); + log.printf(" will import %s and call function %s\n", import.c_str(), + calculateFunName.c_str()); + // Initialize the module and function pointers + pyModule = py::module::import(import.c_str()); + if (!py::hasattr(pyModule,calculateFunName.c_str())) { + error("the function " + calculateFunName + " is not present in "+ import); + } + + pyCalculate = pyModule.attr(calculateFunName.c_str()); + std::string initFunName; + parse("INIT",initFunName); + py::dict initDict; + if(py::hasattr(pyModule,initFunName.c_str())) { + log.printf(" will use %s during the initialization\n", initFunName.c_str()); + auto initFcn = pyModule.attr(initFunName.c_str()); + if (py::isinstance(initFcn)) { + initDict = initFcn; + } else { + initDict = initFcn(this); + } + } else if(initFunName!=PYCV_DEFAULTINIT) { + //If the default INIT is not preset, is not a problem + error("the function "+ initFunName + " is not present in "+ import); + } + { + std::vector components; + parseVector("COMPONENTS", components); + + if (components.size()>1) { + error("Please define multiple COMPONENTS from INIT in python."); + } + } + + if(initDict.contains("COMPONENTS")) { + if(initDict.contains("Value")) { + error("The initialize dict cannot contain both \"Value\" and \"COMPONENTS\""); + } + if(!py::isinstance(initDict["COMPONENTS"])) { + error("COMPONENTS must be a dictionary using with the name of the components as keys"); + } + py::dict components=initDict["COMPONENTS"]; + for(auto comp: components) { + auto settings = py::cast(comp.second); + if(components.size()==1) { //a single component + initializeValue(dynamic_cast<::PLMD::ActionWithValue&>(*this), settings); + valueSettings(settings,getPntrToValue()); + } else { + auto name=std::string(PYCV_COMPONENTPREFIX) + +"-"+py::cast(comp.first); + initializeComponent(dynamic_cast<::PLMD::ActionWithValue&>(*this), + name, + settings); + valueSettings(settings,getPntrToComponent(name)); + } + } + + } else if(initDict.contains("Value")) { + py::dict settingsDict=initDict["Value"]; + initializeValue(dynamic_cast<::PLMD::ActionWithValue&>(*this),settingsDict); + valueSettings(settingsDict,getPntrToValue()); + } else { + warning(" WARNING: by defaults components periodicity is not set and component is added without derivatives - see manual\n"); + //this will crash with an error, beacuse periodicity is not explicitly set + addValue(); + + } + + log.printf(" with function : %s\n",calculateFunName.c_str()); + + log<<" Bibliography " + <1) { // MULTIPLE NAMED COMPONENTS + calculateMultiComponent(r); + } else { // SINGLE COMPONENT + readReturn(r, getPntrToValue()); + } +} catch (const py::error_already_set &e) { + plumed_merror(e.what()); + //vdbg(e.what()); +} +void PythonFunction::readReturn(const py::object &r, Value* valPtr) { +// Is there more than 1 return value? + if (py::isinstance(r)||py::isinstance(r)) { + // 1st return value: CV + py::list rl=r.cast(); + pycvComm_t value = rl[0].cast(); + valPtr->set(value); + if (rl.size() > 1) { + auto nargs = getNumberOfArguments(); + if(!valPtr->hasDerivatives()) + error(valPtr->getName()+" was declared without derivatives, but python returned with derivatives"); + // 2nd return value: gradient: numpy array + py::array_t grad(rl[1]); + if(grad.ndim() != 1 || grad.shape(0) != nargs) { + log.printf("Error: wrong shape for the gradient return argument: should be (nargs=%lu), received %ld \n", + (unsigned long) nargs, grad.shape(0)); + error("PYFUNCTION returned wrong gradient shape error"); + } + + // To optimize, see "direct access" + // https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html + for(size_t i=0; isetDerivative(i,grad.at(i)); + } + } else if (valPtr->hasDerivatives()) + plumed_merror(valPtr->getName()+" was declared with derivatives, but python returned none"); + + } else { + // Only value returned. Might be an error as well. + log.printf(BIASING_DISABLED); + pycvComm_t value = r.cast(); + valPtr->set(value); + } +} + +void PythonFunction::calculateMultiComponent(py::object &r) { + + const auto nc = getNumberOfComponents(); + if (py::isinstance(r)) { + py::dict dataDict = r.cast(); // values + for(int i=0; i < nc; ++i) { + auto component=getPntrToComponent(i); + //get the without "label.prefix-" + std::string key=component->getName().substr( + 2 + getLabel().size() + +PYCV_COMPONENTPREFIX.size()); + if (dataDict.contains(key.c_str())) + readReturn(dataDict[key.c_str()], component); + else + error( "python did not returned " + key ); + } + } else { + // In principle one could handle a "list" return case. + error("Multi-components pyCVs need to return dictionaries"); + } +} + +}// namespace pycv +}// namespace PLMD + diff --git a/plugins/pycv/PythonFunction.h b/plugins/pycv/PythonFunction.h new file mode 100644 index 0000000000..9729c77425 --- /dev/null +++ b/plugins/pycv/PythonFunction.h @@ -0,0 +1,44 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +Copyright (c) 2023 Daniele Rapetti + +The pycv module is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The pycv module 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with plumed. If not, see . ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#ifndef __PLUMED_pycv_PythonFunction_h +#define __PLUMED_pycv_PythonFunction_h +#include "ActionWithPython.h" +#include "function/Function.h" +#include +namespace PLMD { + +class NeighborList; + +namespace pycv { +class PythonFunction : + public function::Function, + public ActionWithPython { + static constexpr auto PYCV_DEFAULTINIT="plumedInit"; + static constexpr auto PYCV_DEFAULTCALCULATE="plumedCalculate"; + ::pybind11::module_ pyModule {}; + ::pybind11::object pyCalculate{}; + void calculateMultiComponent(pybind11::object &); + void readReturn(const pybind11::object &, Value* ); +public: + explicit PythonFunction(const ActionOptions&); +// active methods: + virtual void calculate(); + static void registerKeywords( Keywords& keys ); +}; +} // namespace pycv +} // namespace PLMD +#endif __PLUMED_pycv_PythonFunction_h diff --git a/plugins/pycv/README.md b/plugins/pycv/README.md new file mode 100644 index 0000000000..16c2fbdfd8 --- /dev/null +++ b/plugins/pycv/README.md @@ -0,0 +1,221 @@ +The PYCV module for PLUMED 2 +==================================== + +The [PYCV module](https://giorginolab.github.io/plumed2-pycv) enables +PLUMED2 Collective Variables (CVs) and arbitrary functions to be +defined and auto-differentiated in the Python language. + +Advantages of using PYCV over standard development of CVs in C++ are: + 1. functions may be prototyped in high-level code, using + extensive mathematical libraries, and no boilerplate; + 2. just-in-time compilation + occurs transparently: there are no compilation and link delays + for code changes; + 3. CVs may be automatically differentiated in common cases. + +The code is organized as a standard PLUMED module: development occurs +in a [fork of the original +repository](https://github.com/giorginolab/plumed2-pycv/tree/v2.5.2-pycv/src/pycv). All +code is in the `src/pycv` and `regtest/pycv` directories. + +[![DOI](https://joss.theoj.org/papers/10.21105/joss.01773/status.svg)](https://doi.org/10.21105/joss.01773) +[![plumID:19.075](https://www.plumed-nest.org/eggs/19/075/badge.svg)](https://www.plumed-nest.org/eggs/19/075/) + + +Documentation +------------------------------------ + +The PYCV module defines the following actions: + + * `PYTHONCV`, to implement single- and multi-component CVs in Python; + * `PYTHONFUNCTION`, to implement arbitrary functions. + +In both cases, a Python interpreter is first started; the Python file +indicated in the `IMPORT=` keyword is then imported; from it, an +user-chosen function (`FUNCTION=`) is called to perform the +computations at each timestep. Modules can be shared for multiple +functions, and contain one-time initialization. + +Transparent auto-differentiation, JIT compilation, and vectorization +are available through Google's [JAX +library](https://github.com/google/jax) (recommended). + +Please see the generated documentation and regression tests. + + + + +Prerequisites +------------------------------------ + +Python 3 is required. Under OSX, [Homebrew](https://brew.sh)'s Python +3 package works well. The Conda version is problematic (slow +compilation, linking failures). + +You will also need to install `numpy`, either via `pip3 install +numpy` or your distribution's packages. + +Automatic differentiation examples require the JAX library: install +it with `pip3 install jax jaxlib`. + + + +Installation +------------------------------------ + +Follow the usual PLUMED 2 configuration procedure: + +```bash +./configure --enable-modules=+pycv +make -j4 +make install # optional +``` + +Please inspect the configure messages to see if any missing dependency +prevents PYCV from actually being enabled. Verify the successful installation +with e.g. + + ./src/lib/plumed-static manual --action PYTHONCV + +which should return the manual for `PYTHONCV`. (The `plumed-static` +executable should work even without installation.) + +(It is also possible to compile the module as a `LOAD`-able dynamic +object. Once in the `src/pycv` directory, issue `make PYCV.so` +(`PYCV.dylib` under OSX). The compilation step *should* pick +Python-specific flags as long as the correct `python3-config` +executable is in your path.) + + +Quickstart +------------------------------------ + +Here's a quick example to whet your appetite, following the regression test `rt-jax2`. + + export PLUMED_PROGRAM_NAME=$PWD/src/lib/plumed-static + make -C regtest/pycv/rt-jax2 + +The files in `regtest/pycv/rt-jax2` are reproduced here: + +**File plumed.dat** + +``` +cv1: PYTHONCV ATOMS=1,4,3 IMPORT=jaxcv FUNCTION=cv1 +``` + +**File jaxcv.py** + +```py +# Import the JAX library +import jax.numpy as np +from jax import grad, jit, vmap + +# Implementation of the angle function. @jit really improves speed +@jit +def angle(x): + r1 = x[0,:]-x[1,:] + r2 = x[2,:]-x[1,:] + + costheta = np.dot(r1,r2) / np.sqrt(np.dot(r1,r1) * np.dot(r2,r2)) + # OR: costheta = np.dot(r1,r2) / np.linalg.norm(r1) / np.linalg.norm(r2) + theta = np.arccos(costheta) + return theta + +# Use JAX to auto-gradient it +grad_angle = grad(angle) + +# The CV function actually called +def cv1(x): + return angle(x), grad_angle(x) + +``` + + + + + +Demos and regression tests +------------------------------------ + +A number of PLUMED-style regression tests are provided to test and +demonstrate the package features. + + +Name | Feature +-------|------------ +`rt-1` | Basic test (distance of two atoms), pure Python, no gradient +`rt-2` | CV and gradient explicitly coded, pure Python +`rt-3` | Multiple PYTHON actions in the same file +`rt-f1`| Function, pure Python +`rt-f2`| Function, with JIT and auto-gradient, with MATHEVAL equivalent +`rt-jax1` | Distance CV with JAX reverse-mode differentiation +`rt-jax2` | Angle CV with JAX reverse-mode differentiation and JIT decorator +`rt-jax3` | Radius of curvature, as in [doi:10.1016/j.cpc.2018.02.017](http://doi.org/10.1016/j.cpc.2018.02.017) +`rt-multi1` | Multi-component CV, pure Python +`rt-multi2` | Multi-component CV, with JAX reverse-mode differentiation + + +To run: + +```bash +cd [path_to_repository] +export PLUMED_PROGRAM_NAME=$PWD/src/lib/plumed +cd regtest/pycv +make +``` + + + + + +Common errors +------------------------------------ + +* Missing modules at run time: you may need to set the `PYTHONHOME` + environment variable. + +* `uncaught exception [...] The interpreter is already running` This + occurs if the module is loaded twice. The most common cause is when + it is both built-in in the current kernel, and loaded as a module. + + + +Limitations +------------------------------------ + +Behavior with MPI, and JAX's GPU/TPU offloading are untested. + + + +Author +------------------------------------ + +Toni Giorgino + + +Contributing +------------------------------------ + +Please report bugs and ideas via this repository's *Issues*. + + +Citation +------------------------------------ + +Giorgino T. PYCV: a PLUMED 2 Module Enabling the Rapid Prototyping of +Collective Variables in Python. The Journal of Open Source Software +4(42):1773 + +[![DOI](https://joss.theoj.org/papers/10.21105/joss.01773/status.svg)](https://doi.org/10.21105/joss.01773) +[![plumID:19.075](https://www.plumed-nest.org/eggs/19/075/badge.svg)](https://www.plumed-nest.org/eggs/19/075/) + + +Copyright +------------------------------------ + +PYCV is distributed under the LGPL terms: see COPYRIGHT. + +PYCV includes the [pybind11](https://github.com/pybind/pybind11) +library, distributed under its own license terms (BSD-3). + + diff --git a/plugins/pycv/Readme.md b/plugins/pycv/Readme.md new file mode 100644 index 0000000000..f71dfafc76 --- /dev/null +++ b/plugins/pycv/Readme.md @@ -0,0 +1,40 @@ +## Preparation +```bash +python3 -m venv pycvenv +source ./pycvenv/bin/activate +pip install -U pip +pip install -r requirements +``` +jax is not required for installation, but for some test +### Install jax: + +Go to the original guide in the [jax documenation](https://jax.readthedocs.io/en/latest/installation.html) + +The command shoudl be similar to: + - example if you have a cuda12 compatible device (a wheel for cuda will be installed alognside jax): +`pip install "jax[cuda12_pip]" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html` + - example if you have a cuda12 compatible device, and cuda already installed on your system: +`pip install "jax[cuda12_local]" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html` + +## Standard compilation + +If you have a plumed that supports plumed mklib with multiple files +```bash +./standaloneCompile.sh +``` + +## Develop + +This version is slightly more complex, but if are often modifying the cpp files compiles only the modified files. (You'll need to call `./prepareMakeForDevelop.sh` only the first time) +```bash +./prepareMakeForDevelop.sh +make +``` + +## Set up tests + +Create a symbolic link to the `scripts` directory in a plumed source and you can execute the tests. Plumed must be runnable to execute tests +```bash +ln -s path/to/plumed/source/regtest/scripts . +make +``` \ No newline at end of file diff --git a/plugins/pycv/joss-paper/Makefile b/plugins/pycv/joss-paper/Makefile new file mode 100644 index 0000000000..99feae4aff --- /dev/null +++ b/plugins/pycv/joss-paper/Makefile @@ -0,0 +1,2 @@ +paper.pdf: paper.md paper.bib + ./compile $< diff --git a/plugins/pycv/joss-paper/compile b/plugins/pycv/joss-paper/compile new file mode 100755 index 0000000000..0a7e37dafd --- /dev/null +++ b/plugins/pycv/joss-paper/compile @@ -0,0 +1,181 @@ +#!/usr/bin/env bash + +# Help text + +description="A bash script for compiling JOSS and JOSE papers locally"; + +doc="compile [-j ] [-d ] [-v ] [-i ] [-p ] + [-s ] [-u ] [-r ] [-g ] [-a ] FILE + +Compile a markdown FILE to a JOSS or JOSE paper. + +Options: + -j JOURNAL The journal the paper is being compiled for, either joss or jose, defaults to joss. + -d DOI The DOI of the submitted paper, defaults to an empty string. + -v VOLUME The journal volume the paper was published in, defaults to an empty string. + -i ISSUE The journal issue the paper was published in, defaults to an empty string. + -p PAGE The journal issue page the paper was published in, defaults to an empty string. + -s SUBMITTED The date the paper was submitted, defaults to an empty string. + -u PUBLISHED The date the paper was published, defaults to an empty string. + -r REVIEW The review issue URL, defaults to an empty string. + -g REPOSITORY The URL of the code repository, defaults to an empty string. + -a ARCHIVE The DOI of the code archive, defaults to an empty string. + -h Displays this help text and exit." + +# Default arguments + +journal="joss" +doi="" +volume="" +issue="" +page="" +submitted="" +published="" +review="" +repository="" + +# Get command line arguments + +while getopts ":j:d:v:i:p:s:u:r:g:a:h" opt; do + case $opt in + j) + journal=$OPTARG;; + d) + doi=$OPTARG;; + v) + volume=$OPTARG;; + i) + issue=$OPTARG;; + p) + page=$OPTARG;; + s) + submitted=$OPTARG;; + u) + published=$OPTARG;; + r) + review=$OPTARG;; + g) + repository=$OPTARG;; + a) + archive=$OPTARG;; + h) + echo $description; + echo; + echo "$doc"; + exit 0;; + \?) + echo "Invalid option: -$OPTARG."; + echo; + echo "$doc"; + exit 1;; + :) + echo "Option -$OPTARG requires an argument."; + echo; + echo "$doc"; + exit 1;; + esac; +done; + +paper=${@:$OPTIND:1} + +if [ ! "$paper" ]; then + echo "FILE not specified." + echo; + echo "$doc" + exit 1; +fi; + +# Check if pandoc and pandoc-citeproc are installed + +missing="" + +if [ ! "$(which pandoc)" ]; then + missing="$missing pandoc"; +fi; + +if [ ! "$(which pandoc-citeproc)" ]; then + missing="$missing pandoc-citeproc"; +fi; + +if [ ! "$(which xelatex)" ]; then + missing="$missing xelatex"; +fi; + +if [ "$missing" ]; then + if [ $(echo "$missing" | wc -w) -eq 2 ]; then + echo "Please install $(echo $missing | sed 's/ / and /')."; + else + echo "Please install $(echo $missing | sed 's/ /, /g' | sed 's/\(.*\) /\1 and /')."; + fi; + exit 1; +fi; + +# Set journal specific variables + +outfile="${paper%.md}.pdf" +logo="logo.png" +templateurl="https://raw.githubusercontent.com/openjournals/whedon/master/resources/latex.template" +cslurl="https://raw.githubusercontent.com/openjournals/whedon/master/resources/apa.csl" +logourl="https://github.com/openjournals/whedon/raw/master/resources/joss/logo.png" + +# Set journal full name + +if [ $journal == "joss" ]; then + journalname="Journal of Open Source Software"; +else + journalname="Journal of Open Source Education"; +fi; + +# Download logo and latex template files + +if [ ! -f latex.template ]; then + wget "$templateurl"; +fi; + +if [ ! -f apa.csl ]; then + wget "$cslurl"; +fi; + +if [ ! -f $logo ]; then + wget "$logourl"; +fi; + +# Collect necessary paper metadata + +year=$(grep "date: " $paper | cut -d " " -f 4 -) +bib=$(grep "bibliography: " $paper | cut -d " " -f 2 -) + +title=$( \ + grep "title: " $paper | \ + cut -d " " -f 2- - | \ + sed 's/^"\(.*\)"$/\1/' | \ + sed "s/^'\(.*\)'$/\1/") + +name=$(grep -m 1 "name: " $paper | rev | cut -d " " -f 1 - | rev) + +# Compile the paper + +pandoc \ + --filter pandoc-citeproc \ + --bibliography=$bib \ + --template=latex.template \ + -V logo_path=$logo \ + -V citation_author=$name \ + -V year=$year \ + -V footnote_paper_title="$title" \ + -V journal_name="$journalname" \ + -V formatted_doi="$doi" \ + -V archive_doi="https://doi.org/$archive" \ + -V review_issue_url=$review \ + -V repository=$repository \ + -V submitted=$submitted \ + -V published=$published \ + -V issue=$issue \ + -V volume=$volume \ + -V page=$page \ + -V graphics=true \ + --pdf-engine=xelatex \ + --from markdown+autolink_bare_uris \ + --csl=apa.csl \ + -s $paper \ + -o $outfile diff --git a/plugins/pycv/joss-paper/paper.bib b/plugins/pycv/joss-paper/paper.bib new file mode 100644 index 0000000000..422e9de0cd --- /dev/null +++ b/plugins/pycv/joss-paper/paper.bib @@ -0,0 +1,248 @@ + +@article{tribello_plumed_2014, + title = {{PLUMED} 2: {New} feathers for an old bird}, + volume = {185}, + issn = {0010-4655}, + shorttitle = {{PLUMED} 2}, + url = {http://www.sciencedirect.com/science/article/pii/S0010465513003196}, + doi = {10.1016/j.cpc.2013.09.018}, + abstract = {Abstract +Enhancing sampling and analyzing simulations are central issues in molecular simulation. Recently, we introduced PLUMED, an open-source plug-in that provides some of the most popular molecular dynamics (MD) codes with implementations of a variety of different enhanced sampling algorithms and collective variables (CVs). The rapid changes in this field, in particular new directions in enhanced sampling and dimensionality reduction together with new hardware, require a code that is more flexible and more efficient. We therefore present PLUMED 2 here—a complete rewrite of the code in an object-oriented programming language (C++). This new version introduces greater flexibility and greater modularity, which both extends its core capabilities and makes it far easier to add new methods and CVs. It also has a simpler interface with the MD engines and provides a single software library containing both tools and core facilities. Ultimately, the new code better serves the ever-growing community of users and contributors in coping with the new challenges arising in the field. +Program summary +Program title: PLUMED 2 + +Catalogue identifier: AEEE\_v2\_0 + +Program summary URL:http://cpc.cs.qub.ac.uk/summaries/AEEE\_v2\_0.html + +Program obtainable from: CPC Program Library, Queen’s University, Belfast, N. Ireland + +Licensing provisions: Yes + +No. of lines in distributed program, including test data, etc.: 700646 + +No. of bytes in distributed program, including test data, etc.: 6618136 + +Distribution format: tar.gz + +Programming language: ANSI-C++. + +Computer: Any computer capable of running an executable produced by a C++ compiler. + +Operating system: Linux operating system, Unix OSs. + +Has the code been vectorized or parallelized?: Yes, parallelized using MPI. + +RAM: Depends on the number of atoms, the method chosen and the collective variables used. + +Classification: 3, 7.7, 23. + +Catalogue identifier of previous version: AEEE\_v1\_0. + +Journal reference of previous version: Comput. Phys. Comm. 180 (2009) 1961. + +External routines: GNU libmatheval, Lapack, Blas, MPI. + +Does the new version supersede the previous version?: This version supersedes the previous version for the most part. There are a small number of very specific situations where the previous version is better, due to performance or to non-ported features. We are actively working on porting these last few features into the new code. + +Nature of problem: + +Calculation of free-energy surfaces for molecular systems of interest in biology, chemistry and materials science, on the fly and a posteriori analysis of molecular dynamics trajectories using advanced collective variables. + +Solution method: + +Implementations of various collective variables and enhanced sampling techniques. + +Reasons for new version: + +The old version was difficult to maintain and its design was not as flexible as this new version. This lack of flexibility made it difficult to implement a number of novel methods that have emerged since the release of the original code. + +Summary of revisions: + +The new version of the code has a completely redesigned architecture, which allows for several important enhancements. This allows for a much simpler and robust input syntax and for improved performance. In addition, it provides several, more-complex collective variables which could not have been written using the previous implementation. Furthermore, the entire code is fully documented so it is easier to extend. Finally, the code is designed so that users can implement new variables directly in the input files and thus develop bespoke applications of these powerful algorithms. + +Unusual features: + +PLUMED 2 can be used either as a standalone program, e.g. for a posteriori analysis of trajectories, or as a library embedded in a molecular dynamics code (such as GROMACS, NAMD, Quantum ESPRESSO, and LAMMPS). Interfaces with these particular codes are provided in patches, which a simple script will insert into the underlying molecular dynamics codes source code files. For other molecular dynamics codes there is extensive documentation on how to add PLUMED in our manual. + +Additional comments: + +The distribution file contains a test suite, user and developer documentation and a collection of patches and utilities. + +Running time: + +Depends on the number of atoms, the method chosen and the collective variables used. The regression test suite provided takes approximately 1 min to run.}, + number = {2}, + urldate = {2013-12-06}, + journal = {Computer Physics Communications}, + author = {Tribello, Gareth A. and Bonomi, Massimiliano and Branduardi, Davide and Camilloni, Carlo and Bussi, Giovanni}, + month = feb, + year = {2014}, + keywords = {Dimensional reduction, Enhanced sampling, Molecular dynamics, free energy, molecular dynamics, Free energy}, + pages = {604--613}, + file = {PLUMED 2\: New feathers for an old bird:/Users/toni/Zotero/storage/8FGEJSMP/S0010465513003196.html:text/html;Tribello et al_2014_PLUMED 2.pdf:/Users/toni/Sync/literature/zotero/MD/Metadynamics/Tribello et al_2014_PLUMED 2.pdf:application/pdf} +} + + +@article{the_plumed_consortium_promoting_2019, + title = {Promoting transparency and reproducibility in enhanced molecular simulations}, + volume = {16}, + copyright = {2019 Springer Nature America, Inc.}, + issn = {1548-7105}, + url = {https://www.nature.com/articles/s41592-019-0506-8}, + doi = {10.1038/s41592-019-0506-8}, + abstract = {The PLUMED consortium unifies developers and contributors to PLUMED, an open-source library for enhanced-sampling, free-energy calculations and the analysis of molecular dynamics simulations. Here, we outline our efforts to promote transparency and reproducibility by disseminating protocols for enhanced-sampling molecular simulations.}, + language = {En}, + number = {8}, + urldate = {2019-07-31}, + journal = {Nature Methods}, + author = {{The PLUMED consortium}}, + month = aug, + year = {2019}, + pages = {670}, + file = {Snapshot:/Users/toni/Zotero/storage/CMXUMYDD/s41592-019-0506-8.html:text/html} +} + + +@article{giorgino_how_2018, + title = {How to differentiate collective variables in free energy codes: {Computer}-algebra code generation and automatic differentiation}, + volume = {228}, + issn = {0010-4655}, + shorttitle = {How to differentiate collective variables in free energy codes}, + url = {http://www.sciencedirect.com/science/article/pii/S0010465518300468}, + doi = {10.1016/j.cpc.2018.02.017}, + abstract = {The proper choice of collective variables (CVs) is central to biased-sampling free energy reconstruction methods in molecular dynamics simulations. The PLUMED 2 library, for instance, provides several sophisticated CV choices, implemented in a C++ framework; however, developing new CVs is still time consuming due to the need to provide code for the analytical derivatives of all functions with respect to atomic coordinates. We present two solutions to this problem, namely (a) symbolic differentiation and code generation, and (b) automatic code differentiation, in both cases leveraging open-source libraries (SymPy and Stan Math, respectively). The two approaches are demonstrated and discussed in detail implementing a realistic example CV, the local radius of curvature of a polymer. Users may use the code as a template to streamline the implementation of their own CVs using high-level constructs and automatic gradient computation. +Program summary +Program Title: Practical approaches to the differentiation of collective variables in free energy codes: computer-algebra code generation and automatic differentiation Program Files doi:http://dx.doi.org/10.17632/r4r67bvkdn.1 Licensing provisions: GNU Lesser General Public License Version 3 (LGPL-3) Programming languages: C++, Python Nature of problem: The C++ implementation of collective variables (CVs, functions of atomic coordinates to be used in biased sampling applications) in biasing libraries for atomistic simulations, such as PLUMED [1], requires computation of both the variable and its gradient with respect to the atomic coordinates; coding and testing the analytical derivatives complicate the implementation of new CVs. Solution method: The paper shows two approaches to automate the computation of CV gradients, namely, symbolic differentiation with code generation and automatic code differentiation, demonstrating their implementation entirely with open-source software (respectively, SymPy and the Stan Math Library). Additional comments: The paper’s accompanying code serves as an example and template for the methods described in the paper; it is distributed as the two modules curvature\_codegen and curvature\_autodiff integrated in PLUMED 2’s source tree; the latest version is available at https://github.com/tonigi/plumed2-automatic-gradients . [1] Tribello GA, Bonomi M, Branduardi D, Camilloni C, Bussi G. PLUMED 2: New feathers for an old bird. Computer Physics Communications. 2014 Feb;185(2):604-13.}, + urldate = {2018-04-27}, + journal = {Computer Physics Communications}, + author = {Giorgino, Toni}, + month = jul, + year = {2018}, + note = {2-s2.0-85043302064}, + keywords = {C++, Molecular dynamics, Metadynamics, Free energy, Biased sampling, Symbolic}, + pages = {258--263}, + file = {ScienceDirect Snapshot:/Users/toni/Zotero/storage/43EN4QZQ/S0010465518300468.html:text/html} +} + + +@article{roux_calculation_1995, + title = {The calculation of the potential of mean force using computer simulations}, + volume = {91}, + url = {Roux1995.pdf}, + doi = {10.1016/0010-4655(95)00053-I}, + abstract = {The problem of unbiasing and combining the results of umbrella sampling calculations is reviewed. The weighted histogram analysis method (WHAM) of S. Kumar et al. (J. Comp. Chem. 13 (1992) 1011) is described and compared with other approaches. The method is illustrated with molecular dynamics simulations of the alanine dipeptide for one-and two-dimensional free energy surfaces. The results show that the WHAM approach simplifies considerably the task of recombining the various windows in complex systems.}, + number = {1-3}, + journal = {Computer Physics Communications}, + author = {Roux, Benoît}, + year = {1995}, + keywords = {Computer simulations}, + pages = {275--282}, + annote = {Added to JabRef: 2009.01.09}, + annote = {Owner: toni} +} + + +@article{laio_escaping_2002, + title = {Escaping free-energy minima}, + volume = {99}, + issn = {0027-8424}, + url = {http://www.ncbi.nlm.nih.gov/pubmed/12271136}, + doi = {10.1073/pnas.202427399}, + abstract = {We introduce a powerful method for exploring the properties of the multidimensional free energy surfaces (FESs) of complex many-body systems by means of coarse-grained non-Markovian dynamics in the space defined by a few collective coordinates. A characteristic feature of these dynamics is the presence of a history-dependent potential term that, in time, fills the minima in the FES, allowing the efficient exploration and accurate determination of the FES as a function of the collective coordinates. We demonstrate the usefulness of this approach in the case of the dissociation of a NaCl molecule in water and in the study of the conformational changes of a dialanine in solution.}, + number = {20}, + urldate = {2009-09-26}, + journal = {Proceedings of the National Academy of Sciences of the United States of America}, + author = {Laio, Alessandro and Parrinello, Michele}, + month = oct, + year = {2002}, + pmid = {12271136}, + keywords = {metadynamics, Sodium Chloride, Overcoming, Chemistry, Normal Distribution, Models, Statistical, Thermodynamics, Alanine, Water}, + pages = {12562--12566} +} + + +@article{barducci_metadynamics_2011, + title = {Metadynamics}, + volume = {1}, + issn = {1759-0884}, + url = {http://onlinelibrary.wiley.com/doi/10.1002/wcms.31/abstract}, + doi = {10.1002/wcms.31}, + abstract = {Metadynamics is a powerful technique for enhancing sampling in molecular dynamics simulations and reconstructing the free-energy surface as a function of few selected degrees of freedom, often referred to as collective variables (CVs). In metadynamics, sampling is accelerated by a history-dependent bias potential, which is adaptively constructed in the space of the CVs. Since its first appearance, significant improvements have been made to the original algorithm, leading to an efficient, flexible, and accurate method that has found many successful applications in several domains of science. Here, we discuss first the theory underlying metadynamics and its recent developments. In particular, we focus on the crucial issue of choosing an appropriate set of CVs and on the possible strategies to alleviate this difficulty. Later in the second part, we present a few recent representative applications, which we have classified into three main classes: solid-state physics, chemical reactions, and biomolecules. © 2011 John Wiley \& Sons, Ltd. WIREs Comput Mol Sci 2011 1 826-843 DOI: 10.1002/wcms.31}, + language = {en}, + number = {5}, + urldate = {2017-09-04}, + journal = {Wiley Interdisciplinary Reviews: Computational Molecular Science}, + author = {Barducci, Alessandro and Bonomi, Massimiliano and Parrinello, Michele}, + month = sep, + year = {2011}, + pages = {826--843}, + file = {Snapshot:/Users/toni/Zotero/storage/8JATVU52/abstract.html:text/html} +} + + +@article{branduardi_b_2007, + title = {From {A} to {B} in free energy space}, + volume = {126}, + issn = {0021-9606}, + doi = {10.1063/1.2432340}, + abstract = {The authors present a new method for searching low free energy paths in complex molecular systems at finite temperature. They introduce two variables that are able to describe the position of a point in configurational space relative to a preassigned path. With the help of these two variables the authors combine features of approaches such as metadynamics or umbrella sampling with those of path based methods. This allows global searches in the space of paths to be performed and a new variational principle for the determination of low free energy paths to be established. Contrary to metadynamics or umbrella sampling the path can be described by an arbitrary large number of variables, still the energy profile along the path can be calculated. The authors exemplify the method numerically by studying the conformational changes of alanine dipeptide.}, + language = {eng}, + number = {5}, + journal = {The Journal of Chemical Physics}, + author = {Branduardi, Davide and Gervasio, Francesco Luigi and Parrinello, Michele}, + month = feb, + year = {2007}, + pmid = {17302470}, + keywords = {Dipeptides, Energy Transfer, Computer Simulation, Molecular Conformation, Algorithms, Thermodynamics}, + pages = {054103} +} + + +@article{bonomi_integrative_2017, + title = {Integrative structural and dynamical biology with {PLUMED}-{ISDB}}, + volume = {33}, + issn = {1367-4803}, + url = {https://academic.oup.com/bioinformatics/article/33/24/3999/4085774}, + doi = {10.1093/bioinformatics/btx529}, + abstract = {SummaryAccurate structural models of biological systems can be obtained by properly combining experimental data with a priori physico–chemical knowledge. Here we present PLUMED-ISDB, an open-source, freely-available module of the popular PLUMED library, which enables the simultaneous determination of structure and dynamics of conformationally heterogeneous systems by integrating experimental data with a priori information. This integration is achieved using metainference, a general Bayesian framework that accounts for both noise in the data and their ensemble-averaged nature. PLUMED-ISDB implements different types of experimental data, such as several NMR observables, FRET, SAXS and cryo-electron microscopy data, and enables modelling structure and dynamics of individual proteins, protein complexes, membrane proteins, RNA and DNA, using a variety of enhanced sampling methods and resolutions of the system.Availability and implementationPLUMED-ISDB is freely available at www.plumed.org.Contactmb2006@cam.ac.uk or carlo.camilloni@unimi.itSupplementary informationSupplementary data are available at Bioinformatics online.}, + language = {en}, + number = {24}, + urldate = {2018-02-04}, + journal = {Bioinformatics}, + author = {Bonomi, Massimiliano and Camilloni, Carlo}, + month = dec, + year = {2017}, + keywords = {Software, Molecular Conformation, Models, Molecular, Bayes Theorem}, + pages = {3999--4000}, + file = {Bonomi_Camilloni_2017_Integrative structural and dynamical biology with PLUMED-ISDB.pdf:/Users/toni/Sync/literature/zotero/MD/Metainference/Bonomi_Camilloni_2017_Integrative structural and dynamical biology with PLUMED-ISDB.pdf:application/pdf;Snapshot:/Users/toni/Zotero/storage/VVLH4JVX/4085774.html:text/html} +} + + +@article{pipolo_navigating_2017, + title = {Navigating at {Will} on the {Water} {Phase} {Diagram}}, + volume = {119}, + url = {https://link.aps.org/doi/10.1103/PhysRevLett.119.245701}, + doi = {10.1103/PhysRevLett.119.245701}, + abstract = {Despite the simplicity of its molecular unit, water is a challenging system because of its uniquely rich polymorphism and predicted but yet unconfirmed features. Introducing a novel space of generalized coordinates that capture changes in the topology of the interatomic network, we are able to systematically track transitions among liquid, amorphous, and crystalline forms throughout the whole phase diagram of water, including the nucleation of crystals above and below the melting point. Our approach, based on molecular dynamics and enhanced sampling or free energy calculation techniques, is not specific to water and could be applied to very different structural phase transitions, paving the way towards the prediction of kinetic routes connecting polymorphic structures in a range of materials.}, + number = {24}, + urldate = {2019-08-14}, + journal = {Physical Review Letters}, + author = {Pipolo, S. and Salanne, M. and Ferlat, G. and Klotz, S. and Saitta, A. M. and Pietrucci, F.}, + month = dec, + year = {2017}, + pages = {245701} +} + + +@article{torrie_nonphysical_1977, + title = {Nonphysical sampling distributions in {Monte} {Carlo} free-energy estimation: {Umbrella} sampling}, + volume = {23}, + doi = {10.1016/0021-9991(77)90121-8}, + abstract = {The free energy difference between a model system and some reference system can easily be written as an ensemble average, but the conventional Monte Carlo methods of obtaining such averages are inadequate for the free-energy case. That is because the Boltzmann-weighted sampling distribution ordinarily used is extremely inefficient for the purpose. This paper describes the use of arbitrary sampling distributions chosen to facilitate such estimates. The methods have been tested successfully on the Lennard-Jones system over a wide range of temperature and density, including the gas-liquid coexistence region, and are found to be extremely powerful and economical.}, + number = {2}, + journal = {Journal of Computational Physics}, + author = {Torrie, G. M. and Valleau, J. P.}, + year = {1977}, + pages = {187--199} +} \ No newline at end of file diff --git a/plugins/pycv/joss-paper/paper.md b/plugins/pycv/joss-paper/paper.md new file mode 100644 index 0000000000..b6bd677563 --- /dev/null +++ b/plugins/pycv/joss-paper/paper.md @@ -0,0 +1,178 @@ +--- +title: 'PYCV: a PLUMED 2 Module Enabling the Rapid Prototyping of Collective Variables in Python' +tags: + - Python + - PLUMED + - molecular dynamics + - collective variables + - biased sampling + - gradient +authors: + - name: Toni Giorgino + orcid: 0000-0001-6449-0596 + affiliation: 1 +affiliations: + - name: Institute of Biophysics (IBF-CNR), National Research Council of Italy + index: 1 +date: 15 August 2019 +bibliography: paper.bib +--- + +# Summary + +Collective variables (CVs) are functions of the coordinates of +particles in a molecular system. The choice of CV is +crucial to capture relevant degrees of freedom of the model being +simulated [@barducci_metadynamics_2011]. This is especially important +when employing *biased sampling* techniques such as umbrella sampling +or metadynamics [@torrie_nonphysical_1977; @laio_escaping_2002], which +apply generalized forces to CVs to enhance the sampling of events +otherwise not observable by direct simulation. CVs may be +simple geometrical observables (distances, angles, torsions, etc.), +but often they are more complex functions designed to capture structural +determinants, such as tertiary and quaternary structure of proteins, +experimental observables, crystal symmetries, etc. [@branduardi_b_2007; +@bonomi_integrative_2017; @pipolo_navigating_2017]. + +Iterative development of CVs therefore accounts for most of the +efforts associated with the exploration of molecular systems, and +software packages implementing high-level directives to express +biasing potentials and CVs markedly simplify the task. +In this regard, the PLUMED library [@tribello_plumed_2014] is +especially relevant because it provides numerous pre-defined +functions, a *lingua franca* to express CV combinations, atom groups +and biasing schemes, and an active community +[@the_plumed_consortium_promoting_2019]. Users willing to explore CVs +beyond the pre-defined ones have to implement them in C++, together +with the corresponding (often cumbersome) derivatives +[@giorgino_how_2018]. Compiled code is however unwieldy for iterative +analysis, because it is relatively low-level, error-prone, and +inconvenient in exploratory stages. + +This paper introduces **PYCV**, a module for the PLUMED 2 library +which enables users to define CVs and arbitrary functions in the +Python language. CV implementations may thus be modified and tested +independently of the main code, with essentially no "test latency". +Of note, coordinates are processed as `numpy` arrays, making it +convenient to leverage the vast set of linear algebra and numerical +algorithms provided by `numpy`, `scipy`, and many other open-source +modules. Furthermore, just-in-time compilation and reverse-mode +automatic differentiation are easily accessible using Google's JAX +library. + + +# Usage + +The **PYCV** module registers itself with PLUMED to provide the +following actions: + + * `PYTHONCV`, to implement single- and multi-component CVs; + * `PYTHONFUNCTION`, to implement arbitrary functions. + +The actions are documented in the respective inline manuals (e.g., +`plumed manual --action PYTHONCV`). In both cases, an interpreter is +first started; the Python module indicated in the `IMPORT=` keyword is +then loaded; from it, an user-chosen function (`FUNCTION=`) is called +to perform the computations at each timestep. Modules can contain +multiple functions and one-time initialization. + + + +# Example + +A self-explanatory example is provided for illustration below. It is +equivalent to the *radius of curvature* example shown in +[@giorgino_how_2018]. Further examples are available in the manual and +in `regtest/pycv`. + + +## Plumed input script + +The actions are declared in the PLUMED input file (say, +`plumed.dat`). Here, one declares a CV labelled `rc`, to be computed by +the Python function `curvature.r()`. It will receive a 3-by-3 array +with the coordinates of atoms 1, 4 and 3 (orderly, as rows). The CV +value will be `PRINT`ed, and the atoms subject to a constant generalized +force pushing to increase the curvature. + +``` +# Start plumed.dat ----------------------------------------- +rc: PYTHONCV ATOMS=1,4,3 IMPORT=curvature FUNCTION=r + PRINT ARG=rc FILE=colvar.out + RESTRAINT ARG=rc AT=0 KAPPA=0 SLOPE=1 +# End plumed.dat ------------------------------------------- +``` + + +## Function definition + +The actual function `r` is defined in the `curvature.py` file. It +computes the radius of the circle passing through three given atom +coordinates (the three rows of the input argument, with 0-based +indexing). Note how matrix operations enable a readable translation of +the sine formula. + +The function is expected to return two objects, i.e. the value of the +CV at the given coordinates (a scalar), and its gradient with respect +to each of the 9 coordinates (a 3-by-3 array); here the gradient +function is obtained automatically. Although not directly comparable, +the equivalent C++ implementation required approximately 160 lines of +non-trivial code. + + +```py +# Start curvature.py ---------------------------------------- +# Same calculation (and results) as doi:10.1016/j.cpc.2018.02.017 + +# Import the JAX library +import jax.numpy as np +from jax import grad, jit + +# Implementation of the angle function. @jit really improves speed +@jit +def r_f(x): + r21 = x[0,:]-x[1,:] + r23 = x[2,:]-x[1,:] + r13 = x[2,:]-x[0,:] + + cos2theta = np.dot(r21,r23)**2 / (np.dot(r21,r21) * np.dot(r23,r23)) + sin2theta = 1-cos2theta + + R2= np.dot(r13,r13)/sin2theta/4.0 + return np.sqrt(R2) + +# Use JAX to auto-gradient it +r_g = grad(r_f) + +# PLUMED will call the following function +def r(x): + return r_f(x), r_g(x) + +# End curvature.py --------------------------------------------- +``` + + + +# Conclusions + +**PYCV** enables Python-based prototyping of CVs in PLUMED 2. This +programming model may be an advantage over standard C++-based development in that + +1. functions may be prototyped in high-level language, using extensive +mathematical libraries, without boilerplate; +2. just-in-time +compilation occurs transparently: code changes incur in no compilation +and link delays; and +3. CV code may be automatically differentiated in +common cases. + + + + +# Acknowledgements + +I would like to thank PLUMED's lead authors (Prof. Giovanni Bussi, +Prof. Carlo Camilloni, Prof. Gareth Tribello and Prof. Massimiliano +Bonomi) for inputs and discussions. + +# References diff --git a/plugins/pycv/module.type b/plugins/pycv/module.type new file mode 100644 index 0000000000..de83273033 --- /dev/null +++ b/plugins/pycv/module.type @@ -0,0 +1 @@ +default-off diff --git a/plugins/pycv/plumed-nest/plumed-nest-readme.dat b/plugins/pycv/plumed-nest/plumed-nest-readme.dat new file mode 100644 index 0000000000..c32c8cc487 --- /dev/null +++ b/plugins/pycv/plumed-nest/plumed-nest-readme.dat @@ -0,0 +1,13 @@ +ENDPLUMED + +This is a placeholder file. This PLUMED-NEST entry documents the PYCV +module, which provides the PYTHONCV and PYTHONFUNCTION actions. Since +the module requires compilation and linking, automated tests can not +be run against the official distributions. + +Please refer to the following links: + + * Project page: https://giorginolab.github.io/plumed2-pycv/ + * Software: https://github.com/giorginolab/plumed2-pycv + * Paper: http://doi.org/10.21105/joss.01773 + diff --git a/plugins/pycv/prepareMakeForDevelop.sh b/plugins/pycv/prepareMakeForDevelop.sh new file mode 100755 index 0000000000..e40c26f256 --- /dev/null +++ b/plugins/pycv/prepareMakeForDevelop.sh @@ -0,0 +1,12 @@ +#! /usr/bin/env bash + +if [[ -z $PLUMED_KERNEL ]]; then + echo "$(basename $0) can work only if \"PLUMED_KERNEL\" is defined" + echo "either via module load or sourceme.sh" +fi + +{ + plumed config makefile_conf + echo "PLUMED_INCLUDE=-I$(plumed info --include-dir)/plumed" + echo "PLUMED_KERNEL=-L${PLUMED_KERNEL}" +} > Make.inc diff --git a/plugins/pycv/regtest/.gitignore b/plugins/pycv/regtest/.gitignore new file mode 100644 index 0000000000..db3a96c3cc --- /dev/null +++ b/plugins/pycv/regtest/.gitignore @@ -0,0 +1,3 @@ +tmp/ +report.txt +scripts diff --git a/plugins/pycv/regtest/Makefile b/plugins/pycv/regtest/Makefile new file mode 100644 index 0000000000..ccba4eff62 --- /dev/null +++ b/plugins/pycv/regtest/Makefile @@ -0,0 +1,37 @@ +SUBDIRS := $(subst /,,$(dir $(shell ls */Makefile))) + +SUBDIRSCLEAN := $(addsuffix .clean,$(SUBDIRS)) +SUBDIRSVALGRIND := $(addsuffix .valgrind,$(SUBDIRS)) +SUBDIRSTESTCLEAN := $(addsuffix .testclean,$(SUBDIRS)) + +.PHONY: all checkfail copytodoc clean valgrind $(SUBDIRS) $(SUBDIRSCLEAN) $(SUBDIRSVALGRIND) + +all: $(SUBDIRS) + scripts/check + + +checkfail: + scripts/check --fail + +copytodoc: + scripts/copytodoc + +clean: $(SUBDIRSCLEAN) + +valgrind: $(SUBDIRSVALGRIND) + scripts/check + +testclean: $(SUBDIRSTESTCLEAN) + +$(SUBDIRS): + $(MAKE) -C $@ + +$(SUBDIRSCLEAN): %.clean: + $(MAKE) -C $* clean + +$(SUBDIRSVALGRIND): %.valgrind: + $(MAKE) -C $* valgrind + +$(SUBDIRSTESTCLEAN): %.testclean: + $(MAKE) -C $* testclean + diff --git a/plugins/pycv/regtest/pycvcomm/Makefile b/plugins/pycv/regtest/pycvcomm/Makefile new file mode 100644 index 0000000000..430b3123ed --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/Makefile @@ -0,0 +1 @@ +include ../scripts/module.make diff --git a/plugins/pycv/regtest/pycvcomm/maker.sh b/plugins/pycv/regtest/pycvcomm/maker.sh new file mode 100755 index 0000000000..9f3256c811 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/maker.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +failed="" +for a in rt-*; do + echo $a + ( + cd "$a" || exit + make + + if make; then + echo ok + else + failed="${failed} ${a}" + fi + ) +done +echo $failed diff --git a/plugins/pycv/regtest/pycvcomm/rt-Coordination/Makefile b/plugins/pycv/regtest/pycvcomm/rt-Coordination/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-Coordination/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-Coordination/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-Coordination/colvar.out.reference new file mode 100644 index 0000000000..bbbec97bca --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-Coordination/colvar.out.reference @@ -0,0 +1,6 @@ +#! FIELDS time cvPY coord diff + 0.000000 0.054853 0.054853 -0.000000 + 1.000000 0.000648 0.000648 0.000000 + 2.000000 0.000000 0.000000 0.000000 + 3.000000 0.000648 0.000648 0.000000 + 4.000000 0.054853 0.054853 -0.000000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-Coordination/config b/plugins/pycv/regtest/pycvcomm/rt-Coordination/config new file mode 100644 index 0000000000..b8fdbcf9a4 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-Coordination/config @@ -0,0 +1,10 @@ +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" + +plumed_regtest_after() { + { + head -1 deriv | awk '{printf "%s %s %s %s %s-%s\n" , $1, $2,$3,$4,$5,$6 }' + #I'm giving some(a LOT OF) space to the floating point precision + awk 'function abs(v) {return v < 0 ? -v : v} NR>1{print $1, $2, 0.01 < abs($3-$4) } ' deriv_delta +} \ No newline at end of file diff --git a/plugins/pycv/regtest/pycvcomm/rt-Coordination/deriv_delta.reference b/plugins/pycv/regtest/pycvcomm/rt-Coordination/deriv_delta.reference new file mode 100644 index 0000000000..ca9bc7a311 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-Coordination/deriv_delta.reference @@ -0,0 +1,166 @@ +#! FIELDS time parameter cvPY-coord +0.000000 0 0 +0.000000 1 0 +0.000000 2 0 +0.000000 3 0 +0.000000 4 0 +0.000000 5 0 +0.000000 6 0 +0.000000 7 0 +0.000000 8 0 +0.000000 9 0 +0.000000 10 0 +0.000000 11 0 +0.000000 12 0 +0.000000 13 0 +0.000000 14 0 +0.000000 15 0 +0.000000 16 0 +0.000000 17 0 +0.000000 18 0 +0.000000 19 0 +0.000000 20 0 +0.000000 21 0 +0.000000 22 0 +0.000000 23 0 +0.000000 24 0 +0.000000 25 0 +0.000000 26 0 +0.000000 27 0 +0.000000 28 0 +0.000000 29 0 +0.000000 30 0 +0.000000 31 0 +0.000000 32 0 +1.000000 0 0 +1.000000 1 0 +1.000000 2 0 +1.000000 3 0 +1.000000 4 0 +1.000000 5 0 +1.000000 6 0 +1.000000 7 0 +1.000000 8 0 +1.000000 9 0 +1.000000 10 0 +1.000000 11 0 +1.000000 12 0 +1.000000 13 0 +1.000000 14 0 +1.000000 15 0 +1.000000 16 0 +1.000000 17 0 +1.000000 18 0 +1.000000 19 0 +1.000000 20 0 +1.000000 21 0 +1.000000 22 0 +1.000000 23 0 +1.000000 24 0 +1.000000 25 0 +1.000000 26 0 +1.000000 27 0 +1.000000 28 0 +1.000000 29 0 +1.000000 30 0 +1.000000 31 0 +1.000000 32 0 +2.000000 0 0 +2.000000 1 0 +2.000000 2 0 +2.000000 3 0 +2.000000 4 0 +2.000000 5 0 +2.000000 6 0 +2.000000 7 0 +2.000000 8 0 +2.000000 9 0 +2.000000 10 0 +2.000000 11 0 +2.000000 12 0 +2.000000 13 0 +2.000000 14 0 +2.000000 15 0 +2.000000 16 0 +2.000000 17 0 +2.000000 18 0 +2.000000 19 0 +2.000000 20 0 +2.000000 21 0 +2.000000 22 0 +2.000000 23 0 +2.000000 24 0 +2.000000 25 0 +2.000000 26 0 +2.000000 27 0 +2.000000 28 0 +2.000000 29 0 +2.000000 30 0 +2.000000 31 0 +2.000000 32 0 +3.000000 0 0 +3.000000 1 0 +3.000000 2 0 +3.000000 3 0 +3.000000 4 0 +3.000000 5 0 +3.000000 6 0 +3.000000 7 0 +3.000000 8 0 +3.000000 9 0 +3.000000 10 0 +3.000000 11 0 +3.000000 12 0 +3.000000 13 0 +3.000000 14 0 +3.000000 15 0 +3.000000 16 0 +3.000000 17 0 +3.000000 18 0 +3.000000 19 0 +3.000000 20 0 +3.000000 21 0 +3.000000 22 0 +3.000000 23 0 +3.000000 24 0 +3.000000 25 0 +3.000000 26 0 +3.000000 27 0 +3.000000 28 0 +3.000000 29 0 +3.000000 30 0 +3.000000 31 0 +3.000000 32 0 +4.000000 0 0 +4.000000 1 0 +4.000000 2 0 +4.000000 3 0 +4.000000 4 0 +4.000000 5 0 +4.000000 6 0 +4.000000 7 0 +4.000000 8 0 +4.000000 9 0 +4.000000 10 0 +4.000000 11 0 +4.000000 12 0 +4.000000 13 0 +4.000000 14 0 +4.000000 15 0 +4.000000 16 0 +4.000000 17 0 +4.000000 18 0 +4.000000 19 0 +4.000000 20 0 +4.000000 21 0 +4.000000 22 0 +4.000000 23 0 +4.000000 24 0 +4.000000 25 0 +4.000000 26 0 +4.000000 27 0 +4.000000 28 0 +4.000000 29 0 +4.000000 30 0 +4.000000 31 0 +4.000000 32 0 diff --git a/plugins/pycv/regtest/pycvcomm/rt-Coordination/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-Coordination/plumed.dat new file mode 100644 index 0000000000..d2bb02e14e --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-Coordination/plumed.dat @@ -0,0 +1,18 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +IMPORT=pyCOORDINATION +CALCULATE=pyCoord +... + +coord: COORDINATION GROUPA=@mdatoms,@mdatoms R_0=0.4 + +diff: CUSTOM ARG=cvPY,coord FUNC=y-x PERIODIC=NO + +DUMPDERIVATIVES ARG=cvPY,coord FILE=deriv FMT=%8.4f + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvcomm/rt-Coordination/pyCOORDINATION.py b/plugins/pycv/regtest/pycvcomm/rt-Coordination/pyCOORDINATION.py new file mode 100644 index 0000000000..3a916f91f0 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-Coordination/pyCOORDINATION.py @@ -0,0 +1,95 @@ +# This is a not optimzed implementation of the COORDINATION + +import numpy as np +import plumedCommunications +from sys import stderr as log +from jax import jit + +# import plumedUtilities +print("Imported pyCoord.", file=log) + +N: int = 6 +M: int = 12 +D0: float = 0.0 +R0: float = 0.4 +INVR0: float = 1.0 / R0 +DMAX = D0 + R0 * (0.00001 ** (1.0 / (N - M))) +STRETCH = 1.0 +SHIFT = 0.0 + + +@jit +def jaxSwitch(d): + rdist = d * INVR0 + rNdist = (rdist) ** (N - 1) + ret = (1.0 / (1 + rdist * rNdist)) * STRETCH + ret += SHIFT + dfunc = -N * rNdist * ret * ret / d + dfunc *= STRETCH * INVR0 + return ret, dfunc + + +def switch(d: np.ndarray) -> np.ndarray: + ret = np.zeros_like(d) + dfunc = np.zeros_like(d) + WhereToCalc = d < DMAX # & d > D0 + # print(f"{dfunc=}", file=log) + # not doinf d-D0 for now, so no need for rdist<=0 + ret[WhereToCalc], dfunc[WhereToCalc] = jaxSwitch(d[WhereToCalc]) + return ret, dfunc + + +def stretchSwitch(): + s, _ = jaxSwitch(np.array([0.0, DMAX])) + stretch = 1 / (s[0] - s[1]) + return stretch, -s[1] * stretch + + +# some juggling for calculationg the stretch +STRETCH, SHIFT = stretchSwitch() +print(f"{N=} {M=} {D0=} {R0=} {INVR0=} {DMAX=} {STRETCH=} {SHIFT=}", file=log) + + +def pyCoord(action: plumedCommunications.PythonCVInterface): + atoms = action.getPositions() + nat = atoms.shape[0] + nl = action.getNeighbourList() + + assert nl.size() == ((nat - 1) * nat) // 2 + pbc = action.getPbc() + couples = nl.getClosePairs() + absoluteIndexes=[] + #not so fast, but speed here is not important + for i in action.absoluteIndexes: + absoluteIndexes.append(i.index) + absoluteIndexes=np.array(absoluteIndexes) + #sameIndex = np.where(absoluteIndexes[couples[:, 0]]==absoluteIndexes[couples[:, 1]]) + sameIndex = absoluteIndexes[couples[:, 0]]==absoluteIndexes[couples[:, 1]] + d = atoms[couples[:, 0]] - atoms[couples[:, 1]] + d = pbc.apply(d) + # from here we are in "pure python" + dist = np.linalg.norm(d, axis=1) + + dist[sameIndex] += DMAX*2.0 + + sw, dfunc = switch(dist) + dev = np.zeros_like(atoms) + + predev = d * dfunc.reshape((-1, 1)) + for atomID in range(nat): + # wherePlus =couples[:, 0]==atomID + # whereMinus=couples[:, 1]==atomID + dev[atomID] = np.sum(predev[couples[:, 0] == atomID], axis=0) - np.sum( + predev[couples[:, 1] == atomID], axis=0 + ) + virial = np.zeros((3, 3)) + for i in range(predev.shape[0]): + virial -= np.outer(predev[i], d[i]) + + return np.sum(sw), dev, virial + +#this tests also the concatenating special keywords +plumedInit = { + "Value": plumedCommunications.defaults.COMPONENT, + "GROUPA": "@mdatoms,@mdatoms", +} diff --git a/plugins/pycv/regtest/pycvcomm/rt-Coordination/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-Coordination/traj.xyz new file mode 100644 index 0000000000..1a781c421f --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-Coordination/traj.xyz @@ -0,0 +1,30 @@ +4 +6 6 6 +X 5 5 5 +X 4 5 5 +X 5 4 5 +X 5 5 4 +4 +6 6 6 +X 5 5 5 +X 3 5 5 +X 5 3 5 +X 5 5 3 +4 +6 6 6 +X 5 5 5 +X 2 5 5 +X 5 2 5 +X 5 5 2 +4 +6 6 6 +X 5 5 5 +X 1 5 5 +X 5 1 5 +X 5 5 1 +4 +6 6 6 +X 5 5 5 +X 0 5 5 +X 5 0 5 +X 5 5 0 diff --git a/plugins/pycv/regtest/pycvcomm/rt-MDinformations/Makefile b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-MDinformations/config b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/config new file mode 100644 index 0000000000..7429ccc9fa --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz --mc massCharges.dat" diff --git a/plugins/pycv/regtest/pycvcomm/rt-MDinformations/massCharges.dat b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/massCharges.dat new file mode 100644 index 0000000000..778f45f8a0 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/massCharges.dat @@ -0,0 +1,6 @@ +#! FIELDS index mass charge + 0 2 0.2 + 1 3 0.3 + 2 4 0.4 + 3 5 0.5 + \ No newline at end of file diff --git a/plugins/pycv/regtest/pycvcomm/rt-MDinformations/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/plumed.dat new file mode 100644 index 0000000000..e1c1f2fcb6 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/plumed.dat @@ -0,0 +1,15 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +ATOMS=2,4 +IMPORT=unitTest +CALCULATE=mypytest +INIT=myInit +PREPARE=myPrepare +... + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvcomm/rt-MDinformations/pytest.log.reference b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/pytest.log.reference new file mode 100644 index 0000000000..ff675fcbd8 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/pytest.log.reference @@ -0,0 +1,21 @@ +action label: cvPY +@step 0 +action.getTime()=0.0 +action.getTimeStep()=1.0 +action.isRestart()=False +action.isExchangeStep()=False +@step 1 +action.getTime()=1.0 +action.getTimeStep()=1.0 +action.isRestart()=False +action.isExchangeStep()=False +@step 2 +action.getTime()=2.0 +action.getTimeStep()=1.0 +action.isRestart()=False +action.isExchangeStep()=False +@step 3 +action.getTime()=3.0 +action.getTimeStep()=1.0 +action.isRestart()=False +action.isExchangeStep()=False diff --git a/plugins/pycv/regtest/pycvcomm/rt-MDinformations/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/traj.xyz new file mode 100644 index 0000000000..c0e66cf2b2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 5 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X -5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 -5 diff --git a/plugins/pycv/regtest/pycvcomm/rt-MDinformations/unitTest.py b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/unitTest.py new file mode 100644 index 0000000000..ea8e5056db --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-MDinformations/unitTest.py @@ -0,0 +1,22 @@ +import plumedCommunications as PLMD + +# import plumedUtilities +log = open("pytest.log", "w") + +def myPrint(*args,**kwargs): print(*args,**kwargs, file=log) + +def myInit(action: PLMD.PythonCVInterface): + myPrint(f"action label: {action.label}") + return{"Value": PLMD.defaults.COMPONENT_NODEV,} + +def myPrepare(action: PLMD.PythonCVInterface): + myPrint(f"@step {action.getStep()}") + myPrint(f"{action.getTime()=}") + myPrint(f"{action.getTimeStep()=}") + myPrint(f"{action.isRestart()=}") + myPrint(f"{action.isExchangeStep()=}") + return {} + + +def mypytest(action: PLMD.PythonCVInterface): + return 0.0 diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/Makefile b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/colvar.out.reference new file mode 100644 index 0000000000..458ff63b29 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/colvar.out.reference @@ -0,0 +1,6 @@ +#! FIELDS time cvPY.py-ax cvPY.py-ay cvPY.py-az cvPY.py-bx cvPY.py-by cvPY.py-bz cvPY.py-cx cvPY.py-cy cvPY.py-cz + 0.000000 6.000000 0.000000 0.000000 0.000000 6.000000 0.000000 0.000000 0.000000 6.000000 + 1.000000 6.000000 0.000000 0.000000 0.000000 5.000000 0.000000 0.000000 0.000000 4.000000 + 2.000000 1.000000 1.000000 0.000000 1.000000 -1.000000 0.000000 0.000000 0.000000 1.000000 + 3.000000 1.000000 1.000000 0.000000 1.000000 0.250000 0.000000 1.000000 1.000000 -1.000000 + 4.000000 2.000000 3.000000 0.000000 6.000000 0.250000 -3.200000 0.000000 1.000000 -1.000000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/config b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/plumed.dat new file mode 100644 index 0000000000..51cc22945b --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/plumed.dat @@ -0,0 +1,10 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... + PYCVINTERFACE + ATOMS=1 + IMPORT=pyGetBoxes + CALCULATE=pyBox +... + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/pyGetBoxes.py b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/pyGetBoxes.py new file mode 100644 index 0000000000..803a4937b7 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/pyGetBoxes.py @@ -0,0 +1,40 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications + +# import plumedUtilities +log = open("pydist.log", "w") + +print("Imported my pydist.", file=log) + +plumedInit = { + "COMPONENTS": { + "ax": plumedCommunications.defaults.COMPONENT, + "ay": plumedCommunications.defaults.COMPONENT, + "az": plumedCommunications.defaults.COMPONENT, + "bx": plumedCommunications.defaults.COMPONENT, + "by": plumedCommunications.defaults.COMPONENT, + "bz": plumedCommunications.defaults.COMPONENT, + "cx": plumedCommunications.defaults.COMPONENT, + "cy": plumedCommunications.defaults.COMPONENT, + "cz": plumedCommunications.defaults.COMPONENT, + } +} + + +def pyBox(action: plumedCommunications.PythonCVInterface): + d = action.getPbc().getBox() + print(f"{d=}", file=log) + ret = {} + grad = {} + for i, name in enumerate(["a", "b", "c"]): + for j, coord in enumerate(["x", "y", "z"]): + ret[f"{name}{coord}"] = (d[i, j], np.zeros((1, 3))) + print(ret, file=log) + return ret diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/traj.xyz new file mode 100644 index 0000000000..7aeffbc703 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getBox/traj.xyz @@ -0,0 +1,15 @@ +1 +6 6 6 +X 0 0 0 +1 +6 5 4 +X 0 0 0 +1 +1 1 0 1 -1 0 0 0 1 +X 0 0 0 +1 +1 1 0 1 0.25 0 1 1 -1 +X 0 0 0 +1 +2 3 0 6 0.25 -3.2 0 1 -1 +X 0 0 0 diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/Makefile b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/colvar.out.reference new file mode 100644 index 0000000000..e2eeeb61dc --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/colvar.out.reference @@ -0,0 +1,6 @@ +#! FIELDS time cvPY.py-aix cvPY.py-aiy cvPY.py-aiz cvPY.py-bix cvPY.py-biy cvPY.py-biz cvPY.py-cix cvPY.py-ciy cvPY.py-ciz + 0.000000 0.166667 0.000000 0.000000 0.000000 0.166667 0.000000 0.000000 0.000000 0.166667 + 1.000000 0.166667 0.000000 0.000000 0.000000 0.200000 0.000000 0.000000 0.000000 0.250000 + 2.000000 0.500000 0.500000 -0.000000 0.500000 -0.500000 -0.000000 -0.000000 -0.000000 1.000000 + 3.000000 -0.333333 1.333333 0.000000 1.333333 -1.333333 0.000000 1.000000 0.000000 -1.000000 + 4.000000 0.123431 0.125523 -0.401674 0.251046 -0.083682 0.267782 0.251046 -0.083682 -0.732218 diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/config b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/plumed.dat new file mode 100644 index 0000000000..c0ee689288 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/plumed.dat @@ -0,0 +1,10 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... + PYCVINTERFACE + ATOMS=1 + IMPORT=pyGetBoxes + CALCULATE=pyInvBox +... + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/pyGetBoxes.py b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/pyGetBoxes.py new file mode 100644 index 0000000000..4f9ccd7968 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/pyGetBoxes.py @@ -0,0 +1,40 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications + +# import plumedUtilities +log = open("pydist.log", "w") + +print("Imported my pydist.", file=log) +print(plumedCommunications.defaults.COMPONENT, file=log) + +plumedInit = dict( + COMPONENTS=dict( + aix=plumedCommunications.defaults.COMPONENT, + aiy=plumedCommunications.defaults.COMPONENT, + aiz=plumedCommunications.defaults.COMPONENT, + bix=plumedCommunications.defaults.COMPONENT, + biy=plumedCommunications.defaults.COMPONENT, + biz=plumedCommunications.defaults.COMPONENT, + cix=plumedCommunications.defaults.COMPONENT, + ciy=plumedCommunications.defaults.COMPONENT, + ciz=plumedCommunications.defaults.COMPONENT, + ) +) + + +def pyInvBox(action: plumedCommunications.PythonCVInterface): + invBox = action.getPbc().getInvBox() + print(f"{invBox=}", file=log) + ret = {} + for i, name in enumerate(["a", "b", "c"]): + for j, coord in enumerate(["x", "y", "z"]): + ret[f"{name}i{coord}"] = (invBox[i, j], np.zeros((1, 3))) + print(ret, file=log) + return ret diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/traj.xyz new file mode 100644 index 0000000000..7aeffbc703 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBC-getInvBox/traj.xyz @@ -0,0 +1,15 @@ +1 +6 6 6 +X 0 0 0 +1 +6 5 4 +X 0 0 0 +1 +1 1 0 1 -1 0 0 0 1 +X 0 0 0 +1 +1 1 0 1 0.25 0 1 1 -1 +X 0 0 0 +1 +2 3 0 6 0.25 -3.2 0 1 -1 +X 0 0 0 diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBCs/Makefile b/plugins/pycv/regtest/pycvcomm/rt-PBCs/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBCs/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBCs/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-PBCs/colvar.out.reference new file mode 100644 index 0000000000..b98552dff1 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBCs/colvar.out.reference @@ -0,0 +1,6 @@ +#! FIELDS time cvPY cvCPP + 0.000000 1.000000 1.000000 + 1.000000 1.000000 1.000000 + 2.000000 1.000000 1.000000 + 3.000000 1.000000 1.000000 + 4.000000 1.000000 1.000000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBCs/config b/plugins/pycv/regtest/pycvcomm/rt-PBCs/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBCs/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBCs/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-PBCs/plumed.dat new file mode 100644 index 0000000000..a5905a98e2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBCs/plumed.dat @@ -0,0 +1,7 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: PYCVINTERFACE ATOMS=1,4 IMPORT=pydistancePBCs CALCULATE=pydist + +cvCPP: DISTANCE ATOMS=1,4 + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBCs/pydistancePBCs.py b/plugins/pycv/regtest/pycvcomm/rt-PBCs/pydistancePBCs.py new file mode 100644 index 0000000000..0da907ec60 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBCs/pydistancePBCs.py @@ -0,0 +1,28 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications + +# import plumedUtilities +log = open("pydist.log", "w") + +print("Imported my pydist.", file=log) + +plumedInit={"Value":plumedCommunications.defaults.COMPONENT_NODEV} + +def pydist(action: plumedCommunications.PythonCVInterface): + at: np.ndarray = action.getPositions() + + # print(at0,file=log) + d = at[0] - at[1] + d = action.getPbc().apply([d]) + assert d.shape[1] == 3, "d is not a (*,3) array" + d = np.linalg.norm(d[0]) + + # print(f"{at1},{at2}",file=log) + return d diff --git a/plugins/pycv/regtest/pycvcomm/rt-PBCs/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-PBCs/traj.xyz new file mode 100644 index 0000000000..90abff41f7 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-PBCs/traj.xyz @@ -0,0 +1,30 @@ +4 +6 6 6 +X 0 0 0 +X 0 5 5 +X 5 5 0 +X 5 0 0 +4 +6 6 6 +X 0 0 0 +X 5 5 0 +X 0 5 5 +X 0 5 0 +4 +6 6 6 +X 0 0 0 +X 0 5 0 +X -5 5 0 +X 0 0 5 +4 +6 6 6 +X 0 0 0 +X 0 5 0 +X 0 5 -5 +X 1 0 0 +4 +6 6 6 +X 0 0 0 +X 0 5 5 +X 0 5 -5 +X -1 0 0 diff --git a/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/Makefile b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/colvar.out.reference new file mode 100644 index 0000000000..bec5227863 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cvPY.py-absoluteIndex0index cvPY.py-absoluteIndex0serial cvPY.py-absoluteIndex1index cvPY.py-absoluteIndex1serial + 0.000000 1.000000 2.000000 3.000000 4.000000 + 1.000000 1.000000 2.000000 3.000000 4.000000 + 2.000000 1.000000 2.000000 3.000000 4.000000 + 3.000000 1.000000 2.000000 3.000000 4.000000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/config b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/plumed.dat new file mode 100644 index 0000000000..e707444539 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/plumed.dat @@ -0,0 +1,13 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +ATOMS=2,4 +IMPORT=unitTest +CALCULATE=mypytest +... + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/traj.xyz new file mode 100644 index 0000000000..c0e66cf2b2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 5 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X -5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 -5 diff --git a/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/unitTest.py b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/unitTest.py new file mode 100644 index 0000000000..1bdd5a6cf7 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-absoluteIndexes/unitTest.py @@ -0,0 +1,29 @@ +import plumedCommunications as PLMD + +# import plumedUtilities +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) +nodevnoperiod = {"period": None, "derivative": False} +plumedInit = dict( + COMPONENTS=dict( + absoluteIndex0index=nodevnoperiod, + absoluteIndex0serial=nodevnoperiod, + absoluteIndex1index=nodevnoperiod, + absoluteIndex1serial=nodevnoperiod, + ) +) + + +def mypytest(action: PLMD.PythonCVInterface): + ret = { + "absoluteIndex0index": action.absoluteIndexes[0].index, + "absoluteIndex0serial": action.absoluteIndexes[0].serial, + } + indexes = action.absoluteIndexes + ret["absoluteIndex1index"] = indexes[1].index + ret["absoluteIndex1serial"] = indexes[1].serial + # the following lines are guarantee to fail :) + # action.absoluteIndexes[0].index=0 + # indexes[1].index=0 + return ret diff --git a/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/Makefile b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/colvar.out.reference new file mode 100644 index 0000000000..f18f9f5841 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/colvar.out.reference @@ -0,0 +1,8 @@ +#! FIELDS time cvPY + 0.000000 2.000000 + 1.000000 2.000000 + 2.000000 2.000000 + 3.000000 2.000000 + 4.000000 2.000000 + 5.000000 2.000000 + 6.000000 2.000000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/config b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/plumed.dat new file mode 100644 index 0000000000..9e39640651 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/plumed.dat @@ -0,0 +1,11 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +IMPORT=unitTest +... + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/traj.xyz new file mode 100644 index 0000000000..1ed965943b --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/traj.xyz @@ -0,0 +1,28 @@ +2 +100 100 100 +X 0 0 0 +X 99 0 0 +2 +100 100 100 +X 0 0 0 +X 0 99 0 +2 +100 100 100 +X 0 0 0 +X 0 0 99 +2 +100 100 100 +X 0 0 0 +X 0 99 99 +2 +100 100 100 +X 0 0 0 +X 99 0 99 +2 +100 100 100 +X 0 0 0 +X 99 99 0 +2 +100 100 100 +X 0 0 0 +X 99 99 99 diff --git a/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/unitTest.py b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/unitTest.py new file mode 100644 index 0000000000..c4e9c536be --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-autoFunctions/unitTest.py @@ -0,0 +1,19 @@ +import plumedCommunications as PLMD +import numpy +from sys import stderr as log +#log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + + +def plumedInitio(action: PLMD.PythonCVInterface): + # return {"Value": {"period": [None,0,0]}} + # return {"Value": {"period": None}} + return {"Value": {"period": ["0",0.3]}} +plumedInit={"Value": {"period": None},"NOPBC":False, + "ATOMS":"1,2"} + +def plumedCalculate(action: PLMD.PythonCVInterface): + ret = [action.nat] + + return ret diff --git a/plugins/pycv/regtest/pycvcomm/rt-doc/Makefile b/plugins/pycv/regtest/pycvcomm/rt-doc/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-doc/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-doc/config b/plugins/pycv/regtest/pycvcomm/rt-doc/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-doc/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-doc/config.reference b/plugins/pycv/regtest/pycvcomm/rt-doc/config.reference new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-doc/config.reference @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-doc/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-doc/plumed.dat new file mode 100644 index 0000000000..1c4fdefee5 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-doc/plumed.dat @@ -0,0 +1,5 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvdist: PYCVINTERFACE IMPORT=pyhelp + +#PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-doc/pyhelp.py b/plugins/pycv/regtest/pycvcomm/rt-doc/pyhelp.py new file mode 100644 index 0000000000..bc2754d199 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-doc/pyhelp.py @@ -0,0 +1,24 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import plumedCommunications +import pydoc + +def plumedInit(_): + with open('PythonCVInterface.help.txt', 'w') as f: + h = pydoc.Helper(output=f) + h(plumedCommunications.PythonCVInterface) + with open('plumedCommunications.help.txt', 'w') as f: + h = pydoc.Helper(output=f) + h(plumedCommunications) + with open('plumedCommunications.defaults.help.txt', 'w') as f: + h = pydoc.Helper(output=f) + h(plumedCommunications.defaults) + return {"Value":plumedCommunications.defaults.COMPONENT_NODEV, "ATOMS":"1"} + +def plumedCalculate(_): + return 0 diff --git a/plugins/pycv/regtest/pycvcomm/rt-doc/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-doc/traj.xyz new file mode 100644 index 0000000000..b3b4e45d61 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-doc/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 5 5 +X 4 5 5 +X 5 4 5 +X 5 5 4 +4 +100 100 100 +X 5 5 5 +X 3 5 5 +X 5 3 5 +X 5 5 3 +4 +100 100 100 +X 5 5 5 +X 2 5 5 +X 5 2 5 +X 5 5 2 +4 +100 100 100 +X 5 5 5 +X 1 5 5 +X 5 1 5 +X 5 5 1 diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPosition/Makefile b/plugins/pycv/regtest/pycvcomm/rt-getPosition/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPosition/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPosition/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-getPosition/colvar.out.reference new file mode 100644 index 0000000000..c696b438b3 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPosition/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cvPY cvCPP + 0.000000 5.000000 5.000000 + 1.000000 8.660254 8.660254 + 2.000000 11.180340 11.180340 + 3.000000 8.660254 8.660254 diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPosition/config b/plugins/pycv/regtest/pycvcomm/rt-getPosition/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPosition/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPosition/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-getPosition/plumed.dat new file mode 100644 index 0000000000..dfdb9973c3 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPosition/plumed.dat @@ -0,0 +1,10 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: PYCVINTERFACE ATOMS=1,4 IMPORT=pydistancegetAtPos CALCULATE=pydist + +cvCPP: DISTANCE ATOMS=1,4 + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPosition/pydistancegetAtPos.py b/plugins/pycv/regtest/pycvcomm/rt-getPosition/pydistancegetAtPos.py new file mode 100644 index 0000000000..a618c0636f --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPosition/pydistancegetAtPos.py @@ -0,0 +1,29 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications + +# import plumedUtilities +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +plumedInit = {"Value": plumedCommunications.defaults.COMPONENT_NODEV} + + +def pydist(action: plumedCommunications.PythonCVInterface): + at: list = [ + action.getPosition(0), + action.getPosition(1), + ] + d = at[0] - at[1] + d = np.linalg.norm(d) + print(f"{at[0]},{at[1]},{d}", file=log) + + # print(f"{at1},{at2}",file=log) + return d diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPosition/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-getPosition/traj.xyz new file mode 100644 index 0000000000..c0e66cf2b2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPosition/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 5 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X -5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 -5 diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/Makefile b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/colvar.out.reference new file mode 100644 index 0000000000..c696b438b3 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cvPY cvCPP + 0.000000 5.000000 5.000000 + 1.000000 8.660254 8.660254 + 2.000000 11.180340 11.180340 + 3.000000 8.660254 8.660254 diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/config b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/plumed.dat new file mode 100644 index 0000000000..63a85ce1d2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/plumed.dat @@ -0,0 +1,7 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: PYCVINTERFACE GROUPA=1,4 IMPORT=pydistancegetAtPos CALCULATE=pydist + +cvCPP: DISTANCE ATOMS=1,4 + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/pydistancegetAtPos.py b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/pydistancegetAtPos.py new file mode 100644 index 0000000000..f6813d041a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/pydistancegetAtPos.py @@ -0,0 +1,33 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications +from sys import stderr as log + +# import plumedUtilities +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +plumedInit = {"Value": plumedCommunications.defaults.COMPONENT_NODEV} + + +def pydist(action: plumedCommunications.PythonCVInterface): + # NB: This is not a realistic case of using the neigbour list!!! + # cvPY: PYCVINTERFACE GROUPA=1,4 IMPORT=pydistancegetAtPos CALCULATE=pydist + # ^ using this line should behave like calling "ATOMS=1,4" (with this function) + atoms = action.getPositions() + nl = action.getNeighbourList() + assert nl.size() == 1 + NLlist = nl.getClosePairs()[0] + + d = atoms[NLlist[0]] - atoms[NLlist[1]] + d = np.linalg.norm(d) + print(f"{atoms[0]},{atoms[1]},{d}", file=log) + + return d diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/traj.xyz new file mode 100644 index 0000000000..c0e66cf2b2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNL/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 5 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X -5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 -5 diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/Makefile b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/colvar.out.reference new file mode 100644 index 0000000000..0a41fa2874 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cvPY.py-x cvPY.py-y cvPY.py-z + 0.000000 1.000000 1.000000 1.000000 + 1.000000 2.000000 2.000000 2.000000 + 2.000000 3.000000 3.000000 3.000000 + 3.000000 4.000000 4.000000 4.000000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/config b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/plumed.dat new file mode 100644 index 0000000000..15c9d4cedc --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/plumed.dat @@ -0,0 +1,11 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +GROUPA=1,1,1 GROUPB=2,3,4 +IMPORT=pydistancegetAtPos +CALCULATE=pydistInPair +PAIR +... + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/pydistancegetAtPos.py b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/pydistancegetAtPos.py new file mode 100644 index 0000000000..489ee5038c --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/pydistancegetAtPos.py @@ -0,0 +1,38 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications +from sys import stderr as log + +# import plumedUtilities +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +plumedInit = { + "COMPONENTS": { + "x": plumedCommunications.defaults.COMPONENT, + "y": plumedCommunications.defaults.COMPONENT, + "z": plumedCommunications.defaults.COMPONENT, + } +} + + +def pydistInPair(action: plumedCommunications.PythonCVInterface): + # NB: This is not a realistic case of using the neigbour list!!! + # cvPY: PYCVINTERFACE GROUPA=1,4 IMPORT=pydistancegetAtPos CALCULATE=pydist + + atoms = action.getPositions() + nl = action.getNeighbourList() + assert nl.size() == 3 + + x, y, z = [atoms[pair[0]] - atoms[pair[1]] for pair in nl.getClosePairs()] + + zero = np.zeros(atoms.shape) + # MUST work with tuple or lists + return {"x": [x[0], zero], "y": (y[1], zero), "z": (z[2], zero)} diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/traj.xyz new file mode 100644 index 0000000000..b3b4e45d61 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositionNLPair/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 5 5 +X 4 5 5 +X 5 4 5 +X 5 5 4 +4 +100 100 100 +X 5 5 5 +X 3 5 5 +X 5 3 5 +X 5 5 3 +4 +100 100 100 +X 5 5 5 +X 2 5 5 +X 5 2 5 +X 5 5 2 +4 +100 100 100 +X 5 5 5 +X 1 5 5 +X 5 1 5 +X 5 5 1 diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositions/Makefile b/plugins/pycv/regtest/pycvcomm/rt-getPositions/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositions/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositions/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-getPositions/colvar.out.reference new file mode 100644 index 0000000000..c696b438b3 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositions/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cvPY cvCPP + 0.000000 5.000000 5.000000 + 1.000000 8.660254 8.660254 + 2.000000 11.180340 11.180340 + 3.000000 8.660254 8.660254 diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositions/config b/plugins/pycv/regtest/pycvcomm/rt-getPositions/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositions/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositions/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-getPositions/plumed.dat new file mode 100644 index 0000000000..b392518120 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositions/plumed.dat @@ -0,0 +1,7 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: PYCVINTERFACE ATOMS=1,4 IMPORT=pydistancegetAt CALCULATE=pydist + +cvCPP: DISTANCE ATOMS=1,4 + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositions/pydistancegetAt.py b/plugins/pycv/regtest/pycvcomm/rt-getPositions/pydistancegetAt.py new file mode 100644 index 0000000000..05aa36e1bc --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositions/pydistancegetAt.py @@ -0,0 +1,33 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications + +# import plumedUtilities +log = open("pydist.log", "w") + +print("Imported my pydist.", file=log) + +plumedInit = {"Value": plumedCommunications.defaults.COMPONENT_NODEV} + + +def pydist_(x): + # print("call",file=log) + return 0 + + +def pydist(action: plumedCommunications.PythonCVInterface): + at: np.ndarray = action.getPositions() + + # print(at0,file=log) + d = at[0] - at[1] + d = np.linalg.norm(d) + print(f"{at[0]},{at[1]},{d}", file=log) + + # print(f"{at1},{at2}",file=log) + return d diff --git a/plugins/pycv/regtest/pycvcomm/rt-getPositions/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-getPositions/traj.xyz new file mode 100644 index 0000000000..c0e66cf2b2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-getPositions/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 5 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X -5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 -5 diff --git a/plugins/pycv/regtest/pycvcomm/rt-makeWhole/Makefile b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-makeWhole/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/colvar.out.reference new file mode 100644 index 0000000000..7f5c42d6de --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/colvar.out.reference @@ -0,0 +1,8 @@ +#! FIELDS time cvPY.py-dbefore cvPY.py-dafter + 0.000000 99.000000 1.000000 + 1.000000 99.000000 1.000000 + 2.000000 99.000000 1.000000 + 3.000000 140.007143 1.414214 + 4.000000 140.007143 1.414214 + 5.000000 140.007143 1.414214 + 6.000000 171.473030 1.732051 diff --git a/plugins/pycv/regtest/pycvcomm/rt-makeWhole/config b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-makeWhole/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/plumed.dat new file mode 100644 index 0000000000..3eb8d69361 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/plumed.dat @@ -0,0 +1,13 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +ATOMS=1,2 +IMPORT=unitTest +CALCULATE=mypytest +... + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvcomm/rt-makeWhole/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/traj.xyz new file mode 100644 index 0000000000..1ed965943b --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/traj.xyz @@ -0,0 +1,28 @@ +2 +100 100 100 +X 0 0 0 +X 99 0 0 +2 +100 100 100 +X 0 0 0 +X 0 99 0 +2 +100 100 100 +X 0 0 0 +X 0 0 99 +2 +100 100 100 +X 0 0 0 +X 0 99 99 +2 +100 100 100 +X 0 0 0 +X 99 0 99 +2 +100 100 100 +X 0 0 0 +X 99 99 0 +2 +100 100 100 +X 0 0 0 +X 99 99 99 diff --git a/plugins/pycv/regtest/pycvcomm/rt-makeWhole/unitTest.py b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/unitTest.py new file mode 100644 index 0000000000..5cd2aa6579 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-makeWhole/unitTest.py @@ -0,0 +1,26 @@ +import plumedCommunications as PLMD +import numpy + +# import plumedUtilities +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) +plumedInit = { + "COMPONENTS": { + "dbefore": PLMD.defaults.COMPONENT_NODEV, + "dafter": PLMD.defaults.COMPONENT_NODEV, + } +} + + +def mypytest(action: PLMD.PythonCVInterface): + atoms = action.getPositions() + d = atoms[0] - atoms[1] + ret = { + "dbefore": numpy.linalg.norm(d), + } + action.makeWhole() + atoms = action.getPositions() + d = atoms[0] - atoms[1] + ret["dafter"] = numpy.linalg.norm(d) + return ret diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/Makefile b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/colvar.out.reference new file mode 100644 index 0000000000..1037e53989 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cvPY.py-mass0 cvPY.py-mass1 cvPY.py-charge0 cvPY.py-charge1 + 0.000000 3.000000 5.000000 0.300000 0.500000 + 1.000000 3.000000 5.000000 0.300000 0.500000 + 2.000000 3.000000 5.000000 0.300000 0.500000 + 3.000000 3.000000 5.000000 0.300000 0.500000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/config b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/config new file mode 100644 index 0000000000..7429ccc9fa --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz --mc massCharges.dat" diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/massCharges.dat b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/massCharges.dat new file mode 100644 index 0000000000..778f45f8a0 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/massCharges.dat @@ -0,0 +1,6 @@ +#! FIELDS index mass charge + 0 2 0.2 + 1 3 0.3 + 2 4 0.4 + 3 5 0.5 + \ No newline at end of file diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/plumed.dat new file mode 100644 index 0000000000..e707444539 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/plumed.dat @@ -0,0 +1,13 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +ATOMS=2,4 +IMPORT=unitTest +CALCULATE=mypytest +... + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/traj.xyz new file mode 100644 index 0000000000..c0e66cf2b2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 5 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X -5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 -5 diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/unitTest.py b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/unitTest.py new file mode 100644 index 0000000000..77e7fac8fb --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndCharges/unitTest.py @@ -0,0 +1,26 @@ +import plumedCommunications as PLMD + +# import plumedUtilities +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +plumedInit = { + "COMPONENTS": { + "mass0": PLMD.defaults.COMPONENT_NODEV, + "mass1": PLMD.defaults.COMPONENT_NODEV, + "charge0": PLMD.defaults.COMPONENT_NODEV, + "charge1": PLMD.defaults.COMPONENT_NODEV, + } +} + + +def mypytest(action: PLMD.PythonCVInterface): + ret = { + "mass0": action.mass(0), + "charge0": action.charge(0), + "mass1": action.mass(1), + "charge1": action.charge(1), + } + + return ret diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/Makefile b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/colvar.out.reference new file mode 100644 index 0000000000..5436dc9a34 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cvPY.py-mass0 cvPY.py-mass1 cvPY.py-charge0 cvPY.py-charge1 + 0.000000 1 1 1 1 + 1.000000 1 1 1 1 + 2.000000 1 1 1 1 + 3.000000 1 1 1 1 diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/config b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/config new file mode 100644 index 0000000000..7429ccc9fa --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz --mc massCharges.dat" diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/massCharges.dat b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/massCharges.dat new file mode 100644 index 0000000000..778f45f8a0 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/massCharges.dat @@ -0,0 +1,6 @@ +#! FIELDS index mass charge + 0 2 0.2 + 1 3 0.3 + 2 4 0.4 + 3 5 0.5 + \ No newline at end of file diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/plumed.dat new file mode 100644 index 0000000000..c420e88be1 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/plumed.dat @@ -0,0 +1,12 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +ATOMS=2,4 +IMPORT=unitTest +CALCULATE=mypytest +INIT=myInit +... + +#I am returnin booleans +PRINT FILE=colvar.out ARG=* FMT=%1.0f diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/traj.xyz new file mode 100644 index 0000000000..c0e66cf2b2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 5 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X -5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 -5 diff --git a/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/unitTest.py b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/unitTest.py new file mode 100644 index 0000000000..21d38abc12 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-massesAndChargesArray/unitTest.py @@ -0,0 +1,37 @@ +import plumedCommunications as PLMD +from sys import stderr as log +import numpy as np + +print("Imported unitTest", file=log) + + +def myInit(action: PLMD.PythonCVInterface): + t = np.loadtxt("massCharges.dat") + action.data["masses"] = t[:, 1] + action.data["charges"] = t[:, 2] + print("Calling myInit", file=log) + return { + "COMPONENTS": { + "mass0": PLMD.defaults.COMPONENT_NODEV, + "mass1": PLMD.defaults.COMPONENT_NODEV, + "charge0": PLMD.defaults.COMPONENT_NODEV, + "charge1": PLMD.defaults.COMPONENT_NODEV, + } + } + + +def mypytest(action: PLMD.PythonCVInterface): + masses = action.masses() + charges = action.charges() + action.data["masses"] + action.data["charges"] + ret = {} + + for i in range(action.nat): + ret[f"mass{i}"] = ( + action.data["masses"][action.absoluteIndexes[i].index] == masses[i] + ) + ret[f"charge{i}"] = ( + action.data["charges"][action.absoluteIndexes[i].index] == charges[i] + ) + return ret diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/GRAD_d01.reference b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/GRAD_d01.reference new file mode 100644 index 0000000000..6a3ba30709 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/GRAD_d01.reference @@ -0,0 +1,73 @@ +#! FIELDS time parameter cvPY.py-d01 + 0.000000 0 1.0000 + 0.000000 1 0.0000 + 0.000000 2 0.0000 + 0.000000 3 -1.0000 + 0.000000 4 0.0000 + 0.000000 5 0.0000 + 0.000000 6 0.0000 + 0.000000 7 0.0000 + 0.000000 8 0.0000 + 0.000000 9 -5.0000 + 0.000000 10 0.0000 + 0.000000 11 0.0000 + 0.000000 12 0.0000 + 0.000000 13 0.0000 + 0.000000 14 0.0000 + 0.000000 15 0.0000 + 0.000000 16 0.0000 + 0.000000 17 0.0000 + 1.000000 0 1.0000 + 1.000000 1 0.0000 + 1.000000 2 0.0000 + 1.000000 3 -1.0000 + 1.000000 4 0.0000 + 1.000000 5 0.0000 + 1.000000 6 0.0000 + 1.000000 7 0.0000 + 1.000000 8 0.0000 + 1.000000 9 -5.0000 + 1.000000 10 0.0000 + 1.000000 11 0.0000 + 1.000000 12 0.0000 + 1.000000 13 0.0000 + 1.000000 14 0.0000 + 1.000000 15 0.0000 + 1.000000 16 0.0000 + 1.000000 17 0.0000 + 2.000000 0 1.0000 + 2.000000 1 0.0000 + 2.000000 2 0.0000 + 2.000000 3 -1.0000 + 2.000000 4 0.0000 + 2.000000 5 0.0000 + 2.000000 6 0.0000 + 2.000000 7 0.0000 + 2.000000 8 0.0000 + 2.000000 9 -5.0000 + 2.000000 10 0.0000 + 2.000000 11 0.0000 + 2.000000 12 0.0000 + 2.000000 13 0.0000 + 2.000000 14 0.0000 + 2.000000 15 0.0000 + 2.000000 16 0.0000 + 2.000000 17 0.0000 + 3.000000 0 1.0000 + 3.000000 1 0.0000 + 3.000000 2 0.0000 + 3.000000 3 -1.0000 + 3.000000 4 0.0000 + 3.000000 5 0.0000 + 3.000000 6 0.0000 + 3.000000 7 0.0000 + 3.000000 8 0.0000 + 3.000000 9 -5.0000 + 3.000000 10 0.0000 + 3.000000 11 0.0000 + 3.000000 12 0.0000 + 3.000000 13 0.0000 + 3.000000 14 0.0000 + 3.000000 15 0.0000 + 3.000000 16 0.0000 + 3.000000 17 0.0000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/GRAD_d02.reference b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/GRAD_d02.reference new file mode 100644 index 0000000000..bf6603954d --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/GRAD_d02.reference @@ -0,0 +1,73 @@ +#! FIELDS time parameter cvPY.py-d02 + 0.000000 0 0.0000 + 0.000000 1 -1.0000 + 0.000000 2 0.0000 + 0.000000 3 0.0000 + 0.000000 4 0.0000 + 0.000000 5 0.0000 + 0.000000 6 0.0000 + 0.000000 7 1.0000 + 0.000000 8 0.0000 + 0.000000 9 0.0000 + 0.000000 10 0.0000 + 0.000000 11 0.0000 + 0.000000 12 0.0000 + 0.000000 13 -5.0000 + 0.000000 14 0.0000 + 0.000000 15 0.0000 + 0.000000 16 0.0000 + 0.000000 17 0.0000 + 1.000000 0 0.5774 + 1.000000 1 -0.5774 + 1.000000 2 -0.5774 + 1.000000 3 0.0000 + 1.000000 4 0.0000 + 1.000000 5 0.0000 + 1.000000 6 -0.5774 + 1.000000 7 0.5774 + 1.000000 8 0.5774 + 1.000000 9 -2.8868 + 1.000000 10 2.8868 + 1.000000 11 2.8868 + 1.000000 12 2.8868 + 1.000000 13 -2.8868 + 1.000000 14 -2.8868 + 1.000000 15 2.8868 + 1.000000 16 -2.8868 + 1.000000 17 -2.8868 + 2.000000 0 0.8944 + 2.000000 1 -0.4472 + 2.000000 2 0.0000 + 2.000000 3 0.0000 + 2.000000 4 0.0000 + 2.000000 5 0.0000 + 2.000000 6 -0.8944 + 2.000000 7 0.4472 + 2.000000 8 0.0000 + 2.000000 9 -8.9443 + 2.000000 10 4.4721 + 2.000000 11 0.0000 + 2.000000 12 4.4721 + 2.000000 13 -2.2361 + 2.000000 14 0.0000 + 2.000000 15 0.0000 + 2.000000 16 0.0000 + 2.000000 17 0.0000 + 3.000000 0 0.5774 + 3.000000 1 -0.5774 + 3.000000 2 0.5774 + 3.000000 3 0.0000 + 3.000000 4 0.0000 + 3.000000 5 0.0000 + 3.000000 6 -0.5774 + 3.000000 7 0.5774 + 3.000000 8 -0.5774 + 3.000000 9 -2.8868 + 3.000000 10 2.8868 + 3.000000 11 -2.8868 + 3.000000 12 2.8868 + 3.000000 13 -2.8868 + 3.000000 14 2.8868 + 3.000000 15 -2.8868 + 3.000000 16 2.8868 + 3.000000 17 -2.8868 diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/Makefile b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/colvar.out.reference new file mode 100644 index 0000000000..0daaf3d811 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cvPY.py-d01 cvPY.py-d02 + 0.000000 5.000000 5.000000 + 1.000000 5.000000 8.660254 + 2.000000 5.000000 11.180340 + 3.000000 5.000000 8.660254 diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/config b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/plumed.dat new file mode 100644 index 0000000000..ced981eb49 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/plumed.dat @@ -0,0 +1,8 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: PYCVINTERFACE ATOMS=1,2,4 IMPORT=pydistancegetAt CALCULATE=pydist NOPBC + +DUMPDERIVATIVES ARG=cvPY.py-d01 FILE=GRAD_d01 FMT=%8.4f +DUMPDERIVATIVES ARG=cvPY.py-d02 FILE=GRAD_d02 FMT=%8.4f + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/pydistancegetAt.py b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/pydistancegetAt.py new file mode 100644 index 0000000000..56d2be1faa --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/pydistancegetAt.py @@ -0,0 +1,37 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications +from sys import stderr as log + +# import plumedUtilities +# log = open("pydist.log", "w") + +print("Imported my pydist.", file=log) +plumedInit = dict( + COMPONENTS=dict( + d01=plumedCommunications.defaults.COMPONENT, + d02=plumedCommunications.defaults.COMPONENT, + ) +) + + +def pydist(action: plumedCommunications.PythonCVInterface): + at: np.ndarray = action.getPositions() + nat = at.shape[0] + # print(at0,file=log) + ret = {} + for rn, j in [["d01", 1], ["d02", 2]]: + d = at[0] - at[j] + val = np.linalg.norm(d) + grad = np.zeros((nat, 3)) + grad[0] = d / val + grad[j] = -d / val + # print(f"{rn} {val} {grad}", file=log) + ret[rn] = (val, grad) + return ret diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/traj.xyz new file mode 100644 index 0000000000..c0e66cf2b2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleComponents/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 5 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X -5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 -5 diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/Makefile b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/colvar.out.reference new file mode 100644 index 0000000000..84a9ace59e --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/colvar.out.reference @@ -0,0 +1,12 @@ +#! FIELDS time cvPY.py-nonPeriodic cvPY.py-Periodic cvPY.py-PeriodicPI +#! SET min_cvPY.py-Periodic 0 +#! SET max_cvPY.py-Periodic 1.3 +#! SET min_cvPY.py-PeriodicPI 0 +#! SET max_cvPY.py-PeriodicPI pi + 0.000000 0.000000 0.000000 0.000000 + 1.000000 1.000000 1.000000 1.000000 + 2.000000 2.000000 0.700000 2.000000 + 3.000000 3.000000 0.400000 3.000000 + 4.000000 4.000000 0.100000 0.858407 + 5.000000 5.000000 1.100000 1.858407 + 6.000000 6.000000 0.800000 2.858407 diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/config b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/plumed.dat new file mode 100644 index 0000000000..62bbc19ce9 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/plumed.dat @@ -0,0 +1,13 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +IMPORT=unitTest +INIT=init +CALCULATE=mypytest +... + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/traj.xyz new file mode 100644 index 0000000000..1ed965943b --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/traj.xyz @@ -0,0 +1,28 @@ +2 +100 100 100 +X 0 0 0 +X 99 0 0 +2 +100 100 100 +X 0 0 0 +X 0 99 0 +2 +100 100 100 +X 0 0 0 +X 0 0 99 +2 +100 100 100 +X 0 0 0 +X 0 99 99 +2 +100 100 100 +X 0 0 0 +X 99 0 99 +2 +100 100 100 +X 0 0 0 +X 99 99 0 +2 +100 100 100 +X 0 0 0 +X 99 99 99 diff --git a/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/unitTest.py b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/unitTest.py new file mode 100644 index 0000000000..eccd4b75f4 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multipleValuePeriodicity/unitTest.py @@ -0,0 +1,25 @@ +import plumedCommunications as PLMD +import numpy + +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +init = dict( + COMPONENTS=dict( + nonPeriodic=dict(period=None), + Periodic={"period": ["0", "1.3"]}, + PeriodicPI={"period": ["0", "pi"]}, + ), + ATOMS="1,2", +) + + +def mypytest(action: PLMD.PythonCVInterface): + ret = { + "nonPeriodic": action.getStep(), + "Periodic": action.getStep(), + "PeriodicPI": action.getStep(), + } + + return ret diff --git a/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/Makefile b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/colvar.out.reference new file mode 100644 index 0000000000..b67d75f789 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cv1 cv2 + 0.000000 0.360000 0.360000 + 1.000000 0.720000 0.720000 + 2.000000 1.080000 1.080000 + 3.000000 1.440000 1.440000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/config b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/config new file mode 100644 index 0000000000..82e4472874 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz --dump-forces forces --dump-forces-fmt=%10.6f" diff --git a/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/distcv.py b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/distcv.py new file mode 100644 index 0000000000..f9b87de6a6 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/distcv.py @@ -0,0 +1,14 @@ +import numpy as np +import plumedCommunications + +# In reality one should not log stuff here... +log = open("log.txt", "w", 1) + +print("At import", file=log) + +plumedInit={"Value": plumedCommunications.defaults.COMPONENT_NODEV,} + +# The CV function actually called +def cv(X): + #0.36 is arbitrary + return 0.36*(X.getStep()+1) diff --git a/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/plumed.dat new file mode 100644 index 0000000000..b940f99eb7 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/plumed.dat @@ -0,0 +1,6 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cv1: PYCVINTERFACE ATOMS=1,3 IMPORT=distcv CALCULATE=cv +cv2: PYCVINTERFACE ATOMS=1,4 IMPORT=distcv CALCULATE=cv + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/traj.xyz new file mode 100644 index 0000000000..c0e66cf2b2 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-multiplecalls/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 5 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X -5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 -5 diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/Makefile b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/colvar.out.reference new file mode 100644 index 0000000000..90210dc277 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cv1 + 0.000000 1.000000 + 1.000000 2.000000 + 2.000000 3.000000 + 3.000000 4.000000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/config b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/plumed.dat new file mode 100644 index 0000000000..5856aa6929 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/plumed.dat @@ -0,0 +1,5 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cv1: PYCVINTERFACE ATOMS=@mdatoms IMPORT=pycvPerFrame CALCULATE=pydist PREPARE=changeAtom + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/pycvPerFrame.py b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/pycvPerFrame.py new file mode 100644 index 0000000000..a359216a20 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/pycvPerFrame.py @@ -0,0 +1,32 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications + +log = open("pycv.log", "w") + +print("Imported.", file=log) +plumedInit={"Value": plumedCommunications.defaults.COMPONENT,} + +def changeAtom(plmdAction: plumedCommunications.PythonCVInterface): + print(f"pyCVCALLED") + toret = {"setAtomRequest": [0, int(plmdAction.getStep()) + 1]} + # this is just for "fun" + if plmdAction.getStep() == 3: + toret["setAtomRequest"][1] = 1 + print(toret) + return toret + + +def pydist(action: plumedCommunications.PythonCVInterface): + at: np.ndarray = action.getPositions() + d = at[0] - at[1] + d = np.linalg.norm(d) + print(f"{at[0]},{at[1]},{d}", file=log) + + return d diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/traj.xyz new file mode 100644 index 0000000000..ead294112f --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtom/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 0 0 0 +X 0 0 1 +X 0 0 0 +X 0 0 0 +4 +100 100 100 +X 0 0 0 +X 0 0 0 +X 0 0 2 +X 0 0 0 +4 +100 100 100 +X 0 0 0 +X 0 0 0 +X 0 0 0 +X 0 0 3 +4 +100 100 100 +X 0 0 0 +X 0 0 4 +X 0 0 0 +X 0 0 0 diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/Makefile b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/colvar.out.reference new file mode 100644 index 0000000000..90210dc277 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cv1 + 0.000000 1.000000 + 1.000000 2.000000 + 2.000000 3.000000 + 3.000000 4.000000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/config b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/plumed.dat new file mode 100644 index 0000000000..5856aa6929 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/plumed.dat @@ -0,0 +1,5 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cv1: PYCVINTERFACE ATOMS=@mdatoms IMPORT=pycvPerFrame CALCULATE=pydist PREPARE=changeAtom + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/pycvPerFrame.py b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/pycvPerFrame.py new file mode 100644 index 0000000000..92729421ed --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/pycvPerFrame.py @@ -0,0 +1,32 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications + +log = open("pycv.log", "w") + +print("Imported.", file=log) +plumedInit={"Value": plumedCommunications.defaults.COMPONENT,} + +def changeAtom(plmdAction: plumedCommunications.PythonCVInterface): + print(f"pyCVCALLED") + toret = {"setAtomRequest": f"1, {int(plmdAction.getStep()) + 2}"} + # this is just for "fun" + if plmdAction.getStep() == 3: + toret["setAtomRequest"] = "1,2" + print(toret) + return toret + + +def pydist(action: plumedCommunications.PythonCVInterface): + at: np.ndarray = action.getPositions() + d = at[0] - at[1] + d = np.linalg.norm(d) + print(f"{at[0]},{at[1]},{d}", file=log) + + return d diff --git a/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/traj.xyz new file mode 100644 index 0000000000..ead294112f --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-newFrameNewAtomSTR/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 0 0 0 +X 0 0 1 +X 0 0 0 +X 0 0 0 +4 +100 100 100 +X 0 0 0 +X 0 0 0 +X 0 0 2 +X 0 0 0 +4 +100 100 100 +X 0 0 0 +X 0 0 0 +X 0 0 0 +X 0 0 3 +4 +100 100 100 +X 0 0 0 +X 0 0 4 +X 0 0 0 +X 0 0 0 diff --git a/plugins/pycv/regtest/pycvcomm/rt-persistentData/Makefile b/plugins/pycv/regtest/pycvcomm/rt-persistentData/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-persistentData/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-persistentData/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-persistentData/colvar.out.reference new file mode 100644 index 0000000000..972b5473fc --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-persistentData/colvar.out.reference @@ -0,0 +1,5 @@ +#! FIELDS time cv1 + 0.000000 0.000000 + 1.000000 1.000000 + 2.000000 3.000000 + 3.000000 6.000000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-persistentData/config b/plugins/pycv/regtest/pycvcomm/rt-persistentData/config new file mode 100644 index 0000000000..66a5c0dad7 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-persistentData/config @@ -0,0 +1,8 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" + +#this will showcase that you can import a complex module!!! +plumed_regtest_before(){ + cp -r ../pycvPersistentData . +} diff --git a/plugins/pycv/regtest/pycvcomm/rt-persistentData/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-persistentData/plumed.dat new file mode 100644 index 0000000000..1a1d9d6cf0 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-persistentData/plumed.dat @@ -0,0 +1,5 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cv1: PYCVINTERFACE ATOMS=@mdatoms IMPORT=pycvPersistentData CALCULATE=pydist INIT=pyinit + +PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvcomm/rt-persistentData/pycvPersistentData/__init__.py b/plugins/pycv/regtest/pycvcomm/rt-persistentData/pycvPersistentData/__init__.py new file mode 100644 index 0000000000..b6a505fa38 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-persistentData/pycvPersistentData/__init__.py @@ -0,0 +1,33 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import numpy as np +import plumedCommunications +from plumedCommunications.defaults import COMPONENT_NODEV +from sys import stderr as log +#log = open("pycv.log", "w") + +print("Imported pycvPersistentData", file=log) + +def pyinit(plmdAction: plumedCommunications.PythonCVInterface): + print("Calling pyinit", file=log) + plmdAction.log("---Calling pyinit---") + plmdAction.lognl("Logging from Python :)") + print(f"{plmdAction.data=}", file=log) + plmdAction.data["pycv"]=0 + print(f"{plmdAction.data=}", file=log) + return {"Value":COMPONENT_NODEV} + +def pydist(plmdAction: plumedCommunications.PythonCVInterface): + plmdAction.log("Calling pydist: ") + plmdAction.lognl(plmdAction.getStep()) + print("Calling pydist", file=log) + print(f"{plmdAction.data=}, {plmdAction.getStep()=}", file=log) + + plmdAction.data["pycv"]+=plmdAction.getStep() + d=plmdAction.data["pycv"] + return d diff --git a/plugins/pycv/regtest/pycvcomm/rt-persistentData/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-persistentData/traj.xyz new file mode 100644 index 0000000000..ead294112f --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-persistentData/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 0 0 0 +X 0 0 1 +X 0 0 0 +X 0 0 0 +4 +100 100 100 +X 0 0 0 +X 0 0 0 +X 0 0 2 +X 0 0 0 +4 +100 100 100 +X 0 0 0 +X 0 0 0 +X 0 0 0 +X 0 0 3 +4 +100 100 100 +X 0 0 0 +X 0 0 4 +X 0 0 0 +X 0 0 0 diff --git a/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/Makefile b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/colvar.out.reference b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/colvar.out.reference new file mode 100644 index 0000000000..5dd8eb12e0 --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/colvar.out.reference @@ -0,0 +1,10 @@ +#! FIELDS time cvPY +#! SET min_cvPY 0 +#! SET max_cvPY 1.3 + 0.000000 0.000000 + 1.000000 1.000000 + 2.000000 0.700000 + 3.000000 0.400000 + 4.000000 0.100000 + 5.000000 1.100000 + 6.000000 0.800000 diff --git a/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/config b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/plumed.dat b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/plumed.dat new file mode 100644 index 0000000000..f46a9cdf4b --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/plumed.dat @@ -0,0 +1,14 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +ATOMS=1,2 +IMPORT=unitTest +INIT=init +CALCULATE=mypytest +... + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/traj.xyz b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/traj.xyz new file mode 100644 index 0000000000..1ed965943b --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/traj.xyz @@ -0,0 +1,28 @@ +2 +100 100 100 +X 0 0 0 +X 99 0 0 +2 +100 100 100 +X 0 0 0 +X 0 99 0 +2 +100 100 100 +X 0 0 0 +X 0 0 99 +2 +100 100 100 +X 0 0 0 +X 0 99 99 +2 +100 100 100 +X 0 0 0 +X 99 0 99 +2 +100 100 100 +X 0 0 0 +X 99 99 0 +2 +100 100 100 +X 0 0 0 +X 99 99 99 diff --git a/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/unitTest.py b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/unitTest.py new file mode 100644 index 0000000000..580f020f2d --- /dev/null +++ b/plugins/pycv/regtest/pycvcomm/rt-valuePeriodicity/unitTest.py @@ -0,0 +1,17 @@ +import plumedCommunications as PLMD +import numpy + +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + + +def init(action: PLMD.PythonCVInterface): + return {"COMPONENTS": {"val":{"period": ["0", 1.3]}}} + return {"Value": {"period": ["0", "1.3"]}} + + +def mypytest(action: PLMD.PythonCVInterface): + ret = [action.getStep()] + + return ret diff --git a/plugins/pycv/regtest/pycvfunc/Makefile b/plugins/pycv/regtest/pycvfunc/Makefile new file mode 100644 index 0000000000..430b3123ed --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/Makefile @@ -0,0 +1 @@ +include ../scripts/module.make diff --git a/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/Makefile b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/colvar.out.reference b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/colvar.out.reference new file mode 100644 index 0000000000..68d678ab8c --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/colvar.out.reference @@ -0,0 +1,10 @@ +#! FIELDS time fPY.py-difference fPY.py-bringBackInPbc dz +#! SET min_dz 0.0 +#! SET max_dz 1.3 + 0.000000 0.000000 0.000000 0.000000 + 1.000000 0.300000 0.000000 0.000000 + 2.000000 -0.400000 0.600000 0.300000 + 3.000000 -0.100000 0.900000 0.300000 + 4.000000 0.200000 1.200000 0.300000 + 5.000000 0.200000 0.000000 0.000000 + 6.000000 -0.500000 0.500000 0.300000 diff --git a/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/config b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/plumed.dat b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/plumed.dat new file mode 100644 index 0000000000..084470a2d6 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/plumed.dat @@ -0,0 +1,15 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +d12c: DISTANCE ATOMS=1,2 COMPONENTS + +dz: COMBINE ARG=d12c.z PERIODIC=0.0,1.3 + +fPY: ... +PYFUNCTION +IMPORT=unitTest +INIT=initForF +CALCULATE=function +ARG=dz +... + +PRINT FILE=colvar.out ARG=fPY.py-difference,fPY.py-bringBackInPbc,dz diff --git a/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/traj.xyz b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/traj.xyz new file mode 100644 index 0000000000..eff86f6f4d --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/traj.xyz @@ -0,0 +1,35 @@ +3 +100 100 100 +X 0 0 0 +X 99 0 0 +X 99 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 0 +X 0 99 50 +3 +100 100 100 +X 0 0 0 +X 0 0 99 +X 0 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 99 +X 50 99 50 +3 +100 100 100 +X 0 0 0 +X 99 0 99 +X 98 0 50 +3 +100 100 100 +X 0 0 0 +X 99 99 0 +X 99 98 50 +3 +100 100 100 +X 0 0 0 +X 99 99 99 +X 99 99 50 diff --git a/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/unitTest.py b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/unitTest.py new file mode 100644 index 0000000000..b59db34482 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-ArgsMethods/unitTest.py @@ -0,0 +1,22 @@ +import plumedCommunications as PLMD +import numpy + +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +initForF = { + "COMPONENTS": { + "difference": PLMD.defaults.COMPONENT_NODEV, + "bringBackInPbc": PLMD.defaults.COMPONENT_NODEV, + } +} + + +def function(action: PLMD.PythonFunction): + arg = action.arguments() + + return { + "difference": action.difference(0, action.getStep(), arg[0]), + "bringBackInPbc": action.bringBackInPbc(0, arg[0] * action.getStep()), + } diff --git a/plugins/pycv/regtest/pycvfunc/rt-Components/Makefile b/plugins/pycv/regtest/pycvfunc/rt-Components/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-Components/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvfunc/rt-Components/colvar.x.out.reference b/plugins/pycv/regtest/pycvfunc/rt-Components/colvar.x.out.reference new file mode 100644 index 0000000000..4a5a76cb0c --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-Components/colvar.x.out.reference @@ -0,0 +1,8 @@ +#! FIELDS time dc.x fPY.py-x + 0.000000 -1.000000 -1.000000 + 1.000000 0.000000 0.000000 + 2.000000 0.000000 0.000000 + 3.000000 0.000000 0.000000 + 4.000000 -1.000000 -1.000000 + 5.000000 -1.000000 -1.000000 + 6.000000 -1.000000 -1.000000 diff --git a/plugins/pycv/regtest/pycvfunc/rt-Components/colvar.y.out.reference b/plugins/pycv/regtest/pycvfunc/rt-Components/colvar.y.out.reference new file mode 100644 index 0000000000..c9f63e3c51 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-Components/colvar.y.out.reference @@ -0,0 +1,8 @@ +#! FIELDS time dc.y fPY.py-y + 0.000000 0.000000 0.000000 + 1.000000 -1.000000 -1.000000 + 2.000000 0.000000 0.000000 + 3.000000 -1.000000 -1.000000 + 4.000000 0.000000 0.000000 + 5.000000 -1.000000 -1.000000 + 6.000000 -1.000000 -1.000000 diff --git a/plugins/pycv/regtest/pycvfunc/rt-Components/colvar.z.out.reference b/plugins/pycv/regtest/pycvfunc/rt-Components/colvar.z.out.reference new file mode 100644 index 0000000000..c14b32f42f --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-Components/colvar.z.out.reference @@ -0,0 +1,8 @@ +#! FIELDS time dc.z fPY.py-z + 0.000000 0.000000 0.000000 + 1.000000 0.000000 0.000000 + 2.000000 -1.000000 -1.000000 + 3.000000 -1.000000 -1.000000 + 4.000000 -1.000000 -1.000000 + 5.000000 0.000000 0.000000 + 6.000000 -1.000000 -1.000000 diff --git a/plugins/pycv/regtest/pycvfunc/rt-Components/config b/plugins/pycv/regtest/pycvfunc/rt-Components/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-Components/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvfunc/rt-Components/plumed.dat b/plugins/pycv/regtest/pycvfunc/rt-Components/plumed.dat new file mode 100644 index 0000000000..08d4b480ae --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-Components/plumed.dat @@ -0,0 +1,18 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +dc: DISTANCE ATOMS=1,2 COMPONENTS + +fPY: ... +PYFUNCTION +IMPORT=unitTest +INIT=initForF +CALCULATE=function +ARG=dc.x,dc.y,dc.z +... + +PRINT FILE=colvar.x.out ARG=dc.x,fPY.py-x +PRINT FILE=colvar.y.out ARG=dc.y,fPY.py-y +PRINT FILE=colvar.z.out ARG=dc.z,fPY.py-z + + + diff --git a/plugins/pycv/regtest/pycvfunc/rt-Components/traj.xyz b/plugins/pycv/regtest/pycvfunc/rt-Components/traj.xyz new file mode 100644 index 0000000000..eff86f6f4d --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-Components/traj.xyz @@ -0,0 +1,35 @@ +3 +100 100 100 +X 0 0 0 +X 99 0 0 +X 99 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 0 +X 0 99 50 +3 +100 100 100 +X 0 0 0 +X 0 0 99 +X 0 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 99 +X 50 99 50 +3 +100 100 100 +X 0 0 0 +X 99 0 99 +X 98 0 50 +3 +100 100 100 +X 0 0 0 +X 99 99 0 +X 99 98 50 +3 +100 100 100 +X 0 0 0 +X 99 99 99 +X 99 99 50 diff --git a/plugins/pycv/regtest/pycvfunc/rt-Components/unitTest.py b/plugins/pycv/regtest/pycvfunc/rt-Components/unitTest.py new file mode 100644 index 0000000000..686171fd47 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-Components/unitTest.py @@ -0,0 +1,24 @@ +import plumedCommunications as PLMD +import numpy + +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +initForF = { + "COMPONENTS": { + "x": PLMD.defaults.COMPONENT_NODEV, + "y": PLMD.defaults.COMPONENT_NODEV, + "z": PLMD.defaults.COMPONENT_NODEV, + } +} + + +def function(action: PLMD.PythonFunction): + arg = action.arguments() + + return { + "x": arg[0], + "y": arg[1], + "z": arg[2], + } diff --git a/plugins/pycv/regtest/pycvfunc/rt-MDinformations/Makefile b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvfunc/rt-MDinformations/config b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvfunc/rt-MDinformations/plumed.dat b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/plumed.dat new file mode 100644 index 0000000000..36b4fd9dd6 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/plumed.dat @@ -0,0 +1,11 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +fPY: ... +PYFUNCTION +IMPORT=unitTest +... + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvfunc/rt-MDinformations/pytest.log.reference b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/pytest.log.reference new file mode 100644 index 0000000000..ceeb9b0906 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/pytest.log.reference @@ -0,0 +1,21 @@ +action label: fPY +@step 0 +action.getTime()=0.0 +action.getTimeStep()=1.0 +action.isRestart()=False +action.isExchangeStep()=False +@step 1 +action.getTime()=1.0 +action.getTimeStep()=1.0 +action.isRestart()=False +action.isExchangeStep()=False +@step 2 +action.getTime()=2.0 +action.getTimeStep()=1.0 +action.isRestart()=False +action.isExchangeStep()=False +@step 3 +action.getTime()=3.0 +action.getTimeStep()=1.0 +action.isRestart()=False +action.isExchangeStep()=False diff --git a/plugins/pycv/regtest/pycvfunc/rt-MDinformations/traj.xyz b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/traj.xyz new file mode 100644 index 0000000000..c0e66cf2b2 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 5 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X -5 5 0 +4 +100 100 100 +X 5 0 0 +X 0 0 0 +X 0 5 0 +X 0 5 -5 diff --git a/plugins/pycv/regtest/pycvfunc/rt-MDinformations/unitTest.py b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/unitTest.py new file mode 100644 index 0000000000..38e8d5d3fb --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-MDinformations/unitTest.py @@ -0,0 +1,18 @@ +import plumedCommunications as PLMD + +# import plumedUtilities +log = open("pytest.log", "w") + +def myPrint(*args,**kwargs): print(*args,**kwargs, file=log) + +def plumedInit(action: PLMD.PythonFunction): + myPrint(f"action label: {action.label}") + return{"Value": PLMD.defaults.COMPONENT_NODEV,} + +def plumedCalculate(action: PLMD.PythonFunction): + myPrint(f"@step {action.getStep()}") + myPrint(f"{action.getTime()=}") + myPrint(f"{action.getTimeStep()=}") + myPrint(f"{action.isRestart()=}") + myPrint(f"{action.isExchangeStep()=}") + return 0.0 diff --git a/plugins/pycv/regtest/pycvfunc/rt-arguments/Makefile b/plugins/pycv/regtest/pycvfunc/rt-arguments/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-arguments/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvfunc/rt-arguments/colvar.out.reference b/plugins/pycv/regtest/pycvfunc/rt-arguments/colvar.out.reference new file mode 100644 index 0000000000..eb4c8a7633 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-arguments/colvar.out.reference @@ -0,0 +1,8 @@ +#! FIELDS time d1 d2 fPY + 0.000000 1.000000 50.000000 50.000000 + 1.000000 1.000000 50.000000 50.000000 + 2.000000 1.000000 50.000000 50.000000 + 3.000000 1.414214 25.000000 50.000000 + 4.000000 1.414214 25.000000 50.000000 + 5.000000 2.000000 12.500000 50.000000 + 6.000000 1.732051 50.000000 150.000000 diff --git a/plugins/pycv/regtest/pycvfunc/rt-arguments/config b/plugins/pycv/regtest/pycvfunc/rt-arguments/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-arguments/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvfunc/rt-arguments/plumed.dat b/plugins/pycv/regtest/pycvfunc/rt-arguments/plumed.dat new file mode 100644 index 0000000000..062124aef7 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-arguments/plumed.dat @@ -0,0 +1,18 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +d1: DISTANCE ATOMS=1,2 +d2: DISTANCE ATOMS=1,3 + + +fPY: ... +PYFUNCTION +IMPORT=unitTest +INIT=initForF +CALCULATE=function +ARG=d1,d2 +... + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvfunc/rt-arguments/traj.xyz b/plugins/pycv/regtest/pycvfunc/rt-arguments/traj.xyz new file mode 100644 index 0000000000..7df2fa7133 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-arguments/traj.xyz @@ -0,0 +1,35 @@ +3 +100 100 100 +X 0 0 0 +X 99 0 0 +X 0 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 0 +X 0 0 50 +3 +100 100 100 +X 0 0 0 +X 0 0 99 +X 0 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 99 +X 25 0 0 +3 +100 100 100 +X 0 0 0 +X 99 0 99 +X 0 0 25 +3 +100 100 100 +X 0 0 0 +X 0 98 0 +X 12.5 0 0 +3 +100 100 100 +X 0 0 0 +X 99 99 99 +X 0 0 50 diff --git a/plugins/pycv/regtest/pycvfunc/rt-arguments/unitTest.py b/plugins/pycv/regtest/pycvfunc/rt-arguments/unitTest.py new file mode 100644 index 0000000000..d9dc7c4395 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-arguments/unitTest.py @@ -0,0 +1,14 @@ +import plumedCommunications as PLMD +import numpy + +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +initForF={"Value": PLMD.defaults.COMPONENT_NODEV} + +def function(action: PLMD.PythonFunction): + arg = [action.argument(0), + action.argument(1) + ] + return arg[0]*arg[0]*arg[1] \ No newline at end of file diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/Makefile b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/colvar.out.reference b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/colvar.out.reference new file mode 100644 index 0000000000..9dfd7ab2e0 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/colvar.out.reference @@ -0,0 +1,8 @@ +#! FIELDS time fPY + 0.000000 0.000000 + 1.000000 0.000000 + 2.000000 0.000000 + 3.000000 0.000000 + 4.000000 0.000000 + 5.000000 0.000000 + 6.000000 0.000000 diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/config b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/plumed.dat b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/plumed.dat new file mode 100644 index 0000000000..b95108ff9d --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/plumed.dat @@ -0,0 +1,20 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +dc: DISTANCE ATOMS=1,2 COMPONENTS +d: DISTANCE ATOMS=1,2 + + +fPY: ... +PYFUNCTION +IMPORT=unitTest +INIT=initForF +CALCULATE=function +ARG=dc.x,dc.y,dc.z,d +... + + + +PRINT FILE=colvar.out ARG=fPY + + + diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/traj.xyz b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/traj.xyz new file mode 100644 index 0000000000..eff86f6f4d --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/traj.xyz @@ -0,0 +1,35 @@ +3 +100 100 100 +X 0 0 0 +X 99 0 0 +X 99 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 0 +X 0 99 50 +3 +100 100 100 +X 0 0 0 +X 0 0 99 +X 0 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 99 +X 50 99 50 +3 +100 100 100 +X 0 0 0 +X 99 0 99 +X 98 0 50 +3 +100 100 100 +X 0 0 0 +X 99 99 0 +X 99 98 50 +3 +100 100 100 +X 0 0 0 +X 99 99 99 +X 99 99 50 diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/unitTest.py b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/unitTest.py new file mode 100644 index 0000000000..aca9582b59 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsArray/unitTest.py @@ -0,0 +1,14 @@ +import plumedCommunications as PLMD +import numpy + +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +initForF = {"Value": PLMD.defaults.COMPONENT_NODEV} + + +def function(action: PLMD.PythonFunction): + arg = action.arguments() + + return numpy.abs(numpy.sqrt(arg[0] ** 2 + arg[1] ** 2 + arg[2] ** 2) - arg[3]) diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/Makefile b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/colvar.out.reference b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/colvar.out.reference new file mode 100644 index 0000000000..9dfd7ab2e0 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/colvar.out.reference @@ -0,0 +1,8 @@ +#! FIELDS time fPY + 0.000000 0.000000 + 1.000000 0.000000 + 2.000000 0.000000 + 3.000000 0.000000 + 4.000000 0.000000 + 5.000000 0.000000 + 6.000000 0.000000 diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/config b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/plumed.dat b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/plumed.dat new file mode 100644 index 0000000000..b95108ff9d --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/plumed.dat @@ -0,0 +1,20 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +dc: DISTANCE ATOMS=1,2 COMPONENTS +d: DISTANCE ATOMS=1,2 + + +fPY: ... +PYFUNCTION +IMPORT=unitTest +INIT=initForF +CALCULATE=function +ARG=dc.x,dc.y,dc.z,d +... + + + +PRINT FILE=colvar.out ARG=fPY + + + diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/traj.xyz b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/traj.xyz new file mode 100644 index 0000000000..eff86f6f4d --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/traj.xyz @@ -0,0 +1,35 @@ +3 +100 100 100 +X 0 0 0 +X 99 0 0 +X 99 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 0 +X 0 99 50 +3 +100 100 100 +X 0 0 0 +X 0 0 99 +X 0 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 99 +X 50 99 50 +3 +100 100 100 +X 0 0 0 +X 99 0 99 +X 98 0 50 +3 +100 100 100 +X 0 0 0 +X 99 99 0 +X 99 98 50 +3 +100 100 100 +X 0 0 0 +X 99 99 99 +X 99 99 50 diff --git a/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/unitTest.py b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/unitTest.py new file mode 100644 index 0000000000..5e30769949 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-argumentsWithComponents/unitTest.py @@ -0,0 +1,16 @@ +import plumedCommunications as PLMD +import numpy + +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +initForF = {"Value": PLMD.defaults.COMPONENT_NODEV} + + +def function(action: PLMD.PythonFunction): + arg = [] + for i in range(action.nargs): + arg.append(action.argument(i)) + + return numpy.abs(numpy.sqrt(arg[0] ** 2 + arg[1] ** 2 + arg[2] ** 2) - arg[3]) diff --git a/plugins/pycv/regtest/pycvfunc/rt-derivative/Makefile b/plugins/pycv/regtest/pycvfunc/rt-derivative/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-derivative/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvfunc/rt-derivative/colvar.out.reference b/plugins/pycv/regtest/pycvfunc/rt-derivative/colvar.out.reference new file mode 100644 index 0000000000..f1f5820950 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-derivative/colvar.out.reference @@ -0,0 +1,8 @@ +#! FIELDS time d1 d2 fPY mul + 0.000000 1.000000 50.000000 50.000000 50.000000 + 1.000000 1.000000 50.000000 50.000000 50.000000 + 2.000000 1.000000 50.000000 50.000000 50.000000 + 3.000000 1.414214 25.000000 35.355339 35.355339 + 4.000000 1.414214 25.000000 35.355339 35.355339 + 5.000000 2.000000 12.500000 25.000000 25.000000 + 6.000000 1.732051 50.000000 86.602540 86.602540 diff --git a/plugins/pycv/regtest/pycvfunc/rt-derivative/config b/plugins/pycv/regtest/pycvfunc/rt-derivative/config new file mode 100644 index 0000000000..7e13a932fd --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-derivative/config @@ -0,0 +1,11 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" + +plumed_regtest_after() { + { + head -1 deriv | awk '{printf "%s %s %s %s %s-%s\n" , $1, $2,$3,$4,$5,$6 }' + #I'm giving some(a LOT OF) space to the floating point precision + awk 'function abs(v) {return v < 0 ? -v : v} NR>1{print $1, $2, 0.0001 < abs($3-$4) } ' deriv_delta +} diff --git a/plugins/pycv/regtest/pycvfunc/rt-derivative/deriv_delta.reference b/plugins/pycv/regtest/pycvfunc/rt-derivative/deriv_delta.reference new file mode 100644 index 0000000000..a44a09dd13 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-derivative/deriv_delta.reference @@ -0,0 +1,15 @@ +#! FIELDS time parameter mul-fPY +0.000000 0 0 +0.000000 1 0 +1.000000 0 0 +1.000000 1 0 +2.000000 0 0 +2.000000 1 0 +3.000000 0 0 +3.000000 1 0 +4.000000 0 0 +4.000000 1 0 +5.000000 0 0 +5.000000 1 0 +6.000000 0 0 +6.000000 1 0 diff --git a/plugins/pycv/regtest/pycvfunc/rt-derivative/plumed.dat b/plugins/pycv/regtest/pycvfunc/rt-derivative/plumed.dat new file mode 100644 index 0000000000..2f134563ab --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-derivative/plumed.dat @@ -0,0 +1,18 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +d1: DISTANCE ATOMS=1,2 +d2: DISTANCE ATOMS=1,3 + + +fPY: ... +PYFUNCTION +IMPORT=unitTest +INIT=initForF +CALCULATE=function +ARG=d1,d2 +... + +mul: CUSTOM ARG=d1,d2 FUNC=x*y PERIODIC=NO + +PRINT FILE=colvar.out ARG=* +DUMPDERIVATIVES ARG=mul,fPY FILE=deriv \ No newline at end of file diff --git a/plugins/pycv/regtest/pycvfunc/rt-derivative/traj.xyz b/plugins/pycv/regtest/pycvfunc/rt-derivative/traj.xyz new file mode 100644 index 0000000000..7df2fa7133 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-derivative/traj.xyz @@ -0,0 +1,35 @@ +3 +100 100 100 +X 0 0 0 +X 99 0 0 +X 0 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 0 +X 0 0 50 +3 +100 100 100 +X 0 0 0 +X 0 0 99 +X 0 0 50 +3 +100 100 100 +X 0 0 0 +X 0 99 99 +X 25 0 0 +3 +100 100 100 +X 0 0 0 +X 99 0 99 +X 0 0 25 +3 +100 100 100 +X 0 0 0 +X 0 98 0 +X 12.5 0 0 +3 +100 100 100 +X 0 0 0 +X 99 99 99 +X 0 0 50 diff --git a/plugins/pycv/regtest/pycvfunc/rt-derivative/unitTest.py b/plugins/pycv/regtest/pycvfunc/rt-derivative/unitTest.py new file mode 100644 index 0000000000..a0c386bfe1 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-derivative/unitTest.py @@ -0,0 +1,14 @@ +import plumedCommunications as PLMD +import numpy + +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + +initForF={"Value": PLMD.defaults.COMPONENT} + +def function(action: PLMD.PythonFunction): + arg = [action.argument(0), + action.argument(1) + ] + return arg[0]*arg[1], [arg[1], arg[0]] diff --git a/plugins/pycv/regtest/pycvfunc/rt-doc/Makefile b/plugins/pycv/regtest/pycvfunc/rt-doc/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-doc/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvfunc/rt-doc/config b/plugins/pycv/regtest/pycvfunc/rt-doc/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-doc/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvfunc/rt-doc/config.reference b/plugins/pycv/regtest/pycvfunc/rt-doc/config.reference new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-doc/config.reference @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvfunc/rt-doc/plumed.dat b/plugins/pycv/regtest/pycvfunc/rt-doc/plumed.dat new file mode 100644 index 0000000000..04c3517f44 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-doc/plumed.dat @@ -0,0 +1,5 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvdist: PYFUNCTION IMPORT=pyhelp + +#PRINT FILE=colvar.out ARG=* diff --git a/plugins/pycv/regtest/pycvfunc/rt-doc/pyhelp.py b/plugins/pycv/regtest/pycvfunc/rt-doc/pyhelp.py new file mode 100644 index 0000000000..bc2754d199 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-doc/pyhelp.py @@ -0,0 +1,24 @@ +# This is only a partial example. This function does not compute the +# gradient, so it is useless for biasing. See the other regression +# tests for how to auto-grad. + +# And, of course, one should not call slow functions (such as print) +# in the CV calculation. + +import plumedCommunications +import pydoc + +def plumedInit(_): + with open('PythonCVInterface.help.txt', 'w') as f: + h = pydoc.Helper(output=f) + h(plumedCommunications.PythonCVInterface) + with open('plumedCommunications.help.txt', 'w') as f: + h = pydoc.Helper(output=f) + h(plumedCommunications) + with open('plumedCommunications.defaults.help.txt', 'w') as f: + h = pydoc.Helper(output=f) + h(plumedCommunications.defaults) + return {"Value":plumedCommunications.defaults.COMPONENT_NODEV, "ATOMS":"1"} + +def plumedCalculate(_): + return 0 diff --git a/plugins/pycv/regtest/pycvfunc/rt-doc/traj.xyz b/plugins/pycv/regtest/pycvfunc/rt-doc/traj.xyz new file mode 100644 index 0000000000..b3b4e45d61 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-doc/traj.xyz @@ -0,0 +1,24 @@ +4 +100 100 100 +X 5 5 5 +X 4 5 5 +X 5 4 5 +X 5 5 4 +4 +100 100 100 +X 5 5 5 +X 3 5 5 +X 5 3 5 +X 5 5 3 +4 +100 100 100 +X 5 5 5 +X 2 5 5 +X 5 2 5 +X 5 5 2 +4 +100 100 100 +X 5 5 5 +X 1 5 5 +X 5 1 5 +X 5 5 1 diff --git a/plugins/pycv/regtest/pycvfunc/rt-withPYCV/Makefile b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/plugins/pycv/regtest/pycvfunc/rt-withPYCV/colvar.out.reference b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/colvar.out.reference new file mode 100644 index 0000000000..e7a436fa00 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/colvar.out.reference @@ -0,0 +1,10 @@ +#! FIELDS time cvPY fPY +#! SET min_cvPY 0 +#! SET max_cvPY 1.3 + 0.000000 0.000000 0.000000 + 1.000000 1.000000 1.000000 + 2.000000 0.700000 0.490000 + 3.000000 0.400000 0.160000 + 4.000000 0.100000 0.010000 + 5.000000 1.100000 1.210000 + 6.000000 0.800000 0.640000 diff --git a/plugins/pycv/regtest/pycvfunc/rt-withPYCV/config b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/config new file mode 100644 index 0000000000..010fed6f0a --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/config @@ -0,0 +1,3 @@ + +type=driver +arg="--plumed plumed.dat --ixyz traj.xyz" diff --git a/plugins/pycv/regtest/pycvfunc/rt-withPYCV/plumed.dat b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/plumed.dat new file mode 100644 index 0000000000..9fd1278102 --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/plumed.dat @@ -0,0 +1,22 @@ +LOAD GLOBAL FILE=../../../../PythonCVInterface.so + +cvPY: ... +PYCVINTERFACE +ATOMS=1,2 +IMPORT=unitTest +INIT=init +CALCULATE=mypytest +... + +fPY: ... +PYFUNCTION +IMPORT=unitTest +INIT=initForF +CALCULATE=function +ARG=cvPY +... + +PRINT FILE=colvar.out ARG=* + + + diff --git a/plugins/pycv/regtest/pycvfunc/rt-withPYCV/traj.xyz b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/traj.xyz new file mode 100644 index 0000000000..1ed965943b --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/traj.xyz @@ -0,0 +1,28 @@ +2 +100 100 100 +X 0 0 0 +X 99 0 0 +2 +100 100 100 +X 0 0 0 +X 0 99 0 +2 +100 100 100 +X 0 0 0 +X 0 0 99 +2 +100 100 100 +X 0 0 0 +X 0 99 99 +2 +100 100 100 +X 0 0 0 +X 99 0 99 +2 +100 100 100 +X 0 0 0 +X 99 99 0 +2 +100 100 100 +X 0 0 0 +X 99 99 99 diff --git a/plugins/pycv/regtest/pycvfunc/rt-withPYCV/unitTest.py b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/unitTest.py new file mode 100644 index 0000000000..39d3de9c3a --- /dev/null +++ b/plugins/pycv/regtest/pycvfunc/rt-withPYCV/unitTest.py @@ -0,0 +1,22 @@ +import plumedCommunications as PLMD +import numpy + +log = open("pydist.log", "w") + +print("Imported my pydist+.", file=log) + + +def init(action: PLMD.PythonCVInterface): + return {"COMPONENTS": {"val":{"period": ["0", 1.3]}}} + +initForF={"Value": PLMD.defaults.COMPONENT_NODEV} + + +def mypytest(action: PLMD.PythonCVInterface): + ret = [action.getStep()] + + return ret + +def function(action: PLMD.PythonFunction): + arg = [action.argument(0)] + return arg[0]*arg[0] \ No newline at end of file diff --git a/plugins/pycv/requirements.txt b/plugins/pycv/requirements.txt new file mode 100644 index 0000000000..52671c7685 --- /dev/null +++ b/plugins/pycv/requirements.txt @@ -0,0 +1,2 @@ +pybind11 +numpy diff --git a/plugins/pycv/standaloneCompile.sh b/plugins/pycv/standaloneCompile.sh new file mode 100755 index 0000000000..c027bd4d5a --- /dev/null +++ b/plugins/pycv/standaloneCompile.sh @@ -0,0 +1,16 @@ +# Note that in conda libpython3xx is not found in the path returned by ldflags. IMHO it is a bug. +# The workaround is to -L appropriately. Will be fixed here. + +conda_fixup=${CONDA_PREFIX+-L$CONDA_PREFIX/lib} +if [ ! -z "$conda_fixup" ]; then + echo "CONDA_PREFIX is set. Assuming conda and enabling a workaround for missing -L in python3-config --ldflags --embed" +fi + +export PLUMED_MKLIB_CFLAGS="$(python3-config --cflags --embed) $(python -m pybind11 --includes)" + +export PLUMED_MKLIB_LDFLAGS="$(python3-config --ldflags --embed) $conda_fixup" + +echo PLUMED_MKLIB_CFLAGS=$PLUMED_MKLIB_CFLAGS +echo PLUMED_MKLIB_LDFLAGS=$PLUMED_MKLIB_LDFLAGS + +plumed mklib PythonCVInterface.cpp ActionWithPython.cpp PlumedPythonEmbeddedModule.cpp diff --git a/regtest/.gitignore b/regtest/.gitignore index 1515e1a328..8092844aeb 100644 --- a/regtest/.gitignore +++ b/regtest/.gitignore @@ -26,6 +26,7 @@ !/pamm !/isdb !/ves +!/pycv !/funnel !/opes !/tools diff --git a/regtest/scripts/scritps b/regtest/scripts/scritps new file mode 120000 index 0000000000..b94cd1b39a --- /dev/null +++ b/regtest/scripts/scritps @@ -0,0 +1 @@ +scritps \ No newline at end of file