! Copyright (c) 2017, 2019 Kannan Masilamani <kannan.masilamani@uni-siegen.de>
! Copyright (c) 2019 Peter Vitt <peter.vitt2@uni-siegen.de>
!
! Redistribution and use in source and binary forms, with or without
! modification, are permitted provided that the following conditions are met:
!
! 1. Redistributions of source code must retain the above copyright notice,
! this list of conditions and the following disclaimer.
!
! 2. Redistributions in binary form must reproduce the above copyright notice,
! this list of conditions and the following disclaimer in the documentation
! and/or other materials provided with the distribution.
!
! THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY OF SIEGEN “AS IS” AND ANY EXPRESS
! OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! IN NO EVENT SHALL UNIVERSITY OF SIEGEN OR CONTRIBUTORS BE LIABLE FOR ANY
! DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
! (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
! ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
! SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! **************************************************************************** !
!> author: Kannan Masilamani, Verena Krupp
!! This module contains routine to retrieve state variables for getElement,
!! getPoint, setupIndices and getValOfIndex
!!
?? include 'header/lbm_macros.inc'
?? include 'treelm/source/deriveMacros.inc'
module mus_stateVar_module

  use, intrinsic :: iso_c_binding,  only: c_ptr, c_f_pointer, c_loc
  use env_module,                   only: rk, long_k

  use tem_logging_module,           only: logUnit
  use tem_debug_module,             only: dbgUnit
  use tem_aux_module,               only: tem_abort
  use tem_varSys_module,            only: tem_varSys_type,                    &
    &                                     tem_varSys_op_type
  use tem_grow_array_module,        only: grw_realArray_type, &
    &                                     grw_intArray_type, &
    &                                     append, init, truncate
  use tem_pointData_module,         only: tem_pointData_list_type, &
    &                                     init, append, truncate
  use treelmesh_module,             only: treelmesh_type
  use tem_geometry_module,          only: tem_CoordOfReal, &
    &                                     tem_PosofId, tem_BaryOfId
  use tem_time_module,              only: tem_time_type
  use tem_topology_module,          only: tem_IdOfCoord
  use tem_topology_module,          only: tem_levelOf
  use tem_element_module,           only: eT_minRelevant, eT_maxRelevant, &
    &                                     eT_fluid

  ! include musubi modules
  use mus_scheme_type_module,  only: mus_scheme_type
  use mus_varSys_module,       only: mus_varSys_data_type
  use mus_connectivity_module, only: mus_getSrcElemPosForIntp


  implicit none

  private

  public :: mus_access_state_forElement
  public :: mus_access_stateFetch_forElement
  public :: mus_access_stateFetch_now_forElement
  public :: mus_stateVar_forPoint
  public :: mus_accessVar_setupIndices
  public :: mus_stateVar_fromIndex
  public :: mus_stateVar_Fetch_fromIndex
  public :: mus_stateVar_Fetch_now_fromIndex

contains

  ! ************************************************************************* !
  !> Return the solver state variable for a given set of elements
  !!
  !! The interface has to comply to the abstract interface
  !! [[tem_varSys_module:tem_varSys_proc_element]].
  !!
?? copy :: get_element_headtxt(mus_access_state_forElement)
    ! --------------------------------------------------------------------- !
    integer :: statePos, iElem, iComp, iLevel
    type(mus_varSys_data_type), pointer :: fPtr
    integer :: nElems_loc
    ! --------------------------------------------------------------------- !
    call C_F_POINTER( fun%method_Data, fPtr )

    ! res is always AOS layout
    res = 0.0_rk
    do iElem = 1, nElems
      ! if state array is defined level wise then use levelPointer(pos)
      ! to access state array
      statePos = fPtr%solverData%geometry%levelPointer( elemPos(iElem) )
      iLevel = tem_levelOf( tree%treeID( elemPos(iElem) ) )
      nElems_loc = fPtr%solverData%scheme%pdf( iLevel )%nSize
      do iComp = 1, fun%nComponents
        res( (iElem-1)*fun%nComponents+iComp ) =                        &
          & fPtr%solverData%scheme%state( iLevel )%val(                 &
          ! position of this state variable in the state array
& ?IDX?( fun%state_varPos(iComp),statePos,varSys%nScalars,nElems_loc ), &
          & fPtr%solverData%scheme%pdf( iLevel )%nNext )
      end do !iComp
    end do !iElem

  end subroutine mus_access_state_forElement
  ! ************************************************************************* !

  ! ************************************************************************* !
  !> Return the solver state variable for a given set of elements by using
  !! FETCH macro for nNow
  !!
  !! The interface has to comply to the abstract interface
  !! [[tem_varSys_module:tem_varSys_proc_element]].
  !!
?? copy :: get_element_headtxt(mus_access_stateFetch_now_forElement)
    ! --------------------------------------------------------------------- !
    integer :: statePos, iElem, iComp, iLevel
    type(mus_varSys_data_type), pointer :: fPtr
    type(mus_scheme_type), pointer :: scheme
    integer :: nSize, iFld, QQ, nScalars
    ! --------------------------------------------------------------------- !
    call C_F_POINTER( fun%method_Data, fPtr )
    scheme => fPtr%solverData%scheme
    QQ = scheme%layout%fStencil%QQ
    nScalars = varSys%nSCalars
    iFld = fun%input_varPos(1)

    ! res is always AOS layout
    res = 0.0_rk
    do iElem = 1, nElems
      ! if state array is defined level wise then use levelPointer(pos)
      ! to access state array
      statePos = fPtr%solverData%geometry%levelPointer( elemPos(iElem) )
      iLevel = tem_levelOf( tree%treeID( elemPos(iElem) ) )
      nSize = scheme%pdf( iLevel )%nSize
      do iComp = 1, fun%nComponents
        res( (iElem-1)*fun%nComponents+iComp ) =                        &
          & scheme%state( iLevel )%val(                                 &
          ! position of this state variable in the state array
& ?FETCH?(iComp,iFld,statePos,QQ,nScalars,nSize,scheme%pdf(iLevel)%neigh), &
& scheme%pdf( iLevel )%nNow )
      end do !iComp
    end do !iElem

  end subroutine mus_access_stateFetch_now_forElement
  ! ************************************************************************* !


  ! ************************************************************************* !
  !> Return the solver state variable for a given set of elements by using
  !! FETCH macro for nNext
  !!
  !! The interface has to comply to the abstract interface
  !! [[tem_varSys_module:tem_varSys_proc_element]].
  !!
?? copy :: get_element_headtxt(mus_access_stateFetch_forElement)
    ! --------------------------------------------------------------------- !
    integer :: statePos, iElem, iComp, iLevel
    type(mus_varSys_data_type), pointer :: fPtr
    type(mus_scheme_type), pointer :: scheme
    integer :: nSize, iFld, QQ, nScalars
    ! --------------------------------------------------------------------- !
    call C_F_POINTER( fun%method_Data, fPtr )
    scheme => fPtr%solverData%scheme
    QQ = scheme%layout%fStencil%QQ
    nScalars = varSys%nSCalars
    iFld = fun%input_varPos(1)

    ! res is always AOS layout
    res = 0.0_rk
    do iElem = 1, nElems
      ! if state array is defined level wise then use levelPointer(pos)
      ! to access state array
      statePos = fPtr%solverData%geometry%levelPointer( elemPos(iElem) )
      iLevel = tem_levelOf( tree%treeID( elemPos(iElem) ) )
      nSize = scheme%pdf( iLevel )%nSize
      do iComp = 1, fun%nComponents
        res( (iElem-1)*fun%nComponents+iComp ) =                        &
          & scheme%state( iLevel )%val(                                 &
          ! position of this state variable in the state array
& ?FETCH?(iComp,iFld,statePos,QQ,nScalars,nSize,scheme%pdf(iLevel)%neigh), &
& scheme%pdf( iLevel )%nNext )
      end do !iComp
    end do !iElem

  end subroutine mus_access_stateFetch_forElement
  ! ************************************************************************* !

  ! ************************************************************************* !
  !> State variable for a given set of points using linear interpolation.
  !!
  !! The interface has to comply to the abstract interface
  !! [[tem_varSys_module:tem_varSys_proc_point]].
  !!
?? copy :: get_point_headtxt(mus_stateVar_forPoint)
    ! --------------------------------------------------------------------- !
    type(mus_varSys_data_type), pointer :: fPtr
    type(mus_scheme_type), pointer :: scheme
    integer :: statePos, iPnt, iLevel, elemPos
    integer :: coord(4)
    integer, allocatable :: srcElemPos(:)
    real(kind=rk), allocatable :: weights(:), srcRes(:), pntVal(:)
    integer :: iSrc, iComp, nSrcElems, nSize
    ! --------------------------------------------------------------------- !
!write(dbgUnit(1),*) 'Derive for point :'//trim(varSys%varname%val(fun%myPos))
    call C_F_POINTER( fun%method_Data, fPtr )
    scheme => fPtr%solverData%scheme
    allocate(srcElemPos(scheme%layout%fStencil%QQ))
    allocate(weights(scheme%layout%fStencil%QQ))
    allocate(srcRes(scheme%layout%fStencil%QQ*fun%nComponents))
    allocate(pntVal(fun%nComponents))

    res = 0.0_rk
    do iPnt = 1, nPnts
      weights = 0.0_rk
      srcRes = 0.0_rk
      coord =  tem_CoordOfReal(tree, point(iPnt,:), tree%global%maxLevel )
      ! returns position of existing element in tree which contains point
      ! 0 if no corresponding node is found,
      ! or the negative of the found ID, if it is a virtual node.
      elemPos = abs(tem_PosofId( tem_IdOfCoord(coord), tree%treeID ))

      ! skip this point if no corresponding element is found
      if (elemPos == 0) cycle

      ! level of existing element
      iLevel = tem_levelOf( tree%treeID( elemPos ) )

      ! position of element in levelDesc total list
      statePos = fPtr%solverData%geometry%levelPointer( elemPos )

      ! get position of source element position in state array for interpolation
      ! using neighbor connectivity array.
      ! Also calculate weights for interpolation using distance between the
      ! point and source element barycenter
      call mus_getSrcElemPosForIntp(                              &
        & srcElemPos  = srcElemPos,                               &
        & weights     = weights,                                  &
        & nSrcElems   = nSrcElems,                                &
        & point       = point(iPnt,:),                            &
        & elemPos     = statePos,                                 &
        & neigh       = fPtr%solverData%scheme%pdf(iLevel)%neigh, &
        & baryOfTotal = scheme%levelDesc(iLevel)%baryOfTotal,     &
        & nElems      = scheme%pdf(iLevel)%nSize,                 &
        & nSolve      = scheme%pdf(iLevel)%nElems_solve,          &
        & stencil     = scheme%layout%fStencil,                   &
        & excludeHalo = .true.,                                   &
        & nScalars    = varSys%nScalars                           )

      ! get source element values
      do iSrc = 1, nSrcElems
        ! position of element in levelDesc total list
        statePos = srcElemPos(iSrc)
        nSize = scheme%pdf( iLevel )%nSize
        do iComp = 1, fun%nComponents
          srcRes( (iSrc-1)*fun%nComponents + iComp )                  &
            & = scheme%state( iLevel )%val(                           &
& ?IDX?( fun%state_varPos(iComp), statePos, varSys%nScalars, nSize ), &
            & scheme%pdf( iLevel )%nNext )
        end do !iComp
      end do !iSrc

      ! Linear interpolation res = sum(weight_i*phi_i)
      pntVal = 0.0_rk
      do iSrc = 1, nSrcElems
        pntVal(:) = pntVal(:) + weights(iSrc) &
          & * srcRes( (iSrc-1)*fun%nComponents+1 : iSrc*fun%nComponents )
      end do
      res( (iPnt-1)*fun%nComponents+1 : iPnt*fun%nComponents ) = pntVal

    end do !iPnt
  end subroutine mus_stateVar_forPoint
  ! ************************************************************************* !


  ! ************************************************************************* !
  !> This routine takes points coordinates, stores them in the method_data and
  !! return indices where points are located in the growing array of points or
  !! values ( sometimes we do not need to store the points )
  !! It is need to setup points for every variable. Points will be provided by
  !! boundaries or sources depends on what uses the variable. This points do not
  !! change with time . This indices will be stored in corresponding boundary
  !! or source to evaluate a value on that point later using
  !! tem_varSys_proc_getValOfIndex.
  subroutine mus_accessVar_setupIndices( fun, varSys, point, offset_bit, &
    &                                   iLevel, tree, nPnts, idx        )
    ! -------------------------------------------------------------------------- !
    !> Description of the method to obtain the variables, for this routine
    !! we need the location where to store the points.
    class(tem_varSys_op_type), intent(in) :: fun
    !> The variable system to obtain the variable from.
    type(tem_varSys_type), intent(in) :: varSys
    !>  arrays of points for which the indices are returned
    real(kind=rk), intent(in) :: point(:,:)
    !> Offset bit encoded as character for every point.
    !! If not present default is to center i.e offset_bit = achar(1+4+16)
    character, optional, intent(in) :: offset_bit(:)
    !> the point data need to be loaded levelwise, we need the current level
    integer, intent(in) :: iLevel
    !> global treelm mesh info
    type(treelmesh_type), intent(in) :: tree
    !> number of points
    integer, intent(in) :: nPnts
    integer, intent(out) :: idx(:)
    ! -------------------------------------------------------------------------- !
    integer :: iPoint
    integer :: coord(4)
    integer :: elemPos, loc_level, statePos
    type(mus_varSys_data_type), pointer :: fPtr
    type(mus_scheme_type), pointer :: scheme
    real(kind=rk), allocatable :: weights(:)
    integer, allocatable :: srcElemPos(:)
    integer :: nSrcElems
    integer(kind=long_k) :: treeID
    ! -------------------------------------------------------------------------- !

!    write(dbgUnit(4),*) 'setup indices for the points of variable ', &
!      &                 trim(varSys%varname%val(fun%myPos))

    call C_F_POINTER( fun%method_Data, fPtr )
    scheme => fPtr%solverData%scheme

    allocate(srcElemPos(scheme%layout%fStencil%QQ))
    allocate(weights(scheme%layout%fStencil%QQ))

    idx = 0
    ! store elemPos of each point in global treeID list
    do iPoint = 1, nPnts
      weights = 0.0_rk

      ! Get the treeID of the element and then get the element position
      ! in the levelwise array
      coord =  tem_CoordOfReal( mesh  = tree,                &
        &                       point = point(iPoint,:),     &
        &                       level = tree%global%maxLevel )

      treeID = tem_IdOfCoord(coord)
      ! returns position of existing element in tree which contains point
      ! 0 if no corresponding node is found,
      ! or the negative of the found ID, if it is a virtual node.
      elemPos = abs(tem_PosofId( treeID, tree%treeID ))

!      write(dbgUnit(1),*) 'iPnt ', iPoint, ' elemPos ', elemPos, &
!        & 'treeID ', treeID, 'point ', point(iPoint,:)

      ! skip this point if no corresponding element is found
      if (elemPos == 0) cycle
      ! store point data only if element position is found in tree

      ! level of existing element
      loc_level = tem_levelOf( tree%treeID( elemPos ) )

      ! append grw array for this point's element position in tree
      call append(me  = fPtr%pointData%pntLvl(iLevel)%elemPos, &
        &         val = elemPos                                )
      ! Actual level on which this point was found
      call append(me  = fPtr%pointData%pntLvl(iLevel)%pntLevel, &
        &         val = loc_level                               )
      ! Append points to growing array of points
      call append(me  = fPtr%pointData%pntLvl(iLevel)%grwPnt, &
        &         val = point(iPoint,:)                       )

      ! position of element in levelDesc total list
      statePos = fPtr%solverData%geometry%levelPointer( elemPos )

      ! get position of source element position in state array for interpolation
      ! using neighbor connectivity array.
      ! Also calculate weights for interpolation using distance between the
      ! point and source element barycenter
      ! Excluded halo elements as they require communication of all links
      call mus_getSrcElemPosForIntp(                                 &
        & srcElemPos  = srcElemPos,                                  &
        & weights     = weights,                                     &
        & nSrcElems   = nSrcElems,                                   &
        & point       = point(iPoint,:),                             &
        & elemPos     = statePos,                                    &
        & neigh       = fPtr%solverData%scheme%pdf(loc_level)%neigh, &
        & baryOfTotal = scheme%levelDesc(loc_level)%baryOfTotal,     &
        & nElems      = scheme%pdf(loc_level)%nSize,                 &
        & nSolve      = scheme%pdf(loc_level)%nElems_solve,          &
        & stencil     = scheme%layout%fStencil,                      &
        & excludeHalo = .true.,                                      &
        & nScalars    = varSys%nScalars                              )

      ! First position of source elements
      call append(me  = fPtr%pointData%pntLvl(iLevel)%srcElem%first,  &
        &         val = fPtr%pointData%pntLvl(iLevel)%srcElem%elemPos &
        &               %nVals + 1                                    )

      ! Append all src elemPos
      call append(me  = fPtr%pointData%pntLvl(iLevel)%srcElem%elemPos, &
        &         val = srcElemPos(1:nSrcElems)                        )

      ! last position of source elements
      call append(me  = fPtr%pointData%pntLvl(iLevel)%srcElem%last,   &
        &         val = fPtr%pointData%pntLvl(iLevel)%srcElem%elemPos &
        &               %nVals                                        )

      ! weights for srcElements
      call append(me  = fPtr%pointData%pntLvl(iLevel)%srcElem%weight, &
        &         val = weights(1:nSrcElems)                          )

      ! store the index,
      idx(iPoint)= fPtr%pointData%pntLvl(iLevel)%elemPos%nVals
    end do
!    write(dbgUnit(1),*) 'varName ', trim(varSys%varName%val(fun%myPos))
!    write(dbgUnit(1),*) 'idx ', idx

    fPtr%pointData%pntLvl(iLevel)%nPnts = fPtr%pointData%pntLvl(iLevel)%grwPnt &
      &                                       %coordX%nVals
    call truncate(fPtr%pointData%pntLvl(iLevel)%grwPnt)
    call truncate(fPtr%pointData%pntLvl(iLevel)%elemPos)
    call truncate(fPtr%pointData%pntLvl(iLevel)%srcElem%first)
    call truncate(fPtr%pointData%pntLvl(iLevel)%srcElem%last)
    call truncate(fPtr%pointData%pntLvl(iLevel)%srcElem%elemPos)
    call truncate(fPtr%pointData%pntLvl(iLevel)%srcElem%weight)

  end subroutine mus_accessVar_setupIndices
  ! ************************************************************************* !


  ! ************************************************************************* !
  !> Routine to get the actual value for a given array of indices.
  !! The indices belong to the grwarray of points storing levelwise in
  !! Pointdata%pntLvl(iLevel).
  !! Hence this routines takes the indeices as input, can refer to the pointData
  !! and evaluate the variable and returns the values
  subroutine mus_stateVar_fromIndex( fun, varSys, time, iLevel, idx, &
    &                                idxLen,  nVals,  res            )
    ! -------------------------------------------------------------------------- !
    !> Description of the method to obtain the variables,
    class(tem_varSys_op_type), intent(in) :: fun

    !> The variable system to obtain the variable from.
    type(tem_varSys_type), intent(in) :: varSys

    !> Point in time at which to evaluate the variable.
    type(tem_time_type), intent(in) :: time

    !> Level on which values are requested
    integer, intent(in) :: iLevel

    !> Index of points in the growing array and variable val array to
    !! return.
    !! Size: n
    integer, intent(in) :: idx(:)

    !> With idx as start index in contiguous memory,
    !! idxLength defines length of each contiguous memory
    !! Size: dependes on number of first index for contiguous array,
    !! but the sum of all idxLen is equal to nVals
    integer, optional, intent(in) :: idxLen(:)

    !> Number of values to obtain for this variable (vectorized access).
    integer, intent(in) :: nVals

    !> Resulting values for the requested variable.
    !!
    !! Dimension: n requested entries x nComponents of this variable
    !! Access: (iElem-1)*fun%nComponents + iComp
    real(kind=rk), intent(out) :: res(:)
    ! -------------------------------------------------------------------------- !
    type(mus_varSys_data_type), pointer :: fPtr
    type(mus_scheme_type), pointer :: scheme
    integer :: iComp, iSrc, iVal, elemPos, first, last, nSize, loc_level
    integer :: iSrcElems
    real(kind=rk), allocatable :: srcRes(:), pntVal(:)
    real(kind=rk) :: weight
    ! -------------------------------------------------------------------------- !

!    write(dbgUnit(4),*) 'get the values of indices for variable ',  &
!      &                  trim(varSys%varname%val(fun%myPos))

    call C_F_POINTER( fun%method_Data, fPtr )
    scheme => fPtr%solverData%scheme
    allocate(srcRes(scheme%layout%fStencil%QQ*fun%nComponents))
    allocate(pntVal(fun%nComponents))

    res = 0.0_rk

    ! distinguish if we have an array of index or we have contingous memory
    ! access where index are always first entries!
    if (present(idxLen)) then
      call tem_abort('Error: idxLen is not supported in get_valOfIndex for ' &
        &          //'state variable')
    else
      ! Get the state value at the specific point
      do iVal = 1, nVals
        if (idx(iVal)>0) then

          ! elemPos in tree
          ! elemPos = fPtr%pointData%pntLvl(iLevel)%elemPos%val(idx(iVal))

          first = fPtr%pointData%pntLvl(iLevel)%srcElem%first%val(idx(iVal))
          last = fPtr%pointData%pntLvl(iLevel)%srcElem%last%val(idx(iVal))
          loc_level = fPtr%pointData%pntLvl(iLevel)%pntLevel%val(idx(iVal))
          nSize = scheme%pdf( loc_level )%nSize

          ! get pdf's of source elements
          srcRes = 0.0_rk
          iSrcElems = 0
          do iSrc = first, last
            iSrcElems = iSrcElems + 1

            ! position of element in levelDesc total list
            elemPos = fPtr%pointData%pntLvl(iLevel)%srcElem%elemPos%val(iSrc)

            do iComp = 1, fun%nComponents
              srcRes( (iSrcElems-1)*fun%nComponents + iComp )       &
                & = scheme%state( loc_level )%val(                  &
& ?IDX?( fun%state_varPos(iComp), elemPos, varSys%nScalars,nSize ), &
                & scheme%pdf( loc_level )%nNext )
            end do !iComp
          end do !iSrc

          ! Linear interpolation res = sum(weight_i*phi_i)
          pntVal = 0.0_rk
          iSrcElems = 0
          do iSrc = first, last
            weight = fPtr%pointData%pntLvl(iLevel)%srcElem%weight%val(iSrc)
            iSrcElems = iSrcElems + 1
            pntVal(:) = pntVal(:) + weight &
              & * srcRes( (iSrcElems-1)*fun%nComponents+1 &
              &         : iSrcElems*fun%nComponents )
          end do

          ! get the state value for each component of this
          res( (iVal-1)*fun%nComponents+1: iVal*fun%nComponents ) = pntVal
        end if !idx>0
      end do !iVal
    end if ! idx_len

  end subroutine mus_stateVar_fromIndex
  ! ************************************************************************* !


  ! ************************************************************************* !
  !> Routine to get the actual value for a given array of indices.
  !! The indices belong to the grwarray of points storing levelwise in
  !! Pointdata%pntLvl(iLevel).
  !! Hence this routines takes the indeices as input, can refer to the pointData
  !! and evaluate the variable and returns the values
  subroutine mus_stateVar_Fetch_fromIndex( fun, varSys, time, iLevel, idx, &
    &                                      idxLen,  nVals,  res            )
    ! -------------------------------------------------------------------------- !
    !> Description of the method to obtain the variables,
    class(tem_varSys_op_type), intent(in) :: fun

    !> The variable system to obtain the variable from.
    type(tem_varSys_type), intent(in) :: varSys

    !> Point in time at which to evaluate the variable.
    type(tem_time_type), intent(in) :: time

    !> Level on which values are requested
    integer, intent(in) :: iLevel

    !> Index of points in the growing array and variable val array to
    !! return.
    !! Size: n
    integer, intent(in) :: idx(:)

    !> With idx as start index in contiguous memory,
    !! idxLength defines length of each contiguous memory
    !! Size: dependes on number of first index for contiguous array,
    !! but the sum of all idxLen is equal to nVals
    integer, optional, intent(in) :: idxLen(:)

    !> Number of values to obtain for this variable (vectorized access).
    integer, intent(in) :: nVals

    !> Resulting values for the requested variable.
    !!
    !! Dimension: n requested entries x nComponents of this variable
    !! Access: (iElem-1)*fun%nComponents + iComp
    real(kind=rk), intent(out) :: res(:)
    ! -------------------------------------------------------------------------- !
    type(mus_varSys_data_type), pointer :: fPtr
    type(mus_scheme_type), pointer :: scheme
    integer :: iComp, iSrc, iVal, elemPos, first, last, nSize, loc_level
    integer :: nSrcElems, iFld, QQ, nScalars
    real(kind=rk), allocatable :: srcRes(:), pntVal(:)
    real(kind=rk) :: weight
    ! -------------------------------------------------------------------------- !

!    write(dbgUnit(4),*) 'get the values of indices for variable ',  &
!      &                  trim(varSys%varname%val(fun%myPos))

    call C_F_POINTER( fun%method_Data, fPtr )
    scheme => fPtr%solverData%scheme
    QQ = scheme%layout%fStencil%QQ
    nScalars = varSys%nSCalars
    allocate(srcRes(QQ*fun%nComponents))
    allocate(pntVal(fun%nComponents))
    iFld = fun%input_varPos(1)

    res = 0.0_rk

    ! distinguish if we have an array of index or we have contingous memory
    ! access where index are always first entries!
    if (present(idxLen)) then
      call tem_abort('Error: idxLen is not supported in get_valOfIndex for ' &
        &          //'state variable')
    else
      ! Get the state value at the specific point
      do iVal = 1, nVals
        if (idx(iVal)>0) then

          ! elemPos in tree
          ! elemPos = fPtr%pointData%pntLvl(iLevel)%elemPos%val(idx(iVal))
          first = fPtr%pointData%pntLvl(iLevel)%srcElem%first%val(idx(iVal))
          last = fPtr%pointData%pntLvl(iLevel)%srcElem%last%val(idx(iVal))
          loc_level = fPtr%pointData%pntLvl(iLevel)%pntLevel%val(idx(iVal))
          nSize = scheme%pdf( loc_level )%nSize

          ! get pdf's of source elements
          srcRes = 0.0_rk
          nSrcElems = 0
          do iSrc = first, last
            nSrcElems = nSrcElems + 1

            ! position of element in levelDesc total list
            elemPos = fPtr%pointData%pntLvl(iLevel)%srcElem%elemPos%val(iSrc)

            do iComp = 1, fun%nComponents
              srcRes( (nSrcElems-1)*fun%nComponents + iComp )                &
                & = scheme%state( loc_level )%val(                           &
& ?FETCH?(iComp,iFld,elemPos,QQ,nScalars,nSize,scheme%pdf(loc_level)%neigh), &
& scheme%pdf( loc_level )%nNext )
            end do !iComp
          end do !iSrc

          ! Linear interpolation res = sum(weight_i*phi_i)
          pntVal = 0.0_rk
          nSrcElems = 0
          do iSrc = first, last
            weight = fPtr%pointData%pntLvl(iLevel)%srcElem%weight%val(iSrc)
            nSrcElems = nSrcElems + 1
            pntVal(:) = pntVal(:) + weight &
              & * srcRes( (nSrcElems-1)*fun%nComponents+1 &
              &         : nSrcElems*fun%nComponents )
          end do

          ! get the state value for each component of this
          ! get the state value for each component of this
          res( (iVal-1)*fun%nComponents+1: iVal*fun%nComponents ) = pntVal

        end if !idx>0
      end do !iVal
    end if ! idx_len

  end subroutine mus_stateVar_Fetch_fromIndex
  ! ************************************************************************* !


  ! ************************************************************************* !
  !> Routine to get the actual value for a given array of indices.
  !! The indices belong to the grwarray of points storing levelwise in
  !! Pointdata%pntLvl(iLevel).
  !! Hence this routines takes the indeices as input, can refer to the pointData
  !! and evaluate the variable and returns the values
  subroutine mus_stateVar_Fetch_now_fromIndex( fun, varSys, time, iLevel,  &
    &                                          idx, idxLen,  nVals,  res   )
    ! -------------------------------------------------------------------------- !
    !> Description of the method to obtain the variables,
    class(tem_varSys_op_type), intent(in) :: fun

    !> The variable system to obtain the variable from.
    type(tem_varSys_type), intent(in) :: varSys

    !> Point in time at which to evaluate the variable.
    type(tem_time_type), intent(in) :: time

    !> Level on which values are requested
    integer, intent(in) :: iLevel

    !> Index of points in the growing array and variable val array to
    !! return.
    !! Size: n
    integer, intent(in) :: idx(:)

    !> With idx as start index in contiguous memory,
    !! idxLength defines length of each contiguous memory
    !! Size: dependes on number of first index for contiguous array,
    !! but the sum of all idxLen is equal to nVals
    integer, optional, intent(in) :: idxLen(:)

    !> Number of values to obtain for this variable (vectorized access).
    integer, intent(in) :: nVals

    !> Resulting values for the requested variable.
    !!
    !! Dimension: n requested entries x nComponents of this variable
    !! Access: (iElem-1)*fun%nComponents + iComp
    real(kind=rk), intent(out) :: res(:)
    ! -------------------------------------------------------------------------- !
    type(mus_varSys_data_type), pointer :: fPtr
    type(mus_scheme_type), pointer :: scheme
    integer :: iComp, iSrc, iVal, elemPos, first, last, nSize, loc_level
    integer :: nSrcElems, iFld, QQ, nScalars
    real(kind=rk), allocatable :: srcRes(:), pntVal(:)
    real(kind=rk) :: weight
    ! -------------------------------------------------------------------------- !

!    write(dbgUnit(4),*) 'get the values of indices for variable ',  &
!      &                  trim(varSys%varname%val(fun%myPos))

    call C_F_POINTER( fun%method_Data, fPtr )
    scheme => fPtr%solverData%scheme
    QQ = scheme%layout%fStencil%QQ
    nScalars = varSys%nSCalars
    allocate(srcRes(QQ*fun%nComponents))
    allocate(pntVal(fun%nComponents))
    iFld = fun%input_varPos(1)

    res = 0.0_rk

    ! distinguish if we have an array of index or we have contingous memory
    ! access where index are always first entries!
    if (present(idxLen)) then
      call tem_abort('Error: idxLen is not supported in get_valOfIndex for ' &
        &          //'state variable')
    else
      ! Get the state value at the specific point
      do iVal = 1, nVals
        if (idx(iVal)>0) then

          ! elemPos in tree
          ! elemPos = fPtr%pointData%pntLvl(iLevel)%elemPos%val(idx(iVal))
          first = fPtr%pointData%pntLvl(iLevel)%srcElem%first%val(idx(iVal))
          last = fPtr%pointData%pntLvl(iLevel)%srcElem%last%val(idx(iVal))
          loc_level = fPtr%pointData%pntLvl(iLevel)%pntLevel%val(idx(iVal))
          nSize = scheme%pdf( loc_level )%nSize

          ! get pdf's of source elements
          srcRes = 0.0_rk
          nSrcElems = 0
          do iSrc = first, last
            nSrcElems = nSrcElems + 1

            ! position of element in levelDesc total list
            elemPos = fPtr%pointData%pntLvl(iLevel)%srcElem%elemPos%val(iSrc)

            do iComp = 1, fun%nComponents
              srcRes( (nSrcElems-1)*fun%nComponents + iComp )          &
                & = scheme%state( loc_level )%val(                        &
& ?FETCH?(iComp,iFld,elemPos,QQ,nScalars,nSize,scheme%pdf(loc_level)%neigh), &
& scheme%pdf( loc_level )%nNow )
            end do !iComp
          end do !iSrc

          ! Linear interpolation res = sum(weight_i*phi_i)
          pntVal = 0.0_rk
          nSrcElems = 0
          do iSrc = first, last
            weight = fPtr%pointData%pntLvl(iLevel)%srcElem%weight%val(iSrc)
            nSrcElems = nSrcElems + 1
            pntVal(:) = pntVal(:) + weight &
              & * srcRes( (nSrcElems-1)*fun%nComponents+1 &
              &         : nSrcElems*fun%nComponents )
          end do

          ! get the state value for each component of this
          ! get the state value for each component of this
          res( (iVal-1)*fun%nComponents+1: iVal*fun%nComponents ) = pntVal

        end if !idx>0
      end do !iVal
    end if ! idx_len

  end subroutine mus_stateVar_Fetch_now_fromIndex
  ! ************************************************************************* !

end module mus_stateVar_module
! **************************************************************************** !

