Skip to content
Snippets Groups Projects
OFile.cpp 6.68 KiB
Newer Older
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   Copyright (c) 2013 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 "Exception.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);
     }
     if(comm)comm->Barrier();
     fp=std::fopen(const_cast<char*>(this->path.c_str()),"w");
  }
  if(plumed) plumed->insertFile(*this);
  return *this;
}

}