Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2012-2020 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 4139096 : size_t OFile::llwrite(const char*ptr,size_t s) {
47 : size_t r;
48 4139096 : if(linked) return linked->llwrite(ptr,s);
49 4139079 : if(! (comm && comm->Get_rank()>0)) {
50 3422886 : if(!fp) plumed_merror("writing on uninitilized File");
51 3422886 : 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 3421623 : r=fwrite(ptr,1,s,fp);
59 : }
60 : }
61 : // This barrier is apparently useless since it comes
62 : // just before a Bcast.
63 : //
64 : // Anyway, it looks like it is solving an issue that appeared on
65 : // TRAVIS (at least on my laptop) so I add it here.
66 : // GB
67 4139079 : if(comm) comm->Barrier();
68 :
69 :
70 4139079 : if(comm) comm->Bcast(r,0);
71 4139079 : return r;
72 : }
73 :
74 6285 : OFile::OFile():
75 : linked(NULL),
76 : fieldChanged(false),
77 : backstring("bck"),
78 : enforceRestart_(false),
79 37710 : enforceBackup_(false)
80 : {
81 6285 : fmtField();
82 6285 : buflen=1;
83 6285 : actual_buffer_length=0;
84 6285 : buffer.reset(new char [buflen]);
85 : // these are set to zero to avoid valgrind errors
86 18855 : for(int i=0; i<buflen; ++i) buffer[i]=0;
87 6285 : buffer_string.reset(new char [1000]);
88 : // these are set to zero to avoid valgrind errors
89 12576285 : for(unsigned i=0; i<1000; ++i) buffer_string[i]=0;
90 6285 : }
91 :
92 4 : OFile& OFile::link(OFile&l) {
93 4 : fp=NULL;
94 4 : gzfp=NULL;
95 4 : linked=&l;
96 4 : return *this;
97 : }
98 :
99 2575 : OFile& OFile::setLinePrefix(const std::string&l) {
100 2575 : linePrefix=l;
101 2575 : return *this;
102 : }
103 :
104 19625657 : int OFile::printf(const char*fmt,...) {
105 : va_list arg;
106 19625657 : va_start(arg, fmt);
107 19625657 : int r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
108 19625657 : va_end(arg);
109 19625657 : if(r>=buflen-actual_buffer_length) {
110 : int newlen=buflen;
111 37561 : while(newlen<=r+actual_buffer_length) newlen*=2;
112 12623 : std::unique_ptr<char[]> newbuf{new char [newlen]};
113 12623 : memmove(newbuf.get(),buffer.get(),buflen);
114 4082929 : for(int k=buflen; k<newlen; k++) newbuf[k]=0;
115 : buffer=std::move(newbuf);
116 12623 : buflen=newlen;
117 : va_list arg;
118 12623 : va_start(arg, fmt);
119 12623 : r=std::vsnprintf(&buffer[actual_buffer_length],buflen-actual_buffer_length,fmt,arg);
120 12623 : va_end(arg);
121 : }
122 19625657 : 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.get();
126 : char*p2;
127 : // newline is only searched in the just added portion:
128 19625657 : char*psearch=p1+actual_buffer_length;
129 19625657 : actual_buffer_length+=r;
130 27495507 : while((p2=strchr(psearch,'\n'))) {
131 4139079 : if(linePrefix.length()>0) llwrite(linePrefix.c_str(),linePrefix.length());
132 3934925 : llwrite(p1,p2-p1+1);
133 3934925 : actual_buffer_length-=(p2-p1)+1;
134 3934925 : p1=p2+1;
135 : psearch=p1;
136 : };
137 19625657 : if(buffer.get()!=p1) memmove(buffer.get(),p1,actual_buffer_length);
138 19625657 : return r;
139 : }
140 :
141 24944 : OFile& OFile::addConstantField(const std::string&name) {
142 : Field f;
143 : f.name=name;
144 24944 : const_fields.push_back(f);
145 24944 : return *this;
146 : }
147 :
148 :
149 2986 : OFile& OFile::clearFields() {
150 : fields.clear();
151 : const_fields.clear();
152 : previous_fields.clear();
153 2986 : return *this;
154 : }
155 :
156 12845781 : OFile& OFile::fmtField(const std::string&fmt) {
157 12845781 : this->fieldFmt=fmt;
158 12845781 : return *this;
159 : }
160 :
161 11854 : OFile& OFile::fmtField() {
162 11854 : this->fieldFmt="%23.16lg";
163 11854 : return *this;
164 : }
165 :
166 13442114 : 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 : if(std::isnan(v)) v=std::numeric_limits<double>::quiet_NaN();
171 : sprintf(buffer_string.get(),fieldFmt.c_str(),v);
172 26884228 : printField(name,buffer_string.get());
173 13442114 : return *this;
174 : }
175 :
176 5684023 : OFile& OFile::printField(const std::string&name,int v) {
177 : sprintf(buffer_string.get()," %d",v);
178 11368046 : printField(name,buffer_string.get());
179 5684023 : return *this;
180 : }
181 :
182 34116836 : OFile& OFile::printField(const std::string&name,const std::string & v) {
183 : unsigned i;
184 516779834 : for(i=0; i<const_fields.size(); i++) if(const_fields[i].name==name) break;
185 34116836 : if(i>=const_fields.size()) {
186 : Field field;
187 : field.name=name;
188 : field.value=v;
189 14416915 : fields.push_back(field);
190 : } else {
191 39399842 : if(const_fields[i].value!=v) fieldChanged=true;
192 : const_fields[i].value=v;
193 : }
194 34116836 : return *this;
195 : }
196 :
197 5039 : OFile& OFile::setupPrintValue( Value *val ) {
198 5039 : if( val->isPeriodic() ) {
199 506 : addConstantField("min_" + val->getName() );
200 506 : addConstantField("max_" + val->getName() );
201 : }
202 5039 : return *this;
203 : }
204 :
205 155393 : OFile& OFile::printField( Value* val, const double& v ) {
206 310786 : printField( val->getName(), v );
207 155393 : if( val->isPeriodic() ) {
208 10857 : std::string min, max; val->getDomain( min, max );
209 21714 : printField( "min_" + val->getName(), min );
210 21714 : printField("max_" + val->getName(), max );
211 : }
212 155393 : return *this;
213 : }
214 :
215 3474614 : OFile& OFile::printField() {
216 : bool reprint=false;
217 6944936 : if(fieldChanged || fields.size()!=previous_fields.size()) {
218 : reprint=true;
219 32230388 : } else for(unsigned i=0; i<fields.size(); i++) {
220 57522058 : if( previous_fields[i].name!=fields[i].name ||
221 14380513 : (fields[i].constant && fields[i].value!=previous_fields[i].value) ) {
222 : reprint=true;
223 : break;
224 : }
225 : }
226 3474614 : if(reprint) {
227 5254 : printf("#! FIELDS");
228 119720 : for(unsigned i=0; i<fields.size(); i++) printf(" %s",fields[i].name.c_str());
229 5254 : printf("\n");
230 85172 : for(unsigned i=0; i<const_fields.size(); i++) {
231 24888 : printf("#! SET %s %s",const_fields[i].name.c_str(),const_fields[i].value.c_str());
232 24888 : printf("\n");
233 : }
234 : }
235 50199973 : for(unsigned i=0; i<fields.size(); i++) printf("%s",fields[i].value.c_str());
236 3474614 : printf("\n");
237 3474614 : previous_fields=fields;
238 : fields.clear();
239 3474614 : fieldChanged=false;
240 3474614 : return *this;
241 : }
242 :
243 121 : void OFile::setBackupString( const std::string& str ) {
244 121 : backstring=str;
245 121 : }
246 :
247 36 : void OFile::backupAllFiles( const std::string& str ) {
248 36 : if(str=="/dev/null") return;
249 72 : plumed_assert( backstring!="bck" && !checkRestart());
250 : size_t found=str.find_last_of("/\\");
251 72 : std::string filename = appendSuffix(str,getSuffix());
252 36 : std::string directory=filename.substr(0,found+1);
253 36 : std::string file=filename.substr(found+1);
254 36 : if( FileExist(filename) ) backupFile("bck", filename);
255 0 : for(int i=0;; i++) {
256 36 : std::string num; Tools::convert(i,num);
257 180 : std::string filestr = directory + backstring + "." + num + "." + file;
258 36 : if( !FileExist(filestr) ) break;
259 0 : backupFile( "bck", filestr);
260 0 : }
261 : }
262 :
263 2689 : void OFile::backupFile( const std::string& bstring, const std::string& fname ) {
264 2845 : if(fname=="/dev/null") return;
265 2533 : int maxbackup=100;
266 2533 : if(std::getenv("PLUMED_MAXBACKUP")) Tools::convert(std::getenv("PLUMED_MAXBACKUP"),maxbackup);
267 2533 : if(maxbackup>0 && (!comm || comm->Get_rank()==0)) {
268 2124 : FILE* ff=std::fopen(const_cast<char*>(fname.c_str()),"r");
269 2124 : if(ff) {
270 58 : std::fclose(ff);
271 : std::string backup;
272 : size_t found=fname.find_last_of("/\\");
273 58 : std::string directory=fname.substr(0,found+1);
274 58 : std::string file=fname.substr(found+1);
275 0 : for(int i=0;; i++) {
276 : std::string num;
277 58 : Tools::convert(i,num);
278 58 : if(i>maxbackup) plumed_merror("cannot backup file "+file+" maximum number of backup is "+num+"\n");
279 348 : backup=directory+bstring +"."+num+"."+file;
280 58 : FILE* fff=std::fopen(backup.c_str(),"r");
281 58 : if(!fff) break;
282 0 : else std::fclose(fff);
283 0 : }
284 58 : int check=rename(fname.c_str(),backup.c_str());
285 58 : plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+strerror(errno));
286 : }
287 : }
288 : }
289 :
290 2955 : OFile& OFile::open(const std::string&path) {
291 2955 : plumed_assert(!cloned);
292 2955 : eof=false;
293 2955 : err=false;
294 2955 : fp=NULL;
295 2955 : gzfp=NULL;
296 2955 : this->path=path;
297 8865 : this->path=appendSuffix(path,getSuffix());
298 2955 : if(checkRestart()) {
299 532 : fp=std::fopen(const_cast<char*>(this->path.c_str()),"a");
300 266 : mode="a";
301 532 : if(Tools::extension(this->path)=="gz") {
302 : #ifdef __PLUMED_HAS_ZLIB
303 48 : gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"a9");
304 : #else
305 : plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
306 : #endif
307 : }
308 : } else {
309 2689 : backupFile( backstring, this->path );
310 2689 : if(comm)comm->Barrier();
311 5378 : fp=std::fopen(const_cast<char*>(this->path.c_str()),"w");
312 2689 : mode="w";
313 5378 : if(Tools::extension(this->path)=="gz") {
314 : #ifdef __PLUMED_HAS_ZLIB
315 2 : gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
316 : #else
317 : plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
318 : #endif
319 : }
320 : }
321 2955 : if(plumed) plumed->insertFile(*this);
322 2955 : return *this;
323 : }
324 :
325 114 : OFile& OFile::rewind() {
326 : // we use here "hard" rewind, which means close/reopen
327 : // the reason is that normal rewind does not work when in append mode
328 : // moreover, we can take a backup of the file
329 114 : plumed_assert(fp);
330 114 : clearFields();
331 114 : if(gzfp) {
332 : #ifdef __PLUMED_HAS_ZLIB
333 15 : gzclose((gzFile)gzfp);
334 : #endif
335 99 : } else fclose(fp);
336 114 : if(!comm || comm->Get_rank()==0) {
337 92 : std::string fname=this->path;
338 : size_t found=fname.find_last_of("/\\");
339 92 : std::string directory=fname.substr(0,found+1);
340 92 : std::string file=fname.substr(found+1);
341 276 : std::string backup=directory+backstring +".last."+file;
342 92 : int check=rename(fname.c_str(),backup.c_str());
343 92 : plumed_massert(check==0,"renaming "+fname+" into "+backup+" failed for reason: "+strerror(errno));
344 : }
345 :
346 114 : if(comm) comm->Barrier();
347 :
348 114 : if(gzfp) {
349 : #ifdef __PLUMED_HAS_ZLIB
350 15 : gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"w9");
351 : #endif
352 99 : } else fp=std::fopen(const_cast<char*>(path.c_str()),"w");
353 114 : return *this;
354 : }
355 :
356 7756 : FileBase& OFile::flush() {
357 7756 : if(heavyFlush) {
358 3795 : if(gzfp) {
359 : #ifdef __PLUMED_HAS_ZLIB
360 9 : gzclose(gzFile(gzfp));
361 18 : gzfp=(void*)gzopen(const_cast<char*>(path.c_str()),"a");
362 : #endif
363 : } else {
364 3786 : fclose(fp);
365 7572 : fp=std::fopen(const_cast<char*>(path.c_str()),"a");
366 : }
367 : } else {
368 3961 : FileBase::flush();
369 : // if(gzfp) gzflush(gzFile(gzfp),Z_FINISH);
370 : // for some reason flushing with Z_FINISH has problems on linux
371 : // I thus use this (incomplete) flush
372 : #ifdef __PLUMED_HAS_ZLIB
373 3961 : if(gzfp) gzflush(gzFile(gzfp),Z_FULL_FLUSH);
374 : #endif
375 : }
376 7756 : return *this;
377 : }
378 :
379 2991 : bool OFile::checkRestart()const {
380 2991 : if(enforceRestart_) return true;
381 2990 : else if(enforceBackup_) return false;
382 3837 : else if(action) return action->getRestart();
383 191 : else if(plumed) return plumed->getRestart();
384 : else return false;
385 : }
386 :
387 1 : OFile& OFile::enforceRestart() {
388 1 : enforceRestart_=true;
389 1 : enforceBackup_=false;
390 1 : return *this;
391 : }
392 :
393 976 : OFile& OFile::enforceBackup() {
394 976 : enforceBackup_=true;
395 976 : enforceRestart_=false;
396 976 : return *this;
397 : }
398 :
399 :
400 5517 : }
|