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