Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2012-2019 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 : #ifdef __PLUMED_HAS_ZLIB
38 : #include <zlib.h>
39 : #endif
40 :
41 : namespace PLMD {
42 :
43 3775319 : size_t OFile::llwrite(const char*ptr,size_t s) {
44 : size_t r;
45 3775319 : if(linked) return linked->llwrite(ptr,s);
46 3775314 : if(! (comm && comm->Get_rank()>0)) {
47 3083905 : if(!fp) plumed_merror("writing on uninitilized File");
48 3083905 : if(gzfp) {
49 : #ifdef __PLUMED_HAS_ZLIB
50 1254 : r=gzwrite(gzFile(gzfp),ptr,s);
51 : #else
52 : plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
53 : #endif
54 : } else {
55 3082651 : r=fwrite(ptr,1,s,fp);
56 : }
57 : }
58 : // This barrier is apparently useless since it comes
59 : // just before a Bcast.
60 : //
61 : // Anyway, it looks like it is solving an issue that appeared on
62 : // TRAVIS (at least on my laptop) so I add it here.
63 : // GB
64 3775314 : if(comm) comm->Barrier();
65 :
66 :
67 3775314 : if(comm) comm->Bcast(r,0);
68 3775314 : return r;
69 : }
70 :
71 5579 : OFile::OFile():
72 : linked(NULL),
73 : fieldChanged(false),
74 : backstring("bck"),
75 : enforceRestart_(false),
76 5579 : enforceBackup_(false)
77 : {
78 5579 : fmtField();
79 5579 : buflen=1;
80 5579 : actual_buffer_length=0;
81 5579 : buffer=new char[buflen];
82 : // these are set to zero to avoid valgrind errors
83 5579 : for(int i=0; i<buflen; ++i) buffer[i]=0;
84 5579 : buffer_string=new char [1000];
85 : // these are set to zero to avoid valgrind errors
86 5579 : for(unsigned i=0; i<1000; ++i) buffer_string[i]=0;
87 5579 : }
88 :
89 15374 : OFile::~OFile() {
90 5579 : delete [] buffer_string;
91 5579 : delete [] buffer;
92 9795 : }
93 :
94 1 : OFile& OFile::link(OFile&l) {
95 1 : fp=NULL;
96 1 : gzfp=NULL;
97 1 : linked=&l;
98 1 : return *this;
99 : }
100 :
101 2225 : OFile& OFile::setLinePrefix(const std::string&l) {
102 2225 : linePrefix=l;
103 2225 : return *this;
104 : }
105 :
106 17495067 : int OFile::printf(const char*fmt,...) {
107 : va_list arg;
108 17495067 : va_start(arg, fmt);
109 17495067 : int r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
110 17495067 : va_end(arg);
111 17495067 : if(r>=buflen-actual_buffer_length) {
112 10573 : int newlen=buflen;
113 10573 : while(newlen<=r+actual_buffer_length) newlen*=2;
114 10573 : char* newbuf=new char [newlen];
115 10573 : memmove(newbuf,buffer,buflen);
116 10573 : for(int k=buflen; k<newlen; k++) newbuf[k]=0;
117 10573 : delete [] buffer;
118 10573 : buffer=newbuf;
119 10573 : buflen=newlen;
120 : va_list arg;
121 10573 : va_start(arg, fmt);
122 10573 : r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
123 10573 : va_end(arg);
124 : }
125 17495067 : plumed_massert(r>-1 && r<buflen-actual_buffer_length,"error using fmt string " + std::string(fmt));
126 :
127 : // Line is buffered until newline, then written with a PLUMED: prefix
128 17495067 : char*p1=buffer;
129 : char*p2;
130 : // newline is only searched in the just added portion:
131 17495067 : char*psearch=p1+actual_buffer_length;
132 17495067 : actual_buffer_length+=r;
133 38575688 : while((p2=strchr(psearch,'\n'))) {
134 3585554 : if(linePrefix.length()>0) llwrite(linePrefix.c_str(),linePrefix.length());
135 3585554 : llwrite(p1,p2-p1+1);
136 3585554 : actual_buffer_length-=(p2-p1)+1;
137 3585554 : p1=p2+1;
138 3585554 : psearch=p1;
139 : };
140 17495067 : if(buffer!=p1) memmove(buffer,p1,actual_buffer_length);
141 17495067 : return r;
142 : }
143 :
144 24288 : OFile& OFile::addConstantField(const std::string&name) {
145 24288 : Field f;
146 24288 : f.name=name;
147 24288 : const_fields.push_back(f);
148 24288 : return *this;
149 : }
150 :
151 :
152 2954 : OFile& OFile::clearFields() {
153 2954 : fields.clear();
154 2954 : const_fields.clear();
155 2954 : previous_fields.clear();
156 2954 : return *this;
157 : }
158 :
159 12012778 : OFile& OFile::fmtField(const std::string&fmt) {
160 12012778 : this->fieldFmt=fmt;
161 12012778 : return *this;
162 : }
163 :
164 11125 : OFile& OFile::fmtField() {
165 11125 : this->fieldFmt="%23.16lg";
166 11125 : return *this;
167 : }
168 :
169 12600607 : OFile& OFile::printField(const std::string&name,double v) {
170 12600607 : sprintf(buffer_string,fieldFmt.c_str(),v);
171 12600607 : printField(name,buffer_string);
172 12600607 : return *this;
173 : }
174 :
175 5335694 : OFile& OFile::printField(const std::string&name,int v) {
176 5335694 : sprintf(buffer_string," %d",v);
177 5335694 : printField(name,buffer_string);
178 5335694 : return *this;
179 : }
180 :
181 32063837 : OFile& OFile::printField(const std::string&name,const std::string & v) {
182 : unsigned i;
183 32063837 : for(i=0; i<const_fields.size(); i++) if(const_fields[i].name==name) break;
184 32063837 : if(i>=const_fields.size()) {
185 13521895 : Field field;
186 13521895 : field.name=name;
187 13521895 : field.value=v;
188 13521895 : fields.push_back(field);
189 : } else {
190 18541942 : if(const_fields[i].value!=v) fieldChanged=true;
191 18541942 : const_fields[i].value=v;
192 : }
193 32063837 : return *this;
194 : }
195 :
196 4001 : OFile& OFile::setupPrintValue( Value *val ) {
197 4001 : if( val->isPeriodic() ) {
198 223 : addConstantField("min_" + val->getName() );
199 223 : addConstantField("max_" + val->getName() );
200 : }
201 4001 : return *this;
202 : }
203 :
204 113894 : OFile& OFile::printField( Value* val, const double& v ) {
205 113894 : printField( val->getName(), v );
206 113894 : if( val->isPeriodic() ) {
207 12866 : std::string min, max; val->getDomain( min, max );
208 6433 : printField( "min_" + val->getName(), min );
209 12866 : printField("max_" + val->getName(), max );
210 : }
211 113894 : return *this;
212 : }
213 :
214 3251368 : OFile& OFile::printField() {
215 3251368 : bool reprint=false;
216 3251368 : if(fieldChanged || fields.size()!=previous_fields.size()) {
217 5043 : reprint=true;
218 16733461 : } else for(unsigned i=0; i<fields.size(); i++) {
219 26974276 : if( previous_fields[i].name!=fields[i].name ||
220 13487136 : (fields[i].constant && fields[i].value!=previous_fields[i].value) ) {
221 2 : reprint=true;
222 2 : break;
223 : }
224 : }
225 3251368 : if(reprint) {
226 5045 : printf("#! FIELDS");
227 5045 : for(unsigned i=0; i<fields.size(); i++) printf(" %s",fields[i].name.c_str());
228 5045 : printf("\n");
229 29309 : for(unsigned i=0; i<const_fields.size(); i++) {
230 24264 : printf("#! SET %s %s",const_fields[i].name.c_str(),const_fields[i].value.c_str());
231 24264 : printf("\n");
232 : }
233 : }
234 3251368 : for(unsigned i=0; i<fields.size(); i++) printf("%s",fields[i].value.c_str());
235 3251368 : printf("\n");
236 3251368 : previous_fields=fields;
237 3251368 : fields.clear();
238 3251368 : fieldChanged=false;
239 3251368 : return *this;
240 : }
241 :
242 55 : void OFile::setBackupString( const std::string& str ) {
243 55 : backstring=str;
244 55 : }
245 :
246 4 : void OFile::backupAllFiles( const std::string& str ) {
247 8 : if(str=="/dev/null") return;
248 4 : plumed_assert( backstring!="bck" && !checkRestart());
249 4 : size_t found=str.find_last_of("/\\");
250 4 : std::string filename = appendSuffix(str,getSuffix());
251 8 : std::string directory=filename.substr(0,found+1);
252 8 : std::string file=filename.substr(found+1);
253 4 : if( FileExist(filename) ) backupFile("bck", filename);
254 4 : for(int i=0;; i++) {
255 4 : std::string num; Tools::convert(i,num);
256 4 : std::string filestr = directory + backstring + "." + num + "." + file;
257 4 : if( !FileExist(filestr) ) break;
258 0 : backupFile( "bck", filestr);
259 4 : }
260 : }
261 :
262 2483 : void OFile::backupFile( const std::string& bstring, const std::string& fname ) {
263 4966 : if(fname=="/dev/null") return;
264 2335 : int maxbackup=100;
265 2335 : if(std::getenv("PLUMED_MAXBACKUP")) Tools::convert(std::getenv("PLUMED_MAXBACKUP"),maxbackup);
266 2335 : if(maxbackup>0 && (!comm || comm->Get_rank()==0)) {
267 1948 : FILE* ff=std::fopen(const_cast<char*>(fname.c_str()),"r");
268 1948 : if(ff) {
269 52 : std::fclose(ff);
270 52 : std::string backup;
271 52 : size_t found=fname.find_last_of("/\\");
272 104 : std::string directory=fname.substr(0,found+1);
273 104 : std::string file=fname.substr(found+1);
274 52 : for(int i=0;; i++) {
275 52 : std::string num;
276 52 : Tools::convert(i,num);
277 52 : if(i>maxbackup) plumed_merror("cannot backup file "+file+" maximum number of backup is "+num+"\n");
278 52 : backup=directory+bstring +"."+num+"."+file;
279 52 : FILE* fff=std::fopen(backup.c_str(),"r");
280 52 : if(!fff) break;
281 0 : else std::fclose(fff);
282 0 : }
283 52 : int check=rename(fname.c_str(),backup.c_str());
284 104 : plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+strerror(errno));
285 : }
286 : }
287 : }
288 :
289 2712 : OFile& OFile::open(const std::string&path) {
290 2712 : plumed_assert(!cloned);
291 2712 : eof=false;
292 2712 : err=false;
293 2712 : fp=NULL;
294 2712 : gzfp=NULL;
295 2712 : this->path=path;
296 2712 : this->path=appendSuffix(path,getSuffix());
297 2712 : if(checkRestart()) {
298 229 : fp=std::fopen(const_cast<char*>(this->path.c_str()),"a");
299 229 : mode="a";
300 229 : if(Tools::extension(this->path)=="gz") {
301 : #ifdef __PLUMED_HAS_ZLIB
302 24 : gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"a9");
303 : #else
304 : plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
305 : #endif
306 : }
307 : } else {
308 2483 : backupFile( backstring, this->path );
309 2483 : if(comm)comm->Barrier();
310 2483 : fp=std::fopen(const_cast<char*>(this->path.c_str()),"w");
311 2483 : mode="w";
312 2483 : if(Tools::extension(this->path)=="gz") {
313 : #ifdef __PLUMED_HAS_ZLIB
314 1 : gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
315 : #else
316 : plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
317 : #endif
318 : }
319 : }
320 2712 : if(plumed) plumed->insertFile(*this);
321 2712 : return *this;
322 : }
323 :
324 94 : OFile& OFile::rewind() {
325 : // we use here "hard" rewind, which means close/reopen
326 : // the reason is that normal rewind does not work when in append mode
327 : // moreover, we can take a backup of the file
328 94 : plumed_assert(fp);
329 94 : clearFields();
330 94 : if(gzfp) {
331 : #ifdef __PLUMED_HAS_ZLIB
332 15 : gzclose((gzFile)gzfp);
333 : #endif
334 79 : } else fclose(fp);
335 94 : if(!comm || comm->Get_rank()==0) {
336 76 : std::string fname=this->path;
337 76 : size_t found=fname.find_last_of("/\\");
338 152 : std::string directory=fname.substr(0,found+1);
339 152 : std::string file=fname.substr(found+1);
340 152 : std::string backup=directory+backstring +".last."+file;
341 76 : int check=rename(fname.c_str(),backup.c_str());
342 152 : plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+strerror(errno));
343 : }
344 94 : if(gzfp) {
345 : #ifdef __PLUMED_HAS_ZLIB
346 15 : gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
347 : #endif
348 79 : } else fp=std::fopen(const_cast<char*>(path.c_str()),"w");
349 94 : return *this;
350 : }
351 :
352 7149 : FileBase& OFile::flush() {
353 7149 : if(heavyFlush) {
354 3747 : if(gzfp) {
355 : #ifdef __PLUMED_HAS_ZLIB
356 9 : gzclose(gzFile(gzfp));
357 9 : gzfp=(void*)gzopen(const_cast<char*>(path.c_str()),"a");
358 : #endif
359 : } else {
360 3738 : fclose(fp);
361 3738 : fp=std::fopen(const_cast<char*>(path.c_str()),"a");
362 : }
363 : } else {
364 3402 : FileBase::flush();
365 : // if(gzfp) gzflush(gzFile(gzfp),Z_FINISH);
366 : // for some reason flushing with Z_FINISH has problems on linux
367 : // I thus use this (incomplete) flush
368 : #ifdef __PLUMED_HAS_ZLIB
369 3402 : if(gzfp) gzflush(gzFile(gzfp),Z_FULL_FLUSH);
370 : #endif
371 : }
372 7149 : return *this;
373 : }
374 :
375 2716 : bool OFile::checkRestart()const {
376 2716 : if(enforceRestart_) return true;
377 2715 : else if(enforceBackup_) return false;
378 1763 : else if(action) return action->getRestart();
379 170 : else if(plumed) return plumed->getRestart();
380 170 : else return false;
381 : }
382 :
383 1 : OFile& OFile::enforceRestart() {
384 1 : enforceRestart_=true;
385 1 : enforceBackup_=false;
386 1 : return *this;
387 : }
388 :
389 952 : OFile& OFile::enforceBackup() {
390 952 : enforceBackup_=true;
391 952 : enforceRestart_=false;
392 952 : return *this;
393 : }
394 :
395 :
396 4821 : }
|