LCOV - code coverage report
Current view: top level - tools - OFile.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 210 227 92.5 %
Date: 2024-10-18 14:00:25 Functions: 28 30 93.3 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-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 "OFile.h"
      23             : #include "Exception.h"
      24             : #include "core/Action.h"
      25             : #include "core/PlumedMain.h"
      26             : #include "core/Value.h"
      27             : #include "Communicator.h"
      28             : #include "Tools.h"
      29             : #include <cstdarg>
      30             : #include <cstring>
      31             : 
      32             : #include <iostream>
      33             : #include <string>
      34             : #include <cstdlib>
      35             : #include <cerrno>
      36             : 
      37             : #include <memory>
      38             : #include <utility>
      39             : 
      40             : #ifdef __PLUMED_HAS_ZLIB
      41             : #include <zlib.h>
      42             : #endif
      43             : 
      44             : namespace PLMD {
      45             : 
      46     6065733 : size_t OFile::llwrite(const char*ptr,size_t s) {
      47             :   size_t r;
      48     6065733 :   if(linked) return linked->llwrite(ptr,s);
      49     6065681 :   if(! (comm && comm->Get_rank()>0)) {
      50     5186023 :     if(!fp) plumed_merror("writing on uninitialized File");
      51     5186023 :     if(gzfp) {
      52             : #ifdef __PLUMED_HAS_ZLIB
      53        1263 :       r=gzwrite(gzFile(gzfp),ptr,s);
      54             : #else
      55             :       plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
      56             : #endif
      57             :     } else {
      58     5184760 :       r=std::fwrite(ptr,1,s,fp);
      59             :     }
      60             :   }
      61     6065681 :   if(comm) {
      62             : //  This barrier is apparently useless since it comes
      63             : //  just before a Bcast.
      64             : //
      65             : //  Anyway, it looks like it is solving an issue that appeared on
      66             : //  TRAVIS (at least on my laptop) so I add it here.
      67             : //  GB
      68     5203141 :     comm->Barrier();
      69     5203141 :     comm->Bcast(r,0);
      70             :   }
      71             : 
      72     6065681 :   return r;
      73             : }
      74             : 
      75      821906 : OFile::OFile():
      76      821906 :   linked(NULL),
      77      821906 :   fieldChanged(false),
      78      821906 :   backstring("bck"),
      79      821906 :   enforceRestart_(false),
      80      821906 :   enforceBackup_(false)
      81             : {
      82      821906 :   fmtField();
      83      821906 :   buflen=1;
      84      821906 :   actual_buffer_length=0;
      85      821906 :   buffer.resize(buflen);
      86             : // these are set to zero to avoid valgrind errors
      87     1643812 :   for(int i=0; i<buflen; ++i) buffer[i]=0;
      88             : // these are set to zero to avoid valgrind errors
      89      821906 :   buffer_string.resize(1000,0);
      90      821906 : }
      91             : 
      92          11 : OFile& OFile::link(OFile&l) {
      93          11 :   fp=NULL;
      94          11 :   gzfp=NULL;
      95          11 :   linked=&l;
      96          11 :   return *this;
      97             : }
      98             : 
      99      806698 : OFile& OFile::setLinePrefix(const std::string&l) {
     100      806698 :   linePrefix=l;
     101      806698 :   return *this;
     102             : }
     103             : 
     104    25084276 : int OFile::printf(const char*fmt,...) {
     105             :   va_list arg;
     106    25084276 :   va_start(arg, fmt);
     107    25084276 :   int r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
     108    25084276 :   va_end(arg);
     109    25084276 :   if(r>=buflen-actual_buffer_length) {
     110             :     int newlen=buflen;
     111       55331 :     while(newlen<=r+actual_buffer_length) newlen*=2;
     112       18446 :     std::vector<char> newbuf(newlen);
     113       18446 :     std::memmove(newbuf.data(),buffer.data(),buflen);
     114    13877619 :     for(int k=buflen; k<newlen; k++) newbuf[k]=0;
     115             :     std::swap(buffer,newbuf);
     116       18446 :     buflen=newlen;
     117             :     va_list arg;
     118       18446 :     va_start(arg, fmt);
     119       18446 :     r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
     120       18446 :     va_end(arg);
     121             :   }
     122    25084276 :   plumed_massert(r>-1 && r<buflen-actual_buffer_length,"error using fmt string " + std::string(fmt));
     123             : 
     124             : // Line is buffered until newline, then written with a PLUMED: prefix
     125             :   char*p1=buffer.data();
     126             :   char*p2;
     127             : // newline is only searched in the just added portion:
     128    25084276 :   char*psearch=p1+actual_buffer_length;
     129    25084276 :   actual_buffer_length+=r;
     130    30564091 :   while((p2=std::strchr(psearch,'\n'))) {
     131     5479815 :     if(linePrefix.length()>0) llwrite(linePrefix.c_str(),linePrefix.length());
     132     5479815 :     llwrite(p1,p2-p1+1);
     133     5479815 :     actual_buffer_length-=(p2-p1)+1;
     134     5479815 :     p1=p2+1;
     135             :     psearch=p1;
     136             :   };
     137    25084276 :   if(buffer.data()!=p1) std::memmove(buffer.data(),p1,actual_buffer_length);
     138    25084276 :   return r;
     139             : }
     140             : 
     141       28074 : OFile& OFile::addConstantField(const std::string&name) {
     142             :   Field f;
     143             :   f.name=name;
     144       28074 :   const_fields.push_back(f);
     145       28074 :   return *this;
     146             : }
     147             : 
     148             : 
     149        3202 : OFile& OFile::clearFields() {
     150        3202 :   fields.clear();
     151        3202 :   const_fields.clear();
     152        3202 :   previous_fields.clear();
     153        3202 :   return *this;
     154             : }
     155             : 
     156    14310261 : OFile& OFile::fmtField(const std::string&fmt) {
     157    14310261 :   this->fieldFmt=fmt;
     158    14310261 :   return *this;
     159             : }
     160             : 
     161      827777 : OFile& OFile::fmtField() {
     162      827777 :   this->fieldFmt="%23.16lg";
     163      827777 :   return *this;
     164             : }
     165             : 
     166    15804337 : OFile& OFile::printField(const std::string&name,double v) {
     167             : // When one tries to print -nan we print nan instead.
     168             : // The distinction between +nan and -nan is not well defined
     169             : // Always printing nan simplifies some regtest (special functions computed our of range).
     170    15804337 :   if(std::isnan(v)) v=std::numeric_limits<double>::quiet_NaN();
     171             :   std::snprintf(buffer_string.data(),buffer_string.size(),fieldFmt.c_str(),v);
     172    15804337 :   printField(name,buffer_string.data());
     173    15804337 :   return *this;
     174             : }
     175             : 
     176     6140690 : OFile& OFile::printField(const std::string&name,int v) {
     177             :   std::snprintf(buffer_string.data(),buffer_string.size()," %d",v);
     178     6140690 :   printField(name,buffer_string.data());
     179     6140690 :   return *this;
     180             : }
     181             : 
     182           1 : OFile& OFile::printField(const std::string&name,long int v) {
     183             :   std::snprintf(buffer_string.data(),buffer_string.size()," %ld",v);
     184           1 :   printField(name,buffer_string.data());
     185           1 :   return *this;
     186             : }
     187             : 
     188           0 : OFile& OFile::printField(const std::string&name,long long int v) {
     189             :   std::snprintf(buffer_string.data(),buffer_string.size()," %lld",v);
     190           0 :   printField(name,buffer_string.data());
     191           0 :   return *this;
     192             : }
     193             : 
     194          31 : OFile& OFile::printField(const std::string&name,unsigned v) {
     195             :   std::snprintf(buffer_string.data(),buffer_string.size()," %u",v);
     196          31 :   printField(name,buffer_string.data());
     197          31 :   return *this;
     198             : }
     199             : 
     200           1 : OFile& OFile::printField(const std::string&name,long unsigned v) {
     201             :   std::snprintf(buffer_string.data(),buffer_string.size()," %lu",v);
     202           1 :   printField(name,buffer_string.data());
     203           1 :   return *this;
     204             : }
     205             : 
     206          47 : OFile& OFile::printField(const std::string&name,long long unsigned v) {
     207             :   std::snprintf(buffer_string.data(),buffer_string.size()," %llu",v);
     208          47 :   printField(name,buffer_string.data());
     209          47 :   return *this;
     210             : }
     211             : 
     212    38566483 : OFile& OFile::printField(const std::string&name,const std::string & v) {
     213             :   unsigned i;
     214   201368399 :   for(i=0; i<const_fields.size(); i++) if(const_fields[i].name==name) break;
     215    38566483 :   if(i>=const_fields.size()) {
     216             :     Field field;
     217             :     field.name=name;
     218             :     field.value=v;
     219    16821341 :     fields.push_back(field);
     220             :   } else {
     221    21745142 :     if(const_fields[i].value!=v) fieldChanged=true;
     222             :     const_fields[i].value=v;
     223             :   }
     224    38566483 :   return *this;
     225             : }
     226             : 
     227        6729 : OFile& OFile::setupPrintValue( Value *val ) {
     228        6729 :   if( val->isPeriodic() ) {
     229         510 :     addConstantField("min_" + val->getName() );
     230        1020 :     addConstantField("max_" + val->getName() );
     231             :   }
     232        6729 :   return *this;
     233             : }
     234             : 
     235        7286 : OFile& OFile::printField( Value* val, const double& v ) {
     236        7286 :   printField( val->getName(), v );
     237        7286 :   if( val->isPeriodic() ) {
     238        1586 :     std::string min, max; val->getDomain( min, max );
     239        1586 :     printField( "min_" + val->getName(), min );
     240        3172 :     printField("max_" + val->getName(), max );
     241             :   }
     242        7286 :   return *this;
     243             : }
     244             : 
     245     3841581 : OFile& OFile::printField() {
     246             :   bool reprint=false;
     247     3841581 :   if(fieldChanged || fields.size()!=previous_fields.size()) {
     248             :     reprint=true;
     249    20067980 :   } else for(unsigned i=0; i<fields.size(); i++) {
     250    16232683 :       if( previous_fields[i].name!=fields[i].name ||
     251    16232681 :           (fields[i].constant && fields[i].value!=previous_fields[i].value) ) {
     252             :         reprint=true;
     253             :         break;
     254             :       }
     255             :     }
     256     3841581 :   if(reprint) {
     257        6284 :     printf("#! FIELDS");
     258      594946 :     for(unsigned i=0; i<fields.size(); i++) printf(" %s",fields[i].name.c_str());
     259        6284 :     printf("\n");
     260       34294 :     for(unsigned i=0; i<const_fields.size(); i++) {
     261       28010 :       printf("#! SET %s %s",const_fields[i].name.c_str(),const_fields[i].value.c_str());
     262       28010 :       printf("\n");
     263             :     }
     264             :   }
     265    20662922 :   for(unsigned i=0; i<fields.size(); i++) printf("%s",fields[i].value.c_str());
     266     3841581 :   printf("\n");
     267     3841581 :   previous_fields=fields;
     268     3841581 :   fields.clear();
     269     3841581 :   fieldChanged=false;
     270     3841581 :   return *this;
     271             : }
     272             : 
     273         194 : void OFile::setBackupString( const std::string& str ) {
     274         194 :   backstring=str;
     275         194 : }
     276             : 
     277           0 : void OFile::backupAllFiles( const std::string& str ) {
     278           0 :   if(str=="/dev/null") return;
     279           0 :   plumed_assert( backstring!="bck" && !checkRestart());
     280           0 :   size_t found=str.find_last_of("/\\");
     281           0 :   std::string filename = appendSuffix(str,getSuffix());
     282           0 :   std::string directory=filename.substr(0,found+1);
     283           0 :   std::string file=filename.substr(found+1);
     284           0 :   if( FileExist(filename) ) backupFile("bck", filename);
     285           0 :   for(int i=0;; i++) {
     286           0 :     std::string num; Tools::convert(i,num);
     287           0 :     std::string filestr = directory + backstring + "." + num + "." + file;
     288           0 :     if( !FileExist(filestr) ) break;
     289           0 :     backupFile( "bck", filestr);
     290           0 :   }
     291             : }
     292             : 
     293        3766 : void OFile::backupFile( const std::string& bstring, const std::string& fname ) {
     294        3766 :   if(fname=="/dev/null") return;
     295        3612 :   int maxbackup=100;
     296        3612 :   if(std::getenv("PLUMED_MAXBACKUP")) Tools::convert(std::getenv("PLUMED_MAXBACKUP"),maxbackup);
     297        3612 :   if(maxbackup>0 && (!comm || comm->Get_rank()==0)) {
     298        3047 :     FILE* ff=std::fopen(const_cast<char*>(fname.c_str()),"r");
     299        3047 :     if(ff) {
     300             :       // no exception here
     301         122 :       std::fclose(ff);
     302             :       std::string backup;
     303         122 :       size_t found=fname.find_last_of("/\\");
     304         122 :       std::string directory=fname.substr(0,found+1);
     305         122 :       std::string file=fname.substr(found+1);
     306         122 :       for(int i=0;; i++) {
     307             :         std::string num;
     308         171 :         Tools::convert(i,num);
     309         171 :         if(i>maxbackup) plumed_merror("cannot backup file "+file+" maximum number of backup is "+num+"\n");
     310         342 :         backup=directory+bstring +"."+num+"."+file;
     311         171 :         FILE* fff=std::fopen(backup.c_str(),"r");
     312             :         // no exception here
     313         171 :         if(!fff) break;
     314          49 :         else std::fclose(fff);
     315          49 :       }
     316         122 :       int check=rename(fname.c_str(),backup.c_str());
     317         122 :       plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+std::strerror(errno));
     318             :     }
     319             :   }
     320             : }
     321             : 
     322        4138 : OFile& OFile::open(const std::string&path) {
     323        4138 :   plumed_assert(!cloned);
     324        4138 :   eof=false;
     325        4138 :   err=false;
     326        4138 :   fp=NULL;
     327        4138 :   gzfp=NULL;
     328        4138 :   this->path=path;
     329        8276 :   this->path=appendSuffix(path,getSuffix());
     330        4138 :   if(checkRestart()) {
     331         372 :     fp=std::fopen(const_cast<char*>(this->path.c_str()),"a");
     332         372 :     mode="a";
     333         744 :     if(Tools::extension(this->path)=="gz") {
     334             : #ifdef __PLUMED_HAS_ZLIB
     335          12 :       gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"a9");
     336             : #else
     337             :       plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
     338             : #endif
     339             :     }
     340             :   } else {
     341        3766 :     backupFile( backstring, this->path );
     342        3766 :     if(comm)comm->Barrier();
     343        3766 :     fp=std::fopen(const_cast<char*>(this->path.c_str()),"w");
     344        3766 :     mode="w";
     345        7532 :     if(Tools::extension(this->path)=="gz") {
     346             : #ifdef __PLUMED_HAS_ZLIB
     347          13 :       gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
     348             : #else
     349             :       plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
     350             : #endif
     351             :     }
     352             :   }
     353        4138 :   if(plumed) plumed->insertFile(*this);
     354        4138 :   return *this;
     355             : }
     356             : 
     357         135 : OFile& OFile::rewind() {
     358             : // we use here "hard" rewind, which means close/reopen
     359             : // the reason is that normal rewind does not work when in append mode
     360             : // moreover, we can take a backup of the file
     361         135 :   plumed_assert(fp);
     362         135 :   clearFields();
     363             : 
     364         135 :   if(!comm || comm->Get_rank()==0) {
     365         104 :     std::string fname=this->path;
     366         104 :     size_t found=fname.find_last_of("/\\");
     367         104 :     std::string directory=fname.substr(0,found+1);
     368         104 :     std::string file=fname.substr(found+1);
     369         208 :     std::string backup=directory+backstring +".last."+file;
     370         104 :     int check=rename(fname.c_str(),backup.c_str());
     371         104 :     plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+std::strerror(errno));
     372             :   }
     373             : 
     374         135 :   if(comm) comm->Barrier();
     375             : 
     376         135 :   if(gzfp) {
     377             : #ifdef __PLUMED_HAS_ZLIB
     378          15 :     gzclose((gzFile)gzfp);
     379             :     // no exception here
     380          15 :     gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
     381             : #endif
     382             :   } else {
     383         120 :     std::fclose(fp);
     384             :     // no exception here
     385         120 :     fp=std::fopen(const_cast<char*>(path.c_str()),"w");
     386             :   }
     387         135 :   return *this;
     388             : }
     389             : 
     390        9416 : FileBase& OFile::flush() {
     391        9416 :   if(heavyFlush) {
     392        3908 :     if(gzfp) {
     393             : #ifdef __PLUMED_HAS_ZLIB
     394           9 :       gzclose(gzFile(gzfp));
     395             :       // no exception here
     396           9 :       gzfp=(void*)gzopen(const_cast<char*>(path.c_str()),"a");
     397             : #endif
     398             :     } else {
     399        3899 :       std::fclose(fp);
     400             :       // no exception here
     401        3899 :       fp=std::fopen(const_cast<char*>(path.c_str()),"a");
     402             :     }
     403             :   } else {
     404        5508 :     FileBase::flush();
     405             :     // if(gzfp) gzflush(gzFile(gzfp),Z_FINISH);
     406             :     // for some reason flushing with Z_FINISH has problems on linux
     407             :     // I thus use this (incomplete) flush
     408             : #ifdef __PLUMED_HAS_ZLIB
     409        5508 :     if(gzfp) gzflush(gzFile(gzfp),Z_FULL_FLUSH);
     410             : #endif
     411             :   }
     412        9416 :   return *this;
     413             : }
     414             : 
     415        4138 : bool OFile::checkRestart()const {
     416        4138 :   if(enforceRestart_) return true;
     417        4127 :   else if(enforceBackup_) return false;
     418        2983 :   else if(action) return action->getRestart();
     419         344 :   else if(plumed) return plumed->getRestart();
     420             :   else return false;
     421             : }
     422             : 
     423          11 : OFile& OFile::enforceRestart() {
     424          11 :   enforceRestart_=true;
     425          11 :   enforceBackup_=false;
     426          11 :   return *this;
     427             : }
     428             : 
     429        1144 : OFile& OFile::enforceBackup() {
     430        1144 :   enforceBackup_=true;
     431        1144 :   enforceRestart_=false;
     432        1144 :   return *this;
     433             : }
     434             : 
     435             : 
     436             : }

Generated by: LCOV version 1.16