ActuatorLine.f90 Source File


Source Code

#include "Includes.h"

module ActuatorLine
#if defined(NAVIERSTOKES)
    use SMConstants
    use MPI_Process_Info
#ifdef _HAS_MPI_
    use mpi
#endif
    implicit none

private
public farm
!
!  ******************************
!  DEFINE TURBINE, BLADE, AIRFOIL
!  ******************************
! 
!
    type airfoil_t
    integer                         :: num_aoa
    integer                         :: num_Re 
    real(KIND=RP), allocatable      :: aoa(:)  ! in rad
    real(KIND=RP), allocatable      :: Re(:)  ! reynolds number
    real(KIND=RP), allocatable      :: cl(:,:)
    real(KIND=RP), allocatable      :: cd(:,:)
    end type

    type blade_t
    real(KIND=RP), allocatable      :: r_R(:)  ! in m
    real(KIND=RP), allocatable      :: chord(:)   ! in m
    real(KIND=RP), allocatable      :: twist(:)    ! in rad
    real(KIND=RP)                   :: azimuth_angle   ! in rad
    integer, allocatable            :: num_airfoils(:)    ! for different Re
    CHARACTER(LEN=30), allocatable  :: airfoil_files(:,:)  ! file names for Cl-Cd
    type(airfoil_t), allocatable    :: airfoil_t(:)  ! airfoil data AoA-Cl-Cd
    real(KIND=RP), allocatable      :: local_velocity(:)  ! local flow speed at blade section, im m/s
    real(KIND=RP), allocatable      :: local_angle(:)  ! local flow angle at blade section, in rad
    real(KIND=RP), allocatable      :: local_lift(:)  ! blade sectional Lift
    real(KIND=RP), allocatable      :: local_drag(:)  ! blade sectional Lift
    real(KIND=RP), allocatable      :: point_xyz_loc(:,:) ! x,y location of blade points
    real(KIND=RP), allocatable      :: local_torque(:)  ! N.m
    real(KIND=RP), allocatable      :: local_thrust(:)  ! N
    real(KIND=RP), allocatable      :: local_thrust_temp(:)  ! N
    real(KIND=RP), allocatable      :: local_rotor_force(:)  ! N
    real(KIND=RP), allocatable      :: local_rotor_force_temp(:)  ! N
    real(KIND=RP), allocatable      :: local_root_bending(:)  ! N.m
    real(KIND=RP), allocatable      :: gauss_epsil_delta(:)  ! HO element size, for calculate force Gaussian
    real(KIND=RP)                   :: tip_c1,tip_c2  ! tip force corrections
    real(KIND=RP), allocatable      :: local_gaussian_sum(:) ! necessary for Gaussian weighted average
    real(KIND=RP), allocatable      :: local_Re(:) ! local Re based on local conditions and the chord of the airfoil at the blade section
    end type

    type turbine_t
    integer                        :: num_blades=3  ! number of blades -> hardcoded 3 blades
    real(KIND=RP)                  :: radius ! turb radius in mm
    real(KIND=RP)                  :: blade_pitch ! turb radius in rad
    real(KIND=RP)                  :: rot_speed ! rad/s
    real(KIND=RP)                  :: hub_cood_x,hub_cood_y,hub_cood_z ! hub height in mm
    real(KIND=RP)                  :: normal_x, normal_y, normal_z ! rotor normal pointing backward
    type(blade_t)                  :: blade_t(3) ! hardcoded 3 blades
    integer                        :: num_blade_sections  ! number of 2D section for Cl-Cd data
    real(KIND=RP)                  :: blade_torque(3) ! N.m
    real(KIND=RP)                  :: blade_thrust(3) ! N
    real(KIND=RP)                  :: blade_root_bending(3) !N.m
    real(KIND=RP)                  :: Cp ! turbine power coef.
    real(KIND=RP)                  :: Ct ! turbine thrust coef.
    real(KIND=RP), allocatable     :: average_conditions(:,:) ! time and blade average local variables at the blade section
    end type
                                   
    type Farm_t
    integer                        :: num_turbines
    type(turbine_t), allocatable   :: turbine_t(:)
    real(KIND=RP)                  :: gauss_epsil  ! force Gaussian shape
    integer                        :: epsilon_type
    logical                        :: calculate_with_projection
    logical                        :: active = .false.
    logical                        :: save_average = .false.
    logical                        :: save_instant = .false.
    logical                        :: verbose = .false.
    logical                        :: averageSubElement = .true.
    character(len=LINE_LENGTH)     :: file_name
    integer                        :: number_iterations
    integer                        :: save_iterations

   contains
        procedure   :: ConstructFarm
        procedure   :: DestructFarm
        procedure   :: UpdateFarm
        procedure   :: ForcesFarm             
        procedure   :: WriteFarmForces
        procedure   :: GaussianInterpolation
        procedure   :: FarmUpdateLocalForces
        procedure   :: FarmUpdateBladeForces
        procedure   :: FindActuatorPointElement
    end type

   Abstract Interface
    Function element_averageQ_f(mesh,eID,xi)
       use HexMeshClass
       use PhysicsStorage
       use NodalStorageClass
       use SMConstants
       implicit none

       type(HexMesh), intent(in)    :: mesh
       integer, intent(in)          :: eID 
       integer                      :: k, j, i
       real(kind=RP), dimension(NDIM), intent(in) :: xi

       real(kind=RP), dimension(NCONS)   :: element_averageQ_f
    End Function element_averageQ_f
   End Interface

    type(Farm_t)                  :: farm

    ! max 10 airfoils file names per section
    integer, parameter           :: MAX_AIRFOIL_FILES = 10
    procedure(element_averageQ_f), pointer :: element_averageQ

!  ========
contains
!  ========
!
!///////////////////////////////////////////////////////////////////////////////////////
!
   subroutine ConstructFarm(self, controlVariables, t0)
       use FTValueDictionaryClass
       use mainKeywordsModule, only: solutionFileNameKey
       use FileReadingUtilities      , only: getFileName
       use PhysicsStorage
       use fluiddata
       use MPI_Process_Info
       implicit none
       class(farm_t) , intent(inout)                :: self
       TYPE(FTValueDictionary), intent(in)          :: controlVariables
       real(kind=RP), intent(in)                    :: t0
