diff --git a/src/tools/File.cpp b/src/tools/File.cpp deleted file mode 100644 index 0fc8a52e21304855f298d350c44a31f7932ad4d0..0000000000000000000000000000000000000000 --- a/src/tools/File.cpp +++ /dev/null @@ -1,550 +0,0 @@ -/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Copyright (c) 2012 The plumed team - (see the PEOPLE file at the root of the distribution for a list of names) - - See http://www.plumed-code.org for more information. - - This file is part of plumed, version 2.0. - - plumed is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - plumed is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with plumed. If not, see <http://www.gnu.org/licenses/>. -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ -#include "File.h" -#include "PlumedException.h" -#include "core/Action.h" -#include "core/PlumedMain.h" -#include "core/Value.h" -#include "Communicator.h" -#include "Tools.h" -#include <cstdarg> -#include <cstring> - -#include <iostream> -#include <string> - -using namespace PLMD; - -void FileBase::test(){ - PLMD::OFile pof; - pof.open("ciao"); - pof.printf("%s\n","test1"); - pof.setLinePrefix("plumed: "); - pof.printf("%s\n","test2"); - pof.setLinePrefix(""); - pof.addConstantField("x2").printField("x2",67.0); - pof.printField("x1",10.0).printField("x3",20.12345678901234567890).printField(); - pof.printField("x1",10.0).printField("x3",-1e70*20.12345678901234567890).printField(); - pof.printField("x3",10.0).printField("x2",777.0).printField("x1",-1e70*20.12345678901234567890).printField(); - pof.printField("x3",67.0).printField("x1",18.0).printField(); - pof.close(); - - PLMD::IFile pif; - std::string s; - pif.open("ciao"); - pif.getline(s); std::printf("%s\n",s.c_str()); - pif.getline(s); std::printf("%s\n",s.c_str()); - - int x1,x2,x3; - while(pif.scanField("x1",x1).scanField("x3",x2).scanField("x2",x3).scanField()){ - std::cout<<"CHECK "<<x1<<" "<<x2<<" "<<x3<<"\n"; - } - pif.close(); -} - -size_t OFile::llwrite(const char*ptr,size_t s){ - size_t r; - if(linked) return linked->llwrite(ptr,s); - if(! (comm && comm->Get_rank()>0)){ - if(!fp) plumed_merror("writing on uninitilized File"); - r=fwrite(ptr,1,s,fp); - } - if(comm) comm->Bcast(&r,1,0); - return r; -} - -size_t IFile::llread(char*ptr,size_t s){ - plumed_assert(fp); - size_t r; - r=fread(ptr,1,s,fp); - if(feof(fp)) eof=true; - if(ferror(fp)) err=true; - return r; -} - -FileBase& FileBase::link(FILE*fp){ - this->fp=fp; - cloned=true; - return *this; -} - -FileBase& FileBase::flush(){ - fflush(fp); - if(heavyFlush){ - fclose(fp); - fp=std::fopen(const_cast<char*>(path.c_str()),"a"); - } - return *this; -} - -FileBase& FileBase::link(Communicator&comm){ - plumed_massert(!fp,"cannot link an already open file"); - this->comm=&comm; - return *this; -} - -FileBase& FileBase::link(PlumedMain&plumed){ - plumed_massert(!fp,"cannot link an already open file"); - this->plumed=&plumed; - link(plumed.comm); - return *this; -} - -FileBase& FileBase::link(Action&action){ - plumed_massert(!fp,"cannot link an already open file"); - this->action=&action; - link(action.plumed); - return *this; -} - -FileBase& FileBase::open(const std::string& path,const std::string& mode){ - plumed_assert(!cloned); - eof=false; - err=false; - fp=NULL; - if(plumed){ - this->path=path+plumed->getSuffix(); - fp=std::fopen(const_cast<char*>(this->path.c_str()),const_cast<char*>(mode.c_str())); - } - if(!fp){ - this->path=path; - fp=std::fopen(const_cast<char*>(this->path.c_str()),const_cast<char*>(mode.c_str())); - } - if(plumed) plumed->insertFile(*this); - plumed_massert(fp,"file " + path + "cannot be found"); - return *this; -} - - -bool FileBase::FileExist(const std::string& path){ - FILE *ff=NULL; - bool do_exist=false; - if(plumed){ - this->path=path+plumed->getSuffix(); - ff=std::fopen(const_cast<char*>(this->path.c_str()),"r"); - } - if(!ff){ - this->path=path; - ff=std::fopen(const_cast<char*>(this->path.c_str()),"r"); - } - if(ff) {do_exist=true; fclose(ff);} - return do_exist; -} - -bool FileBase::isOpen(){ - bool isopen=false; - if(fp) isopen=true; - return isopen; -} - -void FileBase::close(){ - plumed_assert(!cloned); - eof=false; - err=false; - std::fclose(fp); - fp=NULL; -} - -FileBase::FileBase(): - fp(NULL), - comm(NULL), - plumed(NULL), - action(NULL), - cloned(false), - eof(false), - err(false), - heavyFlush(false) -{ -} - -FileBase::~FileBase() -{ - if(plumed) plumed->eraseFile(*this); - if(!cloned && fp) fclose(fp); -} - -FileBase::operator bool()const{ - return !eof; -} - - -OFile::OFile(): - linked(NULL), - fieldChanged(false) -{ - fmtField(); - buflen=1; - buffer=new char[buflen]; -// these are set to zero to avoid valgrind errors - for(unsigned i=0;i<buflen;++i) buffer[i]=0; - buffer_string=new char [1000]; -// these are set to zero to avoid valgrind errors - for(unsigned i=0;i<1000;++i) buffer_string[i]=0; -} - -OFile::~OFile(){ - delete [] buffer_string; - delete [] buffer; -} - -OFile& OFile::link(OFile&l){ - fp=NULL; - linked=&l; - return *this; -} - -OFile& OFile::setLinePrefix(const std::string&l){ - linePrefix=l; - return *this; -} - -int OFile::printf(const char*fmt,...){ - size_t pointer=strlen(buffer); - va_list arg; - va_start(arg, fmt); - int r=std::vsnprintf(&buffer[pointer],buflen-pointer,fmt,arg); - va_end(arg); - if(r>=buflen-pointer){ - int newlen=buflen; - while(newlen<=r+pointer) newlen*=2; - char* newbuf=new char [newlen]; - memmove(newbuf,buffer,buflen); - for(int k=buflen;k<newlen;k++) newbuf[k]=0; - delete [] buffer; - buffer=newbuf; - buflen=newlen; - va_list arg; - va_start(arg, fmt); - r=std::vsnprintf(&buffer[pointer],buflen-pointer,fmt,arg); - va_end(arg); - } - plumed_massert(r>-1 && r<buflen-pointer,"error using fmt string " + std::string(fmt)); - -// Line is buffered until newline, then written with a PLUMED: prefix - char*p1=buffer; - char*p2; - while((p2=strchr(p1,'\n'))){ - *p2='\0'; - if(linePrefix.length()>0) llwrite(linePrefix.c_str(),linePrefix.length()); - llwrite(p1,std::strlen(p1)); - llwrite("\n",1); - p1=p2+1; - }; - memmove(buffer,p1,strlen(p1)+1); - return r; -} - -OFile& OFile::addConstantField(const std::string&name){ - Field f; - f.name=name; - const_fields.push_back(f); - return *this; -} - - -OFile& OFile::clearFields(){ - fields.clear(); - const_fields.clear(); - previous_fields.clear(); - return *this; -} - -OFile& OFile::fmtField(const std::string&fmt){ - this->fieldFmt=fmt; - return *this; -} - -OFile& OFile::fmtField(){ - this->fieldFmt="%23.16lg"; - return *this; -} - -OFile& OFile::printField(const std::string&name,double v){ - sprintf(buffer_string,fieldFmt.c_str(),v); - printField(name,buffer_string); - return *this; -} - -OFile& OFile::printField(const std::string&name,int v){ - sprintf(buffer_string," %d",v); - printField(name,buffer_string); - return *this; -} - -OFile& OFile::printField(const std::string&name,const std::string & v){ - unsigned i; - for(i=0;i<const_fields.size();i++) if(const_fields[i].name==name) break; - if(i>=const_fields.size()){ - Field field; - field.name=name; - field.value=v; - fields.push_back(field); - } else { - if(const_fields[i].value!=v) fieldChanged=true; - const_fields[i].value=v; - } - return *this; -} - -OFile& OFile::setupPrintValue( Value *val ){ - if( val->isPeriodic() ){ - addConstantField("min_" + val->getName() ); - addConstantField("max_" + val->getName() ); - } - return *this; -} - -OFile& OFile::printField( Value* val, const double& v ){ - printField( val->getName(), v ); - if( val->isPeriodic() ){ - std::string min, max; val->getDomain( min, max ); - printField( "min_" + val->getName(), min ); - printField("max_" + val->getName(), max ); - } - return *this; -} - -OFile& OFile::printField(){ - bool reprint=false; - if(fieldChanged || fields.size()!=previous_fields.size()){ - reprint=true; - } else for(unsigned i=0;i<fields.size();i++){ - if( previous_fields[i].name!=fields[i].name || - (fields[i].constant && fields[i].value!=previous_fields[i].value) ){ - reprint=true; - break; - } - } - if(reprint){ - printf("#! FIELDS"); - for(unsigned i=0;i<fields.size();i++) printf(" %s",fields[i].name.c_str()); - printf("\n"); - for(unsigned i=0;i<const_fields.size();i++){ - printf("#! SET %s %s",const_fields[i].name.c_str(),const_fields[i].value.c_str()); - printf("\n"); - } - } - for(unsigned i=0;i<fields.size();i++) printf("%s",fields[i].value.c_str()); - printf("\n"); - previous_fields=fields; - fields.clear(); - fieldChanged=false; - return *this; -} - -OFile& OFile::open(const std::string&path){ - plumed_assert(!cloned); - eof=false; - err=false; - fp=NULL; - this->path=path; - if(plumed){ - this->path+=plumed->getSuffix(); - } - if(plumed && plumed->getRestart()){ - fp=std::fopen(const_cast<char*>(this->path.c_str()),"a"); - } else { - if(!comm || comm->Get_rank()==0){ - FILE* ff=std::fopen(const_cast<char*>(this->path.c_str()),"r"); - FILE* fff=NULL; - if(ff){ - std::string backup; - size_t found=this->path.find_last_of("/\\"); - std::string directory=this->path.substr(0,found+1); - std::string file=this->path.substr(found+1); - for(int i=0;;i++){ - std::string num; - Tools::convert(i,num); - backup=directory+"bck."+num+"."+file; - fff=std::fopen(backup.c_str(),"r"); - if(!fff) break; - } - int check=rename(this->path.c_str(),backup.c_str()); - plumed_massert(check==0,"renaming "+this->path+" into "+backup+" failed for some reason"); - } - if(ff) std::fclose(ff); - if(fff) std::fclose(fff); - } - comm->Barrier(); - fp=std::fopen(const_cast<char*>(this->path.c_str()),"w"); - } - if(plumed) plumed->insertFile(*this); - return *this; -} - - -IFile& IFile::advanceField(){ - plumed_assert(!inMiddleOfField); - std::string line; - bool done=false; - while(!done){ - getline(line); - if(!*this){return *this;} - std::vector<std::string> words=Tools::getWords(line); - if(words.size()>=2 && words[0]=="#!" && words[1]=="FIELDS"){ - fields.clear(); - for(unsigned i=2;i<words.size();i++){ - Field field; - field.name=words[i]; - fields.push_back(field); - } - } else if(words.size()==4 && words[0]=="#!" && words[1]=="SET"){ - Field field; - field.name=words[2]; - field.value=words[3]; - field.constant=true; - fields.push_back(field); - } else { - unsigned nf=0; - for(unsigned i=0;i<fields.size();i++) if(!fields[i].constant) nf++; - Tools::trimComments(line); - words=Tools::getWords(line); - if( words.size()==nf ){ - unsigned j=0; - for(unsigned i=0;i<fields.size();i++){ - if(fields[i].constant) continue; - fields[i].value=words[j]; - fields[i].read=false; - j++; - } - done=true; - } else if( words.size()!=0 ) { - plumed_merror("mismatch between number of fields in file and expected number"); - } - } - } - inMiddleOfField=true; - return *this; -} - -IFile& IFile::open(const std::string&name){ - FileBase::open(name,"r"); - return *this; -} - -IFile& IFile::scanFieldList(std::vector<std::string>&s){ - if(!inMiddleOfField) advanceField(); - if(!*this) return *this; - s.clear(); - for(unsigned i=0;i<fields.size();i++) - s.push_back(fields[i].name); - return *this; -} - -bool IFile::FieldExist(const std::string& s){ - std::vector<std::string> slist; - scanFieldList(slist); - int mycount = (int) std::count(slist.begin(), slist.end(), s); - if(mycount>0) return true; - else return false; -} - -IFile& IFile::scanField(const std::string&name,std::string&str){ - if(!inMiddleOfField) advanceField(); - if(!*this) return *this; - unsigned i=findField(name); - str=fields[i].value; - fields[i].read=true; - return *this; -} - -IFile& IFile::scanField(const std::string&name,double &x){ - std::string str; - scanField(name,str); - if(*this) Tools::convert(str,x); - return *this; -} - -IFile& IFile::scanField(const std::string&name,int &x){ - std::string str; - scanField(name,str); - if(*this) Tools::convert(str,x); - return *this; -} - -IFile& IFile::scanField(Value* val){ - double ff; scanField( val->getName(), ff ); - val->set( ff ); - if( FieldExist("min_" + val->getName() ) ){ - std::string min, max; - scanField("min_" + val->getName(), min ); - scanField("max_" + val->getName(), max ); - val->setDomain( min, max ); - } else { - val->setNotPeriodic(); - } - return *this; -} - -IFile& IFile::scanField(){ - if(!ignoreFields){ - for(unsigned i=0;i<fields.size();i++){ - plumed_assert(fields[i].read); - } - } - inMiddleOfField=false; - return *this; -} - -IFile::IFile(): - inMiddleOfField(false), - ignoreFields(false) -{ -} - -IFile::~IFile(){ - plumed_assert(!inMiddleOfField); -} - -IFile& IFile::getline(std::string &str){ - char tmp; - str=""; - fpos_t pos; - fgetpos(fp,&pos); - while(llread(&tmp,1)==1 && tmp && tmp!='\n' && !eof && !err){ - str+=tmp; - } - if(tmp!='\n' || err){ - eof = true; - str=""; - fsetpos(fp,&pos); - } - return *this; -} - -unsigned IFile::findField(const std::string&name)const{ - unsigned i; - for(i=0;i<fields.size();i++) if(fields[i].name==name) break; - if(i>=fields.size()) plumed_merror(name); - return i; -} - -void IFile::reset(bool reset){ - eof = reset; - err = reset; - if(!reset) clearerr(fp); - return; -} - -void IFile::allowIgnoredFields(){ - ignoreFields=true; -} diff --git a/src/tools/File.h b/src/tools/File.h index 99888dcebefb4e0ac775c70c0ad05812010095ad..4bafe99f5caea140194046f275d5acc481f6e3ea 100644 --- a/src/tools/File.h +++ b/src/tools/File.h @@ -22,314 +22,7 @@ #ifndef __PLUMED_tools_File_h #define __PLUMED_tools_File_h -#include <cstdio> -#include <vector> -#include <string> -#include <sstream> - -namespace PLMD{ - -class Communicator; -class PlumedMain; -class Action; -class Value; - -/** -Base class for dealing with files. - -This class just provides things which are common among OFile and IFile -*/ - -class FileBase{ -/// Copy constructor is disabled (private and unimplemented) - FileBase(const FileBase&); -/// Assignment operator is disabled (private and unimplemented) - FileBase& operator=(const FileBase&); -protected: -/// Internal tool. -/// Base for IFile::Field and OFile::Field - class FieldBase{ -// everything is public to simplify usage - public: - std::string name; - std::string value; - bool constant; - FieldBase(): constant(false){} - }; - -/// file pointer - FILE* fp; -/// communicator. NULL if not set - Communicator* comm; -/// pointer to main plumed object. NULL if not linked - PlumedMain* plumed; -/// pointer to corresponding action. NULL if not linked - Action* action; -/// Control closing on destructor. -/// If true, file will not be closed in destructor - bool cloned; -/// Private constructor. -/// In this manner one cannot instantiate a FileBase object - FileBase(); -/// Set to true when end of file is encountered - bool eof; -/// Set to true when error is encountered - bool err; -/// path of the opened file - std::string path; -/// Set to true if you want flush to be heavy (close/reopen) - bool heavyFlush; -public: -/// Link to an already open filed - FileBase& link(FILE*); -/// Link to a PlumedMain object -/// Automatically links also the corresponding Communicator. - FileBase& link(PlumedMain&); -/// Link to a Communicator object - FileBase& link(Communicator&); -/// Link to an Action object. -/// Automatically links also the corresponding PlumedMain and Communicator. - FileBase& link(Action&); -/// Flushes the file to disk - FileBase& flush(); -/// Closes the file -/// Should be used only for explicitely opened files. - void close(); -/// Virtual destructor (allows inheritance) - virtual ~FileBase(); -/// Runs a small testcase - static void test(); -/// Check for error/eof. - operator bool () const; -/// Set heavyFlush flag - void setHeavyFlush(){ heavyFlush=true;}; -/// Opens the file (without auto-backup) - FileBase& open(const std::string&name,const std::string& mode); -/// Check if the file exists - bool FileExist(const std::string& path); -/// Check if a file is open - bool isOpen(); -}; - -/** -\ingroup TOOLBOX -Class for output files - -This class provides features similar to those in the standard C "FILE*" type, -but only for sequential output. See IFile for sequential input. - -See the example here for a possible use: -\verbatim -#include "File.h" - -int main(){ - PLMD::OFile pof; - pof.open("ciao","w"); - pof.printf("%s\n","test1"); - pof.setLinePrefix("plumed: "); - pof.printf("%s\n","test2"); - pof.setLinePrefix(""); - pof.addConstantField("x2").printField("x2",67.0); - pof.printField("x1",10.0).printField("x3",20.12345678901234567890).printField(); - pof.printField("x1",10.0).printField("x3",-1e70*20.12345678901234567890).printField(); - pof.printField("x3",10.0).printField("x2",777.0).printField("x1",-1e70*20.12345678901234567890).printField(); - pof.printField("x3",67.0).printField("x1",18.0).printField(); - pof.close(); - return 0; -} -\endverbatim - -This program is expected to produce a file "ciao" which reads -\verbatim -test1 -plumed: test2 -#! FIELDS x1 x3 -#! SET x2 67 - 10 20.12345678901234 - 10 -2.012345678901235e+71 -#! FIELDS x1 x3 -#! SET x2 777 - -2.012345678901235e+71 10 - 18 67 -\endverbatim - -Notes -- "x2" is declared as "constant", which means that it is written using the "SET" -keyword. Thus, everytime it is modified, all the headers are repeated in the output file. -- printField() without arguments is used as a "newline". -- 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) - -*/ - -class OFile: -public virtual FileBase{ -/// Pointer to a linked OFile. -/// see link(OFile&) - OFile* linked; -/// Internal buffer for printf - char* buffer_string; -/// Internal buffer (generic use) - char* buffer; -/// Internal buffer length - int buflen; -/// Class identifying a single field for fielded output - class Field: - public FieldBase{ - }; -/// Low-level write - size_t llwrite(const char*,size_t); -/// True if fields has changed. -/// This could be due to a change in the list of fields or a reset -/// of a nominally constant field - bool fieldChanged; -/// Format for fields writing - std::string fieldFmt; -/// All the previously defined variable fields - std::vector<Field> previous_fields; -/// All the defined variable fields - std::vector<Field> fields; -/// All the defined constant fields - std::vector<Field> const_fields; -/// Prefix for line (e.g. "PLUMED: ") - std::string linePrefix; -/// Temporary ostringstream for << output - std::ostringstream oss; -/// Find field index given name - unsigned findField(const std::string&name)const; -public: -/// Constructor - OFile(); -/// Destructor - ~OFile(); -/// Allows overloading of link - using FileBase::link; -/// Allows overloading of open - using FileBase::open; -/// Allows linking this OFile to another one. -/// In this way, everything written to this OFile will be immediately -/// written on the linked OFile. Notice that a OFile should -/// be either opened explicitly, linked to a FILE or linked to a OFile - OFile& link(OFile&); -/// Opens the file using automatic append/backup - OFile& open(const std::string&name); -/// Set the prefix for output. -/// Typically "PLUMED: ". Notice that lines with a prefix cannot -/// be parsed using fields in a IFile. - OFile& setLinePrefix(const std::string&); -/// Set the format for writing double precision fields - OFile& fmtField(const std::string&); -/// Reset the format for writing double precision fields to its default - OFile& fmtField(); -/// Set the value of a double precision field - OFile& printField(const std::string&,double); -/// Set the value of a int field - OFile& printField(const std::string&,int); -/// Set the value of a string field - OFile& printField(const std::string&,const std::string&); -/// - OFile& addConstantField(const std::string&); -/// Used to setup printing of values - OFile& setupPrintValue( Value *val ); -/// Print a value - OFile& printField( Value* val, const double& v ); -/** Close a line. -Typically used as -\verbatim - of.printField("a",a).printField("b",b).printField(); -\endverbatim -*/ - OFile& printField(); -/** -Resets the list of fields. -As it is only possible to add new constant fields (addConstantField()), -this method can be used to clean the field list. -*/ - OFile& clearFields(); -/// Formatted output with explicit format - a la printf - int printf(const char*fmt,...); -/// Formatted output with << operator - template <class T> - friend OFile& operator<<(OFile&,const T &); -}; - - -/** -\ingroup TOOLBOX -Class for input files - -This class provides features similar to those in the standard C "FILE*" type, -but only for sequential input. See OFile for sequential output. - -*/ -class IFile: -/// Class identifying a single field for fielded output -public virtual FileBase{ - class Field: - public FieldBase{ - public: - bool read; - Field(): read(false) {} - }; -/// Low-level read. -/// Note: in parallel, all processes read - size_t llread(char*,size_t); -/// All the defined fields - std::vector<Field> fields; -/// Flag set in the middle of a field reading - bool inMiddleOfField; -/// Set to true if you want to allow fields to be ignored in the read in file - bool ignoreFields; -/// Advance to next field (= read one line) - IFile& advanceField(); -/// Find field index by name - unsigned findField(const std::string&name)const; -public: -/// Constructor - IFile(); -/// Destructor - ~IFile(); -/// Opens the file - IFile& open(const std::string&name); -/// Gets the list of all fields - IFile& scanFieldList(std::vector<std::string>&); -/// Read a double field - IFile& scanField(const std::string&,double&); -/// Read a int field - IFile& scanField(const std::string&,int&); -/// Read a string field - IFile& scanField(const std::string&,std::string&); -/** - Ends a field-formatted line. - -Typically used as -\verbatim - if.scanField("a",a).scanField("b",b).scanField(); -\endverbatim -*/ - IFile& scanField(); -/// Get a full line as a string - IFile& getline(std::string&); -/// Reset end of file - void reset(bool); -/// Check if a field exist - bool FieldExist(const std::string& s); -/// Read in a value - IFile& scanField(Value* val); -/// Allow some of the fields in the input to be ignored - void allowIgnoredFields(); -}; - -/// Write using << syntax -template <class T> -OFile& operator<<(OFile&of,const T &t){ - of.oss<<t; - of.printf("%s",of.oss.str().c_str()); - of.oss.str(""); - return of; -} - - -} +#include "IFile.h" +#include "OFile.h" #endif diff --git a/src/tools/FileBase.cpp b/src/tools/FileBase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9190df2acbc5e834ccc72b12a7749015e9f2088 --- /dev/null +++ b/src/tools/FileBase.cpp @@ -0,0 +1,169 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Copyright (c) 2012 The plumed team + (see the PEOPLE file at the root of the distribution for a list of names) + + See http://www.plumed-code.org for more information. + + This file is part of plumed, version 2.0. + + plumed is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + plumed is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with plumed. If not, see <http://www.gnu.org/licenses/>. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#include "File.h" +#include "PlumedException.h" +#include "core/Action.h" +#include "core/PlumedMain.h" +#include "core/Value.h" +#include "Communicator.h" +#include "Tools.h" +#include <cstdarg> +#include <cstring> + +#include <iostream> +#include <string> + +namespace PLMD{ + +void FileBase::test(){ + PLMD::OFile pof; + pof.open("ciao"); + pof.printf("%s\n","test1"); + pof.setLinePrefix("plumed: "); + pof.printf("%s\n","test2"); + pof.setLinePrefix(""); + pof.addConstantField("x2").printField("x2",67.0); + pof.printField("x1",10.0).printField("x3",20.12345678901234567890).printField(); + pof.printField("x1",10.0).printField("x3",-1e70*20.12345678901234567890).printField(); + pof.printField("x3",10.0).printField("x2",777.0).printField("x1",-1e70*20.12345678901234567890).printField(); + pof.printField("x3",67.0).printField("x1",18.0).printField(); + pof.close(); + + PLMD::IFile pif; + std::string s; + pif.open("ciao"); + pif.getline(s); std::printf("%s\n",s.c_str()); + pif.getline(s); std::printf("%s\n",s.c_str()); + + int x1,x2,x3; + while(pif.scanField("x1",x1).scanField("x3",x2).scanField("x2",x3).scanField()){ + std::cout<<"CHECK "<<x1<<" "<<x2<<" "<<x3<<"\n"; + } + pif.close(); +} + +FileBase& FileBase::link(FILE*fp){ + this->fp=fp; + cloned=true; + return *this; +} + +FileBase& FileBase::flush(){ + fflush(fp); + if(heavyFlush){ + fclose(fp); + fp=std::fopen(const_cast<char*>(path.c_str()),"a"); + } + return *this; +} + +FileBase& FileBase::link(Communicator&comm){ + plumed_massert(!fp,"cannot link an already open file"); + this->comm=&comm; + return *this; +} + +FileBase& FileBase::link(PlumedMain&plumed){ + plumed_massert(!fp,"cannot link an already open file"); + this->plumed=&plumed; + link(plumed.comm); + return *this; +} + +FileBase& FileBase::link(Action&action){ + plumed_massert(!fp,"cannot link an already open file"); + this->action=&action; + link(action.plumed); + return *this; +} + +FileBase& FileBase::open(const std::string& path,const std::string& mode){ + plumed_assert(!cloned); + eof=false; + err=false; + fp=NULL; + if(plumed){ + this->path=path+plumed->getSuffix(); + fp=std::fopen(const_cast<char*>(this->path.c_str()),const_cast<char*>(mode.c_str())); + } + if(!fp){ + this->path=path; + fp=std::fopen(const_cast<char*>(this->path.c_str()),const_cast<char*>(mode.c_str())); + } + if(plumed) plumed->insertFile(*this); + plumed_massert(fp,"file " + path + "cannot be found"); + return *this; +} + + +bool FileBase::FileExist(const std::string& path){ + FILE *ff=NULL; + bool do_exist=false; + if(plumed){ + this->path=path+plumed->getSuffix(); + ff=std::fopen(const_cast<char*>(this->path.c_str()),"r"); + } + if(!ff){ + this->path=path; + ff=std::fopen(const_cast<char*>(this->path.c_str()),"r"); + } + if(ff) {do_exist=true; fclose(ff);} + return do_exist; +} + +bool FileBase::isOpen(){ + bool isopen=false; + if(fp) isopen=true; + return isopen; +} + +void FileBase::close(){ + plumed_assert(!cloned); + eof=false; + err=false; + std::fclose(fp); + fp=NULL; +} + +FileBase::FileBase(): + fp(NULL), + comm(NULL), + plumed(NULL), + action(NULL), + cloned(false), + eof(false), + err(false), + heavyFlush(false) +{ +} + +FileBase::~FileBase() +{ + if(plumed) plumed->eraseFile(*this); + if(!cloned && fp) fclose(fp); +} + +FileBase::operator bool()const{ + return !eof; +} + +} diff --git a/src/tools/FileBase.h b/src/tools/FileBase.h new file mode 100644 index 0000000000000000000000000000000000000000..ad77f71075af464d8c8302367728bb772099d947 --- /dev/null +++ b/src/tools/FileBase.h @@ -0,0 +1,113 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Copyright (c) 2012 The plumed team + (see the PEOPLE file at the root of the distribution for a list of names) + + See http://www.plumed-code.org for more information. + + This file is part of plumed, version 2.0. + + plumed is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + plumed is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with plumed. If not, see <http://www.gnu.org/licenses/>. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#ifndef __PLUMED_tools_FileBase_h +#define __PLUMED_tools_FileBase_h + +#include <string> + +namespace PLMD{ + +class Communicator; +class PlumedMain; +class Action; + +/** +Base class for dealing with files. + +This class just provides things which are common among OFile and IFile +*/ + +class FileBase{ +/// Copy constructor is disabled (private and unimplemented) + FileBase(const FileBase&); +/// Assignment operator is disabled (private and unimplemented) + FileBase& operator=(const FileBase&); +protected: +/// Internal tool. +/// Base for IFile::Field and OFile::Field + class FieldBase{ +// everything is public to simplify usage + public: + std::string name; + std::string value; + bool constant; + FieldBase(): constant(false){} + }; + +/// file pointer + FILE* fp; +/// communicator. NULL if not set + Communicator* comm; +/// pointer to main plumed object. NULL if not linked + PlumedMain* plumed; +/// pointer to corresponding action. NULL if not linked + Action* action; +/// Control closing on destructor. +/// If true, file will not be closed in destructor + bool cloned; +/// Private constructor. +/// In this manner one cannot instantiate a FileBase object + FileBase(); +/// Set to true when end of file is encountered + bool eof; +/// Set to true when error is encountered + bool err; +/// path of the opened file + std::string path; +/// Set to true if you want flush to be heavy (close/reopen) + bool heavyFlush; +public: +/// Link to an already open filed + FileBase& link(FILE*); +/// Link to a PlumedMain object +/// Automatically links also the corresponding Communicator. + FileBase& link(PlumedMain&); +/// Link to a Communicator object + FileBase& link(Communicator&); +/// Link to an Action object. +/// Automatically links also the corresponding PlumedMain and Communicator. + FileBase& link(Action&); +/// Flushes the file to disk + FileBase& flush(); +/// Closes the file +/// Should be used only for explicitely opened files. + void close(); +/// Virtual destructor (allows inheritance) + virtual ~FileBase(); +/// Runs a small testcase + static void test(); +/// Check for error/eof. + operator bool () const; +/// Set heavyFlush flag + void setHeavyFlush(){ heavyFlush=true;}; +/// Opens the file (without auto-backup) + FileBase& open(const std::string&name,const std::string& mode); +/// Check if the file exists + bool FileExist(const std::string& path); +/// Check if a file is open + bool isOpen(); +}; + + +} + +#endif diff --git a/src/tools/IFile.cpp b/src/tools/IFile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2032cb0a4f57068916b98f5d6c1c04767172a664 --- /dev/null +++ b/src/tools/IFile.cpp @@ -0,0 +1,203 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Copyright (c) 2012 The plumed team + (see the PEOPLE file at the root of the distribution for a list of names) + + See http://www.plumed-code.org for more information. + + This file is part of plumed, version 2.0. + + plumed is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + plumed is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with plumed. If not, see <http://www.gnu.org/licenses/>. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#include "File.h" +#include "PlumedException.h" +#include "core/Action.h" +#include "core/PlumedMain.h" +#include "core/Value.h" +#include "Communicator.h" +#include "Tools.h" +#include <cstdarg> +#include <cstring> + +#include <iostream> +#include <string> + +namespace PLMD{ + +size_t IFile::llread(char*ptr,size_t s){ + plumed_assert(fp); + size_t r; + r=fread(ptr,1,s,fp); + if(feof(fp)) eof=true; + if(ferror(fp)) err=true; + return r; +} + +IFile& IFile::advanceField(){ + plumed_assert(!inMiddleOfField); + std::string line; + bool done=false; + while(!done){ + getline(line); + if(!*this){return *this;} + std::vector<std::string> words=Tools::getWords(line); + if(words.size()>=2 && words[0]=="#!" && words[1]=="FIELDS"){ + fields.clear(); + for(unsigned i=2;i<words.size();i++){ + Field field; + field.name=words[i]; + fields.push_back(field); + } + } else if(words.size()==4 && words[0]=="#!" && words[1]=="SET"){ + Field field; + field.name=words[2]; + field.value=words[3]; + field.constant=true; + fields.push_back(field); + } else { + unsigned nf=0; + for(unsigned i=0;i<fields.size();i++) if(!fields[i].constant) nf++; + Tools::trimComments(line); + words=Tools::getWords(line); + if( words.size()==nf ){ + unsigned j=0; + for(unsigned i=0;i<fields.size();i++){ + if(fields[i].constant) continue; + fields[i].value=words[j]; + fields[i].read=false; + j++; + } + done=true; + } else if( words.size()!=0 ) { + plumed_merror("mismatch between number of fields in file and expected number"); + } + } + } + inMiddleOfField=true; + return *this; +} + +IFile& IFile::open(const std::string&name){ + FileBase::open(name,"r"); + return *this; +} + +IFile& IFile::scanFieldList(std::vector<std::string>&s){ + if(!inMiddleOfField) advanceField(); + if(!*this) return *this; + s.clear(); + for(unsigned i=0;i<fields.size();i++) + s.push_back(fields[i].name); + return *this; +} + +bool IFile::FieldExist(const std::string& s){ + std::vector<std::string> slist; + scanFieldList(slist); + int mycount = (int) std::count(slist.begin(), slist.end(), s); + if(mycount>0) return true; + else return false; +} + +IFile& IFile::scanField(const std::string&name,std::string&str){ + if(!inMiddleOfField) advanceField(); + if(!*this) return *this; + unsigned i=findField(name); + str=fields[i].value; + fields[i].read=true; + return *this; +} + +IFile& IFile::scanField(const std::string&name,double &x){ + std::string str; + scanField(name,str); + if(*this) Tools::convert(str,x); + return *this; +} + +IFile& IFile::scanField(const std::string&name,int &x){ + std::string str; + scanField(name,str); + if(*this) Tools::convert(str,x); + return *this; +} + +IFile& IFile::scanField(Value* val){ + double ff; scanField( val->getName(), ff ); + val->set( ff ); + if( FieldExist("min_" + val->getName() ) ){ + std::string min, max; + scanField("min_" + val->getName(), min ); + scanField("max_" + val->getName(), max ); + val->setDomain( min, max ); + } else { + val->setNotPeriodic(); + } + return *this; +} + +IFile& IFile::scanField(){ + if(!ignoreFields){ + for(unsigned i=0;i<fields.size();i++){ + plumed_assert(fields[i].read); + } + } + inMiddleOfField=false; + return *this; +} + +IFile::IFile(): + inMiddleOfField(false), + ignoreFields(false) +{ +} + +IFile::~IFile(){ + plumed_assert(!inMiddleOfField); +} + +IFile& IFile::getline(std::string &str){ + char tmp; + str=""; + fpos_t pos; + fgetpos(fp,&pos); + while(llread(&tmp,1)==1 && tmp && tmp!='\n' && !eof && !err){ + str+=tmp; + } + if(tmp!='\n' || err){ + eof = true; + str=""; + fsetpos(fp,&pos); + } + return *this; +} + +unsigned IFile::findField(const std::string&name)const{ + unsigned i; + for(i=0;i<fields.size();i++) if(fields[i].name==name) break; + if(i>=fields.size()) plumed_merror(name); + return i; +} + +void IFile::reset(bool reset){ + eof = reset; + err = reset; + if(!reset) clearerr(fp); + return; +} + +void IFile::allowIgnoredFields(){ + ignoreFields=true; +} + +} diff --git a/src/tools/IFile.h b/src/tools/IFile.h new file mode 100644 index 0000000000000000000000000000000000000000..1d5682e78eeaf0056bec8ac4d5cab064cf9f0ff8 --- /dev/null +++ b/src/tools/IFile.h @@ -0,0 +1,100 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Copyright (c) 2012 The plumed team + (see the PEOPLE file at the root of the distribution for a list of names) + + See http://www.plumed-code.org for more information. + + This file is part of plumed, version 2.0. + + plumed is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + plumed is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with plumed. If not, see <http://www.gnu.org/licenses/>. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#ifndef __PLUMED_tools_IFile_h +#define __PLUMED_tools_IFile_h + +#include "FileBase.h" +#include <vector> + +namespace PLMD{ + +class Value; + +/** +\ingroup TOOLBOX +Class for input files + +This class provides features similar to those in the standard C "FILE*" type, +but only for sequential input. See OFile for sequential output. + +*/ +class IFile: +/// Class identifying a single field for fielded output +public virtual FileBase{ + class Field: + public FieldBase{ + public: + bool read; + Field(): read(false) {} + }; +/// Low-level read. +/// Note: in parallel, all processes read + size_t llread(char*,size_t); +/// All the defined fields + std::vector<Field> fields; +/// Flag set in the middle of a field reading + bool inMiddleOfField; +/// Set to true if you want to allow fields to be ignored in the read in file + bool ignoreFields; +/// Advance to next field (= read one line) + IFile& advanceField(); +/// Find field index by name + unsigned findField(const std::string&name)const; +public: +/// Constructor + IFile(); +/// Destructor + ~IFile(); +/// Opens the file + IFile& open(const std::string&name); +/// Gets the list of all fields + IFile& scanFieldList(std::vector<std::string>&); +/// Read a double field + IFile& scanField(const std::string&,double&); +/// Read a int field + IFile& scanField(const std::string&,int&); +/// Read a string field + IFile& scanField(const std::string&,std::string&); +/** + Ends a field-formatted line. + +Typically used as +\verbatim + if.scanField("a",a).scanField("b",b).scanField(); +\endverbatim +*/ + IFile& scanField(); +/// Get a full line as a string + IFile& getline(std::string&); +/// Reset end of file + void reset(bool); +/// Check if a field exist + bool FieldExist(const std::string& s); +/// Read in a value + IFile& scanField(Value* val); +/// Allow some of the fields in the input to be ignored + void allowIgnoredFields(); +}; + +} + +#endif diff --git a/src/tools/OFile.cpp b/src/tools/OFile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0277288cdc327b823bb5b91ef8f1642e68912d76 --- /dev/null +++ b/src/tools/OFile.cpp @@ -0,0 +1,254 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Copyright (c) 2012 The plumed team + (see the PEOPLE file at the root of the distribution for a list of names) + + See http://www.plumed-code.org for more information. + + This file is part of plumed, version 2.0. + + plumed is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + plumed is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with plumed. If not, see <http://www.gnu.org/licenses/>. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#include "File.h" +#include "PlumedException.h" +#include "core/Action.h" +#include "core/PlumedMain.h" +#include "core/Value.h" +#include "Communicator.h" +#include "Tools.h" +#include <cstdarg> +#include <cstring> + +#include <iostream> +#include <string> + +namespace PLMD{ + +size_t OFile::llwrite(const char*ptr,size_t s){ + size_t r; + if(linked) return linked->llwrite(ptr,s); + if(! (comm && comm->Get_rank()>0)){ + if(!fp) plumed_merror("writing on uninitilized File"); + r=fwrite(ptr,1,s,fp); + } + if(comm) comm->Bcast(&r,1,0); + return r; +} + +OFile::OFile(): + linked(NULL), + fieldChanged(false) +{ + fmtField(); + buflen=1; + buffer=new char[buflen]; +// these are set to zero to avoid valgrind errors + for(unsigned i=0;i<buflen;++i) buffer[i]=0; + buffer_string=new char [1000]; +// these are set to zero to avoid valgrind errors + for(unsigned i=0;i<1000;++i) buffer_string[i]=0; +} + +OFile::~OFile(){ + delete [] buffer_string; + delete [] buffer; +} + +OFile& OFile::link(OFile&l){ + fp=NULL; + linked=&l; + return *this; +} + +OFile& OFile::setLinePrefix(const std::string&l){ + linePrefix=l; + return *this; +} + +int OFile::printf(const char*fmt,...){ + size_t pointer=strlen(buffer); + va_list arg; + va_start(arg, fmt); + int r=std::vsnprintf(&buffer[pointer],buflen-pointer,fmt,arg); + va_end(arg); + if(r>=buflen-pointer){ + int newlen=buflen; + while(newlen<=r+pointer) newlen*=2; + char* newbuf=new char [newlen]; + memmove(newbuf,buffer,buflen); + for(int k=buflen;k<newlen;k++) newbuf[k]=0; + delete [] buffer; + buffer=newbuf; + buflen=newlen; + va_list arg; + va_start(arg, fmt); + r=std::vsnprintf(&buffer[pointer],buflen-pointer,fmt,arg); + va_end(arg); + } + plumed_massert(r>-1 && r<buflen-pointer,"error using fmt string " + std::string(fmt)); + +// Line is buffered until newline, then written with a PLUMED: prefix + char*p1=buffer; + char*p2; + while((p2=strchr(p1,'\n'))){ + *p2='\0'; + if(linePrefix.length()>0) llwrite(linePrefix.c_str(),linePrefix.length()); + llwrite(p1,std::strlen(p1)); + llwrite("\n",1); + p1=p2+1; + }; + memmove(buffer,p1,strlen(p1)+1); + return r; +} + +OFile& OFile::addConstantField(const std::string&name){ + Field f; + f.name=name; + const_fields.push_back(f); + return *this; +} + + +OFile& OFile::clearFields(){ + fields.clear(); + const_fields.clear(); + previous_fields.clear(); + return *this; +} + +OFile& OFile::fmtField(const std::string&fmt){ + this->fieldFmt=fmt; + return *this; +} + +OFile& OFile::fmtField(){ + this->fieldFmt="%23.16lg"; + return *this; +} + +OFile& OFile::printField(const std::string&name,double v){ + sprintf(buffer_string,fieldFmt.c_str(),v); + printField(name,buffer_string); + return *this; +} + +OFile& OFile::printField(const std::string&name,int v){ + sprintf(buffer_string," %d",v); + printField(name,buffer_string); + return *this; +} + +OFile& OFile::printField(const std::string&name,const std::string & v){ + unsigned i; + for(i=0;i<const_fields.size();i++) if(const_fields[i].name==name) break; + if(i>=const_fields.size()){ + Field field; + field.name=name; + field.value=v; + fields.push_back(field); + } else { + if(const_fields[i].value!=v) fieldChanged=true; + const_fields[i].value=v; + } + return *this; +} + +OFile& OFile::setupPrintValue( Value *val ){ + if( val->isPeriodic() ){ + addConstantField("min_" + val->getName() ); + addConstantField("max_" + val->getName() ); + } + return *this; +} + +OFile& OFile::printField( Value* val, const double& v ){ + printField( val->getName(), v ); + if( val->isPeriodic() ){ + std::string min, max; val->getDomain( min, max ); + printField( "min_" + val->getName(), min ); + printField("max_" + val->getName(), max ); + } + return *this; +} + +OFile& OFile::printField(){ + bool reprint=false; + if(fieldChanged || fields.size()!=previous_fields.size()){ + reprint=true; + } else for(unsigned i=0;i<fields.size();i++){ + if( previous_fields[i].name!=fields[i].name || + (fields[i].constant && fields[i].value!=previous_fields[i].value) ){ + reprint=true; + break; + } + } + if(reprint){ + printf("#! FIELDS"); + for(unsigned i=0;i<fields.size();i++) printf(" %s",fields[i].name.c_str()); + printf("\n"); + for(unsigned i=0;i<const_fields.size();i++){ + printf("#! SET %s %s",const_fields[i].name.c_str(),const_fields[i].value.c_str()); + printf("\n"); + } + } + for(unsigned i=0;i<fields.size();i++) printf("%s",fields[i].value.c_str()); + printf("\n"); + previous_fields=fields; + fields.clear(); + fieldChanged=false; + return *this; +} + +OFile& OFile::open(const std::string&path){ + plumed_assert(!cloned); + eof=false; + err=false; + fp=NULL; + this->path=path; + if(plumed){ + this->path+=plumed->getSuffix(); + } + if(plumed && plumed->getRestart()){ + fp=std::fopen(const_cast<char*>(this->path.c_str()),"a"); + } else { + if(!comm || comm->Get_rank()==0){ + FILE* ff=std::fopen(const_cast<char*>(this->path.c_str()),"r"); + FILE* fff=NULL; + if(ff){ + std::string backup; + size_t found=this->path.find_last_of("/\\"); + std::string directory=this->path.substr(0,found+1); + std::string file=this->path.substr(found+1); + for(int i=0;;i++){ + std::string num; + Tools::convert(i,num); + backup=directory+"bck."+num+"."+file; + fff=std::fopen(backup.c_str(),"r"); + if(!fff) break; + } + int check=rename(this->path.c_str(),backup.c_str()); + plumed_massert(check==0,"renaming "+this->path+" into "+backup+" failed for some reason"); + } + if(ff) std::fclose(ff); + if(fff) std::fclose(fff); + } + comm->Barrier(); + fp=std::fopen(const_cast<char*>(this->path.c_str()),"w"); + } + if(plumed) plumed->insertFile(*this); + return *this; +} + +} + + diff --git a/src/tools/OFile.h b/src/tools/OFile.h new file mode 100644 index 0000000000000000000000000000000000000000..b7df1c0bedbe7ecb0facae80ca19c07f68537a22 --- /dev/null +++ b/src/tools/OFile.h @@ -0,0 +1,185 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Copyright (c) 2012 The plumed team + (see the PEOPLE file at the root of the distribution for a list of names) + + See http://www.plumed-code.org for more information. + + This file is part of plumed, version 2.0. + + plumed is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + plumed is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with plumed. If not, see <http://www.gnu.org/licenses/>. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ +#ifndef __PLUMED_tools_OFile_h +#define __PLUMED_tools_OFile_h + +#include "FileBase.h" +#include <vector> +#include <sstream> + +namespace PLMD{ + +/** +\ingroup TOOLBOX +Class for output files + +This class provides features similar to those in the standard C "FILE*" type, +but only for sequential output. See IFile for sequential input. + +See the example here for a possible use: +\verbatim +#include "File.h" + +int main(){ + PLMD::OFile pof; + pof.open("ciao","w"); + pof.printf("%s\n","test1"); + pof.setLinePrefix("plumed: "); + pof.printf("%s\n","test2"); + pof.setLinePrefix(""); + pof.addConstantField("x2").printField("x2",67.0); + pof.printField("x1",10.0).printField("x3",20.12345678901234567890).printField(); + pof.printField("x1",10.0).printField("x3",-1e70*20.12345678901234567890).printField(); + pof.printField("x3",10.0).printField("x2",777.0).printField("x1",-1e70*20.12345678901234567890).printField(); + pof.printField("x3",67.0).printField("x1",18.0).printField(); + pof.close(); + return 0; +} +\endverbatim + +This program is expected to produce a file "ciao" which reads +\verbatim +test1 +plumed: test2 +#! FIELDS x1 x3 +#! SET x2 67 + 10 20.12345678901234 + 10 -2.012345678901235e+71 +#! FIELDS x1 x3 +#! SET x2 777 + -2.012345678901235e+71 10 + 18 67 +\endverbatim + +Notes +- "x2" is declared as "constant", which means that it is written using the "SET" +keyword. Thus, everytime it is modified, all the headers are repeated in the output file. +- printField() without arguments is used as a "newline". +- 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) + +*/ + +class OFile: +public virtual FileBase{ +/// Pointer to a linked OFile. +/// see link(OFile&) + OFile* linked; +/// Internal buffer for printf + char* buffer_string; +/// Internal buffer (generic use) + char* buffer; +/// Internal buffer length + int buflen; +/// Class identifying a single field for fielded output + class Field: + public FieldBase{ + }; +/// Low-level write + size_t llwrite(const char*,size_t); +/// True if fields has changed. +/// This could be due to a change in the list of fields or a reset +/// of a nominally constant field + bool fieldChanged; +/// Format for fields writing + std::string fieldFmt; +/// All the previously defined variable fields + std::vector<Field> previous_fields; +/// All the defined variable fields + std::vector<Field> fields; +/// All the defined constant fields + std::vector<Field> const_fields; +/// Prefix for line (e.g. "PLUMED: ") + std::string linePrefix; +/// Temporary ostringstream for << output + std::ostringstream oss; +/// Find field index given name + unsigned findField(const std::string&name)const; +public: +/// Constructor + OFile(); +/// Destructor + ~OFile(); +/// Allows overloading of link + using FileBase::link; +/// Allows overloading of open + using FileBase::open; +/// Allows linking this OFile to another one. +/// In this way, everything written to this OFile will be immediately +/// written on the linked OFile. Notice that a OFile should +/// be either opened explicitly, linked to a FILE or linked to a OFile + OFile& link(OFile&); +/// Opens the file using automatic append/backup + OFile& open(const std::string&name); +/// Set the prefix for output. +/// Typically "PLUMED: ". Notice that lines with a prefix cannot +/// be parsed using fields in a IFile. + OFile& setLinePrefix(const std::string&); +/// Set the format for writing double precision fields + OFile& fmtField(const std::string&); +/// Reset the format for writing double precision fields to its default + OFile& fmtField(); +/// Set the value of a double precision field + OFile& printField(const std::string&,double); +/// Set the value of a int field + OFile& printField(const std::string&,int); +/// Set the value of a string field + OFile& printField(const std::string&,const std::string&); +/// + OFile& addConstantField(const std::string&); +/// Used to setup printing of values + OFile& setupPrintValue( Value *val ); +/// Print a value + OFile& printField( Value* val, const double& v ); +/** Close a line. +Typically used as +\verbatim + of.printField("a",a).printField("b",b).printField(); +\endverbatim +*/ + OFile& printField(); +/** +Resets the list of fields. +As it is only possible to add new constant fields (addConstantField()), +this method can be used to clean the field list. +*/ + OFile& clearFields(); +/// Formatted output with explicit format - a la printf + int printf(const char*fmt,...); +/// Formatted output with << operator + template <class T> + friend OFile& operator<<(OFile&,const T &); +}; + +/// Write using << syntax +template <class T> +OFile& operator<<(OFile&of,const T &t){ + of.oss<<t; + of.printf("%s",of.oss.str().c_str()); + of.oss.str(""); + return of; +} + + +} + +#endif