//////////////////////////////////////////////////////////////////////
// This file is part of Wavelet Turbulence.
//
// Wavelet Turbulence is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Wavelet Turbulence is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Wavelet Turbulence. If not, see .
//
// Copyright 2008 Theodore Kim and Nils Thuerey
//
// FLUID_3D.cpp: implementation of the FLUID_3D class.
//
//////////////////////////////////////////////////////////////////////
#include "FLUID_3D.h"
#include
#define SOLVER_ACCURACY 1e-06
void FLUID_3D::solvePressurePre(float* field, float* b, unsigned char* skip)
{
int x, y, z;
size_t index;
float *_q, *_Precond, *_h, *_residual, *_direction;
// i = 0
int i = 0;
_residual = new float[_totalCells]; // set 0
_direction = new float[_totalCells]; // set 0
_q = new float[_totalCells]; // set 0
_h = new float[_totalCells]; // set 0
_Precond = new float[_totalCells]; // set 0
memset(_residual, 0, sizeof(float)*_xRes*_yRes*_zRes);
memset(_q, 0, sizeof(float)*_xRes*_yRes*_zRes);
memset(_direction, 0, sizeof(float)*_xRes*_yRes*_zRes);
memset(_h, 0, sizeof(float)*_xRes*_yRes*_zRes);
memset(_Precond, 0, sizeof(float)*_xRes*_yRes*_zRes);
// r = b - Ax
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
// if the cell is a variable
float Acenter = 0.0f;
if (!skip[index])
{
// set the matrix to the Poisson stencil in order
if (!skip[index + 1]) Acenter += 1.;
if (!skip[index - 1]) Acenter += 1.;
if (!skip[index + _xRes]) Acenter += 1.;
if (!skip[index - _xRes]) Acenter += 1.;
if (!skip[index + _slabSize]) Acenter += 1.;
if (!skip[index - _slabSize]) Acenter += 1.;
}
_residual[index] = b[index] - (Acenter * field[index] +
field[index - 1] * (skip[index - 1] ? 0.0 : -1.0f)+
field[index + 1] * (skip[index + 1] ? 0.0 : -1.0f)+
field[index - _xRes] * (skip[index - _xRes] ? 0.0 : -1.0f)+
field[index + _xRes] * (skip[index + _xRes] ? 0.0 : -1.0f)+
field[index - _slabSize] * (skip[index - _slabSize] ? 0.0 : -1.0f)+
field[index + _slabSize] * (skip[index + _slabSize] ? 0.0 : -1.0f) );
_residual[index] = (skip[index]) ? 0.0f : _residual[index];
// P^-1
if(Acenter < 1.0)
_Precond[index] = 0.0;
else
_Precond[index] = 1.0 / Acenter;
// p = P^-1 * r
_direction[index] = _residual[index] * _Precond[index];
}
// deltaNew = transpose(r) * p
float deltaNew = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
deltaNew += _residual[index] * _direction[index];
// delta0 = deltaNew
// float delta0 = deltaNew;
// While deltaNew > (eps^2) * delta0
const float eps = SOLVER_ACCURACY;
//while ((i < _iterations) && (deltaNew > eps*delta0))
float maxR = 2.0f * eps;
// while (i < _iterations)
while ((i < _iterations) && (maxR > 0.001*eps))
{
// (s) q = Ad (p)
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
// if the cell is a variable
float Acenter = 0.0f;
if (!skip[index])
{
// set the matrix to the Poisson stencil in order
if (!skip[index + 1]) Acenter += 1.;
if (!skip[index - 1]) Acenter += 1.;
if (!skip[index + _xRes]) Acenter += 1.;
if (!skip[index - _xRes]) Acenter += 1.;
if (!skip[index + _slabSize]) Acenter += 1.;
if (!skip[index - _slabSize]) Acenter += 1.;
}
_q[index] = Acenter * _direction[index] +
_direction[index - 1] * (skip[index - 1] ? 0.0 : -1.0f) +
_direction[index + 1] * (skip[index + 1] ? 0.0 : -1.0f) +
_direction[index - _xRes] * (skip[index - _xRes] ? 0.0 : -1.0f) +
_direction[index + _xRes] * (skip[index + _xRes] ? 0.0 : -1.0f)+
_direction[index - _slabSize] * (skip[index - _slabSize] ? 0.0 : -1.0f) +
_direction[index + _slabSize] * (skip[index + _slabSize] ? 0.0 : -1.0f);
_q[index] = (skip[index]) ? 0.0f : _q[index];
}
// alpha = deltaNew / (transpose(d) * q)
float alpha = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
alpha += _direction[index] * _q[index];
if (fabs(alpha) > 0.0f)
alpha = deltaNew / alpha;
// x = x + alpha * d
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
field[index] += alpha * _direction[index];
// r = r - alpha * q
maxR = 0.0;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
_residual[index] -= alpha * _q[index];
// maxR = (_residual[index] > maxR) ? _residual[index] : maxR;
}
// if(maxR <= eps)
// break;
// h = P^-1 * r
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
_h[index] = _Precond[index] * _residual[index];
}
// deltaOld = deltaNew
float deltaOld = deltaNew;
// deltaNew = transpose(r) * h
deltaNew = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
deltaNew += _residual[index] * _h[index];
maxR = (_residual[index]* _h[index] > maxR) ? _residual[index]* _h[index] : maxR;
}
// beta = deltaNew / deltaOld
float beta = deltaNew / deltaOld;
// d = h + beta * d
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
_direction[index] = _h[index] + beta * _direction[index];
// i = i + 1
i++;
}
// cout << i << " iterations converged to " << sqrt(maxR) << endl;
if (_h) delete[] _h;
if (_Precond) delete[] _Precond;
if (_residual) delete[] _residual;
if (_direction) delete[] _direction;
if (_q) delete[] _q;
}
//////////////////////////////////////////////////////////////////////
// solve the poisson equation with CG
//////////////////////////////////////////////////////////////////////
#if 0
void FLUID_3D::solvePressure(float* field, float* b, unsigned char* skip)
{
int x, y, z;
size_t index;
// i = 0
int i = 0;
memset(_residual, 0, sizeof(float)*_xRes*_yRes*_zRes);
memset(_q, 0, sizeof(float)*_xRes*_yRes*_zRes);
memset(_direction, 0, sizeof(float)*_xRes*_yRes*_zRes);
// r = b - Ax
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
// if the cell is a variable
float Acenter = 0.0f;
if (!skip[index])
{
// set the matrix to the Poisson stencil in order
if (!skip[index + 1]) Acenter += 1.;
if (!skip[index - 1]) Acenter += 1.;
if (!skip[index + _xRes]) Acenter += 1.;
if (!skip[index - _xRes]) Acenter += 1.;
if (!skip[index + _slabSize]) Acenter += 1.;
if (!skip[index - _slabSize]) Acenter += 1.;
}
_residual[index] = b[index] - (Acenter * field[index] +
field[index - 1] * (skip[index - 1] ? 0.0 : -1.0f)+
field[index + 1] * (skip[index + 1] ? 0.0 : -1.0f)+
field[index - _xRes] * (skip[index - _xRes] ? 0.0 : -1.0f)+
field[index + _xRes] * (skip[index + _xRes] ? 0.0 : -1.0f)+
field[index - _slabSize] * (skip[index - _slabSize] ? 0.0 : -1.0f)+
field[index + _slabSize] * (skip[index + _slabSize] ? 0.0 : -1.0f) );
_residual[index] = (skip[index]) ? 0.0f : _residual[index];
}
// d = r
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
_direction[index] = _residual[index];
// deltaNew = transpose(r) * r
float deltaNew = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
deltaNew += _residual[index] * _residual[index];
// delta0 = deltaNew
// float delta0 = deltaNew;
// While deltaNew > (eps^2) * delta0
const float eps = SOLVER_ACCURACY;
float maxR = 2.0f * eps;
while ((i < _iterations) && (maxR > eps))
{
// q = Ad
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
// if the cell is a variable
float Acenter = 0.0f;
if (!skip[index])
{
// set the matrix to the Poisson stencil in order
if (!skip[index + 1]) Acenter += 1.;
if (!skip[index - 1]) Acenter += 1.;
if (!skip[index + _xRes]) Acenter += 1.;
if (!skip[index - _xRes]) Acenter += 1.;
if (!skip[index + _slabSize]) Acenter += 1.;
if (!skip[index - _slabSize]) Acenter += 1.;
}
_q[index] = Acenter * _direction[index] +
_direction[index - 1] * (skip[index - 1] ? 0.0 : -1.0f) +
_direction[index + 1] * (skip[index + 1] ? 0.0 : -1.0f) +
_direction[index - _xRes] * (skip[index - _xRes] ? 0.0 : -1.0f) +
_direction[index + _xRes] * (skip[index + _xRes] ? 0.0 : -1.0f)+
_direction[index - _slabSize] * (skip[index - _slabSize] ? 0.0 : -1.0f) +
_direction[index + _slabSize] * (skip[index + _slabSize] ? 0.0 : -1.0f);
_q[index] = (skip[index]) ? 0.0f : _q[index];
}
// alpha = deltaNew / (transpose(d) * q)
float alpha = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
alpha += _direction[index] * _q[index];
if (fabs(alpha) > 0.0f)
alpha = deltaNew / alpha;
// x = x + alpha * d
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
field[index] += alpha * _direction[index];
// r = r - alpha * q
maxR = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
_residual[index] -= alpha * _q[index];
maxR = (_residual[index] > maxR) ? _residual[index] : maxR;
}
// deltaOld = deltaNew
float deltaOld = deltaNew;
// deltaNew = transpose(r) * r
deltaNew = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
deltaNew += _residual[index] * _residual[index];
// beta = deltaNew / deltaOld
float beta = deltaNew / deltaOld;
// d = r + beta * d
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
_direction[index] = _residual[index] + beta * _direction[index];
// i = i + 1
i++;
}
// cout << i << " iterations converged to " << maxR << endl;
}
#endif
//////////////////////////////////////////////////////////////////////
// solve the heat equation with CG
//////////////////////////////////////////////////////////////////////
void FLUID_3D::solveHeat(float* field, float* b, unsigned char* skip)
{
int x, y, z;
size_t index;
const float heatConst = _dt * _heatDiffusion / (_dx * _dx);
float *_q, *_residual, *_direction;
// i = 0
int i = 0;
_residual = new float[_totalCells]; // set 0
_direction = new float[_totalCells]; // set 0
_q = new float[_totalCells]; // set 0
memset(_residual, 0, sizeof(float)*_xRes*_yRes*_zRes);
memset(_q, 0, sizeof(float)*_xRes*_yRes*_zRes);
memset(_direction, 0, sizeof(float)*_xRes*_yRes*_zRes);
// r = b - Ax
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
// if the cell is a variable
float Acenter = 1.0f;
if (!skip[index])
{
// set the matrix to the Poisson stencil in order
if (!skip[index + 1]) Acenter += heatConst;
if (!skip[index - 1]) Acenter += heatConst;
if (!skip[index + _xRes]) Acenter += heatConst;
if (!skip[index - _xRes]) Acenter += heatConst;
if (!skip[index + _slabSize]) Acenter += heatConst;
if (!skip[index - _slabSize]) Acenter += heatConst;
}
_residual[index] = b[index] - (Acenter * field[index] +
field[index - 1] * (skip[index - 1] ? 0.0 : -heatConst) +
field[index + 1] * (skip[index + 1] ? 0.0 : -heatConst) +
field[index - _xRes] * (skip[index - _xRes] ? 0.0 : -heatConst) +
field[index + _xRes] * (skip[index + _xRes] ? 0.0 : -heatConst) +
field[index - _slabSize] * (skip[index - _slabSize] ? 0.0 : -heatConst) +
field[index + _slabSize] * (skip[index + _slabSize] ? 0.0 : -heatConst));
_residual[index] = (skip[index]) ? 0.0f : _residual[index];
}
// d = r
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
_direction[index] = _residual[index];
// deltaNew = transpose(r) * r
float deltaNew = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
deltaNew += _residual[index] * _residual[index];
// delta0 = deltaNew
// float delta0 = deltaNew;
// While deltaNew > (eps^2) * delta0
const float eps = SOLVER_ACCURACY;
float maxR = 2.0f * eps;
while ((i < _iterations) && (maxR > eps))
{
// q = Ad
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
// if the cell is a variable
float Acenter = 1.0f;
if (!skip[index])
{
// set the matrix to the Poisson stencil in order
if (!skip[index + 1]) Acenter += heatConst;
if (!skip[index - 1]) Acenter += heatConst;
if (!skip[index + _xRes]) Acenter += heatConst;
if (!skip[index - _xRes]) Acenter += heatConst;
if (!skip[index + _slabSize]) Acenter += heatConst;
if (!skip[index - _slabSize]) Acenter += heatConst;
}
_q[index] = (Acenter * _direction[index] +
_direction[index - 1] * (skip[index - 1] ? 0.0 : -heatConst) +
_direction[index + 1] * (skip[index + 1] ? 0.0 : -heatConst) +
_direction[index - _xRes] * (skip[index - _xRes] ? 0.0 : -heatConst) +
_direction[index + _xRes] * (skip[index + _xRes] ? 0.0 : -heatConst) +
_direction[index - _slabSize] * (skip[index - _slabSize] ? 0.0 : -heatConst) +
_direction[index + _slabSize] * (skip[index + _slabSize] ? 0.0 : -heatConst));
_q[index] = (skip[index]) ? 0.0f : _q[index];
}
// alpha = deltaNew / (transpose(d) * q)
float alpha = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
alpha += _direction[index] * _q[index];
if (fabs(alpha) > 0.0f)
alpha = deltaNew / alpha;
// x = x + alpha * d
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
field[index] += alpha * _direction[index];
// r = r - alpha * q
maxR = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
{
_residual[index] -= alpha * _q[index];
maxR = (_residual[index] > maxR) ? _residual[index] : maxR;
}
// deltaOld = deltaNew
float deltaOld = deltaNew;
// deltaNew = transpose(r) * r
deltaNew = 0.0f;
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
deltaNew += _residual[index] * _residual[index];
// beta = deltaNew / deltaOld
float beta = deltaNew / deltaOld;
// d = r + beta * d
index = _slabSize + _xRes + 1;
for (z = 1; z < _zRes - 1; z++, index += 2 * _xRes)
for (y = 1; y < _yRes - 1; y++, index += 2)
for (x = 1; x < _xRes - 1; x++, index++)
_direction[index] = _residual[index] + beta * _direction[index];
// i = i + 1
i++;
}
// cout << i << " iterations converged to " << maxR << endl;
if (_residual) delete[] _residual;
if (_direction) delete[] _direction;
if (_q) delete[] _q;
}