diff --git a/src/analysis/Analysis.cpp b/src/analysis/Analysis.cpp index 096e6522edadb667ca624d36384c5f1d656f8cf0..a6f2c07a5ae4ea70f6eb245309ec46a59b40d874 100644 --- a/src/analysis/Analysis.cpp +++ b/src/analysis/Analysis.cpp @@ -238,9 +238,12 @@ void Analysis::calculate(){ } // Write data to checkpoint file if( write_chq ) { + // close the restart file so it is flushed + rfile.rewind(); rfile.printField("time",getTime()); rfile.printField("old_normalization",old_norm); for(unsigned i=0;i<getNumberOfArguments();++i) rfile.printField( getPntrToArgument(i), getArgument(i) ); rfile.printField("log_weight",logweights[idata]); rfile.printField(); + rfile.flush(); } // Increment data counter idata++; @@ -291,12 +294,6 @@ void Analysis::finalizeWeights( const bool& ignore_weights ){ void Analysis::runAnalysis(){ - // close the restart file so it is flushed - if( write_chq ){ - rfile.flush(); - rfile.rewind(); - } - // Note : could add multiple walkers here - simply read in the data from all // other walkers here if we are writing the check points. diff --git a/src/tools/OFile.cpp b/src/tools/OFile.cpp index 8d8ce53b3c02038b90a37ae7a2a756e898adf08d..9a28815f8b40f5123197bfcdca9cc76aab4cebee 100644 --- a/src/tools/OFile.cpp +++ b/src/tools/OFile.cpp @@ -284,7 +284,7 @@ OFile& OFile::open(const std::string&path){ if(plumed){ this->path=appendSuffix(path,plumed->getSuffix()); } - if(plumed && plumed->getRestart()){ + if(checkRestart()){ fp=std::fopen(const_cast<char*>(this->path.c_str()),"a"); if(Tools::extension(this->path)=="gz"){ #ifdef __PLUMED_HAS_ZLIB @@ -312,17 +312,28 @@ OFile& OFile::open(const std::string&path){ OFile& OFile::rewind(){ // we use here "hard" rewind, which means close/reopen // the reason is that normal rewind does not work when in append mode +// moreover, we can take a backup of the file plumed_assert(fp); clearFields(); if(gzfp){ #ifdef __PLUMED_HAS_ZLIB gzclose((gzFile)gzfp); - gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9"); #endif - } else { - fclose(fp); - fp=std::fopen(const_cast<char*>(path.c_str()),"w"); + } else fclose(fp); + if(!comm || comm->Get_rank()==0){ + std::string fname=this->path; + size_t found=fname.find_last_of("/\\"); + std::string directory=fname.substr(0,found+1); + std::string file=fname.substr(found+1); + std::string backup=directory+backstring +".last."+file; + int check=rename(fname.c_str(),backup.c_str()); + plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+strerror(errno)); } + if(gzfp){ +#ifdef __PLUMED_HAS_ZLIB + gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9"); +#endif + } else fp=std::fopen(const_cast<char*>(path.c_str()),"w"); return *this; } @@ -349,6 +360,11 @@ FileBase& OFile::flush(){ return *this; } +bool OFile::checkRestart()const{ + if(plumed && plumed->getRestart()) return true; + else return false; +} + } diff --git a/src/tools/OFile.h b/src/tools/OFile.h index 7ae4ffc0a3d59e4476472e8b9e11a10a302d32c8..bd45eeaa5420dd33f1d8c0191bad04f924b7e3e5 100644 --- a/src/tools/OFile.h +++ b/src/tools/OFile.h @@ -43,7 +43,7 @@ See the example here for a possible use: int main(){ PLMD::OFile pof; - pof.open("ciao","w"); + pof.open("ciao"); pof.printf("%s\n","test1"); pof.setLinePrefix("plumed: "); pof.printf("%s\n","test2"); @@ -79,6 +79,65 @@ keyword. Thus, everytime it is modified, all the headers are repeated in the out - most methods return a reference to the OFile itself, to allow chaining many calls on the same line (this is similar to << operator in std::ostream) +\section Using correctly OFile in PLUMED + +When a OFile object is used in PLUMED it can be convenient to link() it +to the Action object where it is defined, or to the PlumedMain object. +This will save in the OFile a pointer to the linked object and will +allow to have some extra information. E.g., if PLUMED is restarting, +files will be appended (notice that this is the only way to +append a OFile - there is no way to enforce it). + +To have all files managed consistently, it is important to use OFile in the proper way. +This should allow multi-replica plumed, restart and backups to work in +the expected way. + +\verbatim +int main(){ +// this is a growing file, containing a full history +// (frames are appended, as in traditional HILLS and COLVAR) + OFile grw; +// this is a single-snapshopt file used e.g. for checkpointing +// (rewritten every time) + OFile snp; + +// open both files at the beginning +// (will go in \ref Action constructor) + grw.open("growing"); + snp.open("snapshot"); + +// trajectory loop + for(int i=0;i<nsteps;i++){ + +// files should be writen in the update() method of an \ref Action + +// write on growing file + grw<<"data at step "<<i<<\n"; + +// flushing +// it takes time, so do it only if data is critical +// better to leave this choice to the user with the FLUSH keyword +// grw.flush(); + +// write on snapshot file + snp.rewind(); + snp<<"snapshot at step "<<i<<"\n"; + snp.flush(); +// the only difference is that snp is rewound +// notice that it should be rewound just before writing +// because rewind is going to move the file out of the way +// to have a safe copy of the file ("bck.last.filename") +// Also notice that snapshots should be flushed +// for this reason, it is better to write them only +// rarely to avoid excessive slow down + + } + + snp.close(); + grw.close(); +} + +\endverbatim */ class OFile: @@ -120,6 +179,8 @@ public virtual FileBase{ std::string backstring; /// Find field index given name unsigned findField(const std::string&name)const; +/// check if we are restarting + bool checkRestart()const; public: /// Constructor OFile();