WallFunctionConnectivity.f90 Source File


Source Code

!
!//////////////////////////////////////////////////////
!
! This module stores the connection of each face of the wall that will be used in the 
! Wall Function with the first normal neighbour
! element and other needed variables
!
!//////////////////////////////////////////////////////
!
#include "Includes.h"
#if defined(NAVIERSTOKES)
Module WallFunctionConnectivity  !

    use SMConstants
    use HexMeshClass
    Implicit None
!   
!  *****************************
!  Default everything to private
!  *****************************
!
    private
!
!  ****************
!  Public variables
!  ****************
!
    public useWallFunc, wallFunBCs
!
!  ******************
!  Public definitions
!  ******************
!
    public Initialize_WallConnection, WallFunctionGatherFlowVariables, WallUpdateMeanV, WallStartMeanV
!

    logical                                                          :: useWallFunc
    character(len=BC_STRING_LENGTH), dimension(:), allocatable       :: wallFunBCs
    integer, dimension(:), allocatable                               :: wallElemIds, wallNormalDirection, wallNormalIndex, wallFaceID
    real(kind=RP), dimension(:,:,:,:), allocatable                   :: meanVelocity
    real(kind=RP)                                                    :: timeCont

    contains 
!   
!------------------------------------------------------------------------------------------------------------------------
!
    Subroutine Initialize_WallConnection(controlVariables, mesh)
!     *******************************************************************
!        This subroutine initializes and store the arrays index that are
!        used to represent the connection of the face to the neighbour element.
!        Also calls the definitions of the wall function to update the 
!        parameter of the models based on the controlVariables.
!     *******************************************************************
!
        use FTValueDictionaryClass
        use Utilities,            only: toLower
        use FileReadingUtilities, only: getCharArrayFromString
        use ElementConnectivityDefinitions, only: normalAxis, FACES_PER_ELEMENT
        use MPI_Process_Info
        use WallFunctionDefinitions, only: Initialize_Wall_Function, wallFuncIndex, STD_WALL, ABL_WALL, u_tau0, useAverageV
        use Headers
#ifdef _HAS_MPI_
       use mpi
#endif
        implicit none
        class(FTValueDictionary),  intent(in)  :: controlVariables
        class(HexMesh), intent(inout)          :: mesh

!       ---------------
!       Local variables
!       ---------------
!
        character(len=LINE_LENGTH)             :: wallBC_str
        integer                                :: numberFacesWall, numberBC
        integer, dimension(:), allocatable     :: zonesWall
        integer                                :: i, j, nz, k, fID, efID, ff
        integer                                :: actualElementID, linkedElementID, normalIndex, oppositeIndex, oppositeNormalIndex
        integer                                :: allFaces, ierr

        call Initialize_Wall_Function(controlVariables, useWallFunc)
        if (.not. useWallFunc) then
            return
        end if

        ! get BC where the Wall Function will be applied
        wallBC_str = controlVariables % stringValueForKey("wall function boundaries", LINE_LENGTH)
        call toLower(wallBC_str)
        call getCharArrayFromString(wallBC_str, BC_STRING_LENGTH, wallFunBCs)

        ! get zones and number of faces for wall function
        numberBC = size(wallFunBCs)
        numberFacesWall = 0
        allocate(zonesWall(numberBC))

        do i = 1, numberBC
            do nz = 1, size(mesh % zones)
                if (trim(mesh % zones(nz) % Name) .eq. trim(wallFunBCs(i))) then
                    zonesWall(i) = nz
                    numberFacesWall = numberFacesWall + mesh % zones(nz) % no_of_faces
                    exit
                end if
            end do
        end do

        if (MPI_Process % doMPIAction) then
#ifdef _HAS_MPI_
            call mpi_allreduce(numberFacesWall, allFaces, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD, ierr)
