Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2012-2020 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/ActionShortcut.h" 23 : #include "core/ActionWithArguments.h" 24 : #include "core/PlumedMain.h" 25 : #include "core/ActionSet.h" 26 : #include "core/ActionRegister.h" 27 : #include "tools/Tools.h" 28 : 29 : namespace PLMD { 30 : namespace bias { 31 : 32 : class Walls : public ActionShortcut { 33 : public: 34 : explicit Walls(const ActionOptions&); 35 : static void registerKeywords(Keywords& keys); 36 : }; 37 : 38 : PLUMED_REGISTER_ACTION(Walls,"UPPER_WALLS") 39 : PLUMED_REGISTER_ACTION(Walls,"LOWER_WALLS") 40 : 41 70 : void Walls::registerKeywords(Keywords& keys) { 42 70 : ActionShortcut::registerKeywords(keys); 43 140 : keys.add("numbered","ARG","the arguments on which the bias is acting"); 44 140 : keys.add("compulsory","AT","the positions of the wall. The a_i in the expression for a wall."); 45 140 : keys.add("compulsory","KAPPA","the force constant for the wall. The k_i in the expression for a wall."); 46 140 : keys.add("compulsory","OFFSET","0.0","the offset for the start of the wall. The o_i in the expression for a wall."); 47 140 : keys.add("compulsory","EXP","2.0","the powers for the walls. The e_i in the expression for a wall."); 48 140 : keys.add("compulsory","EPS","1.0","the values for s_i in the expression for a wall"); 49 140 : keys.add("hidden","STRIDE","the frequency with which the forces due to the bias should be calculated. This can be used to correctly set up multistep algorithms"); 50 140 : keys.addOutputComponent("bias","default","the instantaneous value of the bias potential"); 51 140 : keys.addOutputComponent("force2","default","the instantaneous value of the squared force due to this bias potential"); 52 70 : keys.addActionNameSuffix("_SCALAR"); 53 70 : keys.needsAction("COMBINE"); 54 70 : keys.needsAction("CUSTOM"); 55 70 : keys.needsAction("SUM"); 56 70 : keys.needsAction("COMBINE"); 57 70 : keys.needsAction("BIASVALUE"); 58 70 : } 59 : 60 29 : Walls::Walls(const ActionOptions&ao): 61 : Action(ao), 62 29 : ActionShortcut(ao) { 63 : // Read the arguments 64 : std::vector<std::string> args; 65 58 : parseVector("ARG",args); 66 29 : if( args.size()==0 ) { 67 0 : error("found no input arguments"); 68 : } 69 29 : std::string allargs=args[0]; 70 33 : for(unsigned i=1; i<args.size(); ++i) { 71 8 : allargs += "," + args[i]; 72 : } 73 : std::vector<Value*> vals; 74 29 : ActionWithArguments::interpretArgumentList( args, plumed.getActionSet(), this, vals ); 75 29 : if( vals.size()==0 ) { 76 0 : error("found no input arguments"); 77 : } 78 : 79 : // Find the rank 80 29 : unsigned rank=vals[0]->getRank(); 81 62 : for(unsigned i=0; i<vals.size(); ++i) { 82 33 : if( vals[i]->getRank()>0 && vals[i]->hasDerivatives() ) { 83 0 : error("argument should not be function on grid"); 84 : } 85 33 : if( vals[i]->getRank()!=rank ) { 86 0 : error("all arguments should have same rank"); 87 : } 88 : } 89 29 : if( rank==0 ) { 90 25 : if( getName()=="UPPER_WALLS") { 91 34 : readInputLine( getShortcutLabel() + ": UPPER_WALLS_SCALAR ARG=" + allargs + " " + convertInputLineToString() ); 92 8 : } else if( getName()=="LOWER_WALLS") { 93 16 : readInputLine( getShortcutLabel() + ": LOWER_WALLS_SCALAR ARG=" + allargs + " " + convertInputLineToString() ); 94 : } else { 95 0 : plumed_merror( getName() + " is not valid"); 96 : } 97 : return; 98 : } 99 : 100 : // Note : the sizes of these vectors are checked automatically by parseVector 101 4 : std::vector<std::string> kappa(args.size()); 102 8 : parseVector("KAPPA",kappa); 103 4 : std::vector<std::string> offset(kappa.size()); 104 8 : parseVector("OFFSET",offset); 105 4 : std::vector<std::string> eps(kappa.size()); 106 8 : parseVector("EPS",eps); 107 4 : std::vector<std::string> exp(kappa.size()); 108 8 : parseVector("EXP",exp); 109 4 : std::vector<std::string> at(kappa.size()); 110 8 : parseVector("AT",at); 111 : 112 : std::string biasinp, forceinp; 113 8 : for(unsigned i=0; i<args.size(); ++i) { 114 : std::string argn; 115 4 : std::size_t dot=args[i].find_first_of("."); 116 4 : if(dot!=std::string::npos) { 117 0 : argn = args[i].substr(0,dot) + "_" + args[i].substr(dot+1); 118 : } else { 119 : argn = args[i]; 120 : } 121 : // For periodic arguments the wall acts on the value of the argument in its domain, without periodic images 122 : // (see https://github.com/plumed/plumed2/issues/658). COMBINE would apply PBCs to the difference, so CUSTOM is used instead. 123 : std::vector<Value*> myvals; 124 4 : ActionWithArguments::interpretArgumentList( std::vector<std::string>(1,args[i]), plumed.getActionSet(), this, myvals ); 125 : bool periodic=false; 126 8 : for(unsigned j=0; j<myvals.size(); ++j) { 127 4 : if( myvals[j]->isPeriodic() ) { 128 : periodic=true; 129 : double min, max, atval; 130 2 : myvals[j]->getDomain( min, max ); 131 2 : if( !Tools::convertNoexcept( at[i], atval ) ) { 132 0 : error("could not convert " + at[i] + " into a real number"); 133 : } 134 2 : if( atval<min || atval>max ) { 135 0 : error("the position of the wall (AT) for periodic argument " + myvals[j]->getName() + " should be within its periodic domain"); 136 : } 137 2 : log.printf(" WARNING: argument %s is periodic. The wall acts on its value in the domain [%f,%f]\n" 138 : " without periodic images, so the bias is discontinuous at the boundary of the domain.\n" 139 : " Make sure that the walls keep the argument far from it (e.g. use both an upper and a lower wall).\n", 140 2 : myvals[j]->getName().c_str(), min, max ); 141 : } 142 : } 143 4 : if( periodic ) { 144 4 : readInputLine( getShortcutLabel() + "_cv_" + argn + ": CUSTOM PERIODIC=NO FUNC=x-(" + at[i] + ") ARG=" + args[i] ); 145 : } else { 146 4 : readInputLine( getShortcutLabel() + "_cv_" + argn + ": COMBINE PERIODIC=NO ARG=" + args[i] + " PARAMETERS=" + at[i] ); 147 : } 148 4 : if( getName()=="UPPER_WALLS" ) { 149 6 : readInputLine( getShortcutLabel() + "_scale_" + argn + ": CUSTOM PERIODIC=NO FUNC=(x+" + offset[i] +")/" + eps[i] + " ARG=" + getShortcutLabel() + "_cv_" + argn ); 150 6 : readInputLine( getShortcutLabel() + "_pow_" + argn + ": CUSTOM PERIODIC=NO FUNC=step(x)*x^" + exp[i] + " ARG=" + getShortcutLabel() + "_scale_" + argn ); 151 1 : } else if( getName()=="LOWER_WALLS" ) { 152 2 : readInputLine( getShortcutLabel() + "_scale_" + argn + ": CUSTOM PERIODIC=NO FUNC=(x-" + offset[i] +")/" + eps[i] + " ARG=" + getShortcutLabel() + "_cv_" + argn ); 153 2 : readInputLine( getShortcutLabel() + "_pow_" + argn + ": CUSTOM PERIODIC=NO FUNC=step(-x)*(-x)^" + exp[i] + " ARG=" + getShortcutLabel() + "_scale_" + argn ); 154 : } 155 8 : readInputLine( getShortcutLabel() + "_v_wall_" + argn + ": CUSTOM PERIODIC=NO FUNC=" + kappa[i] +"*x" + " ARG=" + getShortcutLabel() + "_pow_" + argn ); 156 8 : readInputLine( getShortcutLabel() + "_wall_" + argn + ": SUM ARG=" + getShortcutLabel() + "_v_wall_" + argn + " PERIODIC=NO"); 157 12 : readInputLine( getShortcutLabel() + "_force_" + argn + ": CUSTOM PERIODIC=NO FUNC=" + kappa[i] + "*" + exp[i] + "*x/(y*" + eps[i] + ") " + 158 12 : "ARG=" + getShortcutLabel() + "_pow_" + argn + "," + getShortcutLabel() + "_scale_" + argn ); 159 8 : readInputLine( getShortcutLabel() + "_v_force2_" + argn + ": CUSTOM PERIODIC=NO FUNC=x*x ARG=" + getShortcutLabel() + "_force_" + argn ); 160 8 : readInputLine( getShortcutLabel() + "_force2_" + argn + ": SUM ARG=" + getShortcutLabel() + "_v_force2_" + argn + " PERIODIC=NO"); 161 4 : if(i==0) { 162 8 : biasinp = " ARG=" + getShortcutLabel() + "_wall_" + argn; 163 8 : forceinp = " ARG=" + getShortcutLabel() + "_force2_" + argn; 164 : } else { 165 0 : biasinp += "," + getShortcutLabel() + "_wall_" + argn; 166 0 : forceinp += "," + getShortcutLabel() + "_force2_" + argn; 167 : } 168 : } 169 8 : readInputLine( getShortcutLabel() + "_bias: COMBINE PERIODIC=NO " + biasinp ); 170 8 : readInputLine( "BIASVALUE ARG=" + getShortcutLabel() + "_bias" ); 171 8 : readInputLine( getShortcutLabel() + "_force2: COMBINE PERIODIC=NO " + forceinp ); 172 33 : } 173 : 174 : } 175 : }