summaryrefslogtreecommitdiff
path: root/src/boundary/wallFunctionBoundaryPostProcessors3D.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/boundary/wallFunctionBoundaryPostProcessors3D.hh')
-rw-r--r--src/boundary/wallFunctionBoundaryPostProcessors3D.hh876
1 files changed, 876 insertions, 0 deletions
diff --git a/src/boundary/wallFunctionBoundaryPostProcessors3D.hh b/src/boundary/wallFunctionBoundaryPostProcessors3D.hh
new file mode 100644
index 0000000..0e0ebef
--- /dev/null
+++ b/src/boundary/wallFunctionBoundaryPostProcessors3D.hh
@@ -0,0 +1,876 @@
+/* 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
+ * <http://www.openlb.net/>
+ *
+ * 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 <typename T, typename S>
+Musker<T,S>::Musker(T nu, T y, T rho) : AnalyticalF1D<T,S>(1), _nu(nu), _y(y),_rho(rho)
+{
+ this->getName() = "Musker";
+}
+
+template <typename T, typename S>
+bool Musker<T,S>::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 <typename T, typename S>
+PowerLawProfile<T,S>::PowerLawProfile(T nu, T u2, T y2, T y1, T rho) : AnalyticalF1D<T,S>(2), _nu(nu), _u2(u2), _y2(y2), _y1(y1), _rho(rho)
+{
+ this->getName() = "PowerLawProfile";
+}
+
+template <typename T, typename S>
+bool PowerLawProfile<T,S>::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<typename T, typename DESCRIPTOR>
+WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::WallFunctionBoundaryProcessor3D(int x0_, int x1_, int y0_, int y1_, int z0_, int z1_,
+ BlockGeometryStructure3D<T>& blockGeometryStructure,
+ std::vector<int> discreteNormal, std::vector<int> missingIndices,
+ UnitConverter<T, DESCRIPTOR> const& converter, wallFunctionParam<T> const& wallFunctionParam,
+ IndicatorF3D<T>* 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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::processSubDomain(BlockLattice3D<T,DESCRIPTOR>& 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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::getIndices(int index, int value , std::vector<int>& indices)
+{
+ for (int iVel=0; iVel<DESCRIPTOR::q; ++iVel) {
+ if (descriptors::c<DESCRIPTOR>(iVel,index)==value) {
+ indices.push_back(iVel);
+ }
+ }
+}
+
+template<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::calculateWallDistances(IndicatorF3D<T>* 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<T,3> origin(physR[0],physR[1],physR[2]);
+ Vector<T,3> normal(0.,0.,0.);
+ T distance = 0.;
+ Vector<T,3> 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<T,3> direction2(direction);
+ Vector<T,3> 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<S,3> directionN = direction*(1/const_cast<Vector<S,3>&> (direction).norm());
+ Vector<S,3> POS(origin + smallestDistance*directionN); //Point on Surface
+
+ indicator->distance(distance, origin, direction2);
+ Vector<S,3> direction2N = direction2*(1/const_cast<Vector<S,3>&> (direction2).norm());
+ Vector<S,3> POS2(origin + distance*direction2N); //Point on Surface
+
+ indicator->distance(distance, origin, direction3);
+
+ Vector<S,3> direction3N = direction3*(1/const_cast<Vector<S,3>&> (direction3).norm());
+ Vector<S,3> POS3(origin + distance*direction3N); //Point on Surface
+
+ Vector<S,3> vec1 (POS - POS2);
+ Vector<S,3> 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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::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<DESCRIPTOR::d; Dim++) {
+ VelGrad[Dim] = fd::BSGradient(Vel_BC[Dim], Vel_1[Dim], Vel_2[Dim]); // Backward second-order velocity gradient
+ }
+ } else { // orientation == -1
+ for (int Dim=0; Dim<DESCRIPTOR::d; Dim++) {
+ VelGrad[Dim] = fd::FSGradient(Vel_BC[Dim], Vel_1[Dim], Vel_2[Dim]); // Forward second-order velocity gradient
+ }
+ }
+ } else {
+ if (orientation == 1) {
+ for (int Dim=0; Dim<DESCRIPTOR::d; Dim++) {
+ VelGrad[Dim] = fd::centralGradient(Vel_1[Dim], Vel_2[Dim]); // central second-order velocity gradient: centralGradient( y(x+1), y(x-1) )
+ }
+ } else { // orientation == -1
+ for (int Dim=0; Dim<DESCRIPTOR::d; Dim++) {
+ VelGrad[Dim] = fd::centralGradient(Vel_2[Dim], Vel_1[Dim]); // central second-order velocity gradient: centralGradient( y(x+1), y(x-1) )
+ }
+ }
+ }
+}
+
+template<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::computeNeighborsU(BlockLattice3D<T,DESCRIPTOR>& 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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::computeVelocityGradient(BlockLattice3D<T,DESCRIPTOR>& 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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::computeNeighborsRho(BlockLattice3D<T,DESCRIPTOR>& 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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::computeNeighborsRhoU(BlockLattice3D<T,DESCRIPTOR>& 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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::ComputeUWall(BlockLattice3D<T,DESCRIPTOR>& 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<T,DESCRIPTOR>& 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<T,T> musker_u2_Lat_MolVis(Mol_Lat_Nu, y_2, rho_lat);
+ util::Newton1D<T> newton(musker_u2_Lat_MolVis,u2);
+ T tau_w_guess = *(cell.template getFieldPointer<descriptors::TAU_W>());
+ 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<T,T> 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<T,T> 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<descriptors::TAU_W>(&(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<DESCRIPTOR::d; ++iDim) {
+ u[iDim] -= cell.template getFieldPointer<descriptors::FORCE>()[iDim]/2.;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+template<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::ComputeTauEff(BlockLattice3D<T,DESCRIPTOR>& blockLattice, Cell<T,DESCRIPTOR>& 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<descriptors::TAU_W>()[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<descriptors::TAU_EFF>(&(tau_eff));
+
+}
+
+template<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::ComputeRhoWall(BlockLattice3D<T,DESCRIPTOR>& blockLattice, Cell<T,DESCRIPTOR>& 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<DESCRIPTOR::d; ++iDim){
+ u_bc_tmp[iDim] = u_bc[iDim] - cell.template getFieldPointer<descriptors::FORCE>()[iDim] / 2.;
+ }
+ for (int iDim = 0; iDim<DESCRIPTOR::d; ++iDim){
+ u_bc_Perpendicular += u_bc_tmp[iDim]*unit_normal[iDim];
+ }
+ } else {
+ for (int iDim = 0; iDim<DESCRIPTOR::d; ++iDim){
+ u_bc_Perpendicular += u_bc[iDim]*unit_normal[iDim];
+ }
+ }
+
+ T rhoOnWall = T();
+ for (unsigned fIndex=0; fIndex<onWallIndices.size(); ++fIndex) {
+ rhoOnWall += cell[onWallIndices[fIndex]];
+ }
+
+ T rhoNormal = T();
+ for (unsigned fIndex=0; fIndex<normalIndices.size(); ++fIndex) {
+ rhoNormal += cell[normalIndices[fIndex]];
+ }
+
+ rho_bc = ((T)2*rhoNormal+rhoOnWall+(T)1) /
+ ((T)1+u_bc_Perpendicular);
+
+ } else if (_wallFunctionParam.rhoMethod == 1) { // Extrapolation Fluid
+
+ rho_bc = blockLattice.get(x - discreteNormalX, y - discreteNormalY, z - discreteNormalZ).computeRho();
+
+ } else if (_wallFunctionParam.rhoMethod == 2) { // constant rho
+ rho_bc = 1.;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+template<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::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<DESCRIPTOR>(iPop)[iAlpha]*descriptors::c<DESCRIPTOR>(iPop)[iBeta]*fneq_bc[iPop];
+ }
+ if (iAlpha==iBeta) {
+ pi_bc[iPi] -= (1./descriptors::invCs2<T,DESCRIPTOR>())*sum_cell_fneq;
+ }
+ ++iPi;
+ }
+ }
+
+ for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
+ fneq_bc[iPop] = firstOrderLbHelpers<T,DESCRIPTOR>::fromPiToFneq(iPop, pi_bc);
+ }
+}
+
+template<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::computeFneqRNEBB(Cell<T,DESCRIPTOR>& cell, T u_bc[DESCRIPTOR::d], T rho_bc, T fneq_bc[DESCRIPTOR::q])
+{
+ Dynamics<T,DESCRIPTOR>* dynamics = cell.getDynamics();
+ T uSqr_bc = util::normSqr<T,DESCRIPTOR::d>(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<normalInwardsIndices.size(); ++fIndex) {
+ fneq_bc[normalInwardsIndices[fIndex]] = fneq_bc[DESCRIPTOR::opposite[normalInwardsIndices[fIndex]]];
+ }
+
+ computeRFneqfromFneq(fneq_bc);
+}
+
+template<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::computeFneqENeq(BlockLattice3D<T,DESCRIPTOR>& blockLattice, Cell<T,DESCRIPTOR>& cell, int x, int y, int z, T u_bc[DESCRIPTOR::d], T rho_bc, T fneq_bc[DESCRIPTOR::q])
+{
+ Cell<T,DESCRIPTOR>& cell_fluid = blockLattice.get(x - discreteNormalX, y - discreteNormalY, z - discreteNormalZ);
+ Dynamics<T,DESCRIPTOR>* dynamics_fluid = cell_fluid.getDynamics();
+ T rho_fluid, u_fluid[DESCRIPTOR::d];
+ cell_fluid.computeRhoU(rho_fluid,u_fluid);
+ T uSqr_fluid = util::normSqr<T,DESCRIPTOR::d>(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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::computeFneqRSOFD(BlockLattice3D<T,DESCRIPTOR>& blockLattice, Cell<T,DESCRIPTOR>& 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<descriptors::TAU_EFF>()[0];
+ T Factor = -1.*tau_eff*rho_bc/descriptors::invCs2<T,DESCRIPTOR>();
+ int iPi = 0;
+ for (int Alpha=0; Alpha<DESCRIPTOR::d; ++Alpha) {
+ for (int Beta=Alpha; Beta<DESCRIPTOR::d; ++Beta) {
+ pi_bc[iPi] = Factor*(Velocity_Grad[Alpha][Beta] + Velocity_Grad[Beta][Alpha]);
+ ++iPi;
+ }
+ }
+
+ if (_wallFunctionParam.bodyForce) {
+ // Force tensor
+ //Creation of body force tensor (rho/2.)*(G_alpha*U_beta + U_alpha*G_Beta)
+ T F[DESCRIPTOR::d];
+ for (int iDim = 0; iDim < DESCRIPTOR::d; ++iDim) {
+ F[iDim] = cell.template getFieldPointer<descriptors::FORCE>()[iDim];
+ }
+
+ iPi = 0;
+ for (int Alpha=0; Alpha<DESCRIPTOR::d; ++Alpha) {
+ for (int Beta=Alpha; Beta<DESCRIPTOR::d; ++Beta) {
+ pi_bc[iPi] += -1.*(rho_bc/2.)*(F[Alpha]*u_bc[Beta] + u_bc[Alpha]*F[Beta]);
+ ++iPi;
+ }
+ }
+ }
+
+ for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
+ fneq_bc[iPop] = firstOrderLbHelpers<T,DESCRIPTOR>::fromPiToFneq(iPop, pi_bc);
+ }
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::ComputeFneqWall(BlockLattice3D<T,DESCRIPTOR>& blockLattice, Cell<T,DESCRIPTOR>& 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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::ComputeWallFunction(BlockLattice3D<T,DESCRIPTOR>& blockLattice, int x, int y, int z)
+{
+ Cell<T,DESCRIPTOR>& 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<T,DESCRIPTOR>* dynamics_bc = cell_bc.getDynamics();
+ T uSqr_bc = util::normSqr<T,DESCRIPTOR::d>(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<typename T, typename DESCRIPTOR>
+void WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>::process(BlockLattice3D<T,DESCRIPTOR>& blockLattice)
+{
+ processSubDomain(blockLattice, x0, x1, y0, y1, z0, z1);
+}
+
+//////// WallFunctionBoundaryProcessorGenerator3D ////////////////////////////////
+
+template<typename T, typename DESCRIPTOR>
+WallFunctionBoundaryProcessorGenerator3D<T,DESCRIPTOR>::WallFunctionBoundaryProcessorGenerator3D(int x0, int x1, int y0, int y1, int z0, int z1, BlockGeometryStructure3D<T>& blockGeometryStructure,
+ std::vector<int> discreteNormal, std::vector<int> missingIndices,
+ UnitConverter<T, DESCRIPTOR> const& converter, wallFunctionParam<T> const& wallFunctionParam,
+ IndicatorF3D<T>* geoIndicator)
+ : PostProcessorGenerator3D<T,DESCRIPTOR>(x0, x1, y0, y1, z0, z1),
+ _blockGeometryStructure(blockGeometryStructure),
+ _discreteNormal(discreteNormal), _missingIndices(missingIndices),
+ _converter(converter), _wallFunctionParam(wallFunctionParam), _geoIndicator(geoIndicator)
+{ }
+
+template<typename T, typename DESCRIPTOR>
+PostProcessor3D<T,DESCRIPTOR>*
+WallFunctionBoundaryProcessorGenerator3D<T,DESCRIPTOR>::generate() const
+{
+ return new WallFunctionBoundaryProcessor3D<T,DESCRIPTOR>( this->x0, this->x1, this->y0, this->y1, this->z0, this->z1,
+ _blockGeometryStructure, _discreteNormal, _missingIndices,
+ _converter, _wallFunctionParam, _geoIndicator);
+}
+
+template<typename T, typename DESCRIPTOR>
+PostProcessorGenerator3D<T,DESCRIPTOR>*
+WallFunctionBoundaryProcessorGenerator3D<T,DESCRIPTOR>::clone() const
+{
+ return new WallFunctionBoundaryProcessorGenerator3D<T,DESCRIPTOR> (this->x0, this->x1, this->y0, this->y1, this->z0, this->z1,
+ _blockGeometryStructure, _discreteNormal, _missingIndices,
+ _converter, _wallFunctionParam, _geoIndicator);
+}
+
+} // namespace olb
+
+#endif