diff --git a/docker/fedora39-pycv b/docker/fedora39-pycv index d5cd7e0693..e2a0fb36ba 100644 --- a/docker/fedora39-pycv +++ b/docker/fedora39-pycv @@ -17,7 +17,4 @@ RUN source /etc/bashrc \ && cd plugins/pycv \ && ./configurePyCV.sh \ && ln -s $(realpath ../../regtest/scripts) ./regtest/scripts \ - && make check_standalone -# To whom is reading: python > 3.10 gives some problems when using pycv -# from plumed whithin python. SO please remember to chante "check_standalone" -#into "check" to verify that the problem is fixed + && make check diff --git a/plugins/pycv/.gitignore b/plugins/pycv/.gitignore index 0eccc5146d..82878d84b5 100644 --- a/plugins/pycv/.gitignore +++ b/plugins/pycv/.gitignore @@ -1,4 +1,5 @@ /Makefile.conf +pycv_here *.o *.so /env*/ diff --git a/plugins/pycv/FindPlumed.cmake b/plugins/pycv/FindPlumed.cmake index 19315dbd66..cb218f0d5e 100644 --- a/plugins/pycv/FindPlumed.cmake +++ b/plugins/pycv/FindPlumed.cmake @@ -66,10 +66,10 @@ if(NOT Plumed_FOUND) endif() endforeach() endif() - if(${_line} MATCHES ".*-fopenmp.*") - set(Plumed_HAS_MPI + if(${_line} MATCHES ".*-[fq]openmp.*") + set(Plumed_HAS_OPENMP 1 - CACHE INTERNAL "plumed has MPI") + CACHE INTERNAL "plumed has OpenMP") endif() endforeach() diff --git a/plugins/pycv/pyproject.toml b/plugins/pycv/pyproject.toml index 8f6998847c..f72d4153ea 100644 --- a/plugins/pycv/pyproject.toml +++ b/plugins/pycv/pyproject.toml @@ -9,7 +9,7 @@ dependencies = [ "numpy", "pybind11>=2.10.3,<=2.11.1" ] -version = "0.0.1" +version = "0.0.2" description="PyCV support module for plumed" readme = "README.md" authors = [ diff --git a/plugins/pycv/pythontests/test_cv.py b/plugins/pycv/pythontests/test_cv.py index 7d0ddc1631..0d866de276 100644 --- a/plugins/pycv/pythontests/test_cv.py +++ b/plugins/pycv/pythontests/test_cv.py @@ -1,8 +1,7 @@ import unittest import numpy as np -from utilities_for_test import * - +from utilities_for_test import setUpTraj, preparePlumed, cd, create_plumed_var import os THIS_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/plugins/pycv/pythontests/test_cv_calls.py b/plugins/pycv/pythontests/test_cv_calls.py index 4c6086cbf4..219964ab5a 100644 --- a/plugins/pycv/pythontests/test_cv_calls.py +++ b/plugins/pycv/pythontests/test_cv_calls.py @@ -1,7 +1,7 @@ import unittest import numpy as np -from utilities_for_test import * +from utilities_for_test import cd, preparePlumed, create_plumed_var, setUpTraj import os diff --git a/plugins/pycv/pythontests/test_fun_calls.py b/plugins/pycv/pythontests/test_fun_calls.py index 3ba155d062..7736cd67c2 100644 --- a/plugins/pycv/pythontests/test_fun_calls.py +++ b/plugins/pycv/pythontests/test_fun_calls.py @@ -1,7 +1,7 @@ import unittest import numpy as np -from utilities_for_test import * +from utilities_for_test import cd, preparePlumed, create_plumed_var, setUpTraj import os diff --git a/plugins/pycv/src/PythonCVInterface.cpp b/plugins/pycv/src/PythonCVInterface.cpp index 9d41ac5eab..b728ee503f 100644 --- a/plugins/pycv/src/PythonCVInterface.cpp +++ b/plugins/pycv/src/PythonCVInterface.cpp @@ -404,7 +404,7 @@ void PythonCVInterface::registerKeywords( Keywords& keys ) { keys.add("optional","NL_STRIDE","The frequency with which we are updating the atoms in the neighbor list"); //python components 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-"); + keys.addOutputComponent(PYCV_COMPONENTPREFIX.data(),"COMPONENTS","scalar","Each of the components output py the Python code, prefixed by py-"); //python calling keys.add("compulsory","IMPORT","the python file to import, containing the function"); keys.add("compulsory","CALCULATE",PYCV_DEFAULTCALCULATE,"the function to call as calculate method of a CV"); @@ -418,187 +418,191 @@ void PythonCVInterface::registerKeywords( Keywords& keys ) { //TODO: add callable checks!!! -PythonCVInterface::PythonCVInterface(const ActionOptions&ao) ://the catch only applies to pybind11 things +PythonCVInterface::PythonCVInterface(const ActionOptions&ao) try ://the catch only applies to pybind11 things PLUMED_COLVAR_INIT(ao), - ActionWithPython(ao) { - try { - py::gil_scoped_acquire gil; - //Loading the python module - std::string import; - parse("IMPORT",import); - //setting up the calculate function - std::string calculateFunName; - 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); - } + ActionWithPython(ao), + dataContainer([] { + //We need the gil to initialize python objects + py::gil_scoped_acquire gil; + return py::dict(); +}()) { + py::gil_scoped_acquire gil; + //Loading the python module + std::string import; + parse("IMPORT",import); + //setting up the calculate function + std::string calculateFunName; + 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); + 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::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 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::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."); - } + { + 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)); - } + 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); + } 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(); + } - if(atoms.size() !=0 && groupA.size()!=0) { - error("you can choose only between using the neigbourlist OR the atoms"); - } + 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 && 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) { - 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 && 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"); + } - 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) { - 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"); - } + 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) { + pyParse("NL_CUTOFF", initDict, nl_cut); + if (nl_cut <= 0.0) { + error("NL_CUTOFF 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); + pyParse("NL_STRIDE",initDict, nl_st); + if (nl_st <= 0) { + error("NL_STRIDE should be explicitly specified and positive"); } - requestAtoms(nl->getFullAtomList()); - } else { - requestAtoms(atoms); } - - if (getNumberOfComponents()>1) { - log.printf(" it is expected to return dictionaries with %d components\n", - getNumberOfComponents()); + // 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); + } - 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) { - error(e.what()); - //vdbg(e.what()); + 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) { + error(e.what()); + //vdbg(e.what()); } + void PythonCVInterface::prepare() { try { if (nl) { diff --git a/plugins/pycv/src/PythonCVInterface.h b/plugins/pycv/src/PythonCVInterface.h index 23bd60f69f..bf21cae4dd 100644 --- a/plugins/pycv/src/PythonCVInterface.h +++ b/plugins/pycv/src/PythonCVInterface.h @@ -49,7 +49,7 @@ class PythonCVInterface : public Colvar, public ActionWithPython { void calculateMultiComponent(pybind11::object &); void readReturn(const pybind11::object &, Value* ); public: - ::pybind11::dict dataContainer {}; + ::pybind11::dict dataContainer; explicit PythonCVInterface(const ActionOptions&); static void registerKeywords( Keywords& keys ); // active methods: diff --git a/plugins/pycv/src/PythonFunction.cpp b/plugins/pycv/src/PythonFunction.cpp index 6b977f8fa4..7fe17e549d 100644 --- a/plugins/pycv/src/PythonFunction.cpp +++ b/plugins/pycv/src/PythonFunction.cpp @@ -110,7 +110,7 @@ void PythonFunction::registerKeywords( Keywords& keys ) { 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-"); + keys.addOutputComponent(PYCV_COMPONENTPREFIX.data(),"COMPONENTS","scalar","Each of the components output py the Python code, prefixed by py-"); // Why is NOPBC not listed here? }