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 "Function.h" 24 : 25 : namespace PLMD { 26 : namespace function { 27 : 28 : //+PLUMEDOC FUNCTION PIECEWISE 29 : /* 30 : Compute a piece wise straight line through its arguments that passes through a set of ordered control points. 31 : 32 : For variables less than the first 33 : (greater than the last) point, the value of the first (last) point is used. 34 : 35 : \f[ 36 : \frac{y_{i+1}-y_i}{x_{i+1}-x_i}(s-x_i)+y_i ; if x_i<s<x_{i+1} 37 : \f] 38 : \f[ 39 : y_N ; if x>x_{N-1} 40 : \f] 41 : \f[ 42 : y_1 ; if x<x_0 43 : \f] 44 : 45 : Control points are passed using the POINT0=... POINT1=... syntax as in the example below 46 : 47 : If one argument is supplied, it results in a scalar quantity. 48 : If multiple arguments are supplied, it results 49 : in a vector of values. Each value will be named as the name of the original 50 : argument with suffix _pfunc. 51 : 52 : \par Examples 53 : 54 : \plumedfile 55 : dist1: DISTANCE ATOMS=1,10 56 : dist2: DISTANCE ATOMS=2,11 57 : 58 : pw: PIECEWISE POINT0=1,10 POINT1=2,PI POINT2=3,10 ARG=dist1 59 : ppww: PIECEWISE POINT0=1,10 POINT1=2,PI POINT2=3,10 ARG=dist1,dist2 60 : PRINT ARG=pw,ppww.dist1_pfunc,ppww.dist2_pfunc 61 : \endplumedfile 62 : 63 : 64 : */ 65 : //+ENDPLUMEDOC 66 : 67 : 68 : class Piecewise : 69 : public Function 70 : { 71 : std::vector<std::pair<double,double> > points; 72 : public: 73 : explicit Piecewise(const ActionOptions&); 74 : void calculate() override; 75 : static void registerKeywords(Keywords& keys); 76 : }; 77 : 78 : 79 10424 : PLUMED_REGISTER_ACTION(Piecewise,"PIECEWISE") 80 : 81 4 : void Piecewise::registerKeywords(Keywords& keys) { 82 4 : Function::registerKeywords(keys); 83 4 : keys.use("ARG"); 84 8 : keys.add("numbered","POINT","This keyword is used to specify the various points in the function above."); 85 8 : keys.reset_style("POINT","compulsory"); 86 4 : componentsAreNotOptional(keys); 87 8 : keys.addOutputComponent("_pfunc","default","one or multiple instances of this quantity can be referenced elsewhere " 88 : "in the input file. These quantities will be named with the arguments of the " 89 : "function followed by the character string _pfunc. These quantities tell the " 90 : "user the values of the piece wise functions of each of the arguments."); 91 4 : } 92 : 93 3 : Piecewise::Piecewise(const ActionOptions&ao): 94 : Action(ao), 95 3 : Function(ao) 96 : { 97 9 : for(int i=0;; i++) { 98 : std::vector<double> pp; 99 24 : if(!parseNumberedVector("POINT",i,pp) ) break; 100 9 : if(pp.size()!=2) error("points should be in x,y format"); 101 9 : points.push_back(std::pair<double,double>(pp[0],pp[1])); 102 9 : if(i>0 && points[i].first<=points[i-1].first) error("points abscissas should be monotonously increasing"); 103 9 : } 104 : 105 6 : for(unsigned i=0; i<getNumberOfArguments(); i++) 106 4 : if(getPntrToArgument(i)->isPeriodic()) 107 1 : error("Cannot use PIECEWISE on periodic arguments"); 108 : 109 2 : if(getNumberOfArguments()==1) { 110 1 : addValueWithDerivatives(); 111 1 : setNotPeriodic(); 112 : } else { 113 3 : for(unsigned i=0; i<getNumberOfArguments(); i++) { 114 3 : addComponentWithDerivatives( getPntrToArgument(i)->getName()+"_pfunc" ); 115 2 : getPntrToComponent(i)->setNotPeriodic(); 116 : } 117 : } 118 2 : checkRead(); 119 : 120 2 : log.printf(" on points:"); 121 8 : for(unsigned i=0; i<points.size(); i++) log.printf(" (%f,%f)",points[i].first,points[i].second); 122 2 : log.printf("\n"); 123 4 : } 124 : 125 10 : void Piecewise::calculate() { 126 25 : for(unsigned i=0; i<getNumberOfArguments(); i++) { 127 : double val=getArgument(i); 128 : unsigned p=0; 129 37 : for(; p<points.size(); p++) { 130 33 : if(val<points[p].first) break; 131 : } 132 : double f,d; 133 15 : if(p==0) { 134 5 : f=points[0].second; 135 : d=0.0; 136 10 : } else if(p==points.size()) { 137 4 : f=points[points.size()-1].second; 138 : d=0.0; 139 : } else { 140 6 : double m=(points[p].second-points[p-1].second) / (points[p].first-points[p-1].first); 141 6 : f=m*(val-points[p-1].first)+points[p-1].second; 142 : d=m; 143 : } 144 15 : if(getNumberOfArguments()==1) { 145 5 : setValue(f); 146 5 : setDerivative(i,d); 147 : } else { 148 10 : Value* v=getPntrToComponent(i); 149 10 : v->set(f); 150 : v->addDerivative(i,d); 151 : } 152 : } 153 10 : } 154 : 155 : } 156 : } 157 : 158 :