Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2012-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 "core/ActionRegister.h" 23 : #include "FunctionTemplateBase.h" 24 : #include "FunctionShortcut.h" 25 : #include "FunctionOfScalar.h" 26 : 27 : namespace PLMD { 28 : namespace function { 29 : 30 : //+PLUMEDOC FUNCTION PIECEWISE 31 : /* 32 : Compute a piece wise straight line through its arguments that passes through a set of ordered control points. 33 : 34 : This action can be used to calculate a piecewise linear function of an input argument such as the one given below: 35 : 36 : $$ 37 : f(x) = \begin{cases} 38 : 10 & \textrm{if} \quad x<1 \\ 39 : 10 + \frac{\pi - 10}{2-1}(x-1) & \textrm{if} \quad 1 \le x < 2 \\ 40 : \pi + \frac{10 - \pi}{3-2}(x-2) & \textrm{if} \quad 2 \le x \le 3 \\ 41 : 10 & \textrm{otherwise} 42 : \end{cases} 43 : $$ 44 : 45 : The example shown below illustrates how one can use PLUMED to evaluate the function described above for the distance 46 : between atom 1 and atom 2. 47 : 48 : ```plumed 49 : dist1: DISTANCE ATOMS=1,10 50 : pw: PIECEWISE POINT0=1,10 POINT1=2,PI POINT2=3,10 ARG=dist1 51 : PRINT ARG=pw FILE=colvar 52 : ``` 53 : 54 : As you can see from the example above, the control points for the picewise function are passed using the `POINT0=...` `POINT1=...` syntax. 55 : You can specify as many of these control points as you want. These control points then serce as the $x_i$ and $y_i$ values in the following expression. 56 : 57 : For variables less than the first 58 : (greater than the last) point, the value of the first (last) point is used. 59 : 60 : $$ 61 : \frac{y_{i+1}-y_i}{x_{i+1}-x_i}(s-x_i)+y_i ; if x_i<s<x_{i+1} 62 : $$ 63 : 64 : If the input value of $s$ is smaller than the lowest specified $x_i$ value then this action outputs the $y_i$ value that corresponds to the smallest of the input $x_i$ values. 65 : Similarly, if the input value of $s$ is larger than the highest specified $x_i$ value the $y_i$ value that corresponds to the largest of the input $x_i$ values is output. 66 : 67 : ## Using multiple scalars in input 68 : 69 : The following example illustrates what happens when multiple scalar arguments are passed to this action: 70 : 71 : ```plumed 72 : dist1: DISTANCE ATOMS=1,10 73 : dist2: DISTANCE ATOMS=2,11 74 : 75 : ppww: PIECEWISE POINT0=1,10 POINT1=2,PI POINT2=3,10 ARG=dist1,dist2 76 : PRINT ARG=ppww.dist1_pfunc,ppww.dist2_pfunc 77 : ``` 78 : 79 : In essence the piecewise function is applied to each of the input arguments in turn. Hence, in the example above the PIECEWISE command outputs two values. The first of 80 : these `ppww.dist1_pfunc` is the result that is obtained when the piecewise function is applied to the argument `dist1`. The second is then the result that is obtained when the 81 : piecewise function is applied to the argument `dist2`. 82 : 83 : ## Non rank zero arguments 84 : 85 : This argument currently cannot accept non-rank zero arguments. However, it would be extremely straightforward to add functionality to ensure that if a PIECEWISE command receives 86 : a vector, matrix or function on grid in input it will output a vector, matrix or function on grid that is obtained by applying the piecewise function elementwise to each of the 87 : elements of the input vector, matrix or function. 88 : 89 : */ 90 : //+ENDPLUMEDOC 91 : 92 : //+PLUMEDOC FUNCTION PIECEWISE_SCALAR 93 : /* 94 : Compute a piece wise straight line through its arguments that passes through a set of ordered control points. 95 : 96 : \par Examples 97 : 98 : */ 99 : //+ENDPLUMEDOC 100 : 101 33 : class Piecewise : public FunctionTemplateBase { 102 : std::vector<std::pair<double,double> > points; 103 : public: 104 : void registerKeywords(Keywords& keys) override; 105 : void read( ActionWithArguments* action ) override; 106 : void setPeriodicityForOutputs( ActionWithValue* action ) override; 107 : void calc( const ActionWithArguments* action, const std::vector<double>& args, std::vector<double>& vals, Matrix<double>& derivatives ) const override; 108 : }; 109 : 110 : 111 : typedef FunctionShortcut<Piecewise> PiecewiseShortcut; 112 : PLUMED_REGISTER_ACTION(PiecewiseShortcut,"PIECEWISE") 113 : typedef FunctionOfScalar<Piecewise> ScalarPiecewise; 114 : PLUMED_REGISTER_ACTION(ScalarPiecewise,"PIECEWISE_SCALAR") 115 : 116 12 : void Piecewise::registerKeywords(Keywords& keys) { 117 12 : keys.add("numbered","POINT","This keyword is used to specify the various points in the function above."); 118 24 : keys.reset_style("POINT","compulsory"); 119 24 : keys.addOutputComponent("_pfunc","default","scalar","one or multiple instances of this quantity can be referenced elsewhere " 120 : "in the input file. These quantities will be named with the arguments of the " 121 : "function followed by the character string _pfunc. These quantities tell the " 122 : "user the values of the piece wise functions of each of the arguments."); 123 12 : } 124 : 125 3 : void Piecewise::read( ActionWithArguments* action ) { 126 9 : for(int i=0;; i++) { 127 : std::vector<double> pp; 128 24 : if(!action->parseNumberedVector("POINT",i,pp) ) { 129 : break; 130 : } 131 9 : if(pp.size()!=2) { 132 0 : action->error("points should be in x,y format"); 133 : } 134 9 : points.push_back(std::pair<double,double>(pp[0],pp[1])); 135 9 : if(i>0 && points[i].first<=points[i-1].first) { 136 0 : action->error("points abscissas should be monotonously increasing"); 137 : } 138 9 : } 139 : 140 6 : for(unsigned i=0; i<action->getNumberOfArguments(); i++) { 141 4 : if(action->getPntrToArgument(i)->isPeriodic()) { 142 1 : action->error("Cannot use PIECEWISE on periodic arguments"); 143 : } 144 : } 145 2 : action->log.printf(" on points:"); 146 8 : for(unsigned i=0; i<points.size(); i++) { 147 6 : action->log.printf(" (%f,%f)",points[i].first,points[i].second); 148 : } 149 2 : action->log.printf("\n"); 150 2 : } 151 : 152 2 : void Piecewise::setPeriodicityForOutputs( ActionWithValue* action ) { 153 5 : for(unsigned i=0; i<action->getNumberOfComponents(); ++i) { 154 3 : action->copyOutput(i)->setNotPeriodic(); 155 : } 156 2 : } 157 : 158 10 : void Piecewise::calc( const ActionWithArguments* action, const std::vector<double>& args, std::vector<double>& vals, Matrix<double>& derivatives ) const { 159 25 : for(unsigned i=0; i<args.size(); i++) { 160 : unsigned p=0; 161 37 : for(; p<points.size(); p++) { 162 33 : if(args[i]<points[p].first) { 163 : break; 164 : } 165 : } 166 15 : if(p==0) { 167 5 : vals[i]=points[0].second; 168 5 : derivatives(i,i)=0.0; 169 10 : } else if(p==points.size()) { 170 4 : vals[i]=points[points.size()-1].second; 171 4 : derivatives(i,i)=0.0; 172 : } else { 173 6 : double m=(points[p].second-points[p-1].second) / (points[p].first-points[p-1].first); 174 6 : vals[i]=m*(args[i]-points[p-1].first)+points[p-1].second; 175 6 : derivatives(i,i)=m; 176 : } 177 : } 178 10 : } 179 : 180 : } 181 : } 182 : 183 :