#endif
        else
            allFaces = numberFacesWall
        end if
        if (allFaces .eq. 0) then
            useWallFunc = .false.
            if (MPI_Process % isRoot) write(STD_OUT,'(A)') "No wall BC found, the wall function will be deactivated"
            return
        end if

        allocate( wallElemIds(numberFacesWall), wallNormalIndex(numberFacesWall), wallNormalDirection(numberFacesWall), &
                 wallFaceID(numberFacesWall) )

        !get for each face of the wall, the linked element, normalDirection and index
        k = 0
        do j = 1, numberBC
            nz = zonesWall(j)
            do i = 1, mesh % zones(nz) % no_of_faces
                k = k + 1
                fID = mesh % zones(nz) % faces(i)
                actualElementID = mesh % faces(fID) % ElementIDs(1)
                associate ( e => mesh % elements(actualElementID) )
                    elem_loop:do efID = 1, FACES_PER_ELEMENT
                        if ( trim(wallFunBCs(j)) .eq. trim(e % boundaryName(efID)) ) then
                            normalIndex = normalAxis(efID)
                            exit elem_loop
                        end if
                    end do elem_loop
                    oppositeIndex = -1 * normalIndex
                    ! use the maxloc line if the compiler doesn't support findloc
                    ! oppositeIndex = findloc(normalAxis,oppositeIndex,dim=1)
                    oppositeIndex = maxloc(merge(1.0, 0.0, normalAxis == oppositeIndex),dim=1)
                    linkedElementID = e % Connection(oppositeIndex) % globID
                end associate
                linkedElementID = getElementID(mesh, linkedElementID)
                wallFaceID(k) = fID
                wallElemIds(k) = linkedElementID
                !get the normalIndex of the linked element instead of actual element, needed for rotated meshes
                do ff = 1, FACES_PER_ELEMENT
                    if (mesh % elements(linkedElementID) % Connection(ff) % globID .eq. mesh % elements(actualElementID) % globID) then
                        oppositeNormalIndex = normalAxis(ff)
                        exit
                    end if 
                end do
                wallNormalDirection(k) = abs(oppositeNormalIndex)
                wallNormalIndex(k) = getNormalIndex(mesh % faces(fID), mesh % elements(linkedElementID), wallNormalDirection(k))
            end do
        end do

!       Initialize u_tau storage for the wall faces
!       -------------------------------------------
        do i = 1, numberFacesWall
            fID = wallFaceID(i)
            associate( f => mesh%faces(fID) )
                f % storage(1) % u_tau_NS = u_tau0
            end associate
        end do 

!       Allocate average velocity if requested, only for same p in all faces
!       -------------------------------------------
        if (useAverageV) then
            fID = wallFaceID(1)
            associate( f => mesh%faces(fID) )
                allocate( meanVelocity(NDIM,numberFacesWall,0:f % Nf(1),0:f % nf(2)) )
            end associate
            ! meanVelocity = 0.0_RP
            call WallStartMeanV(mesh)
            timeCont = 0.0_RP
        end if

!       Describe the Wall function
!       --------------------------
        if ( .not. MPI_Process % isRoot ) return
        write(STD_OUT,'(/)')
        call Subsection_Header("Wall function")

        write(STD_OUT,'(30X,A,A28,I0)') "->", "Number of faces: ", allFaces
        select case (wallFuncIndex)
            case (STD_WALL)
                write(STD_OUT,'(30X,A,A28,A)') "->", "Wall Function Law: ", "Reichardt"
            case (ABL_WALL)
                write(STD_OUT,'(30X,A,A28,A10)') "->", "Wall Function Law: ", "ABL"
        end select
        if (useAverageV) write(STD_OUT,'(30X,A,A28)') "->", "Wall Function using time averaged values"

    End Subroutine Initialize_WallConnection

    integer Function getNormalIndex(f, e, normalDirection)
        use FaceClass
        use ElementClass
        implicit none

        class(Element), intent(in)                                       :: e
        class(Face), intent(in)                                          :: f
        integer, intent(in)                                              :: normalDirection

!       ---------------
!       Local variables
!       ---------------
!
        real(kind=RP), dimension(NDIM)                                   :: x0, xN, xf
        real(kind=RP)                                                    :: dx(2)
        integer                                                          :: N, indexArray(2), minIndex

        xf = f % geom % x(:,0,0)
        x0 = e % geom % x(:,0,0,0)
        select case (normalDirection)
        case (1)
            N = e % Nxyz(1)
            xN = e % geom % x(:,N,0,0)
        case (2)
            N = e % Nxyz(2)
            xN = e % geom % x(:,0,N,0)
        case (3)
            N = e % Nxyz(3)
            xN = e % geom % x(:,0,0,N)
        case default
           write(STD_OUT,'(A)') "Error: wallNormalDirection not found in axisMap"
           errorMessage(STD_OUT)
           error stop 
        end select

        indexArray = [0,N]
        dx(1) = norm2(xf-x0)
        dx(2) = norm2(xf-xN)
        minIndex = minloc(dx,dim=1)

        getNormalIndex = indexArray(minIndex)

    End Function getNormalIndex

    Subroutine WallFunctionGatherFlowVariables(mesh, f, V, rho, mu, dWall, Vavg)

