/****************************************************************************** * * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method * Copyright 2003-2006 Nils Thuerey * * Standard LBM Factory implementation * *****************************************************************************/ #include "solver_class.h" #include "solver_relax.h" #include "particletracer.h" #include "loop_tools.h" #include /*****************************************************************************/ /*! perform a single LBM step */ /*****************************************************************************/ double globdfcnt; double globdfavg[19]; double globdfmax[19]; double globdfmin[19]; extern int glob_mpindex,glob_mpnum; extern bool globOutstrForce; // simulation object interface void LbmFsgrSolver::step() { stepMain(); } // lbm main step void messageOutputForce(string from); void LbmFsgrSolver::stepMain() { myTime_t timestart = getTime(); initLevelOmegas(); markedClearList(); // DMC clearMarkedCellsList // safety check, counter reset mNumUsedCells = 0; mNumInterdCells = 0; mNumInvIfCells = 0; //debugOutNnl("LbmFsgrSolver::step : "<mMaxNoCells) mMaxNoCells = mNumUsedCells; if(mNumUsedCells0)&&(mInitialMass>0.0)) { LbmFloat mscale = mInitialMass/mCurrentMass; mscale = 1.0; const LbmFloat dchh = 0.001; if(mCurrentMassmInitialMass) mscale = 1.0-dchh; // use mass rescaling? // with float precision this seems to be nonsense... const bool MREnable = false; const int MSInter = 2; static int mscount = 0; if( (MREnable) && ((mLevel[0].lsteps%MSInter)== (MSInter-1)) && ( ABS( (mInitialMass/mCurrentMass)-1.0 ) > 0.01) && ( dsbits & (1<<(mMaxRefine-0)) ) ){ // example: FORCE RESCALE MASS! ini:1843.5, cur:1817.6, f=1.01425 step:22153 levstep:5539 msc:37 // mass rescale MASS RESCALE check errMsg("MDTDD","\n\n"); errMsg("MDTDD","FORCE RESCALE MASS! " <<"ini:"<=0 ; lev--) { //for(int workSet = 0; workSet<=1; workSet++) { int wss = 0; int wse = 1; #if COMPRESSGRIDS==1 if(lev== mMaxRefine) wss = wse = mLevel[lev].setCurr; #endif // COMPRESSGRIDS==1 for(int workSet = wss; workSet<=wse; workSet++) { // COMPRT FSGR_FORIJK1(lev) { if( (RFLAG(lev,i,j,k, workSet) & (CFFluid| CFInter| CFGrFromCoarse| CFGrFromFine| CFGrNorm)) ) { FORDF0 { QCELL(lev, i,j,k,workSet, l) *= mscale; } QCELL(lev, i,j,k,workSet, dMass) *= mscale; QCELL(lev, i,j,k,workSet, dFfrac) *= mscale; } else { continue; } } } mLevel[lev].lmass *= mscale; } } mCurrentMass *= mscale; }// if mass scale test */ else { // use current mass after full step for initial setting if((mMaxRefine>0)&&(mInitialMass<=0.0) && (levsteps == (mMaxRefine+1))) { mInitialMass = mCurrentMass; debMsgStd("MDTDD",DM_NOTIFY,"Second Initial Mass Init: "<checkDumpTextPositions(mSimulationTime); mpParticles->checkTrails(mSimulationTime); } // one of the last things to do - adapt timestep // was in fineAdvance before... if(mTimeAdap) { adaptTimestep(); } // time adaptivity #ifndef WIN32 // good indicator for instabilities if( (!finite(mMxvx)) || (!finite(mMxvy)) || (!finite(mMxvz)) ) { CAUSE_PANIC; } if( (!finite(mCurrentMass)) || (!finite(mCurrentVolume)) ) { CAUSE_PANIC; } #endif // WIN32 // output total step time myTime_t timeend2 = getTime(); double mdelta = (lastMass-mCurrentMass); if(ABS(mdelta)<1e-12) mdelta=0.; double effMLSUPS = ((double)mNumUsedCells / ((timeend2-timestart)/(double)1000.0) ) / (1000000.0); if(mInitDone) { if(effMLSUPS>10000){ effMLSUPS = -1; } else { mAvgMLSUPS += effMLSUPS; mAvgMLSUPSCnt += 1.0; } // track average mlsups } debMsgStd("LbmFsgrSolver::stepMain", DM_MSG, "mmpid:"<mPanic){ FSGR_FORIJK_BOUNDS(mMaxRefine) { \ if(RFLAG(mMaxRefine,i,j,k,mLevel[mMaxRefine].setCurr)&(CFFluid|CFInter)) { \ for(int l=0;lsetFluidVolumeHeight(mFVHeight); } // advance time before timestep change mSimulationTime += mpParam->getTimestep(); // time adaptivity mpParam->setSimulationMaxSpeed( sqrt(mMaxVlen / 1.5) ); //if(mStartSymm) { checkSymmetry("step2"); } // DEBUG if(!mSilent){ debMsgStd("fineAdvance",DM_NOTIFY," stepped from "<cDfNum; l++) { LbmFloat df = QCELL(lev, i,j,k,workSet, l); compRho += df; compUx += (this->dfDvecX[l]*df); compUy += (this->dfDvecY[l]*df); compUz += (this->dfDvecZ[l]*df); } LbmVec u(compUx,compUy,compUz); LbmFloat nu = norm(u); if(nu>maxUlen) { maxU = u; maxUlen = nu; mi=i; mj=j; mk=k; } if(compRho>maxRho) { maxRho=compRho; ri=i; rj=j; rk=k; } } else { continue; } } errMsg("MAXVELCHECK"," at "<mFarfMode>0)) { return; } # endif // ELBEEM_PLUGIN!=1 // main loop region const bool doReduce = true; const int gridLoopBound=1; GRID_REGION_INIT(); #if PARALLEL==1 #pragma omp parallel default(shared) \ reduction(+: \ calcCurrentMass,calcCurrentVolume, \ calcCellsFilled,calcCellsEmptied, \ calcNumUsedCells ) GRID_REGION_START(); #else // PARALLEL==1 GRID_REGION_START(); #endif // PARALLEL==1 // local to main CellFlagType nbflag[LBM_DFNUM]; int oldFlag, newFlag, nbored; LbmFloat m[LBM_DFNUM]; LbmFloat rho, ux, uy, uz, tmp, usqr; // smago vars LbmFloat lcsmqadd, lcsmeq[LBM_DFNUM], lcsmomega; // ifempty cell conversion flags bool iffilled, ifemptied; LbmFloat nbfracs[LBM_DFNUM]; // ffracs of neighbors int recons[LBM_DFNUM]; // reconstruct this DF? int numRecons; // how many are reconstructed? LbmFloat mass=0., change=0., lcsmqo=0.; rho= ux= uy= uz= usqr= tmp= 0.; lcsmqadd = lcsmomega = 0.; FORDF0{ lcsmeq[l] = 0.; } // --- // now stream etc. // use //template functions for 2D/3D GRID_LOOP_START(); // loop start // stream from current set to other, then collide and store //errMsg("l2"," at "<>24; if(!isValid) { // make new if cell const LbmVec vel(mObjectSpeeds[OId]); // TODO add OPT3D treatment FORDF0 { RAC(tcel, l) = this->getCollideEq(l, iniRho,vel[0],vel[1],vel[2]); } RAC(tcel, dMass) = RAC(tcel, dFfrac) = iniRho; RAC(tcel, dFlux) = FLUX_INIT; changeFlag(lev, i,j,k, TSET(lev), CFInter); calcCurrentMass += iniRho; calcCurrentVolume += 1.0; calcNumUsedCells++; mInitialMass += iniRho; // dont treat cell until next step continue; } } else // these are exclusive if(oldFlag & (CFMbndOutflow)) { int isnotValid = oldFlag & (CFFluid); if(isnotValid) { // remove fluid cells, shouldnt be here anyway LbmFloat fluidRho = m[0]; FORDF1 { fluidRho += m[l]; } mInitialMass -= fluidRho; const LbmFloat iniRho = 0.0; RAC(tcel, dMass) = RAC(tcel, dFfrac) = iniRho; RAC(tcel, dFlux) = FLUX_INIT; changeFlag(lev, i,j,k, TSET(lev), CFInter); // same as ifemptied for if below LbmPoint oemptyp; oemptyp.flag = 0; oemptyp.x = i; oemptyp.y = j; oemptyp.z = k; LIST_EMPTY(oemptyp); calcCellsEmptied++; continue; } } if(oldFlag & (CFBnd|CFEmpty|CFGrFromCoarse|CFUnused)) { *pFlagDst = oldFlag; continue; } /*if( oldFlag & CFNoBndFluid ) { // TEST ME FASTER? OPTIMIZED_STREAMCOLLIDE; PERFORM_USQRMAXCHECK; RAC(tcel,dFfrac) = 1.0; *pFlagDst = (CellFlagType)oldFlag; // newFlag; calcCurrentMass += rho; calcCurrentVolume += 1.0; calcNumUsedCells++; continue; }// TEST ME FASTER? */ // only neighbor flags! not own flag nbored = 0; #if OPT3D==0 FORDF1 { nbflag[l] = RFLAG_NB(lev, i,j,k,SRCS(lev),l); nbored |= nbflag[l]; } #else nbflag[dSB] = *(pFlagSrc + (-mLevel[lev].lOffsy+-mLevel[lev].lOffsx)); nbored |= nbflag[dSB]; nbflag[dWB] = *(pFlagSrc + (-mLevel[lev].lOffsy+-1)); nbored |= nbflag[dWB]; nbflag[ dB] = *(pFlagSrc + (-mLevel[lev].lOffsy)); nbored |= nbflag[dB]; nbflag[dEB] = *(pFlagSrc + (-mLevel[lev].lOffsy+ 1)); nbored |= nbflag[dEB]; nbflag[dNB] = *(pFlagSrc + (-mLevel[lev].lOffsy+ mLevel[lev].lOffsx)); nbored |= nbflag[dNB]; nbflag[dSW] = *(pFlagSrc + (-mLevel[lev].lOffsx+-1)); nbored |= nbflag[dSW]; nbflag[ dS] = *(pFlagSrc + (-mLevel[lev].lOffsx)); nbored |= nbflag[dS]; nbflag[dSE] = *(pFlagSrc + (-mLevel[lev].lOffsx+ 1)); nbored |= nbflag[dSE]; nbflag[ dW] = *(pFlagSrc + (-1)); nbored |= nbflag[dW]; nbflag[ dE] = *(pFlagSrc + ( 1)); nbored |= nbflag[dE]; nbflag[dNW] = *(pFlagSrc + ( mLevel[lev].lOffsx+-1)); nbored |= nbflag[dNW]; nbflag[ dN] = *(pFlagSrc + ( mLevel[lev].lOffsx)); nbored |= nbflag[dN]; nbflag[dNE] = *(pFlagSrc + ( mLevel[lev].lOffsx+ 1)); nbored |= nbflag[dNE]; nbflag[dST] = *(pFlagSrc + ( mLevel[lev].lOffsy+-mLevel[lev].lOffsx)); nbored |= nbflag[dST]; nbflag[dWT] = *(pFlagSrc + ( mLevel[lev].lOffsy+-1)); nbored |= nbflag[dWT]; nbflag[ dT] = *(pFlagSrc + ( mLevel[lev].lOffsy)); nbored |= nbflag[dT]; nbflag[dET] = *(pFlagSrc + ( mLevel[lev].lOffsy+ 1)); nbored |= nbflag[dET]; nbflag[dNT] = *(pFlagSrc + ( mLevel[lev].lOffsy+ mLevel[lev].lOffsx)); nbored |= nbflag[dNT]; // */ #endif // pointer to destination cell calcNumUsedCells++; // FLUID cells if( oldFlag & CFFluid ) { // only standard fluid cells (with nothing except fluid as nbs if(oldFlag&CFMbndInflow) { // force velocity for inflow, necessary to have constant direction of flow // FIXME , test also set interface cells? const int OId = oldFlag>>24; //? DEFAULT_STREAM; //const LbmFloat fluidRho = 1.0; // for submerged inflows, streaming would have to be performed... LbmFloat fluidRho = m[0]; FORDF1 { fluidRho += m[l]; } const LbmVec vel(mObjectSpeeds[OId]); ux=vel[0], uy=vel[1], uz=vel[2]; usqr = 1.5 * (ux*ux + uy*uy + uz*uz); FORDF0 { RAC(tcel, l) = this->getCollideEq(l, fluidRho,ux,uy,uz); } rho = fluidRho; //errMsg("INFLOW_DEBUG","std at "<dfInv[(l)] ] // update mass // only do boundaries for fluid cells, and interface cells without // any fluid neighbors (assume that interface cells _with_ fluid // neighbors are affected enough by these) // which Df's have to be reconstructed? // for fluid cells - just the f_i difference from streaming to empty cells ---- numRecons = 0; bool onlyBndnb = ((!(oldFlag&CFNoBndFluid))&&(oldFlag&CFNoNbFluid)&&(nbored&CFBndNoslip)); //onlyBndnb = false; // DEBUG test off FORDF1 { // dfl loop recons[l] = 0; nbfracs[l] = 0.0; // finally, "normal" interface cells ---- if( nbflag[l]&(CFFluid|CFBnd) ) { // NEWTEST! FIXME check!!!!!!!!!!!!!!!!!! change = nbdf(l) - MYDF(l); } // interface cells - distuingish cells that shouldn't fill/empty else if( nbflag[l] & CFInter ) { LbmFloat mynbfac,nbnbfac; // NEW TEST t1 // t2 -> off if((oldFlag&CFNoBndFluid)&&(nbflag[l]&CFNoBndFluid)) { mynbfac = QCELL_NB(lev, i,j,k,SRCS(lev),l, dFlux) / QCELL(lev, i,j,k,SRCS(lev), dFlux); nbnbfac = 1.0/mynbfac; onlyBndnb = false; } else { mynbfac = nbnbfac = 1.0; // switch calc flux off goto changeDefault; // NEWSURFT //change = 0.; goto changeDone; // NEWSURFT } //mynbfac = nbnbfac = 1.0; // switch calc flux off t3 // perform interface case handling if ((oldFlag|nbflag[l])&(CFNoNbFluid|CFNoNbEmpty)) { switch (oldFlag&(CFNoNbFluid|CFNoNbEmpty)) { case 0: // we are a normal cell so... switch (nbflag[l]&(CFNoNbFluid|CFNoNbEmpty)) { case CFNoNbFluid: // just fill current cell = empty neighbor change = nbnbfac*nbdf(l) ; goto changeDone; case CFNoNbEmpty: // just empty current cell = fill neighbor change = - mynbfac*MYDF(l) ; goto changeDone; } break; case CFNoNbFluid: // we dont have fluid nb's so... switch (nbflag[l]&(CFNoNbFluid|CFNoNbEmpty)) { case 0: case CFNoNbEmpty: // we have no fluid nb's -> just empty change = - mynbfac*MYDF(l) ; goto changeDone; } break; case CFNoNbEmpty: // we dont have empty nb's so... switch (nbflag[l]&(CFNoNbFluid|CFNoNbEmpty)) { case 0: case CFNoNbFluid: // we have no empty nb's -> just fill change = nbnbfac*nbdf(l); goto changeDone; } break; }} // inter-inter exchange changeDefault: ; // just do normal mass exchange... change = ( nbnbfac*nbdf(l) - mynbfac*MYDF(l) ) ; changeDone: ; nbfracs[l] = QCELL_NB(lev, i,j,k, SRCS(lev),l, dFfrac); if(nbfracs[l]<0.) nbfracs[l] = 0.; // NEWSURFT change *= (myfrac + nbfracs[l]) * 0.5; } // the other cell is interface // last alternative - reconstruction in this direction else { // empty + bnd case recons[l] = 1; numRecons++; change = 0.0; } // modify mass at SRCS mass += change; } // l // normal interface, no if empty/fluid // computenormal LbmFloat surfaceNormal[3]; computeFluidSurfaceNormal(ccel,pFlagSrc, surfaceNormal); if( (ABS(surfaceNormal[0])+ABS(surfaceNormal[1])+ABS(surfaceNormal[2])) > LBM_EPSILON) { // normal ok and usable... FORDF1 { if( (this->dfDvecX[l]*surfaceNormal[0] + this->dfDvecY[l]*surfaceNormal[1] + this->dfDvecZ[l]*surfaceNormal[2]) // dot Dvec,norml > LBM_EPSILON) { recons[l] = 2; numRecons++; } } } // calculate macroscopic cell values LbmFloat oldUx, oldUy, oldUz; LbmFloat oldRho; // OLD rho = ccel->rho; # define REFERENCE_PRESSURE 1.0 // always atmosphere... # if OPT3D==0 oldRho=RAC(ccel,0); oldUx = oldUy = oldUz = 0.0; for(int l=1; lcDfNum; l++) { oldRho += RAC(ccel,l); oldUx += (this->dfDvecX[l]*RAC(ccel,l)); oldUy += (this->dfDvecY[l]*RAC(ccel,l)); oldUz += (this->dfDvecZ[l]*RAC(ccel,l)); } // reconstruct dist funcs from empty cells FORDF1 { if(recons[ l ]) { m[ this->dfInv[l] ] = this->getCollideEq(l, REFERENCE_PRESSURE, oldUx,oldUy,oldUz) + this->getCollideEq(this->dfInv[l], REFERENCE_PRESSURE, oldUx,oldUy,oldUz) - MYDF( l ); } } ux=oldUx, uy=oldUy, uz=oldUz; // no local vars, only for usqr usqr = 1.5 * (ux*ux + uy*uy + uz*uz); // needed later on # else // OPT3D==0 oldRho = + RAC(ccel,dC) + RAC(ccel,dN ) + RAC(ccel,dS ) + RAC(ccel,dE ) + RAC(ccel,dW ) + RAC(ccel,dT ) + RAC(ccel,dB ) + RAC(ccel,dNE) + RAC(ccel,dNW) + RAC(ccel,dSE) + RAC(ccel,dSW) + RAC(ccel,dNT) + RAC(ccel,dNB) + RAC(ccel,dST) + RAC(ccel,dSB) + RAC(ccel,dET) + RAC(ccel,dEB) + RAC(ccel,dWT) + RAC(ccel,dWB); oldUx = + RAC(ccel,dE) - RAC(ccel,dW) + RAC(ccel,dNE) - RAC(ccel,dNW) + RAC(ccel,dSE) - RAC(ccel,dSW) + RAC(ccel,dET) + RAC(ccel,dEB) - RAC(ccel,dWT) - RAC(ccel,dWB); oldUy = + RAC(ccel,dN) - RAC(ccel,dS) + RAC(ccel,dNE) + RAC(ccel,dNW) - RAC(ccel,dSE) - RAC(ccel,dSW) + RAC(ccel,dNT) + RAC(ccel,dNB) - RAC(ccel,dST) - RAC(ccel,dSB); oldUz = + RAC(ccel,dT) - RAC(ccel,dB) + RAC(ccel,dNT) - RAC(ccel,dNB) + RAC(ccel,dST) - RAC(ccel,dSB) + RAC(ccel,dET) - RAC(ccel,dEB) + RAC(ccel,dWT) - RAC(ccel,dWB); // now reconstruction ux=oldUx, uy=oldUy, uz=oldUz; // no local vars, only for usqr rho = REFERENCE_PRESSURE; usqr = 1.5 * (ux*ux + uy*uy + uz*uz); // needed later on if(recons[dN ]) { m[dS ] = EQN + EQS - MYDF(dN ); } if(recons[dS ]) { m[dN ] = EQS + EQN - MYDF(dS ); } if(recons[dE ]) { m[dW ] = EQE + EQW - MYDF(dE ); } if(recons[dW ]) { m[dE ] = EQW + EQE - MYDF(dW ); } if(recons[dT ]) { m[dB ] = EQT + EQB - MYDF(dT ); } if(recons[dB ]) { m[dT ] = EQB + EQT - MYDF(dB ); } if(recons[dNE]) { m[dSW] = EQNE + EQSW - MYDF(dNE); } if(recons[dNW]) { m[dSE] = EQNW + EQSE - MYDF(dNW); } if(recons[dSE]) { m[dNW] = EQSE + EQNW - MYDF(dSE); } if(recons[dSW]) { m[dNE] = EQSW + EQNE - MYDF(dSW); } if(recons[dNT]) { m[dSB] = EQNT + EQSB - MYDF(dNT); } if(recons[dNB]) { m[dST] = EQNB + EQST - MYDF(dNB); } if(recons[dST]) { m[dNB] = EQST + EQNB - MYDF(dST); } if(recons[dSB]) { m[dNT] = EQSB + EQNT - MYDF(dSB); } if(recons[dET]) { m[dWB] = EQET + EQWB - MYDF(dET); } if(recons[dEB]) { m[dWT] = EQEB + EQWT - MYDF(dEB); } if(recons[dWT]) { m[dEB] = EQWT + EQEB - MYDF(dWT); } if(recons[dWB]) { m[dET] = EQWB + EQET - MYDF(dWB); } # endif // inflow bc handling if(oldFlag & (CFMbndInflow)) { // fill if cells in inflow region if(myfrac<0.5) { mass += 0.25; mInitialMass += 0.25; } const int OId = oldFlag>>24; const LbmVec vel(mObjectSpeeds[OId]); ux=vel[0], uy=vel[1], uz=vel[2]; //? usqr = 1.5 * (ux*ux + uy*uy + uz*uz); //FORDF0 { RAC(tcel, l) = this->getCollideEq(l, fluidRho,ux,uy,uz); } rho = fluidRho; rho = REFERENCE_PRESSURE; FORDF0 { RAC(tcel, l) = this->getCollideEq(l, rho,ux,uy,uz); } //errMsg("INFLOW_DEBUG","if at "<RWVEL_WINDTHRESH) && (lcsmqomSizez-SLOWDOWNREGION) ) { LbmFloat nuz = uz; if(k>mSizez-SLOWDOWNREGION) { // special case LbmFloat zfac = (LbmFloat)( k-(mSizez-SLOWDOWNREGION) ); zfac /= (LbmFloat)(SLOWDOWNREGION); nuz += (1.0) * zfac; // check max speed? OFF? //errMsg("TOPT"," at "<0.025) { const LbmFloat add = this->dfLength[l]*(-ux*this->dfDvecX[l]-uy*this->dfDvecY[l]-nuz*this->dfDvecZ[l])*jdf; RAC(tcel,l) += add; } } //errMsg("TOPDOWNCORR"," jdf:"<0.0095)) { // add if((prob0.012)) { // add } else { doAdd = false; }// dont... } // remove noise if(usqr<0.0001) doAdd=false; // TODO check!? // dont try to subtract from empty cells // ensure cell has enough mass for new drop LbmFloat newPartsize = 1.0; if(mPartUsePhysModel) { // 1-10 newPartsize += 9.0* (rand()/(RAND_MAX+1.0)); } else { // 1-5, overall size has to be less than // .62 (ca. 0.5) to make drops significantly smaller // than a full cell! newPartsize += 4.0* (rand()/(RAND_MAX+1.0)); } LbmFloat dropmass = ParticleObject::getMass(mPartDropMassSub*newPartsize); //PARTMASS(mPartDropMassSub*newPartsize); // mass: 4/3 pi r^3 rho while(dropmass>mass) { newPartsize -= 0.2; dropmass = ParticleObject::getMass(mPartDropMassSub*newPartsize); } if(newPartsize<=1.) doAdd=false; if( (doAdd) ) { // init new particle for(int s=0; s<1; s++) { // one part! const LbmFloat posjitter = 0.05; const LbmFloat posjitteroffs = posjitter*-0.5; LbmFloat jpx = posjitteroffs+ posjitter* (rand()/(RAND_MAX+1.0)); LbmFloat jpy = posjitteroffs+ posjitter* (rand()/(RAND_MAX+1.0)); LbmFloat jpz = posjitteroffs+ posjitter* (rand()/(RAND_MAX+1.0)); const LbmFloat jitterstr = 1.0; const LbmFloat jitteroffs = jitterstr*-0.5; LbmFloat jx = jitteroffs+ jitterstr* (rand()/(RAND_MAX+1.0)); LbmFloat jy = jitteroffs+ jitterstr* (rand()/(RAND_MAX+1.0)); LbmFloat jz = jitteroffs+ jitterstr* (rand()/(RAND_MAX+1.0)); // average normal & velocity // -> mostly along velocity dir, many into surface // fluid velocity (not normalized!) LbmVec flvelVel = LbmVec(ux,uy,uz); LbmFloat flvelLen = norm(flvelVel); // surface normal LbmVec normVel = LbmVec(surfaceNormal[0],surfaceNormal[1],surfaceNormal[2]); normalize(normVel); LbmFloat normScale = (0.01+flvelLen); // jitter vector, 0.2 * flvel LbmVec jittVel = LbmVec(jx,jy,jz)*(0.05+flvelLen)*0.1; // weighten velocities const LbmFloat flvelWeight = 0.9; LbmVec newpartVel = normVel*normScale*(1.-flvelWeight) + flvelVel*(flvelWeight) + jittVel; // offset towards surface (hide popping) jpx += -normVel[0]*0.4; jpy += -normVel[1]*0.4; jpz += -normVel[2]*0.4; LbmFloat srci=i+0.5+jpx, srcj=j+0.5+jpy, srck=k+0.5+jpz; int type=0; type = PART_DROP; # if LBMDIM==2 newpartVel[2]=0.; srck=0.5; # endif // LBMDIM==2 // subtract drop mass mass -= dropmass; // init new particle { ParticleObject np( ntlVec3Gfx(srci,srcj,srck) ); np.setVel(newpartVel[0],newpartVel[1],newpartVel[2]); np.setStatus(PART_IN); np.setType(type); //if((s%3)==2) np.setType(PART_FLOAT); np.setSize(newPartsize); //errMsg("NEWPART"," at "<= (rho * (1.0+FSGR_MAGICNR)) ) { iffilled = 1; } // interface cell if empty? if( (mass) <= (rho * ( -FSGR_MAGICNR)) ) { ifemptied = 1; } if(oldFlag & (CFMbndOutflow)) { mInitialMass -= mass; mass = myfrac = 0.0; iffilled = 0; ifemptied = 1; } // looks much nicer... LISTTRICK # if FSGR_LISTTRICK==1 //if((oldFlag&CFNoNbEmpty)&&(newFlag&CFNoNbEmpty)) { TEST_IF_CHECK; } if(newFlag&CFNoBndFluid) { // test NEW TEST if(!iffilled) { // remove cells independent from amount of change... if( (oldFlag & CFNoNbEmpty)&&(newFlag & CFNoNbEmpty)&& ( (mass>(rho*FSGR_LISTTTHRESHFULL)) || ((nbored&CFInter)==0) )) { //if((nbored&CFInter)==0){ errMsg("NBORED!CFINTER","filled "< better cell conversions RAC(tcel,dFfrac) = (mass/rho); // init new flux value float flux = FLUX_INIT; // dxqn on if(newFlag&CFNoBndFluid) { //flux = 50.0; // extreme on for(int nn=1; nncDfNum; nn++) { if(nbflag[nn] & (CFFluid|CFInter|CFBnd)) { flux += this->dfLength[nn]; } } // optical hack - smooth slow moving // surface regions if(usqr< sssUsqrLimit) { for(int nn=1; nncDfNum; nn++) { if(nbfracs[nn]!=0.0) { LbmFloat curSmooth = (sssUsqrLimit-usqr)*sssUsqrLimitInv; if(curSmooth>1.0) curSmooth=1.0; flux *= (1.0+ smoothStrength*curSmooth * (nbfracs[nn]-myfrac)) ; } } } // NEW TEST */ } // flux = FLUX_INIT; // calc flux off QCELL(lev, i,j,k,TSET(lev), dFlux) = flux; // */ // perform mass exchange with streamed values QCELL(lev, i,j,k,TSET(lev), dMass) = mass; // MASST // set new flag *pFlagDst = (CellFlagType)newFlag; calcCurrentMass += mass; calcCurrentVolume += RAC(tcel,dFfrac); // interface cell handling done... #if PARALLEL!=1 GRID_LOOPREG_END(); #else // PARALLEL==1 #include "paraloopend.h" // = GRID_LOOPREG_END(); #endif // PARALLEL==1 // write vars from computations to class mLevel[lev].lmass = calcCurrentMass; mLevel[lev].lvolume = calcCurrentVolume; mNumFilledCells = calcCellsFilled; mNumEmptiedCells = calcCellsEmptied; mNumUsedCells = calcNumUsedCells; } void LbmFsgrSolver::preinitGrids() { const int lev = mMaxRefine; const bool doReduce = false; const int gridLoopBound=0; // preinit both grids for(int s=0; s<2; s++) { GRID_REGION_INIT(); #if PARALLEL==1 #pragma omp parallel default(shared) \ reduction(+: \ calcCurrentMass,calcCurrentVolume, \ calcCellsFilled,calcCellsEmptied, \ calcNumUsedCells ) #endif // PARALLEL==1 GRID_REGION_START(); GRID_LOOP_START(); for(int l=0; l-LBM_EPSILON) ret = 0.0; else ret = scal * -1.0; } //errMsg("massd", PRINT_IJK<<" nv"<dfVecX[l], nj=j+this->dfVecY[l], nk=k+this->dfVecZ[l]; //if((LBMDIM>2)&&( (ni<=0) || (nj<=0) || (nk<=0) || (ni>=mLevel[workLev].lSizex-1) || (nj>=mLevel[workLev].lSizey-1) || (nk>=mLevel[workLev].lSizez-1) )) { if( (ni<=0) || (nj<=0) || (ni>=mLevel[workLev].lSizex-1) || (nj>=mLevel[workLev].lSizey-1) # if LBMDIM==3 || (nk<=0) || (nk>=mLevel[workLev].lSizez-1) # endif // LBMDIM==1 ) { continue; } // new bc, dont treat cells on boundary NEWBC if( RFLAG(workLev, ni,nj,nk, workSet) & CFEmpty ){ // preinit speed, get from average surrounding cells // interpolate from non-workset to workset, sets are handled in function // new and empty interface cell, dont change old flag here! addToNewInterList(ni,nj,nk); LbmFloat avgrho = 0.0; LbmFloat avgux = 0.0, avguy = 0.0, avguz = 0.0; interpolateCellValues(workLev,ni,nj,nk,workSet, avgrho,avgux,avguy,avguz); // careful with l's... FORDF0M { QCELL(workLev,ni,nj,nk, workSet, m) = this->getCollideEq( m,avgrho, avgux, avguy, avguz ); //QCELL(workLev,ni,nj,nk, workSet, l) = avgnbdf[l]; // CHECK FIXME test? } //errMsg("FNEW", PRINT_VEC(ni,nj,nk)<<" mss"< let fill cells+surrounding interface cells have the higher importance */ for( vector::iterator iter=mListEmpty.begin(); iter != mListEmpty.end(); iter++ ) { int i=iter->x, j=iter->y, k=iter->z; if((RFLAG(workLev,i,j,k, workSet)&(CFInter|CFNoDelete)) == (CFInter|CFNoDelete)){ errMsg("A"," ARGHARGRAG "); } // DEBUG if(debugFlagreinit) errMsg("EMPT", PRINT_IJK<<" mss"<dfVecX[l], nj=j+this->dfVecY[l], nk=k+this->dfVecZ[l]; if( RFLAG(workLev,ni,nj,nk, workSet) & CFFluid){ // init fluid->interface //RFLAG(workLev,ni,nj,nk, workSet) = (CellFlagType)(CFInter); changeFlag(workLev,ni,nj,nk, workSet, CFInter); /* new mass = current density */ LbmFloat nbrho = QCELL(workLev,ni,nj,nk, workSet, dC); for(int rl=1; rl< this->cDfNum ; ++rl) { nbrho += QCELL(workLev,ni,nj,nk, workSet, rl); } QCELL(workLev,ni,nj,nk, workSet, dMass) = nbrho; QCELL(workLev,ni,nj,nk, workSet, dFfrac) = 1.0; // store point addToNewInterList(ni,nj,nk); } if( RFLAG(workLev,ni,nj,nk, workSet) & CFInter){ // test, also add to list... addToNewInterList(ni,nj,nk); } // NEW? } /* for symmetry, set our flag right now */ changeFlag(workLev,i,j,k, workSet, CFEmpty); // mark cell not be changed mass... - not necessary, not in list anymore anyway! } // emptylist // precompute weights to get rid of order dependancies vector vWeights; vWeights.resize( mListFull.size() + mListEmpty.size() ); int weightIndex = 0; int nbCount = 0; LbmFloat nbWeights[LBM_DFNUM]; LbmFloat nbTotWeights = 0.0; for( vector::iterator iter=mListFull.begin(); iter != mListFull.end(); iter++ ) { int i=iter->x, j=iter->y, k=iter->z; nbCount = 0; nbTotWeights = 0.0; FORDF1 { int ni=i+this->dfVecX[l], nj=j+this->dfVecY[l], nk=k+this->dfVecZ[l]; if( RFLAG(workLev,ni,nj,nk, workSet) & CFInter) { nbCount++; if(iter->flag&1) nbWeights[l] = 1.; // NEWSURFT else nbWeights[l] = getMassdWeight(1,i,j,k,workSet,l); // NEWSURFT nbTotWeights += nbWeights[l]; } else { nbWeights[l] = -100.0; // DEBUG; } } if(nbCount>0) { //errMsg("FF I", PRINT_IJK<<" "<::iterator iter=mListEmpty.begin(); iter != mListEmpty.end(); iter++ ) { int i=iter->x, j=iter->y, k=iter->z; nbCount = 0; nbTotWeights = 0.0; FORDF1 { int ni=i+this->dfVecX[l], nj=j+this->dfVecY[l], nk=k+this->dfVecZ[l]; if( RFLAG(workLev,ni,nj,nk, workSet) & CFInter) { nbCount++; if(iter->flag&1) nbWeights[l] = 1.; // NEWSURFT else nbWeights[l] = getMassdWeight(0,i,j,k,workSet,l); // NEWSURFT nbTotWeights += nbWeights[l]; } else { nbWeights[l] = -100.0; // DEBUG; } } if(nbCount>0) { //errMsg("EE I", PRINT_IJK<<" "<::iterator iter=mListFull.begin(); iter != mListFull.end(); iter++ ) { int i=iter->x, j=iter->y, k=iter->z; LbmFloat myrho = QCELL(workLev,i,j,k, workSet, dC); FORDF1 { myrho += QCELL(workLev,i,j,k, workSet, l); } // QCELL.rho LbmFloat massChange = QCELL(workLev,i,j,k, workSet, dMass) - myrho; if(vWeights[weightIndex].numNbs>0.0) { const LbmFloat nbTotWeightsp = vWeights[weightIndex].val[0]; //errMsg("FF I", PRINT_IJK<<" "<dfVecX[l], nj=j+this->dfVecY[l], nk=k+this->dfVecZ[l]; if( RFLAG(workLev,ni,nj,nk, workSet) & CFInter) { LbmFloat change = -1.0; if(nbTotWeightsp>0.0) { //change = massChange * ( nbWeights[l]/nbTotWeightsp ); change = massChange * ( vWeights[weightIndex].val[l]/nbTotWeightsp ); } else { change = (LbmFloat)(massChange/vWeights[weightIndex].numNbs); } QCELL(workLev,ni,nj,nk, workSet, dMass) += change; } } massChange = 0.0; } else { // Problem! no interface neighbors mFixMass += massChange; //TTT mNumProblems++; //errMsg(" FULL PROBLEM ", PRINT_IJK<<" "<::iterator iter=mListEmpty.begin(); iter != mListEmpty.end(); iter++ ) { int i=iter->x, j=iter->y, k=iter->z; LbmFloat massChange = QCELL(workLev, i,j,k, workSet, dMass); if(vWeights[weightIndex].numNbs>0.0) { const LbmFloat nbTotWeightsp = vWeights[weightIndex].val[0]; //errMsg("EE I", PRINT_IJK<<" "<dfVecX[l], nj=j+this->dfVecY[l], nk=k+this->dfVecZ[l]; if( RFLAG(workLev,ni,nj,nk, workSet) & CFInter) { LbmFloat change = -1.0; if(nbTotWeightsp>0.0) { change = massChange * ( vWeights[weightIndex].val[l]/nbTotWeightsp ); } else { change = (LbmFloat)(massChange/vWeights[weightIndex].numNbs); } QCELL(workLev, ni,nj,nk, workSet, dMass) += change; } } massChange = 0.0; } else { // Problem! no interface neighbors mFixMass += massChange; //TTT mNumProblems++; //errMsg(" EMPT PROBLEM ", PRINT_IJK<<" "<::iterator iter=mListEmpty.begin(); iter != mListEmpty.end(); iter++ ) { int i=iter->x, j=iter->y, k=iter->z; changeFlag(workLev,i,j,k, otherSet, CFEmpty); } // check if some of the new interface cells can be removed again // never happens !!! not necessary // calculate ffrac for new IF cells NEW // how many are really new interface cells? int numNewIf = 0; for( vector::iterator iter=mListNewInter.begin(); iter != mListNewInter.end(); iter++ ) { int i=iter->x, j=iter->y, k=iter->z; if(!(RFLAG(workLev,i,j,k, workSet)&CFInter)) { continue; // FIXME remove from list? } numNewIf++; } // redistribute mass, reinit flags if(debugFlagreinit) errMsg("NEWIF", "total:"<::iterator iter=mListNewInter.begin(); iter != mListNewInter.end(); iter++ ) { int i=iter->x, j=iter->y, k=iter->z; if((i<=0) || (j<=0) || (i>=mLevel[workLev].lSizex-1) || (j>=mLevel[workLev].lSizey-1) || ((LBMDIM==3) && ((k<=0) || (k>=mLevel[workLev].lSizez-1) ) ) ) { continue; } // new bc, dont treat cells on boundary NEWBC if(!(RFLAG(workLev,i,j,k, workSet)&CFInter)) { //errMsg("???"," "<::iterator iter=mListNewInter.begin(); iter != mListNewInter.end(); iter++ ) { int i=iter->x, j=iter->y, k=iter->z; if(!(RFLAG(workLev,i,j,k, workSet)&CFInter)) { continue; } initInterfaceVars(workLev, i,j,k, workSet, false); //int level, int i,int j,int k,int workSet, bool initMass) { //LbmFloat nrho = 0.0; //FORDF0 { nrho += QCELL(workLev, i,j,k, workSet, l); } //QCELL(workLev,i,j,k, workSet, dFfrac) = QCELL(workLev,i,j,k, workSet, dMass)/nrho; //QCELL(workLev,i,j,k, workSet, dFlux) = FLUX_INIT; } if(mListNewInter.size()>0){ //errMsg("FixMassDisted"," fm:"<