void SemiLagrangeMAC(FlagGrid& flags, MACGrid& vel, MACGrid& dst, MACGrid& src, Real dt) { if (flags.isObstacle(i,j,k)) { dst(i,j,k) = 0; return; } // std::cout << " Don't worry, I MADE IT till here " << std::endl ; if ( isNotFluidMAC(flags,i,j,k) ) { dst(i,j,k) = src(i,j,k); return; } // std::cout << " Don't worry, I REACHED till here " << std::endl ; // get currect velocity at MAC position // no need to shift xpos etc. as lookup field is also shifted Vec3 xpos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getAtMACX(i,j,k) * dt; Real vx = src.getInterpolatedComponent<0>(xpos); Vec3 ypos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getAtMACY(i,j,k) * dt; Real vy = src.getInterpolatedComponent<1>(ypos); Vec3 zpos = Vec3(i+0.5f,j+0.5f,k+0.5f) - vel.getAtMACZ(i,j,k) * dt; Real vz = src.getInterpolatedComponent<2>(zpos); // std::cout << " Don't worry, I CAN till here " << std::endl ; dst(i,j,k) = Vec3(vx,vy,vz); // std::cout << " Don't worry, I COULD till here " << std::endl ; }
void fnAdvectSemiLagrange_time_fraction<MACGrid>(FluidSolver* parent, FlagGrid& flags, MACGrid& vel, MACGrid& orig, int order, Real strength, Real time_fraction) { Real dt = (time_fraction) * parent->getDt(); std::cout << " time_fraction = " << time_fraction << std::endl ; std::cout << " dt = " << dt << std::endl ; // forward step MACGrid fwd(parent); SemiLagrangeMAC (flags, vel, fwd, orig, dt); if (order == 1) { orig.swap(fwd); } else if (order == 2) { // MacCormack MACGrid bwd(parent); MACGrid newGrid(parent); // bwd <- backwards step SemiLagrangeMAC (flags, vel, bwd, fwd, -dt); // newGrid <- compute correction MacCormackCorrect<Vec3> (flags, newGrid, orig, fwd, bwd, strength, false, true); // clamp values MacCormackClampMAC (flags, vel, newGrid, orig, fwd, dt); orig.swap(newGrid); } }
void MacCormackClamp(FlagGrid& flags, MACGrid& vel, Grid<T>& dst, Grid<T>& orig, Grid<T>& fwd, Real dt) { if (flags.isObstacle(i,j,k)) return; if ( isNotFluid(flags,i,j,k) ) { dst(i,j,k) = fwd(i,j,k); return; } T dval = dst(i,j,k); Vec3i upperClamp = flags.getSize() - 1; // lookup forward/backward 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 ); dval = doClampComponent<T>(upperClamp, orig, dval, posFwd ); // 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) ) { dval = fwd(i,j,k); } dst(i,j,k) = dval; }
//! add Buoyancy force based on smoke density KERNEL(bnd=1) void KnAddBuoyancy(FlagGrid& flags, Grid<Real>& density, MACGrid& vel, Vec3 strength) { if (!flags.isFluid(i,j,k)) return; if (flags.isFluid(i-1,j,k)) vel(i,j,k).x += (0.5 * strength.x) * (density(i,j,k)+density(i-1,j,k)); if (flags.isFluid(i,j-1,k)) vel(i,j,k).y += (0.5 * strength.y) * (density(i,j,k)+density(i,j-1,k)); if (vel.is3D() && flags.isFluid(i,j,k-1)) vel(i,j,k).z += (0.5 * strength.z) * (density(i,j,k)+density(i,j,k-1)); }
//! 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; }
//! enforce a constant inflow/outflow at the grid boundaries PYTHON void setInflowBcs(MACGrid& vel, string dire, Vec3 value) { for(size_t i=0; i<dire.size(); i++) { if (dire[i] >= 'x' && dire[i] <= 'z') { int dim = dire[i]-'x'; KnSetInflow(vel,dim,0,value); } else if (dire[i] >= 'X' && dire[i] <= 'Z') { int dim = dire[i]-'X'; KnSetInflow(vel,dim,vel.getSize()[dim]-1,value); } else errMsg("invalid character in direction string. Only [xyzXYZ] allowed."); } }
//! Kernel: Set matrix stencils and velocities to enable open boundaries KERNEL void SetOpenBound(Grid<Real>& A0, Grid<Real>& Ai, Grid<Real>& Aj, Grid<Real>& Ak, MACGrid& vel, Vector3D<bool> lowerBound, Vector3D<bool> upperBound) { // set velocity boundary conditions if (lowerBound.x && i == 0) vel(0,j,k) = vel(1,j,k); if (lowerBound.y && j == 0) vel(i,0,k) = vel(i,1,k); if (lowerBound.z && k == 0) vel(i,j,0) = vel(i,j,1); if (upperBound.x && i == maxX-1) vel(maxX-1,j,k) = vel(maxX-2,j,k); if (upperBound.y && j == maxY-1) vel(i,maxY-1,k) = vel(i,maxY-2,k); if (upperBound.z && k == maxZ-1) vel(i,j,maxZ-1) = vel(i,j,maxZ-2); // set matrix stencils at boundary if ((lowerBound.x && i<=1) || (upperBound.x && i>=maxX-2) || (lowerBound.y && j<=1) || (upperBound.y && j>=maxY-2) || (lowerBound.z && k<=1) || (upperBound.z && k>=maxZ-2)) { A0(i,j,k) = vel.is3D() ? 6.0 : 4.0; Ai(i,j,k) = -1.0; Aj(i,j,k) = -1.0; if (vel.is3D()) Ak(i,j,k) = -1.0; } }
inline Real doClampComponentMAC(const Vec3i& upperClamp, MACGrid& orig, Real dst, const Vec3i& posFwd) { // clamp forward lookup to grid const int i0 = clamp(posFwd.x, 0, upperClamp.x-1); const int j0 = clamp(posFwd.y, 0, upperClamp.y-1); const int k0 = clamp(posFwd.z, 0, (orig.is3D() ? (upperClamp.z-1) : 1) ); const int i1 = i0+1, j1 = j0+1, k1= (orig.is3D() ? (k0+1) : k0); if (!orig.isInBounds(Vec3i(i0,j0,k0),1)) return dst; // find min/max around fwd pos Real minv = orig(i0,j0,k0)[c], maxv = minv; getMinMax(minv, maxv, orig(i1,j0,k0)[c]); getMinMax(minv, maxv, orig(i0,j1,k0)[c]); getMinMax(minv, maxv, orig(i1,j1,k0)[c]); getMinMax(minv, maxv, orig(i0,j0,k1)[c]); getMinMax(minv, maxv, orig(i1,j0,k1)[c]); getMinMax(minv, maxv, orig(i0,j1,k1)[c]); getMinMax(minv, maxv, orig(i1,j1,k1)[c]); return clamp(dst, minv, maxv); }
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; }
//! 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); } }