LCOV - code coverage report
Current view: top level - function - Custom.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 81 83 97.6 %
Date: 2025-04-08 18:07:56 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #include "Custom.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "FunctionShortcut.h"
      25             : #include "FunctionOfScalar.h"
      26             : #include "FunctionOfVector.h"
      27             : #include "FunctionOfMatrix.h"
      28             : #include "tools/OpenMP.h"
      29             : #include "tools/LeptonCall.h"
      30             : 
      31             : namespace PLMD {
      32             : namespace function {
      33             : 
      34             : //+PLUMEDOC FUNCTION CUSTOM
      35             : /*
      36             : Calculate a combination of variables using a custom expression.
      37             : 
      38             : CUSTOM is one of the most useful actions in PLUMED.  This action takes in a list of arguments and then uses
      39             : the [lepton mathematical expression parser](https://simtk.org/projects/lepton) to evaluate a user defined function
      40             : of these input arguments. We can thus use this action in the input below to perform a metadynamics
      41             : simulation using the difference between two distances as a CV.
      42             : 
      43             : ```plumed
      44             : dAB: DISTANCE ATOMS=10,12
      45             : dAC: DISTANCE ATOMS=10,15
      46             : diff: CUSTOM ARG=dAB,dAC FUNC=y-x PERIODIC=NO
      47             : # notice: the previous line could be replaced with the following
      48             : # diff: COMBINE ARG=dAB,dAC COEFFICIENTS=-1,1
      49             : METAD ARG=diff SIGMA=0.1 HEIGHT=0.5 BIASFACTOR=10 PACE=100
      50             : ```
      51             : 
      52             : The particular function that should be evaluated from the input arguments is specified using the `FUNC` keyword.
      53             : The function provided to the `FUNC` keyword is written in terms of `x` and `y` in the input above.  `x` here deonetes the
      54             : first argument provided to the ARG keyword, `dAB`, while `y` is the second argument provided to the `ARG` keyword, `dAC`.
      55             : 
      56             : ## The VAR keyword
      57             : 
      58             : If you wish, you can rewrite the example input above more transparantly as:
      59             : 
      60             : ```plumed
      61             : dAB: DISTANCE ATOMS=10,12
      62             : dAC: DISTANCE ATOMS=10,15
      63             : diff: CUSTOM ARG=dAB,dAC VAR=dAB,dAC FUNC=dAC-dAB PERIODIC=NO
      64             : METAD ARG=diff SIGMA=0.1 HEIGHT=0.5 BIASFACTOR=10 PACE=100
      65             : ```
      66             : 
      67             : By using the `VAR` keyword here we ensure that we can use the labels of the arguments in the mathematical expression that
      68             : we provide to the `FUNC` argument.  Notice that, if you have four or more arguments you must use the `VAR` keyword.  With
      69             : three or less arguments the `VAR` keyword can be ommitted and the symbols `x`, `y` and `z` can be used to denote the first,
      70             : second and third arguments respectively.
      71             : 
      72             : The following input illustrates a case where using the `VAR` keyword is essential.  This input tells PLUMED to
      73             : print the angle between the vector connecting atoms 1,2 and the vector connecting atoms 2,3.
      74             : 
      75             : ```plumed
      76             : d1: DISTANCE ATOMS=1,2 COMPONENTS
      77             : d2: DISTANCE ATOMS=2,3 COMPONENTS
      78             : theta: CUSTOM ...
      79             :   ARG=d1.x,d1.y,d1.z,d2.x,d2.y,d2.z
      80             :   VAR=ax,ay,az,bx,by,bz
      81             :   FUNC=acos((ax*bx+ay*by+az*bz)/sqrt((ax*ax+ay*ay+az*az)*(bx*bx+by*by+bz*bz)))
      82             :   PERIODIC=NO
      83             : ...
      84             : 
      85             : PRINT ARG=theta
      86             : ```
      87             : 
      88             : ## Functions and constants
      89             : 
      90             : The previous examples demonstrates how CUSTOM can be used to evaluate mathematical expressions that involve taking powers (^), adding (+),
      91             : multiplying (*), dividing (/) and subtracting (-) variables and can control the order in which operations are performed by using parenthesis.
      92             : In addition to these basic binary operations you can also use a range of mathematical functions in your expressions.  The following table lists all the mathematical functions
      93             : that you can use in in the input to the `FUNC` keyword for CUSTOM.
      94             : 
      95             : | Function | Description |
      96             : |:--------:|:------------|
      97             : | sqrt(x)       | The square root of x |
      98             : | exp(x)        | The exponential of x i.e. $e^{x}$ |
      99             : | log(x)        | The natural logarithm of x |
     100             : | sin(x)        | The sine of x |
     101             : | cos(x)        | The cosine of x |
     102             : | sec(x)        | The reciprocal of the cosine of $x$. $\frac{1}{\cos(x)}$ |
     103             : | csc(x)        | The reciprocal of the sine of $x$. $\frac{1}{\sin(x)}$ |
     104             : | tan(x)        | The tangent of x i.e. $\frac{\sin(x)}{\cos(x)}$ |
     105             : | cot(x)        | The reciprocal of the tangent of $x$. $\frac{1}{\tan(x)}$ |
     106             : | asin(x)       | The principal arc sine of x. Returns $-\frac{\pi}{2} \le y \le \frac{\pi}{2}$ which gives $x = \sin(y)$ |
     107             : | acos(x)       | The principal arc cosine of x. Returns $0 \le y \le \pi$ which gives $x=\cos(y)$ |
     108             : | atan(x)       | The principal arc tangent of x.  Returns $-\frac{\pi}{2} \le y \le \frac{\pi}{2}$ which gives $x = \tan(y)$ |
     109             : | atan2(x,y)    | The principal arg tangent of $\frac{x}{y}$.  Returns $-\pi \le z \le \pi$ which gives $\frac{x}{y} = \tan(z)$ |
     110             : | sinh(x)       | The hyperbolic sine of $x$ |
     111             : | cosh(x)       | the hyperbolic cosine of $x$ |
     112             : | tanh(x)       | The hyperbolic tangent of $x$ |
     113             : | erf(x)        | The [error function](https://en.wikipedia.org/wiki/Error_function) $\frac{2}{\sqrt{\pi}}\int_0^x e^{-t^2} \textrm{d}t$ |
     114             : | erfc(x)       | The complementary error function $1-\frac{2}{\sqrt{\pi}}\int_0^x e^{-t^2} \textrm{d}t$ |
     115             : | step(x)       | 1 if $x \ge 0$ and 0 otherwise |
     116             : | delta(x)      | inf if $x=0$ and 0 otherwise |
     117             : | nandelta(x)   | nan if $x=0$ and 0 otherwise |
     118             : | square(x)     | The square of x i.e. $x^2$ |
     119             : | cube(x)       | The cube of x i.e. $x^3$ |
     120             : | recip(x)      | The reciprocal of x i.e. $\frac{1}{x}$ |
     121             : | min(x,y)      | If $x<y$ this function returns $x$.  If $y\le x$ this function returns $y$ |
     122             : | max(x,y)      | If $x>y$ this function returns $x$.  If $y\ge x$ this function returns $y$ |
     123             : | abs(x)        | The absolute value of x |
     124             : | floor(x)      | The largest integer that is less than $x$ |
     125             : | ceil(x)       | The smallest integer that is greater than $x$ |
     126             : | select(x,y,z) | If $x==0$ this returns $z$ otherwise this returns $y$ |
     127             : | acot(x)       | Returns the value of $-\frac{\pi}{2} \le y \le \frac{\pi}{2}$ which gives $\frac{1}{x}=\tan(y)$ |
     128             : | asec(x)       | Returns the value of $0 \le y \le \pi$ which gives $\frac{1}{x}=\cos(y)$ |
     129             : | acsc(x)       | Returns the value of $-\frac{\pi}{2} \le y \le \frac{\pi}{2}$ which gives $\frac{1}{x}=\sin(y)$ |
     130             : | coth(x)       | The recipricoal of the hyperbolic tangent of $x$. $\frac{1}{\tanh(x)}$ |
     131             : | sech(x)       | The recipricoal of the hyperbolic cosine of $x$. $\frac{1}{\cosh(x)}$ |
     132             : | csch(x)       | The recipricoal of the hyperbolic sine of $x$. $\frac{1}{\sinh(x)}$ |
     133             : | asinh(x)      | The nonnegative area hyperbolic sine of $x$. Returns $y$ which gives $x=\sinh(y)$ |
     134             : | acosh(x)      | The nonnegative area hyperbolic cosine of $x$. Returns $0\le y \le \infty$ which gives $x=\cosh(y)$ |
     135             : | atanh(x)      | The nonnegative area hyperbolic tangent of $-1 \le x \le 1$.  Returns $y$ which gives $x=\tanh(y)$. |
     136             : | acoth(x)      | The inverse hyperbolic tangent of $x$ is calculated as $\frac{1}{2}\ln\left( \frac{x+1}{x-1} \right)$ |
     137             : | asech(x)      | The inverse hyperbolic secant of $x$ is calculated as $\log\left( \sqrt{\frac{1}{x}-1}\sqrt{\frac{1}{x}+1} + \frac{1}{x}\right)$ |
     138             : | acsch(x)      | The inverse hyperbolic cosecant of $x$ is calculated as $\log\left(\frac{1}{x}+\sqrt{\frac{1}{x^2}+1}\right)$ |
     139             : 
     140             : Notice, that you can also incorporate the following constants in the expressions that are used in the input to FUNC:
     141             : 
     142             : | Symbol | Description |
     143             : | :----- |:------------|
     144             : | `e` | Euler's number - the base for the natural logarithm |
     145             : | `log2e` | $1 / \log(2)$ |
     146             : | `log10e` | $1 / \log(10)$ |
     147             : | `ln2` | $\log(2)$ |
     148             : | `ln10` | $\log(10)$ |
     149             : | `pi`   | the circle constant $\pi$ |
     150             : | `pi_2` | $\pi / 2$ |
     151             : | `pi_4` | $\pi / 4$ |
     152             : | `sqrt2 | $\sqrt(2)$ |
     153             : | `sqrt1_2 ` | $\sqrt(0.5)$ |
     154             : 
     155             : The way one of these constants can be used in an expression is illustrated in the following example:
     156             : 
     157             : ```plumed
     158             : d: DISTANCE ATOMS=1,2
     159             : # This function is evaluating the area of the circle whose radius is
     160             : # given by the distance between atoms 1 and 2.
     161             : c: CUSTOM ARG=d FUNC=pi*x*x PERIODIC=NO
     162             : PRINT ARG=c FILE=colvar
     163             : ```
     164             : 
     165             : ## The step function
     166             : 
     167             : The `step` operation (that is the Heaviside function) from the table above allow you to use if clauses in CUSTOM actions.
     168             : As discussed in the table `step(x)` is 1 when `x` is positive and `0` when `x` is negative.  So the function `step(1-x)` is
     169             : 1 if x is less than one and zero otherwise.
     170             : 
     171             : Using the `step` operation multiple times in a function allows you to perform logical operations.  For example, the equivalent of the AND operator
     172             : is the product so, for example, `step(1.0-x)*step(x-0.5)` is only equal to 1 when x is greater than 0.5 AND less than 1.0.
     173             : By a similar logic we can use the function `1-step(1.0-x)*step(x-0.5)` to create a value that is 1 if x is less than 0.5 OR
     174             : greater than 1.0.
     175             : 
     176             : The example below illustrtes how we can put these ideas of using the `step` operation into practise.
     177             : 
     178             : ```plumed
     179             : d: DISTANCE ATOMS=10,15
     180             : m: CUSTOM ARG=d FUNC=0.5*step(0.5-x)+x*step(x-0.5) PERIODIC=NO
     181             : # check the function you are applying:
     182             : PRINT ARG=d,m FILE=checkme
     183             : RESTRAINT ARG=d AT=0.5 KAPPA=10.0
     184             : ```
     185             : 
     186             : The meaning of the function `0.5*step(0.5-x)+x*step(x-0.5)` in this example is:
     187             : - If x<0.5 (step(0.5-x)!=0) use 0.5
     188             : - If x>0.5 (step(x-0.5)!=0) use x
     189             : Notice that the same result can be achieved by using [UPPER_WALLS](UPPER_WALLS.md)
     190             : However, with CUSTOM you can create much more complex definitions.
     191             : 
     192             : Notice that we can apply a force on the value `m` by using the [RESTRAINT](RESTRAINT.md) command as the function we defined in the expression that was passed to the `FUNC` keyword is continuous.
     193             : In general, however, you must be careful when using the `step`, `delta`, `nandelta` and `select` functions as you can easily write expression for
     194             : discontinuous functions by using these operations. If you want to check if a function you have created using `step`
     195             : is continuous you can easily plot the function in gnuplot by using a commands like those shown below
     196             : 
     197             : ````
     198             : # this allow to step function to be used in gnuplot:
     199             : gnuplot> step(x)=0.5*(erf(x*10000000)+1)
     200             : # here you can test your function
     201             : gnuplot> p 0.5*step(0.5-x)+x*step(x-0.5)
     202             : ````
     203             : 
     204             : ## Using TIME as a variable
     205             : 
     206             : Notice that you can use CUSTOM to implement a [MOVINGRESTRAINT](MOVINGRESTRAINT.md) as shown below.
     207             : 
     208             : ```plumed
     209             : t: TIME
     210             : d: DISTANCE ATOMS=1,2
     211             : f: CUSTOM ARG=d,t FUNC=100*(x-((0.2-0.1)*y/100))^2 PERIODIC=NO
     212             : BIASVALUE ARG=f
     213             : ```
     214             : 
     215             : In a 100~ps simulation that was run with this input the distance beetween atom 1 and 2 would be forced to increase from 0.1 to 0.2 nm.
     216             : 
     217             : ## Using CUSTOM in shortcuts
     218             : 
     219             : The CUSTOM action is used in many of the [shortcut actions](shortcuts.md) that are implemented in PLUMED. We think that using this action in these places is beneficial as it ensures that the mathematical
     220             : expressions that are used in the method are visible in the log. We have found that there are many actions that
     221             : are used in relatively few papers. When implementing these actions we think that sharing implementations of these methods that are comprehensible is more important than sharing methods that are
     222             : fast.
     223             : 
     224             : As an example of why this is useful consider some of the variants of the DISTANCE keyword that were present in PLUMED 1.3. These variants allowed you to compute the distance between a point and a line defined by
     225             : two other points or the progression along that line.  In PLUMED 2.10 we can implement these variables using the following input file:
     226             : 
     227             : ```plumed
     228             : # take center of atoms 1 to 10 as reference point 1
     229             : p1: CENTER ATOMS=1-10
     230             : # take center of atoms 11 to 20 as reference point 2
     231             : p2: CENTER ATOMS=11-20
     232             : # take center of atoms 21 to 30 as reference point 3
     233             : p3: CENTER ATOMS=21-30
     234             : 
     235             : # compute distances
     236             : d12: DISTANCE ATOMS=p1,p2
     237             : d13: DISTANCE ATOMS=p1,p3
     238             : d23: DISTANCE ATOMS=p2,p3
     239             : 
     240             : # compute progress variable of the projection of point p3
     241             : # along the vector joining p1 and p2
     242             : # notice that progress is measured from the middle point
     243             : onaxis: CUSTOM ARG=d13,d23,d12 FUNC=(0.5*(y^2-x^2)/z) PERIODIC=NO
     244             : 
     245             : # compute between point p3 and the vector joining p1 and p2
     246             : fromaxis: CUSTOM ARG=d13,d23,d12,onaxis VAR=x,y,z,o FUNC=(0.5*(y^2+x^2)-o^2-0.25*z^2) PERIODIC=NO
     247             : 
     248             : PRINT ARG=onaxis,fromaxis
     249             : ```
     250             : 
     251             : The equations in this input were also used to combine [RMSD](RMSD.md) values from different snapshots of a protein so as to define
     252             : progression (S) and distance (Z) variables in the paper that is cited in the bibliography.  We can understand how these expressions
     253             : are derived by noting that $x$, $y$ and $z$ are the distances between atoms 1 and 3, 2 and 3 and 1 and 2 respectively.  The projection
     254             : of the vector connecting atom 1 to atom 3 onto the vector connecting atom 1 to atom 2 is thus $x\cos(\theta)$, where theta is the angle
     255             : between the vector connecting atoms 1 and 3 and the vector connecting atoms 1 and 2.  We can arrive at the following expression for $x\cos(\theta)$
     256             : by rearranging the cosine rule:
     257             : 
     258             : $$
     259             : x\cos(\theta) = \frac{y^2 - x^2}{z} - \frac{z}{2}
     260             : $$
     261             : 
     262             : Notice that the value called `onaxis` in the above input is thus $o=x\cos(\theta) + \frac{z}{2}$.  Adding the factor of $\frac{z}{2}$ ensures that the origin
     263             : is at the center of the bond connecting atom 1 to atom 2.
     264             : 
     265             : The value `fromaxis` measures the square of the distance from the the line.  It is calculated using pythagoras theorem as follows:
     266             : 
     267             : $$
     268             : f^2 = y^2 - x^2\cos^2(\theta)
     269             : $$
     270             : 
     271             : Inserting $x\cos(\theta) = o - \frac{z}{2}$ into this expression gives:
     272             : 
     273             : $$
     274             : f^2 = y^2 - (o -\frac{z}{2})^2 = y^2 - o^2 + oz - \frac{z^2}{4}
     275             : $$
     276             : 
     277             : Inserting the fact that $oz = \frac{y^2 - x^2}{2}$, which comes from the expression for $o$ that was used to calculate `onaxis`, gets us to the expression that is used to calculate `fromaxis`.
     278             : 
     279             : ## CUSTOM with vector arguments
     280             : 
     281             : The examples above have shown how CUSTOM operates when the input arguments are scalars. You can also pass vectors in the argument to a CUSTOM action. The function you define will then
     282             : be applied to each element of the vector in turn.  So, for example in the input below a vector that contains three angles is passed to the CUSTOM action. The CUSTOM action calculates the
     283             : cosine of these three angles and outputs them in a three dimensional vector called `c` that is printed to the colvar file.
     284             : 
     285             : ```plumed
     286             : a: ANGLE ATOMS1=1,2,3 ATOMS2=4,5,6 ATOMS3=7,8,9
     287             : c: CUSTOM ARG=a FUNC=cos(x) PERIODIC=NO
     288             : PRINT ARG=c FILE=colvar
     289             : ```
     290             : 
     291             : You are not confined to passing a single vector to CUSTOM. The input below shows what you can do by pass two vectors with the same numbers of elements. The first element of the vector
     292             : output by the CUSTOM action here contains the projection of the vector connecting atom 1 and 2 on the vector connecting atom 2 and 3, the second element contains the projection of the
     293             : vector connecting atoms 4 and 5 on the vector connecting atoms 5 and 6 and the final element contains the projection of the vector connecting atoms 7 and 8 on the vector connecting atoms
     294             : 8 and 9.
     295             : 
     296             : ```plumed
     297             : d: DISTANCE ATOMS1=1,2 ATOMS2=4,5 ATOMS3=7,8
     298             : a: ANGLE ATOMS1=1,2,3 ATOMS2=4,5,6 ATOMS3=7,8,9
     299             : c: CUSTOM ARG=d,a FUNC=x*cos(y) PERIODIC=NO
     300             : PRINT ARG=c FILE=colvar
     301             : ```
     302             : 
     303             : Notice, when we multiply two vectors in CUSTOM the output is a vector.  This product that emerges from using a CUSTOM action is __not__ the scalar or cross product of the input vectors.
     304             : 
     305             : Lastly, notice that you can pass a mixture of scalars and vectors in the input to a CUSTOM action as shown below.
     306             : 
     307             : ```plumed
     308             : d: DISTANCE ATOMS=1,2
     309             : a: ANGLE ATOMS1=1,2,3 ATOMS2=1,2,4 ATOMS3=1,2,5 ATOMS4=1,2,6
     310             : c: CUSTOM ARG=d,a FUNC=x*cos(y) PERIODIC=NO
     311             : PRINT ARG=c FILE=colvar
     312             : ```
     313             : 
     314             : The multiplication of a scalar by a vector in the above input is done in [the usual way](https://en.wikipedia.org/wiki/Scalar_multiplication).
     315             : Similarly, dividing a vector by a scalar is equivalent to multiplying the vector by the reciprocal of the scalar.  If you write an expression that adds or subtract a
     316             : scalar from a vector addition is performed.  To understand why consider the following example that adds the constant `c` to the input vector of distances `d`. The
     317             : result of this operation is a vector `f` that contains the three input distances from `d` with 23 added to each of them.
     318             : 
     319             : ```plumed
     320             : c: CONSTANT VALUE=23
     321             : d: DISTANCE ATOMS1=1,2 ATOMS2=4,5 ATOMS3=7,8
     322             : f: CUSTOM ARG=d,c FUNC=x+y PERIODIC=NO
     323             : PRINT ARG=f FILE=colvar
     324             : ```
     325             : 
     326             : ## CUSTOM with matrix arguments
     327             : 
     328             : You can also pass matrices in the argument to a CUSTOM action. These input matrices are treated similarly to input vectors. In other words, any function you define
     329             : is applied to each element of the matrix in turn so if the input matrix is $N \times $M$ the output matrix is also $N \times M$.  The following example illustrates how you
     330             : can use this functionality to calculate all the angles between a set of bond vectors.
     331             : 
     332             : ```plumed
     333             : # Calculate the vectors connecting four atoms
     334             : d: DISTANCE COMPONENTS ATOMS1=1,2 ATOMS2=3,4 ATOMS3=5,6 ATOMS4=7,8
     335             : # Calculate the norm of these four vectors
     336             : norm: CUSTOM ARG=d.x,d.y,d.z FUNC=sqrt(x*x+y*y+z*z) PERIODIC=NO
     337             : # Now calculate the directors of the vectors
     338             : norm_x: CUSTOM ARG=d.x,norm FUNC=x/y PERIODIC=NO
     339             : norm_y: CUSTOM ARG=d.y,norm FUNC=x/y PERIODIC=NO
     340             : norm_z: CUSTOM ARG=d.z,norm FUNC=x/y PERIODIC=NO
     341             : # And combine all these directors in a matrix
     342             : stack: VSTACK ARG=norm_x,norm_y,norm_z
     343             : # Now calculate the matrix of dot products between directors (these are the cosines of the angles)
     344             : stackT: TRANSPOSE ARG=stack
     345             : cosa: MATRIX_PRODUCT ARG=stack,stackT
     346             : # And finally get the 4x4 matrix of angles and print it to a file
     347             : angles: CUSTOM ARG=cosa FUNC=acos(x) PERIODIC=NO
     348             : PRINT ARG=angles FILE=colvar
     349             : ```
     350             : 
     351             : Notice that you can pass multiple $N\times M$ matrices in the input to a CUSTOM action as illustrated in the example below:
     352             : 
     353             : ```plumed
     354             : c1: CONSTANT VALUES=2,3,4,5 NROWS=2 NCOLS=2
     355             : c2: CONSTANT VALUES=1,0,0,1 NROWS=2 NCOLS=2
     356             : f: CUSTOM ARG=c1,c2 FUNC=x*y PERIODIC=NO
     357             : PRINT ARG=f FILE=colvar
     358             : ```
     359             : 
     360             : The four by four matrix that is calculated by the custom action in this input is given by:
     361             : 
     362             : $$
     363             : f = \left(
     364             : \begin{matrix}
     365             : 2 & 0 \\
     366             : 0 & 5
     367             : \end{matrix}
     368             : \right)
     369             : $$
     370             : 
     371             : which is the element-wise [Hadamard product](https://en.wikipedia.org/wiki/Hadamard_product_(matrices)) and __not__ the matrix product.
     372             : By a similar token if you have a custom command that takes two matrices in input and use `FUNC=x/y` the Hadamard product between the matrix
     373             : x and a matrix that contains the reciprocals of each of the elements in y is computed.  If you wish to calcalculate the
     374             : [product of two matrices](https://en.wikipedia.org/wiki/Matrix_multiplication) you should use the [MATRIX_PRODUCT](MATRIX_PRODUCT.md) comamnd.
     375             : Similarly, if you want to calculate the product of a matrix and a vector you should use the [MATRIX_VECTOR_PRODUCT](MATRIX_VECTOR_PRODUCT.md)
     376             : command.
     377             : 
     378             : Lastly, note that you can pass a mixture of scalars and $N\times M$ matrices in the input to a CUSTOM command. As with vectors, you can think of
     379             : any scalars you pass as being converted into $N\times M$ matrix in which every element is equal to the input scalar.
     380             : 
     381             : ## CUSTOM with grid arguments
     382             : 
     383             : CUSTOM will also accept a function on a grid in input.  You might use this feature if you want to calculate a free energy from a histogram
     384             : using $F(s) = -k_BT\log[H(s)]$ as illustrated in the input below:
     385             : 
     386             : ```plumed
     387             : x: DISTANCE ATOMS=1,2
     388             : hA: HISTOGRAM ARG=x GRID_MIN=0.0 GRID_MAX=3.0 GRID_BIN=100 BANDWIDTH=0.1
     389             : fE: CUSTOM ARG=hA FUNC=-2.5*log(x) PERIODIC=NO
     390             : DUMPGRID ARG=fE FILE=fes.dat
     391             : ```
     392             : 
     393             : Another way that this functonality is routinely used in PLUMED is in calculating the density field for a symmetry function. The example below shows how you
     394             : would do this in practise. The final output here is a function on a grid that tells you the average value of the FCC order parameter at each point in the cell.
     395             : 
     396             : ```plumed
     397             : # Evaluate the FCC order parameter for all of the atoms
     398             : fcc: FCCUBIC SPECIES=1-5184 SWITCH={CUBIC D_0=1.2 D_MAX=1.5} ALPHA=27
     399             : # Calculate the distance between each atom and the origin on atom 1
     400             : dens_dist: DISTANCES ORIGIN=1 ATOMS=fcc COMPONENTS
     401             : # Do a KDE using the FCC order parameters for the weights of each gaussian and the positions of the atoms as the centers
     402             : dens_numer: KDE HEIGHTS=fcc_n ARG=dens_dist.x,dens_dist.y,dens_dist.z GRID_BIN=14,14,28 BANDWIDTH=1.0,1.0,1.0
     403             : # Estimate the density of atoms at each point in the box
     404             : ones: ONES SIZE=5184
     405             : dens_denom: KDE ARG=dens_dist.x,dens_dist.y,dens_dist.z GRID_BIN=14,14,28 HEIGHTS=ones BANDWIDTH=1.0,1.0,1.0
     406             : # Now calculate the average value of the order parameter at each point in the box
     407             : dens: CUSTOM ARG=dens_numer,dens_denom FUNC=x/y PERIODIC=NO
     408             : DUMPCUBE ARG=dens FILE=dens.cube FMT=%8.4f
     409             : ```
     410             : 
     411             : */
     412             : //+ENDPLUMEDOC
     413             : 
     414             : //+PLUMEDOC FUNCTION MATHEVAL_SCALAR
     415             : /*
     416             : Calculate a function of a set of input scalars
     417             : 
     418             : See \ref MATHEVAL
     419             : 
     420             : \par Examples
     421             : 
     422             : */
     423             : //+ENDPLUMEDOC
     424             : 
     425             : //+PLUMEDOC FUNCTION CUSTOM_SCALAR
     426             : /*
     427             : Calculate a function of a set of input scalars
     428             : 
     429             : See \ref CUSTOM
     430             : 
     431             : \par Examples
     432             : 
     433             : */
     434             : //+ENDPLUMEDOC
     435             : 
     436             : //+PLUMEDOC FUNCTION MATHEVAL_VECTOR
     437             : /*
     438             : Calculate a function of a set of input vectors elementwise
     439             : 
     440             : See \ref MATHEVAL
     441             : 
     442             : \par Examples
     443             : 
     444             : */
     445             : //+ENDPLUMEDOC
     446             : 
     447             : //+PLUMEDOC FUNCTION CUSTOM_VECTOR
     448             : /*
     449             : Calculate a function of a set of input vectors elementwise
     450             : 
     451             : See \ref CUSTOM
     452             : 
     453             : \par Examples
     454             : 
     455             : */
     456             : //+ENDPLUMEDOC
     457             : 
     458             : //+PLUMEDOC COLVAR CUSTOM_MATRIX
     459             : /*
     460             : Calculate an arbitrary function piecewise for one or multiple input matrices.
     461             : 
     462             : \par Examples
     463             : 
     464             : */
     465             : //+ENDPLUMEDOC
     466             : 
     467             : //+PLUMEDOC COLVAR MATHEVAL_MATRIX
     468             : /*
     469             : Calculate an arbitrary function piecewise for one or multiple input matrices.
     470             : 
     471             : \par Examples
     472             : 
     473             : */
     474             : //+ENDPLUMEDOC
     475             : 
     476             : typedef FunctionShortcut<Custom> CustomShortcut;
     477             : PLUMED_REGISTER_ACTION(CustomShortcut,"CUSTOM")
     478             : PLUMED_REGISTER_ACTION(CustomShortcut,"MATHEVAL")
     479             : typedef FunctionOfScalar<Custom> ScalarCustom;
     480             : PLUMED_REGISTER_ACTION(ScalarCustom,"CUSTOM_SCALAR")
     481             : PLUMED_REGISTER_ACTION(ScalarCustom,"MATHEVAL_SCALAR")
     482             : typedef FunctionOfVector<Custom> VectorCustom;
     483             : PLUMED_REGISTER_ACTION(VectorCustom,"CUSTOM_VECTOR")
     484             : PLUMED_REGISTER_ACTION(VectorCustom,"MATHEVAL_VECTOR")
     485             : typedef FunctionOfMatrix<Custom> MatrixCustom;
     486             : PLUMED_REGISTER_ACTION(MatrixCustom,"CUSTOM_MATRIX")
     487             : PLUMED_REGISTER_ACTION(MatrixCustom,"MATHEVAL_MATRIX")
     488             : 
     489             : //+PLUMEDOC FUNCTION MATHEVAL
     490             : /*
     491             : An alias to the CUSTOM function that can also be used to calaculate combinations of variables using a custom expression.
     492             : 
     493             : The documentation for this action is identical to that for [CUSTOM](CUSTOM.md).  You can thus use it to evaluate a evaluate an arbitrary function as in the following example input:
     494             : 
     495             : ```plumed
     496             : dAB: DISTANCE ATOMS=10,12
     497             : dAC: DISTANCE ATOMS=10,15
     498             : diff: MATHEVAL ARG=dAB,dAC FUNC=y-x PERIODIC=NO
     499             : # notice: the previous line could be replaced with the following
     500             : # diff: COMBINE ARG=dAB,dAC COEFFICIENTS=-1,1
     501             : METAD ARG=diff SIGMA=0.1 HEIGHT=0.5 BIASFACTOR=10 PACE=100
     502             : ```
     503             : 
     504             : This alias is kept in order to maintain compatibility with previous PLUMED versions.
     505             : However, notice that as of PLUMED 2.5 the libmatheval library is not linked anymore,
     506             : and that the MATHEVAL action evaluates functions [the Lepton library](https://simtk.org/projects/lepton).
     507             : 
     508             : \par Examples
     509             : 
     510             : Just replace \ref CUSTOM with \ref MATHEVAL.
     511             : 
     512             : \plumedfile
     513             : d: DISTANCE ATOMS=10,15
     514             : m: MATHEVAL ARG=d FUNC=0.5*step(0.5-x)+x*step(x-0.5) PERIODIC=NO
     515             : # check the function you are applying:
     516             : PRINT ARG=d,m FILE=checkme
     517             : RESTRAINT ARG=d AT=0.5 KAPPA=10.0
     518             : \endplumedfile
     519             : (see also \ref DISTANCE, \ref PRINT, and \ref RESTRAINT)
     520             : 
     521             : */
     522             : //+ENDPLUMEDOC
     523             : 
     524       10770 : void Custom::registerKeywords(Keywords& keys) {
     525       10770 :   keys.use("PERIODIC");
     526       10770 :   keys.add("compulsory","FUNC","the function you wish to evaluate");
     527       10770 :   keys.add("optional","VAR","the names to give each of the arguments in the function.  If you have up to three arguments in your function you can use x, y and z to refer to them.  Otherwise you must use this flag to give your variables names.");
     528       21540 :   keys.setValueDescription("scalar/vector/matrix/grid","an arbitrary function");
     529       10770 :   keys.addDOI("10.1093/nar/gkv872");
     530       10770 : }
     531             : 
     532        2996 : void Custom::read( ActionWithArguments* action ) {
     533             :   // Read in the variables
     534             :   std::vector<std::string> var;
     535        2996 :   parseVector(action,"VAR",var);
     536        5992 :   parse(action,"FUNC",func);
     537        2996 :   if(var.size()==0) {
     538        2937 :     var.resize(action->getNumberOfArguments());
     539        2937 :     if(var.size()>3) {
     540           0 :       action->error("Using more than 3 arguments you should explicitly write their names with VAR");
     541             :     }
     542        2937 :     if(var.size()>0) {
     543             :       var[0]="x";
     544             :     }
     545        2937 :     if(var.size()>1) {
     546             :       var[1]="y";
     547             :     }
     548        2937 :     if(var.size()>2) {
     549             :       var[2]="z";
     550             :     }
     551             :   }
     552        2996 :   if(var.size()!=action->getNumberOfArguments()) {
     553           0 :     action->error("Size of VAR array should be the same as number of arguments");
     554             :   }
     555             :   // Check for operations that are not multiplication (this can probably be done much more cleverly)
     556        2996 :   bool onlymultiplication = func.find("*")!=std::string::npos;
     557             :   // Find first bracket in expression
     558        2996 :   if( func.find("(")!=std::string::npos ) {
     559        1596 :     std::size_t br = func.find_first_of("(");
     560        1596 :     std::string subexpr=func.substr(0,br);
     561        1596 :     onlymultiplication = func.find("*")!=std::string::npos;
     562        1596 :     if( subexpr.find("/")!=std::string::npos ) {
     563         159 :       std::size_t sl = func.find_first_of("/");
     564         159 :       std::string aa = subexpr.substr(0,sl);
     565             :       subexpr=aa;
     566             :     }
     567        1596 :     if( subexpr.find("+")!=std::string::npos || subexpr.find("-")!=std::string::npos ) {
     568             :       onlymultiplication=false;
     569             :     }
     570             :     // Now work out which vars are in multiplication
     571        1489 :     if( onlymultiplication ) {
     572        2321 :       for(unsigned i=0; i<var.size(); ++i) {
     573        1465 :         if( subexpr.find(var[i])!=std::string::npos &&
     574             :             action->getPntrToArgument(i)->isDerivativeZeroWhenValueIsZero() ) {
     575         316 :           check_multiplication_vars.push_back(i);
     576             :         }
     577             :       }
     578             :     }
     579        1400 :   } else if( func.find("/")!=std::string::npos ) {
     580             :     onlymultiplication=true;
     581         821 :     if( func.find("+")!=std::string::npos || func.find("-")!=std::string::npos ) {
     582             :       onlymultiplication=false;
     583             :     }
     584             :     if( onlymultiplication ) {
     585         820 :       std::size_t br = func.find_first_of("/");
     586         820 :       std::string subexpr=func.substr(0,br);
     587        2280 :       for(unsigned i=0; i<var.size(); ++i) {
     588        1460 :         if( subexpr.find(var[i])!=std::string::npos &&
     589             :             action->getPntrToArgument(i)->isDerivativeZeroWhenValueIsZero() ) {
     590          31 :           check_multiplication_vars.push_back(i);
     591             :         }
     592             :       }
     593             :     }
     594         579 :   } else if( func.find("+")!=std::string::npos || func.find("-")!=std::string::npos ) {
     595             :     onlymultiplication=false;
     596             :   } else {
     597        1028 :     for(unsigned i=0; i<var.size(); ++i) {
     598         624 :       if( action->getPntrToArgument(i)->isDerivativeZeroWhenValueIsZero() ) {
     599         197 :         check_multiplication_vars.push_back(i);
     600             :       }
     601             :     }
     602             :   }
     603        2996 :   if( check_multiplication_vars.size()>0 ) {
     604         451 :     action->log.printf("  optimizing implementation as function only involves multiplication \n");
     605             :   }
     606             : 
     607        2996 :   action->log.printf("  with function : %s\n",func.c_str());
     608        2996 :   action->log.printf("  with variables :");
     609        7809 :   for(unsigned i=0; i<var.size(); i++) {
     610        4813 :     action->log.printf(" %s",var[i].c_str());
     611             :   }
     612        2996 :   action->log.printf("\n");
     613        2996 :   function.set( func, var, action );
     614        2996 :   std::vector<double> zeros( action->getNumberOfArguments(), 0 );
     615        2996 :   double fval = abs(function.evaluate(zeros));
     616        2996 :   zerowhenallzero=(fval<epsilon );
     617        2996 :   if( zerowhenallzero ) {
     618        1201 :     action->log.printf("  not calculating when all arguments are zero \n");
     619             :   }
     620        2996 : }
     621             : 
     622           5 : std::string Custom::getGraphInfo( const std::string& name ) const {
     623          10 :   return FunctionTemplateBase::getGraphInfo( name ) + + "\n" + "FUNC=" + func;
     624             : }
     625             : 
     626        1819 : bool Custom::getDerivativeZeroIfValueIsZero() const {
     627        1819 :   return check_multiplication_vars.size()>0;
     628             : }
     629             : 
     630         966 : std::vector<Value*> Custom::getArgumentsToCheck( const std::vector<Value*>& args ) {
     631         966 :   std::vector<Value*> fargs( check_multiplication_vars.size() );
     632        2093 :   for(unsigned i=0; i<check_multiplication_vars.size(); ++i) {
     633        1127 :     fargs[i] = args[check_multiplication_vars[i]];
     634             :   }
     635         966 :   return fargs;
     636             : }
     637             : 
     638    20478004 : void Custom::calc( const ActionWithArguments* action, const std::vector<double>& args, std::vector<double>& vals, Matrix<double>& derivatives ) const {
     639    20478004 :   if( args.size()>1 ) {
     640             :     bool allzero=false;
     641    15780962 :     if( check_multiplication_vars.size()>0 ) {
     642    17313345 :       for(unsigned i=0; i<check_multiplication_vars.size(); ++i) {
     643    10885579 :         if( fabs(args[check_multiplication_vars[i]])<epsilon ) {
     644             :           allzero=true;
     645             :           break;
     646             :         }
     647             :       }
     648     5142171 :     } else if( zerowhenallzero ) {
     649     2860794 :       allzero=(fabs(args[0])<epsilon);
     650     3296324 :       for(unsigned i=1; i<args.size(); ++i) {
     651     3151815 :         if( fabs(args[i])>epsilon ) {
     652             :           allzero=false;
     653             :           break;
     654             :         }
     655             :       }
     656             :     }
     657    13499585 :     if( allzero ) {
     658     4351594 :       vals[0]=0;
     659    13352933 :       for(unsigned i=0; i<args.size(); i++) {
     660     9001339 :         derivatives(0,i) = 0.0;
     661             :       }
     662             :       return;
     663             :     }
     664             :   }
     665    16126410 :   vals[0] = function.evaluate( args );
     666    16126410 :   if( !noderiv ) {
     667    47278265 :     for(unsigned i=0; i<args.size(); i++) {
     668    31160198 :       derivatives(0,i) = function.evaluateDeriv( i, args );
     669             :     }
     670             :   }
     671             : }
     672             : 
     673             : }
     674             : }
     675             : 
     676             : 

Generated by: LCOV version 1.16