1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2    Copyright (c) 2012-2021 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 "Keywords.h"
23 #include "Log.h"
24 #include "Tools.h"
25 #include <iostream>
26 
27 namespace PLMD {
28 
KeyType(const std::string & type)29 Keywords::KeyType::KeyType( const std::string& type ) {
30   if( type=="compulsory" ) {
31     style=compulsory;
32   } else if( type=="flag" ) {
33     style=flag;
34   } else if( type=="optional" ) {
35     style=optional;
36   } else if( type.find("atoms")!=std::string::npos || type.find("residues")!=std::string::npos ) {
37     style=atoms;
38   } else if( type=="hidden" ) {
39     style=hidden;
40   } else if( type=="vessel" ) {
41     style=vessel;
42   } else {
43     plumed_massert(false,"invalid keyword specifier " + type);
44   }
45 }
46 
setStyle(const std::string & type)47 void Keywords::KeyType::setStyle( const std::string& type ) {
48   if( type=="compulsory" ) {
49     style=compulsory;
50   } else if( type=="flag" ) {
51     style=flag;
52   } else if( type=="optional" ) {
53     style=optional;
54   } else if( type.find("atoms")!=std::string::npos || type.find("residues")!=std::string::npos ) {
55     style=atoms;
56   } else if( type=="hidden" ) {
57     style=hidden;
58   } else if( type=="vessel" ) {
59     style=vessel;
60   } else {
61     plumed_massert(false,"invalid keyword specifier " + type);
62   }
63 }
64 
add(const Keywords & newkeys)65 void Keywords::add( const Keywords& newkeys ) {
66   newkeys.copyData( keys, reserved_keys, types, allowmultiple, documentation, booldefs, numdefs, atomtags, cnames, ckey, cdocs  );
67 }
68 
copyData(std::vector<std::string> & kk,std::vector<std::string> & rk,std::map<std::string,KeyType> & tt,std::map<std::string,bool> & am,std::map<std::string,std::string> & docs,std::map<std::string,bool> & bools,std::map<std::string,std::string> & nums,std::map<std::string,std::string> & atags,std::vector<std::string> & cnam,std::map<std::string,std::string> & ck,std::map<std::string,std::string> & cd) const69 void Keywords::copyData( std::vector<std::string>& kk, std::vector<std::string>& rk, std::map<std::string,KeyType>& tt, std::map<std::string,bool>& am,
70                          std::map<std::string,std::string>& docs, std::map<std::string,bool>& bools, std::map<std::string,std::string>& nums,
71                          std::map<std::string,std::string>& atags, std::vector<std::string>& cnam, std::map<std::string,std::string>& ck,
72                          std::map<std::string,std::string>& cd ) const {
73   for(unsigned i=0; i<keys.size(); ++i) {
74     std::string thiskey=keys[i];
75     for(unsigned j=0; j<kk.size(); ++j) plumed_massert( thiskey!=kk[j], "keyword " + thiskey + " is in twice" );
76     for(unsigned j=0; j<rk.size(); ++j) plumed_massert( thiskey!=rk[j], "keyword " + thiskey + " is in twice" );
77     kk.push_back( thiskey );
78     plumed_massert( types.count( thiskey ), "no type data on keyword " + thiskey + " to copy" );
79     tt.insert( std::pair<std::string,KeyType>( thiskey,types.find(thiskey)->second) );
80     if( (types.find(thiskey)->second).isAtomList() ) atags.insert( std::pair<std::string,std::string>( thiskey,atomtags.find(thiskey)->second) );
81     plumed_massert( allowmultiple.count( thiskey ), "no numbered data on keyword " + thiskey + " to copy" );
82     am.insert( std::pair<std::string,bool>(thiskey,allowmultiple.find(thiskey)->second) );
83     plumed_massert( documentation.count( thiskey ), "no documentation for keyword " + thiskey + " to copy" );
84     docs.insert( std::pair<std::string,std::string>(thiskey,documentation.find(thiskey)->second) );
85     if( booldefs.count( thiskey ) ) bools.insert( std::pair<std::string,bool>( thiskey,booldefs.find(thiskey)->second) );
86     if( numdefs.count( thiskey ) ) nums.insert( std::pair<std::string,std::string>( thiskey,numdefs.find(thiskey)->second) );
87   }
88   for(unsigned i=0; i<reserved_keys.size(); ++i) {
89     std::string thiskey=reserved_keys[i];
90     for(unsigned j=0; j<kk.size(); ++j) plumed_massert( thiskey!=kk[j], "keyword " + thiskey + " is in twice" );
91     for(unsigned j=0; j<rk.size(); ++j) plumed_massert( thiskey!=rk[j], "keyword " + thiskey + " is in twice" );
92     rk.push_back( thiskey );
93     plumed_massert( types.count( thiskey ), "no type data on keyword " + thiskey + " to copy" );
94     tt.insert( std::pair<std::string,KeyType>( thiskey,types.find(thiskey)->second) );
95     if( (types.find(thiskey)->second).isAtomList() ) atags.insert( std::pair<std::string,std::string>( thiskey,atomtags.find(thiskey)->second) );
96     plumed_massert( allowmultiple.count( thiskey ), "no numbered data on keyword " + thiskey + " to copy" );
97     am.insert( std::pair<std::string,bool>(thiskey,allowmultiple.find(thiskey)->second) );
98     plumed_massert( documentation.count( thiskey ), "no documentation for keyword " + thiskey + " to copy" );
99     docs.insert( std::pair<std::string,std::string>(thiskey,documentation.find(thiskey)->second) );
100     if( booldefs.count( thiskey ) ) bools.insert( std::pair<std::string,bool>( thiskey,booldefs.find(thiskey)->second) );
101     if( numdefs.count( thiskey ) ) nums.insert( std::pair<std::string,std::string>( thiskey,numdefs.find(thiskey)->second) );
102   }
103   for(unsigned i=0; i<cnames.size(); ++i) {
104     std::string thisnam=cnames[i];
105     for(unsigned j=0; j<cnam.size(); ++j) plumed_massert( thisnam!=cnam[j], "component " + thisnam + " is in twice" );
106     cnam.push_back( thisnam );
107     plumed_massert( ckey.count( thisnam ), "no keyword data on component " + thisnam + " to copy" );
108     ck.insert( std::pair<std::string,std::string>( thisnam, ckey.find(thisnam)->second) );
109     plumed_massert( cdocs.count( thisnam ), "no documentation on component " + thisnam + " to copy" );
110     cd.insert( std::pair<std::string,std::string>( thisnam, cdocs.find(thisnam)->second) );
111   }
112 }
113 
reserve(const std::string & t,const std::string & k,const std::string & d)114 void Keywords::reserve( const std::string & t, const std::string & k, const std::string & d ) {
115   plumed_assert( !exists(k) && !reserved(k) );
116   std::string fd, lowkey=k;
117   // Convert to lower case
118   std::transform(lowkey.begin(),lowkey.end(),lowkey.begin(),tolower);
119 // Remove any underscore characters
120   for(unsigned i=0;; ++i) {
121     std::size_t num=lowkey.find_first_of("_");
122     if( num==std::string::npos ) break;
123     lowkey.erase( lowkey.begin() + num, lowkey.begin() + num + 1 );
124   }
125   if( t=="vessel" ) {
126     fd = d + " The final value can be referenced using <em>label</em>." + lowkey;
127     if(d.find("flag")==std::string::npos) fd += ".  You can use multiple instances of this keyword i.e. " +
128           k +"1, " + k + "2, " + k + "3...  The corresponding values are then "
129           "referenced using <em>label</em>."+ lowkey +"-1,  <em>label</em>." + lowkey +
130           "-2,  <em>label</em>." + lowkey + "-3...";
131     allowmultiple.insert( std::pair<std::string,bool>(k,true) );
132     types.insert( std::pair<std::string,KeyType>(k,KeyType("vessel")) );
133   } else if( t=="numbered" ) {
134     fd = d + " You can use multiple instances of this keyword i.e. " + k +"1, " + k + "2, " + k + "3...";
135     allowmultiple.insert( std::pair<std::string,bool>(k,true) );
136     types.insert( std::pair<std::string,KeyType>(k,KeyType("optional")) );
137   } else {
138     fd = d;
139     if( t=="atoms" && isaction ) fd = d + ".  For more information on how to specify lists of atoms see \\ref Group";
140     allowmultiple.insert( std::pair<std::string,bool>(k,false) );
141     types.insert( std::pair<std::string,KeyType>(k,KeyType(t)) );
142     if( (types.find(k)->second).isAtomList() ) atomtags.insert( std::pair<std::string,std::string>(k,t) );
143   }
144   documentation.insert( std::pair<std::string,std::string>(k,fd) );
145   reserved_keys.push_back(k);
146 }
147 
reserveFlag(const std::string & k,const bool def,const std::string & d)148 void Keywords::reserveFlag( const std::string & k, const bool def, const std::string & d ) {
149   plumed_assert( !exists(k) && !reserved(k) );
150   std::string defstr;
151   if( def ) { defstr="( default=on ) "; } else { defstr="( default=off ) "; }
152   types.insert( std::pair<std::string,KeyType>(k,KeyType("flag")) );
153   std::string fd,lowkey=k; std::transform(lowkey.begin(),lowkey.end(),lowkey.begin(),tolower);
154   fd=defstr + d;
155   documentation.insert( std::pair<std::string,std::string>(k,fd) );
156   allowmultiple.insert( std::pair<std::string,bool>(k,false) );
157   booldefs.insert( std::pair<std::string,bool>(k,def) );
158   reserved_keys.push_back(k);
159 }
160 
use(const std::string & k)161 void Keywords::use( const std::string & k ) {
162   plumed_massert( reserved(k), "the " + k + " keyword is not reserved");
163   for(unsigned i=0; i<reserved_keys.size(); ++i) {
164     if(reserved_keys[i]==k) keys.push_back( reserved_keys[i] );
165   }
166 }
167 
reset_style(const std::string & k,const std::string & style)168 void Keywords::reset_style( const std::string & k, const std::string & style ) {
169   plumed_massert( exists(k) || reserved(k), "no " + k + " keyword" );
170   (types.find(k)->second).setStyle(style);
171   if( (types.find(k)->second).isVessel() ) allowmultiple[k]=true;
172   if( (types.find(k)->second).isAtomList() ) atomtags.insert( std::pair<std::string,std::string>(k,style) );
173 }
174 
add(const std::string & t,const std::string & k,const std::string & d)175 void Keywords::add( const std::string & t, const std::string & k, const std::string & d ) {
176   plumed_massert( !exists(k) && t!="flag" && !reserved(k) && t!="vessel", "keyword " + k + " has already been registered");
177   std::string fd;
178   if( t=="numbered" ) {
179     fd=d + " You can use multiple instances of this keyword i.e. " + k +"1, " + k + "2, " + k + "3...";
180     allowmultiple.insert( std::pair<std::string,bool>(k,true) );
181     types.insert( std::pair<std::string,KeyType>(k, KeyType("optional")) );
182   } else {
183     fd=d;
184     allowmultiple.insert( std::pair<std::string,bool>(k,false) );
185     types.insert( std::pair<std::string,KeyType>(k,KeyType(t)) );
186     if( (types.find(k)->second).isAtomList() ) atomtags.insert( std::pair<std::string,std::string>(k,t) );
187   }
188   if( t=="atoms" && isaction ) fd = d + ".  For more information on how to specify lists of atoms see \\ref Group";
189   documentation.insert( std::pair<std::string,std::string>(k,fd) );
190   keys.push_back(k);
191 }
192 
add(const std::string & t,const std::string & k,const std::string & def,const std::string & d)193 void Keywords::add( const std::string & t, const std::string & k, const std::string &  def, const std::string & d ) {
194   plumed_assert( !exists(k) && !reserved(k) &&  (t=="compulsory" || t=="hidden" )); // An optional keyword can't have a default
195   types.insert(  std::pair<std::string,KeyType>(k, KeyType(t)) );
196   documentation.insert( std::pair<std::string,std::string>(k,"( default=" + def + " ) " + d) );
197   allowmultiple.insert( std::pair<std::string,bool>(k,false) );
198   numdefs.insert( std::pair<std::string,std::string>(k,def) );
199   keys.push_back(k);
200 }
201 
addFlag(const std::string & k,const bool def,const std::string & d)202 void Keywords::addFlag( const std::string & k, const bool def, const std::string & d ) {
203   plumed_massert( !exists(k) && !reserved(k), "keyword " + k + " has already been registered");
204   std::string defstr;
205   if( def ) { defstr="( default=on ) "; } else { defstr="( default=off ) "; }
206   types.insert( std::pair<std::string,KeyType>(k,KeyType("flag")) );
207   documentation.insert( std::pair<std::string,std::string>(k,defstr + d) );
208   allowmultiple.insert( std::pair<std::string,bool>(k,false) );
209   booldefs.insert( std::pair<std::string,bool>(k,def) );
210   keys.push_back(k);
211 }
212 
remove(const std::string & k)213 void Keywords::remove( const std::string & k ) {
214   bool found=false; unsigned j=0, n=0;
215 
216   while(true) {
217     for(j=0; j<keys.size(); j++) if(keys[j]==k)break;
218     for(n=0; n<reserved_keys.size(); n++) if(reserved_keys[n]==k)break;
219     if(j<keys.size()) {
220       keys.erase(keys.begin()+j);
221       found=true;
222     } else if(n<reserved_keys.size()) {
223       reserved_keys.erase(reserved_keys.begin()+n);
224       found=true;
225     } else break;
226   }
227   // Delete documentation, type and so on from the description
228   types.erase(k); documentation.erase(k); allowmultiple.erase(k); booldefs.erase(k); numdefs.erase(k);
229   plumed_massert(found,"You are trying to forbid " + k + " a keyword that isn't there"); // You have tried to forbid a keyword that isn't there
230 }
231 
numbered(const std::string & k) const232 bool Keywords::numbered( const std::string & k ) const {
233   if( style( k,"atoms") ) return true;
234   plumed_massert( allowmultiple.count(k), "Did not find keyword " + k );
235   return allowmultiple.find(k)->second;
236 }
237 
style(const std::string & k,const std::string & t) const238 bool Keywords::style( const std::string & k, const std::string & t ) const {
239   plumed_massert( types.count(k), "Did not find keyword " + k );
240 
241   if( (types.find(k)->second).toString()==t ) return true;
242   return false;
243 }
244 
size() const245 unsigned Keywords::size() const {
246   return keys.size();
247 }
248 
getKeyword(const unsigned i) const249 std::string Keywords::getKeyword( const unsigned i ) const {
250   plumed_assert( i<size() );
251   return keys[i];
252 }
253 
exists(const std::string & k) const254 bool Keywords::exists( const std::string & k ) const {
255   for(unsigned i=0; i<keys.size(); ++i) {
256     if( keys[i]==k ) return true;
257   }
258   return false;
259 }
260 
reserved(const std::string & k) const261 bool Keywords::reserved( const std::string & k ) const {
262   for(unsigned i=0; i<reserved_keys.size(); ++i) {
263     if( reserved_keys[i]==k ) return true;
264   }
265   return false;
266 }
267 
print_template(const std::string & actionname,bool include_optional) const268 void Keywords::print_template(const std::string& actionname, bool include_optional) const {
269   unsigned nkeys=0;
270   printf("%s",actionname.c_str());
271   for(unsigned i=0; i<keys.size(); ++i) {
272     if ( (types.find(keys[i])->second).isAtomList() ) nkeys++;
273   }
274   if( nkeys>0 ) {
275     std::string prevtag="start";
276     for(unsigned i=0; i<keys.size(); ++i) {
277       if( (types.find(keys[i])->second).isAtomList() ) {
278         plumed_massert( atomtags.count(keys[i]), "keyword " + keys[i] + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello");
279         if( prevtag!="start" && prevtag!=atomtags.find(keys[i])->second ) break;
280         if( (atomtags.find(keys[i])->second).find("residues")!=std::string::npos) printf(" %s=<residue selection>", keys[i].c_str() );
281         else printf(" %s=<atom selection>", keys[i].c_str() );
282         prevtag=atomtags.find(keys[i])->second;
283       }
284     }
285   }
286   nkeys=0;
287   for(unsigned i=0; i<keys.size(); ++i) {
288     if ( include_optional || \
289          (types.find(keys[i])->second).isCompulsory() ) nkeys++;
290   }
291   if( nkeys>0 ) {
292     for(unsigned i=0; i<keys.size(); ++i) {
293       if ( (types.find(keys[i])->second).isCompulsory() ) {
294         std::string def;
295         if( getDefaultValue( keys[i], def) ) {
296           printf(" %s=%s ", keys[i].c_str(), def.c_str() );
297         } else {
298           printf(" %s=    ", keys[i].c_str() );
299         }
300       } else if (include_optional) {
301         // TG no defaults for optional keywords?
302         printf(" [%s]", keys[i].c_str() );
303       }
304     }
305   }
306   printf("\n");
307 }
308 
print_vim() const309 void Keywords::print_vim() const {
310   for(unsigned i=0; i<keys.size(); ++i) {
311     if( (types.find(keys[i])->second).isFlag() ) {
312       printf( ",flag:%s", keys[i].c_str() );
313     } else {
314       if( allowmultiple.find(keys[i])->second ) printf(",numbered:%s",keys[i].c_str() );
315       else printf(",option:%s",keys[i].c_str() );
316     }
317   }
318   fprintf(stdout,"\n");
319   print(stdout);
320 }
321 
print_html() const322 void Keywords::print_html() const {
323 
324 // This is the part that outputs the details of the components
325   if( cnames.size()>0 ) {
326     unsigned ndef=0;
327     for(unsigned i=0; i<cnames.size(); ++i) {
328       if(ckey.find(cnames[i])->second=="default") ndef++;
329     }
330 
331     if( ndef>0 ) {
332       std::cout<<"\\par Description of components\n\n";
333       std::cout<<cstring<<"\n\n";
334       std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
335       printf("<tr> <td width=5%%> <b> Quantity </b> </td> <td> <b> Description </b> </td> </tr>\n");
336       unsigned nndef=0;
337       for(unsigned i=0; i<cnames.size(); ++i) {
338         //plumed_assert( ckey.find(cnames[i])->second=="default" );
339         if( ckey.find(cnames[i])->second!="default" ) { nndef++; continue; }
340         printf("<tr>\n");
341         printf("<td width=15%%> <b> %s </b></td>\n",cnames[i].c_str() );
342         printf("<td> %s </td>\n",(cdocs.find(cnames[i])->second).c_str() );
343         printf("</tr>\n");
344       }
345       std::cout<<"</table>\n\n";
346       if( nndef>0 ) {
347         std::cout<<"In addition the following quantities can be calculated by employing the keywords listed below"<<std::endl;
348         std::cout<<"\n\n";
349         std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
350         printf("<tr> <td width=5%%> <b> Quantity </b> </td> <td> <b> Keyword </b> </td> <td> <b> Description </b> </td> </tr>\n");
351         for(unsigned i=0; i<cnames.size(); ++i) {
352           if( ckey.find(cnames[i])->second!="default") {
353             printf("<tr>\n");
354             printf("<td width=5%%> <b> %s </b></td> <td width=10%%> <b> %s </b> </td> \n",
355                    cnames[i].c_str(),(ckey.find(cnames[i])->second).c_str() );
356             printf("<td> %s </td>\n",(cdocs.find(cnames[i])->second).c_str() );
357             printf("</tr>\n");
358           }
359         }
360         std::cout<<"</table>\n\n";
361       }
362     } else {
363       unsigned nregs=0;
364       for(unsigned i=0; i<cnames.size(); ++i) {
365         if( exists(ckey.find(cnames[i])->second) ) nregs++;
366       }
367       if( nregs>0 ) {
368         std::cout<<"\\par Description of components\n\n";
369         std::cout<<cstring<<"\n\n";
370         std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
371         printf("<tr> <td width=5%%> <b> Quantity </b> </td> <td> <b> Keyword </b> </td> <td> <b> Description </b> </td> </tr>\n");
372         for(unsigned i=0; i<cnames.size(); ++i) {
373           if( exists(ckey.find(cnames[i])->second) ) {
374             printf("<tr>\n");
375             printf("<td width=5%%> <b> %s </b></td> <td width=10%%> <b> %s </b> </td> \n",
376                    cnames[i].c_str(),(ckey.find(cnames[i])->second).c_str() );
377             printf("<td> %s </td>\n",(cdocs.find(cnames[i])->second).c_str() );
378             printf("</tr>\n");
379           }
380         }
381         std::cout<<"</table>\n\n";
382       }
383     }
384   }
385 
386   unsigned nkeys=0;
387   for(unsigned i=0; i<keys.size(); ++i) {
388     if ( (types.find(keys[i])->second).isAtomList() ) nkeys++;
389   }
390   if( nkeys>0 ) {
391     if(isaction && isatoms) std::cout<<"\\par The atoms involved can be specified using\n\n";
392     else if(isaction) std::cout<<"\\par The data to analyze can be the output from another analysis algorithm\n\n";
393     else std::cout<<"\\par The input trajectory is specified using one of the following\n\n";
394     std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
395     std::string prevtag="start"; unsigned counter=0;
396     for(unsigned i=0; i<keys.size(); ++i) {
397       if ( (types.find(keys[i])->second).isAtomList() ) {
398         plumed_massert( atomtags.count(keys[i]), "keyword " + keys[i] + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello");
399         if( prevtag!="start" && prevtag!=atomtags.find(keys[i])->second && isaction ) {
400           std::cout<<"</table>\n\n";
401           if( isatoms ) std::cout<<"\\par Or alternatively by using\n\n";
402           else if( counter==0 ) { std::cout<<"\\par Alternatively data can be collected from the trajectory using \n\n"; counter++; }
403           else std::cout<<"\\par Lastly data collected in a previous analysis action can be reanalyzed by using the keyword \n\n";
404           std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
405         }
406         print_html_item( keys[i] );
407         prevtag=atomtags.find(keys[i])->second;
408       }
409     }
410     std::cout<<"</table>\n\n";
411   }
412   nkeys=0;
413   for(unsigned i=0; i<keys.size(); ++i) {
414     if ( (types.find(keys[i])->second).isCompulsory() ) nkeys++;
415   }
416   if( nkeys>0 ) {
417     if(isaction) std::cout<< "\\par Compulsory keywords\n\n";
418     else std::cout<<"\\par The following must be present\n\n";
419     std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
420     for(unsigned i=0; i<keys.size(); ++i) {
421       if ( (types.find(keys[i])->second).isCompulsory() ) print_html_item( keys[i] );
422     }
423     std::cout<<"</table>\n\n";
424   }
425   nkeys=0;
426   for(unsigned i=0; i<keys.size(); ++i) {
427     if ( (types.find(keys[i])->second).isFlag() || (types.find(keys[i])->second).isOptional() || (types.find(keys[i])->second).isVessel() ) nkeys++;
428   }
429   if( nkeys>0 ) {
430     if(isaction) std::cout<<"\\par Options\n\n";
431     else std::cout<<"\\par The following options are available\n\n";
432     std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
433     for(unsigned i=0; i<keys.size(); ++i) {
434       if ( (types.find(keys[i])->second).isFlag() ) print_html_item( keys[i] );
435     }
436     std::cout<<"\n";
437   }
438   nkeys=0;
439   for(unsigned i=0; i<keys.size(); ++i) {
440     if ( (types.find(keys[i])->second).isOptional() || (types.find(keys[i])->second).isVessel() ) nkeys++;
441   }
442   if( nkeys>0 ) {
443     for(unsigned i=0; i<keys.size(); ++i) {
444       if ( (types.find(keys[i])->second).isOptional() || (types.find(keys[i])->second).isVessel() ) print_html_item( keys[i] );
445     }
446   }
447   std::cout<<"</table>\n\n";
448 }
449 
print_spelling() const450 void Keywords::print_spelling() const {
451   for(unsigned i=0; i<keys.size(); ++i) printf("%s\n", keys[i].c_str() );
452   for(unsigned i=0; i<cnames.size(); ++i) printf("%s\n",cnames[i].c_str() );
453 }
454 
print(FILE * out) const455 void Keywords::print( FILE* out ) const {
456   unsigned nkeys=0;
457   for(unsigned i=0; i<keys.size(); ++i) {
458     if ( (types.find(keys[i])->second).isAtomList() ) nkeys++;
459   }
460   if( nkeys>0 ) {
461     fprintf(out,"The input trajectory can be in any of the following formats: \n\n");
462     for(unsigned i=0; i<keys.size(); ++i) {
463       if ( (types.find(keys[i])->second).isAtomList() ) printKeyword( keys[i], out );
464     }
465   }
466   nkeys=0;
467   for(unsigned i=0; i<keys.size(); ++i) {
468     if ( (types.find(keys[i])->second).isCompulsory() ) nkeys++;
469   }
470   unsigned ncompulsory=nkeys;
471   if( nkeys>0 ) {
472     fprintf(out,"\nThe following arguments are compulsory: \n\n");
473     for(unsigned i=0; i<keys.size(); ++i) {
474       if ( (types.find(keys[i])->second).isCompulsory() ) printKeyword( keys[i], out );   //log.printKeyword( keys[i], documentation[i] );
475     }
476   }
477   nkeys=0;
478   for(unsigned i=0; i<keys.size(); ++i) {
479     if ( (types.find(keys[i])->second).isFlag() ) nkeys++;
480   }
481   if( nkeys>0 ) {
482     if(ncompulsory>0) fprintf( out,"\nIn addition you may use the following options: \n\n");
483     else fprintf( out,"\nThe following options are available\n\n");
484     for(unsigned i=0; i<keys.size(); ++i) {
485       if ( (types.find(keys[i])->second).isFlag() ) printKeyword( keys[i], out );   //log.printKeyword( keys[i], documentation[i] );
486     }
487   }
488   nkeys=0;
489   for(unsigned i=0; i<keys.size(); ++i) {
490     if ( (types.find(keys[i])->second).isOptional() || (types.find(keys[i])->second).isVessel() ) nkeys++;
491   }
492   if( nkeys>0 ) {
493     for(unsigned i=0; i<keys.size(); ++i) {
494       if ( (types.find(keys[i])->second).isOptional() || (types.find(keys[i])->second).isVessel() ) printKeyword( keys[i], out );   //log.printKeyword( keys[i], documentation[i] );
495     }
496     fprintf(out,"\n");
497   }
498 }
499 
printKeyword(const std::string & key,FILE * out) const500 void Keywords::printKeyword( const std::string& key, FILE* out ) const {
501   bool killdot=( (documentation.find(key)->second).find("\\f$")!=std::string::npos ); // Check for latex
502   std::vector<std::string> w=Tools::getWords( documentation.find(key)->second );
503   fprintf(out,"%23s - ", key.c_str() );
504   unsigned nl=0; std::string blank=" ";
505   for(unsigned i=0; i<w.size(); ++i) {
506     nl+=w[i].length() + 1;
507     if( nl>60 ) {
508       fprintf(out,"\n%23s   %s ", blank.c_str(), w[i].c_str() ); nl=0;
509     } else {
510       fprintf(out,"%s ", w[i].c_str() );
511     }
512     if( killdot && w[i].find(".")!=std::string::npos ) break; // If there is latex only write up to first dot
513   }
514   fprintf(out,"\n");
515 }
516 
print(Log & log) const517 void Keywords::print( Log& log ) const {
518   unsigned nkeys=0;
519   for(unsigned i=0; i<keys.size(); ++i) {
520     if ( (types.find(keys[i])->second).isAtomList() ) nkeys++;
521   }
522   if (nkeys>0 ) {
523     log.printf( "The input for this keyword can be specified using one of the following \n\n");
524     for(unsigned i=0; i<keys.size(); ++i) {
525       if ( (types.find(keys[i])->second).isAtomList() ) printKeyword( keys[i], log );   //log.printKeyword( keys[i], documentation[i] );
526     }
527   }
528   nkeys=0;
529   for(unsigned i=0; i<keys.size(); ++i) {
530     if ( (types.find(keys[i])->second).isCompulsory() ) nkeys++;
531   }
532   if( nkeys>0 ) {
533     log.printf( "\n The compulsory keywords for this action are: \n\n");
534     for(unsigned i=0; i<keys.size(); ++i) {
535       if ( (types.find(keys[i])->second).isCompulsory() ) printKeyword( keys[i], log );   //log.printKeyword( keys[i], documentation[i] );
536     }
537   }
538   nkeys=0;
539   for(unsigned i=0; i<keys.size(); ++i) {
540     if ( (types.find(keys[i])->second).isFlag() ) nkeys++;
541   }
542   if( nkeys>0 ) {
543     log.printf( "\n The following options are available: \n\n");
544     for(unsigned i=0; i<keys.size(); ++i) {
545       if ( (types.find(keys[i])->second).isFlag() ) printKeyword( keys[i], log );   //log.printKeyword( keys[i], documentation[i] );
546     }
547     log.printf("\n");
548   }
549   nkeys=0;
550   for(unsigned i=0; i<keys.size(); ++i) {
551     if ( (types.find(keys[i])->second).isOptional() ) nkeys++;
552   }
553   if( nkeys>0 ) {
554     for(unsigned i=0; i<keys.size(); ++i) {
555       if ( (types.find(keys[i])->second).isOptional() ) printKeyword( keys[i], log );   //log.printKeyword( keys[i], documentation[i] );
556     }
557     log.printf("\n");
558   }
559 }
560 
printKeyword(const std::string & key,Log & log) const561 void Keywords::printKeyword( const std::string& key, Log& log ) const {
562   bool killdot=( (documentation.find(key)->second).find("\\f$")!=std::string::npos ); // Check for latex
563   std::vector<std::string> w=Tools::getWords( documentation.find(key)->second );
564   log.printf("%23s - ", key.c_str() );
565   unsigned nl=0; std::string blank=" ";
566   for(unsigned i=0; i<w.size(); ++i) {
567     nl+=w[i].length() + 1;
568     if( nl>60 ) {
569       log.printf("\n%23s   %s ", blank.c_str(), w[i].c_str() ); nl=0;
570     } else {
571       log.printf("%s ", w[i].c_str() );
572     }
573     if( killdot && w[i].find(".")!=std::string::npos ) break; // If there is latex only write up to first dot
574   }
575   log.printf("\n");
576 }
577 
getTooltip(const std::string & name) const578 std::string Keywords::getTooltip( const std::string& name ) const {
579   std::size_t dd=name.find_first_of("0123456789"); std::string kname=name.substr(0,dd);
580   if( !exists(kname) ) return "<b> could not find this keyword </b>";
581   std::string mystring, docstr = documentation.find(kname)->second;
582   if( types.find(kname)->second.isCompulsory() ) {
583     mystring += "<b>compulsory keyword ";
584     if( docstr.find("default")!=std::string::npos ) {
585       std::size_t bra = docstr.find_first_of(")"); mystring += docstr.substr(0,bra+1); docstr = docstr.substr(bra+1);
586     }
587     mystring += "</b>\n";
588   }
589   std::vector<std::string> w=Tools::getWords( docstr ); unsigned nl=0;
590   for(unsigned i=0; i<w.size(); ++i) {
591     nl+=w[i].length() + 1;
592     if( nl>80 ) { mystring += w[i] + "\n"; nl=0; }
593     else { mystring += w[i] + " "; }
594     if( w[i].find(".")!=std::string::npos ) break; // Only write up the the first dot
595   }
596   return mystring;
597 }
598 
print_html_item(const std::string & key) const599 void Keywords::print_html_item( const std::string& key ) const {
600   printf("<tr>\n");
601   printf("<td width=15%%> <b> %s </b></td>\n",key.c_str() );
602   printf("<td> %s </td>\n",(documentation.find(key)->second).c_str() );
603   printf("</tr>\n");
604 }
605 
get(const unsigned k) const606 std::string Keywords::get( const unsigned k ) const {
607   plumed_assert( k<size() );
608   return keys[k];
609 }
610 
getLogicalDefault(std::string key,bool & def) const611 bool Keywords::getLogicalDefault( std::string key, bool& def ) const {
612   if( booldefs.find(key)!=booldefs.end() ) {
613     def=booldefs.find(key)->second;
614     return true;
615   } else {
616     return false;
617   }
618 }
619 
getDefaultValue(std::string key,std::string & def) const620 bool Keywords::getDefaultValue( std::string key, std::string& def ) const {
621   plumed_assert( style(key,"compulsory") || style(key,"hidden") );
622 
623   if( numdefs.find(key)!=numdefs.end() ) {
624     def=numdefs.find(key)->second;
625     return true;
626   } else {
627     return false;
628   }
629 }
630 
destroyData()631 void Keywords::destroyData() {
632   keys.clear(); reserved_keys.clear(); types.clear();
633   allowmultiple.clear(); documentation.clear();
634   booldefs.clear(); numdefs.clear(); atomtags.clear();
635   ckey.clear(); cdocs.clear(); ckey.clear();
636 }
637 
setComponentsIntroduction(const std::string & instr)638 void Keywords::setComponentsIntroduction( const std::string& instr ) {
639   cstring = instr;
640 }
641 
addOutputComponent(const std::string & name,const std::string & key,const std::string & descr)642 void Keywords::addOutputComponent( const std::string& name, const std::string& key, const std::string& descr ) {
643   plumed_assert( !outputComponentExists( name, false ) );
644   plumed_massert( name.find("-")==std::string::npos,"dash is reseved character in component names" );
645 
646   std::size_t num2=name.find_first_of("_");
647   if( num2!=std::string::npos ) plumed_massert( num2==0, "underscore is reserved character in component names that has special meaning");
648 
649   ckey.insert( std::pair<std::string,std::string>(name,key) );
650   cdocs.insert( std::pair<std::string,std::string>(name,descr) );
651   cnames.push_back(name);
652 }
653 
outputComponentExists(const std::string & name,const bool & custom) const654 bool Keywords::outputComponentExists( const std::string& name, const bool& custom ) const {
655   if( custom && cstring.find("customize")!=std::string::npos ) return true;
656 
657   std::string sname;
658   std::size_t num=name.find_first_of("-");
659   std::size_t num2=name.find_last_of("_");
660 
661   if( num2!=std::string::npos ) sname=name.substr(num2);
662   else if( num!=std::string::npos ) sname=name.substr(0,num);
663   else sname=name;
664 
665   for(unsigned i=0; i<cnames.size(); ++i) {
666     if( sname==cnames[i] ) return true;
667   }
668   return false;
669 }
670 
getOutputComponentDescription(const std::string & name) const671 std::string Keywords::getOutputComponentDescription( const std::string& name ) const {
672   if( cstring.find("customized")!=std::string::npos ) return "the label of this action is set by user in the input. See documentation above.";
673 
674   bool found=false;
675   for(unsigned i=0; i<cnames.size(); ++i) {
676     if( name==cnames[i] ) found=true;
677   }
678   if( !found ) plumed_merror("could not find output component named " + name );
679   return cdocs.find(name)->second;
680 }
681 
removeComponent(const std::string & name)682 void Keywords::removeComponent( const std::string& name ) {
683   bool found=false;
684 
685   while(true) {
686     unsigned j;
687     for(j=0; j<cnames.size(); j++) if(cnames[j]==name)break;
688     if(j<cnames.size()) {
689       cnames.erase(cnames.begin()+j);
690       found=true;
691     } else break;
692   }
693   // Delete documentation, type and so on from the description
694   cdocs.erase(name); ckey.erase(name);
695   plumed_massert(found,"You are trying to remove " + name + " a component that isn't there");
696 }
697 
698 }
699