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