/** \file elbeem/intern/solver_interface.cpp * \ingroup elbeem */ /****************************************************************************** * * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method * All code distributed as part of El'Beem is covered by the version 2 of the * GNU General Public License. See the file COPYING for details. * Copyright 2003-2006 Nils Thuerey * * Combined 2D/3D Lattice Boltzmann Interface Class * contains stuff to be statically compiled * *****************************************************************************/ /* LBM Files */ #include "ntl_matrices.h" #include "solver_interface.h" #include "ntl_ray.h" #include "ntl_world.h" #include "elbeem.h" /****************************************************************************** * Interface Constructor *****************************************************************************/ LbmSolverInterface::LbmSolverInterface() : mPanic( false ), mSizex(10), mSizey(10), mSizez(10), mAllfluid(false), mStepCnt( 0 ), mFixMass( 0.0 ), mOmega( 1.0 ), mGravity(0.0), mSurfaceTension( 0.0 ), mBoundaryEast( (CellFlagType)(CFBnd) ),mBoundaryWest( (CellFlagType)(CFBnd) ),mBoundaryNorth( (CellFlagType)(CFBnd) ), mBoundarySouth( (CellFlagType)(CFBnd) ),mBoundaryTop( (CellFlagType)(CFBnd) ),mBoundaryBottom( (CellFlagType)(CFBnd) ), mInitDone( false ), mInitDensityGradient( false ), mpSifAttrs( NULL ), mpSifSwsAttrs(NULL), mpParam( NULL ), mpParticles(NULL), mNumParticlesLost(0), mNumInvalidDfs(0), mNumFilledCells(0), mNumEmptiedCells(0), mNumUsedCells(0), mMLSUPS(0), mDebugVelScale( 0.01 ), mNodeInfoString("+"), mvGeoStart(-1.0), mvGeoEnd(1.0), mpSimTrafo(NULL), mAccurateGeoinit(0), mName("lbm_default") , mpIso( NULL ), mIsoValue(0.499), mSilent(false) , mLbmInitId(1) , mpGiTree( NULL ), mpGiObjects( NULL ), mGiObjInside(), mpGlob( NULL ), mRefinementDesired(0), mOutputSurfacePreview(0), mPreviewFactor(0.25), mSmoothSurface(1.0), mSmoothNormals(1.0), mIsoSubdivs(1), mPartGenProb(0.), mDumpVelocities(false), mMarkedCells(), mMarkedCellIndex(0), mDomainBound("noslip"), mDomainPartSlipValue(0.1), mFarFieldSize(0.), mPartDropMassSub(0.1), // good default mPartUsePhysModel(false), mTForceStrength(0.0), mCppfStage(0), mDumpRawText(false), mDumpRawBinary(false), mDumpRawBinaryZip(true) #if PARALLEL==1 , mNumOMPThreads(1) #endif // PARALLEL==1 { #if ELBEEM_PLUGIN==1 if(gDebugLevel<=1) setSilent(true); #endif mpSimTrafo = new ntlMat4Gfx(0.0); mpSimTrafo->initId(); if(getenv("ELBEEM_RAWDEBUGDUMP")) { debMsgStd("LbmSolverInterface",DM_MSG,"Using env var ELBEEM_RAWDEBUGDUMP, mDumpRawText on",2); mDumpRawText = true; } if(getenv("ELBEEM_BINDEBUGDUMP")) { debMsgStd("LbmSolverInterface",DM_MSG,"Using env var ELBEEM_RAWDEBUGDUMP, mDumpRawText on",2); mDumpRawBinary = true; } } LbmSolverInterface::~LbmSolverInterface() { if(mpSimTrafo) delete mpSimTrafo; } /****************************************************************************** * initialize correct grid sizes given a geometric bounding box * and desired grid resolutions, all params except maxrefine * will be modified *****************************************************************************/ void initGridSizes(int &sizex, int &sizey, int &sizez, ntlVec3Gfx &geoStart, ntlVec3Gfx &geoEnd, int mMaxRefine, bool parallel) { // fix size inits to force cubic cells and mult4 level dimensions const int debugGridsizeInit = 1; if(debugGridsizeInit) debMsgStd("initGridSizes",DM_MSG,"Called - size X:"<maxGridSize) maxGridSize = sizey; if(sizez>maxGridSize) maxGridSize = sizez; LbmFloat maxGeoSize = (geoEnd[0]-geoStart[0]); // get max size if((geoEnd[1]-geoStart[1])>maxGeoSize) maxGeoSize = (geoEnd[1]-geoStart[1]); if((geoEnd[2]-geoStart[2])>maxGeoSize) maxGeoSize = (geoEnd[2]-geoStart[2]); // FIXME better divide max geo size by corresponding resolution rather than max? no prob for rx==ry==rz though LbmFloat cellSize = (maxGeoSize / (LbmFloat)maxGridSize); if(debugGridsizeInit) debMsgStd("initGridSizes",DM_MSG,"Start:"<=0; i--) { currResx /= 2.0; currResy /= 2.0; currResz /= 2.0; rcellSize = ((currResz*currResy*currResx) *ddTotalNum); memCnt += (double)(sizeof(CellFlagType) * (rcellSize/ddTotalNum +4.0) *2.0); memCnt += (double)(sizeof(LbmFloat) * (rcellSize +4.0) *2.0); if(debugMemEst) debMsgStd("calculateMemreqEstimate",DM_MSG,"refine "<0.) { memCnt += (double)( (3*sizeof(int)+sizeof(float)) * ((resx+2)*(resy+2)*(resz+2)) ); } else { // ignore 3 int slices... memCnt += (double)( ( sizeof(float)) * ((resx+2)*(resy+2)*(resz+2)) ); } if(debugMemEst) debMsgStd("calculateMemreqEstimate",DM_MSG,"iso, mc:"<sfac){ memd /= sfac; sizeStr="KB"; } if(memd>sfac){ memd /= sfac; sizeStr="MB"; } if(memd>sfac){ memd /= sfac; sizeStr="GB"; } if(memd>sfac){ memd /= sfac; sizeStr="TB"; } // return values std::ostringstream ret; if(memCnt< 1024.0*1024.0) { // show full MBs ret << (ceil(memd)); } else { // two digits for anything larger than MB ret << (ceil(memd*100.0)/100.0); } ret << " "<< sizeStr; *reqret = memCnt; *reqstr = ret.str(); //debMsgStd("LbmFsgrSolver::initialize",DM_MSG,"Required Grid memory: "<< memd <<" "<< sizeStr<<" ",4); } void LbmSolverInterface::initDomainTrafo(float *mat) { mpSimTrafo->initArrayCheck(mat); } /*******************************************************************************/ /*! parse a boundary flag string */ CellFlagType LbmSolverInterface::readBoundaryFlagInt(string name, int defaultValue, string source,string target, bool needed) { string val = mpSifAttrs->readString(name, "", source, target, needed); if(!strcmp(val.c_str(),"")) { // no value given... return CFEmpty; } if(!strcmp(val.c_str(),"bnd_no")) { return (CellFlagType)( CFBnd ); } if(!strcmp(val.c_str(),"bnd_free")) { return (CellFlagType)( CFBnd ); } if(!strcmp(val.c_str(),"fluid")) { /* might be used for some in/out flow cases */ return (CellFlagType)( CFFluid ); } errMsg("LbmSolverInterface::readBoundaryFlagInt","Invalid value '"<readMat4Gfx("domain_trafo" , (*mpSimTrafo), "ntlBlenderDumper","mpSimTrafo", false, mpSimTrafo ); LbmVec sizeVec(mSizex,mSizey,mSizez); sizeVec = vec2L( mpSifAttrs->readVec3d("size", vec2P(sizeVec), "LbmSolverInterface", "sizeVec", false) ); mSizex = (int)sizeVec[0]; mSizey = (int)sizeVec[1]; mSizez = (int)sizeVec[2]; // param needs size in any case // test solvers might not have mpPara, though if(mpParam) mpParam->setSize(mSizex, mSizey, mSizez ); mInitDensityGradient = mpSifAttrs->readBool("initdensitygradient", mInitDensityGradient,"LbmSolverInterface", "mInitDensityGradient", false); mIsoValue = mpSifAttrs->readFloat("isovalue", mIsoValue, "LbmOptSolver","mIsoValue", false ); mDebugVelScale = mpSifAttrs->readFloat("debugvelscale", mDebugVelScale,"LbmSolverInterface", "mDebugVelScale", false); mNodeInfoString = mpSifAttrs->readString("nodeinfo", mNodeInfoString, "SimulationLbm","mNodeInfoString", false ); mDumpVelocities = mpSifAttrs->readBool("dump_velocities", mDumpVelocities, "SimulationLbm","mDumpVelocities", false ); if(getenv("ELBEEM_DUMPVELOCITIES")) { int get = atoi(getenv("ELBEEM_DUMPVELOCITIES")); if((get==0)||(get==1)) { mDumpVelocities = get; debMsgStd("LbmSolverInterface::parseAttrList",DM_NOTIFY,"Using envvar ELBEEM_DUMPVELOCITIES to set mDumpVelocities to "<readFloat("farfieldsize", mFarFieldSize,"LbmSolverInterface", "mFarFieldSize", false); // old compat float sizeScale = mpSifAttrs->readFloat("test_scale", 0.,"LbmTestdata", "mSizeScale", false); if((mFarFieldSize<=0.)&&(sizeScale>0.)) { mFarFieldSize=sizeScale; errMsg("LbmTestdata","Warning - using mSizeScale..."); } mPartDropMassSub = mpSifAttrs->readFloat("part_dropmass", mPartDropMassSub,"LbmSolverInterface", "mPartDropMassSub", false); mCppfStage = mpSifAttrs->readInt("cppfstage", mCppfStage,"LbmSolverInterface", "mCppfStage", false); mPartGenProb = mpSifAttrs->readFloat("partgenprob", mPartGenProb,"LbmFsgrSolver", "mPartGenProb", false); mPartUsePhysModel = mpSifAttrs->readBool("partusephysmodel", mPartUsePhysModel,"LbmFsgrSolver", "mPartUsePhysModel", false); mIsoSubdivs = mpSifAttrs->readInt("isosubdivs", mIsoSubdivs,"LbmFsgrSolver", "mIsoSubdivs", false); } /*******************************************************************************/ /*! geometry initialization */ /*******************************************************************************/ /*****************************************************************************/ /*! init tree for certain geometry init */ void LbmSolverInterface::initGeoTree() { if(mpGlob == NULL) { errFatal("LbmSolverInterface::initGeoTree","Requires globals!",SIMWORLD_INITERROR); return; } ntlScene *scene = mpGlob->getSimScene(); mpGiObjects = scene->getObjects(); mGiObjInside.resize( mpGiObjects->size() ); mGiObjDistance.resize( mpGiObjects->size() ); mGiObjSecondDist.resize( mpGiObjects->size() ); for(size_t i=0; isize(); i++) { if((*mpGiObjects)[i]->getGeoInitIntersect()) mAccurateGeoinit=true; //debMsgStd("LbmSolverInterface::initGeoTree",DM_MSG,"id:"<getName() <<" gid:"<<(*mpGiObjects)[i]->getGeoInitId(), 9 ); } debMsgStd("LbmSolverInterface::initGeoTree",DM_MSG,"Accurate geo init: "<size() ,10); if(mpGiTree != NULL) delete mpGiTree; char treeFlag = (1<<(this->mLbmInitId+4)); mpGiTree = new ntlTree( # if FSGR_STRICT_DEBUG!=1 15, 8, // TREEwarning - fixed values for depth & maxtriangles here... # else // FSGR_STRICT_DEBUG==1 10, 20, // reduced/slower debugging values # endif // FSGR_STRICT_DEBUG==1 scene, treeFlag ); } /*****************************************************************************/ /*! destroy tree etc. when geometry init done */ void LbmSolverInterface::freeGeoTree() { if(mpGiTree != NULL) { delete mpGiTree; mpGiTree = NULL; } } int globGeoInitDebug = 0; int globGICPIProblems = 0; /*****************************************************************************/ /*! check for a certain flag type at position org */ bool LbmSolverInterface::geoInitCheckPointInside(ntlVec3Gfx org, int flags, int &OId, gfxReal &distance, int shootDir) { // shift ve ctors to avoid rounding errors org += ntlVec3Gfx(0.0001); OId = -1; // select shooting direction ntlVec3Gfx dir = ntlVec3Gfx(1., 0., 0.); if(shootDir==1) dir = ntlVec3Gfx(0., 1., 0.); else if(shootDir==2) dir = ntlVec3Gfx(0., 0., 1.); else if(shootDir==-2) dir = ntlVec3Gfx(0., 0., -1.); else if(shootDir==-1) dir = ntlVec3Gfx(0., -1., 0.); ntlRay ray(org, dir, 0, 1.0, mpGlob); bool done = false; bool inside = false; vector giObjFirstHistSide; giObjFirstHistSide.resize( mpGiObjects->size() ); for(size_t i=0; iintersectX(ray,distance,normal, triIns, flags, true); else mpGiTree->intersect(ray,distance,normal, triIns, flags, true); if(triIns) { ntlVec3Gfx norg = ray.getOrigin() + ray.getDirection()*distance; LbmFloat orientation = dot(normal, dir); OId = triIns->getObjectId(); if(orientation<=0.0) { // outside hit normal *= -1.0; mGiObjInside[OId]++; if(giObjFirstHistSide[OId]==0) giObjFirstHistSide[OId] = 1; if(globGeoInitDebug) errMsg("IIO"," oid:"<0) { bool mess = false; if((mGiObjInside[i]%2)==1) { if(giObjFirstHistSide[i] != -1) mess=true; } else { if(giObjFirstHistSide[i] != 1) mess=true; } if(mess) { //errMsg("IIIproblem","At "<0.0)) { if( (distance<0.0) || // first intersection -> good ((distance>0.0)&&(distance>mGiObjDistance[i])) // more than one intersection -> use closest one ) { distance = mGiObjDistance[i]; OId = i; inside = true; } } } if(!inside) { distance = firstHit; OId = firstOId; } if(globGeoInitDebug) errMsg("CHIII","i"<intersectX(ray,distance,normal, triIns, flags, true); else mpGiTree->intersect(ray,distance,normal, triIns, flags, true); if(triIns) { // check outside intersect LbmFloat orientation = dot(normal, dir); if(orientation<=0.0) return false; OId = triIns->getObjectId(); return true; } return false; } } bool LbmSolverInterface::geoInitCheckPointInside(ntlVec3Gfx org, ntlVec3Gfx dir, int flags, int &OId, gfxReal &distance, const gfxReal halfCellsize, bool &thinHit, bool recurse) { // shift ve ctors to avoid rounding errors org += ntlVec3Gfx(0.0001); //? OId = -1; ntlRay ray(org, dir, 0, 1.0, mpGlob); //int insCnt = 0; bool done = false; bool inside = false; for(size_t i=0; iintersect(ray,distance,normal, triIns, flags, true); if(triIns) { ntlVec3Gfx norg = ray.getOrigin() + ray.getDirection()*distance; LbmFloat orientation = dot(normal, dir); OId = triIns->getObjectId(); if(orientation<=0.0) { // outside hit normal *= -1.0; //mGiObjDistance[OId] = -1.0; //errMsg("IIO"," oid:"<0.0)) { if( (distance<0.0) || // first intersection -> good ((distance>0.0)&&(distance>mGiObjDistance[i])) // more than one intersection -> use closest one ) { distance = mGiObjDistance[i]; OId = i; inside = true; } } } // now check for thin hits if(!inside) { distance = -1.0; for(size_t i=0; i=2)&&(mGiObjDistance[i]>0.0)&&(mGiObjSecondDist[i]>0.0)&& (mGiObjDistance[i]<1.0*halfCellsize)&&(mGiObjSecondDist[i]<2.0*halfCellsize) ) { if( (distance<0.0) || // first intersection -> good ((distance>0.0)&&(distance>mGiObjDistance[i])) // more than one intersection -> use closest one ) { distance = mGiObjDistance[i]; OId = i; inside = true; thinHit = true; } } } } if(!inside) { // check for hit in this cell, opposite to current dir (only recurse once) if(recurse) { gfxReal r_distance; int r_OId; bool ret = geoInitCheckPointInside(org, dir*-1.0, flags, r_OId, r_distance, halfCellsize, thinHit, false); if((ret)&&(thinHit)) { OId = r_OId; distance = 0.0; return true; } } } // really no hit... if(!inside) { distance = firstHit; OId = firstOId; /*if((mGiObjDistance[OId]>0.0)&&(mGiObjSecondDist[OId]>0.0)) { const gfxReal thisdist = mGiObjSecondDist[OId]-mGiObjDistance[OId]; // dont walk over this cell... if(thisdistintersect(ray,distance,normal, triIns, flags, true); if(triIns) { // check outside intersect LbmFloat orientation = dot(normal, dir); if(orientation<=0.0) return false; OId = triIns->getObjectId(); return true; } return false; } } /*****************************************************************************/ ntlVec3Gfx LbmSolverInterface::getGeoMaxMovementVelocity(LbmFloat simtime, LbmFloat stepsize) { ntlVec3Gfx max(0.0); if(mpGlob == NULL) return max; // mpGiObjects has to be inited here... for(int i=0; i< (int)mpGiObjects->size(); i++) { //errMsg("LbmSolverInterface::getGeoMaxMovementVelocity","i="<getName() ); // DEBUG if( (*mpGiObjects)[i]->getGeoInitType() & (FGI_FLUID|FGI_MBNDINFLOW) ){ //ntlVec3Gfx objMaxVel = obj->calculateMaxVel(sourceTime,targetTime); ntlVec3Gfx orgvel = (*mpGiObjects)[i]->calculateMaxVel( simtime, simtime+stepsize ); if( normNoSqrt(orgvel) > normNoSqrt(max) ) { max = orgvel; } //errMsg("MVT","i"<getInitialVelocity(simtime)<<" o"<getInitialVelocity(simtime); if( normNoSqrt(inivel) > normNoSqrt(max) ) { max = inivel; } } } errMsg("LbmSolverInterface::getGeoMaxMovementVelocity", "max="<< max ); // DEBUG return max; } /*******************************************************************************/ /*! cell iteration functions */ /*******************************************************************************/ /*****************************************************************************/ //! add cell to mMarkedCells list void LbmSolverInterface::addCellToMarkedList( CellIdentifierInterface *cid ) { for(size_t i=0; iequal(cid) ) return; //mMarkedCells[i]->setEnd(false); } mMarkedCells.push_back( cid ); //cid->setEnd(true); } /*****************************************************************************/ //! marked cell iteration methods CellIdentifierInterface* LbmSolverInterface::markedGetFirstCell( ) { if(mMarkedCells.size() > 0){ return mMarkedCells[0]; } return NULL; } CellIdentifierInterface* LbmSolverInterface::markedAdvanceCell() { mMarkedCellIndex++; if(mMarkedCellIndex>=(int)mMarkedCells.size()) return NULL; return mMarkedCells[mMarkedCellIndex]; } void LbmSolverInterface::markedClearList() { // FIXME free cids? mMarkedCells.clear(); } #if PARALLEL==1 void LbmSolverInterface::setNumOMPThreads(int num_threads) { mNumOMPThreads = num_threads; } #endif // PARALLEL==1 /*******************************************************************************/ /*! string helper functions */ /*******************************************************************************/ // 32k string convertSingleFlag2String(CellFlagType cflag) { CellFlagType flag = cflag; if(flag == CFUnused ) return string("cCFUnused"); if(flag == CFEmpty ) return string("cCFEmpty"); if(flag == CFBnd ) return string("cCFBnd"); if(flag == CFBndNoslip ) return string("cCFBndNoSlip"); if(flag == CFBndFreeslip ) return string("cCFBndFreeSlip"); if(flag == CFBndPartslip ) return string("cCFBndPartSlip"); if(flag == CFNoInterpolSrc ) return string("cCFNoInterpolSrc"); if(flag == CFFluid ) return string("cCFFluid"); if(flag == CFInter ) return string("cCFInter"); if(flag == CFNoNbFluid ) return string("cCFNoNbFluid"); if(flag == CFNoNbEmpty ) return string("cCFNoNbEmpty"); if(flag == CFNoDelete ) return string("cCFNoDelete"); if(flag == CFNoBndFluid ) return string("cCFNoBndFluid"); if(flag == CFGrNorm ) return string("cCFGrNorm"); if(flag == CFGrFromFine ) return string("cCFGrFromFine"); if(flag == CFGrFromCoarse ) return string("cCFGrFromCoarse"); if(flag == CFGrCoarseInited ) return string("cCFGrCoarseInited"); if(flag == CFMbndInflow ) return string("cCFMbndInflow"); if(flag == CFMbndOutflow ) return string("cCFMbndOutflow"); if(flag == CFInvalid ) return string("cfINVALID"); std::ostringstream mult; int val = 0; if(flag != 0) { while(! (flag&1) ) { flag = flag>>1; val++; } } else { val = -1; } if(val>=24) { mult << "cfOID_" << (flag>>24) <<"_TYPE"; } else { mult << "cfUNKNOWN_" << val <<"_TYPE"; } return mult.str(); } //! helper function to convert flag to string (for debuggin) string convertCellFlagType2String( CellFlagType cflag ) { int flag = cflag; const int jmax = sizeof(CellFlagType)*8; bool somefound = false; std::ostringstream mult; mult << "["; for(int j=0; j