diff --git a/regtest/rt17/COLVAR.reference b/regtest/rt17/COLVAR.reference index ed31025d8d38411a7c1f932d9e7d1569c9f28bae..d309dca201be6e93d48619bd246e25c4c6a50cf8 100644 --- a/regtest/rt17/COLVAR.reference +++ b/regtest/rt17/COLVAR.reference @@ -1,6 +1,8 @@ -#! FIELDS time d1.x d2 d3 +#! FIELDS time d1.x 0.000000 8.126 +#! FIELDS time d2 0.500000 27.263 1.000000 27.786 +#! FIELDS time d3 1.500000 21.565 2.000000 21.329 diff --git a/src/GenericPrint.cpp b/src/GenericPrint.cpp index aabee48899ebe79e1a54f0617f977c6b0ddecb7e..84fed22910803e1b9737f7d6868a37dc55829514 100644 --- a/src/GenericPrint.cpp +++ b/src/GenericPrint.cpp @@ -56,8 +56,10 @@ public ActionPilot, public ActionWithArguments { string file; - FILE* fp; + PlumedOFile ofile; string fmt; +// small internal utility + void updateFields(); ///////////////////////////////////////// // these are crazy things just for debug: // they allow to change regularly the @@ -94,27 +96,22 @@ GenericPrint::GenericPrint(const ActionOptions&ao): Action(ao), ActionPilot(ao), ActionWithArguments(ao), -fp(NULL), fmt("%f"), rotate(0) { parse("FILE",file); if(file.length()>0){ - if(comm.Get_rank()==0){ - fp=fopen(file.c_str(),"a"); - log.printf(" on file %s\n",file.c_str()); - fprintf(fp,"#! FIELDS time"); - for(unsigned i=0;i<getNumberOfArguments();i++){ - fprintf(fp," %s",getPntrToArgument(i)->getName().c_str()); - }; - fprintf(fp,"\n"); - } + ofile.open(file.c_str(),"wa"); + log.printf(" on file %s\n",file.c_str()); } else { log.printf(" on plumed log file\n"); + ofile.link(log); } + updateFields(); parse("FMT",fmt); fmt=" "+fmt; log.printf(" with format %s\n",fmt.c_str()); + ofile.fmtFields(fmt); ///////////////////////////////////////// // these are crazy things just for debug: // they allow to change regularly the @@ -126,6 +123,7 @@ rotate(0) vector<Value*> a(1,rotateArguments[0]); requestArguments(vector<Value*>(1,rotateArguments[0])); rotateLast=0; + updateFields(); } ///////////////////////////////////////// checkRead(); @@ -143,30 +141,30 @@ void GenericPrint::prepare(){ rotateLast++; rotateLast%=rotateArguments.size(); requestArguments(vector<Value*>(1,rotateArguments[rotateLast])); + updateFields(); } } ///////////////////////////////////////// } void GenericPrint::update(){ - if(comm.Get_rank()!=0)return; - if(!fp){ - log.printf("PRINT:"); - for(unsigned i=0;i<getNumberOfArguments();i++){ - log.printf(fmt.c_str(),getArgument(i)); - }; - log.printf("\n"); - } else { - fprintf(fp," %f",getTime()); + ofile.printField("time",getTime()); for(unsigned i=0;i<getNumberOfArguments();i++){ - fprintf(fp,fmt.c_str(),getArgument(i)); + ofile.printField(getPntrToArgument(i)->getName(),getArgument(i)); }; - fprintf(fp,"\n"); - } + ofile.printField(); } GenericPrint::~GenericPrint(){ - if(fp) fclose(fp); +} + +void GenericPrint::updateFields(){ + ofile.clearFields(); + ofile.addField("time"); + ofile.fmtField("time"," %f"); + for(unsigned i=0;i<getNumberOfArguments();i++){ + ofile.addField(getPntrToArgument(i)->getName()); + } } } diff --git a/src/Log.cpp b/src/Log.cpp index 40354c5eb0713709d79561b18412f067c5f89649..17f88dce8cdb3c8cfeb79487505f78d34993ed1c 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -19,62 +19,5 @@ 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 <cstdarg> -#include <cstring> #include "Log.h" -#include "PlumedCommunicator.h" -#include "PlumedException.h" - -using namespace std; -using namespace PLMD; - -int Log::printf(const char*fmt,...){ - if(comm.Get_rank()>0)return 0; - int pointer=strlen(buffer); - va_list arg; - va_start(arg, fmt); - int r=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'; - fprintf(fp,"PLUMED: %s\n",p1); - p1=p2+1; - }; - memmove(buffer,p1,strlen(p1)+1); - return r; -} - -Log::Log(PlumedCommunicator &comm): - fp(stdout), - toBeClosed(false), - comm(comm){ - buffer=new char[buflen]; - for(int i=0;i<buflen;i++) buffer[i]='\0'; -} - -Log::~Log(){ - if(comm.Get_rank()>0)return; - if(!fp && toBeClosed)fclose(fp); - delete [] buffer; -} - -void Log::setFile(string str){ - if(comm.Get_rank()>0)return; - fp=fopen(str.c_str(),"w"); - toBeClosed=true; -} - -void Log::set(FILE*f){ - if(comm.Get_rank()>0)return; - fp=f; -} - -void Log::flush(){ - fflush(fp); -} diff --git a/src/Log.h b/src/Log.h index a2a155c93a87ca993b691e55fdfd8f7c1e75921a..b112c01649e3a29f2876cfe7db754fd50642981c 100644 --- a/src/Log.h +++ b/src/Log.h @@ -26,6 +26,8 @@ #include <string> #include <sstream> +#include "PlumedFile.h" + namespace PLMD{ class PlumedCommunicator; @@ -36,42 +38,11 @@ class PlumedCommunicator; /// also to write with a << operator. Moreover, it can prefix /// lines with the "PLUMED:" prefix, useful to grep out plumed /// log from output -class Log +class Log : + public PlumedOFile { -/// Actual FILE stream - FILE* fp; -/// Flag to know if, when Log is destructed, file should be closed - bool toBeClosed; - static const int buflen=10000; - std::ostringstream oss; - char* buffer; -/// Communicator (to write only with the first processor) - PlumedCommunicator& comm; -public: -/// Initialize on a given communicator - Log(PlumedCommunicator&); - ~Log(); -/// Set a file with a specific name - void setFile(std::string str); -/// Link to an already open FILE stream - void set(FILE*f); - void flush(); -/// Standard printf-like function - int printf(const char*fmt,...); - template <class T> - friend Log& operator<<(Log&,const T &); }; -/// Write using << syntax -template <class T> -Log& operator<<(Log&log,const T &t){ - log.oss<<t; - log.printf("%s",log.oss.str().c_str()); - log.oss.str(""); - return log; -} - - } #endif diff --git a/src/PlumedFile.cpp b/src/PlumedFile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7088a2a928b926f7306362f19dfd6fcacf83249 --- /dev/null +++ b/src/PlumedFile.cpp @@ -0,0 +1,276 @@ +#include "PlumedFile.h" +#include "PlumedException.h" +#include "Action.h" +#include "PlumedMain.h" +#include "PlumedCommunicator.h" +#include <cstdarg> +#include <cstring> + +using namespace PLMD; + +void PlumedFileBase::test(){ + PLMD::PlumedOFile pof; + pof.open("ciao","w"); + pof.printf("%s\n","test1"); + pof.setLinePrefix("plumed: "); + pof.printf("%s\n","test2"); + pof.addField("x1"); + pof.addField("x2",67.0); + pof.addField("x3"); + 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).printField("x1",-1e70*20.12345678901234567890).printField(); + pof.printField("x3",67.0).printField("x1",18.0).printField(); + pof.close(); + + PLMD::PlumedIFile pif; + std::string s; + pif.open("ciao","r"); + pif.getline(s); std::printf("%s\n",s.c_str()); + pif.getline(s); std::printf("%s\n",s.c_str()); + pif.close(); +} + +size_t PlumedOFile::llwrite(const char*ptr,size_t s){ + size_t r; + if(fp){ + if(! (comm && comm->Get_rank()>0)){ + r=fwrite(ptr,1,s,fp); + } + if(comm) comm->Bcast(&r,1,0); + } else if(linked){ + (*linked)<<ptr; + r=strlen(ptr); + } else plumed_merror("writing on uninitilized PlumedFile"); + return r; +} + +size_t PlumedIFile::llread(char*ptr,size_t s){ + plumed_assert(fp); + size_t r; + if(! (comm && comm->Get_rank()>0)){ + r=fread(ptr,1,s,fp); + } + if(comm) comm->Bcast(&r,1,0); + if(comm) comm->Bcast(ptr,r,0); + return r; +} + +PlumedFileBase& PlumedFileBase::link(FILE*fp){ + this->fp=fp; + cloned=true; + return *this; +} + +PlumedFileBase& PlumedFileBase::flush(){ + fflush(fp); + return *this; +} + +PlumedFileBase& PlumedFileBase::link(PlumedCommunicator&comm){ + this->comm=&comm; + return *this; +} + +PlumedFileBase& PlumedFileBase::link(PlumedMain&plumed){ + this->plumed=&plumed; + link(plumed.comm); + return *this; +} + +PlumedFileBase& PlumedFileBase::link(Action&action){ + this->action=&action; + link(action.plumed); + return *this; +} + +PlumedFileBase& PlumedFileBase::open(const std::string& path,const std::string& mode){ + plumed_assert(!cloned); + fp=NULL; + if(plumed){ + const std::string pathsuf=path+plumed->getSuffix(); + fp=std::fopen(const_cast<char*>(pathsuf.c_str()),const_cast<char*>(mode.c_str())); + } + if(!fp) fp=std::fopen(const_cast<char*>(path.c_str()),const_cast<char*>(mode.c_str())); + plumed_massert(fp,"file " + path + "cannot be found"); + return *this; +} + +void PlumedFileBase::close(){ + plumed_assert(!cloned); + std::fclose(fp); + fp=NULL; +} + +PlumedFileBase::PlumedFileBase(): + fp(NULL), + comm(NULL), + plumed(NULL), + action(NULL), + cloned(false) +{ +} + +PlumedFileBase::~PlumedFileBase() +{ + if(!cloned) fclose(fp); +} + +PlumedOFile::PlumedOFile(): + linked(NULL), + fieldChanged(false), + fieldFmt("%22.16lg") +{ + buffer=new char[buflen]; +} + +PlumedOFile::~PlumedOFile(){ + delete [] buffer; +} + +PlumedOFile& PlumedOFile::link(PlumedOFile&l){ + fp=NULL; + linked=&l; + return *this; +} + +PlumedOFile& PlumedOFile::setLinePrefix(const std::string&l){ + linePrefix=l; + return *this; +} + +int PlumedOFile::printf(const char*fmt,...){ + int pointer=strlen(buffer); + va_list arg; + va_start(arg, fmt); + int 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; +} + +PlumedOFile& PlumedOFile::addField(const std::string&name){ + Field f; + f.name=name; + fields.push_back(f); + fieldChanged=true; + return *this; +} + +PlumedOFile& PlumedOFile::addField(const std::string&name,double v){ + Field f; + f.name=name; + f.value=v; + f.constant=true; + f.set=true; + fields.push_back(f); + fieldChanged=true; + return *this; +} + +PlumedOFile& PlumedOFile::clearFields(){ + fields.clear(); + fieldChanged=true; + return *this; +} + +PlumedOFile& PlumedOFile::fmtFields(const std::string&fmt){ + fieldFmt=fmt; + return *this; +} + +PlumedOFile& PlumedOFile::fmtField(const std::string&name,const std::string&fmt){ + unsigned i; + for(i=0;i<fields.size();i++) if(fields[i].name==name) break; + plumed_assert(i<fields.size()); + fields[i].fmt=fmt; + return *this; +} + +PlumedOFile& PlumedOFile::printField(const std::string&name,double v){ + unsigned i; + for(i=0;i<fields.size();i++) if(fields[i].name==name) break; + plumed_assert(i<fields.size()); + if(fields[i].constant) fieldChanged=true; + fields[i].value=v; + fields[i].set=true; + return *this; +} + +PlumedOFile& PlumedOFile::printField(){ + if(fieldChanged){ + printf("#! FIELDS"); + for(unsigned i=0;i<fields.size();i++){ + printf(" %s",fields[i].name.c_str()); + } + printf("\n"); + for(unsigned i=0;i<fields.size();i++) + if(fields[i].constant){ + std::string fmt; + if(fields[i].fmt.length()>0) fmt=fields[i].fmt; + else fmt=fieldFmt; + printf("#! SET %s ",fields[i].name.c_str()); + printf(fmt.c_str(),fields[i].value); + printf("\n"); + } + } + fieldChanged=false; + for(unsigned i=0;i<fields.size();i++){ + plumed_assert(fields[i].set); + if(!fields[i].constant){ + std::string fmt; + if(fields[i].fmt.length()>0) fmt=fields[i].fmt; + else fmt=fieldFmt; +// printf(" "); + printf(fmt.c_str(),fields[i].value); + fields[i].set=false; + } + } + printf("\n"); + return *this; +} + +PlumedIFile& PlumedIFile::scanFieldList(std::vector<std::string>&s){ + s=fields; + return *this; +} + +PlumedIFile& PlumedIFile::scanField(const std::string&,double&){ + plumed_error(); + return *this; +} + +PlumedIFile& PlumedIFile::scanField(){ + plumed_error(); + return *this; +} + +PlumedIFile::PlumedIFile(){ +} + +PlumedIFile::~PlumedIFile(){ +} + +PlumedIFile& PlumedIFile::getline(std::string &str){ + char tmp; + str=""; + while(llread(&tmp,1)==1 && tmp && tmp!='\n'){ + str+=tmp; + } + return *this; +} + + + diff --git a/src/PlumedFile.h b/src/PlumedFile.h new file mode 100644 index 0000000000000000000000000000000000000000..c6ebf120fbf7b7fc8c256deb96dd5037d6c5c2ed --- /dev/null +++ b/src/PlumedFile.h @@ -0,0 +1,116 @@ +#ifndef __PLUMED_PlumedFile_h +#define __PLUMED_PlumedFile_h + +#include <cstdio> +#include <vector> +#include <string> +#include <sstream> + +namespace PLMD{ + +class PlumedCommunicator; +class PlumedMain; +class Action; + +class PlumedFileBase{ +protected: + class FieldBase{ + public: + std::string name; + double value; + bool constant; + FieldBase(): value(0.0),constant(false){} + }; + + static const int buflen=10000; + FILE* fp; + PlumedCommunicator* comm; + PlumedMain* plumed; + Action* action; + bool cloned; + PlumedFileBase(); +public: + PlumedFileBase& link(FILE*); + PlumedFileBase& link(PlumedMain&); + PlumedFileBase& link(PlumedCommunicator&); + PlumedFileBase& link(Action&); + PlumedFileBase& flush(); + PlumedFileBase& open(const std::string&name,const std::string& mode); + void close(); + virtual ~PlumedFileBase(); + static void test(); +}; + +class PlumedOFile: +public virtual PlumedFileBase{ + PlumedOFile* linked; + char* buffer; + class Field: + public FieldBase{ + public: + bool set; + std::string fmt; + Field(): set(false) {} + }; + size_t llwrite(const char*,size_t); + bool fieldChanged; + std::string fieldFmt; + std::vector<Field> fields; + std::string linePrefix; + std::ostringstream oss; +public: + PlumedOFile(); + ~PlumedOFile(); + using PlumedFileBase::link; + PlumedOFile& link(PlumedOFile&); + PlumedOFile& setLinePrefix(const std::string&); + PlumedOFile& addField(const std::string&); + PlumedOFile& addField(const std::string&,double); + PlumedOFile& clearFields(); + PlumedOFile& fmtFields(const std::string&); + PlumedOFile& fmtField(const std::string&,const std::string&); + PlumedOFile& printField(const std::string&,double); + PlumedOFile& printField(); + int printf(const char*fmt,...); + template <class T> + friend PlumedOFile& operator<<(PlumedOFile&,const T &); +}; + +class PlumedIFile: +public virtual PlumedFileBase{ + class Field: + public FieldBase{ + public: + bool read; + Field(): read(false) {} + }; + size_t llread(char*,size_t); + std::vector<std::string> fields; +public: + PlumedIFile(); + ~PlumedIFile(); + PlumedIFile& scanFieldList(std::vector<std::string>&); + PlumedIFile& scanField(const std::string&,double&); + PlumedIFile& scanField(); + PlumedIFile& getline(std::string&); +}; + +class PlumedFile: +public PlumedOFile, +public PlumedIFile +{ +}; + +/// Write using << syntax +template <class T> +PlumedOFile& operator<<(PlumedOFile&of,const T &t){ + of.oss<<t; + of.printf("%s",of.oss.str().c_str()); + of.oss.str(""); + return of; +} + + +} + +#endif diff --git a/src/PlumedMain.cpp b/src/PlumedMain.cpp index a55e712aee636e72331525aa38561a9b3454739f..29ec7d53a42be9ba59714269a6e21f3f424b06a8 100644 --- a/src/PlumedMain.cpp +++ b/src/PlumedMain.cpp @@ -53,7 +53,7 @@ PlumedMain::PlumedMain(): stopwatch(*new Stopwatch), grex(NULL), initialized(false), - log(*new Log(comm)), + log(*new Log), citations(*new Citations), step(0), active(false), @@ -62,6 +62,9 @@ PlumedMain::PlumedMain(): bias(0.0), novirial(false) { + log.link(comm); + log.setLinePrefix("PLUMED: "); + log.link(stdout); stopwatch.start(); stopwatch.pause(); } @@ -260,11 +263,11 @@ void PlumedMain::cmd(const std::string & word,void*val){ MDEngine=static_cast<char*>(val); } else if(word=="setLog"){ CHECK_NOTINIT(initialized,word); - log.set(static_cast<FILE*>(val)); + log.link(static_cast<FILE*>(val)); } else if(word=="setLogFile"){ CHECK_NOTINIT(initialized,word); CHECK_NULL(val,word); - log.setFile(static_cast<char*>(val)); + log.open(static_cast<char*>(val),"w"); } else if(word=="getExchangesFlag"){ CHECK_INIT(initialized,word); CHECK_NULL(val,word);