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