TripForceClass.f90 Source File


Source Code

!//////////////////////////////////////////////////////
!
!This class represents the numerical force use to induce turbulence, or tripping
!following JFM by Schlatter & Orlu 2012: 
!Turbulent boundary layers at moderate Reynolds numbers: infow length and tripping effects

#include "Includes.h"
Module TripForceClass
  use SMConstants
  use HexMeshClass
  use NodeClass
  use Utilities
  use FTValueDictionaryClass
  use PhysicsStorage
#ifdef HAS_MKL
  use MKL_DFTI, forget => DFTI_DOUBLE, DFTI_DOUBLE => DFTI_DOUBLE_R
#endif
  Implicit None

  private
  public randomTrip

  !definition of gtripClass 
  type gtripClass
    real(kind=RP), dimension(:), allocatable                 :: g, z
    real(kind=RP), dimension(:,:), allocatable               :: h ! the first two dimensions are the old and new values of the random signal respectively, the 3 one is the steady state
    real(kind=RP)                                            :: At, As, ts, zs
    integer                                                  :: Ncutz, N, tsubstep ! N is the number of points to be evaluated in the spanwise direction
    logical                                                  :: isSimetric

  contains

    procedure :: construct      => gtripConstruct
    procedure :: destruct       => gtripDestruct
    procedure :: updateInTime   => gTripForce
    procedure :: randSignal
    procedure :: forceAtz       => getgTripForceAtZ

  end type gtripClass

  !definition of the complete tripClass 
  type ftripClass
      integer                                               :: numberOfTrips
      real(kind=RP), dimension(:,:), allocatable            :: centerPositions
      real(kind=RP), dimension(2)                           :: spatialGaussianAtenuation
      real(kind=RP), dimension(:,:), allocatable            :: normals
      type(gtripClass)                                      :: gTrip
      logical                                               :: active = .false.

      contains

    procedure :: construct      => ftripConstruct
    procedure :: destruct       => ftripDestruct
    procedure :: getTripForce
    procedure :: getTripSource

  end type ftripClass

  type(ftripClass)                                          :: randomTrip

  contains
!/////////////////////////////////////////////////////////////////////////
!           FTRIP PROCEDURES --------------------------
!/////////////////////////////////////////////////////////////////////////

  Subroutine ftripConstruct(self, mesh, controlVariables)
    use FileReadingUtilities, only: getRealArrayFromString, getCharArrayFromString
    use Utilities,            only: toLower
    use MPI_Process_Info
    use Headers
#ifdef _HAS_MPI_
    use mpi
#endif
    implicit none

    class(ftripClass)                                         :: self
    type(HexMesh), intent(in)                                 :: mesh
    type(FTValueDictionary), intent(in)                       :: controlVariables

    !local variables
    real(kind=RP), parameter                                  :: min_normal = 1.0e-15_RP
    integer                                                   :: N, fID, i, partitionRank, ierr
    real(kind=RP)                                             :: x1, x2, y1, y2
    character(len=LINE_LENGTH)                                :: gaussAten_str, trip_zones_str
    character(len=LINE_LENGTH), dimension(:), allocatable     :: trip_zones
    integer, dimension(2)                                     :: faceIndex
    integer, dimension(:), allocatable                        :: trip_zones_index
    real(kind=RP), dimension(NDIM)                            :: temp_normals
    real(kind=RP), dimension(2)                               :: temp_center

    if (controlVariables % containsKey("trip center")) then
        x1 = controlVariables % doublePrecisionValueForKey("trip center")
    else 
        error stop "Trip center must be defined"
    end if

    if (controlVariables % containsKey("trip attenuation")) then
        gaussAten_str = trim(controlVariables % stringValueForKey("trip attenuation", LINE_LENGTH))
        self % spatialGaussianAtenuation = getRealArrayFromString(gaussAten_str)
    else 
        error stop "Trip attenuation must be defined"
    end if
    
    if (controlVariables % containsKey("trip zone")) then
        trip_zones_str = trim(controlVariables % stringValueForKey("trip zone", LINE_LENGTH))
        call toLower(trip_zones_str)
        call getCharArrayFromString(trip_zones_str, LINE_LENGTH, trip_zones)
    else 
        error stop "Trip zone must be defined"
    end if
    
    N = 1
    if (controlVariables % containsKey("trip center 2")) then
        x2 = controlVariables % doublePrecisionValueForKey("trip center 2")
        N = 2
    end if
    
    if (size(trip_zones) .ne. N) error stop "The length of the trip zones is not the same as the trip center length"

    allocate(self % centerPositions(N,2), self % normals(N,NDIM), trip_zones_index(N))
    ! only x of centerPositions is read, y is calculated
    ! if two x are set, the first is search at y>=0 and the second at y<0
    self % numberOfTrips = N

    trip_zones_index = 0
    do i = 1, size(mesh % zones)
      if (trim(mesh % zones(i) % Name) .eq. trim(trip_zones(1))) trip_zones_index(1) = i
      if (N .eq. 2 .and. trim(mesh % zones(i) % Name) .eq. trim(trip_zones(2))) trip_zones_index(2) = i
    end do

    if (trip_zones_index(1) .eq. 0) error stop "Trip zone not found"

    call getFaceTripIndex(mesh % zones(trip_zones_index(1)), mesh, x1, 1, fID, faceIndex, partitionRank, yNegative = .false.)
    if (partitionRank .eq. MPI_Process % rank) then
      temp_center = mesh % faces(fID) % geom % x(1:2,faceIndex(1),faceIndex(2))
      temp_normals = mesh % faces(fID) % geom % normal(:,faceIndex(1),faceIndex(2))
      do i = 1, NDIM
        if (abs(temp_normals(i)) .le. min_normal) temp_normals(i) = 0.0_RP
      end do
    end if
    if (MPI_Process % doMPIAction) then
