/* This file is part of the OpenLB library
*
* Copyright (C) 2006, Orestis Malaspinas and 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.
*/
#ifndef INAMURO_NEWTON_RAPHSON_DYNAMICS_HH
#define INAMURO_NEWTON_RAPHSON_DYNAMICS_HH
#include "inamuroNewtonRaphsonDynamics.h"
#include "dynamics/latticeDescriptors.h"
#include "core/util.h"
#include "dynamics/lbHelpers.h"
#include
namespace olb {
template
InamuroNewtonRaphsonDynamics::InamuroNewtonRaphsonDynamics (
T omega, Momenta& momenta )
: BasicDynamics(momenta),
_boundaryDynamics(omega, momenta),
clout(std::cout,"InamuroNewtonRaphsonDynamics")
{
_xi[0] = (T)1;
for (int iDim = 1; iDim < DESCRIPTOR::d; ++iDim) {
_xi[iDim] = T();
}
}
template
T InamuroNewtonRaphsonDynamics::
computeEquilibrium(int iPop, T rho, const T u[DESCRIPTOR::d], T uSqr) const
{
return _boundaryDynamics.computeEquilibrium(iPop, rho, u, uSqr);
}
template
void InamuroNewtonRaphsonDynamics::collide (
Cell& cell,
LatticeStatistics& statistics )
{
typedef DESCRIPTOR L;
T rho, u[L::d];
this->_momenta.computeRhoU(cell,rho,u);
std::vector missingIndexes = util::subIndexOutgoing();
std::vector knownIndexes;
bool test[L::q];
for (int iPop = 0; iPop < L::q; ++iPop) {
test[iPop] = true;
}
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
test[missingIndexes[iPop]] = false;
}
for (int iPop = 0; iPop < L::q; ++iPop) {
if (test[iPop]) {
knownIndexes.push_back(iPop);
}
}
T approxMomentum[L::d];
computeApproxMomentum(approxMomentum,cell,rho,u,_xi,knownIndexes,missingIndexes);
T error = computeError(rho, u,approxMomentum);
int counter = 0;
while (error > 1.0e-15) {
++counter;
T gradError[L::d], gradGradError[L::d][L::d];
computeGradGradError(gradGradError,gradError,rho,u,_xi,approxMomentum,missingIndexes);
bool everythingWentFine = newtonRaphson(_xi,gradError,gradGradError);
if ((counter > 100000) || everythingWentFine == false) {
// if we need more that 100000 iterations or
// if we have a problem with the inversion of the
// jacobian matrix, we stop the program and
// print this error message on the screen.
clout << "Failed to converge...." << std::endl;
clout << "Error = " << error << std::endl;
clout << "u = (" << rho*u[0];
for (int iD=1; iD(uCs);
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
cell[missingIndexes[iPop]] = computeEquilibrium(missingIndexes[iPop],_xi[0],uCs,uCsSqr);
}
_boundaryDynamics.collide(cell, statistics);
}
template
T InamuroNewtonRaphsonDynamics::getOmega() const
{
return _boundaryDynamics.getOmega();
}
template
void InamuroNewtonRaphsonDynamics::setOmega(T omega)
{
_boundaryDynamics.setOmega(omega);
}
template
void InamuroNewtonRaphsonDynamics::
computeApproxMomentum(T approxMomentum[DESCRIPTOR::d],const Cell &cell,
const T &rho, const T u[DESCRIPTOR::d], const T xi[DESCRIPTOR::d],
const std::vector knownIndexes,const std::vector missingIndexes)
{
typedef DESCRIPTOR L;
T csVel[L::d];
int counter = 0;
for (int iDim = 0; iDim < L::d; ++iDim) {
if (direction == iDim) {
++counter;
csVel[iDim] = u[iDim];
} else {
csVel[iDim] = u[iDim] + xi[iDim+1-counter];
}
}
T csVelSqr = util::normSqr(csVel);
for (int iDim = 0; iDim < L::d; ++iDim) {
approxMomentum[iDim] = T();
for (unsigned iPop = 0; iPop < knownIndexes.size(); ++iPop) {
approxMomentum[iDim] += descriptors::c(knownIndexes[iPop],iDim) *
cell[knownIndexes[iPop]];
}
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
approxMomentum[iDim] += descriptors::c(missingIndexes[iPop],iDim) *
computeEquilibrium(missingIndexes[iPop],xi[0],csVel,csVelSqr);
}
}
}
template
T InamuroNewtonRaphsonDynamics::
computeError(const T &rho, const T u[DESCRIPTOR::d], const T approxMomentum[DESCRIPTOR::d])
{
typedef DESCRIPTOR L;
T err = T();
for (int iDim = 0; iDim < L::d; ++iDim) {
err += (rho * u[iDim]-approxMomentum[iDim]) * (rho * u[iDim]-approxMomentum[iDim]);
}
return sqrt(err);
}
template
void InamuroNewtonRaphsonDynamics::computeGradGradError(
T gradGradError[DESCRIPTOR::d][DESCRIPTOR::d],T gradError[DESCRIPTOR::d],
const T &rho, const T u[DESCRIPTOR::d],const T xi[DESCRIPTOR::d],
const T approxMomentum[DESCRIPTOR::d],
const std::vector missingIndexes)
{
typedef DESCRIPTOR L;
T csVel[L::d];
int counter = 0;
for (int iDim = 0; iDim < L::d; ++iDim) {
if (direction == iDim) {
++counter;
csVel[iDim] = u[iDim];
} else {
csVel[iDim] = u[iDim] + xi[iDim+1-counter];
}
}
T csVelSqr = util::normSqr(csVel);
std::vector df[L::d];
for (int iXi = 0; iXi < L::d; ++iXi) {
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
df[iXi].push_back(T());
}
}
// all the null terms are allready contained in df (no need for else in the ifs)
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
T cu = T();
for (int iDim = 0; iDim < L::d; ++iDim) {
cu += descriptors::c(missingIndexes[iPop],iDim) * csVel[iDim];
}
df[0][iPop] = descriptors::t(missingIndexes[iPop])
* ((T)1+descriptors::invCs2()*cu
+ 0.5 * descriptors::invCs2() * descriptors::invCs2() * cu * cu
- 0.5 * descriptors::invCs2() * csVelSqr);
}
counter = 0;
for (int iDim = 0; iDim < L::d; ++iDim) {
if (direction == iDim) {
++counter;
} else {
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
T temp = T();
for (int jDim = 0; jDim < L::d; ++jDim) {
temp += descriptors::c(missingIndexes[iPop],jDim) * csVel[jDim];
}
df[iDim+1-counter][iPop] = xi[0]*descriptors::t(missingIndexes[iPop]) *
(descriptors::invCs2()*descriptors::c(missingIndexes[iPop],iDim)
+ descriptors::invCs2()*descriptors::invCs2()*descriptors::c(missingIndexes[iPop],iDim)*temp
- descriptors::invCs2()*csVel[iDim]);
}
}
}
std::vector ddf[L::d][L::d];
for (int iAlpha = 0; iAlpha < L::d; ++iAlpha) {
for (int iBeta = 0; iBeta < L::d; ++iBeta) {
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
ddf[iAlpha][iBeta].push_back(T());
}
}
}
// ddf contains allready all the zero terms
counter = 0;
for (int iDim = 0; iDim < L::d; ++iDim) {
if (direction == iDim) {
++counter;
} else {
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
T temp = T();
for (int jDim = 0; jDim < L::d; ++jDim) {
temp += descriptors::c(missingIndexes[iPop],jDim) * csVel[jDim];
}
T d_rho_sa = descriptors::t(missingIndexes[iPop]) *
(descriptors::invCs2()*descriptors::c(missingIndexes[iPop],iDim)
+ descriptors::invCs2()*descriptors::invCs2()*descriptors::c(missingIndexes[iPop],iDim)*temp
- descriptors::invCs2()*csVel[iDim]);
ddf[iDim+1-counter][0][iPop] = d_rho_sa;
ddf[0][iDim+1-counter][iPop] = d_rho_sa;
}
}
}
for (int iAlpha = 1; iAlpha < L::d; ++iAlpha) {
for (int iBeta = 1; iBeta < L::d; ++iBeta) {
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
ddf[iAlpha][iBeta][iPop] = descriptors::t(missingIndexes[iPop])*xi[0] *
descriptors::invCs2()*descriptors::invCs2()*descriptors::c(missingIndexes[iPop],iAlpha)*descriptors::c(missingIndexes[iPop],iBeta);
if (iAlpha == iBeta) {
ddf[iAlpha][iBeta][iPop] -= descriptors::t(missingIndexes[iPop])*xi[0] * descriptors::invCs2();
}
}
}
}
// computation of the vector gradError
counter = 0;
for (int iDim = 0; iDim < L::d; ++iDim) {
T du[L::d];
gradError[iDim] = T();
for (int jDim = 0; jDim < L::d; ++jDim) {
du[jDim] = T();
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
du[jDim] += descriptors::c(missingIndexes[iPop],jDim) * df[iDim][iPop];
}
gradError[iDim] += (approxMomentum[jDim]- rho * u[jDim]) * du[jDim];
}
gradError[iDim] *= (T)2;
}
// computation of the matrix gradGradError
for (int iAlpha = 0; iAlpha < L::d; ++iAlpha) {
for (int iBeta = 0; iBeta < L::d; ++iBeta) {
gradGradError[iAlpha][iBeta] = T();
T duAlpha[L::d], duBeta[L::d], ddu[L::d];
for (int iDim = 0; iDim < L::d; ++iDim) {
duAlpha[iDim] = T();
duBeta[iDim] = T();
ddu[iDim] = T();
for (unsigned iPop = 0; iPop < missingIndexes.size(); ++iPop) {
duAlpha[iDim] += descriptors::c(missingIndexes[iPop],iDim)
* df[iAlpha][iPop];
duBeta[iDim] += descriptors::c(missingIndexes[iPop],iDim)
* df[iBeta][iPop];
ddu[iDim] += descriptors::c(missingIndexes[iPop],iDim)
* ddf[iAlpha][iBeta][iPop];
}
gradGradError[iAlpha][iBeta] += duAlpha[iDim] * duBeta[iDim]
+ (approxMomentum[iDim]-rho * u[iDim]) * ddu[iDim];
}
gradGradError[iAlpha][iBeta] *= (T)2;
// gradGradError = 2*sum_alpha(du_alpha/dxi_beta*du_alpha/dxi_gamma +
// approxMomentum_alpha-u_alpha*d^2u_alpha/(dxi_gamma*dxi_beta))
}
}
}
template
bool InamuroNewtonRaphsonDynamics::
newtonRaphson(T xi[DESCRIPTOR::d],
const T gradError[DESCRIPTOR::d],
const T gradGradError[DESCRIPTOR::d][DESCRIPTOR::d])
{
typedef DESCRIPTOR L;
T invGradGradError[L::d][L::d];
bool inversion = invert(gradGradError,invGradGradError);
for (int iXi = 0; iXi < L::d; ++iXi) {
T correction = T();
for (int iAlpha = 0; iAlpha < L::d; ++iAlpha) {
correction += invGradGradError[iXi][iAlpha] * gradError[iAlpha];
}
xi[iXi] -= correction;
}
return inversion;
}
template
bool InamuroNewtonRaphsonDynamics::
invert(const T a[2][2],T invA[2][2])
{
T detA = a[0][0]*a[1][1] - a[0][1]*a[1][0];
if (fabs(detA) < 1.0e-13) {
clout << "error detA too small! = " << detA << std::endl;
for (int iAlpha = 0; iAlpha < 2; ++iAlpha) {
for (int iBeta = 0; iBeta < 2; ++iBeta) {
clout << a[iAlpha][iBeta] << "\t";
}
clout << std::endl;
}
return false;
} else {
invA[0][0] = a[1][1];
invA[1][1] = a[0][0];
invA[0][1] = -a[1][0];
invA[1][0] = -a[0][1];
for (int iPop = 0; iPop < 2; ++iPop) {
for (int jPop = 0; jPop < 2; ++jPop) {
invA[iPop][jPop] /= detA;
}
}
return true;
}
}
template
bool InamuroNewtonRaphsonDynamics::invert(const T a[3][3],T invA[3][3])
{
T detA = a[0][0]*a[1][1]*a[2][2] + a[1][0]*a[2][1]*a[0][2] + a[2][0]*a[0][1]*a[1][2]
- a[0][0]*a[2][1]*a[1][2] - a[2][0]*a[1][1]*a[0][2] - a[1][0]*a[0][1]*a[2][2];
if (fabs(detA) < 1.0e-13) {
clout << "Error: detA too small! = " << detA << std::endl;
for (int iAlpha = 0; iAlpha < 3; ++iAlpha) {
for (int iBeta = 0; iBeta < 3; ++iBeta) {
clout << a[iAlpha][iBeta] << "\t";
}
clout << std::endl;
}
return false;
} else {
invA[0][0] = a[1][1]*a[2][2]-a[1][2]*a[2][1];
invA[0][1] = a[0][2]*a[2][1]-a[0][1]*a[2][2];
invA[0][2] = a[0][1]*a[1][2]-a[0][2]*a[1][1];
invA[1][0] = a[1][2]*a[2][0]-a[1][0]*a[2][2];
invA[1][1] = a[0][0]*a[2][2]-a[0][2]*a[2][0];
invA[1][2] = a[0][2]*a[1][0]-a[0][0]*a[1][2];
invA[2][0] = a[1][0]*a[2][1]-a[1][1]*a[2][0];
invA[2][1] = a[0][1]*a[2][0]-a[0][0]*a[2][1];
invA[2][2] = a[0][0]*a[1][1]-a[0][1]*a[1][0];
for (int iPop = 0; iPop < 3; ++iPop) {
for (int jPop = 0; jPop < 3; ++jPop) {
invA[iPop][jPop] /= detA;
}
}
return true;
}
}
} // namespace olb
#endif