blob: f80ed9a53f3148bb20f5fa893da93f896e40215d [file] [log] [blame]
//Code written by Richard O. Lee and Christian Bienia
//Modified by Christian Fensch
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <fstream>
#include <math.h>
#include <assert.h>
#include "fluid.hpp"
#include "cellpool.hpp"
#ifdef ENABLE_VISUALIZATION
#include "fluidview.hpp"
#endif
#ifdef ENABLE_PARSEC_HOOKS
#include <hooks.h>
#endif
//Uncomment to add code to check that Courant–Friedrichs–Lewy condition is satisfied at runtime
//#define ENABLE_CFL_CHECK
//Define ENABLE_STATISTICS to collect additional information about the particles at runtime.
//#define ENABLE_STATISTICS
////////////////////////////////////////////////////////////////////////////////
cellpool pool;
fptype restParticlesPerMeter, h, hSq;
fptype densityCoeff, pressureCoeff, viscosityCoeff;
int nx, ny, nz; // number of grid cells in each dimension
Vec3 delta; // cell dimensions
int numParticles = 0;
int numCells = 0;
Cell *cells = NULL;
Cell *cells2 = NULL;
int *cnumPars = 0;
int *cnumPars2 = 0;
Cell **last_cells = NULL; //helper array with pointers to last cell structure of "cells" array lists
#ifdef ENABLE_VISUALIZATION
Vec3 vMax(0.0,0.0,0.0);
Vec3 vMin(0.0,0.0,0.0);
#endif
////////////////////////////////////////////////////////////////////////////////
void InitSim(char const *fileName)
{
std::cout << "Loading file \"" << fileName << "\"..." << std::endl;
std::ifstream file(fileName, std::ios::binary);
if(!file) {
std::cerr << "Error opening file. Aborting." << std::endl;
exit(1);
}
//Always use single precision float variables b/c file format uses single precision
float restParticlesPerMeter_le;
int numParticles_le;
file.read((char *)&restParticlesPerMeter_le, FILE_SIZE_FLOAT);
file.read((char *)&numParticles_le, FILE_SIZE_INT);
if(!isLittleEndian()) {
restParticlesPerMeter = bswap_float(restParticlesPerMeter_le);
numParticles = bswap_int32(numParticles_le);
} else {
restParticlesPerMeter = restParticlesPerMeter_le;
numParticles = numParticles_le;
}
cellpool_init(&pool, numParticles);
h = kernelRadiusMultiplier / restParticlesPerMeter;
hSq = h*h;
#ifndef ENABLE_DOUBLE_PRECISION
fptype coeff1 = 315.0 / (64.0*pi*powf(h,9.0));
fptype coeff2 = 15.0 / (pi*powf(h,6.0));
fptype coeff3 = 45.0 / (pi*powf(h,6.0));
#else
fptype coeff1 = 315.0 / (64.0*pi*pow(h,9.0));
fptype coeff2 = 15.0 / (pi*pow(h,6.0));
fptype coeff3 = 45.0 / (pi*pow(h,6.0));
#endif //ENABLE_DOUBLE_PRECISION
fptype particleMass = 0.5*doubleRestDensity / (restParticlesPerMeter*restParticlesPerMeter*restParticlesPerMeter);
densityCoeff = particleMass * coeff1;
pressureCoeff = 3.0*coeff2 * 0.50*stiffnessPressure * particleMass;
viscosityCoeff = viscosity * coeff3 * particleMass;
Vec3 range = domainMax - domainMin;
nx = (int)(range.x / h);
ny = (int)(range.y / h);
nz = (int)(range.z / h);
assert(nx >= 1 && ny >= 1 && nz >= 1);
numCells = nx*ny*nz;
std::cout << "Number of cells: " << numCells << std::endl;
delta.x = range.x / nx;
delta.y = range.y / ny;
delta.z = range.z / nz;
assert(delta.x >= h && delta.y >= h && delta.z >= h);
//make sure Cell structure is multiple of estiamted cache line size
assert(sizeof(Cell) % CACHELINE_SIZE == 0);
//make sure helper Cell structure is in sync with real Cell structure
#pragma warning( disable : 1684) // warning #1684: conversion from pointer to same-sized integral type (potential portability problem)
assert(offsetof(struct Cell_aux, padding) == offsetof(struct Cell, padding));
#if defined(WIN32)
cells = (struct Cell*)_aligned_malloc(sizeof(struct Cell) * numCells, CACHELINE_SIZE);
cells2 = (struct Cell*)_aligned_malloc(sizeof(struct Cell) * numCells, CACHELINE_SIZE);
cnumPars = (int*)_aligned_malloc(sizeof(int) * numCells, CACHELINE_SIZE);
cnumPars2 = (int*)_aligned_malloc(sizeof(int) * numCells, CACHELINE_SIZE);
last_cells = (struct Cell **)_aligned_malloc(sizeof(struct Cell *) * numCells, CACHELINE_SIZE);
assert((cells!=NULL) && (cells2!=NULL) && (cnumPars!=NULL) && (cnumPars2!=NULL) && (last_cells!=NULL));
#else
int rv0 = posix_memalign((void **)(&cells), CACHELINE_SIZE, sizeof(struct Cell) * numCells);
int rv1 = posix_memalign((void **)(&cells2), CACHELINE_SIZE, sizeof(struct Cell) * numCells);
int rv2 = posix_memalign((void **)(&cnumPars), CACHELINE_SIZE, sizeof(int) * numCells);
int rv3 = posix_memalign((void **)(&cnumPars2), CACHELINE_SIZE, sizeof(int) * numCells);
int rv4 = posix_memalign((void **)(&last_cells), CACHELINE_SIZE, sizeof(struct Cell *) * numCells);
assert((rv0==0) && (rv1==0) && (rv2==0) && (rv3==0) && (rv4==0));
#endif
// because cells and cells2 are not allocated via new
// we construct them here
for(int i=0; i<numCells; ++i)
{
new (&cells[i]) Cell;
new (&cells2[i]) Cell;
}
memset(cnumPars, 0, numCells*sizeof(int));
//Always use single precision float variables b/c file format uses single precision
float px, py, pz, hvx, hvy, hvz, vx, vy, vz;
for(int i = 0; i < numParticles; ++i)
{
file.read((char *)&px, FILE_SIZE_FLOAT);
file.read((char *)&py, FILE_SIZE_FLOAT);
file.read((char *)&pz, FILE_SIZE_FLOAT);
file.read((char *)&hvx, FILE_SIZE_FLOAT);
file.read((char *)&hvy, FILE_SIZE_FLOAT);
file.read((char *)&hvz, FILE_SIZE_FLOAT);
file.read((char *)&vx, FILE_SIZE_FLOAT);
file.read((char *)&vy, FILE_SIZE_FLOAT);
file.read((char *)&vz, FILE_SIZE_FLOAT);
if(!isLittleEndian()) {
px = bswap_float(px);
py = bswap_float(py);
pz = bswap_float(pz);
hvx = bswap_float(hvx);
hvy = bswap_float(hvy);
hvz = bswap_float(hvz);
vx = bswap_float(vx);
vy = bswap_float(vy);
vz = bswap_float(vz);
}
int ci = (int)(((fptype)px - domainMin.x) / delta.x);
int cj = (int)(((fptype)py - domainMin.y) / delta.y);
int ck = (int)(((fptype)pz - domainMin.z) / delta.z);
if(ci < 0) ci = 0; else if(ci >= nx) ci = nx-1;
if(cj < 0) cj = 0; else if(cj >= ny) cj = ny-1;
if(ck < 0) ck = 0; else if(ck >= nz) ck = nz-1;
int index = (ck*ny + cj)*nx + ci;
Cell *cell = &cells[index];
//go to last cell structure in list
int np = cnumPars[index];
while(np > PARTICLES_PER_CELL) {
cell = cell->next;
np = np - PARTICLES_PER_CELL;
}
//add another cell structure if everything full
if( (np % PARTICLES_PER_CELL == 0) && (cnumPars[index] != 0) ) {
cell->next = cellpool_getcell(&pool);
cell = cell->next;
np = np - PARTICLES_PER_CELL; // np = 0;
}
//add particle to cell
cell->p[np].x = px;
cell->p[np].y = py;
cell->p[np].z = pz;
cell->hv[np].x = hvx;
cell->hv[np].y = hvy;
cell->hv[np].z = hvz;
cell->v[np].x = vx;
cell->v[np].y = vy;
cell->v[np].z = vz;
#ifdef ENABLE_VISUALIZATION
vMin.x = std::min(vMin.x, cell->v[np].x);
vMax.x = std::max(vMax.x, cell->v[np].x);
vMin.y = std::min(vMin.y, cell->v[np].y);
vMax.y = std::max(vMax.y, cell->v[np].y);
vMin.z = std::min(vMin.z, cell->v[np].z);
vMax.z = std::max(vMax.z, cell->v[np].z);
#endif
++cnumPars[index];
}
std::cout << "Number of particles: " << numParticles << std::endl;
}
////////////////////////////////////////////////////////////////////////////////
void SaveFile(char const *fileName)
{
std::cout << "Saving file \"" << fileName << "\"..." << std::endl;
std::ofstream file(fileName, std::ios::binary);
assert(file);
//Always use single precision float variables b/c file format uses single precision
if(!isLittleEndian()) {
float restParticlesPerMeter_le;
int numParticles_le;
restParticlesPerMeter_le = bswap_float((float)restParticlesPerMeter);
numParticles_le = bswap_int32(numParticles);
file.write((char *)&restParticlesPerMeter_le, FILE_SIZE_FLOAT);
file.write((char *)&numParticles_le, FILE_SIZE_INT);
} else {
file.write((char *)&restParticlesPerMeter, FILE_SIZE_FLOAT);
file.write((char *)&numParticles, FILE_SIZE_INT);
}
int count = 0;
for(int i = 0; i < numCells; ++i)
{
Cell *cell = &cells[i];
int np = cnumPars[i];
for(int j = 0; j < np; ++j)
{
//Always use single precision float variables b/c file format uses single precision
float px, py, pz, hvx, hvy, hvz, vx,vy, vz;
if(!isLittleEndian()) {
px = bswap_float((float)(cell->p[j % PARTICLES_PER_CELL].x));
py = bswap_float((float)(cell->p[j % PARTICLES_PER_CELL].y));
pz = bswap_float((float)(cell->p[j % PARTICLES_PER_CELL].z));
hvx = bswap_float((float)(cell->hv[j % PARTICLES_PER_CELL].x));
hvy = bswap_float((float)(cell->hv[j % PARTICLES_PER_CELL].y));
hvz = bswap_float((float)(cell->hv[j % PARTICLES_PER_CELL].z));
vx = bswap_float((float)(cell->v[j % PARTICLES_PER_CELL].x));
vy = bswap_float((float)(cell->v[j % PARTICLES_PER_CELL].y));
vz = bswap_float((float)(cell->v[j % PARTICLES_PER_CELL].z));
} else {
px = (float)(cell->p[j % PARTICLES_PER_CELL].x);
py = (float)(cell->p[j % PARTICLES_PER_CELL].y);
pz = (float)(cell->p[j % PARTICLES_PER_CELL].z);
hvx = (float)(cell->hv[j % PARTICLES_PER_CELL].x);
hvy = (float)(cell->hv[j % PARTICLES_PER_CELL].y);
hvz = (float)(cell->hv[j % PARTICLES_PER_CELL].z);
vx = (float)(cell->v[j % PARTICLES_PER_CELL].x);
vy = (float)(cell->v[j % PARTICLES_PER_CELL].y);
vz = (float)(cell->v[j % PARTICLES_PER_CELL].z);
}
file.write((char *)&px, FILE_SIZE_FLOAT);
file.write((char *)&py, FILE_SIZE_FLOAT);
file.write((char *)&pz, FILE_SIZE_FLOAT);
file.write((char *)&hvx, FILE_SIZE_FLOAT);
file.write((char *)&hvy, FILE_SIZE_FLOAT);
file.write((char *)&hvz, FILE_SIZE_FLOAT);
file.write((char *)&vx, FILE_SIZE_FLOAT);
file.write((char *)&vy, FILE_SIZE_FLOAT);
file.write((char *)&vz, FILE_SIZE_FLOAT);
++count;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
cell = cell->next;
}
}
}
assert(count == numParticles);
}
////////////////////////////////////////////////////////////////////////////////
void CleanUpSim()
{
// first return extended cells to cell pools
for(int i=0; i< numCells; ++i)
{
Cell& cell = cells[i];
while(cell.next)
{
Cell *temp = cell.next;
cell.next = temp->next;
cellpool_returncell(&pool, temp);
}
}
// now return cell pools
cellpool_destroy(&pool);
#if defined(WIN32)
_aligned_free(cells);
_aligned_free(cells2);
_aligned_free(cnumPars);
_aligned_free(cnumPars2);
_aligned_free(last_cells);
#else
free(cells);
free(cells2);
free(cnumPars);
free(cnumPars2);
free(last_cells);
#endif
}
////////////////////////////////////////////////////////////////////////////////
void RebuildGrid()
{
//swap src and dest arrays with particles
std::swap(cells, cells2);
//swap src and dest arrays with counts of particles
std::swap(cnumPars, cnumPars2);
// Note, in parallel versions the above swaps may
// occure outside RebuildGrid()
//initialize destination data structures
memset(cnumPars, 0, numCells*sizeof(int));
for(int i=0; i<numCells; i++)
{
cells[i].next = NULL;
last_cells[i] = &cells[i];
}
//iterate through source cell lists
for(int i = 0; i < numCells; ++i)
{
Cell *cell2 = &cells2[i];
int np2 = cnumPars2[i];
//iterate through source particles
for(int j = 0; j < np2; ++j)
{
//get destination for source particle
int ci = (int)((cell2->p[j % PARTICLES_PER_CELL].x - domainMin.x) / delta.x);
int cj = (int)((cell2->p[j % PARTICLES_PER_CELL].y - domainMin.y) / delta.y);
int ck = (int)((cell2->p[j % PARTICLES_PER_CELL].z - domainMin.z) / delta.z);
// confine to domain
// Note, if ProcessCollisions() is working properly these tests are useless
if(ci < 0) ci = 0; else if(ci >= nx) ci = nx-1;
if(cj < 0) cj = 0; else if(cj >= ny) cj = ny-1;
if(ck < 0) ck = 0; else if(ck >= nz) ck = nz-1;
#ifdef ENABLE_CFL_CHECK
//check that source cell is a neighbor of destination cell
bool cfl_cond_satisfied=false;
for(int di = -1; di <= 1; ++di)
for(int dj = -1; dj <= 1; ++dj)
for(int dk = -1; dk <= 1; ++dk)
{
int ii = ci + di;
int jj = cj + dj;
int kk = ck + dk;
if(ii >= 0 && ii < nx && jj >= 0 && jj < ny && kk >= 0 && kk < nz)
{
int index = (kk*ny + jj)*nx + ii;
if(index == i)
{
cfl_cond_satisfied=true;
break;
}
}
}
if(!cfl_cond_satisfied)
{
std::cerr << "FATAL ERROR: Courant–Friedrichs–Lewy condition not satisfied." << std::endl;
exit(1);
}
#endif //ENABLE_CFL_CHECK
//get last pointer in correct destination cell list
int index = (ck*ny + cj)*nx + ci;
Cell *cell = last_cells[index];
int np = cnumPars[index];
//add another cell structure if everything full
if( (np % PARTICLES_PER_CELL == 0) && (cnumPars[index] != 0) ) {
cell->next = cellpool_getcell(&pool);
cell = cell->next;
last_cells[index] = cell;
}
++cnumPars[index];
//copy source to destination particle
cell->p[np % PARTICLES_PER_CELL].x = cell2->p[j % PARTICLES_PER_CELL].x;
cell->p[np % PARTICLES_PER_CELL].y = cell2->p[j % PARTICLES_PER_CELL].y;
cell->p[np % PARTICLES_PER_CELL].z = cell2->p[j % PARTICLES_PER_CELL].z;
cell->hv[np % PARTICLES_PER_CELL].x = cell2->hv[j % PARTICLES_PER_CELL].x;
cell->hv[np % PARTICLES_PER_CELL].y = cell2->hv[j % PARTICLES_PER_CELL].y;
cell->hv[np % PARTICLES_PER_CELL].z = cell2->hv[j % PARTICLES_PER_CELL].z;
cell->v[np % PARTICLES_PER_CELL].x = cell2->v[j % PARTICLES_PER_CELL].x;
cell->v[np % PARTICLES_PER_CELL].y = cell2->v[j % PARTICLES_PER_CELL].y;
cell->v[np % PARTICLES_PER_CELL].z = cell2->v[j % PARTICLES_PER_CELL].z;
//move pointer to next source cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
Cell *temp = cell2;
cell2 = cell2->next;
//return cells to pool that are not statically allocated head of lists
if(temp != &cells2[i]) {
cellpool_returncell(&pool, temp);
}
}
} // for(int j = 0; j < np2; ++j)
//return cells to pool that are not statically allocated head of lists
if((cell2 != NULL) && (cell2 != &cells2[i])) {
cellpool_returncell(&pool, cell2);
}
} // for(int i = 0; i < numCells; ++i)
}
////////////////////////////////////////////////////////////////////////////////
int GetNeighborCells(int ci, int cj, int ck, int *neighCells)
{
int numNeighCells = 0;
// have the nearest particles first -> help branch prediction
int my_index = (ck*ny + cj)*nx + ci;
neighCells[numNeighCells] = my_index;
++numNeighCells;
for(int di = -1; di <= 1; ++di)
for(int dj = -1; dj <= 1; ++dj)
for(int dk = -1; dk <= 1; ++dk)
{
int ii = ci + di;
int jj = cj + dj;
int kk = ck + dk;
if(ii >= 0 && ii < nx && jj >= 0 && jj < ny && kk >= 0 && kk < nz)
{
int index = (kk*ny + jj)*nx + ii;
if((index < my_index) && (cnumPars[index] != 0))
{
neighCells[numNeighCells] = index;
++numNeighCells;
}
}
}
return numNeighCells;
}
////////////////////////////////////////////////////////////////////////////////
void ComputeForces()
{
for(int i = 0; i < numCells; ++i)
{
Cell *cell = &cells[i];
int np = cnumPars[i];
for(int j = 0; j < np; ++j)
{
cell->density[j % PARTICLES_PER_CELL] = 0.0;
cell->a[j % PARTICLES_PER_CELL] = externalAcceleration;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
cell = cell->next;
}
}
}
int neighCells[3*3*3];
int cindex = 0;
for(int ck = 0; ck < nz; ++ck)
for(int cj = 0; cj < ny; ++cj)
for(int ci = 0; ci < nx; ++ci, ++cindex)
{
int np = cnumPars[cindex];
if(np == 0)
continue;
int numNeighCells = GetNeighborCells(ci, cj, ck, neighCells);
Cell *cell = &cells[cindex];
for(int ipar = 0; ipar < np; ++ipar)
{
for(int inc = 0; inc < numNeighCells; ++inc)
{
int cindexNeigh = neighCells[inc];
Cell *neigh = &cells[cindexNeigh];
int numNeighPars = cnumPars[cindexNeigh];
for(int iparNeigh = 0; iparNeigh < numNeighPars; ++iparNeigh)
{
//Check address to make sure densities are computed only once per pair
if(&neigh->p[iparNeigh % PARTICLES_PER_CELL] < &cell->p[ipar % PARTICLES_PER_CELL])
{
fptype distSq = (cell->p[ipar % PARTICLES_PER_CELL] - neigh->p[iparNeigh % PARTICLES_PER_CELL]).GetLengthSq();
if(distSq < hSq)
{
fptype t = hSq - distSq;
fptype tc = t*t*t;
cell->density[ipar % PARTICLES_PER_CELL] += tc;
neigh->density[iparNeigh % PARTICLES_PER_CELL] += tc;
}
}
//move pointer to next cell in list if end of array is reached
if(iparNeigh % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
neigh = neigh->next;
}
}
}
//move pointer to next cell in list if end of array is reached
if(ipar % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
cell = cell->next;
}
}
}
const fptype tc = hSq*hSq*hSq;
for(int i = 0; i < numCells; ++i)
{
Cell *cell = &cells[i];
int np = cnumPars[i];
for(int j = 0; j < np; ++j)
{
cell->density[j % PARTICLES_PER_CELL] += tc;
cell->density[j % PARTICLES_PER_CELL] *= densityCoeff;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
cell = cell->next;
}
}
}
cindex = 0;
for(int ck = 0; ck < nz; ++ck)
for(int cj = 0; cj < ny; ++cj)
for(int ci = 0; ci < nx; ++ci, ++cindex)
{
int np = cnumPars[cindex];
if(np == 0)
continue;
int numNeighCells = GetNeighborCells(ci, cj, ck, neighCells);
Cell *cell = &cells[cindex];
for(int ipar = 0; ipar < np; ++ipar)
{
for(int inc = 0; inc < numNeighCells; ++inc)
{
int cindexNeigh = neighCells[inc];
Cell *neigh = &cells[cindexNeigh];
int numNeighPars = cnumPars[cindexNeigh];
for(int iparNeigh = 0; iparNeigh < numNeighPars; ++iparNeigh)
{
//Check address to make sure forces are computed only once per pair
if(&neigh->p[iparNeigh % PARTICLES_PER_CELL] < &cell->p[ipar % PARTICLES_PER_CELL])
{
Vec3 disp = cell->p[ipar % PARTICLES_PER_CELL] - neigh->p[iparNeigh % PARTICLES_PER_CELL];
fptype distSq = disp.GetLengthSq();
if(distSq < hSq)
{
#ifndef ENABLE_DOUBLE_PRECISION
fptype dist = sqrtf(std::max(distSq, (fptype)1e-12));
#else
fptype dist = sqrt(std::max(distSq, 1e-12));
#endif //ENABLE_DOUBLE_PRECISION
fptype hmr = h - dist;
Vec3 acc = disp * pressureCoeff * (hmr*hmr/dist) * (cell->density[ipar % PARTICLES_PER_CELL]+neigh->density[iparNeigh % PARTICLES_PER_CELL] - doubleRestDensity);
acc += (neigh->v[iparNeigh % PARTICLES_PER_CELL] - cell->v[ipar % PARTICLES_PER_CELL]) * viscosityCoeff * hmr;
acc /= cell->density[ipar % PARTICLES_PER_CELL] * neigh->density[iparNeigh % PARTICLES_PER_CELL];
cell->a[ipar % PARTICLES_PER_CELL] += acc;
neigh->a[iparNeigh % PARTICLES_PER_CELL] -= acc;
}
}
//move pointer to next cell in list if end of array is reached
if(iparNeigh % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
neigh = neigh->next;
}
}
}
//move pointer to next cell in list if end of array is reached
if(ipar % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
cell = cell->next;
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
// ProcessCollisions() with container walls
// Under the assumptions that
// a) a particle will not penetrate a wall
// b) a particle will not migrate further than once cell
// c) the parSize is smaller than a cell
// then only the particles at the perimiters may be influenced by the walls
#if 0
void ProcessCollisions()
{
for(int i = 0; i < numCells; ++i)
{
Cell *cell = &cells[i];
int np = cnumPars[i];
for(int j = 0; j < np; ++j)
{
Vec3 pos = cell->p[j % PARTICLES_PER_CELL] + cell->hv[j % PARTICLES_PER_CELL] * timeStep;
fptype diff = parSize - (pos.x - domainMin.x);
if(diff > epsilon)
cell->a[j % PARTICLES_PER_CELL].x += stiffnessCollisions*diff - damping*cell->v[j % PARTICLES_PER_CELL].x;
diff = parSize - (domainMax.x - pos.x);
if(diff > epsilon)
cell->a[j % PARTICLES_PER_CELL].x -= stiffnessCollisions*diff + damping*cell->v[j % PARTICLES_PER_CELL].x;
diff = parSize - (pos.y - domainMin.y);
if(diff > epsilon)
cell->a[j % PARTICLES_PER_CELL].y += stiffnessCollisions*diff - damping*cell->v[j % PARTICLES_PER_CELL].y;
diff = parSize - (domainMax.y - pos.y);
if(diff > epsilon)
cell->a[j % PARTICLES_PER_CELL].y -= stiffnessCollisions*diff + damping*cell->v[j % PARTICLES_PER_CELL].y;
diff = parSize - (pos.z - domainMin.z);
if(diff > epsilon)
cell->a[j % PARTICLES_PER_CELL].z += stiffnessCollisions*diff - damping*cell->v[j % PARTICLES_PER_CELL].z;
diff = parSize - (domainMax.z - pos.z);
if(diff > epsilon)
cell->a[j % PARTICLES_PER_CELL].z -= stiffnessCollisions*diff + damping*cell->v[j % PARTICLES_PER_CELL].z;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
#else
// Notes on USE_ImpeneratableWall
// When particle is detected beyond cell wall it is repositioned at cell wall
// velocity is not changed, thus conserving momentum.
// What this means though it the prior AdvanceParticles had positioned the
// particle beyond the cell wall and thus the visualization will show these
// as artifacts. The proper place for USE_ImpeneratableWall is after AdvanceParticles.
// This would entail a 2nd pass on the perimiters after AdvanceParticles (as opposed
// to inside AdvanceParticles). Your fluid dynamisist should properly devise the
// equasions.
void ProcessCollisions()
{
int x,y,z, index;
x=0; // along the domainMin.x wall
for(y=0; y<ny; ++y)
{
for(z=0; z<nz; ++z)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype pos_x = cell->p[ji].x + cell->hv[ji].x * timeStep;
fptype diff = parSize - (pos_x - domainMin.x);
if(diff > epsilon)
cell->a[ji].x += stiffnessCollisions*diff - damping*cell->v[ji].x;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
x=nx-1; // along the domainMax.x wall
for(y=0; y<ny; ++y)
{
for(z=0; z<nz; ++z)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype pos_x = cell->p[ji].x + cell->hv[ji].x * timeStep;
fptype diff = parSize - (domainMax.x - pos_x);
if(diff > epsilon)
cell->a[ji].x -= stiffnessCollisions*diff + damping*cell->v[ji].x;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
y=0; // along the domainMin.y wall
for(x=0; x<nx; ++x)
{
for(z=0; z<nz; ++z)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype pos_y = cell->p[ji].y + cell->hv[ji].y * timeStep;
fptype diff = parSize - (pos_y - domainMin.y);
if(diff > epsilon)
cell->a[ji].y += stiffnessCollisions*diff - damping*cell->v[ji].y;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
y=ny-1; // along the domainMax.y wall
for(x=0; x<nx; ++x)
{
for(z=0; z<nz; ++z)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype pos_y = cell->p[ji].y + cell->hv[ji].y * timeStep;
fptype diff = parSize - (domainMax.y - pos_y);
if(diff > epsilon)
cell->a[ji].y -= stiffnessCollisions*diff + damping*cell->v[ji].y;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
z=0; // along the domainMin.z wall
for(x=0; x<nx; ++x)
{
for(y=0; y<ny; ++y)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype pos_z = cell->p[ji].z + cell->hv[ji].z * timeStep;
fptype diff = parSize - (pos_z - domainMin.z);
if(diff > epsilon)
cell->a[ji].z += stiffnessCollisions*diff - damping*cell->v[ji].z;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
z=nz-1; // along the domainMax.z wall
for(x=0; x<nx; ++x)
{
for(y=0; y<ny; ++y)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype pos_z = cell->p[ji].z + cell->hv[ji].z * timeStep;
fptype diff = parSize - (domainMax.z - pos_z);
if(diff > epsilon)
cell->a[ji].z -= stiffnessCollisions*diff + damping*cell->v[ji].z;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
}
#define USE_ImpeneratableWall
#if defined(USE_ImpeneratableWall)
void ProcessCollisions2()
{
int x,y,z, index;
x=0; // along the domainMin.x wall
for(y=0; y<ny; ++y)
{
for(z=0; z<nz; ++z)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype diff = cell->p[ji].x - domainMin.x;
if(diff < Zero)
{
cell->p[ji].x = domainMin.x - diff;
cell->v[ji].x = -cell->v[ji].x;
cell->hv[ji].x = -cell->hv[ji].x;
}
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
x=nx-1; // along the domainMax.x wall
for(y=0; y<ny; ++y)
{
for(z=0; z<nz; ++z)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype diff = domainMax.x - cell->p[ji].x;
if(diff < Zero)
{
cell->p[ji].x = domainMax.x + diff;
cell->v[ji].x = -cell->v[ji].x;
cell->hv[ji].x = -cell->hv[ji].x;
}
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
y=0; // along the domainMin.y wall
for(x=0; x<nx; ++x)
{
for(z=0; z<nz; ++z)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype diff = cell->p[ji].y - domainMin.y;
if(diff < Zero)
{
cell->p[ji].y = domainMin.y - diff;
cell->v[ji].y = -cell->v[ji].y;
cell->hv[ji].y = -cell->hv[ji].y;
}
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
y=ny-1; // along the domainMax.y wall
for(x=0; x<nx; ++x)
{
for(z=0; z<nz; ++z)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype diff = domainMax.y - cell->p[ji].y;
if(diff < Zero)
{
cell->p[ji].y = domainMax.y + diff;
cell->v[ji].y = -cell->v[ji].y;
cell->hv[ji].y = -cell->hv[ji].y;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
}
z=0; // along the domainMin.z wall
for(x=0; x<nx; ++x)
{
for(y=0; y<ny; ++y)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype diff = cell->p[ji].z - domainMin.z;
if(diff < Zero)
{
cell->p[ji].z = domainMin.z - diff;
cell->v[ji].z = -cell->v[ji].z;
cell->hv[ji].z = -cell->hv[ji].z;
}
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
z=nz-1; // along the domainMax.z wall
for(x=0; x<nx; ++x)
{
for(y=0; y<ny; ++y)
{
int ci = (z*ny + y)*nx + x;
Cell *cell = &cells[ci];
int np = cnumPars[ci];
for(int j = 0; j < np; ++j)
{
int ji = j % PARTICLES_PER_CELL;
fptype diff = domainMax.z - cell->p[ji].z;
if(diff < Zero)
{
cell->p[ji].z = domainMax.z + diff;
cell->v[ji].z = -cell->v[ji].z;
cell->hv[ji].z = -cell->hv[ji].z;
}
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1)
cell = cell->next;
}
}
}
}
#endif
#endif
////////////////////////////////////////////////////////////////////////////////
void AdvanceParticles()
{
for(int i = 0; i < numCells; ++i)
{
Cell *cell = &cells[i];
int np = cnumPars[i];
for(int j = 0; j < np; ++j)
{
Vec3 v_half = cell->hv[j % PARTICLES_PER_CELL] + cell->a[j % PARTICLES_PER_CELL]*timeStep;
#if defined(USE_ImpeneratableWall)
// N.B. The integration of the position can place the particle
// outside the domain. Although we could place a test in this loop
// we would be unnecessarily testing particles on interior cells.
// Therefore, to reduce the amount of computations we make a later
// pass on the perimiter cells to account for particle migration
// beyond domain
#endif
cell->p[j % PARTICLES_PER_CELL] += v_half * timeStep;
cell->v[j % PARTICLES_PER_CELL] = cell->hv[j % PARTICLES_PER_CELL] + v_half;
cell->v[j % PARTICLES_PER_CELL] *= 0.5;
cell->hv[j % PARTICLES_PER_CELL] = v_half;
//move pointer to next cell in list if end of array is reached
if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
cell = cell->next;
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
void AdvanceFrame()
{
RebuildGrid();
ComputeForces();
ProcessCollisions();
AdvanceParticles();
#if defined(USE_ImpeneratableWall)
// N.B. The integration of the position can place the particle
// outside the domain. We now make a pass on the perimiter cells
// to account for particle migration beyond domain.
ProcessCollisions2();
#endif
#ifdef ENABLE_STATISTICS
float mean, stddev;
int i;
mean = (float)numParticles/(float)numCells;
stddev = 0.0;
for(i=0; i<numCells; i++) {
stddev += (mean-cnumPars[i])*(mean-cnumPars[i]);
}
stddev = sqrtf(stddev);
std::cout << "Cell statistics: mean=" << mean << " particles, stddev=" << stddev << " particles." << std::endl;
#endif
}
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
#ifdef PARSEC_VERSION
#define __PARSEC_STRING(x) #x
#define __PARSEC_XSTRING(x) __PARSEC_STRING(x)
std::cout << "PARSEC Benchmark Suite Version "__PARSEC_XSTRING(PARSEC_VERSION) << std::endl << std::flush;
#else
std::cout << "PARSEC Benchmark Suite" << std::endl << std::flush;
#endif //PARSEC_VERSION
#ifdef ENABLE_PARSEC_HOOKS
__parsec_bench_begin(__parsec_fluidanimate);
#endif
if(argc < 4 || argc >= 6)
{
std::cout << "Usage: " << argv[0] << " <threadnum> <framenum> <.fluid input file> [.fluid output file]" << std::endl;
return -1;
}
int threadnum = atoi(argv[1]);
int framenum = atoi(argv[2]);
//Check arguments
if(threadnum != 1) {
std::cerr << "<threadnum> must be 1 (serial version)" << std::endl;
return -1;
}
if(framenum < 1) {
std::cerr << "<framenum> must at least be 1" << std::endl;
return -1;
}
#ifdef ENABLE_CFL_CHECK
std::cout << "WARNING: Check for Courant–Friedrichs–Lewy condition enabled. Do not use for performance measurements." << std::endl;
#endif
InitSim(argv[3]);
#ifdef ENABLE_VISUALIZATION
InitVisualizationMode(&argc, argv, &AdvanceFrame, &numCells, &cells, &cnumPars);
#endif
#ifndef ENABLE_VISUALIZATION
//core of benchmark program (the Region-of-Interest)
#ifdef ENABLE_PARSEC_HOOKS
__parsec_roi_begin();
#endif
for(int i = 0; i < framenum; ++i)
AdvanceFrame();
#ifdef ENABLE_PARSEC_HOOKS
__parsec_roi_end();
#endif
#else //ENABLE_VISUALIZATION
Visualize();
#endif //ENABLE_VISUALIZATION
if(argc > 4)
SaveFile(argv[4]);
CleanUpSim();
#ifdef ENABLE_PARSEC_HOOKS
__parsec_bench_end();
#endif
return 0;
}
////////////////////////////////////////////////////////////////////////////////