#ifdef _HAS_MPI_
          call mpi_Bcast(temp_center, 2, MPI_DOUBLE, partitionRank, MPI_COMM_WORLD, ierr)
          call mpi_Bcast(temp_normals, NDIM, MPI_DOUBLE, partitionRank, MPI_COMM_WORLD, ierr)
#endif
    end if 
    self % centerPositions(1,:) = temp_center
    self % normals(1,:) = temp_normals

    if (N .eq. 2) then
      call getFaceTripIndex(mesh % zones(trip_zones_index(2)), mesh, x2, 1, fID, faceIndex, partitionRank, yNegative = .true.)
      if (partitionRank .eq. MPI_Process % rank) then
        temp_center = mesh % faces(fID) % geom % x(1:2,faceIndex(1),faceIndex(2))
        temp_normals = mesh % faces(fID) % geom % normal(:,faceIndex(1),faceIndex(2))
        do i = 1, NDIM
          if (abs(temp_normals(i)) .le. min_normal) temp_normals(i) = 0
        end do
      end if
      if (MPI_Process % doMPIAction) then
#ifdef _HAS_MPI_
        call mpi_Bcast(temp_center, 2, MPI_DOUBLE, partitionRank, MPI_COMM_WORLD, ierr)
        call mpi_Bcast(temp_normals, NDIM, MPI_DOUBLE, partitionRank, MPI_COMM_WORLD, ierr)
#endif
      end if 
      self % centerPositions(2,:) = temp_center
      self % normals(2,:) = temp_normals
    end if

    self % active = .true.

!   Describe the Trip
!   --------------------------
    if ( MPI_Process % isRoot ) then
        write(STD_OUT,'(/)')
        call Subsection_Header("Trip Source Term")
        ! write(STD_OUT,'(30X,A,A28,A)') "->", "Trip Type: ", "Random"
        write(STD_OUT,'(30X,A,A28,I0)') "->", "Number of trips: ", N
        if (N .eq. 2) write(STD_OUT,'(30X,A,A28,A,ES10.3,A,ES10.3,A)') "->", "Second Trip center: ", "[",self % centerPositions(2,1),",",self % centerPositions(2,2),"]"
        write(STD_OUT,'(30X,A,A28,A,ES10.3,A,ES10.3,A)') "->", "First Trip center: ", "[",self % centerPositions(1,1),",",self % centerPositions(1,2),"]"
    end if

    call self % gTrip % construct(mesh, controlVariables)

  End Subroutine ftripConstruct
!
  Subroutine ftripDestruct(self)

    class(ftripClass), intent(inout)                          :: self

    safedeallocate(self % centerPositions)
    safedeallocate(self % normals)
    call self % gTrip % destruct()

  End Subroutine ftripDestruct
