Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2011-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 "Tools.h"
23 : #include "AtomNumber.h"
24 : #include "Exception.h"
25 : #include "IFile.h"
26 : #include "lepton/Lepton.h"
27 : #include <cstring>
28 : #include <dirent.h>
29 : #include <iostream>
30 : #include <map>
31 : #if defined(__PLUMED_HAS_CHDIR) || defined(__PLUMED_HAS_GETCWD)
32 : #include <unistd.h>
33 : #endif
34 :
35 : using namespace std;
36 : namespace PLMD {
37 :
38 : template<class T>
39 6492614 : bool Tools::convertToAny(const string & str,T & t) {
40 12985245 : istringstream istr(str.c_str());
41 6492626 : bool ok=static_cast<bool>(istr>>t);
42 6492626 : if(!ok) return false;
43 : string remaining;
44 4404333 : istr>>remaining;
45 10896942 : return remaining.length()==0;
46 : }
47 :
48 213328 : bool Tools::convert(const string & str,int & t) {
49 213328 : return convertToAny(str,t);
50 : }
51 :
52 85 : bool Tools::convert(const string & str,long int & t) {
53 85 : return convertToAny(str,t);
54 : }
55 :
56 357408 : bool Tools::convert(const string & str,unsigned & t) {
57 357408 : return convertToAny(str,t);
58 : }
59 :
60 207977 : bool Tools::convert(const string & str,AtomNumber &a) {
61 : unsigned i;
62 207977 : bool r=convert(str,i);
63 207977 : if(r) a.setSerial(i);
64 207977 : return r;
65 : }
66 :
67 : template<class T>
68 5921793 : bool Tools::convertToReal(const string & str,T & t) {
69 5921793 : if(convertToAny(str,t)) return true;
70 8319635 : if(str=="PI" || str=="+PI" || str=="+pi" || str=="pi") {
71 1040008 : t=pi; return true;
72 2079887 : } else if(str=="-PI" || str=="-pi") {
73 1039924 : t=-pi; return true;
74 : }
75 : try {
76 22 : t=lepton::Parser::parse(str).evaluate(lepton::Constants());
77 2 : return true;
78 18 : } catch(const PLMD::lepton::Exception& exc) {
79 : }
80 18 : if( str.find("PI")!=std::string::npos ) {
81 0 : std::size_t pi_start=str.find_first_of("PI");
82 0 : if(str.substr(pi_start)!="PI") return false;
83 0 : istringstream nstr(str.substr(0,pi_start));
84 0 : T ff=0.0; bool ok=static_cast<bool>(nstr>>ff);
85 0 : if(!ok) return false;
86 0 : t=ff*pi;
87 0 : std::string remains; nstr>>remains;
88 0 : return remains.length()==0;
89 18 : } else if( str.find("pi")!=std::string::npos ) {
90 14 : std::size_t pi_start=str.find_first_of("pi");
91 28 : if(str.substr(pi_start)!="pi") return false;
92 28 : istringstream nstr(str.substr(0,pi_start));
93 28 : T ff=0.0; bool ok=static_cast<bool>(nstr>>ff);
94 14 : if(!ok) return false;
95 14 : t=ff*pi;
96 14 : std::string remains; nstr>>remains;
97 28 : return remains.length()==0;
98 4 : } else if(str=="NAN") {
99 0 : t=std::numeric_limits<double>::quiet_NaN();
100 0 : return true;
101 : }
102 : return false;
103 : }
104 :
105 0 : bool Tools::convert(const string & str,float & t) {
106 0 : return convertToReal(str,t);
107 : }
108 :
109 5921668 : bool Tools::convert(const string & str,double & t) {
110 5921668 : return convertToReal(str,t);
111 : }
112 :
113 128 : bool Tools::convert(const string & str,long double & t) {
114 128 : return convertToReal(str,t);
115 : }
116 :
117 31312 : bool Tools::convert(const string & str,string & t) {
118 : t=str;
119 31312 : return true;
120 : }
121 :
122 904226 : vector<string> Tools::getWords(const string & line,const char* separators,int * parlevel,const char* parenthesis) {
123 904226 : plumed_massert(strlen(parenthesis)==1,"multiple parenthesis type not available");
124 904226 : plumed_massert(parenthesis[0]=='(' || parenthesis[0]=='[' || parenthesis[0]=='{',
125 0 : "only ( [ { allowed as parenthesis");
126 904226 : if(!separators) separators=" \t\n";
127 904226 : const string sep(separators);
128 904226 : char openpar=parenthesis[0];
129 : char closepar;
130 : if(openpar=='(') closepar=')';
131 904226 : if(openpar=='[') closepar=']';
132 904226 : if(openpar=='{') closepar='}';
133 : vector<string> words;
134 : string word;
135 : int parenthesisLevel=0;
136 904226 : if(parlevel) parenthesisLevel=*parlevel;
137 52471068 : for(unsigned i=0; i<line.length(); i++) {
138 : bool found=false;
139 : bool onParenthesis=false;
140 25783421 : if(line[i]==openpar || line[i]==closepar) onParenthesis=true;
141 25783421 : if(line[i]==closepar) {
142 1915 : parenthesisLevel--;
143 1915 : plumed_massert(parenthesisLevel>=0,"Extra closed parenthesis in '" + line + "'");
144 : }
145 206139958 : if(parenthesisLevel==0) for(unsigned j=0; j<sep.length(); j++) if(line[i]==sep[j]) found=true;
146 : // If at parenthesis level zero (outer)
147 25783421 : if(!(parenthesisLevel==0 && (found||onParenthesis))) word.push_back(line[i]);
148 : //if(onParenthesis) word.push_back(' ');
149 25783421 : if(line[i]==openpar) parenthesisLevel++;
150 31303148 : if(found && word.length()>0) {
151 1322673 : if(!parlevel) plumed_massert(parenthesisLevel==0,"Unmatching parenthesis in '" + line + "'");
152 1322673 : words.push_back(word);
153 : word.clear();
154 : }
155 : }
156 904226 : if(word.length()>0) {
157 791300 : if(!parlevel) plumed_massert(parenthesisLevel==0,"Unmatching parenthesis in '" + line + "'");
158 791300 : words.push_back(word);
159 : }
160 904226 : if(parlevel) *parlevel=parenthesisLevel;
161 904226 : return words;
162 : }
163 :
164 6639 : bool Tools::getParsedLine(IFile& ifile,vector<string> & words) {
165 6639 : string line("");
166 6639 : words.clear();
167 : bool stat;
168 : bool inside=false;
169 6639 : int parlevel=0;
170 : bool mergenext=false;
171 28591 : while((stat=ifile.getline(line))) {
172 21281 : trimComments(line);
173 21281 : trim(line);
174 26416 : if(line.length()==0) continue;
175 16146 : vector<string> w=getWords(line,NULL,&parlevel);
176 16146 : if(!w.empty()) {
177 26322 : if(inside && *(w.begin())=="...") {
178 : inside=false;
179 2091 : if(w.size()==2) plumed_massert(w[1]==words[0],"second word in terminating \"...\" "+w[1]+" line, if present, should be equal to first word of directive: "+words[0]);
180 1085 : plumed_massert(w.size()<=2,"terminating \"...\" lines cannot consist of more than two words");
181 1085 : w.clear();
182 15060 : } else if(*(w.end()-1)=="...") {
183 : inside=true;
184 1085 : w.erase(w.end()-1);
185 : };
186 : int i0=0;
187 16273 : if(mergenext && words.size()>0 && w.size()>0) {
188 192 : words[words.size()-1]+=" "+w[0];
189 : i0=1;
190 : }
191 97462 : for(unsigned i=i0; i<w.size(); ++i) words.push_back(w[i]);
192 : }
193 16146 : mergenext=(parlevel>0);
194 16146 : if(!inside)break;
195 10178 : }
196 6639 : plumed_massert(parlevel==0,"non matching parenthesis");
197 6639 : if(words.size()>0) return true;
198 671 : return stat;
199 : }
200 :
201 :
202 2557435 : bool Tools::getline(FILE* fp,string & line) {
203 : line="";
204 : const int bufferlength=1024;
205 : char buffer[bufferlength];
206 : bool ret;
207 2618813440 : for(int i=0; i<bufferlength; i++) buffer[i]='\0';
208 2557435 : while((ret=fgets(buffer,bufferlength,fp))) {
209 2556778 : line.append(buffer);
210 2556778 : unsigned ss=strlen(buffer);
211 2556778 : if(ss>0) if(buffer[ss-1]=='\n') break;
212 : };
213 7670991 : if(line.length()>0) if(*(line.end()-1)=='\n') line.erase(line.end()-1);
214 5115393 : if(line.length()>0) if(*(line.end()-1)=='\r') line.erase(line.end()-1);
215 2557435 : return ret;
216 : }
217 :
218 415252 : void Tools::trim(string & s) {
219 415252 : size_t n=s.find_last_not_of(" \t");
220 830504 : s=s.substr(0,n+1);
221 415252 : }
222 :
223 178685 : void Tools::trimComments(string & s) {
224 178685 : size_t n=s.find_first_of("#");
225 357370 : s=s.substr(0,n);
226 178685 : }
227 :
228 290537 : bool Tools::caseInSensStringCompare(const std::string & str1, const std::string &str2)
229 : {
230 1019762 : return ((str1.size() == str2.size()) && std::equal(str1.begin(), str1.end(), str2.begin(), [](char c1, char c2) {
231 465184 : return (c1 == c2 || std::toupper(c1) == std::toupper(c2));
232 1019762 : }));
233 : }
234 :
235 62558 : bool Tools::getKey(vector<string>& line,const string & key,string & s,int rep) {
236 : s.clear();
237 385883 : for(auto p=line.begin(); p!=line.end(); ++p) {
238 290537 : if((*p).length()==0) continue;
239 290537 : string x=(*p).substr(0,key.length());
240 290537 : if(caseInSensStringCompare(x,key)) {
241 29770 : if((*p).length()==key.length())return false;
242 29769 : string tmp=(*p).substr(key.length(),(*p).length());
243 29769 : line.erase(p);
244 : s=tmp;
245 29769 : const std::string multi("@replicas:");
246 29769 : if(rep>=0 && startWith(s,multi)) {
247 48 : s=s.substr(multi.length(),s.length());
248 24 : std::vector<std::string> words=getWords(s,"\t\n ,");
249 24 : plumed_massert(rep<static_cast<int>(words.size()),"Number of fields in " + s + " not consistent with number of replicas");
250 48 : s=words[rep];
251 : }
252 : return true;
253 : }
254 : };
255 : return false;
256 : }
257 :
258 4007 : void Tools::interpretRanges(std::vector<std::string>&s) {
259 : vector<string> news;
260 16972 : for(const auto & p :s) {
261 8958 : news.push_back(p);
262 8958 : size_t dash=p.find("-");
263 16953 : if(dash==string::npos) continue;
264 : int first;
265 3064 : if(!Tools::convert(p.substr(0,dash),first)) continue;
266 963 : int stride=1;
267 : int second;
268 1926 : size_t colon=p.substr(dash+1).find(":");
269 963 : if(colon!=string::npos) {
270 68 : if(!Tools::convert(p.substr(dash+1).substr(0,colon),second) ||
271 68 : !Tools::convert(p.substr(dash+1).substr(colon+1),stride)) continue;
272 : } else {
273 1892 : if(!Tools::convert(p.substr(dash+1),second)) continue;
274 : }
275 963 : news.resize(news.size()-1);
276 963 : if(first<=second) {
277 962 : plumed_massert(stride>0,"interpreting ranges "+ p + ", stride should be positive");
278 197024 : for(int i=first; i<=second; i+=stride) {
279 : string ss;
280 197024 : convert(i,ss);
281 197024 : news.push_back(ss);
282 : }
283 : } else {
284 1 : plumed_massert(stride<0,"interpreting ranges "+ p + ", stride should be positive");
285 2 : for(int i=first; i>=second; i+=stride) {
286 : string ss;
287 2 : convert(i,ss);
288 2 : news.push_back(ss);
289 : }
290 : }
291 : }
292 4007 : s=news;
293 4007 : }
294 :
295 6228 : void Tools::interpretLabel(vector<string>&s) {
296 6477 : if(s.size()<2)return;
297 5979 : string s0=s[0];
298 5979 : unsigned l=s0.length();
299 5979 : if(l<1) return;
300 11958 : if(s0[l-1]==':') {
301 3245 : s[0]=s[1];
302 12980 : s[1]="LABEL="+s0.substr(0,l-1);
303 : }
304 11958 : std::transform(s[0].begin(), s[0].end(), s[0].begin(), ::toupper);
305 : }
306 :
307 3822 : vector<string> Tools::ls(const string&d) {
308 : DIR*dir;
309 : vector<string> result;
310 3822 : if ((dir=opendir(d.c_str()))) {
311 : #if defined(__PLUMED_HAS_READDIR_R)
312 : struct dirent ent;
313 : #endif
314 : while(true) {
315 : struct dirent *res;
316 : #if defined(__PLUMED_HAS_READDIR_R)
317 59718 : readdir_r(dir,&ent,&res);
318 : #else
319 : // cppcheck complains about this:
320 : // (portability) Non reentrant function 'readdir' called. For threadsafe applications it is recommended to use the reentrant replacement function 'readdir_r'.
321 : // since we use it only if readdir_r is not available, I suppress the warning
322 : // GB
323 : // cppcheck-suppress readdirCalled
324 : res=readdir(dir);
325 : #endif
326 59718 : if(!res) break;
327 320088 : if(string(res->d_name)!="." && string(res->d_name)!="..") result.push_back(res->d_name);
328 : }
329 55896 : closedir (dir);
330 : }
331 3822 : return result;
332 : }
333 :
334 4137 : void Tools::stripLeadingAndTrailingBlanks( std::string& str ) {
335 4137 : std::size_t first=str.find_first_not_of(' ');
336 4137 : std::size_t last=str.find_last_not_of(' ');
337 8242 : if( first<=last && first!=std::string::npos) str=str.substr(first,last+1);
338 4137 : }
339 :
340 10159 : std::string Tools::extension(const std::string&s) {
341 10159 : size_t n=s.find_last_of(".");
342 : std::string ext;
343 17777 : if(n!=std::string::npos && n+1<s.length() && n+5>=s.length()) {
344 14908 : ext=s.substr(n+1);
345 7454 : if(ext.find("/")!=std::string::npos) ext="";
346 7454 : string base=s.substr(0,n);
347 7454 : if(base.length()==0) ext="";
348 14908 : if(base.length()>0 && base[base.length()-1]=='/') ext="";
349 : }
350 10159 : return ext;
351 : }
352 :
353 14 : double Tools::bessel0( const double& val ) {
354 14 : if (fabs(val)<3.75) {
355 2 : double y = Tools::fastpow( val/3.75, 2 );
356 2 : return 1 + y*(3.5156229 +y*(3.0899424 + y*(1.2067492+y*(0.2659732+y*(0.0360768+y*0.0045813)))));
357 : }
358 12 : double ax=fabs(val), y=3.75/ax, bx=std::exp(ax)/sqrt(ax);
359 12 : ax=0.39894228+y*(0.01328592+y*(0.00225319+y*(-0.00157565+y*(0.00916281+y*(-0.02057706+y*(0.02635537+y*(-0.01647633+y*0.00392377)))))));
360 12 : return ax*bx;
361 : }
362 :
363 728701 : bool Tools::startWith(const std::string & full,const std::string &start) {
364 1457402 : return (full.substr(0,start.length())==start);
365 : }
366 :
367 54537 : bool Tools::findKeyword(const std::vector<std::string>&line,const std::string&key) {
368 54537 : const std::string search(key+"=");
369 576473 : for(const auto & p : line) {
370 490434 : if(startWith(p,search)) return true;
371 : }
372 : return false;
373 : }
374 :
375 469 : Tools::DirectoryChanger::DirectoryChanger(const char*path) {
376 469 : if(!path) return;
377 469 : if(std::strlen(path)==0) return;
378 : #ifdef __PLUMED_HAS_GETCWD
379 0 : char* ret=getcwd(cwd,buffersize);
380 0 : plumed_assert(ret)<<"Name of current directory too long, increase buffer size";
381 : #else
382 : plumed_error()<<"You are trying to use DirectoryChanger but your system does not support getcwd";
383 : #endif
384 : #ifdef __PLUMED_HAS_CHDIR
385 0 : int r=chdir(path);
386 0 : plumed_assert(r==0) <<"Cannot chdir to directory "<<path<<". The directory must exist!";
387 : #else
388 : plumed_error()<<"You are trying to use DirectoryChanger but your system does not support chdir";
389 : #endif
390 : }
391 :
392 469 : Tools::DirectoryChanger::~DirectoryChanger() {
393 : #ifdef __PLUMED_HAS_CHDIR
394 469 : if(strlen(cwd)==0) return;
395 0 : int ret=chdir(cwd);
396 : // we cannot put an assertion here (in a destructor) otherwise cppcheck complains
397 : // we thus just report the problem
398 0 : if(ret!=0) fprintf(stderr,"+++ WARNING: cannot cd back to directory %s\n",cwd);
399 : #endif
400 469 : }
401 :
402 5874 : }
|