Move \ingroup onto same line to be more compact and make it clear the file is in the group.
498 lines
13 KiB
C++
498 lines
13 KiB
C++
/** \file \ingroup elbeem
|
|
*/
|
|
/******************************************************************************
|
|
*
|
|
* El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
|
|
* Copyright 2003-2006 Nils Thuerey
|
|
*
|
|
* Global C style utility funcions
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#ifdef WIN32
|
|
// for timing
|
|
#include <windows.h>
|
|
#else
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <sys/times.h>
|
|
#endif
|
|
|
|
#include "utilities.h"
|
|
|
|
#ifndef NOPNG
|
|
#ifdef WIN32
|
|
#include "png.h"
|
|
#else
|
|
#include <png.h>
|
|
#endif
|
|
#endif // NOPNG
|
|
#include <zlib.h>
|
|
|
|
// global debug level
|
|
#ifdef DEBUG
|
|
int gDebugLevel = DEBUG;
|
|
#else // DEBUG
|
|
int gDebugLevel = 0;
|
|
#endif // DEBUG
|
|
|
|
// global world state, acces with get/setElbeemState
|
|
int gElbeemState = SIMWORLD_INVALID;
|
|
|
|
// access global state of elbeem simulator
|
|
void setElbeemState(int set) {
|
|
gElbeemState = set;
|
|
}
|
|
int getElbeemState(void) {
|
|
return gElbeemState;
|
|
}
|
|
int isSimworldOk(void) {
|
|
return (getElbeemState() >=0);
|
|
}
|
|
|
|
// last error as string, acces with get/setElbeemErrorString
|
|
char gElbeemErrorString[256] = {'-','\0' };
|
|
|
|
// access elbeem simulator error string
|
|
void setElbeemErrorString(const char* set) {
|
|
strncpy(gElbeemErrorString, set, 256);
|
|
}
|
|
char* getElbeemErrorString(void) { return gElbeemErrorString; }
|
|
|
|
|
|
//! for interval debugging output
|
|
myTime_t globalIntervalTime = 0;
|
|
//! color output setting for messages (0==off, else on)
|
|
#ifdef WIN32
|
|
// switch off first call
|
|
#define DEF_globalColorSetting -1
|
|
#else // WIN32
|
|
// linux etc., on by default
|
|
#define DEF_globalColorSetting 1
|
|
#endif // WIN32
|
|
int globalColorSetting = DEF_globalColorSetting; // linux etc., on by default
|
|
int globalFirstEnvCheck = 0;
|
|
void resetGlobalColorSetting() { globalColorSetting = DEF_globalColorSetting; }
|
|
|
|
// global string for formatting vector output, TODO test!?
|
|
const char *globVecFormatStr = "V[%f,%f,%f]";
|
|
|
|
|
|
// global mp on/off switch
|
|
bool glob_mpactive = false;
|
|
// global access to mpi index, for debugging (e.g. in utilities.cpp)
|
|
int glob_mpnum = -1;
|
|
int glob_mpindex = -1;
|
|
int glob_mppn = -1;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// helper function that converts a string to integer,
|
|
// and returns an alternative value if the conversion fails
|
|
int convertString2Int(const char *str, int alt)
|
|
{
|
|
int val;
|
|
char *endptr;
|
|
bool success=true;
|
|
|
|
val = strtol(str, &endptr, 10);
|
|
if( (str==endptr) ||
|
|
((str!=endptr) && (*endptr != '\0')) ) success = false;
|
|
|
|
if(!success) {
|
|
return alt;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//! helper function that converts a flag field to a readable integer
|
|
string convertFlags2String(int flags) {
|
|
std::ostringstream ret;
|
|
ret <<"(";
|
|
int max = sizeof(int)*8;
|
|
for(int i=0; i<max; i++) {
|
|
if(flags & (1<<31)) ret <<"1";
|
|
else ret<<"0";
|
|
if(i<max-1) {
|
|
//ret << ",";
|
|
if((i%8)==7) ret << " ";
|
|
}
|
|
flags = flags << 1;
|
|
}
|
|
ret <<")";
|
|
return ret.str();
|
|
}
|
|
|
|
#ifndef NOPNG
|
|
//-----------------------------------------------------------------------------
|
|
//! write png image
|
|
int writePng(const char *fileName, unsigned char **rowsp, int w, int h)
|
|
{
|
|
// defaults for elbeem
|
|
const int colortype = PNG_COLOR_TYPE_RGBA;
|
|
const int bitdepth = 8;
|
|
png_structp png_ptr = NULL;
|
|
png_infop info_ptr = NULL;
|
|
png_bytep *rows = rowsp;
|
|
|
|
//FILE *fp = fopen(fileName, "wb");
|
|
FILE *fp = NULL;
|
|
string doing = "open for writing";
|
|
if (!(fp = fopen(fileName, "wb"))) goto fail;
|
|
|
|
if(!png_ptr) {
|
|
doing = "create png write struct";
|
|
if (!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) goto fail;
|
|
}
|
|
if(!info_ptr) {
|
|
doing = "create png info struct";
|
|
if (!(info_ptr = png_create_info_struct(png_ptr))) goto fail;
|
|
}
|
|
|
|
if (setjmp(png_jmpbuf(png_ptr))) goto fail;
|
|
doing = "init IO";
|
|
png_init_io(png_ptr, fp);
|
|
doing = "write header";
|
|
png_set_IHDR(png_ptr, info_ptr, w, h, bitdepth, colortype, PNG_INTERLACE_NONE,
|
|
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
|
doing = "write info";
|
|
png_write_info(png_ptr, info_ptr);
|
|
doing = "write image";
|
|
png_write_image(png_ptr, rows);
|
|
doing = "write end";
|
|
png_write_end(png_ptr, NULL);
|
|
doing = "write destroy structs";
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
|
|
fclose( fp );
|
|
return 0;
|
|
|
|
fail:
|
|
errMsg("writePng","Write_png: could not "<<doing<<" !");
|
|
if(fp) fclose( fp );
|
|
if(png_ptr || info_ptr) png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
return -1;
|
|
}
|
|
#else // NOPNG
|
|
// fallback - write ppm
|
|
int writePng(const char *fileName, unsigned char **rowsp, int w, int h)
|
|
{
|
|
gzFile gzf;
|
|
string filentemp(fileName);
|
|
// remove suffix
|
|
if((filentemp.length()>4) && (filentemp[filentemp.length()-4]=='.')) {
|
|
filentemp[filentemp.length()-4] = '\0';
|
|
}
|
|
std::ostringstream filennew;
|
|
filennew << filentemp.c_str();
|
|
filennew << ".ppm.gz";
|
|
|
|
gzf = gzopen(filennew.str().c_str(), "wb9");
|
|
if(!gzf) goto fail;
|
|
|
|
gzprintf(gzf,"P6\n%d %d\n255\n",w,h);
|
|
// output binary pixels
|
|
for(int j=0;j<h;j++) {
|
|
for(int i=0;i<h;i++) {
|
|
// remove alpha values
|
|
gzwrite(gzf,&rowsp[j][i*4],3);
|
|
}
|
|
}
|
|
|
|
gzclose( gzf );
|
|
errMsg("writePng/ppm","Write_png/ppm: wrote to "<<filennew.str()<<".");
|
|
return 0;
|
|
|
|
fail:
|
|
errMsg("writePng/ppm","Write_png/ppm: could not write to "<<filennew.str()<<" !");
|
|
return -1;
|
|
}
|
|
#endif // NOPNG
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// helper function to determine current time
|
|
myTime_t getTime()
|
|
{
|
|
myTime_t ret = 0;
|
|
#ifdef WIN32
|
|
LARGE_INTEGER liTimerFrequency;
|
|
QueryPerformanceFrequency(&liTimerFrequency);
|
|
LARGE_INTEGER liLastTime;
|
|
QueryPerformanceCounter(&liLastTime);
|
|
ret = (INT)( ((double)liLastTime.QuadPart / liTimerFrequency.QuadPart)*1000 ); // - mFirstTime;
|
|
#else
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
tz.tz_minuteswest = 0;
|
|
tz.tz_dsttime = 0;
|
|
gettimeofday(&tv,&tz);
|
|
ret = (tv.tv_sec*1000)+(tv.tv_usec/1000); //-mFirstTime;
|
|
#endif
|
|
return (myTime_t)ret;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// convert time to readable string
|
|
string getTimeString(myTime_t usecs) {
|
|
std::ostringstream ret;
|
|
//myTime_t us = usecs % 1000;
|
|
myTime_t ms = (myTime_t)( (double)usecs / (60.0*1000.0) );
|
|
myTime_t ss = (myTime_t)( ((double)usecs / 1000.0) - ((double)ms*60.0) );
|
|
int ps = (int)( ((double)usecs - (double)ss*1000.0)/10.0 );
|
|
|
|
//ret.setf(ios::showpoint|ios::fixed);
|
|
//ret.precision(5); ret.width(7);
|
|
|
|
if(ms>0) {
|
|
ret << ms<<"m"<< ss<<"s" ;
|
|
} else {
|
|
if(ps>0) {
|
|
ret << ss<<".";
|
|
if(ps<10) { ret <<"0"; }
|
|
ret <<ps<<"s" ;
|
|
} else {
|
|
ret << ss<<"s" ;
|
|
}
|
|
}
|
|
return ret.str();
|
|
}
|
|
|
|
//! helper to check if a bounding box was specified in the right way
|
|
bool checkBoundingBox(ntlVec3Gfx s, ntlVec3Gfx e, string checker) {
|
|
if( (s[0]>e[0]) ||
|
|
(s[1]>e[1]) ||
|
|
(s[2]>e[2]) ) {
|
|
errFatal("checkBoundingBox","Check by '"<<checker<<"' for BB "<<s<<":"<<e<<" failed! Aborting...",SIMWORLD_INITERROR);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// debug message output
|
|
|
|
static string col_black ( "\033[0;30m");
|
|
static string col_dark_gray ( "\033[1;30m");
|
|
static string col_bright_gray ( "\033[0;37m");
|
|
static string col_red ( "\033[0;31m");
|
|
static string col_bright_red ( "\033[1;31m");
|
|
static string col_green ( "\033[0;32m");
|
|
static string col_bright_green ( "\033[1;32m");
|
|
static string col_bright_yellow ( "\033[1;33m");
|
|
static string col_yellow ( "\033[0;33m");
|
|
static string col_cyan ( "\033[0;36m");
|
|
static string col_bright_cyan ( "\033[1;36m");
|
|
static string col_purple ( "\033[0;35m");
|
|
static string col_bright_purple ( "\033[1;35m");
|
|
static string col_neutral ( "\033[0m");
|
|
static string col_std = col_bright_gray;
|
|
|
|
std::ostringstream globOutstr;
|
|
bool globOutstrForce=false;
|
|
#define DM_NONE 100
|
|
void messageOutputForce(string from) {
|
|
bool org = globOutstrForce;
|
|
globOutstrForce = true;
|
|
messageOutputFunc(from, DM_NONE, "\n", 0);
|
|
globOutstrForce = org;
|
|
}
|
|
|
|
void messageOutputFunc(string from, int id, string msg, myTime_t interval) {
|
|
// fast skip
|
|
if((id!=DM_FATAL)&&(gDebugLevel<=0)) return;
|
|
|
|
if(interval>0) {
|
|
myTime_t currTime = getTime();
|
|
if((currTime - globalIntervalTime)>interval) {
|
|
globalIntervalTime = getTime();
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// colors off?
|
|
if( (globalColorSetting == -1) || // off for e.g. win32
|
|
((globalColorSetting==1) && ((id==DM_FATAL)||( getenv("ELBEEM_NOCOLOROUT") )) )
|
|
) {
|
|
// only reset once
|
|
col_std = col_black = col_dark_gray = col_bright_gray =
|
|
col_red = col_bright_red = col_green =
|
|
col_bright_green = col_bright_yellow =
|
|
col_yellow = col_cyan = col_bright_cyan =
|
|
col_purple = col_bright_purple = col_neutral = "";
|
|
globalColorSetting = 0;
|
|
}
|
|
|
|
std::ostringstream sout;
|
|
if(id==DM_DIRECT) {
|
|
sout << msg;
|
|
} else {
|
|
sout << col_cyan<< from;
|
|
switch(id) {
|
|
case DM_MSG:
|
|
sout << col_std << " message:";
|
|
break;
|
|
case DM_NOTIFY:
|
|
sout << col_bright_cyan << " note:" << col_std;
|
|
break;
|
|
case DM_IMPORTANT:
|
|
sout << col_yellow << " important:" << col_std;
|
|
break;
|
|
case DM_WARNING:
|
|
sout << col_bright_red << " warning:" << col_std;
|
|
break;
|
|
case DM_ERROR:
|
|
sout << col_red << " error:" << col_red;
|
|
break;
|
|
case DM_FATAL:
|
|
sout << col_red << " fatal("<<gElbeemState<<"):" << col_red;
|
|
break;
|
|
case DM_NONE:
|
|
// only internal debugging msgs
|
|
break;
|
|
default:
|
|
// this shouldnt happen...
|
|
sout << col_red << " --- messageOutputFunc error: invalid id ("<<id<<") --- aborting... \n\n" << col_std;
|
|
break;
|
|
}
|
|
sout <<" "<< msg << col_std;
|
|
}
|
|
|
|
if(id==DM_FATAL) {
|
|
strncpy(gElbeemErrorString,sout.str().c_str(), 256);
|
|
// dont print?
|
|
if(gDebugLevel==0) return;
|
|
sout << "\n"; // add newline for output
|
|
}
|
|
|
|
// determine output - file==1/stdout==0 / globstr==2
|
|
char filen[256];
|
|
strcpy(filen,"debug_unini.txt");
|
|
int fileout = false;
|
|
#if ELBEEM_MPI==1
|
|
std::ostringstream mpin;
|
|
if(glob_mpindex>=0) {
|
|
mpin << "elbeem_log_"<< glob_mpindex <<".txt";
|
|
} else {
|
|
mpin << "elbeem_log_ini.txt";
|
|
}
|
|
fileout = 1;
|
|
strncpy(filen, mpin.str().c_str(),255); filen[255]='\0';
|
|
#else
|
|
strncpy(filen, "elbeem_debug_log.txt",255);
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
// windows causes trouble with direct output
|
|
fileout = 1;
|
|
#endif // WIN32
|
|
|
|
#if PARALLEL==1
|
|
fileout = 2;// buffer out, switch off again...
|
|
if(globOutstrForce) fileout=1;
|
|
#endif
|
|
if(getenv("ELBEEM_FORCESTDOUT")) {
|
|
fileout = 0;// always direct out
|
|
}
|
|
//fprintf(stdout,"out deb %d, %d, '%s',l%d \n",globOutstrForce,fileout, filen, globOutstr.str().size() );
|
|
|
|
#if PARALLEL==1
|
|
#pragma omp critical
|
|
#endif // PARALLEL==1
|
|
{
|
|
if(fileout==1) {
|
|
// debug level is >0 anyway, so write to file...
|
|
FILE *logf = fopen(filen,"a+");
|
|
// dont complain anymore here...
|
|
if(logf) {
|
|
if(globOutstrForce) {
|
|
fprintf(logf, "%s",globOutstr.str().c_str() );
|
|
globOutstr.str(""); // reset
|
|
}
|
|
fprintf(logf, "%s",sout.str().c_str() );
|
|
fclose(logf);
|
|
}
|
|
} else if(fileout==2) {
|
|
globOutstr << sout.str();
|
|
} else {
|
|
// normal stdout output
|
|
fprintf(stdout, "%s",sout.str().c_str() );
|
|
if(id!=DM_DIRECT) fflush(stdout);
|
|
}
|
|
} // omp crit
|
|
}
|
|
|
|
// helper functions from external program using elbeem lib (e.g. Blender)
|
|
/* set gDebugLevel according to env. var */
|
|
extern "C"
|
|
void elbeemCheckDebugEnv(void) {
|
|
const char *strEnvName = "BLENDER_ELBEEMDEBUG";
|
|
const char *strEnvName2 = "ELBEEM_DEBUGLEVEL";
|
|
if(globalFirstEnvCheck) return;
|
|
|
|
if(getenv(strEnvName)) {
|
|
gDebugLevel = atoi(getenv(strEnvName));
|
|
if(gDebugLevel>0) debMsgStd("performElbeemSimulation",DM_NOTIFY,"Using envvar '"<<strEnvName<<"'='"<<getenv(strEnvName)<<"', debugLevel set to: "<<gDebugLevel<<"\n", 1);
|
|
}
|
|
if(getenv(strEnvName2)) {
|
|
gDebugLevel = atoi(getenv(strEnvName2));
|
|
if(gDebugLevel>0) debMsgStd("performElbeemSimulation",DM_NOTIFY,"Using envvar '"<<strEnvName2<<"'='"<<getenv(strEnvName2)<<"', debugLevel set to: "<<gDebugLevel<<"\n", 1);
|
|
}
|
|
if(gDebugLevel< 0) gDebugLevel = 0;
|
|
if(gDebugLevel>10) gDebugLevel = 0; // only use valid values
|
|
globalFirstEnvCheck = 1;
|
|
}
|
|
|
|
/* elbeem debug output function */
|
|
extern "C"
|
|
void elbeemDebugOut(char *msg) {
|
|
elbeemCheckDebugEnv();
|
|
// external messages default to debug level 5...
|
|
if(gDebugLevel<5) return;
|
|
// delegate to messageOutputFunc
|
|
messageOutputFunc("[External]",DM_MSG,msg,0);
|
|
}
|
|
|
|
/* set elbeem debug output level (0=off to 10=full on) */
|
|
extern "C"
|
|
void elbeemSetDebugLevel(int level) {
|
|
if(level<0) level=0;
|
|
if(level>10) level=10;
|
|
gDebugLevel=level;
|
|
}
|
|
|
|
|
|
/* estimate how much memory a given setup will require */
|
|
#include "solver_interface.h"
|
|
|
|
extern "C"
|
|
double elbeemEstimateMemreq(int res,
|
|
float sx, float sy, float sz,
|
|
int refine, char *retstr) {
|
|
int resx = res, resy = res, resz = res;
|
|
// dont use real coords, just place from 0.0 to sizeXYZ
|
|
ntlVec3Gfx vgs(0.0), vge(sx,sy,sz);
|
|
initGridSizes( resx,resy,resz, vgs,vge, refine, 0);
|
|
|
|
double memreq = -1.0;
|
|
string memreqStr("");
|
|
// ignore farfield for now...
|
|
calculateMemreqEstimate(resx,resy,resz, refine, 0., &memreq, NULL, &memreqStr );
|
|
|
|
if(retstr) {
|
|
// copy at max. 32 characters
|
|
strncpy(retstr, memreqStr.c_str(), 32 );
|
|
retstr[31] = '\0';
|
|
}
|
|
return memreq;
|
|
}
|
|
|
|
|
|
|