LCOV - code coverage report
Current view: top level - tools - OFile.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 227 249 91.2 %
Date: 2025-03-25 09:33:27 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     6079167 : size_t OFile::llwrite(const char*ptr,size_t s) {
      47             :   size_t r;
      48     6079167 :   if(linked) {
      49          52 :     return linked->llwrite(ptr,s);
      50             :   }
      51     6079115 :   if(! (comm && comm->Get_rank()>0)) {
      52     5194605 :     if(!fp) {
      53           0 :       plumed_merror("writing on uninitialized File");
      54             :     }
      55     5194605 :     if(gzfp) {
      56             : #ifdef __PLUMED_HAS_ZLIB
      57        1263 :       r=gzwrite(gzFile(gzfp),ptr,s);
      58             : #else
      59             :       plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
      60             : #endif
      61             :     } else {
      62     5193342 :       r=std::fwrite(ptr,1,s,fp);
      63             :     }
      64             :   }
      65     6079115 :   if(comm) {
      66             : //  This barrier is apparently useless since it comes
      67             : //  just before a Bcast.
      68             : //
      69             : //  Anyway, it looks like it is solving an issue that appeared on
      70             : //  TRAVIS (at least on my laptop) so I add it here.
      71             : //  GB
      72     5216581 :     comm->Barrier();
      73     5216581 :     comm->Bcast(r,0);
      74             :   }
      75             : 
      76     6079115 :   return r;
      77             : }
      78             : 
      79      822143 : OFile::OFile():
      80      822143 :   linked(NULL),
      81      822143 :   fieldChanged(false),
      82      822143 :   backstring("bck"),
      83      822143 :   enforceRestart_(false),
      84      822143 :   enforceBackup_(false) {
      85      822143 :   fmtField();
      86      822143 :   buflen=1;
      87      822143 :   actual_buffer_length=0;
      88      822143 :   buffer.resize(buflen);
      89             : // these are set to zero to avoid valgrind errors
      90     1644286 :   for(int i=0; i<buflen; ++i) {
      91      822143 :     buffer[i]=0;
      92             :   }
      93             : // these are set to zero to avoid valgrind errors
      94      822143 :   buffer_string.resize(1000,0);
      95      822143 : }
      96             : 
      97          11 : OFile& OFile::link(OFile&l) {
      98          11 :   fp=NULL;
      99          11 :   gzfp=NULL;
     100          11 :   linked=&l;
     101          11 :   return *this;
     102             : }
     103             : 
     104      806820 : OFile& OFile::setLinePrefix(const std::string&l) {
     105      806820 :   linePrefix=l;
     106      806820 :   return *this;
     107             : }
     108             : 
     109    25154618 : int OFile::printf(const char*fmt,...) {
     110             :   va_list arg;
     111    25154618 :   va_start(arg, fmt);
     112    25154618 :   int r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
     113    25154618 :   va_end(arg);
     114    25154618 :   if(r>=buflen-actual_buffer_length) {
     115             :     int newlen=buflen;
     116       56922 :     while(newlen<=r+actual_buffer_length) {
     117       37667 :       newlen*=2;
     118             :     }
     119       19255 :     std::vector<char> newbuf(newlen);
     120       19255 :     std::memmove(newbuf.data(),buffer.data(),buflen);
     121    13927668 :     for(int k=buflen; k<newlen; k++) {
     122    13908413 :       newbuf[k]=0;
     123             :     }
     124             :     std::swap(buffer,newbuf);
     125       19255 :     buflen=newlen;
     126             :     va_list arg;
     127       19255 :     va_start(arg, fmt);
     128       19255 :     r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
     129       19255 :     va_end(arg);
     130             :   }
     131    25154618 :   plumed_massert(r>-1 && r<buflen-actual_buffer_length,"error using fmt string " + std::string(fmt));
     132             : 
     133             : // Line is buffered until newline, then written with a PLUMED: prefix
     134             :   char*p1=buffer.data();
     135             :   char*p2;
     136             : // newline is only searched in the just added portion:
     137    25154618 :   char*psearch=p1+actual_buffer_length;
     138    25154618 :   actual_buffer_length+=r;
     139    30648092 :   while((p2=std::strchr(psearch,'\n'))) {
     140     5493474 :     if(linePrefix.length()>0) {
     141      585641 :       llwrite(linePrefix.c_str(),linePrefix.length());
     142             :     }
     143     5493474 :     llwrite(p1,p2-p1+1);
     144     5493474 :     actual_buffer_length-=(p2-p1)+1;
     145     5493474 :     p1=p2+1;
     146             :     psearch=p1;
     147             :   };
     148    25154618 :   if(buffer.data()!=p1) {
     149     5481342 :     std::memmove(buffer.data(),p1,actual_buffer_length);
     150             :   }
     151    25154618 :   return r;
     152             : }
     153             : 
     154       28074 : OFile& OFile::addConstantField(const std::string&name) {
     155             :   Field f;
     156             :   f.name=name;
     157       28074 :   const_fields.push_back(f);
     158       28074 :   return *this;
     159             : }
     160             : 
     161             : 
     162        3228 : OFile& OFile::clearFields() {
     163        3228 :   fields.clear();
     164        3228 :   const_fields.clear();
     165        3228 :   previous_fields.clear();
     166        3228 :   return *this;
     167             : }
     168             : 
     169    14341139 : OFile& OFile::fmtField(const std::string&fmt) {
     170    14341139 :   this->fieldFmt=fmt;
     171    14341139 :   return *this;
     172             : }
     173             : 
     174      828014 : OFile& OFile::fmtField() {
     175      828014 :   this->fieldFmt="%23.16lg";
     176      828014 :   return *this;
     177             : }
     178             : 
     179    15839159 : OFile& OFile::printField(const std::string&name,double v) {
     180             : // When one tries to print -nan we print nan instead.
     181             : // The distinction between +nan and -nan is not well defined
     182             : // Always printing nan simplifies some regtest (special functions computed our of range).
     183    15839159 :   if(std::isnan(v)) {
     184             :     v=std::numeric_limits<double>::quiet_NaN();
     185             :   }
     186             :   std::snprintf(buffer_string.data(),buffer_string.size(),fieldFmt.c_str(),v);
     187    15839159 :   printField(name,buffer_string.data());
     188    15839159 :   return *this;
     189             : }
     190             : 
     191     6150443 : OFile& OFile::printField(const std::string&name,int v) {
     192             :   std::snprintf(buffer_string.data(),buffer_string.size()," %d",v);
     193     6150443 :   printField(name,buffer_string.data());
     194     6150443 :   return *this;
     195             : }
     196             : 
     197           1 : OFile& OFile::printField(const std::string&name,long int v) {
     198             :   std::snprintf(buffer_string.data(),buffer_string.size()," %ld",v);
     199           1 :   printField(name,buffer_string.data());
     200           1 :   return *this;
     201             : }
     202             : 
     203           0 : OFile& OFile::printField(const std::string&name,long long int v) {
     204             :   std::snprintf(buffer_string.data(),buffer_string.size()," %lld",v);
     205           0 :   printField(name,buffer_string.data());
     206           0 :   return *this;
     207             : }
     208             : 
     209          31 : OFile& OFile::printField(const std::string&name,unsigned v) {
     210             :   std::snprintf(buffer_string.data(),buffer_string.size()," %u",v);
     211          31 :   printField(name,buffer_string.data());
     212          31 :   return *this;
     213             : }
     214             : 
     215           1 : OFile& OFile::printField(const std::string&name,long unsigned v) {
     216             :   std::snprintf(buffer_string.data(),buffer_string.size()," %lu",v);
     217           1 :   printField(name,buffer_string.data());
     218           1 :   return *this;
     219             : }
     220             : 
     221          47 : OFile& OFile::printField(const std::string&name,long long unsigned v) {
     222             :   std::snprintf(buffer_string.data(),buffer_string.size()," %llu",v);
     223          47 :   printField(name,buffer_string.data());
     224          47 :   return *this;
     225             : }
     226             : 
     227    38611058 : OFile& OFile::printField(const std::string&name,const std::string & v) {
     228             :   unsigned i;
     229   201412974 :   for(i=0; i<const_fields.size(); i++)
     230   184547058 :     if(const_fields[i].name==name) {
     231             :       break;
     232             :     }
     233    38611058 :   if(i>=const_fields.size()) {
     234             :     Field field;
     235             :     field.name=name;
     236             :     field.value=v;
     237    16865916 :     fields.push_back(field);
     238             :   } else {
     239    21745142 :     if(const_fields[i].value!=v) {
     240       28006 :       fieldChanged=true;
     241             :     }
     242             :     const_fields[i].value=v;
     243             :   }
     244    38611058 :   return *this;
     245             : }
     246             : 
     247        6849 : OFile& OFile::setupPrintValue( Value *val ) {
     248        6849 :   if( val->isPeriodic() ) {
     249         510 :     addConstantField("min_" + val->getName() );
     250        1020 :     addConstantField("max_" + val->getName() );
     251             :   }
     252        6849 :   return *this;
     253             : }
     254             : 
     255        7286 : OFile& OFile::printField( Value* val, const double& v ) {
     256        7286 :   printField( val->getName(), v );
     257        7286 :   if( val->isPeriodic() ) {
     258             :     std::string min, max;
     259        1586 :     val->getDomain( min, max );
     260        1586 :     printField( "min_" + val->getName(), min );
     261        3172 :     printField("max_" + val->getName(), max );
     262             :   }
     263        7286 :   return *this;
     264             : }
     265             : 
     266     3855388 : OFile& OFile::printField() {
     267             :   bool reprint=false;
     268     3855388 :   if(fieldChanged || fields.size()!=previous_fields.size()) {
     269             :     reprint=true;
     270             :   } else
     271    20125848 :     for(unsigned i=0; i<fields.size(); i++) {
     272    16276827 :       if( previous_fields[i].name!=fields[i].name ||
     273    16276825 :           (fields[i].constant && fields[i].value!=previous_fields[i].value) ) {
     274             :         reprint=true;
     275             :         break;
     276             :       }
     277             :     }
     278     3855388 :   if(reprint) {
     279        6367 :     printf("#! FIELDS");
     280      595460 :     for(unsigned i=0; i<fields.size(); i++) {
     281      589093 :       printf(" %s",fields[i].name.c_str());
     282             :     }
     283        6367 :     printf("\n");
     284       34377 :     for(unsigned i=0; i<const_fields.size(); i++) {
     285       28010 :       printf("#! SET %s %s",const_fields[i].name.c_str(),const_fields[i].value.c_str());
     286       28010 :       printf("\n");
     287             :     }
     288             :   }
     289    20721304 :   for(unsigned i=0; i<fields.size(); i++) {
     290    16865916 :     printf("%s",fields[i].value.c_str());
     291             :   }
     292     3855388 :   printf("\n");
     293     3855388 :   previous_fields=fields;
     294     3855388 :   fields.clear();
     295     3855388 :   fieldChanged=false;
     296     3855388 :   return *this;
     297             : }
     298             : 
     299         194 : void OFile::setBackupString( const std::string& str ) {
     300         194 :   backstring=str;
     301         194 : }
     302             : 
     303           0 : void OFile::backupAllFiles( const std::string& str ) {
     304           0 :   if(str=="/dev/null") {
     305           0 :     return;
     306             :   }
     307           0 :   plumed_assert( backstring!="bck" && !checkRestart());
     308           0 :   size_t found=str.find_last_of("/\\");
     309           0 :   std::string filename = appendSuffix(str,getSuffix());
     310           0 :   std::string directory=filename.substr(0,found+1);
     311           0 :   std::string file=filename.substr(found+1);
     312           0 :   if( FileExist(filename) ) {
     313           0 :     backupFile("bck", filename);
     314             :   }
     315           0 :   for(int i=0;; i++) {
     316             :     std::string num;
     317           0 :     Tools::convert(i,num);
     318           0 :     std::string filestr = directory + backstring + "." + num + "." + file;
     319           0 :     if( !FileExist(filestr) ) {
     320             :       break;
     321             :     }
     322           0 :     backupFile( "bck", filestr);
     323           0 :   }
     324             : }
     325             : 
     326        3840 : void OFile::backupFile( const std::string& bstring, const std::string& fname ) {
     327        3840 :   if(fname=="/dev/null") {
     328         154 :     return;
     329             :   }
     330        3686 :   int maxbackup=100;
     331        3686 :   if(std::getenv("PLUMED_MAXBACKUP")) {
     332          78 :     Tools::convert(std::getenv("PLUMED_MAXBACKUP"),maxbackup);
     333             :   }
     334        3686 :   if(maxbackup>0 && (!comm || comm->Get_rank()==0)) {
     335        3109 :     FILE* ff=std::fopen(const_cast<char*>(fname.c_str()),"r");
     336        3109 :     if(ff) {
     337             :       // no exception here
     338         123 :       std::fclose(ff);
     339             :       std::string backup;
     340         123 :       size_t found=fname.find_last_of("/\\");
     341         123 :       std::string directory=fname.substr(0,found+1);
     342         123 :       std::string file=fname.substr(found+1);
     343         123 :       for(int i=0;; i++) {
     344             :         std::string num;
     345         172 :         Tools::convert(i,num);
     346         172 :         if(i>maxbackup) {
     347           0 :           plumed_merror("cannot backup file "+file+" maximum number of backup is "+num+"\n");
     348             :         }
     349         344 :         backup=directory+bstring +"."+num+"."+file;
     350         172 :         FILE* fff=std::fopen(backup.c_str(),"r");
     351             :         // no exception here
     352         172 :         if(!fff) {
     353             :           break;
     354             :         } else {
     355          49 :           std::fclose(fff);
     356             :         }
     357          49 :       }
     358         123 :       int check=rename(fname.c_str(),backup.c_str());
     359         123 :       plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+std::strerror(errno));
     360             :     }
     361             :   }
     362             : }
     363             : 
     364        4221 : OFile& OFile::open(const std::string&path) {
     365        4221 :   plumed_assert(!cloned);
     366        4221 :   eof=false;
     367        4221 :   err=false;
     368        4221 :   fp=NULL;
     369        4221 :   gzfp=NULL;
     370        4221 :   this->path=path;
     371        8442 :   this->path=appendSuffix(path,getSuffix());
     372        4221 :   if(checkRestart()) {
     373         381 :     fp=std::fopen(const_cast<char*>(this->path.c_str()),"a");
     374         381 :     mode="a";
     375         762 :     if(Tools::extension(this->path)=="gz") {
     376             : #ifdef __PLUMED_HAS_ZLIB
     377          12 :       gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"a9");
     378             : #else
     379             :       plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
     380             : #endif
     381             :     }
     382             :   } else {
     383        3840 :     backupFile( backstring, this->path );
     384        3840 :     if(comm) {
     385        3797 :       comm->Barrier();
     386             :     }
     387        3840 :     fp=std::fopen(const_cast<char*>(this->path.c_str()),"w");
     388        3840 :     mode="w";
     389        7680 :     if(Tools::extension(this->path)=="gz") {
     390             : #ifdef __PLUMED_HAS_ZLIB
     391          13 :       gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
     392             : #else
     393             :       plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
     394             : #endif
     395             :     }
     396             :   }
     397        4221 :   if(plumed) {
     398        3876 :     plumed->insertFile(*this);
     399             :   }
     400        4221 :   return *this;
     401             : }
     402             : 
     403         161 : OFile& OFile::rewind() {
     404             : // we use here "hard" rewind, which means close/reopen
     405             : // the reason is that normal rewind does not work when in append mode
     406             : // moreover, we can take a backup of the file
     407         161 :   plumed_assert(fp);
     408         161 :   clearFields();
     409             : 
     410         161 :   if(!comm || comm->Get_rank()==0) {
     411         130 :     std::string fname=this->path;
     412         130 :     size_t found=fname.find_last_of("/\\");
     413         130 :     std::string directory=fname.substr(0,found+1);
     414         130 :     std::string file=fname.substr(found+1);
     415         260 :     std::string backup=directory+backstring +".last."+file;
     416         130 :     int check=rename(fname.c_str(),backup.c_str());
     417         130 :     plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+std::strerror(errno));
     418             :   }
     419             : 
     420         161 :   if(comm) {
     421         161 :     comm->Barrier();
     422             :   }
     423             : 
     424         161 :   if(gzfp) {
     425             : #ifdef __PLUMED_HAS_ZLIB
     426          15 :     gzclose((gzFile)gzfp);
     427             :     // no exception here
     428          15 :     gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
     429             : #endif
     430             :   } else {
     431         146 :     std::fclose(fp);
     432             :     // no exception here
     433         146 :     fp=std::fopen(const_cast<char*>(path.c_str()),"w");
     434             :   }
     435         161 :   return *this;
     436             : }
     437             : 
     438        9563 : FileBase& OFile::flush() {
     439        9563 :   if(heavyFlush) {
     440        3918 :     if(gzfp) {
     441             : #ifdef __PLUMED_HAS_ZLIB
     442           9 :       gzclose(gzFile(gzfp));
     443             :       // no exception here
     444           9 :       gzfp=(void*)gzopen(const_cast<char*>(path.c_str()),"a");
     445             : #endif
     446             :     } else {
     447        3909 :       std::fclose(fp);
     448             :       // no exception here
     449        3909 :       fp=std::fopen(const_cast<char*>(path.c_str()),"a");
     450             :     }
     451             :   } else {
     452        5645 :     FileBase::flush();
     453             :     // if(gzfp) gzflush(gzFile(gzfp),Z_FINISH);
     454             :     // for some reason flushing with Z_FINISH has problems on linux
     455             :     // I thus use this (incomplete) flush
     456             : #ifdef __PLUMED_HAS_ZLIB
     457        5645 :     if(gzfp) {
     458          31 :       gzflush(gzFile(gzfp),Z_FULL_FLUSH);
     459             :     }
     460             : #endif
     461             :   }
     462        9563 :   return *this;
     463             : }
     464             : 
     465        4221 : bool OFile::checkRestart()const {
     466        4221 :   if(enforceRestart_) {
     467             :     return true;
     468        4210 :   } else if(enforceBackup_) {
     469             :     return false;
     470        3066 :   } else if(action) {
     471        2722 :     return action->getRestart();
     472         344 :   } else if(plumed) {
     473           0 :     return plumed->getRestart();
     474             :   } else {
     475             :     return false;
     476             :   }
     477             : }
     478             : 
     479          11 : OFile& OFile::enforceRestart() {
     480          11 :   enforceRestart_=true;
     481          11 :   enforceBackup_=false;
     482          11 :   return *this;
     483             : }
     484             : 
     485        1144 : OFile& OFile::enforceBackup() {
     486        1144 :   enforceBackup_=true;
     487        1144 :   enforceRestart_=false;
     488        1144 :   return *this;
     489             : }
     490             : 
     491             : 
     492             : }

Generated by: LCOV version 1.16