!        ---------------
!        Local variables
!        ---------------
!
         integer     ::  i, j, k, ii, fid, io, n_aoa
         CHARACTER(LEN=LINE_LENGTH) :: arg, char1
         CHARACTER(LEN=LINE_LENGTH) :: solution_file
         real(kind=RP)              :: initial_azimutal

    if (.not. controlVariables % logicalValueForKey("use actuatorline")) return

    self % epsilon_type = controlVariables % getValueOrDefault("actuator epsilon type", 0)
    self % calculate_with_projection = controlVariables % getValueOrDefault("actuator calculate with projection", .false.)
    self % save_average = controlVariables % getValueOrDefault("actuator save average", .false.)
    self % save_instant = controlVariables % getValueOrDefault("actuator save instant", .false.)
    self % save_iterations = controlVariables % getValueOrDefault("actuator save iteration", 1)
    self % verbose = controlVariables % getValueOrDefault("actuator verbose", .false.)
    self % averageSubElement = controlVariables % getValueOrDefault("actuator average subelement", .true.)

    if (self % averageSubElement) then
        element_averageQ => semi_element_averageQ
    else
        element_averageQ => full_element_averageQ
    end if 

    arg='./ActuatorDef/Act_ActuatorDef.dat'
    OPEN( newunit = fid,file=trim(arg),status="old",action="read")

    READ(fid,'(A132)') char1
    READ(fid,'(A132)') char1
    READ(fid,'(A132)') char1
    READ(fid,'(A132)') char1
    READ(fid,*) self%num_turbines

    if (self % verbose .and. MPI_Process % isRoot) then
        print *,'-------------------------'
        print *,achar(27)//'[34m READING FARM DEFINITION'
        write(*,*) "Number of turbines in farm:", self%num_turbines
    endif

    READ(fid,'(A132)') char1

    allocate(self%turbine_t(self%num_turbines))

    do i = 1, self%num_turbines
       READ(fid,*) self%turbine_t(i)%hub_cood_x, self%turbine_t(i)%hub_cood_y, self%turbine_t(i)%hub_cood_z
    ENDDO

    READ(fid,'(A132)') char1

    do i = 1, self%num_turbines
       READ(fid,*) self%turbine_t(i)%radius
    ENDDO

    READ(fid,'(A132)') char1

    do i = 1, self%num_turbines
       READ(fid,*) self%turbine_t(i)%normal_x, self%turbine_t(i)%normal_y, self%turbine_t(i)%normal_z
    ENDDO
    
    READ(fid,'(A132)') char1

    do i = 1, self%num_turbines
       READ(fid,*) self%turbine_t(i)%rot_speed
    ENDDO

    READ(fid,'(A132)') char1

    do i = 1, self%num_turbines
       READ(fid,*) self%turbine_t(i)%blade_pitch
    ENDDO
    

    READ(fid,'(A132)') char1
    READ(fid,'(A132)') char1

    ! Read blade info, we assume all 3 blades are the same for one turbine
    READ(fid,*) self%turbine_t(1)%num_blade_sections

     associate (num_blade_sections => self%turbine_t(1)%num_blade_sections)

     READ(fid,'(A132)') char1

     do i=1, self%num_turbines
      do j=1, self%turbine_t(i)%num_blades
     allocate( self%turbine_t(i)%blade_t(j)%r_R(0:num_blade_sections),self%turbine_t(i)%blade_t(j)%chord(num_blade_sections), &
     self%turbine_t(i)%blade_t(j)%twist(num_blade_sections), &
     self%turbine_t(i)%blade_t(j)%num_airfoils(num_blade_sections), &
     self%turbine_t(i)%blade_t(j)%airfoil_files(num_blade_sections,MAX_AIRFOIL_FILES),self%turbine_t(i)%blade_t(j)%airfoil_t(num_blade_sections), &
     self%turbine_t(i)%blade_t(j)%local_velocity(num_blade_sections), self%turbine_t(i)%blade_t(j)%local_angle(num_blade_sections), &
     self%turbine_t(i)%blade_t(j)%local_lift(num_blade_sections), self%turbine_t(i)%blade_t(j)%local_drag(num_blade_sections), &
     self%turbine_t(i)%blade_t(j)%point_xyz_loc(num_blade_sections,3),self%turbine_t(i)%blade_t(j)%local_torque(num_blade_sections), &
     self%turbine_t(i)%blade_t(j)%local_thrust(num_blade_sections),self%turbine_t(i)%blade_t(j)%local_root_bending(num_blade_sections), &
     self%turbine_t(i)%blade_t(j)%local_rotor_force(num_blade_sections),self%turbine_t(i)%blade_t(j)%local_gaussian_sum(num_blade_sections), &
     self%turbine_t(i)%blade_t(j)%local_Re(num_blade_sections), self%turbine_t(1)%blade_t(j)%gauss_epsil_delta(num_blade_sections) )

         do k=1, num_blade_sections
            self%turbine_t(i)%blade_t(j)%airfoil_files(k,:)=' '
         enddo
      ENDDO
   enddo

    if (self%calculate_with_projection) then
        do i=1, self%num_turbines
          do j=1, self%turbine_t(i)%num_blades
         allocate( self%turbine_t(i)%blade_t(j)%local_thrust_temp(num_blade_sections), &
                   self%turbine_t(i)%blade_t(j)%local_rotor_force_temp(num_blade_sections) )
          enddo
       enddo
   end if

    endassociate

    self%turbine_t(1)%blade_t(1)%r_R(0) = 0.0_RP
   do i = 1, self%turbine_t(1)%num_blade_sections
      READ(fid,*) self%turbine_t(1)%blade_t(1)%r_R(i), self%turbine_t(1)%blade_t(1)%chord(i), &
                  self%turbine_t(1)%blade_t(1)%twist(i), self%turbine_t(1)%blade_t(1)%num_airfoils(i)
      
      ! one file per Re for each airfoil
      self%turbine_t(1)%blade_t(1)%airfoil_t(i)%num_Re = self%turbine_t(1)%blade_t(1)%num_airfoils(i)
                    
      do j = 1, self%turbine_t(1)%blade_t(1)%num_airfoils(i)   
            READ(fid,*) self%turbine_t(1)%blade_t(1)%airfoil_files(i,j)  
      enddo
   ENDDO


     ! all turbines have the same blades
     !do i=1, self%num_turbines
     !    do j=1, self%turbine_t(i)%num_blades
     !       self%turbine_t(i)%blade_t(j)=self%turbine_t(1)%blade_t(1)
     !    ENDDO
     ! enddo
     ! write(*,*) "All turbines have the same blades"
   !  write(*,*) self%turbine_t(1)%blade_t(1)%airfoil_files(2,2)

