LCOV - code coverage report
Current view: top level - tools - OFile.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 217 221 98.2 %
Date: 2024-10-11 08:09:47 Functions: 28 28 100.0 %

          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     5373429 : size_t OFile::llwrite(const char*ptr,size_t s) {
      47             :   size_t r;
      48     5373429 :   if(linked) return linked->llwrite(ptr,s);
      49     5373377 :   if(! (comm && comm->Get_rank()>0)) {
      50     4571292 :     if(!fp) plumed_merror("writing on uninitialized File");
      51     4571292 :     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     4570029 :       r=std::fwrite(ptr,1,s,fp);
      59             :     }
      60             :   }
      61             : //  This barrier is apparently useless since it comes
      62             : //  just before a Bcast.
      63             : //
      64             : //  Anyway, it looks like it is solving an issue that appeared on
      65             : //  TRAVIS (at least on my laptop) so I add it here.
      66             : //  GB
      67     5373377 :   if(comm) comm->Barrier();
      68             : 
      69             : 
      70     5373377 :   if(comm) comm->Bcast(r,0);
      71     5373377 :   return r;
      72             : }
      73             : 
      74      418810 : OFile::OFile():
      75      418810 :   linked(NULL),
      76      418810 :   fieldChanged(false),
      77      418810 :   backstring("bck"),
      78      418810 :   enforceRestart_(false),
      79      418810 :   enforceBackup_(false)
      80             : {
      81      418810 :   fmtField();
      82      418810 :   buflen=1;
      83      418810 :   actual_buffer_length=0;
      84      418810 :   buffer=Tools::make_unique<char[]>(buflen);
      85             : // these are set to zero to avoid valgrind errors
      86      837620 :   for(int i=0; i<buflen; ++i) buffer[i]=0;
      87      418810 :   buffer_string=Tools::make_unique<char[]>(1000);
      88             : // these are set to zero to avoid valgrind errors
      89   419228810 :   for(unsigned i=0; i<1000; ++i) buffer_string[i]=0;
      90      418810 : }
      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      404395 : OFile& OFile::setLinePrefix(const std::string&l) {
     100      404395 :   linePrefix=l;
     101      404395 :   return *this;
     102             : }
     103             : 
     104    22722868 : int OFile::printf(const char*fmt,...) {
     105             :   va_list arg;
     106    22722868 :   va_start(arg, fmt);
     107    22722868 :   int r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
     108    22722868 :   va_end(arg);
     109    22722868 :   if(r>=buflen-actual_buffer_length) {
     110             :     int newlen=buflen;
     111       45189 :     while(newlen<=r+actual_buffer_length) newlen*=2;
     112       15223 :     auto newbuf=Tools::make_unique<char[]>(newlen);
     113       15223 :     std::memmove(newbuf.get(),buffer.get(),buflen);
     114     3184571 :     for(int k=buflen; k<newlen; k++) newbuf[k]=0;
     115             :     buffer=std::move(newbuf);
     116       15223 :     buflen=newlen;
     117             :     va_list arg;
     118       15223 :     va_start(arg, fmt);
     119       15223 :     r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
     120       15223 :     va_end(arg);
     121             :   }
     122    22722868 :   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.get();
     126             :   char*p2;
     127             : // newline is only searched in the just added portion:
     128    22722868 :   char*psearch=p1+actual_buffer_length;
     129    22722868 :   actual_buffer_length+=r;
     130    27869020 :   while((p2=std::strchr(psearch,'\n'))) {
     131     5146152 :     if(linePrefix.length()>0) llwrite(linePrefix.c_str(),linePrefix.length());
     132     5146152 :     llwrite(p1,p2-p1+1);
     133     5146152 :     actual_buffer_length-=(p2-p1)+1;
     134     5146152 :     p1=p2+1;
     135             :     psearch=p1;
     136             :   };
     137    22722868 :   if(buffer.get()!=p1) std::memmove(buffer.get(),p1,actual_buffer_length);
     138    22722868 :   return r;
     139             : }
     140             : 
     141       27816 : OFile& OFile::addConstantField(const std::string&name) {
     142             :   Field f;
     143             :   f.name=name;
     144       27816 :   const_fields.push_back(f);
     145       27816 :   return *this;
     146             : }
     147             : 
     148             : 
     149        3201 : OFile& OFile::clearFields() {
     150        3201 :   fields.clear();
     151        3201 :   const_fields.clear();
     152        3201 :   previous_fields.clear();
     153        3201 :   return *this;
     154             : }
     155             : 
     156    14172009 : OFile& OFile::fmtField(const std::string&fmt) {
     157    14172009 :   this->fieldFmt=fmt;
     158    14172009 :   return *this;
     159             : }
     160             : 
     161      424681 : OFile& OFile::fmtField() {
     162      424681 :   this->fieldFmt="%23.16lg";
     163      424681 :   return *this;
     164             : }
     165             : 
     166    15101445 : 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    15101445 :   if(std::isnan(v)) v=std::numeric_limits<double>::quiet_NaN();
     171             :   std::sprintf(buffer_string.get(),fieldFmt.c_str(),v);
     172    15101445 :   printField(name,buffer_string.get());
     173    15101445 :   return *this;
     174             : }
     175             : 
     176     6031476 : OFile& OFile::printField(const std::string&name,int v) {
     177             :   std::sprintf(buffer_string.get()," %d",v);
     178     6031476 :   printField(name,buffer_string.get());
     179     6031476 :   return *this;
     180             : }
     181             : 
     182           1 : OFile& OFile::printField(const std::string&name,long int v) {
     183             :   std::sprintf(buffer_string.get()," %ld",v);
     184           1 :   printField(name,buffer_string.get());
     185           1 :   return *this;
     186             : }
     187             : 
     188          31 : OFile& OFile::printField(const std::string&name,unsigned v) {
     189             :   std::sprintf(buffer_string.get()," %u",v);
     190          31 :   printField(name,buffer_string.get());
     191          31 :   return *this;
     192             : }
     193             : 
     194          48 : OFile& OFile::printField(const std::string&name,long unsigned v) {
     195             :   std::sprintf(buffer_string.get()," %lu",v);
     196          48 :   printField(name,buffer_string.get());
     197          48 :   return *this;
     198             : }
     199             : 
     200    36950358 : OFile& OFile::printField(const std::string&name,const std::string & v) {
     201             :   unsigned i;
     202   189697493 :   for(i=0; i<const_fields.size(); i++) if(const_fields[i].name==name) break;
     203    36950358 :   if(i>=const_fields.size()) {
     204             :     Field field;
     205             :     field.name=name;
     206             :     field.value=v;
     207    16174094 :     fields.push_back(field);
     208             :   } else {
     209    20776264 :     if(const_fields[i].value!=v) fieldChanged=true;
     210             :     const_fields[i].value=v;
     211             :   }
     212    36950358 :   return *this;
     213             : }
     214             : 
     215        5735 : OFile& OFile::setupPrintValue( Value *val ) {
     216        5735 :   if( val->isPeriodic() ) {
     217         475 :     addConstantField("min_" + val->getName() );
     218         950 :     addConstantField("max_" + val->getName() );
     219             :   }
     220        5735 :   return *this;
     221             : }
     222             : 
     223      537963 : OFile& OFile::printField( Value* val, const double& v ) {
     224      537963 :   printField( val->getName(), v );
     225      537963 :   if( val->isPeriodic() ) {
     226       12918 :     std::string min, max; val->getDomain( min, max );
     227       12918 :     printField( "min_" + val->getName(), min );
     228       25836 :     printField("max_" + val->getName(), max );
     229             :   }
     230      537963 :   return *this;
     231             : }
     232             : 
     233     3886692 : OFile& OFile::printField() {
     234             :   bool reprint=false;
     235     3886692 :   if(fieldChanged || fields.size()!=previous_fields.size()) {
     236             :     reprint=true;
     237    19996953 :   } else for(unsigned i=0; i<fields.size(); i++) {
     238    16116186 :       if( previous_fields[i].name!=fields[i].name ||
     239    16116184 :           (fields[i].constant && fields[i].value!=previous_fields[i].value) ) {
     240             :         reprint=true;
     241             :         break;
     242             :       }
     243             :     }
     244     3886692 :   if(reprint) {
     245        5925 :     printf("#! FIELDS");
     246       63837 :     for(unsigned i=0; i<fields.size(); i++) printf(" %s",fields[i].name.c_str());
     247        5925 :     printf("\n");
     248       33677 :     for(unsigned i=0; i<const_fields.size(); i++) {
     249       27752 :       printf("#! SET %s %s",const_fields[i].name.c_str(),const_fields[i].value.c_str());
     250       27752 :       printf("\n");
     251             :     }
     252             :   }
     253    20060786 :   for(unsigned i=0; i<fields.size(); i++) printf("%s",fields[i].value.c_str());
     254     3886692 :   printf("\n");
     255     3886692 :   previous_fields=fields;
     256             :   fields.clear();
     257     3886692 :   fieldChanged=false;
     258     3886692 :   return *this;
     259             : }
     260             : 
     261         121 : void OFile::setBackupString( const std::string& str ) {
     262         121 :   backstring=str;
     263         121 : }
     264             : 
     265          36 : void OFile::backupAllFiles( const std::string& str ) {
     266          36 :   if(str=="/dev/null") return;
     267          36 :   plumed_assert( backstring!="bck" && !checkRestart());
     268          36 :   size_t found=str.find_last_of("/\\");
     269          36 :   std::string filename = appendSuffix(str,getSuffix());
     270          36 :   std::string directory=filename.substr(0,found+1);
     271          36 :   std::string file=filename.substr(found+1);
     272          36 :   if( FileExist(filename) ) backupFile("bck", filename);
     273          36 :   for(int i=0;; i++) {
     274          36 :     std::string num; Tools::convert(i,num);
     275          72 :     std::string filestr = directory + backstring + "." + num + "." + file;
     276          36 :     if( !FileExist(filestr) ) break;
     277           0 :     backupFile( "bck", filestr);
     278           0 :   }
     279             : }
     280             : 
     281        3199 : void OFile::backupFile( const std::string& bstring, const std::string& fname ) {
     282        3199 :   if(fname=="/dev/null") return;
     283        3045 :   int maxbackup=100;
     284        3045 :   if(std::getenv("PLUMED_MAXBACKUP")) Tools::convert(std::getenv("PLUMED_MAXBACKUP"),maxbackup);
     285        3045 :   if(maxbackup>0 && (!comm || comm->Get_rank()==0)) {
     286        2585 :     FILE* ff=std::fopen(const_cast<char*>(fname.c_str()),"r");
     287        2585 :     if(ff) {
     288          65 :       std::fclose(ff);
     289             :       std::string backup;
     290          65 :       size_t found=fname.find_last_of("/\\");
     291          65 :       std::string directory=fname.substr(0,found+1);
     292          65 :       std::string file=fname.substr(found+1);
     293          65 :       for(int i=0;; i++) {
     294             :         std::string num;
     295          65 :         Tools::convert(i,num);
     296          65 :         if(i>maxbackup) plumed_merror("cannot backup file "+file+" maximum number of backup is "+num+"\n");
     297         130 :         backup=directory+bstring +"."+num+"."+file;
     298          65 :         FILE* fff=std::fopen(backup.c_str(),"r");
     299          65 :         if(!fff) break;
     300           0 :         else std::fclose(fff);
     301           0 :       }
     302          65 :       int check=rename(fname.c_str(),backup.c_str());
     303          65 :       plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+std::strerror(errno));
     304             :     }
     305             :   }
     306             : }
     307             : 
     308        3482 : OFile& OFile::open(const std::string&path) {
     309        3482 :   plumed_assert(!cloned);
     310        3482 :   eof=false;
     311        3482 :   err=false;
     312        3482 :   fp=NULL;
     313        3482 :   gzfp=NULL;
     314        3482 :   this->path=path;
     315        6964 :   this->path=appendSuffix(path,getSuffix());
     316        3482 :   if(checkRestart()) {
     317         283 :     fp=std::fopen(const_cast<char*>(this->path.c_str()),"a");
     318         283 :     mode="a";
     319         566 :     if(Tools::extension(this->path)=="gz") {
     320             : #ifdef __PLUMED_HAS_ZLIB
     321          12 :       gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"a9");
     322             : #else
     323             :       plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
     324             : #endif
     325             :     }
     326             :   } else {
     327        3199 :     backupFile( backstring, this->path );
     328        3199 :     if(comm)comm->Barrier();
     329        3199 :     fp=std::fopen(const_cast<char*>(this->path.c_str()),"w");
     330        3199 :     mode="w";
     331        6398 :     if(Tools::extension(this->path)=="gz") {
     332             : #ifdef __PLUMED_HAS_ZLIB
     333          13 :       gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
     334             : #else
     335             :       plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
     336             : #endif
     337             :     }
     338             :   }
     339        3482 :   if(plumed) plumed->insertFile(*this);
     340        3482 :   return *this;
     341             : }
     342             : 
     343         134 : OFile& OFile::rewind() {
     344             : // we use here "hard" rewind, which means close/reopen
     345             : // the reason is that normal rewind does not work when in append mode
     346             : // moreover, we can take a backup of the file
     347         134 :   plumed_assert(fp);
     348         134 :   clearFields();
     349         134 :   if(gzfp) {
     350             : #ifdef __PLUMED_HAS_ZLIB
     351          15 :     gzclose((gzFile)gzfp);
     352             : #endif
     353         119 :   } else fclose(fp);
     354             : 
     355         134 :   if(!comm || comm->Get_rank()==0) {
     356         103 :     std::string fname=this->path;
     357         103 :     size_t found=fname.find_last_of("/\\");
     358         103 :     std::string directory=fname.substr(0,found+1);
     359         103 :     std::string file=fname.substr(found+1);
     360         206 :     std::string backup=directory+backstring +".last."+file;
     361         103 :     int check=rename(fname.c_str(),backup.c_str());
     362         103 :     plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+std::strerror(errno));
     363             :   }
     364             : 
     365         134 :   if(comm) comm->Barrier();
     366             : 
     367         134 :   if(gzfp) {
     368             : #ifdef __PLUMED_HAS_ZLIB
     369          15 :     gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
     370             : #endif
     371         119 :   } else fp=std::fopen(const_cast<char*>(path.c_str()),"w");
     372         134 :   return *this;
     373             : }
     374             : 
     375        8436 : FileBase& OFile::flush() {
     376        8436 :   if(heavyFlush) {
     377        3901 :     if(gzfp) {
     378             : #ifdef __PLUMED_HAS_ZLIB
     379           9 :       gzclose(gzFile(gzfp));
     380           9 :       gzfp=(void*)gzopen(const_cast<char*>(path.c_str()),"a");
     381             : #endif
     382             :     } else {
     383        3892 :       fclose(fp);
     384        3892 :       fp=std::fopen(const_cast<char*>(path.c_str()),"a");
     385             :     }
     386             :   } else {
     387        4535 :     FileBase::flush();
     388             :     // if(gzfp) gzflush(gzFile(gzfp),Z_FINISH);
     389             :     // for some reason flushing with Z_FINISH has problems on linux
     390             :     // I thus use this (incomplete) flush
     391             : #ifdef __PLUMED_HAS_ZLIB
     392        4535 :     if(gzfp) gzflush(gzFile(gzfp),Z_FULL_FLUSH);
     393             : #endif
     394             :   }
     395        8436 :   return *this;
     396             : }
     397             : 
     398        3518 : bool OFile::checkRestart()const {
     399        3518 :   if(enforceRestart_) return true;
     400        3517 :   else if(enforceBackup_) return false;
     401        2373 :   else if(action) return action->getRestart();
     402         218 :   else if(plumed) return plumed->getRestart();
     403             :   else return false;
     404             : }
     405             : 
     406           1 : OFile& OFile::enforceRestart() {
     407           1 :   enforceRestart_=true;
     408           1 :   enforceBackup_=false;
     409           1 :   return *this;
     410             : }
     411             : 
     412        1144 : OFile& OFile::enforceBackup() {
     413        1144 :   enforceBackup_=true;
     414        1144 :   enforceRestart_=false;
     415        1144 :   return *this;
     416             : }
     417             : 
     418             : 
     419             : }

Generated by: LCOV version 1.15