LCOV - code coverage report
Current view: top level - tools - OFile.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 196 202 97.0 %
Date: 2021-11-18 15:22:58 Functions: 27 27 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2020 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     4139096 : size_t OFile::llwrite(const char*ptr,size_t s) {
      47             :   size_t r;
      48     4139096 :   if(linked) return linked->llwrite(ptr,s);
      49     4139079 :   if(! (comm && comm->Get_rank()>0)) {
      50     3422886 :     if(!fp) plumed_merror("writing on uninitilized File");
      51     3422886 :     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     3421623 :       r=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     4139079 :   if(comm) comm->Barrier();
      68             : 
      69             : 
      70     4139079 :   if(comm) comm->Bcast(r,0);
      71     4139079 :   return r;
      72             : }
      73             : 
      74        6285 : OFile::OFile():
      75             :   linked(NULL),
      76             :   fieldChanged(false),
      77             :   backstring("bck"),
      78             :   enforceRestart_(false),
      79       37710 :   enforceBackup_(false)
      80             : {
      81        6285 :   fmtField();
      82        6285 :   buflen=1;
      83        6285 :   actual_buffer_length=0;
      84        6285 :   buffer.reset(new char [buflen]);
      85             : // these are set to zero to avoid valgrind errors
      86       18855 :   for(int i=0; i<buflen; ++i) buffer[i]=0;
      87        6285 :   buffer_string.reset(new char [1000]);
      88             : // these are set to zero to avoid valgrind errors
      89    12576285 :   for(unsigned i=0; i<1000; ++i) buffer_string[i]=0;
      90        6285 : }
      91             : 
      92           4 : OFile& OFile::link(OFile&l) {
      93           4 :   fp=NULL;
      94           4 :   gzfp=NULL;
      95           4 :   linked=&l;
      96           4 :   return *this;
      97             : }
      98             : 
      99        2575 : OFile& OFile::setLinePrefix(const std::string&l) {
     100        2575 :   linePrefix=l;
     101        2575 :   return *this;
     102             : }
     103             : 
     104    19625657 : int OFile::printf(const char*fmt,...) {
     105             :   va_list arg;
     106    19625657 :   va_start(arg, fmt);
     107    19625657 :   int r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
     108    19625657 :   va_end(arg);
     109    19625657 :   if(r>=buflen-actual_buffer_length) {
     110             :     int newlen=buflen;
     111       37561 :     while(newlen<=r+actual_buffer_length) newlen*=2;
     112       12623 :     std::unique_ptr<char[]> newbuf{new char [newlen]};
     113       12623 :     memmove(newbuf.get(),buffer.get(),buflen);
     114     4082929 :     for(int k=buflen; k<newlen; k++) newbuf[k]=0;
     115             :     buffer=std::move(newbuf);
     116       12623 :     buflen=newlen;
     117             :     va_list arg;
     118       12623 :     va_start(arg, fmt);
     119       12623 :     r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
     120       12623 :     va_end(arg);
     121             :   }
     122    19625657 :   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    19625657 :   char*psearch=p1+actual_buffer_length;
     129    19625657 :   actual_buffer_length+=r;
     130    27495507 :   while((p2=strchr(psearch,'\n'))) {
     131     4139079 :     if(linePrefix.length()>0) llwrite(linePrefix.c_str(),linePrefix.length());
     132     3934925 :     llwrite(p1,p2-p1+1);
     133     3934925 :     actual_buffer_length-=(p2-p1)+1;
     134     3934925 :     p1=p2+1;
     135             :     psearch=p1;
     136             :   };
     137    19625657 :   if(buffer.get()!=p1) memmove(buffer.get(),p1,actual_buffer_length);
     138    19625657 :   return r;
     139             : }
     140             : 
     141       24944 : OFile& OFile::addConstantField(const std::string&name) {
     142             :   Field f;
     143             :   f.name=name;
     144       24944 :   const_fields.push_back(f);
     145       24944 :   return *this;
     146             : }
     147             : 
     148             : 
     149        2986 : OFile& OFile::clearFields() {
     150             :   fields.clear();
     151             :   const_fields.clear();
     152             :   previous_fields.clear();
     153        2986 :   return *this;
     154             : }
     155             : 
     156    12845781 : OFile& OFile::fmtField(const std::string&fmt) {
     157    12845781 :   this->fieldFmt=fmt;
     158    12845781 :   return *this;
     159             : }
     160             : 
     161       11854 : OFile& OFile::fmtField() {
     162       11854 :   this->fieldFmt="%23.16lg";
     163       11854 :   return *this;
     164             : }
     165             : 
     166    13442114 : 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             :   if(std::isnan(v)) v=std::numeric_limits<double>::quiet_NaN();
     171             :   sprintf(buffer_string.get(),fieldFmt.c_str(),v);
     172    26884228 :   printField(name,buffer_string.get());
     173    13442114 :   return *this;
     174             : }
     175             : 
     176     5684023 : OFile& OFile::printField(const std::string&name,int v) {
     177             :   sprintf(buffer_string.get()," %d",v);
     178    11368046 :   printField(name,buffer_string.get());
     179     5684023 :   return *this;
     180             : }
     181             : 
     182    34116836 : OFile& OFile::printField(const std::string&name,const std::string & v) {
     183             :   unsigned i;
     184   516779834 :   for(i=0; i<const_fields.size(); i++) if(const_fields[i].name==name) break;
     185    34116836 :   if(i>=const_fields.size()) {
     186             :     Field field;
     187             :     field.name=name;
     188             :     field.value=v;
     189    14416915 :     fields.push_back(field);
     190             :   } else {
     191    39399842 :     if(const_fields[i].value!=v) fieldChanged=true;
     192             :     const_fields[i].value=v;
     193             :   }
     194    34116836 :   return *this;
     195             : }
     196             : 
     197        5039 : OFile& OFile::setupPrintValue( Value *val ) {
     198        5039 :   if( val->isPeriodic() ) {
     199         506 :     addConstantField("min_" + val->getName() );
     200         506 :     addConstantField("max_" + val->getName() );
     201             :   }
     202        5039 :   return *this;
     203             : }
     204             : 
     205      155393 : OFile& OFile::printField( Value* val, const double& v ) {
     206      310786 :   printField( val->getName(), v );
     207      155393 :   if( val->isPeriodic() ) {
     208       10857 :     std::string min, max; val->getDomain( min, max );
     209       21714 :     printField( "min_" + val->getName(), min );
     210       21714 :     printField("max_" + val->getName(), max );
     211             :   }
     212      155393 :   return *this;
     213             : }
     214             : 
     215     3474614 : OFile& OFile::printField() {
     216             :   bool reprint=false;
     217     6944936 :   if(fieldChanged || fields.size()!=previous_fields.size()) {
     218             :     reprint=true;
     219    32230388 :   } else for(unsigned i=0; i<fields.size(); i++) {
     220    57522058 :       if( previous_fields[i].name!=fields[i].name ||
     221    14380513 :           (fields[i].constant && fields[i].value!=previous_fields[i].value) ) {
     222             :         reprint=true;
     223             :         break;
     224             :       }
     225             :     }
     226     3474614 :   if(reprint) {
     227        5254 :     printf("#! FIELDS");
     228      119720 :     for(unsigned i=0; i<fields.size(); i++) printf(" %s",fields[i].name.c_str());
     229        5254 :     printf("\n");
     230       85172 :     for(unsigned i=0; i<const_fields.size(); i++) {
     231       24888 :       printf("#! SET %s %s",const_fields[i].name.c_str(),const_fields[i].value.c_str());
     232       24888 :       printf("\n");
     233             :     }
     234             :   }
     235    50199973 :   for(unsigned i=0; i<fields.size(); i++) printf("%s",fields[i].value.c_str());
     236     3474614 :   printf("\n");
     237     3474614 :   previous_fields=fields;
     238             :   fields.clear();
     239     3474614 :   fieldChanged=false;
     240     3474614 :   return *this;
     241             : }
     242             : 
     243         121 : void OFile::setBackupString( const std::string& str ) {
     244         121 :   backstring=str;
     245         121 : }
     246             : 
     247          36 : void OFile::backupAllFiles( const std::string& str ) {
     248          36 :   if(str=="/dev/null") return;
     249          72 :   plumed_assert( backstring!="bck" && !checkRestart());
     250             :   size_t found=str.find_last_of("/\\");
     251          72 :   std::string filename = appendSuffix(str,getSuffix());
     252          36 :   std::string directory=filename.substr(0,found+1);
     253          36 :   std::string file=filename.substr(found+1);
     254          36 :   if( FileExist(filename) ) backupFile("bck", filename);
     255           0 :   for(int i=0;; i++) {
     256          36 :     std::string num; Tools::convert(i,num);
     257         180 :     std::string filestr = directory + backstring + "." + num + "." + file;
     258          36 :     if( !FileExist(filestr) ) break;
     259           0 :     backupFile( "bck", filestr);
     260           0 :   }
     261             : }
     262             : 
     263        2689 : void OFile::backupFile( const std::string& bstring, const std::string& fname ) {
     264        2845 :   if(fname=="/dev/null") return;
     265        2533 :   int maxbackup=100;
     266        2533 :   if(std::getenv("PLUMED_MAXBACKUP")) Tools::convert(std::getenv("PLUMED_MAXBACKUP"),maxbackup);
     267        2533 :   if(maxbackup>0 && (!comm || comm->Get_rank()==0)) {
     268        2124 :     FILE* ff=std::fopen(const_cast<char*>(fname.c_str()),"r");
     269        2124 :     if(ff) {
     270          58 :       std::fclose(ff);
     271             :       std::string backup;
     272             :       size_t found=fname.find_last_of("/\\");
     273          58 :       std::string directory=fname.substr(0,found+1);
     274          58 :       std::string file=fname.substr(found+1);
     275           0 :       for(int i=0;; i++) {
     276             :         std::string num;
     277          58 :         Tools::convert(i,num);
     278          58 :         if(i>maxbackup) plumed_merror("cannot backup file "+file+" maximum number of backup is "+num+"\n");
     279         348 :         backup=directory+bstring +"."+num+"."+file;
     280          58 :         FILE* fff=std::fopen(backup.c_str(),"r");
     281          58 :         if(!fff) break;
     282           0 :         else std::fclose(fff);
     283           0 :       }
     284          58 :       int check=rename(fname.c_str(),backup.c_str());
     285          58 :       plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+strerror(errno));
     286             :     }
     287             :   }
     288             : }
     289             : 
     290        2955 : OFile& OFile::open(const std::string&path) {
     291        2955 :   plumed_assert(!cloned);
     292        2955 :   eof=false;
     293        2955 :   err=false;
     294        2955 :   fp=NULL;
     295        2955 :   gzfp=NULL;
     296        2955 :   this->path=path;
     297        8865 :   this->path=appendSuffix(path,getSuffix());
     298        2955 :   if(checkRestart()) {
     299         532 :     fp=std::fopen(const_cast<char*>(this->path.c_str()),"a");
     300         266 :     mode="a";
     301         532 :     if(Tools::extension(this->path)=="gz") {
     302             : #ifdef __PLUMED_HAS_ZLIB
     303          48 :       gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"a9");
     304             : #else
     305             :       plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
     306             : #endif
     307             :     }
     308             :   } else {
     309        2689 :     backupFile( backstring, this->path );
     310        2689 :     if(comm)comm->Barrier();
     311        5378 :     fp=std::fopen(const_cast<char*>(this->path.c_str()),"w");
     312        2689 :     mode="w";
     313        5378 :     if(Tools::extension(this->path)=="gz") {
     314             : #ifdef __PLUMED_HAS_ZLIB
     315           2 :       gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
     316             : #else
     317             :       plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
     318             : #endif
     319             :     }
     320             :   }
     321        2955 :   if(plumed) plumed->insertFile(*this);
     322        2955 :   return *this;
     323             : }
     324             : 
     325         114 : OFile& OFile::rewind() {
     326             : // we use here "hard" rewind, which means close/reopen
     327             : // the reason is that normal rewind does not work when in append mode
     328             : // moreover, we can take a backup of the file
     329         114 :   plumed_assert(fp);
     330         114 :   clearFields();
     331         114 :   if(gzfp) {
     332             : #ifdef __PLUMED_HAS_ZLIB
     333          15 :     gzclose((gzFile)gzfp);
     334             : #endif
     335          99 :   } else fclose(fp);
     336         114 :   if(!comm || comm->Get_rank()==0) {
     337          92 :     std::string fname=this->path;
     338             :     size_t found=fname.find_last_of("/\\");
     339          92 :     std::string directory=fname.substr(0,found+1);
     340          92 :     std::string file=fname.substr(found+1);
     341         276 :     std::string backup=directory+backstring +".last."+file;
     342          92 :     int check=rename(fname.c_str(),backup.c_str());
     343          92 :     plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+strerror(errno));
     344             :   }
     345             : 
     346         114 :   if(comm) comm->Barrier();
     347             : 
     348         114 :   if(gzfp) {
     349             : #ifdef __PLUMED_HAS_ZLIB
     350          15 :     gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
     351             : #endif
     352          99 :   } else fp=std::fopen(const_cast<char*>(path.c_str()),"w");
     353         114 :   return *this;
     354             : }
     355             : 
     356        7756 : FileBase& OFile::flush() {
     357        7756 :   if(heavyFlush) {
     358        3795 :     if(gzfp) {
     359             : #ifdef __PLUMED_HAS_ZLIB
     360           9 :       gzclose(gzFile(gzfp));
     361          18 :       gzfp=(void*)gzopen(const_cast<char*>(path.c_str()),"a");
     362             : #endif
     363             :     } else {
     364        3786 :       fclose(fp);
     365        7572 :       fp=std::fopen(const_cast<char*>(path.c_str()),"a");
     366             :     }
     367             :   } else {
     368        3961 :     FileBase::flush();
     369             :     // if(gzfp) gzflush(gzFile(gzfp),Z_FINISH);
     370             :     // for some reason flushing with Z_FINISH has problems on linux
     371             :     // I thus use this (incomplete) flush
     372             : #ifdef __PLUMED_HAS_ZLIB
     373        3961 :     if(gzfp) gzflush(gzFile(gzfp),Z_FULL_FLUSH);
     374             : #endif
     375             :   }
     376        7756 :   return *this;
     377             : }
     378             : 
     379        2991 : bool OFile::checkRestart()const {
     380        2991 :   if(enforceRestart_) return true;
     381        2990 :   else if(enforceBackup_) return false;
     382        3837 :   else if(action) return action->getRestart();
     383         191 :   else if(plumed) return plumed->getRestart();
     384             :   else return false;
     385             : }
     386             : 
     387           1 : OFile& OFile::enforceRestart() {
     388           1 :   enforceRestart_=true;
     389           1 :   enforceBackup_=false;
     390           1 :   return *this;
     391             : }
     392             : 
     393         976 : OFile& OFile::enforceBackup() {
     394         976 :   enforceBackup_=true;
     395         976 :   enforceRestart_=false;
     396         976 :   return *this;
     397             : }
     398             : 
     399             : 
     400        5517 : }

Generated by: LCOV version 1.14