!
  Subroutine getTripForce(self, x, F2, tripIndex)
      use MPI_Process_Info
    implicit none

    class(ftripClass)                                         :: self
    real(kind=RP), dimension(NDIM), intent(in)                :: x
    real(kind=RP), intent(out)                                :: F2
    integer, intent(out)                                      :: tripIndex

    !local variables
    real(kind=RP)                                             :: g_z
    real(kind=RP), dimension(2,2)                             :: lims, gaussLimits
    integer                                                   :: i

    F2 = 0.0_RP
    tripIndex = 0
    gaussLimits(:,1) = 0.0_RP - self % spatialGaussianAtenuation(:) * 9.0_RP/4.0_RP ! aprox 3 std deviation of gaussian function
    gaussLimits(:,2) = 0.0_RP + self % spatialGaussianAtenuation(:) * 9.0_RP/4.0_RP
    do i = 1, self % numberOfTrips
        lims(1,:) = gaussLimits(1,:) + self % centerPositions(i,1)
        lims(2,:) = gaussLimits(2,:) + self % centerPositions(i,2)
        if( (x(1)>lims(1,1) .and. x(1)<lims(1,2)) .and. (x(2)>lims(2,1) .and. x(2)<lims(2,2)) ) then
            g_z = self % gTrip % forceAtz(x(3))
            F2 = exp( -((x(1) - self % centerPositions(i,1)) / self % spatialGaussianAtenuation(1)) ** 2.0_RP &
                      -((x(2) - self % centerPositions(i,2)) / self % spatialGaussianAtenuation(2)) ** 2.0_RP ) * g_z
            tripIndex = i
            exit
        end if
    end do

  End Subroutine getTripForce
!
  Subroutine getTripSource(self, x, S)
    implicit none

    class(ftripClass)                                         :: self
    real(kind=RP), dimension(NDIM), intent(in)                :: x
    real(kind=RP), dimension(NCONS), intent(inout)            :: S

    !local variables
    real(kind=RP)                                             :: F
    integer                                                   :: tripIndex

    if (.not. self % active) return

    call self % getTripForce(x, F, tripIndex)
    S(IRHOU:IRHOW) = S(IRHOU:IRHOW) - F * self % normals(tripIndex,:)

  End Subroutine getTripSource
!
!/////////////////////////////////////////////////////////////////////////
!           GTRIP PROCEDURES --------------------------
!/////////////////////////////////////////////////////////////////////////
!
  Subroutine  gtripConstruct(self, mesh, controlVariables)

    use Headers
    use MPI_Process_Info
#ifdef _HAS_MPI_
    use mpi
#endif
    implicit none

    class(gtripClass)                                         :: self
    type(HexMesh), intent(in)                                 :: mesh
    type(FTValueDictionary), intent(in)                       :: controlVariables

    !local variables
    integer, parameter                                        :: seedSizeOriginal = 2
    integer                                                   :: seedSize, seedSizeNeed
    integer, dimension(:), allocatable                        :: seed, seedTemp
    real(kind=RP)                                             :: dz, zMax, zMin, allzMax, allzMin
    integer                                                   :: eID, k, N, Nz, i, j
    integer                                                   :: ierr

#ifdef HAS_MKL
!
!   ----------------------
!   Read Control variables
!   ----------------------
!
    if (controlVariables % containsKey("trip time scale")) then
        self % ts = controlVariables % doublePrecisionValueForKey("trip time scale")
    else
        error stop "trip time scale must be defined"
    end if
    if (controlVariables % containsKey("trip number of modes")) then
        self % Ncutz = controlVariables % IntegerValueForKey("trip number of modes")
    else
        error stop "trip number of modes must be defined"
    end if
    if (controlVariables % containsKey("trip z points")) then
        N = controlVariables % IntegerValueForKey("trip z points")
    else
        error stop "trip z points must be defined"
    end if

    self % N = N
    if (self % Ncutz .ge. self % N) error stop 'The trip cuttoff number of modes is bigger than the number of points in z'

    self % At = controlVariables % getValueOrDefault("trip amplitude",1.0)
    self % As = controlVariables % getValueOrDefault("trip amplitude steady",0.0)
    self % isSimetric = .FALSE.

    !this needs to be updated for different simulations, but remain constant for the same one if is restarted
    ! the numbers are obtained from a call to RANDOM_SEED with a get; and are hardcoded to the control file or blank and use default
    allocate(seedTemp(seedSizeOriginal))
    seedTemp(1) = controlVariables % getValueOrDefault("random seed 1",930187532)
    seedTemp(2) = controlVariables % getValueOrDefault("random seed 2",597734650)
    if ( MPI_Process % isRoot ) then
        call random_seed(size=seedSizeNeed)
        if (seedSizeNeed .gt. seedSizeOriginal) then
            seedSize = seedSizeNeed
            allocate(seed(seedSize))
            seed(1:seedSizeOriginal) = seedTemp(:)
            do i = seedSizeOriginal+1, seedSizeNeed
                j = mod(i+1, 2) + 1
                seed(i) = seedTemp(j)
            end do
        else
            seedSize = seedSizeOriginal
            allocate(seed(seedSize))
            seed = seedTemp
        end if
        deallocate(seedTemp)
        call random_seed (PUT=seed(1:seedSize))
    end if

    !search for the maximum and minimum values of z in the whole mesh
