/* This file is part of the OpenLB library
*
* Copyright (C) 2018 Marc Haussmann
* 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 WALLFUNCTION_BOUNDARY_POST_PROCESSORS_3D_HH
#define WALLFUNCTION_BOUNDARY_POST_PROCESSORS_3D_HH
#include "wallFunctionBoundaryPostProcessors3D.h"
#include "core/finiteDifference3D.h"
#include "core/blockLattice3D.h"
#include "dynamics/firstOrderLbHelpers.h"
#include "core/util.h"
#include "utilities/vectorHelpers.h"
namespace olb {
template
Musker::Musker(T nu, T y, T rho) : AnalyticalF1D(1), _nu(nu), _y(y),_rho(rho)
{
this->getName() = "Musker";
}
template
bool Musker::operator()(T output[], const S tau_w[])
{
T y_plus = _y*sqrt(tau_w[0]/_rho)/_nu;
T a = 5.424;
T b = 0.119760479041916168;
T c = 0.488023952095808383;
T d = 0.434;
T e = 3.50727901936264842;
output[0] = sqrt(tau_w[0]/_rho)*(a*atan(b*y_plus - c) +
d*log(pow(y_plus+10.6, 9.6)/pow(pow(y_plus, 2) - 8.15*y_plus + 86, 2)) - e);
// Condition for the sub-viscous layer : TODO MARC H
if (output[0] < 0) {
output[0] = y_plus * sqrt(tau_w[0]/_rho);
}
return true;
}
template
PowerLawProfile::PowerLawProfile(T nu, T u2, T y2, T y1, T rho) : AnalyticalF1D(2), _nu(nu), _u2(u2), _y2(y2), _y1(y1), _rho(rho)
{
this->getName() = "PowerLawProfile";
}
template
bool PowerLawProfile::operator()(T output[], const S input[])
{
T tau_w = 0.0246384 * _rho * pow(_nu, 0.25) * pow(_u2, 1.75) / pow(_y2, 0.25);
T u_tau = sqrt(tau_w/_rho);
T y_plus = _y1 * u_tau / _nu;
if (y_plus > 30.0) {
output[0] = tau_w;
output[1] = u_tau * 8.3 * pow(y_plus, 1./7.);
} else if (y_plus < 30.0 && y_plus > 5.) {
output[0] = tau_w;
output[1] = u_tau * (-2.81742 + 4.85723 * log(y_plus));
} else {
output[0] = 2.*_u2 * _rho * _nu / _y2;
if (output[0] < 0.) {
output[0]*=-1.;
}
u_tau = sqrt(output[0]/_rho);
y_plus = _y1 * u_tau / _nu;
output[1] = u_tau * y_plus;
}
return true;
}
//////// WallFunctionBoundaryProcessor3D ////////////////////////////////
template
WallFunctionBoundaryProcessor3D::WallFunctionBoundaryProcessor3D(int x0_, int x1_, int y0_, int y1_, int z0_, int z1_,
BlockGeometryStructure3D& blockGeometryStructure,
std::vector discreteNormal, std::vector missingIndices,
UnitConverter const& converter, wallFunctionParam const& wallFunctionParam,
IndicatorF3D* geoIndicator)
: x0(x0_), x1(x1_), y0(y0_), y1(y1_), z0(z0_), z1(z1_),
_blockGeometryStructure(blockGeometryStructure),
_discreteNormal(discreteNormal), _missingIndices(missingIndices),
_converter(converter), _wallFunctionParam(wallFunctionParam)
{
// Define Direction and orientatation
discreteNormalX = _discreteNormal[0];
discreteNormalY = _discreteNormal[1];
discreteNormalZ = _discreteNormal[2];
int Type_BC = discreteNormalX*discreteNormalX + discreteNormalY*discreteNormalY + discreteNormalZ*discreteNormalZ;
T normal_norm = sqrt(Type_BC); // l2 norm : magnitude of the vector
if (Type_BC == 1) { // Straight plane
if (discreteNormalX != 0) {
orientation = discreteNormalX;
direction = 0;
} else if (discreteNormalY != 0) {
orientation = discreteNormalY;
direction = 1;
} else if (discreteNormalZ != 0) {
orientation = discreteNormalZ;
direction = 2;
}
} else if (Type_BC == 2) { // Edge
orientation = 0;
direction = 0;
} else if (Type_BC == 3) { // Corner
orientation = 0;
direction = 0;
}
if (geoIndicator == NULL) {
/// === STEP 1 : Define distance for boundary and neighbor nodes
y_1 = 0.5; // Default half-way Bounce-Back distance - [m] DESCRIPTOR units
if (_wallFunctionParam.latticeWalldistance > 0.) {
y_1 = _wallFunctionParam.latticeWalldistance; // [m] DESCRIPTOR units
}
y_1 *= normal_norm; // DESCRIPTOR units
// Define the unit normal vector
unit_normal[0] = discreteNormalX / normal_norm;
unit_normal[1] = discreteNormalY / normal_norm;
unit_normal[2] = discreteNormalZ / normal_norm;
} else {
calculateWallDistances(geoIndicator);
if (y_1 > 2. || std::isnan(unit_normal[0]) || std::isnan(unit_normal[1]) || std::isnan(unit_normal[2])) {
y_1 = 0.5; // Default half-way Bounce-Back distance - [m] DESCRIPTOR units
if (_wallFunctionParam.latticeWalldistance > 0.) {
y_1 = _wallFunctionParam.latticeWalldistance; // [m] DESCRIPTOR units
}
y_1 *= normal_norm; // DESCRIPTOR units
// Define the unit normal vector
unit_normal[0] = discreteNormalX / normal_norm;
unit_normal[1] = discreteNormalY / normal_norm;
unit_normal[2] = discreteNormalZ / normal_norm;
}
}
y_2 = y_1 + normal_norm; // DESCRIPTOR units
// Computation of Rho - Zou an He - Speed up
getIndices(direction, 0, onWallIndices);
getIndices(direction, orientation, normalIndices);
// Vector needed for Pi Computation - FNeq Bounce-Back - Speep up
// Malaspinas condition c*n < zero
// Definition of directions pointing towards the fluid
getIndices(direction, -orientation, normalInwardsIndices);
}
template
void WallFunctionBoundaryProcessor3D::processSubDomain(BlockLattice3D& blockLattice, int x0_, int x1_, int y0_, int y1_, int z0_, int z1_)
{
int newX0, newX1, newY0, newY1, newZ0, newZ1;
if ( util::intersect (
x0, x1, y0, y1, z0, z1,
x0_, x1_, y0_, y1_, z0_, z1_,
newX0, newX1, newY0, newY1, newZ0, newZ1 ) ) {
int iX;
#ifdef PARALLEL_MODE_OMP
#pragma omp parallel for
#endif
for (iX=newX0; iX<=newX1; ++iX) {
for (int iY=newY0; iY<=newY1; ++iY) {
for (int iZ=newZ0; iZ<=newZ1; ++iZ) {
ComputeWallFunction(blockLattice, iX,iY,iZ);
}
}
}
}
}
template
void WallFunctionBoundaryProcessor3D::getIndices(int index, int value , std::vector& indices)
{
for (int iVel=0; iVel(iVel,index)==value) {
indices.push_back(iVel);
}
}
}
template
void WallFunctionBoundaryProcessor3D::calculateWallDistances(IndicatorF3D* indicator)
{
T physR[3];
int iX = x0;
int iY = y1;
int iZ = z1;
T scaling = _converter.getConversionFactorLength() * 0.1;
_blockGeometryStructure.getPhysR(physR,iX, iY, iZ);
Vector origin(physR[0],physR[1],physR[2]);
Vector normal(0.,0.,0.);
T distance = 0.;
Vector direction(0.,0.,0.);
int smallestDistance_i = 0;
T smallestDistance = 0.;
bool firstHit = true;
origin[0] = physR[0];
origin[1] = physR[1];
origin[2] = physR[2];
int discreteDirection[6][3];
for (int i=0; i < 6; i++) {
for (int j=0; j < 3; j++) {
discreteDirection[i][j] = 0;
}
}
discreteDirection[0][0] = 1;
discreteDirection[1][0] = -1;
discreteDirection[2][1] = 1;
discreteDirection[3][1] = -1;
discreteDirection[4][2] = 1;
discreteDirection[5][2] = -1;
for (int i=0; i < 6; i++) {
direction[0] = discreteDirection[i][0] * scaling;
direction[1] = discreteDirection[i][1] * scaling;
direction[2] = discreteDirection[i][2] * scaling;
if (indicator->distance(distance, origin, direction)) {
if (firstHit) {
smallestDistance = distance;
smallestDistance_i = i;
firstHit = false;
} else {
if (distance < smallestDistance) {
smallestDistance = distance;
smallestDistance_i = i;
}
}
}
}
direction[0] = discreteDirection[smallestDistance_i][0] * scaling;
direction[1] = discreteDirection[smallestDistance_i][1] * scaling;
direction[2] = discreteDirection[smallestDistance_i][2] * scaling;
Vector direction2(direction);
Vector direction3(direction);
if (smallestDistance_i == 0 || smallestDistance_i == 1 ) {
direction2[1] = direction2[0] * scaling;
direction3[2] = direction3[0] * scaling;
} else if (smallestDistance_i == 2 || smallestDistance_i == 3 ) {
direction2[0] = direction2[1] * scaling;
direction3[2] = direction3[1] * scaling;
} else {
direction2[0] = direction2[2] * scaling;
direction3[1] = direction3[2] * scaling;
}
Vector directionN = direction*(1/const_cast&> (direction).norm());
Vector POS(origin + smallestDistance*directionN); //Point on Surface
indicator->distance(distance, origin, direction2);
Vector direction2N = direction2*(1/const_cast&> (direction2).norm());
Vector POS2(origin + distance*direction2N); //Point on Surface
indicator->distance(distance, origin, direction3);
Vector direction3N = direction3*(1/const_cast&> (direction3).norm());
Vector POS3(origin + distance*direction3N); //Point on Surface
Vector vec1 (POS - POS2);
Vector vec2 (POS - POS3);
normal[0] = -(vec1[1]*vec2[2] - vec1[2]*vec2[1]);
normal[1] = -(vec1[2]*vec2[0] - vec1[0]*vec2[2]);
normal[2] = -(vec1[0]*vec2[1] - vec1[1]*vec2[0]);
T normalMagnitude = sqrt(normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]);
normal[0] /= normalMagnitude;
normal[1] /= normalMagnitude;
normal[2] /= normalMagnitude;
unit_normal[0] = normal[0];
unit_normal[1] = normal[0];
unit_normal[2] = normal[0];
direction[0] = normal[0] * scaling;
direction[1] = normal[1] * scaling;
direction[2] = normal[2] * scaling;
indicator->distance(distance, origin, direction);
y_1 = distance / _converter.getConversionFactorLength();
}
template
void WallFunctionBoundaryProcessor3D::VelGradFromSecondOrderFD(bool NormalGradient, T Vel_BC[DESCRIPTOR::d], T Vel_1[DESCRIPTOR::d], T Vel_2[DESCRIPTOR::d], T VelGrad[DESCRIPTOR::d])
{
if (NormalGradient) {
if (orientation == 1) {
for (int Dim=0; Dim
void WallFunctionBoundaryProcessor3D::computeVelocityGradientTensor(T u_bc[DESCRIPTOR::d], T u_x1[DESCRIPTOR::d], T u_x2[DESCRIPTOR::d], T u_y1[DESCRIPTOR::d], T u_y2[DESCRIPTOR::d], T u_z1[DESCRIPTOR::d], T u_z2[DESCRIPTOR::d], T VelGrad[DESCRIPTOR::d][DESCRIPTOR::d])
{
T dx_u[DESCRIPTOR::d], dy_u[DESCRIPTOR::d], dz_u[DESCRIPTOR::d];
VelGradFromSecondOrderFD(direction == 0, u_bc, u_x1, u_x2, dx_u);
VelGradFromSecondOrderFD(direction == 1, u_bc, u_y1, u_y2, dy_u);
VelGradFromSecondOrderFD(direction == 2, u_bc, u_z1, u_z2, dz_u);
// du/dx du/dy du/dz
// dv/dx dv/dy dv/dz
// dw/dx dw/dy dw/dz
for (int Dim = 0; Dim < DESCRIPTOR::d; Dim++) {
VelGrad[Dim][0] = dx_u[Dim];
VelGrad[Dim][1] = dy_u[Dim];
VelGrad[Dim][2] = dz_u[Dim];
}
}
template
void WallFunctionBoundaryProcessor3D::computeNeighborsU(BlockLattice3D& blockLattice, int x, int y, int z,
T u_x1[DESCRIPTOR::d], T u_x2[DESCRIPTOR::d], T u_y1[DESCRIPTOR::d], T u_y2[DESCRIPTOR::d], T u_z1[DESCRIPTOR::d], T u_z2[DESCRIPTOR::d])
{
using namespace olb::util::tensorIndices3D;
// Computation of local external velocity around lattice node (x,y,z)
if (direction == 0) {
blockLattice.get(x - discreteNormalX, y, z).computeU(u_x1);
blockLattice.get(x - 2*discreteNormalX, y, z).computeU(u_x2);
} else {
ComputeUWall(blockLattice, x + discreteNormalY + discreteNormalZ, y, z, u_x1);
ComputeUWall(blockLattice, x - discreteNormalY - discreteNormalZ, y, z, u_x2);
}
if (direction == 1) {
blockLattice.get(x, y - discreteNormalY, z).computeU(u_y1);
blockLattice.get(x, y - 2*discreteNormalY, z).computeU(u_y2);
} else {
ComputeUWall(blockLattice, x, y + discreteNormalX + discreteNormalZ, z, u_y1);
ComputeUWall(blockLattice, x, y - discreteNormalX - discreteNormalZ, z, u_y2);
}
if (direction == 2) {
blockLattice.get(x, y, z - discreteNormalZ).computeU(u_z1);
blockLattice.get(x, y, z - 2*discreteNormalZ).computeU(u_z2);
} else {
ComputeUWall(blockLattice, x, y, z + discreteNormalX + discreteNormalY, u_z1);
ComputeUWall(blockLattice, x, y, z - discreteNormalX - discreteNormalY, u_z2);
}
}
template
void WallFunctionBoundaryProcessor3D::computeVelocityGradient(BlockLattice3D& blockLattice, int x, int y, int z, T u_bc[DESCRIPTOR::d], T VelGrad[DESCRIPTOR::d][DESCRIPTOR::d])
{
// Computation of neighbor velocity around lattice node (x,y,z)
T u_x1[3], u_x2[3], u_y1[3], u_y2[3], u_z1[3], u_z2[3];
computeNeighborsU(blockLattice, x, y, z, u_x1, u_x2, u_y1, u_y2, u_z1, u_z2);
// Computation of Velocity Gradient Tensor
computeVelocityGradientTensor(u_bc, u_x1, u_x2, u_y1, u_y2, u_z1, u_z2, VelGrad);
}
template
void WallFunctionBoundaryProcessor3D::computeNeighborsRho(BlockLattice3D& blockLattice, int x, int y, int z,
T u_x1[DESCRIPTOR::d], T u_x2[DESCRIPTOR::d], T u_y1[DESCRIPTOR::d], T u_y2[DESCRIPTOR::d], T u_z1[DESCRIPTOR::d], T u_z2[DESCRIPTOR::d],
T& rho_x1, T& rho_x2, T& rho_y1, T& rho_y2, T& rho_z1, T& rho_z2)
{
using namespace olb::util::tensorIndices3D;
// Computation of local external velocity around lattice node (x,y,z)
if (direction == 0) {
rho_x1 = blockLattice.get(x - discreteNormalX, y, z).computeRho();
rho_x2 = blockLattice.get(x - 2*discreteNormalX, y, z).computeRho();
} else {
ComputeRhoWall(blockLattice, blockLattice.get(x + discreteNormalY + discreteNormalZ, y, z), x + discreteNormalY + discreteNormalZ, y, z, u_x1, rho_x1);
ComputeRhoWall(blockLattice, blockLattice.get(x - discreteNormalY - discreteNormalZ, y, z), x - discreteNormalY - discreteNormalZ, y, z, u_x2, rho_x2);
}
if (direction == 1) {
rho_y1 = blockLattice.get(x, y - discreteNormalY, z).computeRho();
rho_y2 = blockLattice.get(x, y - 2*discreteNormalY, z).computeRho();
} else {
ComputeRhoWall(blockLattice, blockLattice.get(x, y - discreteNormalX - discreteNormalZ, z), x, y - discreteNormalX - discreteNormalZ, z, u_y1, rho_y1);
ComputeRhoWall(blockLattice, blockLattice.get(x, y - discreteNormalX - discreteNormalZ, z), x, y - discreteNormalX - discreteNormalZ, z, u_y2, rho_y2);
}
if (direction == 2) {
rho_z1 = blockLattice.get(x, y, z - discreteNormalZ).computeRho();
rho_z2 = blockLattice.get(x, y, z - 2*discreteNormalZ).computeRho();
} else {
ComputeRhoWall(blockLattice, blockLattice.get(x, y, z + discreteNormalX + discreteNormalY), x, y, z + discreteNormalX + discreteNormalY, u_z1, rho_z1);
ComputeRhoWall(blockLattice, blockLattice.get(x, y, z - discreteNormalX - discreteNormalY), x, y, z - discreteNormalX - discreteNormalY, u_z2, rho_z2);
}
}
template
void WallFunctionBoundaryProcessor3D::computeNeighborsRhoU(BlockLattice3D& blockLattice, int x, int y, int z,
T u_x1[DESCRIPTOR::d], T u_x2[DESCRIPTOR::d], T u_y1[DESCRIPTOR::d], T u_y2[DESCRIPTOR::d], T u_z1[DESCRIPTOR::d], T u_z2[DESCRIPTOR::d],
T& rho_x1, T& rho_x2, T& rho_y1, T& rho_y2, T& rho_z1, T& rho_z2)
{
// Computation of neighbor velocity around lattice node (x,y,z)
computeNeighborsU(blockLattice, x, y, z, u_x1, u_x2, u_y1, u_y2, u_z1, u_z2);
// Computation of neighbor density around lattice node (x,y,z)
computeNeighborsRho(blockLattice, x, y, z, u_x1, u_x2, u_y1, u_y2, u_z1, u_z2, rho_x1, rho_x2, rho_y1, rho_y2, rho_z1, rho_z2);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template
void WallFunctionBoundaryProcessor3D::ComputeUWall(BlockLattice3D& blockLattice, int x, int y, int z, T u[DESCRIPTOR::d])
{
/// === Computation of velocity with Musker Profile - Malaspinas and Sagaut (2014) === ///
using namespace olb::util::tensorIndices3D;
Cell& cell = blockLattice.get(x,y,z);
/// === STEP 2 : compute u_2 and rho_2 (neighbor node) -> [m/s] - [m] DESCRIPTOR units
T u_2[DESCRIPTOR::d]; // Velocity to the neighbord lattice in the normal inward direction
T rho_2; // Density in the neighbor lattice in the inwards direction
blockLattice.get(x - discreteNormalX, y - discreteNormalY, z - discreteNormalZ).computeRhoU(rho_2,u_2);
/// === STEP 3 : get local basis vector and neighbor wall parallel velocity
T u_2_dot_unit_normal = u_2[0] * unit_normal[0] +
u_2[1] * unit_normal[1] +
u_2[2] * unit_normal[2]; //scalar Product of the u2 and uniy normal
T u_2_parallel[3];
u_2_parallel[0] = u_2[0] - (u_2_dot_unit_normal * unit_normal[0]);
u_2_parallel[1] = u_2[1] - (u_2_dot_unit_normal * unit_normal[1]);
u_2_parallel[2] = u_2[2] - (u_2_dot_unit_normal * unit_normal[2]);
T u_2_parallel_norm = sqrt(u_2_parallel[0] * u_2_parallel[0] +
u_2_parallel[1] * u_2_parallel[1] +
u_2_parallel[2] * u_2_parallel[2]); // l2 norm : magnitude of the vector
T e_x_loc[3] = {0., 0., 0.}; // Streamwise direction
if (u_2_parallel_norm != 0) {
T inv_u_2_parallel_norm = (1. /u_2_parallel_norm);
e_x_loc[0] = u_2_parallel[0] * inv_u_2_parallel_norm;
e_x_loc[1] = u_2_parallel[1] * inv_u_2_parallel_norm;
e_x_loc[2] = u_2_parallel[2] * inv_u_2_parallel_norm;
}
T u2 = e_x_loc[0] * u_2[0] +
e_x_loc[1] * u_2[1] +
e_x_loc[2] * u_2[2]; //scalar Product of the basis vector e_x_loc and u_2
// STEP 5 : u1 can only be 0 if u2==0 for musker profile
//T* tau_w = new T [1];
T tau_w[1] = {0.};
T u1[1];
if (u2 != 0) {
T rho_phy = _converter.getPhysDensity(); // [kg/m³] SI units
T rho_lat = _converter.getLatticeDensity(rho_phy); // [kg/m³] DESCRIPTOR units
T Mol_Lat_Nu = _converter.getLatticeViscosity(); // [m²/s] DESCRIPTOR units
if (_wallFunctionParam.wallProfile == 0) {
// Musker profile for boundary node using the molecular viscosity and the characteristic density in lattice units
Musker musker_u2_Lat_MolVis(Mol_Lat_Nu, y_2, rho_lat);
util::Newton1D newton(musker_u2_Lat_MolVis,u2);
T tau_w_guess = *(cell.template getFieldPointer());
tau_w[0] = newton.solve(tau_w_guess,false); // Computation of local wall shear stress
if (std::isnan(tau_w[0])) {
//OstreamManager clout(std::cout, "First NAN");
//clout << "Position = [" << x << ", " << y << "," << z << "]" << std::endl;
//clout << "Tau guess = " << tau_w_guess << std::endl;
tau_w[0] = newton.solve(0.,false);
}
if (std::isnan(tau_w[0])) {
OstreamManager clout(std::cout, "Second NAN");
clout << "Position = [" << x << ", " << y << "," << z << "]" << std::endl;
clout << "Tau guess = " << tau_w_guess << std::endl;
tau_w[0] = 0.;
}
// compute u1 - boundary node
Musker musker_u1_Lat_MolVis(Mol_Lat_Nu, y_1, rho_lat);
musker_u1_Lat_MolVis(u1,tau_w);
} else {
// Werner and Wengle profile for boundary node using the molecular viscosity and the characteristic density in lattice units
PowerLawProfile PLP (Mol_Lat_Nu, u2, y_2, y_1, rho_lat);
T input [1];
T output[2];
PLP(output,input);
tau_w[0] = output[0];
u1[0] = output[1];
}
} else {
u1[0] = 0.;
}
// save tau_w for next step
cell.template defineField(&(tau_w[0]));
// STEP 6 : compute velocity vector at the boundary
u[0] = e_x_loc[0] * u1[0];
u[1] = e_x_loc[1] * u1[0];
u[2] = e_x_loc[2] * u1[0];
if (_wallFunctionParam.bodyForce) {
for (int iDim=0; iDim()[iDim]/2.;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
template
void WallFunctionBoundaryProcessor3D::computeVanDriestTauEff(T y_bc, T tau_w, T u_bc, T u_1, T u_2, T& tau_eff)
{
T rho_phy = _converter.getPhysDensity(); // [kg/m³] SI units
T rho = _converter.getLatticeDensity(rho_phy); // [kg/m³] DESCRIPTOR units
T nu_mol = _converter.getLatticeViscosity(); // [m²/s] DESCRIPTOR units
T y_plus = y_bc*sqrt(tau_w/rho)/nu_mol;
T uxdz_abs = std::abs(fd::boundaryGradient(u_bc, u_1, u_2));
T vanDriest = 1 - std::exp(-y_plus / 26.);
T nu_turb = pow(_wallFunctionParam.vonKarman * y_bc * vanDriest, 2.) * uxdz_abs;
T nu_eff = nu_turb + nu_mol;
tau_eff = (3. * nu_eff) + 0.5; // Rectangular integration rule - Collision
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
template
void WallFunctionBoundaryProcessor3D::ComputeTauEff(BlockLattice3D& blockLattice, Cell& cell, int x, int y, int z, T u_bc[DESCRIPTOR::d])
{
T u_z[3];
T u_z2[3];
blockLattice.get(x - discreteNormalX, y - discreteNormalY, z - discreteNormalZ).computeU(u_z);
blockLattice.get(x - 2*discreteNormalX, y - 2*discreteNormalY, z - 2*discreteNormalZ).computeU(u_z2);
T y_bc = 0.5; // Default half-way Bounce-Back distance - [m] DESCRIPTOR units
if (_wallFunctionParam.latticeWalldistance > 0.) {
y_bc = _wallFunctionParam.latticeWalldistance; // [m] DESCRIPTOR units
}
T normal_norm = sqrt(discreteNormalX * discreteNormalX +
discreteNormalY * discreteNormalY +
discreteNormalZ * discreteNormalZ); // l2 norm : magnitude of the vector
y_bc *= normal_norm;
T tau_w = cell.template getFieldPointer()[0];
T tau_eff;
if (_wallFunctionParam.curved == true || orientation == 0) {
T inv_normal_norm = 1. / normal_norm;
T normal[3];
normal[0] = discreteNormalX * (inv_normal_norm);
normal[1] = discreteNormalY * (inv_normal_norm);
normal[2] = discreteNormalZ * (inv_normal_norm);
T u_2_parallel[3];
T sP_normal_u2 = normal[0] * u_z[0] +
normal[1] * u_z[1] +
normal[2] * u_z[2]; //scalar Product of the normal and u2
u_2_parallel[0] = u_z[0] - sP_normal_u2 * normal[0];
u_2_parallel[1] = u_z[1] - sP_normal_u2 * normal[1];
u_2_parallel[2] = u_z[2] - sP_normal_u2 * normal[2];
T u_2_parallel_norm = sqrt(u_2_parallel[0] * u_2_parallel[0] +
u_2_parallel[1] * u_2_parallel[1] +
u_2_parallel[2] * u_2_parallel[2]); // l2 norm : magnitude of the vector
T e_x_loc[3];
T inv_u_2_parallel_norm = (1. /u_2_parallel_norm );
if (u_2_parallel_norm != 0) {
e_x_loc[0] = u_2_parallel[0] * inv_u_2_parallel_norm;
e_x_loc[1] = u_2_parallel[1] * inv_u_2_parallel_norm;
e_x_loc[2] = u_2_parallel[2] * inv_u_2_parallel_norm;
} else {
e_x_loc[0] = T(-discreteNormalX);
e_x_loc[1] = T(-discreteNormalY);
e_x_loc[2] = T(-discreteNormalZ);
}
T u1;
T u2;
T u3;
u1 = e_x_loc[0] * u_bc[0] +
e_x_loc[1] * u_bc[1] +
e_x_loc[2] * u_bc[2];
u2 = e_x_loc[0] * u_z[0] +
e_x_loc[1] * u_z[1] +
e_x_loc[2] * u_z[2];
u3 = e_x_loc[0] * u_z2[0] +
e_x_loc[1] * u_z2[1] +
e_x_loc[2] * u_z2[2];
computeVanDriestTauEff(y_bc, tau_w, u1, u2, u3, tau_eff);
} else {
computeVanDriestTauEff(y_bc, tau_w, u_bc[direction], u_z[direction], u_z2[direction], tau_eff);
}
cell.template defineField(&(tau_eff));
}
template
void WallFunctionBoundaryProcessor3D::ComputeRhoWall(BlockLattice3D& blockLattice, Cell& cell, int x, int y, int z, T u_bc[DESCRIPTOR::d], T& rho_bc)
{
/// === Computation of density - Finite Difference Scheme === ///
if (_wallFunctionParam.rhoMethod == 0) {
/// === Computation of density - Zou and He (1997) === ///
// The code have been copied from VelocityBM -> compute rho method. This implementation it is only valid for straight boundaries
T u_bc_Perpendicular = 0.;
if (_wallFunctionParam.bodyForce) {
T u_bc_tmp[DESCRIPTOR::d];
for (int iDim = 0; iDim()[iDim] / 2.;
}
for (int iDim = 0; iDim
void WallFunctionBoundaryProcessor3D::computeRFneqfromFneq(T fneq_bc[DESCRIPTOR::q])
{
T sum_cell_fneq = 0.;
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
sum_cell_fneq += fneq_bc[iPop];
}
T pi_bc[util::TensorVal< DESCRIPTOR >::n];
int iPi = 0;
for (int iAlpha=0; iAlpha < DESCRIPTOR::d; ++iAlpha) {
for (int iBeta=iAlpha; iBeta < DESCRIPTOR::d; ++iBeta) {
pi_bc[iPi] = T();
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
pi_bc[iPi] += descriptors::c(iPop)[iAlpha]*descriptors::c(iPop)[iBeta]*fneq_bc[iPop];
}
if (iAlpha==iBeta) {
pi_bc[iPi] -= (1./descriptors::invCs2())*sum_cell_fneq;
}
++iPi;
}
}
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
fneq_bc[iPop] = firstOrderLbHelpers::fromPiToFneq(iPop, pi_bc);
}
}
template
void WallFunctionBoundaryProcessor3D::computeFneqRNEBB(Cell& cell, T u_bc[DESCRIPTOR::d], T rho_bc, T fneq_bc[DESCRIPTOR::q])
{
Dynamics* dynamics = cell.getDynamics();
T uSqr_bc = util::normSqr(u_bc);
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
fneq_bc[iPop] = cell[iPop] - dynamics -> computeEquilibrium(iPop,rho_bc,u_bc,uSqr_bc);
}
for (unsigned fIndex=0; fIndex
void WallFunctionBoundaryProcessor3D::computeFneqENeq(BlockLattice3D& blockLattice, Cell& cell, int x, int y, int z, T u_bc[DESCRIPTOR::d], T rho_bc, T fneq_bc[DESCRIPTOR::q])
{
Cell& cell_fluid = blockLattice.get(x - discreteNormalX, y - discreteNormalY, z - discreteNormalZ);
Dynamics* dynamics_fluid = cell_fluid.getDynamics();
T rho_fluid, u_fluid[DESCRIPTOR::d];
cell_fluid.computeRhoU(rho_fluid,u_fluid);
T uSqr_fluid = util::normSqr(u_fluid);
for (int iPop = 0; iPop < DESCRIPTOR::q; ++iPop) {
fneq_bc[iPop] = cell_fluid[iPop] - dynamics_fluid -> computeEquilibrium(iPop,rho_fluid,u_fluid,uSqr_fluid);
}
}
template
void WallFunctionBoundaryProcessor3D::computeFneqRSOFD(BlockLattice3D& blockLattice, Cell& cell, int x, int y, int z, T u_bc[DESCRIPTOR::d], T rho_bc, T fneq_bc[DESCRIPTOR::q])
{
T pi_bc[util::TensorVal< DESCRIPTOR >::n];
T Velocity_Grad[DESCRIPTOR::d][DESCRIPTOR::d];
computeVelocityGradient(blockLattice, x, y, z, u_bc, Velocity_Grad);
// Creation of strain Rate
T tau_eff = cell.template getFieldPointer()[0];
T Factor = -1.*tau_eff*rho_bc/descriptors::invCs2();
int iPi = 0;
for (int Alpha=0; Alpha()[iDim];
}
iPi = 0;
for (int Alpha=0; Alpha::fromPiToFneq(iPop, pi_bc);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template
void WallFunctionBoundaryProcessor3D::ComputeFneqWall(BlockLattice3D& blockLattice, Cell& cell, int x, int y, int z, T u_bc[DESCRIPTOR::d], T rho_bc, T fneq_bc[DESCRIPTOR::q])
{
//regularized NEBB (Latt)
if (_wallFunctionParam.fneqMethod == 0) {
computeFneqRNEBB(cell, u_bc, rho_bc, fneq_bc);
//extrapolation NEQ (Guo Zhaoli)
} else if (_wallFunctionParam.fneqMethod == 1) {
computeFneqENeq(blockLattice, cell, x, y, z, u_bc, rho_bc, fneq_bc);
//Regularized second order finite Differnce
} else if (_wallFunctionParam.fneqMethod == 2) {
computeFneqRSOFD(blockLattice, cell, x, y, z, u_bc, rho_bc, fneq_bc);
//equilibrium scheme
} else if (_wallFunctionParam.fneqMethod == 3) {
for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
fneq_bc[iPop] = 0.;
}
}
}
template
void WallFunctionBoundaryProcessor3D::ComputeWallFunction(BlockLattice3D& blockLattice, int x, int y, int z)
{
Cell& cell_bc = blockLattice.get(x,y,z);
T rho_bc = 0.;
T u_bc[DESCRIPTOR::d];
T fneq_bc[DESCRIPTOR::q];
// Computation of boundary velocity from the wall function
ComputeUWall(blockLattice, x, y, z, u_bc);
if (_wallFunctionParam.useVanDriest) {
// Computation of effective relaxation time from the vanDriest damping function
ComputeTauEff(blockLattice, cell_bc, x, y, z, u_bc);
}
// Computation of density at the boundary node
ComputeRhoWall(blockLattice, cell_bc, x, y, z, u_bc, rho_bc);
// Computation of the second-order moment of non-equilibrium distribution function
ComputeFneqWall(blockLattice, cell_bc, x, y, z, u_bc, rho_bc, fneq_bc);
// Computation of the particle distribution functions according to the regularized formula
Dynamics* dynamics_bc = cell_bc.getDynamics();
T uSqr_bc = util::normSqr(u_bc);
for (int iPop = 0; iPop < DESCRIPTOR::q; ++iPop) {
cell_bc[iPop] = dynamics_bc -> computeEquilibrium(iPop,rho_bc,u_bc,uSqr_bc) + fneq_bc[iPop];
if (std::isnan(cell_bc[iPop])) {
OstreamManager clout(std::cout, "Slip Musker Profile");
clout << "Musker Profile Computation" << std::endl;
clout << "Position = [" << x << ", " << y << "," << z << "]" << std::endl;
clout << "Normal outwards = [" << discreteNormalX << ", " << discreteNormalY << "," << discreteNormalZ << "]" << std::endl;
clout << "Velocity at boundary u_bc = [" << u_bc[0] << ", " << u_bc[1] << "," << u_bc[2] << "]" << std::endl;
clout << "Density at boundary rho_bc = " << rho_bc << std::endl;
exit(1);
}
}
}
template
void WallFunctionBoundaryProcessor3D::process(BlockLattice3D& blockLattice)
{
processSubDomain(blockLattice, x0, x1, y0, y1, z0, z1);
}
//////// WallFunctionBoundaryProcessorGenerator3D ////////////////////////////////
template
WallFunctionBoundaryProcessorGenerator3D::WallFunctionBoundaryProcessorGenerator3D(int x0, int x1, int y0, int y1, int z0, int z1, BlockGeometryStructure3D& blockGeometryStructure,
std::vector discreteNormal, std::vector missingIndices,
UnitConverter const& converter, wallFunctionParam const& wallFunctionParam,
IndicatorF3D* geoIndicator)
: PostProcessorGenerator3D(x0, x1, y0, y1, z0, z1),
_blockGeometryStructure(blockGeometryStructure),
_discreteNormal(discreteNormal), _missingIndices(missingIndices),
_converter(converter), _wallFunctionParam(wallFunctionParam), _geoIndicator(geoIndicator)
{ }
template
PostProcessor3D*
WallFunctionBoundaryProcessorGenerator3D::generate() const
{
return new WallFunctionBoundaryProcessor3D( this->x0, this->x1, this->y0, this->y1, this->z0, this->z1,
_blockGeometryStructure, _discreteNormal, _missingIndices,
_converter, _wallFunctionParam, _geoIndicator);
}
template
PostProcessorGenerator3D*
WallFunctionBoundaryProcessorGenerator3D::clone() const
{
return new WallFunctionBoundaryProcessorGenerator3D (this->x0, this->x1, this->y0, this->y1, this->z0, this->z1,
_blockGeometryStructure, _discreteNormal, _missingIndices,
_converter, _wallFunctionParam, _geoIndicator);
}
} // namespace olb
#endif