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() invocationPREPARE
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
The minimal plumed input is:
cv1: PYTHONCVIMPORT=hellocould not find this keywordATOMS=1 PRINTcould not find this keywordFILE=colvar.outthe name of the file on which to output these quantitiesARG=*the input for this action is the scalar output from one or more other actions.
this should be paired with the hello.py
file:
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:
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.
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).
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.
cv1: PYCVINTERFACE ...ATOMS=@mdatomscould not find this keywordIMPORT=pycvPersistentDatacould not find this keywordCALCULATE=pydistcould not find this keywordINIT=pyinit ... PRINTcould not find this keywordFILE=colvar.outthe name of the file on which to output these quantitiesARG=*the input for this action is the scalar output from one or more other actions.
The plumedCommunications.PythonCVInterface has a data
attribute that is a dictionary and can be used to store data during the calculations
LOADand this py moduleGLOBALcould not find this keywordFILE=PythonCVInterface.so cvdist: PYCVINTERFACEcompulsory keyword file to be loadedIMPORT=pyhelp PRINTcould not find this keywordFILE=colvar.outthe name of the file on which to output these quantitiesARG=*the input for this action is the scalar output from one or more other actions.
Automatic differentiation and transparent compilation (including to GPU) can be performed via Google's JAX library: see the example below.
The following input tells PLUMED to print the distance between atoms 1 and 4.
cv1: PYTHONCVATOMS=1,4could not find this keywordIMPORT=distcvcould not find this keywordCALCULATE=cv PRINTcould not find this keywordFILE=colvar.outthe name of the file on which to output these quantitiesARG=*the input for this action is the scalar output from one or more other actions.
The file distcv.py
should contain something as follows.
Automatic differentiation and transparent compilation (including to GPU) can be performed via Google's JAX library. In a nutshell, it's sufficient to replace numpy
with jax.numpy
. See the following example.
cv1: PYTHONCVPRINT FILE=colvar.out ARG=*ATOMS=1,2,3could not find this keywordIMPORT=jaxcvcould not find this keywordCALCULATE=anglecould not find this keyword
And, in jaxcv.py
...
There are however limitations, 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)
.
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:
cv1: PYTHONCVATOMS=1,3,4could not find this keywordIMPORT=distcvcould not find this keywordFUNCTION=cvcould not find this keywordCOMPONENTS=d12,d13could not find this keyword
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:
export PLUMED_MKLIB_CFLAGS="$(python3-config --cflags --embed) $(python -m pybind11 --includes)" export PLUMED_MKLIB_LDFLAGS="$(python3-config --ldflags --embed)"
and then compile the shared object:
plumed mklib PythonCVInterface.cpp ActionWithPython.cpp PlumedPythonEmbeddedModule.cpp
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