!!$omp parallel do private(eID,k) reduction(MIN:zMin) reduction(MAX:zMax) schedule(runtime)
    zMin = mesh % nodes(mesh % elements(1) % nodeIDs(1)) % x(3)
    zMax = mesh % nodes(mesh % elements(1) % nodeIDs(1)) % x(3)
    do eID = 1, mesh % no_of_elements
       associate ( e => mesh % elements(eID) )
        do k = 1, 6
           associate ( xx => mesh % nodes(e % nodeIDs(k))  %  x(3))
             if (xx < zMin) zMin = xx
             if (xx > zMax) zMax = xx
         end associate
         end do
       end associate
    end do
!!$omp end parallel do
    if ( (MPI_Process % doMPIAction) ) then
#ifdef _HAS_MPI_
      call mpi_allreduce(zMin, allzMin, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD, ierr)
      call mpi_allreduce(zMax, allzMax, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD, ierr)
#endif
    else
      allzMin = zMin
      allzMax = zMax
    end if

    allocate(self % g(0:N-1), self % h(0:N-1,3), self % z(0:N-1))
    ! todo: check why this is used
    ! self % Ncutz = int((xz(2) - xz(1))/ self % zs)
    self % zs = (allzMax - allzMin)/ real(self % Ncutz,RP)

    ! create evenly spaced z array
    dz = (allzMax - allzMin) / (N-1)
    self % z(0:N-1) = [(allzMin + k*dz, k=0, N-1)] ! implicit loop, mini cycle

    ! start of simulation, if is a restart it will be updated
    self % tsubstep = -1

    !get the steady state random signal
    call randSignal(self, self % h(:,3))
    !get the first random signal, for i=0
    call randSignal(self, self % h(:,2))

!   Describe the Trip
!   --------------------------
    if ( .not. MPI_Process % isRoot ) return
    write(STD_OUT,'(30X,A,A28,A,I0,A,I0,A)') "->", "Random seeds: ", "[",seed(1),",",seed(2),"]"
    write(STD_OUT,'(30X,A,A28,F7.4)') "->", "Trip Amplitude: ", self % At
    write(STD_OUT,'(30X,A,A28,ES10.3)') "->", "Trip Time scale: ", self % ts
    write(STD_OUT,'(30X,A,A28,ES10.3)') "->", "Trip Space scale: ", self % zs

#else
      error stop 'MKL not linked correctly'
#endif

  End Subroutine gtripConstruct 
!
  Subroutine  gtripDestruct(self)

    class(gtripClass), intent(inout)                          :: self
 
    if (ALLOCATED(self%g)) DEALLOCATE (self%g)
    if (ALLOCATED(self%h)) DEALLOCATE (self%h)
    if (ALLOCATED(self%z)) DEALLOCATE (self%z)
    self % tsubstep = -1

  End Subroutine gtripDestruct 
!
  Subroutine randSignal(self, signal)
  ! creates a random signal (array) with the first Ncutz modes random and .le. 1.0 and 0 for modes greater than Ncutz.
  ! The array is created in an evenly distributed z (spanwise direction), it needs to be interpolated at the mesh points later.

    use MPI_Process_Info
#ifdef _HAS_MPI_
    use mpi