! read numerical parameters
     READ(fid,'(A132)') char1
     READ(fid,'(A132)') char1
     READ(fid,'(A132)') char1
     READ(fid,'(A132)') char1

     READ(fid,*) self%gauss_epsil

     READ(fid,'(A132)') char1
     READ(fid,*) self%turbine_t(1)%blade_t(1)%tip_c1,self%turbine_t(1)%blade_t(1)%tip_c2

     if (self % epsilon_type .eq. 2 .and. self % calculate_with_projection) then
     end if 

    close(fid)

    if (MPI_Process % isRoot) then

        call Subsection_Header("Actuator Line")
        write(STD_OUT,'(30X,A,A28,I0)') "->", "Number of turbines: ", self % num_turbines
        write(STD_OUT,'(30X,A,A28,I0)') "->", "Number of blade sections: ", self%turbine_t(1)%num_blade_sections

        select case (self % epsilon_type)
        case (0)
            write(STD_OUT,'(30X,A,A28,ES10.3)') "->", 'Fixed Epsilon value: ',self%gauss_epsil
        case (1)
            write(STD_OUT,'(30X,A,A)') "->", 'Epsilon calculated based on drag value'
        case (2)
            write(STD_OUT,'(30X,A)') 'Epsilon calculated based on element size and polynomial order'
            write(STD_OUT,'(30X,A,A28,F10.3)') "->", 'Constant for Epsilon: ',self%gauss_epsil
            if (self%calculate_with_projection) write(STD_OUT,'(30X,A)') 'Warining, epsilon calculated using properties of element 1'
        case default
            write(STD_OUT,'(30X,A,A28,ES10.3)') "->", 'Fixed Epsilon value: ',self%gauss_epsil
        end select
        write(STD_OUT,'(30X,A,A28,F10.3,F10.3)') "->", 'Tip correction constants: ', self%turbine_t(1)%blade_t(1)%tip_c1, self%turbine_t(1)%blade_t(1)%tip_c2
        write(STD_OUT,'(30X,A,A28,L1)') "->", "Projection formulation: ", self % calculate_with_projection
        if (.not. self%calculate_with_projection) write(STD_OUT,'(30X,A,A28,L1)') "->", "Average sub-Element: ", self % averageSubElement
        write(STD_OUT,'(30X,A,A28,L1)') "->", "Save blade average values: ", self % save_average
    end if


  do i = 1, self%turbine_t(1)%num_blade_sections
      arg=trim('./ActuatorDef/'//trim(self%turbine_t(1)%blade_t(1)%airfoil_files(i,1)))
      OPEN( newunit = fid,file=trim(arg),status="old",action="read")
      READ(fid,'(A132)') char1
      READ(fid,*) self%turbine_t(1)%blade_t(1)%airfoil_t(i)%num_aoa 
      close(fid)

      associate (num_re => self%turbine_t(1)%blade_t(1)%airfoil_t(i)%num_Re, num_aoa => self%turbine_t(1)%blade_t(1)%airfoil_t(i)%num_aoa)

         do ii=1, self%num_turbines
            do j=1, self%turbine_t(ii)%num_blades
                  allocate( self%turbine_t(ii)%blade_t(j)%airfoil_t(i)%aoa(num_aoa), &
                        self%turbine_t(ii)%blade_t(j)%airfoil_t(i)%cl(num_aoa,num_re), &
                        self%turbine_t(ii)%blade_t(j)%airfoil_t(i)%cd(num_aoa,num_re), &
                        self%turbine_t(ii)%blade_t(j)%airfoil_t(i)%Re(num_re) )
            enddo
         enddo

         do k = 1, num_re

            arg=trim('./ActuatorDef/'//trim(self%turbine_t(1)%blade_t(1)%airfoil_files(i,k)))
            OPEN( newunit = fid,file=trim(arg),status="old",action="read")
            READ(fid,'(A132)') char1

            READ(fid,*) n_aoa
            if ( n_aoa .ne. num_aoa ) then
                print *, "Error: not same number of AoA in all files for same blade section, file: ", trim(arg)
                call exit(99)
            end if

            READ(fid,'(A132)') char1
            READ(fid,*) self%turbine_t(1)%blade_t(1)%airfoil_t(i)%Re(k)

            if (self % verbose .and. MPI_Process % isRoot) then
                print *,'-------------------------'
                print *,achar(27)//'[34m READING FARM AIRFOIL DATA (Cl-Cd)'
                print*, 'reading: ', trim(arg)
                write(*,*) 'The number of AoA in the file is: ', num_aoa,' '//achar(27)//'[0m '    
            end if 
    
            READ(fid,'(A132)') char1

           do ii = 1,  num_aoa
                READ(fid,*) self%turbine_t(1)%blade_t(1)%airfoil_t(i)%aoa(ii), self%turbine_t(1)%blade_t(1)%airfoil_t(i)%cl(ii,k), &
                            self%turbine_t(1)%blade_t(1)%airfoil_t(i)%cd(ii,k)
                ! file is in deg, convert to rad
                self%turbine_t(1)%blade_t(1)%airfoil_t(i)%aoa(ii) = self%turbine_t(1)%blade_t(1)%airfoil_t(i)%aoa(ii) * PI / 180.0_RP
           enddo

           close(fid)
         end do ! number of airfoil files

      endassociate

    !all airfoils of all blades of all turbines are the same
    !do ii=1, self%num_turbines
    !  do j=1, self%turbine_t(i)%num_blades
    !     do k=1, self%turbine_t(1)%blade_t(1)%num_airfoils(j) 
    !     self%turbine_t(ii)%blade_t(j)%airfoil_t(k)=self%turbine_t(1)%blade_t(1)%airfoil_t(1)
    !     enddo
    !  ENDDO
    !enddo
      
 enddo ! number of blade sections

!$omp do schedule(runtime)private(i)
   do i = 1, self%turbine_t(1)%num_blade_sections
      self%turbine_t(1)%blade_t(1)%point_xyz_loc(i,1) =   self%turbine_t(1)%hub_cood_x
   enddo
!$omp end do

   !all airfoils of all blades of all turbines are the same
   do ii=1, self%num_turbines
      do j=1, self%turbine_t(ii)%num_blades 
         self%turbine_t(ii)%blade_t(j)=self%turbine_t(1)%blade_t(1)
         enddo
   enddo

    ! azimuthal angle for the 3 blades
    ! initial azimuthal angle valid for restaring a simulation with same rotational speed and refValues. Instant azimuthal angle is
    ! calculated with constant rot speed
    initial_azimutal = 0.0_RP
    ! azimuth_angle angle of blades is the angle to respect to +y axis, the angular velocity vector will point to +x
    self%turbine_t(:)%blade_t(1)%azimuth_angle = initial_azimutal
    self%turbine_t(:)%blade_t(2)%azimuth_angle = initial_azimutal + PI*2.0_RP/3.0_RP
    self%turbine_t(:)%blade_t(3)%azimuth_angle = initial_azimutal + PI*4.0_RP/3.0_RP

    ! average_conditions are thrust and rotor force
    if (MPI_Process % isRoot) then
      if (self % save_average) then
          if (self % calculate_with_projection) then
              allocate ( self%turbine_t(1)%average_conditions(self%turbine_t(1)%num_blade_sections,2) )
          else
              ! additional average_conditions are velocity, AoA and Re, writen before forces
              allocate ( self%turbine_t(1)%average_conditions(self%turbine_t(1)%num_blade_sections,5) )
          end if
          self % turbine_t(1) % average_conditions(:,:) = 0.0_RP
          self % number_iterations = 0
      end if
    end if
!
!   Get the solution file name
!   --------------------------
    solution_file = controlVariables % stringValueForKey( solutionFileNameKey, requestedLength = LINE_LENGTH )
    solution_file = trim(getFileName(solution_file))
    self % file_name = trim(solution_file)
!
!   Create output files
!   -------------------
    if (MPI_Process % isRoot) then
      write(arg , '(A,A)') trim(self%file_name) , "_Actuator_Line_Forces.dat"
      open ( newunit = fID , file = trim(arg) , status = "unknown" ,    action = "write" ) 
      write(fid,*) 'time, thrust_1, blade_torque_1, blade_root_bending_1,thrust_2, blade_torque_12 blade_root_bending_2,thrust_3, blade_torque_3, blade_root_bending_3'
      close(fid)
!
      write(arg , '(A,A)') trim(self%file_name) , "_Actuator_Line_CP_CT.dat"
      open ( newunit = fID , file = trim(arg) , status = "unknown" , action = "write" ) 
      write(fid,*) 'time, Cp (power coef.), Ct (thust coef.)'
      close(fid)
!
      if (self % save_average) then
          write(arg , '(A,A)') trim(self%file_name) , "_Actuator_Line_average.dat"
          open ( newunit = fID , file = trim(arg) , status = "unknown" , action = "write" ) 
          if (self % calculate_with_projection) then
              write(fid,*) 'R, Tangential_Force, Axial_Force'
          else
              write(fid,*) 'R, U, AoA, Re, Tangential_Force, Axial_Force'
          end if
          close(fid)
      end if
    end if
!
    self % active = .true.
!
   end subroutine ConstructFarm
!
!///////////////////////////////////////////////////////////////////////////////////////
   subroutine DestructFarm(self)
   implicit none
   class(Farm_t), intent(inout)       :: self

   ! integer                            :: i, j, k

   ! do i=1, self%num_turbines
   !  do j=1, self%turbine_t(j)%num_blades
   !      do k=1, self%turbine_t(i)%num_blade_sections
   !          deallocate ( self%turbine_t(i)%blade_t(j)%airfoil_t(k)%aoa, &
   !                  self%turbine_t(i)%blade_t(j)%airfoil_t(k)%Re, &
   !                  self%turbine_t(i)%blade_t(j)%airfoil_t(k)%cl, &
   !                  self%turbine_t(i)%blade_t(j)%airfoil_t(k)%cd )
   !      end do
   !      deallocate( self%turbine_t(i)%blade_t(j)%r_R, &
   !                  self%turbine_t(i)%blade_t(j)%chord, &
   !                  self%turbine_t(i)%blade_t(j)%twist, &
   !                  self%turbine_t(i)%blade_t(j)%local_velocity, &
   !                  self%turbine_t(i)%blade_t(j)%local_angle, &
   !                  self%turbine_t(i)%blade_t(j)%local_lift, &
   !                  self%turbine_t(i)%blade_t(j)%point_xyz_loc, &
   !                  self%turbine_t(i)%blade_t(j)%local_torque, &
   !                  self%turbine_t(i)%blade_t(j)%local_thrust, &
   !                  self%turbine_t(i)%blade_t(j)%local_rotor_force, &
   !                  self%turbine_t(i)%blade_t(j)%local_root_bending, &
   !                  self%turbine_t(i)%blade_t(j)%local_Re, &
   !                  self%turbine_t(i)%blade_t(j)%num_airfoils, &
   !                  self%turbine_t(i)%blade_t(j)%airfoil_files, &
   !                  self%turbine_t(i)%blade_t(j)%airfoil_t, &
   !                  self%turbine_t(i)%blade_t(j)%local_gaussian_sum, &
   !                  self%turbine_t(i)%blade_t(j)%local_thrust_temp )

   !  end do
   !  safedeallocate(self%turbine_t(i)%average_conditions)
   ! end do

   deallocate(self%turbine_t)

   end subroutine DestructFarm
!
!///////////////////////////////////////////////////////////////////////////////////////
!

   subroutine UpdateFarm(self,time, mesh)
   use fluiddata
   use HexMeshClass
   use PhysicsStorage
   use MPI_Process_Info
   implicit none

   class(Farm_t), intent(inout)      :: self
   real(kind=RP), intent(in)         :: time
   type(HexMesh), intent(in)         :: mesh

   !local variables
   integer                           :: ii, jj, i, j, k
   real(kind=RP)                     :: theta,t, interp, tolerance, delta_temp
   logical                           :: found, allfound
   integer                           :: eID, ierr
   real(kind=RP), dimension(NDIM)    :: x, xi
   real(kind=RP), dimension(NCONS)   :: Q, Qtemp
   real(kind=RP), dimension(:), allocatable  :: aoa

    if (.not. self % active) return

   t = time * Lref / refValues%V
   ! only for constant rot_speed
   theta = self%turbine_t(1)%rot_speed * t
   interp = 1.0_RP
   tolerance=0.2_RP*self%turbine_t(1)%radius

   projection_cond:if (self%calculate_with_projection) then

      delta_temp = (mesh % elements(1) % geom % Volume / product(mesh % elements(1) % Nxyz + 1)) ** (1.0_RP / 3.0_RP)
!
!    ----------------------------------------------------------------------------------
!    calculate for all mesh points its contribution based on the gaussian interpolation
!    ----------------------------------------------------------------------------------
!
!$omp do schedule(runtime)private(ii,jj)
      do jj = 1, self%turbine_t(1)%num_blades

         self%turbine_t(1)%blade_t(jj)%local_lift(:) = 0.0_RP
         self%turbine_t(1)%blade_t(jj)%local_drag(:) = 0.0_RP
         self%turbine_t(1)%blade_t(jj)%local_rotor_force(:) = 0.0_RP
         self%turbine_t(1)%blade_t(jj)%local_rotor_force_temp(:) = 0.0_RP
         self%turbine_t(1)%blade_t(jj)%local_thrust(:) = 0.0_RP
         self%turbine_t(1)%blade_t(jj)%local_thrust_temp(:)=0.0_RP
         self%turbine_t(1)%blade_t(jj)%local_torque(:) = 0.0_RP
         self%turbine_t(1)%blade_t(jj)%local_root_bending(:) = 0.0_RP
         self%turbine_t(1)%blade_t(jj)%local_gaussian_sum(:)= 0.0_RP
      
             do ii = 1, self%turbine_t(1)%num_blade_sections
                ! y,z coordinate of every acutator line point
                self%turbine_t(1)%blade_t(jj)%point_xyz_loc(ii,2) = self%turbine_t(1)%hub_cood_y + self%turbine_t(1)%blade_t(jj)%r_R(ii) * cos(theta+self%turbine_t(1)%blade_t(jj)%azimuth_angle)
                self%turbine_t(1)%blade_t(jj)%point_xyz_loc(ii,3) = self%turbine_t(1)%hub_cood_z + self%turbine_t(1)%blade_t(jj)%r_R(ii) * sin(theta+self%turbine_t(1)%blade_t(jj)%azimuth_angle)

                self % turbine_t(1) % blade_t(jj) % gauss_epsil_delta(ii) = delta_temp
      
              end do
           enddo
!$omp end do
!

! no projection
   else projection_cond
!
!    ----------------------------------------------------------------
!    use the local Q based on the position of the actuator line point
!    ----------------------------------------------------------------
!
!$omp do schedule(runtime)private(ii,jj,eID,Q,Qtemp,delta_temp,xi,found)
     do jj = 1, self%turbine_t(1)%num_blades
       self%turbine_t(1)%blade_t(jj)%local_lift(:) = 0.0_RP
       self%turbine_t(1)%blade_t(jj)%local_drag(:) = 0.0_RP
       self%turbine_t(1)%blade_t(jj)%local_rotor_force(:) = 0.0_RP
       self%turbine_t(1)%blade_t(jj)%local_thrust(:) = 0.0_RP
       self%turbine_t(1)%blade_t(jj)%local_torque(:) = 0.0_RP
       self%turbine_t(1)%blade_t(jj)%local_root_bending(:) = 0.0_RP
!
       do ii = 1, self%turbine_t(1)%num_blade_sections
          ! y,z coordinate of every acutator line point
          self%turbine_t(1)%blade_t(jj)%point_xyz_loc(ii,2) = self%turbine_t(1)%hub_cood_y + self%turbine_t(1)%blade_t(jj)%r_R(ii) * cos(theta+self%turbine_t(1)%blade_t(jj)%azimuth_angle)
          self%turbine_t(1)%blade_t(jj)%point_xyz_loc(ii,3) = self%turbine_t(1)%hub_cood_z + self%turbine_t(1)%blade_t(jj)%r_R(ii) * sin(theta+self%turbine_t(1)%blade_t(jj)%azimuth_angle)
!
!         -----------------------------------
!         get the elements of each line point
!         -----------------------------------
!
          x = [self%turbine_t(1)%blade_t(jj)%point_xyz_loc(ii,1),self%turbine_t(1)%blade_t(jj)%point_xyz_loc(ii,2),self%turbine_t(1)%blade_t(jj)%point_xyz_loc(ii,3)]
          ! found = mesh % FindPointWithCoords(x, eID, xi)
          call self % FindActuatorPointElement(mesh, x, tolerance, eID, xi, found)
          if (found) then
            ! averaged state values of the cell
            Qtemp = element_averageQ(mesh,eID,xi)
            ! Qtemp = semi_element_averageQ(mesh,eID,xi)
            delta_temp = (mesh % elements(eID) % geom % Volume / product(mesh % elements(eID) % Nxyz + 1)) ** (1.0_RP / 3.0_RP)
          else
            Qtemp = 0.0_RP
            delta_temp = 0.0_RP
          end if
          if ( (MPI_Process % doMPIAction) ) then
#ifdef _HAS_MPI_
            call mpi_allreduce(Qtemp, Q, NCONS, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD, ierr)
            call mpi_allreduce(delta_temp, self%turbine_t(1)%blade_t(jj)%gauss_epsil_delta(ii), 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD, ierr)
#endif
          else
              Q = Qtemp
              self % turbine_t(1) % blade_t(jj) % gauss_epsil_delta(ii) = delta_temp

          end if
          if (all(Q .eq. 0.0_RP)) then
            print*, "Actuator line point not found in mesh, x: ", x
            call exit(99)
          end if
          call FarmUpdateLocalForces(self, ii, jj, Q, theta, interp)
        end do
     enddo
!$omp end do

   end if projection_cond
!
   end subroutine UpdateFarm
!
!///////////////////////////////////////////////////////////////////////////////////////
!
   subroutine ForcesFarm(self, x, Q, NS, time)
   use PhysicsStorage
   use fluiddata
   implicit none

   class(Farm_t) , intent(inout)     :: self
   real(kind=RP),intent(in)          :: x(NDIM)
   real(kind=RP),intent(in)          :: Q(NCONS)
   real(kind=RP),intent(inout)       :: NS(NCONS)
   real(kind=RP),intent(in)          :: time

! local vars
   real(kind=RP)                     :: tolerance, Non_dimensional, t, theta, interp, local_gaussian
   integer                           :: ii,jj, LAST_SECTION
   real(kind=RP), dimension(NDIM)    :: actuator_source

    if (.not. self % active) return

    ! 20% of the radius for max of the rotor thickness
    tolerance=0.2_RP*self%turbine_t(1)%radius

        ! turbine is pointing backwards as x positive
if( POW2(x(2)-self%turbine_t(1)%hub_cood_y)+POW2(x(3)-self%turbine_t(1)%hub_cood_z) <= POW2(self%turbine_t(1)%radius+tolerance) &
    .and. (x(1) < self%turbine_t(1)%hub_cood_x+tolerance .and. x(1)>self%turbine_t(1)%hub_cood_x-tolerance)) then

      Non_dimensional = POW2(refValues % V) * refValues % rho / Lref
      t = time * Lref / refValues % V

      theta = self%turbine_t(1)%rot_speed * t

      actuator_source(:) = 0.0_RP
      local_gaussian=0.0_RP

 if (self%calculate_with_projection) then

 
   do jj = 1, self%turbine_t(1)%num_blades
      do ii = 1, self%turbine_t(1)%num_blade_sections
          interp = GaussianInterpolation(self, ii, jj, x)
          call FarmUpdateLocalForces(self, ii, jj,  Q, theta, interp)

          ! minus account action-reaction effect, is the force on the fliud
          actuator_source(1) = actuator_source(1) - self%turbine_t(1)%blade_t(jj)%local_thrust(ii) 
          actuator_source(2) = actuator_source(2) - (-self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii)*sin(self%turbine_t(1)%rot_speed*t + self%turbine_t(1)%blade_t(jj)%azimuth_angle) )
          actuator_source(3) = actuator_source(3) - self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii)*cos(self%turbine_t(1)%rot_speed*t + self%turbine_t(1)%blade_t(jj)%azimuth_angle) 

          !acumulate in temporal variables, for each time step as the non temp are recalculated for each element
          self%turbine_t(1)%blade_t(jj)%local_thrust_temp(ii)=self%turbine_t(1)%blade_t(jj)%local_thrust_temp(ii)+self%turbine_t(1)%blade_t(jj)%local_thrust(ii)
          self%turbine_t(1)%blade_t(jj)%local_rotor_force_temp(ii)=self%turbine_t(1)%blade_t(jj)%local_rotor_force_temp(ii)+self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii)

         self%turbine_t(1)%blade_t(jj)%local_gaussian_sum(ii)=self%turbine_t(1)%blade_t(jj)%local_gaussian_sum(ii)+interp
     
         local_gaussian=local_gaussian+interp

      enddo
  enddo
  
     ! actuator_source(:)=actuator_source(:)/local_gaussian
    
    NS(IRHOU:IRHOW) = NS(IRHOU:IRHOW) + actuator_source(:) / Non_dimensional



