LCOV - code coverage report
Current view: top level - tools - Tools.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 214 236 90.7 %
Date: 2024-10-11 08:09:47 Functions: 38 41 92.7 %

          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             : }

Generated by: LCOV version 1.15