!     *******************************************************************
!        This subroutine get the flow variables of the neighbour element
!        of the face.
!     *******************************************************************
!
        use PhysicsStorage
        use FaceClass
        use VariableConversion, only: get_laminar_mu_kappa
        use WallFunctionDefinitions, only: useAverageV
        implicit none

        class(HexMesh), intent(in)                                       :: mesh
        class(Face), intent(in)                                          :: f
        real(kind=RP), dimension(NDIM,0:f%Nf(1),0:f%Nf(2)), intent(out)  :: V
        real(kind=RP), dimension(0:f%Nf(1),0:f%Nf(2)), intent(out)       :: rho
        real(kind=RP), dimension(0:f%Nf(1),0:f%Nf(2)), intent(out)       :: mu
        real(kind=RP), dimension(0:f%Nf(1),0:f%Nf(2)), intent(out)       :: dWall
        real(kind=RP), dimension(NDIM,0:f%Nf(1),0:f%Nf(2)), intent(out)  :: Vavg

!       ---------------
!       Local variables
!       ---------------
!
        ! real(kind=RP), dimension(NCONS,0:f%Nf(1),0:f%Nf(2))             :: Q
        real(kind=RP), dimension(:,:,:), allocatable                     :: Q, x
        ! real(kind=RP), dimension(NDIM,0:f%Nf(1),0:f%Nf(2))              :: x
        real(kind=RP), dimension(NDIM)                                  :: dWallVector
        real(kind=RP)                                                   :: kappa, invRho
        integer                                                         :: i, j, fInd

        call WallGetFaceConnectedQ(mesh, f, Q, x, fInd)
        do j = 0, f % Nf(2)
            do i = 0, f % Nf(1)
                rho(i,j) = Q(IRHO,i,j)
                invRho = 1.0_RP / rho(i,j)
                V(:,i,j) = Q(IRHOU:IRHOW,i,j) * invRho
                call get_laminar_mu_kappa(Q(:,i,j),mu(i,j),kappa)
                dWallVector(:) = x(:,i,j) - f % geom % x(:,i,j)
                dWall(i,j) = norm2(dWallVector)
            end do
        end do
        
        if (useAverageV) then
            Vavg(:,:,:) = meanVelocity(:,fInd,:,:)
            ! Vavg = reshape( meanVelocity(:,fInd,0:f%Nf(1),0:f%Nf(2)), /NDIM,0:f%Nf(1),0:f%Nf(2)/ )
        else
            Vavg = 0.0_RP
        end if 

    End Subroutine WallFunctionGatherFlowVariables

    Subroutine WallGetFaceConnectedQ(mesh,f,Q,x,faceIndex)
!     *******************************************************************
!        This subroutine get the flow solution of the neighbour element
!        of the face.
!     *******************************************************************
        use PhysicsStorage
        use FaceClass
        type(HexMesh), intent(in)                                        :: mesh
        class(Face), intent(in)                                          :: f
        real(kind=RP), dimension(:,:,:), allocatable, intent(out)        :: Q
        real(kind=RP), dimension(:,:,:), allocatable, intent(out)        :: x
        integer, intent(out)                                             :: faceIndex
