/* 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