#endif
    implicit none

    class(gtripClass)                                         :: self
    real(kind=RP), dimension(0:self%N-1), intent(out)         :: signal

    !local variables
    integer                                                   :: i, ierr
    integer                                                   :: nh ! half of cuttoff number
    complex(kind=CP)                                          :: fourierArg
    real(kind=RP), dimension(self%Ncutz/2+1)                  :: ranNum
    real(kind=RP), dimension(0:self%N+1)                      :: fourierCoefficients, preSignal

    if ( MPI_Process % isRoot ) then
        nh = self%Ncutz/2
        ! the signal coefficients are initialized at zero, it'll be changed for frequencies <= Ncutz
        fourierCoefficients = 0_RP

        ! the mean value, n=0 is random and ampliated by sqrt 2 for the real part and 0 for the img
        ! the coef array is packed in CCS format
        call random_number(ranNum)
        fourierArg = exp(ImgI*2.0_RP*PI*ranNum(1))
        fourierCoefficients(0) = real(fourierArg)*sqrt(2.0_RP)
        fourierCoefficients(1) = 0

        ! for the other values of n less than the cut off, the random amplitude is ampliated by sqrt 2 for the real part and 0 for the
        ! img for the symmetric case
        ! for the non symmetric case, the amplitude is set to 1.0 and the phase is random (both real and part are saved)
        if (self%isSimetric) then
          do i = 1, nh
            fourierArg = exp(ImgI*2.0_RP*PI*ranNum(i+1))
            fourierCoefficients(2*i) = real(fourierArg)*sqrt(2.0_RP)
            fourierCoefficients(2*i+1) = 0
          end do  
        else
          do i = 1, nh
            fourierArg = exp(ImgI*2.0_RP*PI*ranNum(i+1))
            fourierCoefficients(2*i) = real(fourierArg)
            fourierCoefficients(2*i+1) = aimag(fourierArg)
          end do
        end if

        call backfft(preSignal, self % N, fourierCoefficients)
        ! scale and give only the part of the array of interest
    end if
    if ( (MPI_Process % doMPIAction) ) then
#ifdef _HAS_MPI_
          call mpi_Bcast(preSignal, self % N+1, MPI_DOUBLE, 0, MPI_COMM_WORLD, ierr)
#endif
    end if
    signal = 1.0/real(self % Ncutz)*preSignal(0:self % N-1)

  End Subroutine randSignal
!
  Subroutine  gTripForce(self, t)
    !compute the g term of the trip force equation

    implicit none

    class(gtripClass)                                         :: self
    real(kind=RP), intent(in)                                 :: t

    !local variables
    integer                                                   :: i !integer quotient for local time and time cuttoff scale (ts)
    integer                                                   :: j !time iterator
    real(kind=RP)                                             :: b, p ! terms of gTripForce equation, related to time interpolant

    i = int(t/self % ts)
    p = (t - real(i)*self % ts)/self % ts
    b = p*p*(3.0_RP - 2.0_RP*p)

    do j = self % tsubstep + 1, i
      self % h(:,1) = self % h(:,2)
      call randSignal(self,self%h(:,2) )
    end do

    self % tsubstep = i
    self % g = self % At * ( (1.0_RP-b) * self % h(:,1) + b * self % h(:,2) ) + self % As * self % h(:,3)

  End Subroutine gTripForce
!
  Subroutine forfft(coef,x,N,coeffCCS)

    integer, intent(in)                                       :: N
    real(kind=RP), dimension(0:N-1), intent(in)               :: x
    real(kind=RP), dimension(0:N+1), intent(out)               :: coeffCCS
    complex(kind=CP), dimension(0:int(N/2)), intent(out)           :: coef

    ! local variables
    real(kind=RP), dimension(0:N+1)                           :: y
#ifdef HAS_MKL
    type(dfti_descriptor), pointer                            :: hand
#endif
    integer                                                   :: status, i, nh

#ifdef HAS_MKL

    y(0:N-1) = x

    status = DftiCreateDescriptor(hand, DFTI_DOUBLE, DFTI_REAL, 1, N)
    status = DftiCommitDescriptor(hand)
    status = DftiComputeForward( hand, y )
    status = DftiFreeDescriptor(hand)

    nh = N/2
    do i= 0,nh
      coef(i) = cmplx(y(2*i), y(2*i+1))
    end do
    ! if (mod(N,2).ne.0) nh = nh +1
    ! do i= 1,nh-1
    !   coef(nh+i) = conjg(coef(nh-i))
    ! end do
    coeffCCS = y
#else
      error stop 'MKL not linked correctly'
#endif

  End Subroutine forfft 
!
  Subroutine backfft(x,N,coeffCCS)

    integer, intent(in)                                       :: N
    real(kind=RP), dimension(0:N+1), intent(in)               :: coeffCCS
    real(kind=RP), dimension(0:N-1), intent(out)              :: x

    ! local variables
    real(kind=RP), dimension(0:N+1)                           :: y
#ifdef HAS_MKL
    type(dfti_descriptor), pointer                            :: hand
#endif
    integer                                                   :: status, i

#ifdef HAS_MKL

    y = coeffCCS

    status = DftiCreateDescriptor(hand, DFTI_DOUBLE, DFTI_REAL, 1, N)
    status = DftiCommitDescriptor(hand)
    status = DftiComputeForward( hand, y )
    status = DftiFreeDescriptor(hand)

    x = y(0:N-1)