else ! no projection

        ! LAST_SECTION=self%turbine_t(1)%num_blade_sections

       do jj = 1, self%turbine_t(1)%num_blades
          
          do ii = 1, self%turbine_t(1)%num_blade_sections

            interp = GaussianInterpolation(self, ii, jj, x)
    
            ! minus account action-reaction effect, is the force on the fliud
            actuator_source(1) = actuator_source(1) - self%turbine_t(1)%blade_t(jj)%local_thrust(ii) * interp
            actuator_source(2) = actuator_source(2) - (-self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii)*sin(self%turbine_t(1)%rot_speed*t + self%turbine_t(1)%blade_t(jj)%azimuth_angle) )
            actuator_source(3) = actuator_source(3) - self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii)*cos(self%turbine_t(1)%rot_speed*t + self%turbine_t(1)%blade_t(jj)%azimuth_angle) 

            !local_gaussian=local_gaussian+interp

         enddo
     enddo
     
        !actuator_source(:)=actuator_source(:)/local_gaussian

       NS(IRHOU:IRHOW) = NS(IRHOU:IRHOW) + actuator_source(:) / Non_dimensional

    endif
    
   endif

   end subroutine  ForcesFarm
!
!///////////////////////////////////////////////////////////////////////////////////////
!
   subroutine WriteFarmForces(self,time,iter,last)
   use fluiddata
   use PhysicsStorage
   use MPI_Process_Info
   implicit none

   class(Farm_t), intent(inout)  :: self
   real(kind=RP),intent(in)      :: time
   integer, intent(in)           :: iter
   logical, optional             :: last
   integer                       :: fid, io
   CHARACTER(LEN=LINE_LENGTH)    :: arg
   real(kind=RP)                 :: t
   integer                       :: ii, jj
   logical                       :: saveAverage
   logical                       :: save_instant
   integer                       :: ierr
   real(kind=RP), dimension(:), allocatable :: local_thrust_temp, local_rotor_force_temp, local_gaussian_sum

   if (.not. self % active) return

   if (present(last)) then
       saveAverage = last
   else
       saveAverage = .false.
   end if

   save_instant = self%save_instant .and. ( mod(iter,self % save_iterations) .eq. 0 )
   t = time * Lref / refValues%V

   
   if (self%calculate_with_projection) then
     ! this is necessary for Gaussian weighted sum
     
     do jj = 1, self%turbine_t(1)%num_blades
         self%turbine_t(1)%blade_t(jj)%local_thrust(:) = 0.0_RP
         self%turbine_t(1)%blade_t(jj)%local_rotor_force(:) = 0.0_RP
     end do
  
     if ( (MPI_Process % doMPIAction) ) then
       associate (num_blade_sections => self%turbine_t(1)%num_blade_sections)
         allocate( local_thrust_temp(num_blade_sections), local_rotor_force_temp(num_blade_sections), local_gaussian_sum(num_blade_sections) )

         do jj = 1, self%turbine_t(1)%num_blades
           local_thrust_temp = self%turbine_t(1)%blade_t(jj)%local_thrust_temp
           local_rotor_force_temp = self%turbine_t(1)%blade_t(jj)%local_rotor_force_temp
           local_gaussian_sum = self%turbine_t(1)%blade_t(jj)%local_gaussian_sum
  
