//Perform 2nd order Runge Kutta to move the particles in the fluid void FluidSim::advect_particles(float dt) { for(unsigned int p = 0; p < particles.size(); ++p) { Vec2f before = particles[p]; Vec2f start_velocity = get_velocity(before); Vec2f midpoint = before + 0.5f*dt*start_velocity; Vec2f mid_velocity = get_velocity(midpoint); particles[p] += dt*mid_velocity; Vec2f after = particles[p]; if(dist(before,after) > 3*dx) { std::cout << "Before: " << before << " " << "After: " << after << std::endl; std::cout << "Mid point: " << midpoint << std::endl; std::cout << "Start velocity: " << start_velocity << " Time step: " << dt << std::endl; std::cout << "Mid velocity: " << mid_velocity << std::endl; } //Particles can still occasionally leave the domain due to truncation errors, //interpolation error, or large timesteps, so we project them back in for good measure. //Try commenting this section out to see the degree of accumulated error. float phi_value = interpolate_value(particles[p]/dx, nodal_solid_phi); if(phi_value < 0) { Vec2f normal; interpolate_gradient(normal, particles[p]/dx, nodal_solid_phi); normalize(normal); particles[p] -= phi_value*normal; } } }
float interpolate_normal(Vec3f& normal, const Vec3f& point, const Array3f& grid, const Vec3f& origin, const float dx) { float inv_dx = 1/dx; Vec3f temp = (point-origin)*inv_dx; float value = interpolate_gradient(normal, temp, grid); if(mag(normal) != 0) normalize(normal); return value; }
//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; //(At lower grid resolutions, the normal estimate from the signed //distance function is poor, so it doesn't work quite as well. //An exact normal would do better.) //constrain u for(int j = 0; j < u.nj; ++j) for(int i = 0; i < u.ni; ++i) { if(u_weights(i,j) == 0) { //apply constraint Vec2f pos(i*dx, (j+0.5f)*dx); Vec2f vel = get_velocity(pos); Vec2f normal(0,0); interpolate_gradient(normal, pos/dx, nodal_solid_phi); normalize(normal); float perp_component = dot(vel, normal); vel -= perp_component*normal; temp_u(i,j) = vel[0]; } } //constrain v for(int j = 0; j < v.nj; ++j) for(int i = 0; i < v.ni; ++i) { if(v_weights(i,j) == 0) { //apply constraint Vec2f pos((i+0.5f)*dx, j*dx); Vec2f vel = get_velocity(pos); Vec2f normal(0,0); interpolate_gradient(normal, pos/dx, nodal_solid_phi); normalize(normal); float perp_component = dot(vel, normal); vel -= perp_component*normal; temp_v(i,j) = vel[1]; } } //update u = temp_u; v = temp_v; }
void FluidSim::advect_particles(float dt) { for(unsigned int p = 0; p < particles.size(); ++p) { particles[p] = trace_rk2(particles[p], dt); //check boundaries and project exterior particles back in float phi_val = interpolate_value(particles[p]/dx, nodal_solid_phi); if(phi_val < 0) { Vec3f grad; interpolate_gradient(grad, particles[p]/dx, nodal_solid_phi); if(mag(grad) > 0) normalize(grad); particles[p] -= phi_val * grad; } } }
//Perform 2nd order Runge Kutta to move the particles in the fluid void FluidSim::advect_particles(float dt) { for(unsigned int p = 0; p < particles.size(); ++p) { particles[p] = trace_rk2(particles[p], dt); //Particles can still occasionally leave the domain due to truncation errors, //interpolation error, or large timesteps, so we project them back in for good measure. //Try commenting this section out to see the degree of accumulated error. float phi_value = interpolate_value(particles[p]/dx, nodal_solid_phi); if(phi_value < 0) { Vec2f normal; interpolate_gradient(normal, particles[p]/dx, nodal_solid_phi); normalize(normal); particles[p] -= phi_value*normal; } } }
void MarchingTriangles::eval_gradient( float y, float z, Vec2f& g ) { interpolate_gradient( g, Vec2f(y,z), phi ); }