/* This file is part of the OpenLB library
*
* Copyright (C) 2006, 2007, 2017 Orestis Malaspinas, Jonas Latt, Mathias J. Krause
* 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
* A collection of entropic dynamics classes (e.g. Entropic, ForcedEntropic, Entropic, ForcedEntropic) with which a Cell object
* can be instantiated -- generic implementation.
*/
#ifndef ENTROPIC_LB_DYNAMICS_HH
#define ENTROPIC_LB_DYNAMICS_HH
#include
#include
#include "lbHelpers.h"
#include "entropicDynamics.h"
#include "entropicLbHelpers.h"
namespace olb {
//==============================================================================//
/////////////////////////// Class EntropicEqDynamics ///////////////////////////////
//==============================================================================//
/** \param omega_ relaxation parameter, related to the dynamic viscosity
* \param momenta_ a Momenta object to know how to compute velocity momenta
*/
template
EntropicEqDynamics::EntropicEqDynamics (
T omega_, Momenta& momenta_ )
: BasicDynamics(momenta_),
omega(omega_)
{ }
template
T EntropicEqDynamics::computeEquilibrium(int iPop, T rho, const T u[DESCRIPTOR::d], T uSqr) const
{
return entropicLbHelpers::equilibrium(iPop,rho,u);
}
template
void EntropicEqDynamics::collide (
Cell& cell,
LatticeStatistics& statistics )
{
typedef DESCRIPTOR L;
typedef entropicLbHelpers eLbH;
T rho, u[DESCRIPTOR::d];
this->_momenta.computeRhoU(cell, rho, u);
T uSqr = util::normSqr(u);
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
cell[iPop] += omega * (eLbH::equilibrium(iPop,rho,u) - cell[iPop]);
}
statistics.incrementStats(rho, uSqr);
}
template
T EntropicEqDynamics::getOmega() const
{
return omega;
}
template
void EntropicEqDynamics::setOmega(T omega_)
{
omega = omega_;
}
//====================================================================//
//////////////////// Class ForcedEntropicEqDynamics //////////////////////
//====================================================================//
/** \param omega_ relaxation parameter, related to the dynamic viscosity
*/
template
ForcedEntropicEqDynamics::ForcedEntropicEqDynamics (
T omega_, Momenta& momenta_ )
: BasicDynamics(momenta_),
omega(omega_)
{ }
template
T ForcedEntropicEqDynamics::computeEquilibrium(int iPop, T rho, const T u[DESCRIPTOR::d], T uSqr) const
{
return entropicLbHelpers::equilibrium(iPop,rho,u);
}
template
void ForcedEntropicEqDynamics::collide (
Cell& cell,
LatticeStatistics& statistics )
{
typedef DESCRIPTOR L;
typedef entropicLbHelpers eLbH;
T rho, u[DESCRIPTOR::d];
this->_momenta.computeRhoU(cell, rho, u);
T* force = cell.template getFieldPointer();
for (int iDim=0; iDim(u);
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
cell[iPop] += omega * (eLbH::equilibrium(iPop,rho,u) - cell[iPop]);
}
lbHelpers::addExternalForce(cell, u, omega);
statistics.incrementStats(rho, uSqr);
}
template
T ForcedEntropicEqDynamics::getOmega() const
{
return omega;
}
template
void ForcedEntropicEqDynamics::setOmega(T omega_)
{
omega = omega_;
}
//==============================================================================//
/////////////////////////// Class EntropicDynamics ///////////////////////////////
//==============================================================================//
/** \param omega_ relaxation parameter, related to the dynamic viscosity
* \param momenta_ a Momenta object to know how to compute velocity momenta
*/
template
EntropicDynamics::EntropicDynamics (
T omega_, Momenta& momenta_ )
: BasicDynamics(momenta_),
omega(omega_)
{ }
template
T EntropicDynamics::computeEquilibrium(int iPop, T rho, const T u[DESCRIPTOR::d], T uSqr) const
{
return entropicLbHelpers::equilibrium(iPop,rho,u);
}
template
void EntropicDynamics::collide (
Cell& cell,
LatticeStatistics& statistics )
{
typedef DESCRIPTOR L;
typedef entropicLbHelpers eLbH;
T rho, u[DESCRIPTOR::d];
this->_momenta.computeRhoU(cell, rho, u);
T uSqr = util::normSqr(u);
T f[L::q], fEq[L::q], fNeq[L::q];
for (int iPop = 0; iPop < L::q; ++iPop) {
fEq[iPop] = eLbH::equilibrium(iPop,rho,u);
fNeq[iPop] = cell[iPop] - fEq[iPop];
f[iPop] = cell[iPop] + descriptors::t(iPop);
fEq[iPop] += descriptors::t(iPop);
}
//==============================================================================//
//============= Evaluation of alpha using a Newton Raphson algorithm ===========//
//==============================================================================//
T alpha = 2.0;
bool converged = getAlpha(alpha,f,fNeq);
if (!converged) {
std::cout << "Newton-Raphson failed to converge.\n";
exit(1);
}
OLB_ASSERT(converged,"Entropy growth failed to converge!");
T omegaTot = omega / 2.0 * alpha;
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
cell[iPop] *= (T)1-omegaTot;
cell[iPop] += omegaTot * (fEq[iPop]-descriptors::t(iPop));
}
statistics.incrementStats(rho, uSqr);
}
template
T EntropicDynamics::getOmega() const
{
return omega;
}
template
void EntropicDynamics::setOmega(T omega_)
{
omega = omega_;
}
template
T EntropicDynamics::computeEntropy(const T f[])
{
typedef DESCRIPTOR L;
T entropy = T();
for (int iPop = 0; iPop < L::q; ++iPop) {
OLB_ASSERT(f[iPop] > T(), "f[iPop] <= 0");
entropy += f[iPop]*log(f[iPop]/descriptors::t(iPop));
}
return entropy;
}
template
T EntropicDynamics::computeEntropyGrowth(const T f[], const T fNeq[], const T &alpha)
{
typedef DESCRIPTOR L;
T fAlphaFneq[L::q];
for (int iPop = 0; iPop < L::q; ++iPop) {
fAlphaFneq[iPop] = f[iPop] - alpha*fNeq[iPop];
}
return computeEntropy(f) - computeEntropy(fAlphaFneq);
}
template
T EntropicDynamics::computeEntropyGrowthDerivative(const T f[], const T fNeq[], const T &alpha)
{
typedef DESCRIPTOR L;
T entropyGrowthDerivative = T();
for (int iPop = 0; iPop < L::q; ++iPop) {
T tmp = f[iPop] - alpha*fNeq[iPop];
OLB_ASSERT(tmp > T(), "f[iPop] - alpha*fNeq[iPop] <= 0");
entropyGrowthDerivative += fNeq[iPop]*(log(tmp/descriptors::t(iPop)));
}
return entropyGrowthDerivative;
}
template
bool EntropicDynamics::getAlpha(T &alpha, const T f[], const T fNeq[])
{
const T epsilon = std::numeric_limits::epsilon();
T alphaGuess = T();
const T var = 100.0;
const T errorMax = epsilon*var;
T error = 1.0;
int count = 0;
for (count = 0; count < 10000; ++count) {
T entGrowth = computeEntropyGrowth(f,fNeq,alpha);
T entGrowthDerivative = computeEntropyGrowthDerivative(f,fNeq,alpha);
if ((error < errorMax) || (fabs(entGrowth) < var*epsilon)) {
return true;
}
alphaGuess = alpha - entGrowth /
entGrowthDerivative;
error = fabs(alpha-alphaGuess);
alpha = alphaGuess;
}
return false;
}
//====================================================================//
//////////////////// Class ForcedEntropicDynamics //////////////////////
//====================================================================//
/** \param omega_ relaxation parameter, related to the dynamic viscosity
*/
template
ForcedEntropicDynamics::ForcedEntropicDynamics (
T omega_, Momenta& momenta_ )
: BasicDynamics(momenta_),
omega(omega_)
{ }
template
T ForcedEntropicDynamics::computeEquilibrium(int iPop, T rho, const T u[DESCRIPTOR::d], T uSqr) const
{
return entropicLbHelpers::equilibrium(iPop,rho,u);
}
template
void ForcedEntropicDynamics::collide (
Cell& cell,
LatticeStatistics& statistics )
{
typedef DESCRIPTOR L;
typedef entropicLbHelpers eLbH;
T rho, u[DESCRIPTOR::d];
this->_momenta.computeRhoU(cell, rho, u);
T uSqr = util::normSqr(u);
T f[L::q], fEq[L::q], fNeq[L::q];
for (int iPop = 0; iPop < L::q; ++iPop) {
fEq[iPop] = eLbH::equilibrium(iPop,rho,u);
fNeq[iPop] = cell[iPop] - fEq[iPop];
f[iPop] = cell[iPop] + descriptors::t(iPop);
fEq[iPop] += descriptors::t(iPop);
}
//==============================================================================//
//============= Evaluation of alpha using a Newton Raphson algorithm ===========//
//==============================================================================//
T alpha = 2.0;
bool converged = getAlpha(alpha,f,fNeq);
if (!converged) {
std::cout << "Newton-Raphson failed to converge.\n";
exit(1);
}
OLB_ASSERT(converged,"Entropy growth failed to converge!");
T* force = cell.template getFieldPointer();
for (int iDim=0; iDim(u);
T omegaTot = omega / 2.0 * alpha;
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
cell[iPop] *= (T)1-omegaTot;
cell[iPop] += omegaTot * eLbH::equilibrium(iPop,rho,u);
}
lbHelpers::addExternalForce(cell, u, omegaTot);
statistics.incrementStats(rho, uSqr);
}
template
T ForcedEntropicDynamics::getOmega() const
{
return omega;
}
template
void ForcedEntropicDynamics::setOmega(T omega_)
{
omega = omega_;
}
template
T ForcedEntropicDynamics::computeEntropy(const T f[])
{
typedef DESCRIPTOR L;
T entropy = T();
for (int iPop = 0; iPop < L::q; ++iPop) {
OLB_ASSERT(f[iPop] > T(), "f[iPop] <= 0");
entropy += f[iPop]*log(f[iPop]/descriptors::t(iPop));
}
return entropy;
}
template
T ForcedEntropicDynamics::computeEntropyGrowth(const T f[], const T fNeq[], const T &alpha)
{
typedef DESCRIPTOR L;
T fAlphaFneq[L::q];
for (int iPop = 0; iPop < L::q; ++iPop) {
fAlphaFneq[iPop] = f[iPop] - alpha*fNeq[iPop];
}
return computeEntropy(f) - computeEntropy(fAlphaFneq);
}
template
T ForcedEntropicDynamics::computeEntropyGrowthDerivative(const T f[], const T fNeq[], const T &alpha)
{
typedef DESCRIPTOR L;
T entropyGrowthDerivative = T();
for (int iPop = 0; iPop < L::q; ++iPop) {
T tmp = f[iPop] - alpha*fNeq[iPop];
OLB_ASSERT(tmp > T(), "f[iPop] - alpha*fNeq[iPop] <= 0");
entropyGrowthDerivative += fNeq[iPop]*log(tmp/descriptors::t(iPop));
}
return entropyGrowthDerivative;
}
template
bool ForcedEntropicDynamics::getAlpha(T &alpha, const T f[], const T fNeq[])
{
const T epsilon = std::numeric_limits::epsilon();
T alphaGuess = T();
const T var = 100.0;
const T errorMax = epsilon*var;
T error = 1.0;
int count = 0;
for (count = 0; count < 10000; ++count) {
T entGrowth = computeEntropyGrowth(f,fNeq,alpha);
T entGrowthDerivative = computeEntropyGrowthDerivative(f,fNeq,alpha);
if ((error < errorMax) || (fabs(entGrowth) < var*epsilon)) {
return true;
}
alphaGuess = alpha - entGrowth /
entGrowthDerivative;
error = fabs(alpha-alphaGuess);
alpha = alphaGuess;
}
return false;
}
}
#endif