870 lines
28 KiB
C++
870 lines
28 KiB
C++
/*
|
|
FLUIDS v.1 - SPH Fluid Simulator for CPU and GPU
|
|
Copyright (C) 2008. Rama Hoetzlein, http://www.rchoetzlein.com
|
|
|
|
ZLib license
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
|
|
|
|
|
|
#include <conio.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#include <gl/glut.h>
|
|
#else
|
|
#include <GL/glut.h>
|
|
#endif
|
|
|
|
#include "common_defs.h"
|
|
#include "mtime.h"
|
|
#include "fluid_system.h"
|
|
|
|
#ifdef BUILD_CUDA
|
|
#include "fluid_system_host.cuh"
|
|
#endif
|
|
|
|
#define EPSILON 0.00001f //for collision detection
|
|
|
|
FluidSystem::FluidSystem ()
|
|
{
|
|
}
|
|
|
|
void FluidSystem::Initialize ( int mode, int total )
|
|
{
|
|
if ( mode != BFLUID ) {
|
|
printf ( "ERROR: FluidSystem not initialized as BFLUID.\n");
|
|
}
|
|
PointSet::Initialize ( mode, total );
|
|
|
|
FreeBuffers ();
|
|
AddBuffer ( BFLUID, sizeof ( Fluid ), total );
|
|
AddAttribute ( 0, "pos", sizeof ( Vector3DF ), false );
|
|
AddAttribute ( 0, "color", sizeof ( DWORD ), false );
|
|
AddAttribute ( 0, "vel", sizeof ( Vector3DF ), false );
|
|
AddAttribute ( 0, "ndx", sizeof ( unsigned short ), false );
|
|
AddAttribute ( 0, "age", sizeof ( unsigned short ), false );
|
|
|
|
AddAttribute ( 0, "pressure", sizeof ( double ), false );
|
|
AddAttribute ( 0, "density", sizeof ( double ), false );
|
|
AddAttribute ( 0, "sph_force", sizeof ( Vector3DF ), false );
|
|
AddAttribute ( 0, "next", sizeof ( Fluid* ), false );
|
|
AddAttribute ( 0, "tag", sizeof ( bool ), false );
|
|
|
|
SPH_Setup ();
|
|
Reset ( total );
|
|
}
|
|
|
|
void FluidSystem::Reset ( int nmax )
|
|
{
|
|
ResetBuffer ( 0, nmax );
|
|
|
|
m_DT = 0.003; // 0.001; // .001 = for point grav
|
|
|
|
// Reset parameters
|
|
m_Param [ MAX_FRAC ] = 1.0;
|
|
m_Param [ POINT_GRAV ] = 0.0;
|
|
m_Param [ PLANE_GRAV ] = 1.0;
|
|
|
|
m_Param [ BOUND_ZMIN_SLOPE ] = 0.0;
|
|
m_Param [ FORCE_XMAX_SIN ] = 0.0;
|
|
m_Param [ FORCE_XMIN_SIN ] = 0.0;
|
|
m_Toggle [ WRAP_X ] = false;
|
|
m_Toggle [ WALL_BARRIER ] = false;
|
|
m_Toggle [ LEVY_BARRIER ] = false;
|
|
m_Toggle [ DRAIN_BARRIER ] = false;
|
|
m_Param [ SPH_INTSTIFF ] = 1.00;
|
|
m_Param [ SPH_VISC ] = 0.2;
|
|
m_Param [ SPH_INTSTIFF ] = 0.50;
|
|
m_Param [ SPH_EXTSTIFF ] = 20000;
|
|
m_Param [ SPH_SMOOTHRADIUS ] = 0.01;
|
|
|
|
m_Vec [ POINT_GRAV_POS ].Set ( 0, 0, 50 );
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0, 0, -9.8 );
|
|
m_Vec [ EMIT_POS ].Set ( 0, 0, 0 );
|
|
m_Vec [ EMIT_RATE ].Set ( 0, 0, 0 );
|
|
m_Vec [ EMIT_ANG ].Set ( 0, 90, 1.0 );
|
|
m_Vec [ EMIT_DANG ].Set ( 0, 0, 0 );
|
|
}
|
|
|
|
int FluidSystem::AddPoint ()
|
|
{
|
|
xref ndx;
|
|
Fluid* f = (Fluid*) AddElem ( 0, ndx );
|
|
f->sph_force.Set(0,0,0);
|
|
f->vel.Set(0,0,0);
|
|
f->vel_eval.Set(0,0,0);
|
|
f->next = 0x0;
|
|
f->pressure = 0;
|
|
f->density = 0;
|
|
return ndx;
|
|
}
|
|
|
|
int FluidSystem::AddPointReuse ()
|
|
{
|
|
xref ndx;
|
|
Fluid* f;
|
|
if ( NumPoints() <= mBuf[0].max-2 )
|
|
f = (Fluid*) AddElem ( 0, ndx );
|
|
else
|
|
f = (Fluid*) RandomElem ( 0, ndx );
|
|
|
|
f->sph_force.Set(0,0,0);
|
|
f->vel.Set(0,0,0);
|
|
f->vel_eval.Set(0,0,0);
|
|
f->next = 0x0;
|
|
f->pressure = 0;
|
|
f->density = 0;
|
|
return ndx;
|
|
}
|
|
|
|
void FluidSystem::Run ()
|
|
{
|
|
bool bTiming = false;//true;
|
|
|
|
mint::Time start, stop;
|
|
|
|
float ss = m_Param [ SPH_PDIST ] / m_Param[ SPH_SIMSCALE ]; // simulation scale (not Schutzstaffel)
|
|
|
|
if ( m_Vec[EMIT_RATE].x > 0 && (++m_Frame) % (int) m_Vec[EMIT_RATE].x == 0 ) {
|
|
//m_Frame = 0;
|
|
Emit ( ss );
|
|
}
|
|
|
|
#ifdef NOGRID
|
|
// Slow method - O(n^2)
|
|
SPH_ComputePressureSlow ();
|
|
SPH_ComputeForceSlow ();
|
|
#else
|
|
|
|
if ( m_Toggle[USE_CUDA] ) {
|
|
|
|
#ifdef BUILD_CUDA
|
|
// -- GPU --
|
|
start.SetSystemTime ( ACC_NSEC );
|
|
TransferToCUDA ( mBuf[0].data, (int*) &m_Grid[0], NumPoints() );
|
|
if ( bTiming) { stop.SetSystemTime ( ACC_NSEC ); stop = stop - start; printf ( "TO: %s\n", stop.GetReadableTime().c_str() ); }
|
|
|
|
start.SetSystemTime ( ACC_NSEC );
|
|
Grid_InsertParticlesCUDA ();
|
|
if ( bTiming) { stop.SetSystemTime ( ACC_NSEC ); stop = stop - start; printf ( "INSERT (CUDA): %s\n", stop.GetReadableTime().c_str() ); }
|
|
|
|
start.SetSystemTime ( ACC_NSEC );
|
|
SPH_ComputePressureCUDA ();
|
|
if ( bTiming) { stop.SetSystemTime ( ACC_NSEC ); stop = stop - start; printf ( "PRESS (CUDA): %s\n", stop.GetReadableTime().c_str() ); }
|
|
|
|
start.SetSystemTime ( ACC_NSEC );
|
|
SPH_ComputeForceCUDA ();
|
|
if ( bTiming) { stop.SetSystemTime ( ACC_NSEC ); stop = stop - start; printf ( "FORCE (CUDA): %s\n", stop.GetReadableTime().c_str() ); }
|
|
|
|
//** CUDA integrator is incomplete..
|
|
// Once integrator is done, we can remove TransferTo/From steps
|
|
/*start.SetSystemTime ( ACC_NSEC );
|
|
SPH_AdvanceCUDA( m_DT, m_DT/m_Param[SPH_SIMSCALE] );
|
|
if ( bTiming) { stop.SetSystemTime ( ACC_NSEC ); stop = stop - start; printf ( "ADV (CUDA): %s\n", stop.GetReadableTime().c_str() ); }*/
|
|
|
|
start.SetSystemTime ( ACC_NSEC );
|
|
TransferFromCUDA ( mBuf[0].data, (int*) &m_Grid[0], NumPoints() );
|
|
if ( bTiming) { stop.SetSystemTime ( ACC_NSEC ); stop = stop - start; printf ( "FROM: %s\n", stop.GetReadableTime().c_str() ); }
|
|
|
|
// .. Do advance on CPU
|
|
Advance();
|
|
|
|
#endif
|
|
|
|
} else {
|
|
// -- CPU only --
|
|
|
|
start.SetSystemTime ( ACC_NSEC );
|
|
Grid_InsertParticles ();
|
|
if ( bTiming) { stop.SetSystemTime ( ACC_NSEC ); stop = stop - start; printf ( "INSERT: %s\n", stop.GetReadableTime().c_str() ); }
|
|
|
|
start.SetSystemTime ( ACC_NSEC );
|
|
SPH_ComputePressureGrid ();
|
|
if ( bTiming) { stop.SetSystemTime ( ACC_NSEC ); stop = stop - start; printf ( "PRESS: %s\n", stop.GetReadableTime().c_str() ); }
|
|
|
|
start.SetSystemTime ( ACC_NSEC );
|
|
SPH_ComputeForceGridNC ();
|
|
if ( bTiming) { stop.SetSystemTime ( ACC_NSEC ); stop = stop - start; printf ( "FORCE: %s\n", stop.GetReadableTime().c_str() ); }
|
|
|
|
start.SetSystemTime ( ACC_NSEC );
|
|
Advance();
|
|
if ( bTiming) { stop.SetSystemTime ( ACC_NSEC ); stop = stop - start; printf ( "ADV: %s\n", stop.GetReadableTime().c_str() ); }
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
void FluidSystem::SPH_DrawDomain ()
|
|
{
|
|
Vector3DF min, max;
|
|
min = m_Vec[SPH_VOLMIN];
|
|
max = m_Vec[SPH_VOLMAX];
|
|
min.z += 0.5;
|
|
|
|
glColor3f ( 0.0, 0.0, 1.0 );
|
|
glBegin ( GL_LINES );
|
|
glVertex3f ( min.x, min.y, min.z ); glVertex3f ( max.x, min.y, min.z );
|
|
glVertex3f ( min.x, max.y, min.z ); glVertex3f ( max.x, max.y, min.z );
|
|
glVertex3f ( min.x, min.y, min.z ); glVertex3f ( min.x, max.y, min.z );
|
|
glVertex3f ( max.x, min.y, min.z ); glVertex3f ( max.x, max.y, min.z );
|
|
glEnd ();
|
|
}
|
|
|
|
void FluidSystem::Advance ()
|
|
{
|
|
char *dat1, *dat1_end;
|
|
Fluid* p;
|
|
Vector3DF norm, z;
|
|
Vector3DF dir, accel;
|
|
Vector3DF vnext;
|
|
Vector3DF min, max;
|
|
double adj;
|
|
float SL, SL2, ss, radius;
|
|
float stiff, damp, speed, diff;
|
|
SL = m_Param[SPH_LIMIT];
|
|
SL2 = SL*SL;
|
|
|
|
stiff = m_Param[SPH_EXTSTIFF];
|
|
damp = m_Param[SPH_EXTDAMP];
|
|
radius = m_Param[SPH_PRADIUS];
|
|
min = m_Vec[SPH_VOLMIN];
|
|
max = m_Vec[SPH_VOLMAX];
|
|
ss = m_Param[SPH_SIMSCALE];
|
|
|
|
dat1_end = mBuf[0].data + NumPoints()*mBuf[0].stride;
|
|
for ( dat1 = mBuf[0].data; dat1 < dat1_end; dat1 += mBuf[0].stride ) {
|
|
p = (Fluid*) dat1;
|
|
|
|
// Compute Acceleration
|
|
accel = p->sph_force;
|
|
accel *= m_Param[SPH_PMASS];
|
|
|
|
// Velocity limiting
|
|
speed = accel.x*accel.x + accel.y*accel.y + accel.z*accel.z;
|
|
if ( speed > SL2 ) {
|
|
accel *= SL / sqrt(speed);
|
|
}
|
|
|
|
// Boundary Conditions
|
|
|
|
// Z-axis walls
|
|
diff = 2 * radius - ( p->pos.z - min.z - (p->pos.x - m_Vec[SPH_VOLMIN].x) * m_Param[BOUND_ZMIN_SLOPE] )*ss;
|
|
if (diff > EPSILON ) {
|
|
norm.Set ( -m_Param[BOUND_ZMIN_SLOPE], 0, 1.0 - m_Param[BOUND_ZMIN_SLOPE] );
|
|
adj = stiff * diff - damp * norm.Dot ( p->vel_eval );
|
|
accel.x += adj * norm.x; accel.y += adj * norm.y; accel.z += adj * norm.z;
|
|
}
|
|
|
|
diff = 2 * radius - ( max.z - p->pos.z )*ss;
|
|
if (diff > EPSILON) {
|
|
norm.Set ( 0, 0, -1 );
|
|
adj = stiff * diff - damp * norm.Dot ( p->vel_eval );
|
|
accel.x += adj * norm.x; accel.y += adj * norm.y; accel.z += adj * norm.z;
|
|
}
|
|
|
|
// X-axis walls
|
|
if ( !m_Toggle[WRAP_X] ) {
|
|
diff = 2 * radius - ( p->pos.x - min.x + (sin(m_Time*10.0)-1+(p->pos.y*0.025)*0.25) * m_Param[FORCE_XMIN_SIN] )*ss;
|
|
//diff = 2 * radius - ( p->pos.x - min.x + (sin(m_Time*10.0)-1) * m_Param[FORCE_XMIN_SIN] )*ss;
|
|
if (diff > EPSILON ) {
|
|
norm.Set ( 1.0, 0, 0 );
|
|
adj = (m_Param[ FORCE_XMIN_SIN ] + 1) * stiff * diff - damp * norm.Dot ( p->vel_eval ) ;
|
|
accel.x += adj * norm.x; accel.y += adj * norm.y; accel.z += adj * norm.z;
|
|
}
|
|
|
|
diff = 2 * radius - ( max.x - p->pos.x + (sin(m_Time*10.0)-1) * m_Param[FORCE_XMAX_SIN] )*ss;
|
|
if (diff > EPSILON) {
|
|
norm.Set ( -1, 0, 0 );
|
|
adj = (m_Param[ FORCE_XMAX_SIN ]+1) * stiff * diff - damp * norm.Dot ( p->vel_eval );
|
|
accel.x += adj * norm.x; accel.y += adj * norm.y; accel.z += adj * norm.z;
|
|
}
|
|
}
|
|
|
|
// Y-axis walls
|
|
diff = 2 * radius - ( p->pos.y - min.y )*ss;
|
|
if (diff > EPSILON) {
|
|
norm.Set ( 0, 1, 0 );
|
|
adj = stiff * diff - damp * norm.Dot ( p->vel_eval );
|
|
accel.x += adj * norm.x; accel.y += adj * norm.y; accel.z += adj * norm.z;
|
|
}
|
|
diff = 2 * radius - ( max.y - p->pos.y )*ss;
|
|
if (diff > EPSILON) {
|
|
norm.Set ( 0, -1, 0 );
|
|
adj = stiff * diff - damp * norm.Dot ( p->vel_eval );
|
|
accel.x += adj * norm.x; accel.y += adj * norm.y; accel.z += adj * norm.z;
|
|
}
|
|
|
|
// Wall barrier
|
|
if ( m_Toggle[WALL_BARRIER] ) {
|
|
diff = 2 * radius - ( p->pos.x - 0 )*ss;
|
|
if (diff < 2*radius && diff > EPSILON && fabs(p->pos.y) < 3 && p->pos.z < 10) {
|
|
norm.Set ( 1.0, 0, 0 );
|
|
adj = 2*stiff * diff - damp * norm.Dot ( p->vel_eval ) ;
|
|
accel.x += adj * norm.x; accel.y += adj * norm.y; accel.z += adj * norm.z;
|
|
}
|
|
}
|
|
|
|
// Levy barrier
|
|
if ( m_Toggle[LEVY_BARRIER] ) {
|
|
diff = 2 * radius - ( p->pos.x - 0 )*ss;
|
|
if (diff < 2*radius && diff > EPSILON && fabs(p->pos.y) > 5 && p->pos.z < 10) {
|
|
norm.Set ( 1.0, 0, 0 );
|
|
adj = 2*stiff * diff - damp * norm.Dot ( p->vel_eval ) ;
|
|
accel.x += adj * norm.x; accel.y += adj * norm.y; accel.z += adj * norm.z;
|
|
}
|
|
}
|
|
// Drain barrier
|
|
if ( m_Toggle[DRAIN_BARRIER] ) {
|
|
diff = 2 * radius - ( p->pos.z - min.z-15 )*ss;
|
|
if (diff < 2*radius && diff > EPSILON && (fabs(p->pos.x)>3 || fabs(p->pos.y)>3) ) {
|
|
norm.Set ( 0, 0, 1);
|
|
adj = stiff * diff - damp * norm.Dot ( p->vel_eval );
|
|
accel.x += adj * norm.x; accel.y += adj * norm.y; accel.z += adj * norm.z;
|
|
}
|
|
}
|
|
|
|
// Plane gravity
|
|
if ( m_Param[PLANE_GRAV] > 0)
|
|
accel += m_Vec[PLANE_GRAV_DIR];
|
|
|
|
// Point gravity
|
|
if ( m_Param[POINT_GRAV] > 0 ) {
|
|
norm.x = ( p->pos.x - m_Vec[POINT_GRAV_POS].x );
|
|
norm.y = ( p->pos.y - m_Vec[POINT_GRAV_POS].y );
|
|
norm.z = ( p->pos.z - m_Vec[POINT_GRAV_POS].z );
|
|
norm.Normalize ();
|
|
norm *= m_Param[POINT_GRAV];
|
|
accel -= norm;
|
|
}
|
|
|
|
// Leapfrog Integration ----------------------------
|
|
vnext = accel;
|
|
vnext *= m_DT;
|
|
vnext += p->vel; // v(t+1/2) = v(t-1/2) + a(t) dt
|
|
p->vel_eval = p->vel;
|
|
p->vel_eval += vnext;
|
|
p->vel_eval *= 0.5; // v(t+1) = [v(t-1/2) + v(t+1/2)] * 0.5 used to compute forces later
|
|
p->vel = vnext;
|
|
vnext *= m_DT/ss;
|
|
p->pos += vnext; // p(t+1) = p(t) + v(t+1/2) dt
|
|
|
|
if ( m_Param[CLR_MODE]==1.0 ) {
|
|
adj = fabs(vnext.x)+fabs(vnext.y)+fabs(vnext.z) / 7000.0;
|
|
adj = (adj > 1.0) ? 1.0 : adj;
|
|
p->clr = COLORA( 0, adj, adj, 1 );
|
|
}
|
|
if ( m_Param[CLR_MODE]==2.0 ) {
|
|
float v = 0.5 + ( p->pressure / 1500.0);
|
|
if ( v < 0.1 ) v = 0.1;
|
|
if ( v > 1.0 ) v = 1.0;
|
|
p->clr = COLORA ( v, 1-v, 0, 1 );
|
|
}
|
|
|
|
|
|
// Euler integration -------------------------------
|
|
/* accel += m_Gravity;
|
|
accel *= m_DT;
|
|
p->vel += accel; // v(t+1) = v(t) + a(t) dt
|
|
p->vel_eval += accel;
|
|
p->vel_eval *= m_DT/d;
|
|
p->pos += p->vel_eval;
|
|
p->vel_eval = p->vel; */
|
|
|
|
|
|
if ( m_Toggle[WRAP_X] ) {
|
|
diff = p->pos.x - (m_Vec[SPH_VOLMIN].x + 2); // -- Simulates object in center of flow
|
|
if ( diff <= 0 ) {
|
|
p->pos.x = (m_Vec[SPH_VOLMAX].x - 2) + diff*2;
|
|
p->pos.z = 10;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_Time += m_DT;
|
|
}
|
|
|
|
//------------------------------------------------------ SPH Setup
|
|
//
|
|
// Range = +/- 10.0 * 0.006 (r) = 0.12 m (= 120 mm = 4.7 inch)
|
|
// Container Volume (Vc) = 0.001728 m^3
|
|
// Rest Density (D) = 1000.0 kg / m^3
|
|
// Particle Mass (Pm) = 0.00020543 kg (mass = vol * density)
|
|
// Number of Particles (N) = 4000.0
|
|
// Water Mass (M) = 0.821 kg (= 821 grams)
|
|
// Water Volume (V) = 0.000821 m^3 (= 3.4 cups, .21 gals)
|
|
// Smoothing Radius (R) = 0.02 m (= 20 mm = ~3/4 inch)
|
|
// Particle Radius (Pr) = 0.00366 m (= 4 mm = ~1/8 inch)
|
|
// Particle Volume (Pv) = 2.054e-7 m^3 (= .268 milliliters)
|
|
// Rest Distance (Pd) = 0.0059 m
|
|
//
|
|
// Given: D, Pm, N
|
|
// Pv = Pm / D 0.00020543 kg / 1000 kg/m^3 = 2.054e-7 m^3
|
|
// Pv = 4/3*pi*Pr^3 cuberoot( 2.054e-7 m^3 * 3/(4pi) ) = 0.00366 m
|
|
// M = Pm * N 0.00020543 kg * 4000.0 = 0.821 kg
|
|
// V = M / D 0.821 kg / 1000 kg/m^3 = 0.000821 m^3
|
|
// V = Pv * N 2.054e-7 m^3 * 4000 = 0.000821 m^3
|
|
// Pd = cuberoot(Pm/D) cuberoot(0.00020543/1000) = 0.0059 m
|
|
//
|
|
// Ideal grid cell size (gs) = 2 * smoothing radius = 0.02*2 = 0.04
|
|
// Ideal domain size = k*gs/d = k*0.02*2/0.005 = k*8 = {8, 16, 24, 32, 40, 48, ..}
|
|
// (k = number of cells, gs = cell size, d = simulation scale)
|
|
|
|
void FluidSystem::SPH_Setup ()
|
|
{
|
|
m_Param [ SPH_SIMSCALE ] = 0.004; // unit size
|
|
m_Param [ SPH_VISC ] = 0.2; // pascal-second (Pa.s) = 1 kg m^-1 s^-1 (see wikipedia page on viscosity)
|
|
m_Param [ SPH_RESTDENSITY ] = 600.0; // kg / m^3
|
|
m_Param [ SPH_PMASS ] = 0.00020543; // kg
|
|
m_Param [ SPH_PRADIUS ] = 0.004; // m
|
|
m_Param [ SPH_PDIST ] = 0.0059; // m
|
|
m_Param [ SPH_SMOOTHRADIUS ] = 0.01; // m
|
|
m_Param [ SPH_INTSTIFF ] = 1.00;
|
|
m_Param [ SPH_EXTSTIFF ] = 10000.0;
|
|
m_Param [ SPH_EXTDAMP ] = 256.0;
|
|
m_Param [ SPH_LIMIT ] = 200.0; // m / s
|
|
|
|
m_Toggle [ SPH_GRID ] = false;
|
|
m_Toggle [ SPH_DEBUG ] = false;
|
|
|
|
SPH_ComputeKernels ();
|
|
}
|
|
|
|
void FluidSystem::SPH_ComputeKernels ()
|
|
{
|
|
m_Param [ SPH_PDIST ] = pow ( m_Param[SPH_PMASS] / m_Param[SPH_RESTDENSITY], 1/3.0 );
|
|
m_R2 = m_Param [SPH_SMOOTHRADIUS] * m_Param[SPH_SMOOTHRADIUS];
|
|
m_Poly6Kern = 315.0f / (64.0f * 3.141592 * pow( m_Param[SPH_SMOOTHRADIUS], 9) ); // Wpoly6 kernel (denominator part) - 2003 Muller, p.4
|
|
m_SpikyKern = -45.0f / (3.141592 * pow( m_Param[SPH_SMOOTHRADIUS], 6) ); // Laplacian of viscocity (denominator): PI h^6
|
|
m_LapKern = 45.0f / (3.141592 * pow( m_Param[SPH_SMOOTHRADIUS], 6) );
|
|
}
|
|
|
|
void FluidSystem::SPH_CreateExample ( int n, int nmax )
|
|
{
|
|
Vector3DF pos;
|
|
Vector3DF min, max;
|
|
|
|
Reset ( nmax );
|
|
|
|
switch ( n ) {
|
|
case 0: // Wave pool
|
|
|
|
//-- TEST CASE: 2x2x2 grid, 32 particles. NOTE: Set PRADIUS to 0.0004 to reduce wall influence
|
|
// grid 0: 3*3*2 = 18 particles
|
|
// grid 1,2: 3*1*2 = 6 particles
|
|
// grid 3: 1*1*2 = 2 particles
|
|
// grid 4,5,6: 0 = 0 particles
|
|
/*m_Vec [ SPH_VOLMIN ].Set ( -2.5, -2.5, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 2.5, 2.5, 5.0 );
|
|
m_Vec [ SPH_INITMIN ].Set ( -2.5, -2.5, 0 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 2.5, 2.5, 1.6 );*/
|
|
|
|
m_Vec [ SPH_VOLMIN ].Set ( -30, -30, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 30, 30, 40 );
|
|
|
|
//m_Vec [ SPH_INITMIN ].Set ( -5, -5, 10 );
|
|
//m_Vec [ SPH_INITMAX ].Set ( 5, 5, 20 );
|
|
|
|
m_Vec [ SPH_INITMIN ].Set ( -20, -26, 10 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 20, 26, 40 );
|
|
|
|
m_Param [ FORCE_XMIN_SIN ] = 12.0;
|
|
m_Param [ BOUND_ZMIN_SLOPE ] = 0.05;
|
|
break;
|
|
case 1: // Dam break
|
|
m_Vec [ SPH_VOLMIN ].Set ( -30, -14, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 30, 14, 60 );
|
|
m_Vec [ SPH_INITMIN ].Set ( 0, -13, 0 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 29, 13, 30 );
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0.0, 0, -9.8 );
|
|
break;
|
|
case 2: // Dual-Wave pool
|
|
m_Vec [ SPH_VOLMIN ].Set ( -60, -5, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 60, 5, 50 );
|
|
m_Vec [ SPH_INITMIN ].Set ( -46, -5, 0 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 46, 5, 15 );
|
|
m_Param [ FORCE_XMIN_SIN ] = 8.0;
|
|
m_Param [ FORCE_XMAX_SIN ] = 8.0;
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0.0, 0, -9.8 );
|
|
break;
|
|
case 3: // Swirl Stream
|
|
m_Vec [ SPH_VOLMIN ].Set ( -30, -30, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 30, 30, 50 );
|
|
m_Vec [ SPH_INITMIN ].Set ( -30, -30, 0 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 30, 30, 40 );
|
|
m_Vec [ EMIT_POS ].Set ( -20, -20, 22 );
|
|
m_Vec [ EMIT_RATE ].Set ( 1, 4, 0 );
|
|
m_Vec [ EMIT_ANG ].Set ( 0, 120, 1.5 );
|
|
m_Vec [ EMIT_DANG ].Set ( 0, 0, 0 );
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0.0, 0, -9.8 );
|
|
break;
|
|
case 4: // Shockwave
|
|
m_Vec [ SPH_VOLMIN ].Set ( -60, -15, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 60, 15, 50 );
|
|
m_Vec [ SPH_INITMIN ].Set ( -59, -14, 0 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 59, 14, 30 );
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0.0, 0, -9.8 );
|
|
m_Toggle [ WALL_BARRIER ] = true;
|
|
m_Toggle [ WRAP_X ] = true;
|
|
break;
|
|
case 5: // Zero gravity
|
|
m_Vec [ SPH_VOLMIN ].Set ( -40, -40, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 40, 40, 50 );
|
|
m_Vec [ SPH_INITMIN ].Set ( -20, -20, 20 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 20, 20, 40 );
|
|
m_Vec [ EMIT_POS ].Set ( -20, 0, 40 );
|
|
m_Vec [ EMIT_RATE ].Set ( 2, 1, 0 );
|
|
m_Vec [ EMIT_ANG ].Set ( 0, 120, 0.25 );
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0, 0, 0 );
|
|
m_Param [ SPH_INTSTIFF ] = 0.20;
|
|
break;
|
|
case 6: // Point gravity
|
|
m_Vec [ SPH_VOLMIN ].Set ( -40, -40, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 40, 40, 50 );
|
|
m_Vec [ SPH_INITMIN ].Set ( -20, -20, 20 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 20, 20, 40 );
|
|
m_Param [ SPH_INTSTIFF ] = 0.50;
|
|
m_Vec [ EMIT_POS ].Set ( -20, 20, 25 );
|
|
m_Vec [ EMIT_RATE ].Set ( 1, 4, 0 );
|
|
m_Vec [ EMIT_ANG ].Set ( -20, 100, 2.0 );
|
|
m_Vec [ POINT_GRAV_POS ].Set ( 0, 0, 25 );
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0, 0, 0 );
|
|
m_Param [ POINT_GRAV ] = 3.5;
|
|
break;
|
|
case 7: // Levy break
|
|
m_Vec [ SPH_VOLMIN ].Set ( -40, -40, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 40, 40, 50 );
|
|
m_Vec [ SPH_INITMIN ].Set ( 10, -40, 0 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 40, 40, 50 );
|
|
m_Vec [ EMIT_POS ].Set ( 34, 27, 16.6 );
|
|
m_Vec [ EMIT_RATE ].Set ( 2, 9, 0 );
|
|
m_Vec [ EMIT_ANG ].Set ( 118, 200, 1.0 );
|
|
m_Toggle [ LEVY_BARRIER ] = true;
|
|
m_Param [ BOUND_ZMIN_SLOPE ] = 0.1;
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0.0, 0, -9.8 );
|
|
break;
|
|
case 8: // Drain
|
|
m_Vec [ SPH_VOLMIN ].Set ( -20, -20, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 20, 20, 50 );
|
|
m_Vec [ SPH_INITMIN ].Set ( -15, -20, 20 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 20, 20, 50 );
|
|
m_Vec [ EMIT_POS ].Set ( -16, -16, 30 );
|
|
m_Vec [ EMIT_RATE ].Set ( 1, 4, 0 );
|
|
m_Vec [ EMIT_ANG ].Set ( -20, 140, 1.8 );
|
|
m_Toggle [ DRAIN_BARRIER ] = true;
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0.0, 0, -9.8 );
|
|
break;
|
|
case 9: // Tumbler
|
|
m_Vec [ SPH_VOLMIN ].Set ( -30, -30, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 30, 30, 50 );
|
|
m_Vec [ SPH_INITMIN ].Set ( 24, -29, 20 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 29, 29, 40 );
|
|
m_Param [ SPH_VISC ] = 0.1;
|
|
m_Param [ SPH_INTSTIFF ] = 0.50;
|
|
m_Param [ SPH_EXTSTIFF ] = 8000;
|
|
//m_Param [ SPH_SMOOTHRADIUS ] = 0.01;
|
|
m_Param [ BOUND_ZMIN_SLOPE ] = 0.4;
|
|
m_Param [ FORCE_XMIN_SIN ] = 12.00;
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0.0, 0, -9.8 );
|
|
break;
|
|
case 10: // Large sim
|
|
m_Vec [ SPH_VOLMIN ].Set ( -35, -35, 0 );
|
|
m_Vec [ SPH_VOLMAX ].Set ( 35, 35, 60 );
|
|
m_Vec [ SPH_INITMIN ].Set ( -5, -35, 0 );
|
|
m_Vec [ SPH_INITMAX ].Set ( 30, 0, 60 );
|
|
m_Vec [ PLANE_GRAV_DIR ].Set ( 0.0, 0, -9.8 );
|
|
break;
|
|
}
|
|
|
|
SPH_ComputeKernels ();
|
|
|
|
m_Param [ SPH_SIMSIZE ] = m_Param [ SPH_SIMSCALE ] * (m_Vec[SPH_VOLMAX].z - m_Vec[SPH_VOLMIN].z);
|
|
m_Param [ SPH_PDIST ] = pow ( m_Param[SPH_PMASS] / m_Param[SPH_RESTDENSITY], 1/3.0 );
|
|
|
|
float ss = m_Param [ SPH_PDIST ]*0.87 / m_Param[ SPH_SIMSCALE ];
|
|
printf ( "Spacing: %f\n", ss);
|
|
AddVolume ( m_Vec[SPH_INITMIN], m_Vec[SPH_INITMAX], ss ); // Create the particles
|
|
|
|
float cell_size = m_Param[SPH_SMOOTHRADIUS]*2.0; // Grid cell size (2r)
|
|
Grid_Setup ( m_Vec[SPH_VOLMIN], m_Vec[SPH_VOLMAX], m_Param[SPH_SIMSCALE], cell_size, 1.0 ); // Setup grid
|
|
Grid_InsertParticles (); // Insert particles
|
|
|
|
Vector3DF vmin, vmax;
|
|
vmin = m_Vec[SPH_VOLMIN];
|
|
vmin -= Vector3DF(2,2,2);
|
|
vmax = m_Vec[SPH_VOLMAX];
|
|
vmax += Vector3DF(2,2,-2);
|
|
|
|
#ifdef BUILD_CUDA
|
|
FluidClearCUDA ();
|
|
Sleep ( 500 );
|
|
|
|
FluidSetupCUDA ( NumPoints(), sizeof(Fluid), *(float3*)& m_GridMin, *(float3*)& m_GridMax, *(float3*)& m_GridRes, *(float3*)& m_GridSize, (int) m_Vec[EMIT_RATE].x );
|
|
|
|
Sleep ( 500 );
|
|
|
|
FluidParamCUDA ( m_Param[SPH_SIMSCALE], m_Param[SPH_SMOOTHRADIUS], m_Param[SPH_PMASS], m_Param[SPH_RESTDENSITY], m_Param[SPH_INTSTIFF], m_Param[SPH_VISC] );
|
|
#endif
|
|
|
|
}
|
|
|
|
// Compute Pressures - Very slow yet simple. O(n^2)
|
|
void FluidSystem::SPH_ComputePressureSlow ()
|
|
{
|
|
char *dat1, *dat1_end;
|
|
char *dat2, *dat2_end;
|
|
Fluid *p, *q;
|
|
int cnt = 0;
|
|
double dx, dy, dz, sum, dsq, c;
|
|
double d, d2, mR, mR2;
|
|
d = m_Param[SPH_SIMSCALE];
|
|
d2 = d*d;
|
|
mR = m_Param[SPH_SMOOTHRADIUS];
|
|
mR2 = mR*mR;
|
|
|
|
dat1_end = mBuf[0].data + NumPoints()*mBuf[0].stride;
|
|
for ( dat1 = mBuf[0].data; dat1 < dat1_end; dat1 += mBuf[0].stride ) {
|
|
p = (Fluid*) dat1;
|
|
|
|
sum = 0.0;
|
|
cnt = 0;
|
|
|
|
dat2_end = mBuf[0].data + NumPoints()*mBuf[0].stride;
|
|
for ( dat2 = mBuf[0].data; dat2 < dat2_end; dat2 += mBuf[0].stride ) {
|
|
q = (Fluid*) dat2;
|
|
|
|
if ( p==q ) continue;
|
|
dx = ( p->pos.x - q->pos.x)*d; // dist in cm
|
|
dy = ( p->pos.y - q->pos.y)*d;
|
|
dz = ( p->pos.z - q->pos.z)*d;
|
|
dsq = (dx*dx + dy*dy + dz*dz);
|
|
if ( mR2 > dsq ) {
|
|
c = m_R2 - dsq;
|
|
sum += c * c * c;
|
|
cnt++;
|
|
//if ( p == m_CurrP ) q->tag = true;
|
|
}
|
|
}
|
|
p->density = sum * m_Param[SPH_PMASS] * m_Poly6Kern ;
|
|
p->pressure = ( p->density - m_Param[SPH_RESTDENSITY] ) * m_Param[SPH_INTSTIFF];
|
|
p->density = 1.0f / p->density;
|
|
}
|
|
}
|
|
|
|
// Compute Pressures - Using spatial grid, and also create neighbor table
|
|
void FluidSystem::SPH_ComputePressureGrid ()
|
|
{
|
|
char *dat1, *dat1_end;
|
|
Fluid* p;
|
|
Fluid* pcurr;
|
|
int pndx;
|
|
int i, cnt = 0;
|
|
float dx, dy, dz, sum, dsq, c;
|
|
float d, d2, mR, mR2;
|
|
float radius = m_Param[SPH_SMOOTHRADIUS] / m_Param[SPH_SIMSCALE];
|
|
d = m_Param[SPH_SIMSCALE];
|
|
d2 = d*d;
|
|
mR = m_Param[SPH_SMOOTHRADIUS];
|
|
mR2 = mR*mR;
|
|
|
|
dat1_end = mBuf[0].data + NumPoints()*mBuf[0].stride;
|
|
i = 0;
|
|
for ( dat1 = mBuf[0].data; dat1 < dat1_end; dat1 += mBuf[0].stride, i++ ) {
|
|
p = (Fluid*) dat1;
|
|
|
|
sum = 0.0;
|
|
m_NC[i] = 0;
|
|
|
|
Grid_FindCells ( p->pos, radius );
|
|
for (int cell=0; cell < 8; cell++) {
|
|
if ( m_GridCell[cell] != -1 ) {
|
|
pndx = m_Grid [ m_GridCell[cell] ];
|
|
while ( pndx != -1 ) {
|
|
pcurr = (Fluid*) (mBuf[0].data + pndx*mBuf[0].stride);
|
|
if ( pcurr == p ) {pndx = pcurr->next; continue; }
|
|
dx = ( p->pos.x - pcurr->pos.x)*d; // dist in cm
|
|
dy = ( p->pos.y - pcurr->pos.y)*d;
|
|
dz = ( p->pos.z - pcurr->pos.z)*d;
|
|
dsq = (dx*dx + dy*dy + dz*dz);
|
|
if ( mR2 > dsq ) {
|
|
c = m_R2 - dsq;
|
|
sum += c * c * c;
|
|
if ( m_NC[i] < MAX_NEIGHBOR ) {
|
|
m_Neighbor[i][ m_NC[i] ] = pndx;
|
|
m_NDist[i][ m_NC[i] ] = sqrt(dsq);
|
|
m_NC[i]++;
|
|
}
|
|
}
|
|
pndx = pcurr->next;
|
|
}
|
|
}
|
|
m_GridCell[cell] = -1;
|
|
}
|
|
p->density = sum * m_Param[SPH_PMASS] * m_Poly6Kern ;
|
|
p->pressure = ( p->density - m_Param[SPH_RESTDENSITY] ) * m_Param[SPH_INTSTIFF];
|
|
p->density = 1.0f / p->density;
|
|
}
|
|
}
|
|
|
|
// Compute Forces - Very slow, but simple. O(n^2)
|
|
void FluidSystem::SPH_ComputeForceSlow ()
|
|
{
|
|
char *dat1, *dat1_end;
|
|
char *dat2, *dat2_end;
|
|
Fluid *p, *q;
|
|
Vector3DF force, fcurr;
|
|
register double pterm, vterm, dterm;
|
|
double c, r, d, sum, dsq;
|
|
double dx, dy, dz;
|
|
double mR, mR2, visc;
|
|
|
|
d = m_Param[SPH_SIMSCALE];
|
|
mR = m_Param[SPH_SMOOTHRADIUS];
|
|
mR2 = (mR*mR);
|
|
visc = m_Param[SPH_VISC];
|
|
vterm = m_LapKern * visc;
|
|
|
|
dat1_end = mBuf[0].data + NumPoints()*mBuf[0].stride;
|
|
for ( dat1 = mBuf[0].data; dat1 < dat1_end; dat1 += mBuf[0].stride ) {
|
|
p = (Fluid*) dat1;
|
|
|
|
sum = 0.0;
|
|
force.Set ( 0, 0, 0 );
|
|
|
|
dat2_end = mBuf[0].data + NumPoints()*mBuf[0].stride;
|
|
for ( dat2 = mBuf[0].data; dat2 < dat2_end; dat2 += mBuf[0].stride ) {
|
|
q = (Fluid*) dat2;
|
|
|
|
if ( p == q ) continue;
|
|
dx = ( p->pos.x - q->pos.x )*d; // dist in cm
|
|
dy = ( p->pos.y - q->pos.y )*d;
|
|
dz = ( p->pos.z - q->pos.z )*d;
|
|
dsq = (dx*dx + dy*dy + dz*dz);
|
|
if ( mR2 > dsq ) {
|
|
r = sqrt ( dsq );
|
|
c = (mR - r);
|
|
pterm = -0.5f * c * m_SpikyKern * ( p->pressure + q->pressure) / r;
|
|
dterm = c * p->density * q->density;
|
|
force.x += ( pterm * dx + vterm * (q->vel_eval.x - p->vel_eval.x) ) * dterm;
|
|
force.y += ( pterm * dy + vterm * (q->vel_eval.y - p->vel_eval.y) ) * dterm;
|
|
force.z += ( pterm * dz + vterm * (q->vel_eval.z - p->vel_eval.z) ) * dterm;
|
|
}
|
|
}
|
|
p->sph_force = force;
|
|
}
|
|
}
|
|
|
|
// Compute Forces - Using spatial grid. Faster.
|
|
void FluidSystem::SPH_ComputeForceGrid ()
|
|
{
|
|
char *dat1, *dat1_end;
|
|
Fluid *p;
|
|
Fluid *pcurr;
|
|
int pndx;
|
|
Vector3DF force, fcurr;
|
|
register double pterm, vterm, dterm;
|
|
double c, d, dsq, r;
|
|
double dx, dy, dz;
|
|
double mR, mR2, visc;
|
|
float radius = m_Param[SPH_SMOOTHRADIUS] / m_Param[SPH_SIMSCALE];
|
|
|
|
d = m_Param[SPH_SIMSCALE];
|
|
mR = m_Param[SPH_SMOOTHRADIUS];
|
|
mR2 = (mR*mR);
|
|
visc = m_Param[SPH_VISC];
|
|
|
|
dat1_end = mBuf[0].data + NumPoints()*mBuf[0].stride;
|
|
for ( dat1 = mBuf[0].data; dat1 < dat1_end; dat1 += mBuf[0].stride ) {
|
|
p = (Fluid*) dat1;
|
|
|
|
force.Set ( 0, 0, 0 );
|
|
|
|
Grid_FindCells ( p->pos, radius );
|
|
for (int cell=0; cell < 8; cell++) {
|
|
if ( m_GridCell[cell] != -1 ) {
|
|
pndx = m_Grid [ m_GridCell[cell] ];
|
|
while ( pndx != -1 ) {
|
|
pcurr = (Fluid*) (mBuf[0].data + pndx*mBuf[0].stride);
|
|
if ( pcurr == p ) {pndx = pcurr->next; continue; }
|
|
|
|
dx = ( p->pos.x - pcurr->pos.x)*d; // dist in cm
|
|
dy = ( p->pos.y - pcurr->pos.y)*d;
|
|
dz = ( p->pos.z - pcurr->pos.z)*d;
|
|
dsq = (dx*dx + dy*dy + dz*dz);
|
|
if ( mR2 > dsq ) {
|
|
r = sqrt ( dsq );
|
|
c = (mR - r);
|
|
pterm = -0.5f * c * m_SpikyKern * ( p->pressure + pcurr->pressure) / r;
|
|
dterm = c * p->density * pcurr->density;
|
|
vterm = m_LapKern * visc;
|
|
force.x += ( pterm * dx + vterm * (pcurr->vel_eval.x - p->vel_eval.x) ) * dterm;
|
|
force.y += ( pterm * dy + vterm * (pcurr->vel_eval.y - p->vel_eval.y) ) * dterm;
|
|
force.z += ( pterm * dz + vterm * (pcurr->vel_eval.z - p->vel_eval.z) ) * dterm;
|
|
}
|
|
pndx = pcurr->next;
|
|
}
|
|
}
|
|
}
|
|
p->sph_force = force;
|
|
}
|
|
}
|
|
|
|
// Compute Forces - Using spatial grid with saved neighbor table. Fastest.
|
|
void FluidSystem::SPH_ComputeForceGridNC ()
|
|
{
|
|
char *dat1, *dat1_end;
|
|
Fluid *p;
|
|
Fluid *pcurr;
|
|
Vector3DF force, fcurr;
|
|
register float pterm, vterm, dterm;
|
|
int i;
|
|
float c, d;
|
|
float dx, dy, dz;
|
|
float mR, mR2, visc;
|
|
|
|
d = m_Param[SPH_SIMSCALE];
|
|
mR = m_Param[SPH_SMOOTHRADIUS];
|
|
mR2 = (mR*mR);
|
|
visc = m_Param[SPH_VISC];
|
|
|
|
dat1_end = mBuf[0].data + NumPoints()*mBuf[0].stride;
|
|
i = 0;
|
|
|
|
for ( dat1 = mBuf[0].data; dat1 < dat1_end; dat1 += mBuf[0].stride, i++ ) {
|
|
p = (Fluid*) dat1;
|
|
|
|
force.Set ( 0, 0, 0 );
|
|
for (int j=0; j < m_NC[i]; j++ ) {
|
|
pcurr = (Fluid*) (mBuf[0].data + m_Neighbor[i][j]*mBuf[0].stride);
|
|
dx = ( p->pos.x - pcurr->pos.x)*d; // dist in cm
|
|
dy = ( p->pos.y - pcurr->pos.y)*d;
|
|
dz = ( p->pos.z - pcurr->pos.z)*d;
|
|
c = ( mR - m_NDist[i][j] );
|
|
pterm = -0.5f * c * m_SpikyKern * ( p->pressure + pcurr->pressure) / m_NDist[i][j];
|
|
dterm = c * p->density * pcurr->density;
|
|
vterm = m_LapKern * visc;
|
|
force.x += ( pterm * dx + vterm * (pcurr->vel_eval.x - p->vel_eval.x) ) * dterm;
|
|
force.y += ( pterm * dy + vterm * (pcurr->vel_eval.y - p->vel_eval.y) ) * dterm;
|
|
force.z += ( pterm * dz + vterm * (pcurr->vel_eval.z - p->vel_eval.z) ) * dterm;
|
|
}
|
|
p->sph_force = force;
|
|
}
|
|
}
|
|
|