void FluidSim::compute_phi() { //Estimate from particles liquid_phi.assign(3*dx); for(unsigned int p = 0; p < particles.size(); ++p) { Vec2f point = particles[p]; int i,j; float fx,fy; //determine containing cell; get_barycentric((point[0])/dx-0.5f, i, fx, 0, ni); get_barycentric((point[1])/dx-0.5f, j, fy, 0, nj); //compute distance to surrounding few points, keep if it's the minimum for(int j_off = j-2; j_off<=j+2; ++j_off) for(int i_off = i-2; i_off<=i+2; ++i_off) { if(i_off < 0 || i_off >= ni || j_off < 0 || j_off >= nj) continue; Vec2f pos((i_off+0.5f)*dx, (j_off+0.5f)*dx); float phi_temp = dist(pos, point) - 1.02f*particle_radius; liquid_phi(i_off,j_off) = min(liquid_phi(i_off,j_off), phi_temp); } } //"extrapolate" phi into solids if nearby for(int j = 0; j < nj; ++j) { for(int i = 0; i < ni; ++i) { if(liquid_phi(i,j) < 0.5*dx) { float solid_phi_val = 0.25f*(nodal_solid_phi(i,j) + nodal_solid_phi(i+1,j) + nodal_solid_phi(i,j+1) + nodal_solid_phi(i+1,j+1)); if(solid_phi_val < 0) liquid_phi(i,j) = -0.5f*dx; } } } }
//Compute finite-volume style face-weights for fluid from nodal signed distances void FluidSim::compute_weights() { for(int j = 0; j < u_weights.nj; ++j) for(int i = 0; i < u_weights.ni; ++i) { u_weights(i,j) = 1 - fraction_inside(nodal_solid_phi(i,j+1), nodal_solid_phi(i,j)); } for(int j = 0; j < v_weights.nj; ++j) for(int i = 0; i < v_weights.ni; ++i) { v_weights(i,j) = 1 - fraction_inside(nodal_solid_phi(i+1,j), nodal_solid_phi(i,j)); } }
//Initialize the grid-based signed distance field that dictates the position of the solid boundary void FluidSim::set_boundary(float (*phi)(const Vec2f&)) { for(int j = 0; j < nj+1; ++j) for(int i = 0; i < ni+1; ++i) { Vec2f pos(i*dx,j*dx); nodal_solid_phi(i,j) = phi(pos); } }
//Initialize the grid-based signed distance field that dictates the position of the solid boundary void FluidSim::set_boundary(float (*phi)(const Vec3f&)) { for(int k = 0; k < nk+1; ++k) for(int j = 0; j < nj+1; ++j) for(int i = 0; i < ni+1; ++i) { Vec3f pos(i*dx,j*dx,k*dx); nodal_solid_phi(i,j,k) = phi(pos); } for(int k = 0; k < nk; ++k) for(int j = 0; j < nj; ++j) for(int i = 0; i < ni; ++i) { Vec3f pos((i+0.5f)*dx,(j+0.5f)*dx,(k+0.5f)*dx); cell_solid_phi(i,j,k) = phi(pos); } }
void FluidSim::compute_phi() { //grab from particles liquid_phi.assign(3*dx); for(unsigned int p = 0; p < particles.size(); ++p) { Vec3i cell_ind(particles[p] / dx); for(int k = max(0,cell_ind[2] - 1); k <= min(cell_ind[2]+1,nk-1); ++k) { for(int j = max(0,cell_ind[1] - 1); j <= min(cell_ind[1]+1,nj-1); ++j) { for(int i = max(0,cell_ind[0] - 1); i <= min(cell_ind[0]+1,ni-1); ++i) { Vec3f sample_pos((i+0.5f)*dx, (j+0.5f)*dx,(k+0.5f)*dx); float test_val = dist(sample_pos, particles[p]) - particle_radius; if(test_val < liquid_phi(i,j,k)) liquid_phi(i,j,k) = test_val; } } } } //extend phi slightly into solids (this is a simple, naive approach, but works reasonably well) Array3f phi_temp = liquid_phi; for(int k = 0; k < nk; ++k) { for(int j = 0; j < nj; ++j) { for(int i = 0; i < ni; ++i) { if(liquid_phi(i,j,k) < 0.5*dx) { float solid_phi_val = 0.125f*(nodal_solid_phi(i,j,k) + nodal_solid_phi(i+1,j,k) + nodal_solid_phi(i,j+1,k) + nodal_solid_phi(i+1,j+1,k) + nodal_solid_phi(i,j,k+1) + nodal_solid_phi(i+1,j,k+1) + nodal_solid_phi(i,j+1,k+1) + nodal_solid_phi(i+1,j+1,k+1)); if(solid_phi_val < 0) phi_temp(i,j,k) = -0.5f*dx; } } } } liquid_phi = phi_temp; }
//Compute finite-volume style face-weights for fluid from nodal signed distances void FluidSim::compute_weights() { //Compute face area fractions (using marching squares cases). for(int k = 0; k < nk; ++k) for(int j = 0; j < nj; ++j) for(int i = 0; i < ni+1; ++i) { u_weights(i,j,k) = 1 - fraction_inside(nodal_solid_phi(i,j, k), nodal_solid_phi(i,j+1,k), nodal_solid_phi(i,j, k+1), nodal_solid_phi(i,j+1,k+1)); u_weights(i,j,k) = clamp(u_weights(i,j,k),0.0f,1.0f); } for(int k = 0; k < nk; ++k) for(int j = 0; j < nj+1; ++j) for(int i = 0; i < ni; ++i) { v_weights(i,j,k) = 1 - fraction_inside(nodal_solid_phi(i, j,k), nodal_solid_phi(i, j,k+1), nodal_solid_phi(i+1,j,k), nodal_solid_phi(i+1,j,k+1)); v_weights(i,j,k) = clamp(v_weights(i,j,k),0.0f,1.0f); } for(int k = 0; k < nk+1; ++k) for(int j = 0; j < nj; ++j) for(int i = 0; i < ni; ++i) { w_weights(i,j,k) = 1 - fraction_inside(nodal_solid_phi(i, j, k), nodal_solid_phi(i, j+1,k), nodal_solid_phi(i+1,j, k), nodal_solid_phi(i+1,j+1,k)); w_weights(i,j,k) = clamp(w_weights(i,j,k),0.0f,1.0f); } }
void FluidSim::solve_viscosity(float dt) { int ni = liquid_phi.ni; int nj = liquid_phi.nj; //static obstacles for simplicity - for moving objects, //use a spatially varying 2d array, and modify the linear system appropriately float u_obj = 0; float v_obj = 0; Array2c u_state(ni+1,nj,(const char&)0); Array2c v_state(ni,nj+1,(const char&)0); const int SOLID = 1; const int FLUID = 0; printf("Determining states\n"); //just determine if the face position is inside the wall! That's it. for(int j = 0; j < nj; ++j) { for(int i = 0; i < ni+1; ++i) { if(i - 1 < 0 || i >= ni || (nodal_solid_phi(i,j+1) + nodal_solid_phi(i,j))/2 <= 0) u_state(i,j) = SOLID; else u_state(i,j) = FLUID; } } for(int j = 0; j < nj+1; ++j) { for(int i = 0; i < ni; ++i) { if(j - 1 < 0 || j >= nj || (nodal_solid_phi(i+1,j) + nodal_solid_phi(i,j))/2 <= 0) v_state(i,j) = SOLID; else v_state(i,j) = FLUID; } } printf("Building matrix\n"); int elts = (ni+1)*nj + ni*(nj+1); if(vrhs.size() != elts) { vrhs.resize(elts); velocities.resize(elts); vmatrix.resize(elts); } vmatrix.zero(); float factor = dt/sqr(dx); for(int j = 1; j < nj-1; ++j) for(int i = 1; i < ni-1; ++i) { if(u_state(i,j) == FLUID ) { int index = u_ind(i,j); vrhs[index] = u_vol(i,j) * u(i,j); vmatrix.set_element(index,index,u_vol(i,j)); //uxx terms float visc_right = viscosity(i,j); float visc_left = viscosity(i-1,j); float vol_right = c_vol(i,j); float vol_left = c_vol(i-1,j); //u_x_right vmatrix.add_to_element(index,index, 2*factor*visc_right*vol_right); if(u_state(i+1,j) == FLUID) vmatrix.add_to_element(index,u_ind(i+1,j), -2*factor*visc_right*vol_right); else if(u_state(i+1,j) == SOLID) vrhs[index] -= -2*factor*visc_right*vol_right*u_obj; //u_x_left vmatrix.add_to_element(index,index, 2*factor*visc_left*vol_left); if(u_state(i-1,j) == FLUID) vmatrix.add_to_element(index,u_ind(i-1,j), -2*factor*visc_left*vol_left); else if(u_state(i-1,j) == SOLID) vrhs[index] -= -2*factor*visc_left*vol_left*u_obj; //uyy terms float visc_top = 0.25f*(viscosity(i-1,j+1) + viscosity(i-1,j) + viscosity(i,j+1) + viscosity(i,j)); float visc_bottom = 0.25f*(viscosity(i-1,j) + viscosity(i-1,j-1) + viscosity(i,j) + viscosity(i,j-1)); float vol_top = n_vol(i,j+1); float vol_bottom = n_vol(i,j); //u_y_top vmatrix.add_to_element(index,index, +factor*visc_top*vol_top); if(u_state(i,j+1) == FLUID) vmatrix.add_to_element(index,u_ind(i,j+1), -factor*visc_top*vol_top); else if(u_state(i,j+1) == SOLID) vrhs[index] -= -u_obj*factor*visc_top*vol_top; //u_y_bottom vmatrix.add_to_element(index,index, +factor*visc_bottom*vol_bottom); if(u_state(i,j-1) == FLUID) vmatrix.add_to_element(index,u_ind(i,j-1), -factor*visc_bottom*vol_bottom); else if(u_state(i,j-1) == SOLID) vrhs[index] -= -u_obj*factor*visc_bottom*vol_bottom; //vxy terms //v_x_top if(v_state(i,j+1) == FLUID) vmatrix.add_to_element(index,v_ind(i,j+1), -factor*visc_top*vol_top); else if(v_state(i,j+1) == SOLID) vrhs[index] -= -v_obj*factor*visc_top*vol_top; if(v_state(i-1,j+1) == FLUID) vmatrix.add_to_element(index,v_ind(i-1,j+1), factor*visc_top*vol_top); else if(v_state(i-1,j+1) == SOLID) vrhs[index] -= v_obj*factor*visc_top*vol_top; //v_x_bottom if(v_state(i,j) == FLUID) vmatrix.add_to_element(index,v_ind(i,j), +factor*visc_bottom*vol_bottom); else if(v_state(i,j) == SOLID) vrhs[index] -= v_obj*factor*visc_bottom*vol_bottom; if(v_state(i-1,j) == FLUID) vmatrix.add_to_element(index,v_ind(i-1,j), -factor*visc_bottom*vol_bottom); else if(v_state(i-1,j) == SOLID) vrhs[index] -= -v_obj*factor*visc_bottom*vol_bottom; } } for(int j = 1; j < nj; ++j) for(int i = 1; i < ni-1; ++i) { if(v_state(i,j) == FLUID) { int index = v_ind(i,j); vrhs[index] = v_vol(i,j)*v(i,j); vmatrix.set_element(index, index, v_vol(i,j)); //vyy float visc_top = viscosity(i,j); float visc_bottom = viscosity(i,j-1); float vol_top = c_vol(i,j); float vol_bottom = c_vol(i,j-1); //vy_top vmatrix.add_to_element(index,index, +2*factor*visc_top*vol_top); if(v_state(i,j+1) == FLUID) vmatrix.add_to_element(index,v_ind(i,j+1), -2*factor*visc_top*vol_top); else if (v_state(i,j+1) == SOLID) vrhs[index] -= -2*factor*visc_top*vol_top*v_obj; //vy_bottom vmatrix.add_to_element(index,index, +2*factor*visc_bottom*vol_bottom); if(v_state(i,j-1) == FLUID) vmatrix.add_to_element(index,v_ind(i,j-1), -2*factor*visc_bottom*vol_bottom); else if(v_state(i,j-1) == SOLID) vrhs[index] -= -2*factor*visc_bottom*vol_bottom*v_obj; //vxx terms float visc_right = 0.25f*(viscosity(i,j-1) + viscosity(i+1,j-1) + viscosity(i,j) + viscosity(i+1,j)); float visc_left = 0.25f*(viscosity(i,j-1) + viscosity(i-1,j-1) + viscosity(i,j) + viscosity(i-1,j)); float vol_right = n_vol(i+1,j); float vol_left = n_vol(i,j); //v_x_right vmatrix.add_to_element(index,index, +factor*visc_right*vol_right); if(v_state(i+1,j) == FLUID) vmatrix.add_to_element(index,v_ind(i+1,j), -factor*visc_right*vol_right); else if(v_state(i+1,j) == SOLID) vrhs[index] -= -v_obj*factor*visc_right*vol_right; //v_x_left vmatrix.add_to_element(index,index, +factor*visc_left*vol_left); if(v_state(i-1,j) == FLUID) vmatrix.add_to_element(index,v_ind(i-1,j), -factor*visc_left*vol_left); else if(v_state(i-1,j) == SOLID) vrhs[index] -= -v_obj*factor*visc_left*vol_left; //uyx //u_y_right if(u_state(i+1,j) == FLUID) vmatrix.add_to_element(index,u_ind(i+1,j), -factor*visc_right*vol_right); else if(u_state(i+1,j) == SOLID) vrhs[index] -= -u_obj*factor*visc_right*vol_right; if(u_state(i+1,j-1) == FLUID) vmatrix.add_to_element(index,u_ind(i+1,j-1), factor*visc_right*vol_right); else if(u_state(i+1,j-1) == SOLID) vrhs[index] -= u_obj*factor*visc_right*vol_right; //u_y_left if(u_state(i,j) == FLUID) vmatrix.add_to_element(index,u_ind(i,j), factor*visc_left*vol_left); else if(u_state(i,j) == SOLID) vrhs[index] -= u_obj*factor*visc_left*vol_left; if(u_state(i,j-1) == FLUID) vmatrix.add_to_element(index,u_ind(i,j-1), -factor*visc_left*vol_left); else if(u_state(i,j-1) == SOLID) vrhs[index] -= -u_obj*factor*visc_left*vol_left; } } double res_out; int iter_out; solver.solve(vmatrix, vrhs, velocities, res_out, iter_out); for(int j = 0; j < nj; ++j) for(int i = 0; i < ni+1; ++i) if(u_state(i,j) == FLUID) u(i,j) = (float)velocities[u_ind(i,j)]; else if(u_state(i,j) == SOLID) u(i,j) = u_obj; for(int j = 0; j < nj+1; ++j) for(int i = 0; i < ni; ++i) if(v_state(i,j) == FLUID) v(i,j) = (float)velocities[v_ind(i,j)]; else if(v_state(i,j) == SOLID) v(i,j) = v_obj; }