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 "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 : #include <iomanip>
36 :
37 : namespace PLMD {
38 :
39 : template<class T>
40 16511170 : bool Tools::convertToAny(const std::string & str,T & t) {
41 30797180 : std::istringstream istr(str.c_str());
42 16511170 : bool ok=static_cast<bool>(istr>>t);
43 16511170 : if(!ok) return false;
44 : std::string remaining;
45 14407813 : istr>>remaining;
46 14407813 : return remaining.length()==0;
47 16511170 : }
48 :
49 2222566 : bool Tools::convertNoexcept(const std::string & str,int & t) {
50 2222566 : return convertToInt(str,t);
51 : }
52 :
53 88 : bool Tools::convertNoexcept(const std::string & str,long int & t) {
54 88 : return convertToInt(str,t);
55 : }
56 :
57 4876 : bool Tools::convertNoexcept(const std::string & str,unsigned & t) {
58 4876 : return convertToInt(str,t);
59 : }
60 :
61 111 : bool Tools::convertNoexcept(const std::string & str,long unsigned & t) {
62 111 : return convertToInt(str,t);
63 : }
64 :
65 245185 : bool Tools::convertNoexcept(const std::string & str,AtomNumber &a) {
66 : // Note: AtomNumber's are NOT converted as int, so as to
67 : // avoid using lepton conversions.
68 : unsigned i;
69 245185 : bool r=convertToAny(str,i);
70 245185 : if(r) a.setSerial(i);
71 245185 : return r;
72 : }
73 :
74 : template<class T>
75 2227641 : bool Tools::convertToInt(const std::string & str,T & t) {
76 : // First try standard conversion
77 2227641 : if(convertToAny(str,t)) return true;
78 : // Then use lepton
79 : try {
80 2008031 : double r=lepton::Parser::parse(str).evaluate(lepton::Constants());
81 :
82 : // now sanity checks on the resulting number
83 :
84 : // it should not overflow the requested int type:
85 : // (see https://stackoverflow.com/a/526092)
86 2008031 : if(r>std::nextafter(std::numeric_limits<T>::max(), 0)) return false;
87 2008031 : if(r<std::nextafter(std::numeric_limits<T>::min(), 0)) return false;
88 :
89 : // do the actual conversion
90 2008031 : auto tmp=static_cast<T>(std::round(r));
91 :
92 : // it should be *very close* to itself if converted back to double
93 2008031 : double diff= r-static_cast<double>(tmp);
94 2008031 : if(diff*diff > 1e-20) return false;
95 : // this is to accomodate small numerical errors and allow e.g. exp(log(7)) to be integer
96 :
97 : // it should be change if incremented or decremented by one (see https://stackoverflow.com/a/43656140)
98 2008027 : if(r == static_cast<double>(tmp-1)) return false;
99 2008026 : if(r == static_cast<double>(tmp+1)) return false;
100 :
101 : // everything is fine, then store in t
102 2008026 : t=tmp;
103 2008026 : return true;
104 0 : } catch(const PLMD::lepton::Exception& exc) {
105 : }
106 0 : return false;
107 : }
108 :
109 :
110 : template<class T>
111 14035750 : bool Tools::convertToReal(const std::string & str,T & t) {
112 14035750 : if(convertToAny(str,t)) return true;
113 8377914 : if(str=="PI" || str=="+PI" || str=="+pi" || str=="pi") {
114 1047281 : t=pi; return true;
115 2094520 : } else if(str=="-PI" || str=="-pi") {
116 1047205 : t=-pi; return true;
117 : }
118 : try {
119 55 : t=lepton::Parser::parse(str).evaluate(lepton::Constants());
120 24 : return true;
121 31 : } catch(const PLMD::lepton::Exception& exc) {
122 : }
123 31 : if( str.find("PI")!=std::string::npos ) {
124 0 : std::size_t pi_start=str.find_first_of("PI");
125 0 : if(str.substr(pi_start)!="PI") return false;
126 0 : std::istringstream nstr(str.substr(0,pi_start));
127 0 : T ff=0.0; bool ok=static_cast<bool>(nstr>>ff);
128 0 : if(!ok) return false;
129 0 : t=ff*pi;
130 0 : std::string remains; nstr>>remains;
131 0 : return remains.length()==0;
132 31 : } else if( str.find("pi")!=std::string::npos ) {
133 15 : std::size_t pi_start=str.find_first_of("pi");
134 30 : if(str.substr(pi_start)!="pi") return false;
135 15 : std::istringstream nstr(str.substr(0,pi_start));
136 15 : T ff=0.0; bool ok=static_cast<bool>(nstr>>ff);
137 15 : if(!ok) return false;
138 15 : t=ff*pi;
139 15 : std::string remains; nstr>>remains;
140 15 : return remains.length()==0;
141 31 : } else if(str=="NAN") {
142 0 : t=std::numeric_limits<double>::quiet_NaN();
143 0 : return true;
144 : }
145 : return false;
146 : }
147 :
148 0 : bool Tools::convertNoexcept(const std::string & str,float & t) {
149 0 : return convertToReal(str,t);
150 : }
151 :
152 14035622 : bool Tools::convertNoexcept(const std::string & str,double & t) {
153 14035622 : return convertToReal(str,t);
154 : }
155 :
156 128 : bool Tools::convertNoexcept(const std::string & str,long double & t) {
157 128 : return convertToReal(str,t);
158 : }
159 :
160 78063 : bool Tools::convertNoexcept(const std::string & str,std::string & t) {
161 : t=str;
162 78063 : return true;
163 : }
164 :
165 3790859 : std::vector<std::string> Tools::getWords(const std::string & line,const char* separators,int * parlevel,const char* parenthesis, const bool& delete_parenthesis) {
166 3790859 : plumed_massert(std::strlen(parenthesis)==1,"multiple parenthesis type not available");
167 3790859 : plumed_massert(parenthesis[0]=='(' || parenthesis[0]=='[' || parenthesis[0]=='{',
168 : "only ( [ { allowed as parenthesis");
169 3790859 : if(!separators) separators=" \t\n";
170 3790859 : const std::string sep(separators);
171 3790859 : char openpar=parenthesis[0];
172 : char closepar;
173 : if(openpar=='(') closepar=')';
174 3790859 : if(openpar=='[') closepar=']';
175 3790859 : if(openpar=='{') closepar='}';
176 : std::vector<std::string> words;
177 : std::string word;
178 : int parenthesisLevel=0;
179 3790859 : if(parlevel) parenthesisLevel=*parlevel;
180 196819643 : for(unsigned i=0; i<line.length(); i++) {
181 : bool found=false;
182 : bool onParenthesis=false;
183 193028784 : if( (line[i]==openpar || line[i]==closepar) && delete_parenthesis ) onParenthesis=true;
184 193028784 : if(line[i]==closepar) {
185 2022 : parenthesisLevel--;
186 2022 : plumed_massert(parenthesisLevel>=0,"Extra closed parenthesis in '" + line + "'");
187 : }
188 772345627 : if(parenthesisLevel==0) for(unsigned j=0; j<sep.length(); j++) if(line[i]==sep[j]) found=true;
189 : // If at parenthesis level zero (outer)
190 193028784 : if(!(parenthesisLevel==0 && (found||onParenthesis))) word.push_back(line[i]);
191 : //if(onParenthesis) word.push_back(' ');
192 193028784 : if(line[i]==openpar) parenthesisLevel++;
193 193028784 : if(found && word.length()>0) {
194 11518017 : if(!parlevel) plumed_massert(parenthesisLevel==0,"Unmatching parenthesis in '" + line + "'");
195 11518017 : words.push_back(word);
196 : word.clear();
197 : }
198 : }
199 3790859 : if(word.length()>0) {
200 3673744 : if(!parlevel) plumed_massert(parenthesisLevel==0,"Unmatching parenthesis in '" + line + "'");
201 3673744 : words.push_back(word);
202 : }
203 3790859 : if(parlevel) *parlevel=parenthesisLevel;
204 3790859 : return words;
205 0 : }
206 :
207 14560 : bool Tools::getParsedLine(IFile& ifile,std::vector<std::string> & words, bool trimcomments) {
208 14560 : std::string line("");
209 14560 : words.clear();
210 : bool stat;
211 : bool inside=false;
212 14560 : int parlevel=0;
213 : bool mergenext=false;
214 31876 : while((stat=ifile.getline(line))) {
215 31098 : if(trimcomments) trimComments(line);
216 31098 : trim(line);
217 31098 : if(line.length()==0) continue;
218 25038 : std::vector<std::string> w=getWords(line,NULL,&parlevel,"{",trimcomments);
219 25038 : if(!w.empty()) {
220 36292 : if(inside && *(w.begin())=="...") {
221 : inside=false;
222 1203 : 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]);
223 1203 : plumed_massert(w.size()<=2,"terminating \"...\" lines cannot consist of more than two words");
224 1203 : w.clear(); if(!trimcomments) words.push_back("...");
225 23834 : } else if(*(w.end()-1)=="...") {
226 : inside=true;
227 : w.erase(w.end()-1);
228 : };
229 : int i0=0;
230 25037 : if(mergenext && words.size()>0 && w.size()>0) {
231 128 : words[words.size()-1]+=" "+w[0];
232 : i0=1;
233 : }
234 89698 : for(unsigned i=i0; i<w.size(); ++i) words.push_back(w[i]);
235 : }
236 25038 : mergenext=(parlevel>0);
237 25038 : if(!inside)break;
238 11256 : if(!trimcomments && parlevel==0) words.push_back("@newline");
239 11256 : else if(!trimcomments) words[words.size()-1] += " @newline";
240 25038 : }
241 14560 : plumed_massert(parlevel==0,"non matching parenthesis");
242 14560 : if(words.size()>0) return true;
243 : return stat;
244 : }
245 :
246 :
247 2700044 : bool Tools::getline(FILE* fp,std::string & line) {
248 : line="";
249 : const int bufferlength=1024;
250 : char buffer[bufferlength];
251 : bool ret;
252 2767545100 : for(int i=0; i<bufferlength; i++) buffer[i]='\0';
253 2700044 : while((ret=fgets(buffer,bufferlength,fp))) {
254 2699321 : line.append(buffer);
255 2699321 : unsigned ss=std::strlen(buffer);
256 2699321 : if(ss>0) if(buffer[ss-1]=='\n') break;
257 : };
258 2700044 : if(line.length()>0) if(*(line.end()-1)=='\n') line.erase(line.end()-1);
259 2700044 : if(line.length()>0) if(*(line.end()-1)=='\r') line.erase(line.end()-1);
260 2700044 : return ret;
261 : }
262 :
263 836078 : void Tools::trim(std::string & s) {
264 836078 : size_t n=s.find_last_not_of(" \t");
265 836078 : s=s.substr(0,n+1);
266 836078 : }
267 :
268 1354615 : void Tools::trimComments(std::string & s) {
269 1354615 : size_t n=s.find_first_of("#");
270 1354615 : s=s.substr(0,n);
271 1354615 : }
272 :
273 323265 : bool Tools::caseInSensStringCompare(const std::string & str1, const std::string &str2)
274 : {
275 323265 : return ((str1.size() == str2.size()) && std::equal(str1.begin(), str1.end(), str2.begin(), [](char c1, char c2) {
276 632368 : return (c1 == c2 || std::toupper(c1) == std::toupper(c2));
277 323265 : }));
278 : }
279 :
280 90613 : bool Tools::getKey(std::vector<std::string>& line,const std::string & key,std::string & s,int rep) {
281 : s.clear();
282 359857 : for(auto p=line.begin(); p!=line.end(); ++p) {
283 323265 : if((*p).length()==0) continue;
284 323265 : std::string x=(*p).substr(0,key.length());
285 323265 : if(caseInSensStringCompare(x,key)) {
286 54021 : if((*p).length()==key.length())return false;
287 54020 : std::string tmp=(*p).substr(key.length(),(*p).length());
288 : line.erase(p);
289 : s=tmp;
290 54020 : const std::string multi("@replicas:");
291 54020 : if(rep>=0 && startWith(s,multi)) {
292 24 : s=s.substr(multi.length(),s.length());
293 24 : std::vector<std::string> words=getWords(s,"\t\n ,");
294 24 : plumed_massert(rep<static_cast<int>(words.size()),"Number of fields in " + s + " not consistent with number of replicas");
295 24 : s=words[rep];
296 24 : }
297 : return true;
298 : }
299 : };
300 : return false;
301 : }
302 :
303 11307 : void Tools::interpretRanges(std::vector<std::string>&s) {
304 : std::vector<std::string> news;
305 56956 : for(const auto & p :s) {
306 45649 : news.push_back(p);
307 45649 : size_t dash=p.find("-");
308 46260 : if(dash==std::string::npos) continue;
309 : int first;
310 3184 : if(!Tools::convertToAny(p.substr(0,dash),first)) continue;
311 981 : int stride=1;
312 : int second;
313 981 : size_t colon=p.substr(dash+1).find(":");
314 981 : if(colon!=std::string::npos) {
315 63 : if(!Tools::convertToAny(p.substr(dash+1).substr(0,colon),second) ||
316 84 : !Tools::convertToAny(p.substr(dash+1).substr(colon+1),stride)) continue;
317 : } else {
318 1920 : if(!Tools::convertToAny(p.substr(dash+1),second)) continue;
319 : }
320 981 : news.resize(news.size()-1);
321 981 : if(first<=second) {
322 980 : plumed_massert(stride>0,"interpreting ranges "+ p + ", stride should be positive");
323 198399 : for(int i=first; i<=second; i+=stride) {
324 : std::string ss;
325 197419 : convert(i,ss);
326 197419 : news.push_back(ss);
327 : }
328 : } else {
329 1 : plumed_massert(stride<0,"interpreting ranges "+ p + ", stride should be positive");
330 3 : for(int i=first; i>=second; i+=stride) {
331 : std::string ss;
332 2 : convert(i,ss);
333 2 : news.push_back(ss);
334 : }
335 : }
336 : }
337 11307 : s=news;
338 11307 : }
339 :
340 14107 : void Tools::interpretLabel(std::vector<std::string>&s) {
341 14107 : if(s.size()<2)return;
342 13791 : std::string s0=s[0];
343 13791 : unsigned l=s0.length();
344 13791 : if(l<1) return;
345 13791 : if(s0[l-1]==':') {
346 : s[0]=s[1];
347 21502 : s[1]="LABEL="+s0.substr(0,l-1);
348 : }
349 13791 : std::transform(s[0].begin(), s[0].end(), s[0].begin(), ::toupper);
350 : }
351 :
352 5652 : std::vector<std::string> Tools::ls(const std::string&d) {
353 : DIR*dir;
354 : std::vector<std::string> result;
355 5652 : if ((dir=opendir(d.c_str()))) {
356 : #if defined(__PLUMED_HAS_READDIR_R)
357 : struct dirent ent;
358 : #endif
359 : while(true) {
360 : struct dirent *res;
361 : #if defined(__PLUMED_HAS_READDIR_R)
362 : readdir_r(dir,&ent,&res);
363 : #else
364 88578 : res=readdir(dir);
365 : #endif
366 88578 : if(!res) break;
367 403326 : if(std::string(res->d_name)!="." && std::string(res->d_name)!="..") result.push_back(res->d_name);
368 : }
369 5652 : closedir (dir);
370 : }
371 5652 : return result;
372 0 : }
373 :
374 4320 : void Tools::stripLeadingAndTrailingBlanks( std::string& str ) {
375 4320 : std::size_t first=str.find_first_not_of(' ');
376 4320 : std::size_t last=str.find_last_not_of(' ');
377 8603 : if( first<=last && first!=std::string::npos) str=str.substr(first,last+1);
378 4320 : }
379 :
380 11152 : std::string Tools::extension(const std::string&s) {
381 11152 : size_t n=s.find_last_of(".");
382 : std::string ext;
383 11152 : if(n!=std::string::npos && n+1<s.length() && n+5>=s.length()) {
384 8139 : ext=s.substr(n+1);
385 8139 : if(ext.find("/")!=std::string::npos) ext="";
386 8139 : std::string base=s.substr(0,n);
387 8139 : if(base.length()==0) ext="";
388 8139 : if(base.length()>0 && base[base.length()-1]=='/') ext="";
389 : }
390 11152 : return ext;
391 : }
392 :
393 14 : double Tools::bessel0( const double& val ) {
394 14 : if (std::abs(val)<3.75) {
395 2 : double y = Tools::fastpow( val/3.75, 2 );
396 2 : return 1 + y*(3.5156229 +y*(3.0899424 + y*(1.2067492+y*(0.2659732+y*(0.0360768+y*0.0045813)))));
397 : }
398 12 : double ax=std::abs(val), y=3.75/ax, bx=std::exp(ax)/std::sqrt(ax);
399 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)))))));
400 12 : return ax*bx;
401 : }
402 :
403 876783 : bool Tools::startWith(const std::string & full,const std::string &start) {
404 876783 : return (full.substr(0,start.length())==start);
405 : }
406 :
407 82463 : bool Tools::findKeyword(const std::vector<std::string>&line,const std::string&key) {
408 82463 : const std::string search(key+"=");
409 558849 : for(const auto & p : line) {
410 523600 : if(startWith(p,search)) return true;
411 : }
412 : return false;
413 : }
414 :
415 518 : Tools::DirectoryChanger::DirectoryChanger(const char*path) {
416 518 : if(!path) return;
417 518 : if(std::strlen(path)==0) return;
418 : #ifdef __PLUMED_HAS_GETCWD
419 0 : char* ret=getcwd(cwd,buffersize);
420 0 : plumed_assert(ret)<<"Name of current directory too long, increase buffer size";
421 : #else
422 : plumed_error()<<"You are trying to use DirectoryChanger but your system does not support getcwd";
423 : #endif
424 : #ifdef __PLUMED_HAS_CHDIR
425 0 : int r=chdir(path);
426 0 : plumed_assert(r==0) <<"Cannot chdir to directory "<<path<<". The directory must exist!";
427 : #else
428 : plumed_error()<<"You are trying to use DirectoryChanger but your system does not support chdir";
429 : #endif
430 : }
431 :
432 518 : Tools::DirectoryChanger::~DirectoryChanger() {
433 : #ifdef __PLUMED_HAS_CHDIR
434 518 : if(std::strlen(cwd)==0) return;
435 0 : int ret=chdir(cwd);
436 : // we cannot put an assertion here (in a destructor) otherwise cppcheck complains
437 : // we thus just report the problem
438 0 : if(ret!=0) std::fprintf(stderr,"+++ WARNING: cannot cd back to directory %s\n",cwd);
439 : #endif
440 518 : }
441 :
442 : }
|