/* This file is part of the OpenLB library
*
* Copyright (C) 2006, 2007 Jonas Latt
* E-mail contact: info@openlb.net
* The most recent release of OpenLB can be downloaded at
*
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/** \file
* Helper functions for the implementation of LB dynamics. This file is all
* about efficiency. The generic template code is specialized for commonly
* used Lattices, so that a maximum performance can be taken out of each
* case.
*/
#ifndef LB_HELPERS_H
#define LB_HELPERS_H
#include "latticeDescriptors.h"
#include "core/cell.h"
#include "core/util.h"
namespace olb {
// Forward declarations
template struct lbDynamicsHelpers;
template struct lbExternalHelpers;
template struct lbLatticeHelpers;
/// This structure forwards the calls to the appropriate helper class
template
struct lbHelpers {
static T equilibrium(int iPop, T rho, const T u[DESCRIPTOR::d], const T uSqr)
{
return lbDynamicsHelpers
::equilibrium(iPop, rho, u, uSqr);
}
static T equilibriumFirstOrder(int iPop, T rho, const T u[DESCRIPTOR::d])
{
return lbDynamicsHelpers
::equilibriumFirstOrder(iPop, rho, u);
}
static T incEquilibrium(int iPop, const T j[DESCRIPTOR::d], const T jSqr, const T pressure)
{
return lbDynamicsHelpers
::incEquilibrium(iPop, j, jSqr, pressure);
}
static void computeFneq ( Cell const& cell,
T fNeq[DESCRIPTOR::q], T rho, const T u[DESCRIPTOR::d] )
{
lbDynamicsHelpers::computeFneq(cell, fNeq, rho, u);
}
static T bgkCollision(Cell& cell, T const& rho, const T u[DESCRIPTOR::d], T const& omega)
{
return lbDynamicsHelpers
::bgkCollision(cell, rho, u, omega);
}
static T bgkCollision(Cell& cell, T const& rho, const T u[DESCRIPTOR::d], const T omega[DESCRIPTOR::q])
{
const T uSqr = util::normSqr(u);
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
cell[iPop] *= (T)1-omega[iPop];
cell[iPop] += omega[iPop] * lbDynamicsHelpers::equilibrium (iPop, rho, u, uSqr );
}
return uSqr;
}
static T rlbCollision( Cell& cell, T rho, const T u[DESCRIPTOR::d], T omega )
{
return lbDynamicsHelpers
::rlbCollision( cell, rho, u, omega );
}
static T mrtCollision( Cell& cell, T const& rho, const T u[DESCRIPTOR::d], T invM_S[DESCRIPTOR::q][DESCRIPTOR::q] ) {
return lbDynamicsHelpers
::mrtCollision(cell, rho, u, invM_S );
}
static T incBgkCollision(Cell& cell, T pressure, const T j[DESCRIPTOR::d], T omega)
{
return lbDynamicsHelpers
::incBgkCollision(cell, pressure, j, omega);
}
static T constRhoBgkCollision(Cell& cell,
T rho, const T u[DESCRIPTOR::d], T ratioRho, T omega)
{
return lbDynamicsHelpers
::constRhoBgkCollision(cell, rho, u, ratioRho, omega);
}
static T computeRho(Cell const& cell)
{
return lbDynamicsHelpers
::computeRho(cell);
}
static void computeJ(Cell const& cell, T j[DESCRIPTOR::d] )
{
lbDynamicsHelpers
::computeJ(cell, j);
}
static void computeRhoU(Cell const& cell, T& rho, T u[DESCRIPTOR::d])
{
lbDynamicsHelpers
::computeRhoU(cell, rho, u);
}
static void computeFeq(Cell const& cell, T fEq[DESCRIPTOR::q])
{
T rho{};
T u[2] {};
computeRhoU(cell, rho, u);
const T uSqr = u[0]*u[0] + u[1]*u[1];
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
fEq[iPop] = equilibrium(iPop, rho, u, uSqr);
}
}
static void computeFneq(Cell const& cell, T fNeq[DESCRIPTOR::q])
{
T rho{};
T u[2] {};
computeRhoU(cell, rho, u);
computeFneq(cell, fNeq, rho, u);
}
static void computeRhoJ(Cell const& cell, T& rho, T j[DESCRIPTOR::d])
{
lbDynamicsHelpers
::computeRhoJ(cell, rho, j);
}
static void computeStress(Cell const& cell, T rho, const T u[DESCRIPTOR::d],
T pi[util::TensorVal::n] )
{
lbDynamicsHelpers
::computeStress(cell, rho, u, pi);
}
static void computeAllMomenta(Cell const& cell, T& rho, T u[DESCRIPTOR::d],
T pi[util::TensorVal::n] )
{
lbDynamicsHelpers
::computeAllMomenta(cell, rho, u, pi);
}
static T computePiNeqNormSqr(Cell const& cell)
{
//return lbDynamicsHelpers
//::computePiNeqNormSqr(cell);
T rho, u[DESCRIPTOR::d], pi[util::TensorVal::n];
computeAllMomenta(cell, rho, u, pi);
T PiNeqNormSqr = pi[0]*pi[0] + 2.*pi[1]*pi[1] + pi[2]*pi[2];
if (util::TensorVal::n == 6) {
PiNeqNormSqr += pi[2]*pi[2] + pi[3]*pi[3] + 2.*pi[4]*pi[4] +pi[5]*pi[5];
}
return PiNeqNormSqr;
}
static T computeForcedPiNeqNormSqr(Cell const& cell)
{
//return lbDynamicsHelpers
//::computeForcedPiNeqNormSqr(cell);
T rho, u[DESCRIPTOR::d], pi[util::TensorVal::n];
computeAllMomenta(cell, rho, u, pi);
//Creation of body force tensor (rho/2.)*(G_alpha*U_beta + U_alpha*G_Beta)
T ForceTensor[util::TensorVal::n];
int iPi = 0;
for (int Alpha=0; Alpha()[Alpha]*u[Beta] + u[Alpha]*cell.template getFieldPointer()[Beta]);
++iPi;
}
}
// Creation of second-order moment off-equilibrium tensor
for (int iPi=0; iPi < util::TensorVal::n; ++iPi){
pi[iPi] += ForceTensor[iPi];
}
T PiNeqNormSqr = pi[0]*pi[0] + 2.*pi[1]*pi[1] + pi[2]*pi[2];
if (util::TensorVal::n == 6) {
PiNeqNormSqr += pi[2]*pi[2] + pi[3]*pi[3] + 2.*pi[4]*pi[4] +pi[5]*pi[5];
}
return PiNeqNormSqr;
}
static void modifyVelocity(Cell& cell, const T newU[DESCRIPTOR::d])
{
lbDynamicsHelpers
::modifyVelocity(cell, newU);
}
static void addExternalForce(Cell& cell, const T u[DESCRIPTOR::d], T omega, T amplitude=(T)1)
{
lbExternalHelpers::addExternalForce(cell, u, omega, amplitude);
}
static void swapAndStream2D(Cell **grid, int iX, int iY)
{
lbLatticeHelpers::swapAndStream2D(grid, iX, iY);
}
static void swapAndStream3D(Cell ***grid, int iX, int iY, int iZ)
{
lbLatticeHelpers::swapAndStream3D(grid, iX, iY, iZ);
}
}; // struct lbHelpers
/// All helper functions are inside this structure
template
struct lbDynamicsHelpers {
/// Computation of equilibrium distribution, second order in u
static T equilibrium(int iPop, T rho, const T u[DESCRIPTORBASE::d], const T uSqr)
{
T c_u = T();
for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
c_u += descriptors::c(iPop,iD)*u[iD];
}
return rho * descriptors::t(iPop) * ( (T)1 + descriptors::invCs2() * c_u
+ descriptors::invCs2() * descriptors::invCs2() * (T)0.5 * c_u *c_u
- descriptors::invCs2() * (T)0.5 * uSqr )
- descriptors::t(iPop);
}
/// Computation of equilibrium distribution, first order in u
static T equilibriumFirstOrder(int iPop, T rho, const T u[DESCRIPTORBASE::d])
{
T c_u = T();
for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
c_u += descriptors::c(iPop,iD)*u[iD];
}
return rho * descriptors::t(iPop) * ( (T)1 + c_u * descriptors::invCs2() ) - descriptors::t(iPop);
}
static T incEquilibrium( int iPop, const T j[DESCRIPTORBASE::d],
const T jSqr, const T pressure )
{
T c_j = T();
for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
c_j += descriptors::c(iPop,iD)*j[iD];
}
return descriptors::t(iPop) * ( descriptors::invCs2() * pressure +
descriptors::invCs2() * c_j +
descriptors::invCs2() * descriptors::invCs2()/(T)2 * c_j*c_j -
descriptors::invCs2()/(T)2 * jSqr
) - descriptors::t(iPop);
}
/// Computation of non-equilibrium distribution
static void computeFneq(CellBase const& cell, T fNeq[DESCRIPTORBASE::q], T rho, const T u[DESCRIPTORBASE::d])
{
const T uSqr = util::normSqr(u);
for (int iPop=0; iPop < DESCRIPTORBASE::q; ++iPop) {
fNeq[iPop] = cell[iPop] - equilibrium(iPop, rho, u, uSqr);
}
}
/// BGK collision step
static T bgkCollision(CellBase& cell, T const& rho, const T u[DESCRIPTORBASE::d], T const& omega)
{
const T uSqr = util::normSqr(u);
for (int iPop=0; iPop < DESCRIPTORBASE::q; ++iPop) {
cell[iPop] *= (T)1-omega;
cell[iPop] += omega * lbDynamicsHelpers::equilibrium(iPop, rho, u, uSqr );
}
return uSqr;
}
/// Incompressible BGK collision step
static T incBgkCollision(CellBase& cell, T pressure, const T j[DESCRIPTORBASE::d], T omega)
{
const T jSqr = util::normSqr(j);
for (int iPop=0; iPop < DESCRIPTORBASE::q; ++iPop) {
cell[iPop] *= (T)1-omega;
cell[iPop] += omega * lbDynamicsHelpers::incEquilibrium (
iPop, j, jSqr, pressure );
}
return jSqr;
}
/// BGK collision step with density correction
static T constRhoBgkCollision(CellBase& cell, T rho, const T u[DESCRIPTORBASE::d], T ratioRho, T omega)
{
const T uSqr = util::normSqr(u);
for (int iPop=0; iPop < DESCRIPTORBASE::q; ++iPop) {
T feq = lbDynamicsHelpers::equilibrium(iPop, rho, u, uSqr );
cell[iPop] =
ratioRho*(feq+descriptors::t(iPop))-descriptors::t(iPop) +
((T)1-omega)*(cell[iPop]-feq);
}
return uSqr;
}
/// RLB advection diffusion collision step
static T rlbCollision(CellBase& cell, T rho, const T u[DESCRIPTORBASE::d], T omega )
{
const T uSqr = util::normSqr( u );
// First-order moment for the regularization
T j1[DESCRIPTORBASE::d];
for ( int iD = 0; iD < DESCRIPTORBASE::d; ++iD ) {
j1[iD] = T();
}
T fEq[DESCRIPTORBASE::q];
for ( int iPop = 0; iPop < DESCRIPTORBASE::q; ++iPop ) {
fEq[iPop] = lbDynamicsHelpers::equilibriumFirstOrder( iPop, rho, u );
for ( int iD = 0; iD < DESCRIPTORBASE::d; ++iD ) {
j1[iD] += descriptors::c(iPop,iD) * ( cell[iPop] - fEq[iPop] );
}
}
// Collision step
for ( int iPop = 0; iPop < DESCRIPTORBASE::q; ++iPop ) {
T fNeq = T();
for ( int iD = 0; iD < DESCRIPTORBASE::d; ++iD ) {
fNeq += descriptors::c(iPop,iD) * j1[iD];
}
fNeq *= descriptors::t(iPop) * descriptors::invCs2();
cell[iPop] = fEq[iPop] + ( (T)1 - omega ) * fNeq;
}
return uSqr;
}
///// Computation of all equilibrium distribution (in momenta space)
static void computeMomentaEquilibrium( T momentaEq[DESCRIPTORBASE::q], T rho, const T u[DESCRIPTORBASE::d], T uSqr )
{
for (int iPop = 0; iPop < DESCRIPTORBASE::q; ++iPop) {
momentaEq[iPop] = T();
for (int jPop = 0; jPop < DESCRIPTORBASE::q; ++jPop) {
momentaEq[iPop] += DESCRIPTORBASE::M[iPop][jPop] *
(lbDynamicsHelpers::equilibrium(jPop,rho,u,uSqr) + DESCRIPTORBASE::t[jPop]);
}
}
}
static void computeMomenta(T momenta[DESCRIPTORBASE::q], CellBase& cell)
{
for (int iPop = 0; iPop < DESCRIPTORBASE::q; ++iPop) {
momenta[iPop] = T();
for (int jPop = 0; jPop < DESCRIPTORBASE::q; ++jPop) {
momenta[iPop] += DESCRIPTORBASE::M[iPop][jPop] *
(cell[jPop] + DESCRIPTORBASE::t[jPop]);
}
}
}
static T mrtCollision( CellBase& cell, T const& rho, const T u[DESCRIPTORBASE::d], T invM_S[DESCRIPTORBASE::q][DESCRIPTORBASE::q] )
{
//// Implemented in advectionDiffusionMRTlbHelpers2D.h and advectionDiffusionMRTlbHelpers3D.h
T uSqr = util::normSqr(u);
T momenta[DESCRIPTORBASE::q];
T momentaEq[DESCRIPTORBASE::q];
computeMomenta(momenta, cell);
computeMomentaEquilibrium(momentaEq, rho, u, uSqr);
// std::cout << "momenta = ";
// for (int i=0; i < DESCRIPTORBASE::q; ++i) {
// std::cout << momenta[i] << ", ";
// }
// std::cout << std::endl;
// std::cout << "momentaEq = ";
// for (int i=0; i < DESCRIPTORBASE::q; ++i) {
// std::cout << momentaEq[i] << ", ";
// }
// std::cout << std::endl;
for (int iPop = 0; iPop < DESCRIPTORBASE::q; ++iPop) {
T collisionTerm = T();
for (int jPop = 0; jPop < DESCRIPTORBASE::q; ++jPop) {
collisionTerm += invM_S[iPop][jPop] * (momenta[jPop] - momentaEq[jPop]);
}
cell[iPop] -= collisionTerm;
}
return uSqr;
}
/// Computation of density
static T computeRho(CellBase const& cell)
{
T rho = T();
for (int iPop=0; iPop < DESCRIPTORBASE::q; ++iPop) {
rho += cell[iPop];
}
rho += (T)1;
return rho;
}
/// Computation of momentum
static void computeJ(CellBase const& cell, T j[DESCRIPTORBASE::d])
{
for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
j[iD] = T();
}
for (int iPop=0; iPop < DESCRIPTORBASE::q; ++iPop) {
for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
j[iD] += cell[iPop]*descriptors::c(iPop,iD);
}
}
}
/// Computation of hydrodynamic variables
static void computeRhoU(CellBase const& cell, T& rho, T u[DESCRIPTORBASE::d])
{
rho = T();
for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
u[iD] = T();
}
for (int iPop=0; iPop < DESCRIPTORBASE::q; ++iPop) {
rho += cell[iPop];
for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
u[iD] += cell[iPop]*descriptors::c(iPop,iD);
}
}
rho += (T)1;
for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
u[iD] /= rho;
}
}
/// Computation of hydrodynamic variables
static void computeRhoJ(CellBase const& cell, T& rho, T j[DESCRIPTORBASE::d])
{
rho = T();
for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
j[iD] = T();
}
for (int iPop=0; iPop < DESCRIPTORBASE::q; ++iPop) {
rho += cell[iPop];
for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
j[iD] += cell[iPop]*descriptors::c(iPop,iD);
}
}
rho += (T)1;
}
/// Computation of stress tensor
static void computeStress(CellBase const& cell, T rho, const T u[DESCRIPTORBASE::d],
T pi[util::TensorVal::n] )
{
int iPi = 0;
for (int iAlpha=0; iAlpha < DESCRIPTORBASE::d; ++iAlpha) {
for (int iBeta=iAlpha; iBeta < DESCRIPTORBASE::d; ++iBeta) {
pi[iPi] = T();
for (int iPop=0; iPop < DESCRIPTORBASE::q; ++iPop) {
pi[iPi] += descriptors::c(iPop,iAlpha)*
descriptors::c(iPop,iBeta) * cell[iPop];
}
// stripe off equilibrium contribution
pi[iPi] -= rho*u[iAlpha]*u[iBeta];
if (iAlpha==iBeta) {
pi[iPi] -= 1./descriptors::invCs2()*(rho-(T)1);
}
++iPi;
}
}
}
/// Computation of all hydrodynamic variables
static void computeAllMomenta(CellBase const& cell, T& rho, T u[DESCRIPTORBASE::d],
T pi[util::TensorVal::n] )
{
computeRhoU(cell, rho, u);
computeStress(cell, rho, u, pi);
}
/*
/// Computes squared norm of non-equilibrium part of 2nd momentum
static T computePiNeqNormSqr(CellBase const& cell)
{
T rho, u[DESCRIPTORBASE::d], pi[util::TensorVal::n];
computeAllMomenta(cell, rho, u, pi);
T PiNeqNormSqr = pi[0]*pi[0] + 2.*pi[1]*pi[1] + pi[2]*pi[2];
if (util::TensorVal::n == 6) {
PiNeqNormSqr += pi[2]*pi[2] + pi[3]*pi[3] + 2.*pi[4]*pi[4] +pi[5]*pi[5];
}
return PiNeqNormSqr;
}
/// Computes squared norm of forced non-equilibrium part of 2nd momentum
static T computeForcedPiNeqNormSqr(CellBase const& cell)
{
T rho, u[DESCRIPTORBASE::d], pi[util::TensorVal::n];
computeAllMomenta(cell, rho, u, pi);
//Creation of body force tensor (rho/2.)*(G_alpha*U_beta + U_alpha*G_Beta)
T ForceTensor[util::TensorVal::n];
int iPi = 0;
for (int Alpha=0; Alpha()][Alpha]*u[Beta] + u[Alpha]*cell[DESCRIPTORBASE::template index()][Beta]);
++iPi;
}
}
// Creation of second-order moment off-equilibrium tensor
for (int iPi=0; iPi < util::TensorVal::n; ++iPi){
pi[iPi] += ForceTensor[iPi];
}
T PiNeqNormSqr = pi[0]*pi[0] + 2.*pi[1]*pi[1] + pi[2]*pi[2];
if (util::TensorVal::n == 6) {
PiNeqNormSqr += pi[2]*pi[2] + pi[3]*pi[3] + 2.*pi[4]*pi[4] +pi[5]*pi[5];
}
return PiNeqNormSqr;
}
*/
static void modifyVelocity(CellBase& cell, const T newU[DESCRIPTORBASE::d])
{
T rho, oldU[DESCRIPTORBASE::d];
computeRhoU(cell, rho, oldU);
const T oldUSqr = util::normSqr(oldU);
const T newUSqr = util::normSqr(newU);
for (int iPop=0; iPop
struct lbExternalHelpers {
/// Add a force term after BGK collision
static void addExternalForce(Cell& cell, const T u[DESCRIPTOR::d], T omega, T amplitude)
{
const T* force = cell.template getFieldPointer();
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
T c_u = T();
for (int iD=0; iD < DESCRIPTOR::d; ++iD) {
c_u += descriptors::c(iPop,iD)*u[iD];
}
c_u *= descriptors::invCs2()*descriptors::invCs2();
T forceTerm = T();
for (int iD=0; iD < DESCRIPTOR::d; ++iD) {
forceTerm +=
( ((T)descriptors::c(iPop,iD)-u[iD]) * descriptors::invCs2()
+ c_u * descriptors::c(iPop,iD)
)
* force[iD];
}
forceTerm *= descriptors::t(iPop);
forceTerm *= T(1) - omega/T(2);
forceTerm *= amplitude;
cell[iPop] += forceTerm;
}
}
}; // struct externalFieldHelpers
/// Helper functions with full-lattice access
template
struct lbLatticeHelpers {
/// Swap ("bounce-back") values of a cell (2D), and apply streaming step
static void swapAndStream2D(Cell **grid, int iX, int iY)
{
const int half = DESCRIPTOR::q/2;
for (int iPop=1; iPop<=half; ++iPop) {
int nextX = iX + descriptors::c(iPop,0);
int nextY = iY + descriptors::c(iPop,1);
T fTmp = grid[iX][iY][iPop];
grid[iX][iY][iPop] = grid[iX][iY][iPop+half];
grid[iX][iY][iPop+half] = grid[nextX][nextY][iPop];
grid[nextX][nextY][iPop] = fTmp;
}
}
/// Swap ("bounce-back") values of a cell (3D), and apply streaming step
static void swapAndStream3D(Cell ***grid,
int iX, int iY, int iZ)
{
const int half = DESCRIPTOR::q/2;
for (int iPop=1; iPop<=half; ++iPop) {
int nextX = iX + descriptors::c(iPop,0);
int nextY = iY + descriptors::c(iPop,1);
int nextZ = iZ + descriptors::c(iPop,2);
T fTmp = grid[iX][iY][iZ][iPop];
grid[iX][iY][iZ][iPop] = grid[iX][iY][iZ][iPop+half];
grid[iX][iY][iZ][iPop+half] = grid[nextX][nextY][nextZ][iPop];
grid[nextX][nextY][nextZ][iPop] = fTmp;
}
}
};
/// All boundary helper functions are inside this structure
template
struct BoundaryHelpers {
static void computeStress (
Cell const& cell, T rho, const T u[DESCRIPTOR::d],
T pi[util::TensorVal::n] )
{
typedef DESCRIPTOR L;
const T uSqr = util::normSqr(u);
std::vector const& onWallIndices = util::subIndex();
std::vector const& normalIndices = util::subIndex();
T fNeq[DESCRIPTOR::q];
for (unsigned fIndex=0; fIndex::equilibrium(iPop, rho, u, uSqr);
}
}
for (unsigned fIndex=0; fIndex::equilibrium(iPop, rho, u, uSqr);
}
int iPi = 0;
for (int iAlpha=0; iAlpha(iPop,iAlpha)*descriptors::c(iPop,iBeta)*fNeq[iPop];
}
for (unsigned fIndex=0; fIndex(iPop,iAlpha)*descriptors::c(iPop,iBeta)*
fNeq[iPop];
}
++iPi;
}
}
}
}; // struct boundaryHelpers
} // namespace olb
// The specialized code is directly included. That is because we never want
// it to be precompiled so that in both the precompiled and the
// "include-everything" version, the compiler can apply all the
// optimizations it wants.
#include "lbHelpersD2Q5.h"
#include "lbHelpersD2Q9.h"
#include "lbHelpersD3Q7.h"
#include "lbHelpersD3Q15.h"
#include "lbHelpersD3Q19.h"
#include "lbHelpersD3Q27.h"
#endif