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