#ifdef _HAS_MPI_
                call mpi_allreduce(local_thrust_temp, self%turbine_t(1)%blade_t(jj)%local_thrust_temp, num_blade_sections, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD, ierr)
                call mpi_allreduce(local_rotor_force_temp, self%turbine_t(1)%blade_t(jj)%local_rotor_force_temp, num_blade_sections, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD, ierr)
                call mpi_allreduce(local_gaussian_sum, self%turbine_t(1)%blade_t(jj)%local_gaussian_sum, num_blade_sections, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD, ierr)
#endif
  
         end do
       endassociate
     end if
         
!$omp do schedule(runtime)private(ii,jj)
        do jj = 1, self%turbine_t(1)%num_blades
             do ii = 1, self%turbine_t(1)%num_blade_sections
     
                 self%turbine_t(1)%blade_t(jj)%local_thrust(ii)=self%turbine_t(1)%blade_t(jj)%local_thrust(ii)+self%turbine_t(1)%blade_t(jj)%local_thrust_temp(ii)/self%turbine_t(1)%blade_t(jj)%local_gaussian_sum(ii)
                 self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii)=self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii)+self%turbine_t(1)%blade_t(jj)%local_rotor_force_temp(ii)/self%turbine_t(1)%blade_t(jj)%local_gaussian_sum(ii)
     
             enddo
         enddo
!$omp end do
     
   end if

   if ( .not. MPI_Process % isRoot ) return

   ! save in memory the time step forces for each element blade and the whole blades
   call self % FarmUpdateBladeForces()

