apply first part of patch #6994 - elbeem_warning_patch.diff
[blender.git] / intern / elbeem / intern / utilities.cpp
1 /******************************************************************************
2  *
3  * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
4  * Copyright 2003-2006 Nils Thuerey
5  *
6  * Global C style utility funcions
7  *
8  *****************************************************************************/
9
10
11 #include <iostream>
12 #include <sstream>
13 #include <stdlib.h> /* getenv(3), strtol(3) */
14 #ifdef WIN32
15 // for timing
16 #include <windows.h>
17 #else
18 #include <time.h>
19 #include <sys/time.h>
20 #include <sys/times.h>
21 #endif
22
23 #include "utilities.h"
24
25 #ifndef NOPNG
26 #ifdef WIN32
27 #include "png.h"
28 #else
29 #include <png.h>
30 #endif
31 #endif // NOPNG
32 #include <zlib.h>
33
34 // global debug level
35 #ifdef DEBUG 
36 int gDebugLevel = DEBUG;
37 #else // DEBUG 
38 int gDebugLevel = 0;
39 #endif // DEBUG 
40
41 // global world state, acces with get/setElbeemState
42 int gElbeemState = SIMWORLD_INVALID;
43
44 // access global state of elbeem simulator
45 void setElbeemState(int set) {
46         gElbeemState = set;
47 }
48 int  getElbeemState(void) { 
49         return gElbeemState;
50 }
51 int  isSimworldOk(void) {
52         return (getElbeemState>=0);
53 }
54
55 // last error as string, acces with get/setElbeemErrorString
56 char gElbeemErrorString[256] = {'-','\0' };
57
58 // access elbeem simulator error string
59 void setElbeemErrorString(const char* set) {
60         strncpy(gElbeemErrorString, set, 256);
61 }
62 char* getElbeemErrorString(void) { return gElbeemErrorString; }
63
64
65 //! for interval debugging output
66 myTime_t globalIntervalTime = 0;
67 //! color output setting for messages (0==off, else on)
68 #ifdef WIN32
69 // switch off first call
70 #define DEF_globalColorSetting -1 
71 #else // WIN32
72 // linux etc., on by default
73 #define DEF_globalColorSetting 1 
74 #endif // WIN32
75 int globalColorSetting = DEF_globalColorSetting; // linux etc., on by default
76 int globalFirstEnvCheck = 0;
77 void resetGlobalColorSetting() { globalColorSetting = DEF_globalColorSetting; }
78
79 // global string for formatting vector output, TODO test!?
80 const char *globVecFormatStr = "V[%f,%f,%f]";
81
82
83 // global mp on/off switch
84 bool glob_mpactive = false; 
85 // global access to mpi index, for debugging (e.g. in utilities.cpp)
86 int glob_mpnum = -1;
87 int glob_mpindex = -1;
88 int glob_mppn = -1;
89
90
91 //-----------------------------------------------------------------------------
92 // helper function that converts a string to integer, 
93 // and returns an alternative value if the conversion fails
94 int convertString2Int(const char *str, int alt)
95 {
96         int val;
97         char *endptr;
98         bool success=true;
99
100         val = strtol(str, &endptr, 10);
101         if( (str==endptr) ||
102                         ((str!=endptr) && (*endptr != '\0')) ) success = false;
103
104         if(!success) {
105                 return alt;
106         }
107         return val;
108 }
109
110 //-----------------------------------------------------------------------------
111 //! helper function that converts a flag field to a readable integer
112 string convertFlags2String(int flags) {
113         std::ostringstream ret;
114         ret <<"(";
115         int max = sizeof(int)*8;
116         for(int i=0; i<max; i++) {
117                 if(flags & (1<<31)) ret <<"1";
118                 else ret<<"0";
119                 if(i<max-1) {
120                         //ret << ",";
121                         if((i%8)==7) ret << " ";
122                 }
123                 flags = flags << 1;
124         }       
125         ret <<")";
126         return ret.str();
127 }
128
129 #ifndef NOPNG
130 //-----------------------------------------------------------------------------
131 //! write png image
132 int writePng(const char *fileName, unsigned char **rowsp, int w, int h)
133 {
134         // defaults for elbeem
135         const int colortype = PNG_COLOR_TYPE_RGBA;
136         const int bitdepth = 8;
137         png_structp png_ptr = NULL;
138         png_infop info_ptr = NULL;
139         png_bytep *rows = rowsp;
140
141         //FILE *fp = fopen(fileName, "wb");
142         FILE *fp = NULL;
143         string doing = "open for writing";
144         if (!(fp = fopen(fileName, "wb"))) goto fail;
145
146         if(!png_ptr) {
147                 doing = "create png write struct";
148                 if (!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) goto fail;
149         }
150         if(!info_ptr) {
151                 doing = "create png info struct";
152                 if (!(info_ptr = png_create_info_struct(png_ptr))) goto fail;
153         }
154
155         if (setjmp(png_jmpbuf(png_ptr))) goto fail;
156         doing = "init IO";
157         png_init_io(png_ptr, fp);
158         doing = "write header";
159         png_set_IHDR(png_ptr, info_ptr, w, h, bitdepth, colortype, PNG_INTERLACE_NONE,
160                         PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
161         doing = "write info";
162         png_write_info(png_ptr, info_ptr);
163         doing = "write image";
164         png_write_image(png_ptr, rows);
165         doing = "write end";
166         png_write_end(png_ptr, NULL);
167         doing = "write destroy structs";
168         png_destroy_write_struct(&png_ptr, &info_ptr);
169
170         fclose( fp );
171         return 0;
172
173 fail:   
174         errMsg("writePng","Write_png: could not "<<doing<<" !");
175         if(fp) fclose( fp );
176         if(png_ptr || info_ptr) png_destroy_write_struct(&png_ptr, &info_ptr);
177         return -1;
178 }
179 #else // NOPNG
180 // fallback - write ppm
181 int writePng(const char *fileName, unsigned char **rowsp, int w, int h)
182 {
183         gzFile gzf;
184         string filentemp(fileName);
185         // remove suffix
186         if((filentemp.length()>4) && (filentemp[filentemp.length()-4]=='.')) {
187                 filentemp[filentemp.length()-4] = '\0';
188         }
189         std::ostringstream filennew;
190         filennew << filentemp.c_str();
191         filennew << ".ppm.gz";
192
193         gzf = gzopen(filennew.str().c_str(), "wb9");
194         if(!gzf) goto fail;
195
196         gzprintf(gzf,"P6\n%d %d\n255\n",w,h);
197         // output binary pixels
198         for(int j=0;j<h;j++) {
199                 for(int i=0;i<h;i++) {
200                         // remove alpha values
201                         gzwrite(gzf,&rowsp[j][i*4],3);
202                 }
203         }
204
205         gzclose( gzf );
206         errMsg("writePng/ppm","Write_png/ppm: wrote to "<<filennew.str()<<".");
207         return 0;
208
209 fail:   
210         errMsg("writePng/ppm","Write_png/ppm: could not write to "<<filennew.str()<<" !");
211         return -1;
212 }
213 #endif // NOPNG
214
215
216 //-----------------------------------------------------------------------------
217 // helper function to determine current time
218 myTime_t getTime()
219 {
220         myTime_t ret = 0;
221 #ifdef WIN32
222         LARGE_INTEGER liTimerFrequency;
223         QueryPerformanceFrequency(&liTimerFrequency);
224         LARGE_INTEGER liLastTime;
225         QueryPerformanceCounter(&liLastTime);
226         ret = (INT)( ((double)liLastTime.QuadPart / liTimerFrequency.QuadPart)*1000 ); // - mFirstTime;
227 #else
228         struct timeval tv;
229         struct timezone tz;
230         tz.tz_minuteswest = 0;
231         tz.tz_dsttime = 0;
232         gettimeofday(&tv,&tz);
233         ret = (tv.tv_sec*1000)+(tv.tv_usec/1000); //-mFirstTime;
234 #endif
235         return (myTime_t)ret;
236 }
237 //-----------------------------------------------------------------------------
238 // convert time to readable string
239 string getTimeString(myTime_t usecs) {
240         std::ostringstream ret;
241         //myTime_t us = usecs % 1000;
242         myTime_t ms = (myTime_t)(   (double)usecs / (60.0*1000.0)  );
243         myTime_t ss = (myTime_t)(  ((double)usecs / 1000.0) - ((double)ms*60.0)  );
244         int      ps = (int)(       ((double)usecs - (double)ss*1000.0)/10.0 );
245
246         //ret.setf(ios::showpoint|ios::fixed);
247         //ret.precision(5); ret.width(7);
248
249         if(ms>0) {
250                 ret << ms<<"m"<< ss<<"s" ;
251         } else {
252                 if(ps>0) {
253                         ret << ss<<".";
254                         if(ps<10) { ret <<"0"; }
255                         ret <<ps<<"s" ;
256                 } else {
257                         ret << ss<<"s" ;
258                 }
259         }
260         return ret.str();
261 }
262
263 //! helper to check if a bounding box was specified in the right way
264 bool checkBoundingBox(ntlVec3Gfx s, ntlVec3Gfx e, string checker) {
265         if( (s[0]>e[0]) ||
266                         (s[1]>e[1]) ||
267                         (s[2]>e[2]) ) {
268                 errFatal("checkBoundingBox","Check by '"<<checker<<"' for BB "<<s<<":"<<e<<" failed! Aborting...",SIMWORLD_INITERROR);
269                 return 1;
270         }
271         return 0;
272 }
273
274
275
276 //-----------------------------------------------------------------------------
277 // debug message output
278
279 static string col_black ( "\033[0;30m");
280 static string col_dark_gray ( "\033[1;30m");
281 static string col_bright_gray ( "\033[0;37m");
282 static string col_red ( "\033[0;31m");
283 static string col_bright_red ( "\033[1;31m");
284 static string col_green ( "\033[0;32m");
285 static string col_bright_green ( "\033[1;32m");
286 static string col_bright_yellow ( "\033[1;33m");
287 static string col_yellow ( "\033[0;33m");
288 static string col_cyan ( "\033[0;36m");
289 static string col_bright_cyan ( "\033[1;36m");
290 static string col_purple ( "\033[0;35m");
291 static string col_bright_purple ( "\033[1;35m");
292 static string col_neutral ( "\033[0m");
293 static string col_std = col_bright_gray;
294
295 std::ostringstream globOutstr;
296 bool               globOutstrForce=false;
297 #define DM_NONE      100
298 void messageOutputForce(string from) {
299         bool org = globOutstrForce;
300         globOutstrForce = true;
301         messageOutputFunc(from, DM_NONE, "\n", 0);
302         globOutstrForce = org;
303 }
304
305 void messageOutputFunc(string from, int id, string msg, myTime_t interval) {
306         // fast skip
307         if((id!=DM_FATAL)&&(gDebugLevel<=0)) return;
308
309         if(interval>0) {
310                 myTime_t currTime = getTime();
311                 if((currTime - globalIntervalTime)>interval) {
312                         globalIntervalTime = getTime();
313                 } else {
314                         return;
315                 }
316         }
317
318         // colors off?
319         if( (globalColorSetting == -1) || // off for e.g. win32 
320                   ((globalColorSetting==1) && ((id==DM_FATAL)||( getenv("ELBEEM_NOCOLOROUT") )) )
321                 ) {
322                 // only reset once
323                 col_std = col_black = col_dark_gray = col_bright_gray =  
324                 col_red =  col_bright_red =  col_green =  
325                 col_bright_green =  col_bright_yellow =  
326                 col_yellow =  col_cyan =  col_bright_cyan =  
327                 col_purple =  col_bright_purple =  col_neutral =  "";
328                 globalColorSetting = 0;
329         }
330
331         std::ostringstream sout;
332         if(id==DM_DIRECT) {
333                 sout << msg;
334         } else {
335                 sout << col_cyan<< from;
336                 switch(id) {
337                         case DM_MSG:
338                                 sout << col_std << " message:";
339                                 break;
340                         case DM_NOTIFY:
341                                 sout << col_bright_cyan << " note:" << col_std;
342                                 break;
343                         case DM_IMPORTANT:
344                                 sout << col_yellow << " important:" << col_std;
345                                 break;
346                         case DM_WARNING:
347                                 sout << col_bright_red << " warning:" << col_std;
348                                 break;
349                         case DM_ERROR:
350                                 sout << col_red << " error:" << col_red;
351                                 break;
352                         case DM_FATAL:
353                                 sout << col_red << " fatal("<<gElbeemState<<"):" << col_red;
354                                 break;
355                         case DM_NONE:
356                                 // only internal debugging msgs
357                                 break;
358                         default:
359                                 // this shouldnt happen...
360                                 sout << col_red << " --- messageOutputFunc error: invalid id ("<<id<<") --- aborting... \n\n" << col_std;
361                                 break;
362                 }
363                 sout <<" "<< msg << col_std;
364         }
365
366         if(id==DM_FATAL) {
367                 strncpy(gElbeemErrorString,sout.str().c_str(), 256);
368                 // dont print?
369                 if(gDebugLevel==0) return;
370                 sout << "\n"; // add newline for output
371         }
372
373         // determine output - file==1/stdout==0 / globstr==2
374         char filen[256];
375         strcpy(filen,"debug_unini.txt");
376         int fileout = false;
377 #if ELBEEM_MPI==1
378         std::ostringstream mpin;
379         if(glob_mpindex>=0) {
380                 mpin << "elbeem_log_"<< glob_mpindex <<".txt";
381         } else {
382                 mpin << "elbeem_log_ini.txt";
383         }
384         fileout = 1;
385         strncpy(filen, mpin.str().c_str(),255); filen[255]='\0';
386 #else
387         strncpy(filen, "elbeem_debug_log.txt",255);
388 #endif
389
390 #ifdef WIN32
391         // windows causes trouble with direct output
392         fileout = 1;
393 #endif // WIN32
394
395 #if PARALLEL==1
396         fileout = 2;// buffer out, switch off again...
397         if(globOutstrForce) fileout=1;
398 #endif
399         if(getenv("ELBEEM_FORCESTDOUT")) {
400                 fileout = 0;// always direct out
401         }
402         //fprintf(stdout,"out deb %d, %d, '%s',l%d \n",globOutstrForce,fileout, filen, globOutstr.str().size() );
403
404 #if PARALLEL==1
405 #pragma omp critical 
406 #endif // PARALLEL==1
407         {
408         if(fileout==1) {
409                 // debug level is >0 anyway, so write to file...
410                 FILE *logf = fopen(filen,"a+");
411                 // dont complain anymore here...
412                 if(logf) {
413                         if(globOutstrForce) {
414                                 fprintf(logf, "%s",globOutstr.str().c_str() );
415                                 globOutstr.str(""); // reset
416                         }
417                         fprintf(logf, "%s",sout.str().c_str() );
418                         fclose(logf);
419                 }
420         } else if(fileout==2) {
421                         globOutstr << sout.str();
422         } else {
423                 // normal stdout output
424                 fprintf(stdout, "%s",sout.str().c_str() );
425                 if(id!=DM_DIRECT) fflush(stdout); 
426         }
427         } // omp crit
428 }
429
430 // helper functions from external program using elbeem lib (e.g. Blender)
431 /* set gDebugLevel according to env. var */
432 extern "C" 
433 void elbeemCheckDebugEnv(void) {
434         const char *strEnvName = "BLENDER_ELBEEMDEBUG";
435         const char *strEnvName2 = "ELBEEM_DEBUGLEVEL";
436         if(globalFirstEnvCheck) return;
437
438         if(getenv(strEnvName)) {
439                 gDebugLevel = atoi(getenv(strEnvName));
440                 if(gDebugLevel>0) debMsgStd("performElbeemSimulation",DM_NOTIFY,"Using envvar '"<<strEnvName<<"'='"<<getenv(strEnvName)<<"', debugLevel set to: "<<gDebugLevel<<"\n", 1);
441         }
442         if(getenv(strEnvName2)) {
443                 gDebugLevel = atoi(getenv(strEnvName2));
444                 if(gDebugLevel>0) debMsgStd("performElbeemSimulation",DM_NOTIFY,"Using envvar '"<<strEnvName2<<"'='"<<getenv(strEnvName2)<<"', debugLevel set to: "<<gDebugLevel<<"\n", 1);
445         }
446         if(gDebugLevel< 0) gDebugLevel =  0;
447         if(gDebugLevel>10) gDebugLevel =  0; // only use valid values
448         globalFirstEnvCheck = 1;
449 }
450
451 /* elbeem debug output function */
452 extern "C" 
453 void elbeemDebugOut(char *msg) {
454         elbeemCheckDebugEnv();
455         // external messages default to debug level 5...
456         if(gDebugLevel<5) return;
457         // delegate to messageOutputFunc
458         messageOutputFunc("[External]",DM_MSG,msg,0);
459 }
460
461 /* set elbeem debug output level (0=off to 10=full on) */
462 extern "C" 
463 void elbeemSetDebugLevel(int level) {
464         if(level<0)  level=0;
465         if(level>10) level=10;
466         gDebugLevel=level;
467 }
468
469
470 /* estimate how much memory a given setup will require */
471 #include "solver_interface.h"
472
473 extern "C" 
474 double elbeemEstimateMemreq(int res, 
475                 float sx, float sy, float sz,
476                 int refine, char *retstr) {
477         int resx = res, resy = res, resz = res;
478         // dont use real coords, just place from 0.0 to sizeXYZ
479         ntlVec3Gfx vgs(0.0), vge(sx,sy,sz);
480         initGridSizes( resx,resy,resz, vgs,vge, refine, 0);
481
482         double memreq = -1.0;
483         string memreqStr("");   
484         // ignore farfield for now...
485         calculateMemreqEstimate(resx,resy,resz, refine, 0., &memreq, &memreqStr );
486
487         if(retstr) { 
488                 // copy at max. 32 characters
489                 strncpy(retstr, memreqStr.c_str(), 32 );
490                 retstr[31] = '\0';
491         }
492         return memreq;
493 }
494
495
496