//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); } }
//The following method records the final W and V weights void shadow :: record_final_weights(){ double temp; std::ofstream w_weights("W_weights.txt"); std::ofstream v_weights("V_weights.txt"); //Record all the information into W_weights_final W_weights_final = new double[number_of_elements+1]; for(int i=0; i < (number_of_elements+1); i++){ for(int j=0; j < length_of_input; j++){ W_weights_final[i] += W_weights[i][j]; } } std::cout << "W weights final is: " << std::endl; for(int i=0; i < (number_of_elements+1); i++){ W_weights_final[i] /= length_of_input; temp = W_weights_final[i]; w_weights << temp; w_weights << '\n'; std::cout << W_weights_final[i] << std::endl; } //Record all the information into V_weights_final V_weights_final = new double[(number_of_elements+1)*number_of_elements]; for(int i=0; i < (number_of_elements+1)*number_of_elements; i++){ for(int j=0; j < length_of_input; j++){ V_weights_final[i] += V_weights[i][j]; } } std::cout << "V weights final is: " << std::endl; for(int i=0; i < (number_of_elements+1)*number_of_elements; i++){ V_weights_final[i] /= length_of_input; temp = V_weights_final[i]; std::cout << V_weights_final[i] << std::endl; v_weights << temp; v_weights << '\n'; if(i % (number_of_elements + 1) == 0){ v_weights << "Weights for next node...\n"; } } w_weights.close(); v_weights.close(); }
//For extrapolated points, replace the normal component //of velocity with the object velocity (in this case zero). void FluidSim::constrain_velocity() { temp_u = u; temp_v = v; temp_w = w; //(At lower grid resolutions, the normal estimate from the signed //distance function can be poor, so it doesn't work quite as well. //An exact normal would do better if we had it for the geometry.) //constrain u for(int k = 0; k < u.nk;++k) for(int j = 0; j < u.nj; ++j) for(int i = 0; i < u.ni; ++i) { if(u_weights(i,j,k) == 0) { //apply constraint temp_u(i,j,k) = 0;//vel[0]; } } //constrain v for(int k = 0; k < v.nk;++k) for(int j = 0; j < v.nj; ++j) for(int i = 0; i < v.ni; ++i) { if(v_weights(i,j,k) == 0) { //apply constraint temp_v(i,j,k) = 0; //vel[1]; } } //constrain w for(int k = 0; k < w.nk;++k) for(int j = 0; j < w.nj; ++j) for(int i = 0; i < w.ni; ++i) { if(w_weights(i,j,k) == 0) { //apply constraint temp_w(i,j,k) = 0; //vel[2]; } } //update u = temp_u; v = temp_v; w = temp_w; }
//An implementation of the variational pressure projection solve for static geometry void FluidSim::solve_pressure(float dt) { int ni = v.ni; int nj = u.nj; int nk = u.nk; int system_size = ni*nj*nk; if(rhs.size() != system_size) { rhs.resize(system_size); pressure.resize(system_size); matrix.resize(system_size); } matrix.zero(); rhs.assign(rhs.size(), 0); pressure.assign(pressure.size(), 0); //Build the linear system for pressure for(int k = 1; k < nk-1; ++k) { for(int j = 1; j < nj-1; ++j) { for(int i = 1; i < ni-1; ++i) { int index = i + ni*j + ni*nj*k; rhs[index] = 0; pressure[index] = 0; float centre_phi = liquid_phi(i,j,k); if(centre_phi < 0) { //right neighbour float term = u_weights(i+1,j,k) * dt / sqr(dx); float right_phi = liquid_phi(i+1,j,k); if(right_phi < 0) { matrix.add_to_element(index, index, term); matrix.add_to_element(index, index + 1, -term); } else { float theta = fraction_inside(centre_phi, right_phi); if(theta < 0.01f) theta = 0.01f; matrix.add_to_element(index, index, term/theta); } rhs[index] -= u_weights(i+1,j,k)*u(i+1,j,k) / dx; //left neighbour term = u_weights(i,j,k) * dt / sqr(dx); float left_phi = liquid_phi(i-1,j,k); if(left_phi < 0) { matrix.add_to_element(index, index, term); matrix.add_to_element(index, index - 1, -term); } else { float theta = fraction_inside(centre_phi, left_phi); if(theta < 0.01f) theta = 0.01f; matrix.add_to_element(index, index, term/theta); } rhs[index] += u_weights(i,j,k)*u(i,j,k) / dx; //top neighbour term = v_weights(i,j+1,k) * dt / sqr(dx); float top_phi = liquid_phi(i,j+1,k); if(top_phi < 0) { matrix.add_to_element(index, index, term); matrix.add_to_element(index, index + ni, -term); } else { float theta = fraction_inside(centre_phi, top_phi); if(theta < 0.01f) theta = 0.01f; matrix.add_to_element(index, index, term/theta); } rhs[index] -= v_weights(i,j+1,k)*v(i,j+1,k) / dx; //bottom neighbour term = v_weights(i,j,k) * dt / sqr(dx); float bot_phi = liquid_phi(i,j-1,k); if(bot_phi < 0) { matrix.add_to_element(index, index, term); matrix.add_to_element(index, index - ni, -term); } else { float theta = fraction_inside(centre_phi, bot_phi); if(theta < 0.01f) theta = 0.01f; matrix.add_to_element(index, index, term/theta); } rhs[index] += v_weights(i,j,k)*v(i,j,k) / dx; //far neighbour term = w_weights(i,j,k+1) * dt / sqr(dx); float far_phi = liquid_phi(i,j,k+1); if(far_phi < 0) { matrix.add_to_element(index, index, term); matrix.add_to_element(index, index + ni*nj, -term); } else { float theta = fraction_inside(centre_phi, far_phi); if(theta < 0.01f) theta = 0.01f; matrix.add_to_element(index, index, term/theta); } rhs[index] -= w_weights(i,j,k+1)*w(i,j,k+1) / dx; //near neighbour term = w_weights(i,j,k) * dt / sqr(dx); float near_phi = liquid_phi(i,j,k-1); if(near_phi < 0) { matrix.add_to_element(index, index, term); matrix.add_to_element(index, index - ni*nj, -term); } else { float theta = fraction_inside(centre_phi, near_phi); if(theta < 0.01f) theta = 0.01f; matrix.add_to_element(index, index, term/theta); } rhs[index] += w_weights(i,j,k)*w(i,j,k) / dx; /* //far neighbour term = w_weights(i,j,k+1) * dt / sqr(dx); float far_phi = liquid_phi(i,j,k+1); if(far_phi < 0) { matrix.add_to_element(index, index, term); matrix.add_to_element(index, index + ni*nj, -term); } else { float theta = fraction_inside(centre_phi, far_phi); if(theta < 0.01f) theta = 0.01f; matrix.add_to_element(index, index, term/theta); } rhs[index] -= w_weights(i,j,k+1)*w(i,j,k+1) / dx; //near neighbour term = w_weights(i,j,k) * dt / sqr(dx); float near_phi = liquid_phi(i,j,k-1); if(near_phi < 0) { matrix.add_to_element(index, index, term); matrix.add_to_element(index, index - ni*nj, -term); } else { float theta = fraction_inside(centre_phi, near_phi); if(theta < 0.01f) theta = 0.01f; matrix.add_to_element(index, index, term/theta); } rhs[index] += w_weights(i,j,k)*w(i,j,k) / dx; */ } } } } //Solve the system using Robert Bridson's incomplete Cholesky PCG solver double tolerance; int iterations; solver.set_solver_parameters(1e-18, 1000); bool success = solver.solve(matrix, rhs, pressure, tolerance, iterations); //printf("Solver took %d iterations and had residual %e\n", iterations, tolerance); if(!success) { printf("WARNING: Pressure solve failed!************************************************\n"); } //Apply the velocity update u_valid.assign(0); for(int k = 0; k < u.nk; ++k) for(int j = 0; j < u.nj; ++j) for(int i = 1; i < u.ni-1; ++i) { int index = i + j*ni + k*ni*nj; if(u_weights(i,j,k) > 0 && (liquid_phi(i,j,k) < 0 || liquid_phi(i-1,j,k) < 0)) { float theta = 1; if(liquid_phi(i,j,k) >= 0 || liquid_phi(i-1,j,k) >= 0) theta = fraction_inside(liquid_phi(i-1,j,k), liquid_phi(i,j,k)); if(theta < 0.01f) theta = 0.01f; u(i,j,k) -= dt * (float)(pressure[index] - pressure[index-1]) / dx / theta; u_valid(i,j,k) = 1; } } v_valid.assign(0); for(int k = 0; k < v.nk; ++k) for(int j = 1; j < v.nj-1; ++j) for(int i = 0; i < v.ni; ++i) { int index = i + j*ni + k*ni*nj; if(v_weights(i,j,k) > 0 && (liquid_phi(i,j,k) < 0 || liquid_phi(i,j-1,k) < 0)) { float theta = 1; if(liquid_phi(i,j,k) >= 0 || liquid_phi(i,j-1,k) >= 0) theta = fraction_inside(liquid_phi(i,j-1,k), liquid_phi(i,j,k)); if(theta < 0.01f) theta = 0.01f; v(i,j,k) -= dt * (float)(pressure[index] - pressure[index-ni]) / dx / theta; v_valid(i,j,k) = 1; } } w_valid.assign(0); for(int k = 1; k < w.nk-1; ++k) for(int j = 0; j < w.nj; ++j) for(int i = 0; i < w.ni; ++i) { int index = i + j*ni + k*ni*nj; if(w_weights(i,j,k) > 0 && (liquid_phi(i,j,k) < 0 || liquid_phi(i,j,k-1) < 0)) { float theta = 1; if(liquid_phi(i,j,k) >= 0 || liquid_phi(i,j,k-1) >= 0) theta = fraction_inside(liquid_phi(i,j,k-1), liquid_phi(i,j,k)); if(theta < 0.01f) theta = 0.01f; w(i,j,k) -= dt * (float)(pressure[index] - pressure[index-ni*nj]) / dx / theta; w_valid(i,j,k) = 1; } } for(unsigned int i = 0; i < u_valid.a.size(); ++i) if(u_valid.a[i] == 0) u.a[i] = 0; for(unsigned int i = 0; i < v_valid.a.size(); ++i) if(v_valid.a[i] == 0) v.a[i] = 0; for(unsigned int i = 0; i < w_valid.a.size(); ++i) if(w_valid.a[i] == 0) w.a[i] = 0; }