!write output torque thrust to file
      write(arg , '(A,A)') trim(self%file_name) , "_Actuator_Line_Forces.dat"

      open( newunit = fID , file = trim(arg) , action = "write" , access = "append" , status = "old" )
      write(fid,"(10(2X,ES24.16))") t, &
      self%turbine_t(1)%blade_thrust(1),self%turbine_t(1)%blade_torque(1),self%turbine_t(1)%blade_root_bending(1), &
      self%turbine_t(1)%blade_thrust(2),self%turbine_t(1)%blade_torque(2),self%turbine_t(1)%blade_root_bending(2), &
      self%turbine_t(1)%blade_thrust(3),self%turbine_t(1)%blade_torque(3),self%turbine_t(1)%blade_root_bending(3)
      close(fid)

      write(arg , '(A,A)') trim(self%file_name) , "_Actuator_Line_CP_CT.dat"
      open( newunit = fID , file = trim(arg) , action = "write" , access = "append" , status = "old" )
      write(fid,"(10(2X,ES24.16))") t, self%turbine_t(1)%Cp, self%turbine_t(1)%Ct
      close(fid)

    if (self % save_average .and. saveAverage) then
      write(arg , '(A,A)') trim(self%file_name) , "_Actuator_Line_average.dat"
      open( newunit = fID , file = trim(arg) , action = "write" , access = "append" , status = "old" )
      if (self%calculate_with_projection) then
          do ii = 1, self % turbine_t(1) % num_blade_sections
              write(fid,"(3(2X,ES24.16))") self%turbine_t(1)%blade_t(1)%r_R(ii), self%turbine_t(1)%average_conditions(ii,:)
          end do
      else
          do ii = 1, self % turbine_t(1) % num_blade_sections
              write(fid,"(6(2X,ES24.16))") self%turbine_t(1)%blade_t(1)%r_R(ii), self%turbine_t(1)%average_conditions(ii,:)
          end do
      end if 
      close(fid)
    end if

     if (save_instant) then
         do jj = 1, self%turbine_t(1)%num_blades
          write(arg , '(2A,I3.3,A,I10.10,A)') trim(self%file_name) , "_Actuator_Line_instant_",jj ,"_" ,iter, ".dat"
          open ( newunit = fID , file = trim(arg) , status = "unknown" , action = "write" ) 
          write(fid,*) 'R, U, AoA, Re'
          do ii = 1, self % turbine_t(1) % num_blade_sections
              write(fid,"(4(2X,ES24.16))") self%turbine_t(1)%blade_t(1)%r_R(ii), &
                 self%turbine_t(1)%blade_t(jj)%local_velocity(ii), &
                 ( self%turbine_t(1)%blade_t(jj)%local_angle(ii) - (self%turbine_t(1)%blade_t(jj)%twist(ii) + self%turbine_t(1)%blade_pitch) ) * 180.0_RP / PI, &
                 self%turbine_t(1)%blade_t(jj)%local_Re(ii)
          end do
          close(fid)
         end do
     end if 
  !endif

end subroutine WriteFarmForces
!
!///////////////////////////////////////////////////////////////////////////////////////
!
    Subroutine FarmUpdateLocalForces(self, ii, jj, Q, theta, interp)
        use PhysicsStorage
        use fluiddata
        use VariableConversion, only: Temperature, SutherlandsLaw
        implicit none
        class(Farm_t)                                 :: self
        integer, intent(in)                           :: ii, jj
        real(kind=RP), dimension(NCONS), intent(in)   :: Q
        real(kind=RP), intent(in)                     :: theta
        real(kind=RP), intent(in)                     :: interp

        !local variables
        real(kind=RP)                                 :: density, Cl, Cd, aoa, g1_func, tip_correct, angle_temp
        real(kind=RP)                                 :: wind_speed_axial, wind_speed_rot
        real(kind=RP)                                 :: lift_force, drag_force
        real(kind=RP)                                 :: T, muL
!
!       -----------------------------
!       get airfoil related variables
!       -----------------------------
!
        ! option 1, not recommended, ignore LES velocity directions, just use U0 in x-direction
        ! wind_speed_axial =  refValues % V ! wind goes in the x-direction
        ! wind_speed_axial = sqrt( POW2(Q(IRHOU)) + POW2(Q(IRHOV)) ) / Q(IRHO) * refValues_%V ! wind goes in the x-direction
        ! wind_speed_rot = 0.0_RP

        ! option 2, project [v.w] in the rotational direction (theta in cylindrical coordinates)
        wind_speed_axial = (Q(IRHOU)/Q(IRHO)) * refValues % V ! our x is the z in cylindrical
        wind_speed_rot = ( -Q(IRHOV)*sin(theta+self%turbine_t(1)%blade_t(jj)%azimuth_angle) + Q(IRHOW)*cos(theta+self%turbine_t(1)%blade_t(jj)%azimuth_angle) ) / Q(IRHO) * refValues % V

        density = Q(IRHO) * refValues % rho

        tip_correct = 1.0_RP
        aoa = 0.0_RP

         T     = Temperature(Q)
         muL = SutherlandsLaw(T) * refValues % mu

        self%turbine_t(1)%blade_t(jj)%local_velocity(ii) = sqrt( POW2(self%turbine_t(1)%rot_speed*self%turbine_t(1)%blade_t(jj)%r_R(ii) - wind_speed_rot) + &
                                                                 POW2(wind_speed_axial) )
        self%turbine_t(1)%blade_t(jj)%local_angle(ii) = atan( wind_speed_axial / (self%turbine_t(1)%rot_speed*self%turbine_t(1)%blade_t(jj)%r_R(ii) - wind_speed_rot) ) 

        ! alpha = phi - gamma, gamma = blade pitch + airfoil local twist
        aoa = self%turbine_t(1)%blade_t(jj)%local_angle(ii) - (self%turbine_t(1)%blade_t(jj)%twist(ii) + self%turbine_t(1)%blade_pitch) 
        self%turbine_t(1)%blade_t(jj)%local_Re(ii) = self%turbine_t(1)%blade_t(jj)%local_velocity(ii) * self%turbine_t(1)%blade_t(jj)%chord(ii) * density / muL
        call Get_Cl_Cl_from_airfoil_data(self%turbine_t(1)%blade_t(jj)%airfoil_t(ii), aoa, self%turbine_t(1)%blade_t(jj)%local_Re(ii), Cl, Cd)
!
!       ---------------
!       tip correction
!       ---------------
!
        g1_func=exp(-self%turbine_t(1)%blade_t(1)%tip_c1*(self%turbine_t(1)%num_blades*self%turbine_t(1)%rot_speed*self%turbine_t(1)%radius/refValues%V-self%turbine_t(1)%blade_t(1)%tip_c2))+0.1
        ! in this it should be the local_angle at the tip

        ! without perturbation
        ! angle_temp = atan(refValues%V/(self%turbine_t(1)%rot_speed*self%turbine_t(1)%radius))
        ! only axial wind speed
        ! angle_temp = atan(wind_speed_axial/(self%turbine_t(1)%rot_speed*self%turbine_t(1)%radius))

        ! 1 possibility: use the global radius and angle (i.e. the tip values)
        ! tip_correct = 2.0_RP/PI*(acos( exp(-g1_func*self%turbine_t(1)%num_blades*(self%turbine_t(1)%radius-self%turbine_t(1)%blade_t(jj)%r_R(ii)) / &
                      ! (2.0_RP*self%turbine_t(1)%radius*sin(angle_temp))) ))

        ! 2 possibility: use the local radius and angle
        tip_correct = 2.0_RP/PI*(acos( exp(-g1_func*self%turbine_t(1)%num_blades*(self%turbine_t(1)%radius-self%turbine_t(1)%blade_t(jj)%r_R(ii)) / abs(2.0_RP*self%turbine_t(1)%blade_t(jj)%r_R(ii)*sin(self%turbine_t(1)%blade_t(jj)%local_angle(ii)))) ))