!       Local variables
        integer                                                         :: eID, solIndex
        ! integer                                                         :: faceIndex, eID, solIndex

        allocate( Q(NCONS,0:f % Nf(1),0:f % Nf(2)), x(NDIM,0:f % Nf(1),0:f % Nf(2)) )

        ! use the maxloc line if the compiler doesn't support findloc
        ! faceIndex = findloc(wallFaceID, f % ID, dim=1)
        faceIndex = maxloc(merge(1.0, 0.0, wallFaceID == f % ID),dim=1)
        eID = wallElemIds(faceIndex)
        solIndex = wallNormalIndex(faceIndex)

        ! select the local coordinate directions of the face base on the definition of axisMap in HexElementConnectivityDefinitions
        associate ( e => mesh % elements(eID) )
            select case (wallNormalDirection(faceIndex))
            case (1)
                Q(:,:,:) = e % storage % Q(:,solIndex,:,:)
                x(:,:,:) = e % geom % x(:,solIndex,:,:)
            case (2)
                Q(:,:,:) = e % storage % Q(:,:,solIndex,:)
                x(:,:,:) = e % geom % x(:,:,solIndex,:)
            case (3)
                Q(:,:,:) = e % storage % Q(:,:,:,solIndex)
                x(:,:,:) = e % geom % x(:,:,:,solIndex)
            case default
               write(STD_OUT,'(A)') "Error: wallNormalDirection not found in axisMap"
               errorMessage(STD_OUT)
               error stop 
            end select
        end associate

    End Subroutine WallGetFaceConnectedQ

    Subroutine WallUpdateMeanV(mesh, dt)
        use PhysicsStorage
        use WallFunctionDefinitions, only: useAverageV
        implicit none
        type(HexMesh), intent(in)                       :: mesh
        real(kind=RP),  intent(in)                      :: dt
!
!       ---------------
!       Local variables
!       ---------------
!
        integer                                         :: fIndex, fID, i, j, fInd
        real(kind=RP), dimension(:,:,:), allocatable    :: Q, x
        real(kind=RP)                                   :: invRho
        real(kind=RP), dimension(NDIM)                  :: localV
        logical                                         :: saveLocal

        ! create separate to set initial conditions
        if (.not. useAverageV) return

        do fIndex = 1, size(wallFaceID)
            fID = wallFaceID(fIndex)
            associate( f => mesh%faces(fID) )
                call WallGetFaceConnectedQ(mesh, f, Q, x, fInd)
                do j = 0, f % Nf(2)
                    do i = 0, f % Nf(1)
                        invRho = 1.0_RP / Q(IRHO,i,j)
                        localV(:) = Q(IRHOU:IRHOW,i,j) * invRho
                        meanVelocity(:,fIndex,i,j) = ( (meanVelocity(:,fIndex,i,j) * timeCont) + localV(:) * dt ) / (timeCont+dt)
                    end do
                end do
            end associate
        end do 

        timeCont = timeCont + dt

    End Subroutine WallUpdateMeanV

    Subroutine WallStartMeanV(mesh)
        use PhysicsStorage
        use WallFunctionDefinitions, only: useAverageV
        implicit none
        type(HexMesh), intent(in)                       :: mesh
!
!       ---------------
!       Local variables
!       ---------------
!
        integer                                         :: fIndex, fID, i, j, fInd
        real(kind=RP), dimension(:,:,:), allocatable    :: Q, x
        real(kind=RP)                                   :: invRho
        real(kind=RP), dimension(NDIM)                  :: localV
        logical                                         :: saveLocal

        ! create separate to set initial conditions
        if (.not. useAverageV) return

        do fIndex = 1, size(wallFaceID)
            fID = wallFaceID(fIndex)
            associate( f => mesh%faces(fID) )
                call WallGetFaceConnectedQ(mesh, f, Q, x, fInd)
                do j = 0, f % Nf(2)
                    do i = 0, f % Nf(1)
                        invRho = 1.0_RP / Q(IRHO,i,j)
                        localV(:) = Q(IRHOU:IRHOW,i,j) * invRho
                        meanVelocity(:,fIndex,i,j) = localV(:)
                    end do
                end do
            end associate
        end do 

    End Subroutine WallStartMeanV

    integer Function getElementID(mesh, globeID)

!     *******************************************************************
!        This subroutine get the element ID based on the global element ID
!        needed specially for MPI, to be sure that the element is in the mesh
!        partition.
!     *******************************************************************
!
        implicit none
        class(HexMesh), intent(in)             :: mesh
        integer, intent(in)                    :: globeID
!
!       ---------------
!       Local variables
!       ---------------
!
        integer                                :: eID, solIndex

        do eID = 1, mesh % no_of_elements
            if (mesh % elements(eID) % globID .eq. globeID) then
                getElementID = eID
                return
            end if
        end do

        ! if the code reach this point the element does not exists
        write(STD_OUT,'(A,I0)') "Error: The element of the wall function does not exists in the mesh or mesh partition, global ID: ", globeID
        errorMessage(STD_OUT)
        error stop 

    End Function getElementID

End Module WallFunctionConnectivity
#endif