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