!
!       --------------------------------
!       Save forces on the blade segment
!       --------------------------------
!
         ! lift=Cl*1/2.rho*v_local^2*Surface ; and surface=section area of the blade S=length*chord
         ! lift and drag are multiply by the gaussian interp for the case of each mesh node contributing to the force (for local only is 1)

         lift_force = 0.5_RP * density * Cl * tip_correct * POW2(self%turbine_t(1)%blade_t(jj)%local_velocity(ii)) &
                      * self%turbine_t(1)%blade_t(jj)%chord(ii) * (self%turbine_t(1)%blade_t(jj)%r_R(ii) - self%turbine_t(1)%blade_t(jj)%r_R(ii-1)) * interp

        drag_force = 0.5_RP * density * Cd * tip_correct * POW2(self%turbine_t(1)%blade_t(jj)%local_velocity(ii)) &
                      * self%turbine_t(1)%blade_t(jj)%chord(ii) * (self%turbine_t(1)%blade_t(jj)%r_R(ii) - self%turbine_t(1)%blade_t(jj)%r_R(ii-1)) * interp

        self%turbine_t(1)%blade_t(jj)%local_lift(ii) =  lift_force
        self%turbine_t(1)%blade_t(jj)%local_drag(ii) =  drag_force

        self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii) = lift_force * sin(self%turbine_t(1)%blade_t(jj)%local_angle(ii)) &
                                                              - drag_force * cos(self%turbine_t(1)%blade_t(jj)%local_angle(ii))
                                  
        self%turbine_t(1)%blade_t(jj)%local_thrust(ii) = lift_force * cos(self%turbine_t(1)%blade_t(jj)%local_angle(ii)) & 
                                                         + drag_force * sin(self%turbine_t(1)%blade_t(jj)%local_angle(ii))


            !print *, "wind_speed_axial: ", wind_speed_axial
            !print *, "wind_speed_rot: ", wind_speed_rot
            !print *, "self%turbine_t(1)%blade_t(1)%local_velocity(ii): ", self%turbine_t(1)%blade_t(1)%local_velocity(ii)
            !print *, "self%turbine_t(1)%blade_t(1)%local_angle(ii): ", self%turbine_t(1)%blade_t(1)%local_angle(ii)
            !print *, "aoa: ", aoa
            !print *, "vrel: ", self%turbine_t(1)%blade_t(jj)%local_velocity(ii)
            !print *, "Cl: ", Cl
            !print *, "Cd: ", Cd
            !print *, "density: ", density
            !print *, "interp: ", interp
            !print *, "tip_correct: ", tip_correct
            !print *, "other: ", self%turbine_t(1)%blade_t(jj)%chord(ii) * (self%turbine_t(1)%blade_t(jj)%r_R(ii) - self%turbine_t(1)%blade_t(jj)%r_R(ii-1))
            !print *, "lift_force: ", lift_force
            !print *, "drag_force: ", drag_force
            !print *, "rotor_force: ", self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii)
            !print *, "local_thrust: ", self%turbine_t(1)%blade_t(jj)%local_thrust(ii)
!!
    End Subroutine FarmUpdateLocalForces
!
!///////////////////////////////////////////////////////////////////////////////////////
!
    Subroutine FarmUpdateBladeForces(self)
        use fluiddata
        Implicit None

        class(Farm_t), intent(inout)      :: self
        !local variables
        integer                           :: ii, jj, i, j, k
        real(kind=RP), dimension(:), allocatable  :: aoa
!
!       ------------------------------
!       Save forces on the whole blade
!       ------------------------------
!
!$omp do schedule(runtime)private(ii,jj)
     do jj = 1, self%turbine_t(1)%num_blades
          self%turbine_t(1)%blade_thrust(jj) = 0.0_RP
          self%turbine_t(1)%blade_torque(jj) = 0.0_RP
          self%turbine_t(1)%blade_root_bending(jj) = 0.0_RP

          do ii = 1, self%turbine_t(1)%num_blade_sections

              self%turbine_t(1)%blade_t(jj)%local_torque(ii) = self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii)*self%turbine_t(1)%blade_t(jj)%r_R(ii)

              self%turbine_t(1)%blade_t(jj)%local_root_bending(ii) = sqrt(POW2(self%turbine_t(1)%blade_t(jj)%local_thrust(ii)) + &
                  POW2(self%turbine_t(1)%blade_t(jj)%local_rotor_force(ii))) * self%turbine_t(1)%blade_t(jj)%r_R(ii)

              self%turbine_t(1)%blade_thrust(jj)=self%turbine_t(1)%blade_thrust(jj)+self%turbine_t(1)%blade_t(jj)%local_thrust(ii)
              self%turbine_t(1)%blade_torque(jj)=self%turbine_t(1)%blade_torque(jj)+self%turbine_t(1)%blade_t(jj)%local_torque(ii)
              self%turbine_t(1)%blade_root_bending(jj)=self%turbine_t(1)%blade_root_bending(jj)+self%turbine_t(1)%blade_t(jj)%local_root_bending(ii)
         enddo
     enddo
!$omp end do

     self%turbine_t(1)%Cp = 2.0_RP * (self%turbine_t(1)%blade_torque(1)+self%turbine_t(1)%blade_torque(2)+self%turbine_t(1)%blade_torque(3)) * self%turbine_t(1)%rot_speed / &
                           (refValues%rho * POW3(refValues%V) * pi * POW2(self%turbine_t(1)%radius))

     self%turbine_t(1)%Ct = 2.0_RP * (self%turbine_t(1)%blade_thrust(1)+self%turbine_t(1)%blade_thrust(2)+self%turbine_t(1)%blade_thrust(3)) / &
                           (refValues%rho * POW2(refValues%V) * pi * POW2(self%turbine_t(1)%radius))
!
!        ----------------------
!        Save average variables
!        ----------------------
!
     if (self % save_average) then
         self % turbine_t(1) % average_conditions = self % turbine_t(1) % average_conditions * real(self % number_iterations,RP)

         !saving only for blade 1
         jj =1
         if (self%calculate_with_projection) then
             self % turbine_t(1) % average_conditions(:,1) = self % turbine_t(1) % average_conditions(:,1) + self%turbine_t(1)%blade_t(jj)%local_rotor_force
             self % turbine_t(1) % average_conditions(:,2) = self % turbine_t(1) % average_conditions(:,2) + self%turbine_t(1)%blade_t(jj)%local_thrust
         else
             allocate(aoa(self%turbine_t(1)%num_blade_sections))
             aoa = self%turbine_t(1)%blade_t(jj)%local_angle(:) - (self%turbine_t(1)%blade_t(jj)%twist(:) + self%turbine_t(1)%blade_pitch) 
             self % turbine_t(1) % average_conditions(:,1) = self % turbine_t(1) % average_conditions(:,1) + self%turbine_t(1)%blade_t(jj)%local_velocity
             self % turbine_t(1) % average_conditions(:,2) = self % turbine_t(1) % average_conditions(:,2) + aoa * 180.0_RP / PI
             self % turbine_t(1) % average_conditions(:,3) = self % turbine_t(1) % average_conditions(:,3) + self%turbine_t(1)%blade_t(jj)%local_Re
             self % turbine_t(1) % average_conditions(:,4) = self % turbine_t(1) % average_conditions(:,4) + self%turbine_t(1)%blade_t(jj)%local_rotor_force
             self % turbine_t(1) % average_conditions(:,5) = self % turbine_t(1) % average_conditions(:,5) + self%turbine_t(1)%blade_t(jj)%local_thrust
             deallocate(aoa)
         end if

         self % number_iterations = self % number_iterations + 1
         self % turbine_t(1) % average_conditions = self % turbine_t(1) % average_conditions / real(self % number_iterations,RP)
     end if 
!
    End Subroutine FarmUpdateBladeForces
!
!///////////////////////////////////////////////////////////////////////////////////////
!
    Function GaussianInterpolation(self, ii, jj, x, Cd)
        implicit none
        class(Farm_t), intent(in)               :: self
        integer, intent(in)                     :: ii, jj
        real(kind=RP), intent(in)               :: x(NDIM)
        real(kind=RP), intent(in), optional     :: Cd
        real(kind=RP)                           :: GaussianInterpolation

        !local variables
        real(kind=RP)                           :: epsil

        select case (self % epsilon_type)
        case (0)
! EPSILON - option 1 (from file)
            epsil = self % gauss_epsil
        case (1)
! EPSILON - option 2
            if (present(Cd)) then
                epsil = max(self%turbine_t(1)%blade_t(jj)%chord(ii)/4.0_RP,self%turbine_t(1)%blade_t(jj)%chord(ii)*Cd/2.0_RP)
            else
                epsil = self % gauss_epsil
            end if
        case (2)
! EPSILON - option 3 (k is from file)
! eps = k*delta; k is in gauss_epsil, gauss_epsil_delta is obtained in UpdateFarm
            epsil = self % gauss_epsil * self % turbine_t(1) % blade_t(jj) % gauss_epsil_delta(ii)
        case default
            epsil = self % gauss_epsil
        end select

        GaussianInterpolation = exp( -(POW2(x(1) - self%turbine_t(1)%blade_t(jj)%point_xyz_loc(ii,1)) + &
                  POW2(x(2) - self%turbine_t(1)%blade_t(jj)%point_xyz_loc(ii,2)) + POW2(x(3) - self%turbine_t(1)%blade_t(jj)%point_xyz_loc(ii,3))) / POW2(epsil) ) / ( POW3(epsil) * pi**(3.0_RP/2.0_RP) )

    End Function GaussianInterpolation
