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 "Action.h"
23 : #include "ActionAtomistic.h"
24 : #include "ActionWithValue.h"
25 : #include "ActionWithArguments.h"
26 : #include "ActionWithVirtualAtom.h"
27 : #include "ActionForInterface.h"
28 : #include "DomainDecomposition.h"
29 : #include "PbcAction.h"
30 : #include "ActionToPutData.h"
31 : #include "ActionToGetData.h"
32 : #include "PlumedMain.h"
33 : #include "tools/Log.h"
34 : #include "tools/Exception.h"
35 : #include "tools/Communicator.h"
36 : #include "ActionSet.h"
37 : #include <iostream>
38 :
39 : namespace PLMD {
40 :
41 : Keywords ActionOptions::emptyKeys;
42 :
43 51076 : ActionOptions::ActionOptions(PlumedMain&p,const std::vector<std::string>&l):
44 51076 : plumed(p),
45 51076 : line(l),
46 51076 : keys(emptyKeys) {
47 51076 : }
48 :
49 51074 : ActionOptions::ActionOptions(const ActionOptions&ao,const Keywords&keys):
50 51074 : plumed(ao.plumed),
51 51074 : line(ao.line),
52 51074 : keys(keys) {
53 51074 : }
54 :
55 74713 : void Action::registerKeywords( Keywords& keys ) {
56 74713 : plumed_assert( keys.size()==0 );
57 149426 : keys.add( "hidden", "LABEL", "a label for the action so that its output can be referenced in the input to other actions. Actions with scalar output are referenced using their label only. Actions with vector output must have a separate label for every component. Individual components are then referred to using label.component" );
58 149426 : keys.reserve("optional","UPDATE_FROM","Only update this action from this time");
59 149426 : keys.reserve("optional","UPDATE_UNTIL","Only update this action until this time");
60 149426 : keys.reserve("optional","RESTART","allows per-action setting of restart (YES/NO/AUTO)");
61 74713 : }
62 :
63 51074 : Action::Action(const ActionOptions&ao):
64 51074 : name(ao.line[0]),
65 51074 : line(ao.line),
66 51074 : update_from(std::numeric_limits<double>::max()),
67 51074 : update_until(std::numeric_limits<double>::max()),
68 51074 : timestep(0),
69 51074 : active(false),
70 51074 : restart(ao.plumed.getRestart()),
71 51074 : doCheckPoint(ao.plumed.getCPT()),
72 51074 : never_activate(name=="CONSTANT"),
73 51074 : plumed(ao.plumed),
74 51074 : log(plumed.getLog()),
75 51074 : comm(plumed.comm),
76 51074 : multi_sim_comm(plumed.multi_sim_comm),
77 102148 : keywords(ao.keys) {
78 : // Retrieve the timestep and save it
79 51074 : resetStoredTimestep();
80 :
81 : line.erase(line.begin());
82 102148 : if( !keywords.exists("NO_ACTION_LOG") ) {
83 35411 : log.printf("Action %s\n",name.c_str());
84 35411 : if(ao.fullPath.length()>0) {
85 41 : log<<" from library: "<<ao.fullPath<<"\n";
86 : }
87 : }
88 :
89 51074 : if(comm.Get_rank()==0) {
90 37125 : replica_index=multi_sim_comm.Get_rank();
91 : }
92 51074 : comm.Bcast(replica_index,0);
93 :
94 102148 : if ( keywords.exists("LABEL") ) {
95 101682 : parse("LABEL",label);
96 : }
97 51074 : if(label.length()==0) {
98 : std::string s;
99 3241 : Tools::convert(plumed.getActionSet().size()-plumed.getActionSet().select<ActionForInterface*>().size(),s);
100 6482 : label="@"+s;
101 47833 : } else if ( label.find(".")!=std::string::npos ) {
102 12 : warning("using full stop in an action label should be avaoided as . has a special meaning in PLUMED action labels");
103 : }
104 51074 : if( plumed.getActionSet().selectWithLabel<Action*>(label) ) {
105 0 : error("label " + label + " has been already used");
106 : }
107 102148 : if( !keywords.exists("NO_ACTION_LOG") ) {
108 35411 : log.printf(" with label %s\n",label.c_str());
109 : }
110 102148 : if ( keywords.exists("UPDATE_FROM") ) {
111 4586 : parse("UPDATE_FROM",update_from);
112 : }
113 102148 : if( !keywords.exists("NO_ACTION_LOG") && update_from!=std::numeric_limits<double>::max()) {
114 5 : log.printf(" only update from time %f\n",update_from);
115 : }
116 102148 : if ( keywords.exists("UPDATE_UNTIL") ) {
117 4586 : parse("UPDATE_UNTIL",update_until);
118 : }
119 102148 : if( !keywords.exists("NO_ACTION_LOG") && update_until!=std::numeric_limits<double>::max()) {
120 5 : log.printf(" only update until time %f\n",update_until);
121 : }
122 102148 : if ( keywords.exists("RESTART") ) {
123 2258 : std::string srestart="AUTO";
124 2257 : parse("RESTART",srestart);
125 2257 : if( plumed.parseOnlyMode() ) {
126 0 : restart=false;
127 2257 : } else if(srestart=="YES") {
128 170 : restart=true;
129 2087 : } else if(srestart=="NO") {
130 22 : restart=false;
131 2065 : } else if(srestart=="AUTO") {
132 : // do nothing, this is the default
133 : } else {
134 2 : error("RESTART should be either YES, NO, or AUTO");
135 : }
136 : }
137 51074 : }
138 :
139 100628 : void Action::resetStoredTimestep() {
140 100628 : ActionWithValue* ts = plumed.getActionSet().selectWithLabel<ActionWithValue*>("timestep");
141 100628 : if( ts ) {
142 94341 : timestep = (ts->copyOutput(0))->get();
143 : }
144 100628 : }
145 :
146 102146 : Action::~Action() {
147 51073 : if(files.size()!=0) {
148 0 : std::cerr<<"WARNING: some files open in action "+getLabel()+" where not properly closed. This could lead to data loss!!\n";
149 : }
150 102146 : }
151 :
152 82 : FILE* Action::fopen(const char *path, const char *mode) {
153 : bool write(false);
154 164 : for(const char*p=mode; *p; p++)
155 82 : if(*p=='w' || *p=='a' || *p=='+') {
156 : write=true;
157 : }
158 : FILE* fp;
159 82 : if(write && comm.Get_rank()!=0) {
160 0 : fp=plumed.fopen("/dev/null",mode);
161 : } else {
162 82 : fp=plumed.fopen(path,mode);
163 : }
164 81 : files.insert(fp);
165 81 : return fp;
166 : }
167 :
168 99 : int Action::fclose(FILE*fp) {
169 : files.erase(fp);
170 99 : return plumed.fclose(fp);
171 : }
172 :
173 60948 : void Action::fflush() {
174 60948 : for(const auto & p : files) {
175 0 : std::fflush(p);
176 : }
177 60948 : }
178 :
179 0 : std::string Action::getKeyword(const std::string& key) {
180 : // Check keyword has been registered
181 0 : plumed_massert(keywords.exists(key), "keyword " + key + " has not been registered");
182 :
183 : std::string outkey;
184 0 : if( Tools::getKey(line,key,outkey ) ) {
185 0 : return key + outkey;
186 : }
187 :
188 0 : if( keywords.style(key,"compulsory") ) {
189 0 : if( keywords.getDefaultValue(key,outkey) ) {
190 0 : if( outkey.length()==0 ) {
191 0 : error("keyword " + key + " has weird default value");
192 : }
193 0 : return key + "=" + outkey;
194 : } else {
195 0 : error("keyword " + key + " is compulsory for this action");
196 : }
197 : }
198 0 : return "";
199 : }
200 :
201 95720 : void Action::parseFlag(const std::string&key,bool & t) {
202 : // Check keyword has been registered
203 95720 : plumed_massert(keywords.exists(key), "keyword " + key + " has not been registered");
204 : // Check keyword is a flag
205 191440 : if(!keywords.style(key,"nohtml")) {
206 191440 : plumed_massert( keywords.style(key,"vessel") || keywords.style(key,"flag") || keywords.style(key,"hidden"), "keyword " + key + " is not a flag");
207 : }
208 :
209 : // Read in the flag otherwise get the default value from the keywords object
210 95720 : if(!Tools::parseFlag(line,key,t)) {
211 167330 : if( keywords.style(key,"nohtml") || keywords.style(key,"vessel") ) {
212 0 : t=false;
213 83665 : } else if ( !keywords.getLogicalDefault(key,t) ) {
214 0 : log.printf("ERROR in action %s with label %s : flag %s has no default",name.c_str(),label.c_str(),key.c_str() );
215 0 : plumed_error();
216 : }
217 : }
218 95720 : }
219 :
220 1262985 : void Action::addDependency(Action*action) {
221 : bool found=false;
222 40065368 : for(const auto & d : after ) {
223 39065475 : if( action==d ) {
224 : found=true;
225 : break;
226 : }
227 : }
228 1262985 : if( !found ) {
229 999893 : after.push_back(action);
230 : }
231 1262985 : }
232 :
233 571383 : bool Action::checkForDependency( Action* action ) {
234 1124757 : for(const auto & d : after) {
235 563943 : if( action==d ) {
236 : return true;
237 : }
238 557562 : if( d->checkForDependency(action) ) {
239 : return true;
240 : }
241 : }
242 : return false;
243 : }
244 :
245 5247017 : void Action::activate() {
246 : // This is set to true if actions are only need to be computed in setup (during checkRead)
247 5247017 : if( never_activate ) {
248 : return;
249 : }
250 : // preparation step is called only the first time an Action is activated.
251 : // since it could change its dependences (e.g. in an ActionAtomistic which is
252 : // accessing to a virtual atom), this is done just before dependencies are
253 : // activated
254 5132651 : if(!active) {
255 2649962 : this->unlockRequests();
256 2649962 : prepare();
257 2649962 : this->lockRequests();
258 : } else {
259 : return;
260 : }
261 6545184 : for(const auto & p : after) {
262 3895222 : p->activate();
263 : }
264 2649962 : active=true;
265 : }
266 :
267 2228 : void Action::setOption(const std::string &s) {
268 : // This overloads the action and activate some options
269 2228 : options.insert(s);
270 4370 : for(const auto & p : after) {
271 2142 : p->setOption(s);
272 : }
273 2228 : }
274 :
275 0 : void Action::clearOptions() {
276 : // This overloads the action and activate some options
277 : options.clear();
278 0 : }
279 :
280 :
281 199562 : void Action::clearDependencies() {
282 : after.clear();
283 199562 : }
284 :
285 66935 : void Action::checkRead() {
286 66935 : if(!line.empty()) {
287 1 : std::string msg="cannot understand the following words from the input line : ";
288 2 : for(unsigned i=0; i<line.size(); i++) {
289 1 : if(i>0) {
290 0 : msg = msg + ", ";
291 : }
292 2 : msg = msg + line[i];
293 : }
294 1 : error(msg);
295 : }
296 66934 : setupConstantValues(false);
297 66934 : }
298 :
299 95264 : void Action::setupConstantValues( const bool& have_atoms ) {
300 95264 : if( have_atoms ) {
301 : // This ensures that we switch off actions that only depend on constant when passed from the
302 : // MD code on the first step
303 28330 : ActionAtomistic* at = castToActionAtomistic();
304 28330 : ActionWithValue* av = castToActionWithValue();
305 28330 : if( at && av ) {
306 16002 : never_activate=av->getNumberOfComponents()>0;
307 16049 : for(unsigned i=0; i<av->getNumberOfComponents(); ++i) {
308 16002 : if( !av->copyOutput(i)->isConstant() ) {
309 15955 : never_activate=false;
310 15955 : break;
311 : }
312 : }
313 : }
314 : }
315 95264 : ActionWithArguments* aa = castToActionWithArguments();
316 114486 : if( aa && aa->getNumberOfArguments()>0 && getName()!="BIASVALUE" ) {
317 18581 : never_activate = aa->calculateConstantValues( have_atoms );
318 : }
319 95264 : }
320 :
321 5469614 : long long int Action::getStep()const {
322 5469614 : return plumed.getStep();
323 : }
324 :
325 3721732 : double Action::getTime()const {
326 3721732 : return timestep*getStep();
327 : }
328 :
329 219887 : double Action::getTimeStep()const {
330 219887 : return timestep;
331 : }
332 :
333 682 : double Action::getkBT() {
334 682 : double temp=-1.0;
335 1364 : if( keywords.exists("TEMP") ) {
336 1336 : parse("TEMP",temp);
337 : }
338 1698 : if(temp>=0.0 && keywords.style("TEMP","optional") ) {
339 421 : return getKBoltzmann()*temp;
340 : }
341 261 : ActionForInterface* kb=plumed.getActionSet().selectWithLabel<ActionForInterface*>("kBT");
342 : double kbt=0;
343 261 : if(kb) {
344 3 : kbt=(kb->copyOutput(0))->get();
345 : }
346 377 : if( temp>=0 && keywords.style("TEMP","compulsory") ) {
347 58 : double kB=getKBoltzmann();
348 58 : if( kbt>0 && std::abs(kbt-kB*temp)>1e-4) {
349 : std::string strt1, strt2;
350 0 : Tools::convert( temp, strt1 );
351 0 : Tools::convert( kbt/kB, strt2 );
352 0 : warning("using TEMP=" + strt1 + " while MD engine uses " + strt2 + "\n");
353 : }
354 58 : kbt = kB*temp;
355 58 : plumed_massert(kbt>0,"your MD engine does not pass the temperature to plumed, you must specify it using TEMP");
356 : return kbt;
357 : }
358 : return kbt;
359 : }
360 :
361 0 : void Action::exit(int c) {
362 0 : plumed.exit(c);
363 0 : }
364 :
365 0 : void Action::calculateNumericalDerivatives( ActionWithValue* a ) {
366 0 : plumed_merror("if you get here it means that you are trying to use numerical derivatives for a class that does not implement them");
367 : }
368 :
369 1025832 : void Action::prepare() {
370 1025832 : return;
371 : }
372 :
373 28 : [[noreturn]] void Action::error( const std::string & msg ) const {
374 28 : log.printf("ERROR in input to action %s with label %s : %s \n \n", name.c_str(), label.c_str(), msg.c_str() );
375 84 : plumed_merror("ERROR in input to action " + name + " with label " + label + " : " + msg );
376 : }
377 :
378 4304 : void Action::warning( const std::string & msg ) {
379 4304 : log.printf("WARNING for action %s with label %s : %s \n", name.c_str(), label.c_str(), msg.c_str() );
380 4304 : }
381 :
382 0 : void Action::calculateFromPDB( const PDB& pdb ) {
383 0 : activate();
384 0 : for(const auto & p : after) {
385 0 : ActionWithValue*av=castToActionWithValue();
386 0 : if(av) {
387 0 : av->clearInputForces();
388 0 : av->clearDerivatives();
389 : }
390 0 : p->readAtomsFromPDB( pdb );
391 0 : p->calculate();
392 : }
393 0 : readAtomsFromPDB( pdb );
394 0 : calculate();
395 0 : }
396 :
397 30198 : bool Action::getExchangeStep()const {
398 30198 : return plumed.getExchangeStep();
399 : }
400 :
401 41 : std::string Action::cite(const std::string&s) {
402 41 : return plumed.cite(s);
403 : }
404 :
405 : /// Check if action should be updated.
406 2550402 : bool Action::checkUpdate()const {
407 2550402 : double t=getTime();
408 2550402 : if(t<update_until && (update_from==std::numeric_limits<double>::max() || t>=update_from)) {
409 : return true;
410 : } else {
411 510 : return false;
412 : }
413 : }
414 :
415 1340 : bool Action::getCPT() const {
416 1340 : return plumed.getCPT();
417 : }
418 :
419 15615301 : const Units& Action::getUnits() const {
420 15615301 : return plumed.getUnits();
421 : }
422 :
423 2197 : bool Action::usingNaturalUnits() const {
424 2197 : return plumed.usingNaturalUnits();
425 : }
426 :
427 714 : double Action::getKBoltzmann() const {
428 714 : if( usingNaturalUnits() ) {
429 : return 1.0;
430 : } else {
431 714 : return kBoltzmann/getUnits().getEnergy();
432 : }
433 : }
434 :
435 44 : std::string Action::writeInGraph() const {
436 44 : std::string nam=getName();
437 44 : std::size_t u=nam.find_last_of("_");
438 44 : std::string sub=nam.substr(u+1);
439 118 : if( sub=="SCALAR" || sub=="VECTOR" || sub=="GRID" ) {
440 8 : return nam.substr(0,u);
441 : }
442 : return nam;
443 : }
444 :
445 : }
446 :
|