Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2012-2023 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 : #include <memory>
38 : #include <utility>
39 :
40 : #ifdef __PLUMED_HAS_ZLIB
41 : #include <zlib.h>
42 : #endif
43 :
44 : namespace PLMD {
45 :
46 6080488 : size_t OFile::llwrite(const char*ptr,size_t s) {
47 : size_t r;
48 6080488 : if(linked) {
49 52 : return linked->llwrite(ptr,s);
50 : }
51 6080436 : if(! (comm && comm->Get_rank()>0)) {
52 5195930 : if(!fp) {
53 0 : plumed_merror("writing on uninitialized File");
54 : }
55 5195930 : if(gzfp) {
56 : #ifdef __PLUMED_HAS_ZLIB
57 1263 : r=gzwrite(gzFile(gzfp),ptr,s);
58 : #else
59 : plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
60 : #endif
61 : } else {
62 5194667 : r=std::fwrite(ptr,1,s,fp);
63 : }
64 : }
65 6080436 : if(comm) {
66 : // This barrier is apparently useless since it comes
67 : // just before a Bcast.
68 : //
69 : // Anyway, it looks like it is solving an issue that appeared on
70 : // TRAVIS (at least on my laptop) so I add it here.
71 : // GB
72 5217896 : comm->Barrier();
73 5217896 : comm->Bcast(r,0);
74 : }
75 :
76 6080436 : return r;
77 : }
78 :
79 822188 : OFile::OFile():
80 822188 : linked(NULL),
81 822188 : fieldChanged(false),
82 822188 : backstring("bck"),
83 822188 : enforceRestart_(false),
84 822188 : enforceBackup_(false) {
85 822188 : fmtField();
86 822188 : buflen=1;
87 822188 : actual_buffer_length=0;
88 822188 : buffer.resize(buflen);
89 : // these are set to zero to avoid valgrind errors
90 1644376 : for(int i=0; i<buflen; ++i) {
91 822188 : buffer[i]=0;
92 : }
93 : // these are set to zero to avoid valgrind errors
94 822188 : buffer_string.resize(1000,0);
95 822188 : }
96 :
97 11 : OFile& OFile::link(OFile&l) {
98 11 : fp=NULL;
99 11 : gzfp=NULL;
100 11 : linked=&l;
101 11 : return *this;
102 : }
103 :
104 806850 : OFile& OFile::setLinePrefix(const std::string&l) {
105 806850 : linePrefix=l;
106 806850 : return *this;
107 : }
108 :
109 25158123 : int OFile::printf(const char*fmt,...) {
110 : va_list arg;
111 25158123 : va_start(arg, fmt);
112 25158123 : int r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
113 25158123 : va_end(arg);
114 25158123 : if(r>=buflen-actual_buffer_length) {
115 : int newlen=buflen;
116 57101 : while(newlen<=r+actual_buffer_length) {
117 37806 : newlen*=2;
118 : }
119 19295 : std::vector<char> newbuf(newlen);
120 19295 : std::memmove(newbuf.data(),buffer.data(),buflen);
121 13941899 : for(int k=buflen; k<newlen; k++) {
122 13922604 : newbuf[k]=0;
123 : }
124 : std::swap(buffer,newbuf);
125 19295 : buflen=newlen;
126 : va_list arg;
127 19295 : va_start(arg, fmt);
128 19295 : r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
129 19295 : va_end(arg);
130 : }
131 25158123 : plumed_massert(r>-1 && r<buflen-actual_buffer_length,"error using fmt string " + std::string(fmt));
132 :
133 : // Line is buffered until newline, then written with a PLUMED: prefix
134 : char*p1=buffer.data();
135 : char*p2;
136 : // newline is only searched in the just added portion:
137 25158123 : char*psearch=p1+actual_buffer_length;
138 25158123 : actual_buffer_length+=r;
139 30652346 : while((p2=std::strchr(psearch,'\n'))) {
140 5494223 : if(linePrefix.length()>0) {
141 586213 : llwrite(linePrefix.c_str(),linePrefix.length());
142 : }
143 5494223 : llwrite(p1,p2-p1+1);
144 5494223 : actual_buffer_length-=(p2-p1)+1;
145 5494223 : p1=p2+1;
146 : psearch=p1;
147 : };
148 25158123 : if(buffer.data()!=p1) {
149 5482010 : std::memmove(buffer.data(),p1,actual_buffer_length);
150 : }
151 25158123 : return r;
152 : }
153 :
154 28074 : OFile& OFile::addConstantField(const std::string&name) {
155 : Field f;
156 : f.name=name;
157 28074 : const_fields.push_back(f);
158 28074 : return *this;
159 : }
160 :
161 :
162 3228 : OFile& OFile::clearFields() {
163 3228 : fields.clear();
164 3228 : const_fields.clear();
165 3228 : previous_fields.clear();
166 3228 : return *this;
167 : }
168 :
169 14341913 : OFile& OFile::fmtField(const std::string&fmt) {
170 14341913 : this->fieldFmt=fmt;
171 14341913 : return *this;
172 : }
173 :
174 828059 : OFile& OFile::fmtField() {
175 828059 : this->fieldFmt="%23.16lg";
176 828059 : return *this;
177 : }
178 :
179 15840189 : OFile& OFile::printField(const std::string&name,double v) {
180 : // When one tries to print -nan we print nan instead.
181 : // The distinction between +nan and -nan is not well defined
182 : // Always printing nan simplifies some regtest (special functions computed our of range).
183 15840189 : if(std::isnan(v)) {
184 : v=std::numeric_limits<double>::quiet_NaN();
185 : }
186 : std::snprintf(buffer_string.data(),buffer_string.size(),fieldFmt.c_str(),v);
187 15840189 : printField(name,buffer_string.data());
188 15840189 : return *this;
189 : }
190 :
191 6150573 : OFile& OFile::printField(const std::string&name,int v) {
192 : std::snprintf(buffer_string.data(),buffer_string.size()," %d",v);
193 6150573 : printField(name,buffer_string.data());
194 6150573 : return *this;
195 : }
196 :
197 1 : OFile& OFile::printField(const std::string&name,long int v) {
198 : std::snprintf(buffer_string.data(),buffer_string.size()," %ld",v);
199 1 : printField(name,buffer_string.data());
200 1 : return *this;
201 : }
202 :
203 0 : OFile& OFile::printField(const std::string&name,long long int v) {
204 : std::snprintf(buffer_string.data(),buffer_string.size()," %lld",v);
205 0 : printField(name,buffer_string.data());
206 0 : return *this;
207 : }
208 :
209 31 : OFile& OFile::printField(const std::string&name,unsigned v) {
210 : std::snprintf(buffer_string.data(),buffer_string.size()," %u",v);
211 31 : printField(name,buffer_string.data());
212 31 : return *this;
213 : }
214 :
215 1 : OFile& OFile::printField(const std::string&name,long unsigned v) {
216 : std::snprintf(buffer_string.data(),buffer_string.size()," %lu",v);
217 1 : printField(name,buffer_string.data());
218 1 : return *this;
219 : }
220 :
221 47 : OFile& OFile::printField(const std::string&name,long long unsigned v) {
222 : std::snprintf(buffer_string.data(),buffer_string.size()," %llu",v);
223 47 : printField(name,buffer_string.data());
224 47 : return *this;
225 : }
226 :
227 38612218 : OFile& OFile::printField(const std::string&name,const std::string & v) {
228 : unsigned i;
229 201414134 : for(i=0; i<const_fields.size(); i++)
230 184547058 : if(const_fields[i].name==name) {
231 : break;
232 : }
233 38612218 : if(i>=const_fields.size()) {
234 : Field field;
235 : field.name=name;
236 : field.value=v;
237 16867076 : fields.push_back(field);
238 : } else {
239 21745142 : if(const_fields[i].value!=v) {
240 28006 : fieldChanged=true;
241 : }
242 : const_fields[i].value=v;
243 : }
244 38612218 : return *this;
245 : }
246 :
247 6855 : OFile& OFile::setupPrintValue( Value *val ) {
248 6855 : if( val->isPeriodic() ) {
249 510 : addConstantField("min_" + val->getName() );
250 1020 : addConstantField("max_" + val->getName() );
251 : }
252 6855 : return *this;
253 : }
254 :
255 7286 : OFile& OFile::printField( Value* val, const double& v ) {
256 7286 : printField( val->getName(), v );
257 7286 : if( val->isPeriodic() ) {
258 : std::string min, max;
259 1586 : val->getDomain( min, max );
260 1586 : printField( "min_" + val->getName(), min );
261 3172 : printField("max_" + val->getName(), max );
262 : }
263 7286 : return *this;
264 : }
265 :
266 3855526 : OFile& OFile::printField() {
267 : bool reprint=false;
268 3855526 : if(fieldChanged || fields.size()!=previous_fields.size()) {
269 : reprint=true;
270 : } else
271 20126858 : for(unsigned i=0; i<fields.size(); i++) {
272 16277708 : if( previous_fields[i].name!=fields[i].name ||
273 16277706 : (fields[i].constant && fields[i].value!=previous_fields[i].value) ) {
274 : reprint=true;
275 : break;
276 : }
277 : }
278 3855526 : if(reprint) {
279 6376 : printf("#! FIELDS");
280 595748 : for(unsigned i=0; i<fields.size(); i++) {
281 589372 : printf(" %s",fields[i].name.c_str());
282 : }
283 6376 : printf("\n");
284 34386 : for(unsigned i=0; i<const_fields.size(); i++) {
285 28010 : printf("#! SET %s %s",const_fields[i].name.c_str(),const_fields[i].value.c_str());
286 28010 : printf("\n");
287 : }
288 : }
289 20722602 : for(unsigned i=0; i<fields.size(); i++) {
290 16867076 : printf("%s",fields[i].value.c_str());
291 : }
292 3855526 : printf("\n");
293 3855526 : previous_fields=fields;
294 3855526 : fields.clear();
295 3855526 : fieldChanged=false;
296 3855526 : return *this;
297 : }
298 :
299 194 : void OFile::setBackupString( const std::string& str ) {
300 194 : backstring=str;
301 194 : }
302 :
303 0 : void OFile::backupAllFiles( const std::string& str ) {
304 0 : if(str=="/dev/null") {
305 0 : return;
306 : }
307 0 : plumed_assert( backstring!="bck" && !checkRestart());
308 0 : size_t found=str.find_last_of("/\\");
309 0 : std::string filename = appendSuffix(str,getSuffix());
310 0 : std::string directory=filename.substr(0,found+1);
311 0 : std::string file=filename.substr(found+1);
312 0 : if( FileExist(filename) ) {
313 0 : backupFile("bck", filename);
314 : }
315 0 : for(int i=0;; i++) {
316 : std::string num;
317 0 : Tools::convert(i,num);
318 0 : std::string filestr = directory + backstring + "." + num + "." + file;
319 0 : if( !FileExist(filestr) ) {
320 : break;
321 : }
322 0 : backupFile( "bck", filestr);
323 0 : }
324 : }
325 :
326 3851 : void OFile::backupFile( const std::string& bstring, const std::string& fname ) {
327 3851 : if(fname=="/dev/null") {
328 154 : return;
329 : }
330 3697 : int maxbackup=100;
331 3697 : if(std::getenv("PLUMED_MAXBACKUP")) {
332 78 : Tools::convert(std::getenv("PLUMED_MAXBACKUP"),maxbackup);
333 : }
334 3697 : if(maxbackup>0 && (!comm || comm->Get_rank()==0)) {
335 3120 : FILE* ff=std::fopen(const_cast<char*>(fname.c_str()),"r");
336 3120 : if(ff) {
337 : // no exception here
338 123 : std::fclose(ff);
339 : std::string backup;
340 123 : size_t found=fname.find_last_of("/\\");
341 123 : std::string directory=fname.substr(0,found+1);
342 123 : std::string file=fname.substr(found+1);
343 123 : for(int i=0;; i++) {
344 : std::string num;
345 172 : Tools::convert(i,num);
346 172 : if(i>maxbackup) {
347 0 : plumed_merror("cannot backup file "+file+" maximum number of backup is "+num+"\n");
348 : }
349 344 : backup=directory+bstring +"."+num+"."+file;
350 172 : FILE* fff=std::fopen(backup.c_str(),"r");
351 : // no exception here
352 172 : if(!fff) {
353 : break;
354 : } else {
355 49 : std::fclose(fff);
356 : }
357 49 : }
358 123 : int check=rename(fname.c_str(),backup.c_str());
359 123 : plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+std::strerror(errno));
360 : }
361 : }
362 : }
363 :
364 4232 : OFile& OFile::open(const std::string&path) {
365 4232 : plumed_assert(!cloned);
366 4232 : eof=false;
367 4232 : err=false;
368 4232 : fp=NULL;
369 4232 : gzfp=NULL;
370 4232 : this->path=path;
371 8464 : this->path=appendSuffix(path,getSuffix());
372 4232 : if(checkRestart()) {
373 381 : fp=std::fopen(const_cast<char*>(this->path.c_str()),"a");
374 381 : mode="a";
375 762 : if(Tools::extension(this->path)=="gz") {
376 : #ifdef __PLUMED_HAS_ZLIB
377 12 : gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"a9");
378 : #else
379 : plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
380 : #endif
381 : }
382 : } else {
383 3851 : backupFile( backstring, this->path );
384 3851 : if(comm) {
385 3808 : comm->Barrier();
386 : }
387 3851 : fp=std::fopen(const_cast<char*>(this->path.c_str()),"w");
388 3851 : mode="w";
389 7702 : if(Tools::extension(this->path)=="gz") {
390 : #ifdef __PLUMED_HAS_ZLIB
391 13 : gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
392 : #else
393 : plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
394 : #endif
395 : }
396 : }
397 4232 : if(plumed) {
398 3887 : plumed->insertFile(*this);
399 : }
400 4232 : return *this;
401 : }
402 :
403 161 : OFile& OFile::rewind() {
404 : // we use here "hard" rewind, which means close/reopen
405 : // the reason is that normal rewind does not work when in append mode
406 : // moreover, we can take a backup of the file
407 161 : plumed_assert(fp);
408 161 : clearFields();
409 :
410 161 : if(!comm || comm->Get_rank()==0) {
411 130 : std::string fname=this->path;
412 130 : size_t found=fname.find_last_of("/\\");
413 130 : std::string directory=fname.substr(0,found+1);
414 130 : std::string file=fname.substr(found+1);
415 260 : std::string backup=directory+backstring +".last."+file;
416 130 : int check=rename(fname.c_str(),backup.c_str());
417 130 : plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+std::strerror(errno));
418 : }
419 :
420 161 : if(comm) {
421 161 : comm->Barrier();
422 : }
423 :
424 161 : if(gzfp) {
425 : #ifdef __PLUMED_HAS_ZLIB
426 15 : gzclose((gzFile)gzfp);
427 : // no exception here
428 15 : gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
429 : #endif
430 : } else {
431 146 : std::fclose(fp);
432 : // no exception here
433 146 : fp=std::fopen(const_cast<char*>(path.c_str()),"w");
434 : }
435 161 : return *this;
436 : }
437 :
438 9580 : FileBase& OFile::flush() {
439 9580 : if(heavyFlush) {
440 3918 : if(gzfp) {
441 : #ifdef __PLUMED_HAS_ZLIB
442 9 : gzclose(gzFile(gzfp));
443 : // no exception here
444 9 : gzfp=(void*)gzopen(const_cast<char*>(path.c_str()),"a");
445 : #endif
446 : } else {
447 3909 : std::fclose(fp);
448 : // no exception here
449 3909 : fp=std::fopen(const_cast<char*>(path.c_str()),"a");
450 : }
451 : } else {
452 5662 : FileBase::flush();
453 : // if(gzfp) gzflush(gzFile(gzfp),Z_FINISH);
454 : // for some reason flushing with Z_FINISH has problems on linux
455 : // I thus use this (incomplete) flush
456 : #ifdef __PLUMED_HAS_ZLIB
457 5662 : if(gzfp) {
458 31 : gzflush(gzFile(gzfp),Z_FULL_FLUSH);
459 : }
460 : #endif
461 : }
462 9580 : return *this;
463 : }
464 :
465 4232 : bool OFile::checkRestart()const {
466 4232 : if(enforceRestart_) {
467 : return true;
468 4221 : } else if(enforceBackup_) {
469 : return false;
470 3077 : } else if(action) {
471 2733 : return action->getRestart();
472 344 : } else if(plumed) {
473 0 : return plumed->getRestart();
474 : } else {
475 : return false;
476 : }
477 : }
478 :
479 11 : OFile& OFile::enforceRestart() {
480 11 : enforceRestart_=true;
481 11 : enforceBackup_=false;
482 11 : return *this;
483 : }
484 :
485 1144 : OFile& OFile::enforceBackup() {
486 1144 : enforceBackup_=true;
487 1144 : enforceRestart_=false;
488 1144 : return *this;
489 : }
490 :
491 :
492 : }
|