!
!///////////////////////////////////////////////////////////////////////////////////////
!
! based on HexMesh_FindPointWithCoords, without curvature and with tolerance
    Subroutine FindActuatorPointElement(self, mesh, x, tolerance, eID, xi, success)
       use HexMeshClass
       Implicit None

       class(Farm_t), intent(inout)                  :: self
       type(HexMesh), intent(in)                     :: mesh
       real(kind=RP), dimension(NDIM), intent(in)    :: x       ! physical space
       real(kind=RP), intent(in)                     :: tolerance
       integer, intent(out)                          :: eID 
       real(kind=RP), dimension(NDIM), intent(out)   :: xi      ! computational space
       logical, intent(out)                          :: success
       !
       logical                                       :: found

       success = .false.

       if( POW2(x(2)-self%turbine_t(1)%hub_cood_y)+POW2(x(3)-self%turbine_t(1)%hub_cood_z) > POW2(self%turbine_t(1)%radius+tolerance) &
            .or. (x(1) > self%turbine_t(1)%hub_cood_x+tolerance .or. x(1) < self%turbine_t(1)%hub_cood_x-tolerance)) return
!
!      Search in linear (not curved) mesh (faster and safer)
!      For AL the mesh is expected to be linear
!      -----------------------------------------------------
       do eID = 1, mesh % no_of_elements
          found = mesh % elements(eID) % FindPointInLinElement(x, mesh % nodes)
          if ( found ) exit
       end do
!
!      If found in linear mesh, use FindPointWithCoords in that element and, if necessary, in neighbors...
!        ---------------------------------------------------------------------------------------------------
       if (eID <= mesh % no_of_elements) then
          found = mesh % FindPointWithCoordsInNeighbors(x, xi, eID, 2)
          if ( found ) then
             success = .true.
             return
          end if
       end if

    End Subroutine FindActuatorPointElement
!
!///////////////////////////////////////////////////////////////////////////////////////
!
    subroutine Get_Cl_Cl_from_airfoil_data(airfoil, aoa, Re, Cl_out, Cd_out)
         implicit none
      
         type (airfoil_t), intent(in)   :: airfoil
         real(KIND=RP), intent(in)      :: aoa, Re
         real(KIND=RP), intent(out)     :: Cl_out, Cd_out
         integer                        :: i,k
         real(kind=RP), dimension(2)    :: Cl_inter, Cd_inter
               
         Cl_out=0.0_RP
         Cd_out=0.0_RP

         if (airfoil%num_Re .eq. 1) then
             do i=1, airfoil % num_aoa-1
                 if (airfoil%aoa(i+1)>=aoa .and. airfoil%aoa(i)<=aoa ) then
                    Cl_out=InterpolateAirfoilData(airfoil%aoa(i),airfoil%aoa(i+1),airfoil%cl(i,1),airfoil%cl(i+1,1),aoa)
                    Cd_out=InterpolateAirfoilData(airfoil%aoa(i),airfoil%aoa(i+1),airfoil%cd(i,1),airfoil%cd(i+1,1),aoa)
                    exit
                 endif
             end do
        
         else

             do k=1, airfoil % num_Re-1
                 if (airfoil%Re(k+1)>=Re .and. airfoil%Re(k)<=Re ) then
                     do i=1, airfoil % num_aoa-1
                         if (airfoil%aoa(i+1)>=aoa .and. airfoil%aoa(i)<=aoa ) then

                            Cl_inter(1) = InterpolateAirfoilData(airfoil%aoa(i),airfoil%aoa(i+1),airfoil%cl(i,k),airfoil%cl(i+1,k),aoa)
                            Cl_inter(2) = InterpolateAirfoilData(airfoil%aoa(i),airfoil%aoa(i+1),airfoil%cl(i,k+1),airfoil%cl(i+1,k+1),aoa)
                            Cl_out=InterpolateAirfoilData(airfoil%Re(k),airfoil%Re(k+1),Cl_inter(1),Cl_inter(2),Re)

                            Cd_inter(1) = InterpolateAirfoilData(airfoil%aoa(i),airfoil%aoa(i+1),airfoil%cd(i,k),airfoil%cd(i+1,k),aoa)
                            Cd_inter(2) = InterpolateAirfoilData(airfoil%aoa(i),airfoil%aoa(i+1),airfoil%cd(i,k+1),airfoil%cd(i+1,k+1),aoa)
                            Cd_out=InterpolateAirfoilData(airfoil%Re(k),airfoil%Re(k+1),Cd_inter(1),Cd_inter(2),Re)

                            exit
                         endif
                     end do
                 endif
             end do

         end if

    end subroutine Get_Cl_Cl_from_airfoil_data

! linear interpolation given two points; returns y for new_x following line coefs (a,b) with y=ax+b
function InterpolateAirfoilData(x1,x2,y1,y2,new_x)
   implicit none
    
   real(KIND=RP), intent(in)    :: x1, x2, y1, y2, new_x
   real(KIND=RP)                :: a, b, InterpolateAirfoilData

    if(abs(x1-x2)<1.0e-6_RP) then 
      a=100.0_RP
   else
      a=(y1- y2)/(x1- x2)
   endif
    b= y1-a*x1;
    InterpolateAirfoilData=a*new_x+b
end function

function full_element_averageQ(mesh,eID,xi)
   use HexMeshClass
   use PhysicsStorage
   use NodalStorageClass
   implicit none

   type(HexMesh), intent(in)    :: mesh
   integer, intent(in)          :: eID 
   integer                      :: k, j, i
   real(kind=RP), dimension(NDIM), intent(in) :: xi

   integer                      :: total_points
   real(kind=RP), dimension(NCONS)   :: full_element_averageQ, Qsum

 
   Qsum(:) = 0.0_RP
   total_points = 0
   do k = 0, mesh%elements(eID) % Nxyz(3)   ; do j = 0, mesh%elements(eID) % Nxyz(2) ; do i = 0, mesh%elements(eID) % Nxyz(1)
       Qsum(:)=Qsum(:)+mesh%elements(eID) % Storage % Q(:,i,j,k)
       total_points=total_points + 1
   end do                  ; end do                ; end do

   full_element_averageQ(:) = Qsum(:) / real(total_points,RP)

end function full_element_averageQ

Function semi_element_averageQ(mesh,eID,xi)
   use HexMeshClass
   use PhysicsStorage
   use NodalStorageClass

   Implicit None
   type(HexMesh), intent(in)    :: mesh
   integer, intent(in)          :: eID 
   real(kind=RP), dimension(NDIM), intent(in) :: xi
   real(kind=RP), dimension(NCONS)   :: semi_element_averageQ, Qsum

   integer                      :: k, j, i, direction, N, ind
   integer, dimension(NDIM)     :: firstNodeIndex
   integer                      :: total_points
   type(NodalStorage_t), pointer :: spAxi

   ! fist get the sub element nodes index
   do direction = 1, NDIM

     N = mesh % elements(eID) % Nxyz(direction)
     spAxi   => NodalStorage(N)

     do ind = 0, N
         firstNodeIndex(direction) = ind-1
         if (xi(direction) .le. spAxi%x(ind)) exit
     end do

     if (firstNodeIndex(direction) .eq. -1) firstNodeIndex(direction) = 0

   end do

   nullify(spAxi)

   ! now average on the sub element
   Qsum(:) = 0.0_RP
   total_points = 0
   do k = firstNodeIndex(IZ), firstNodeIndex(IZ)+1   ; do j = firstNodeIndex(IY), firstNodeIndex(IY)+1 ; do i = firstNodeIndex(IX),firstNodeIndex(IX)+1
       Qsum(:) = Qsum(:) + mesh % elements(eID) % Storage % Q(:,i,j,k)
       total_points = total_points + 1
   end do                  ; end do                ; end do

   semi_element_averageQ(:) = Qsum(:) / real(total_points,RP)

End Function semi_element_averageQ

#endif
end module