//! add Forces between fl/fl and fl/em cells KERNEL(bnd=1) void KnAddForce(FlagGrid& flags, MACGrid& vel, Vec3 force) { bool curFluid = flags.isFluid(i,j,k); bool curEmpty = flags.isEmpty(i,j,k); if (!curFluid && !curEmpty) return; if (flags.isFluid(i-1,j,k) || (curFluid && flags.isEmpty(i-1,j,k))) vel(i,j,k).x += force.x; if (flags.isFluid(i,j-1,k) || (curFluid && flags.isEmpty(i,j-1,k))) vel(i,j,k).y += force.y; if (vel.is3D() && (flags.isFluid(i,j,k-1) || (curFluid && flags.isEmpty(i,j,k-1)))) vel(i,j,k).z += force.z; }
void MacCormackCorrect(FlagGrid& flags, Grid<T>& dst, Grid<T>& old, Grid<T>& fwd, Grid<T>& bwd, Real strength, bool isLevelSet, bool isMAC=false ) { // note, replacement for isNotFluidMAC and isNotFluid bool skip = false; if (!flags.isFluid(idx)) skip = true; if(!isMAC) { if( (idx>=flags.getStrideX()) && (!flags.isFluid(idx-flags.getStrideX()) )) skip = true; if( (idx>=flags.getStrideY()) && (!flags.isFluid(idx-flags.getStrideY()) )) skip = true; if ( flags.is3D() ) { if( (idx>=flags.getStrideZ()) &&(!flags.isFluid(idx-flags.getStrideZ()) )) skip = true; } } if ( skip ) { dst[idx] = isLevelSet ? fwd[idx] : (T)0.0; return; } // note, strenth of correction can be modified here dst[idx] = fwd[idx] + strength * 0.5 * (old[idx] - bwd[idx]); }
void SemiLagrange (FlagGrid& flags, MACGrid& vel, Grid<T>& dst, Grid<T>& src, Real dt, bool isLevelset) { if (flags.isObstacle(i,j,k)) { dst(i,j,k) = 0; return; } if (!isLevelset && isNotFluid(flags,i,j,k) ) { dst(i,j,k) = src(i,j,k); return; } // SL traceback Vec3 pos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getCentered(i,j,k) * dt; dst(i,j,k) = src.getInterpolated(pos); }
void MacCormackClampMAC (FlagGrid& flags, MACGrid& vel, MACGrid& dst, MACGrid& orig, MACGrid& fwd, Real dt) { if (flags.isObstacle(i,j,k)) return; if ( isNotFluidMAC(flags,i,j,k) ) { dst(i,j,k) = fwd(i,j,k); return; } Vec3 pos(i,j,k); Vec3 dval = dst(i,j,k); Vec3i upperClamp = flags.getSize() - 1; // get total fwd lookup Vec3i posFwd = toVec3i( Vec3(i,j,k) - vel.getCentered(i,j,k) * dt ); Vec3i posBwd = toVec3i( Vec3(i,j,k) + vel.getCentered(i,j,k) * dt ); // clamp individual components dval.x = doClampComponentMAC<0>(upperClamp, orig, dval.x, toVec3i( pos - vel.getAtMACX(i,j,k) * dt) ); dval.y = doClampComponentMAC<1>(upperClamp, orig, dval.y, toVec3i( pos - vel.getAtMACY(i,j,k) * dt) ); dval.z = doClampComponentMAC<2>(upperClamp, orig, dval.z, toVec3i( pos - vel.getAtMACZ(i,j,k) * dt) ); // test if lookups point out of grid or into obstacle if (posFwd.x < 0 || posFwd.y < 0 || posFwd.z < 0 || posBwd.x < 0 || posBwd.y < 0 || posBwd.z < 0 || posFwd.x > upperClamp.x || posFwd.y > upperClamp.y || ((posFwd.z > upperClamp.z)&&flags.is3D()) || posBwd.x > upperClamp.x || posBwd.y > upperClamp.y || ((posBwd.z > upperClamp.z)&&flags.is3D()) //|| flags.isObstacle(posFwd) || flags.isObstacle(posBwd) // note - this unfortunately introduces asymmetry... TODO update ) { dval = fwd(i,j,k); } // writeback dst(i,j,k) = dval; }
//! set no-stick wall boundary condition between ob/fl and ob/ob cells KERNEL void KnSetWallBcs(FlagGrid& flags, MACGrid& vel, Vector3D<bool> lo, Vector3D<bool> up, bool admm) { bool curFluid = flags.isFluid(i,j,k); bool curObstacle = flags.isObstacle(i,j,k); if (!curFluid && !curObstacle) return; // MLE 2014-07-04 // if not admm, leave it as in orig // if openBound, don't correct anything (solid is as empty) // if admm, correct if vel is pointing outwards // if "inner" obstacle vel if(i>0 && curObstacle && !flags.isFluid(i-1,j,k)) vel(i,j,k).x = 0; if(j>0 && curObstacle && !flags.isFluid(i,j-1,k)) vel(i,j,k).y = 0; // check lo.x if(!lo.x && i>0 && curFluid && flags.isObstacle(i-1,j,k) && ((admm&&vel(i,j,k).x<0)||!admm)) vel(i,j,k).x = 0; // check up.x if(!up.x && i>0 && curObstacle && flags.isFluid(i-1,j,k) && ((admm&&vel(i,j,k).x>0)||!admm)) vel(i,j,k).x = 0; // check lo.y if(!lo.y && j>0 && curFluid && flags.isObstacle(i,j-1,k) && ((admm&&vel(i,j,k).y<0)||!admm)) vel(i,j,k).y = 0; // check up.y if(!up.y && j>0 && curObstacle && flags.isFluid(i,j-1,k) && ((admm&&vel(i,j,k).y>0)||!admm)) vel(i,j,k).y = 0; // check lo.z if(!lo.z && k>0 && curFluid && flags.isObstacle(i,j,k-1) && ((admm&&vel(i,j,k).z<0)||!admm)) vel(i,j,k).z = 0; // check up.z if(!up.z && k>0 && curObstacle && flags.isFluid(i,j,k-1) && ((admm&&vel(i,j,k).z>0)||!admm)) vel(i,j,k).z = 0; /* MLE consider later if (curFluid) { if ((i>0 && flags.isStick(i-1,j,k)) || (i<flags.getSizeX()-1 && flags.isStick(i+1,j,k))) vel(i,j,k).y = vel(i,j,k).z = 0; if ((j>0 && flags.isStick(i,j-1,k)) || (j<flags.getSizeY()-1 && flags.isStick(i,j+1,k))) vel(i,j,k).x = vel(i,j,k).z = 0; if (vel.is3D() && ((k>0 && flags.isStick(i,j,k-1)) || (k<flags.getSizeZ()-1 && flags.isStick(i,j,k+1)))) vel(i,j,k).x = vel(i,j,k).y = 0; } */ }
void CorrectVelGhostFluid (FlagGrid& flags, MACGrid& vel, Grid<Real>& pressure) //, Grid<Real>& phi) { bool curFluid = flags.isFluid(i,j,k); if (!curFluid && !flags.isEmpty(i,j,k)) return; const Real curPress = pressure(i,j,k); //const Real curPhi = phi(i,j,k); // TODO - include ghost fluid factor NT_DEBUG // in contrast to old implementation: // make sure to add gradient for all fluid-empty or fluid-fluid combinations // of neighbors... if (!flags.isObstacle(i-1,j,k) && (curFluid || flags.isFluid(i-1,j,k))) vel(i,j,k).x -= curPress - pressure(i-1,j,k); if (!flags.isObstacle(i,j-1,k) && (curFluid || flags.isFluid(i,j-1,k))) vel(i,j,k).y -= curPress - pressure(i,j-1,k); if (flags.is3D() && (!flags.isObstacle(i,j,k-1) && (curFluid || flags.isFluid(i,j,k-1)))) vel(i,j,k).z -= curPress - pressure(i,j,k-1); }
void TurbulenceParticleSystem::deleteInObstacle(FlagGrid& flags) { for (int i=0; i<size(); i++) if (flags.isObstacle(mData[i].pos)) mData[i].flag |= PDELETE; compress(); }
//! add Buoyancy force based on smoke density PYTHON void print_dt_dx(FlagGrid& flags, Grid<Real>& density, MACGrid& vel) { // Vec3 f = - gravity * flags.getParent()->getDt() / flags.getParent()->getDx(); std::cout << " flags.getParent()->getDt() " << flags.getParent()->getDt() << std::endl ; std::cout << " flags.getParent()->getDx() " << flags.getParent()->getDx() << std::endl ; }
//! add Buoyancy force based on smoke density PYTHON void addBuoyancy(FlagGrid& flags, Grid<Real>& density, MACGrid& vel, Vec3 gravity) { Vec3 f = - gravity * flags.getParent()->getDt() / flags.getParent()->getDx(); KnAddBuoyancy(flags,density, vel, f); }
//! add gravity forces to all fluid cells PYTHON void addGravity(FlagGrid& flags, MACGrid& vel, Vec3 gravity) { Vec3 f = gravity * flags.getParent()->getDt() / flags.getDx(); KnAddForce(flags, vel, f); }
//! Perform pressure projection of the velocity grid PYTHON void solvePressure(MACGrid& vel, Grid<Real>& pressure, FlagGrid& flags, Grid<Real>* phi = 0, Grid<Real>* perCellCorr = 0, Real ghostAccuracy = 0, Real cgMaxIterFac = 1.5, Real cgAccuracy = 1e-3, string openBound = "", string outflow = "", int outflowHeight = 1, int precondition = 0, bool enforceCompatibility = false, bool useResNorm = true ) { //assertMsg(vel.is3D(), "Only 3D grids supported so far"); // parse strings Vector3D<bool> loOpenBound, upOpenBound, loOutflow, upOutflow; convertDescToVec(openBound, loOpenBound, upOpenBound); convertDescToVec(outflow, loOutflow, upOutflow); if (vel.is2D() && (loOpenBound.z || upOpenBound.z)) errMsg("open boundaries for z specified for 2D grid"); // reserve temp grids Grid<Real> rhs(parent); Grid<Real> residual(parent); Grid<Real> search(parent); Grid<Real> A0(parent); Grid<Real> Ai(parent); Grid<Real> Aj(parent); Grid<Real> Ak(parent); Grid<Real> tmp(parent); Grid<Real> pca0(parent); Grid<Real> pca1(parent); Grid<Real> pca2(parent); Grid<Real> pca3(parent); // setup matrix and boundaries MakeLaplaceMatrix (flags, A0, Ai, Aj, Ak); SetOpenBound (A0, Ai, Aj, Ak, vel, loOpenBound, upOpenBound); if (ghostAccuracy > 0) { if (!phi) errMsg("solve_pressure: if ghostAccuracy>0, need to specify levelset phi=xxx"); ApplyGhostFluid (flags, A0, *phi, ghostAccuracy); } // compute divergence and init right hand side MakeRhs kernMakeRhs (flags, rhs, vel, perCellCorr); if (!outflow.empty()) SetOutflow (rhs, loOutflow, upOutflow, outflowHeight); if (enforceCompatibility) rhs += (Real)(-kernMakeRhs.sum / (Real)kernMakeRhs.cnt); // CG const int maxIter = (int)(cgMaxIterFac * flags.getSize().max()); GridCgInterface *gcg; if (vel.is3D()) gcg = new GridCg<ApplyMatrix>(pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak ); else gcg = new GridCg<ApplyMatrix2D>(pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak ); gcg->setAccuracy( cgAccuracy ); gcg->setUseResNorm( useResNorm ); // optional preconditioning gcg->setPreconditioner( (GridCgInterface::PreconditionType)precondition, &pca0, &pca1, &pca2, &pca3); for (int iter=0; iter<maxIter; iter++) { if (!gcg->iterate()) iter=maxIter; } debMsg("FluidSolver::solvePressure iterations:"<<gcg->getIterations()<<", res:"<<gcg->getSigma(), 1); delete gcg; if(ghostAccuracy<=0.) { // ghost fluid off, normal correction CorrectVelocity (flags, vel, pressure ); } else { CorrectVelGhostFluid (flags, vel, pressure); } }
static inline bool isNotFluidMAC(FlagGrid& flags, int i, int j, int k) { if ( flags.isFluid(i,j,k) ) return false; return true; }