#else
      error stop 'MKL not linked correctly'
#endif

  End Subroutine backfft 
!
  Function getgTripForceAtZ(self, specificZ) result(gz)
    ! returns the value of the g term at a given z position, using linear interpolation
    ! it assumes that the array g corresponds position by position to the array z

    implicit none

    class(gtripClass)                                         :: self
    real(kind=RP), intent(in)                                 :: specificZ
    real(kind=RP)                                             :: gz   !g term at a z position

    !local variables
    integer                                                   :: i

    !starts in 1 in case there is a value .eq. to the 0 position
    do i = 1, self % N - 1
      if (specificZ .ge. self % z(i)) exit
    end do  

    gz = self % g(i-1) + ( self % g(i) - self % g(i-1) ) / ( self % z(i) - self % z(i-1) ) * (specificZ - self%z(i-1))

  End Function getgTripForceAtZ 
!
!/////////////////////////////////////////////////////////////////////////
!           HELPER PROCEDURES --------------------------
!/////////////////////////////////////////////////////////////////////////
!
  Subroutine getFaceTripIndex(zone, mesh, x, x_dim, faceID, faceIndex, partitionRank, yNegative)
    use ZoneClass
    use MPI_Process_Info
#ifdef _HAS_MPI_
    use mpi
#endif
    implicit none

    type(Zone_t), intent(in)                                  :: zone
    type(HexMesh), intent(in)                                 :: mesh
    real(kind=RP), intent(in)                                 :: x
    integer, intent(in)                                       :: x_dim
    logical, intent(in), optional                             :: yNegative
    integer, intent(out)                                      :: faceID
    integer, dimension(2), intent(out)                        :: faceIndex
    integer, intent(out)                                      :: partitionRank

    !local variables
    integer                                                   :: i, j, faceZoneID, fID, yIndex, allfID
    integer                                                   :: nx, ny, ierr
    real(kind=RP)                                             :: x_diff, minx_diff, lmin, lmax
    logical                                                   :: onlyYNegative
    integer                                                   :: tempRank

    if (present(yNegative)) then
        onlyYNegative = yNegative
    else
        onlyYNegative = .false.
    end if 

    yIndex = x_dim + 1
    if (yIndex .gt. NDIM) yIndex = yIndex - NDIM

    faceID = 0
    do faceZoneID = 1, zone % no_of_faces
      fID = zone % faces(faceZoneID)
      associate( f => mesh % faces(fID) )
        nx = f % Nf(1)
        ny = f % Nf(2)
        lmin = minval([f % geom % x(x_dim,0,0), f % geom % x(x_dim,nx,ny)])
        lmax = maxval([f % geom % x(x_dim,0,0), f % geom % x(x_dim,nx,ny)])
        if ( (x .ge. lmin) .and. (x .le. lmax) ) then
          if (onlyYNegative .eqv. (f % geom % x(yIndex,0,0) .lt. 0.0_RP)) then
            faceID = fID
            exit
          end if
        end if 
      end associate
    end do

    ! if partition does not found the face, just store the info of the one that did it, and which partition number is
    if (MPI_Process % doMPIAction) then
#ifdef _HAS_MPI_
      call mpi_barrier(MPI_COMM_WORLD, ierr)
      call mpi_allreduce(faceID, allfID, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD, ierr)
#endif

      if (allfID .eq. 0) error stop "trip center not found in any face of the zone"

      ! start value for allreduce
      tempRank = 0
      if (allfID .eq. faceID) tempRank = MPI_Process % rank
#ifdef _HAS_MPI_
      call mpi_allreduce(tempRank, partitionRank, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD, ierr)
#endif
    else
      if (faceID .eq. 0) error stop "trip center not found in any face of the zone"
      partitionRank = 0
    end if

    !only calculate faceIndex if its in the partition
    faceIndex = 0
    if (partitionRank .eq. MPI_Process % rank) then
        minx_diff = abs(mesh % faces(faceID) % geom % x(x_dim,0,0) - x)
        do j = 0, ny
          do i = 0, nx
            x_diff = abs(mesh % faces(faceID) % geom % x(x_dim,i,j) - x)
            if (x_diff .le. minx_diff) then
                minx_diff = x_diff
                faceIndex = [i,j]
            end if
          end do
        end do
    end if

  End Subroutine getFaceTripIndex
!
End Module TripForceClass