atl_modg_kernel_module.f90 Source File


This file depends on

sourcefile~~atl_modg_kernel_module.f90~~EfferentGraph sourcefile~atl_modg_kernel_module.f90 atl_modg_kernel_module.f90 sourcefile~atl_boundary_module.f90 atl_boundary_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_boundary_module.f90 sourcefile~atl_cube_elem_module.f90 atl_cube_elem_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_cube_elem_module.f90 sourcefile~atl_elemental_time_integration_module.f90 atl_elemental_time_integration_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_elemental_time_integration_module.f90 sourcefile~atl_eqn_euler_derive_module.f90 atl_eqn_euler_derive_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_eqn_euler_derive_module.f90 sourcefile~atl_eqn_euler_var_module.f90 atl_eqn_euler_var_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_eqn_euler_var_module.f90 sourcefile~atl_equation_module.f90 atl_equation_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_equation_module.f90 sourcefile~atl_facedata_module.f90 atl_facedata_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_facedata_module.f90 sourcefile~atl_kerneldata_module.f90 atl_kerneldata_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_kerneldata_module.f90 sourcefile~atl_materialini_module.f90 atl_materialIni_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_materialini_module.f90 sourcefile~atl_materialprp_module.f90 atl_materialPrp_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_materialprp_module.f90 sourcefile~atl_parallel_module.f90 atl_parallel_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_parallel_module.f90 sourcefile~atl_penalization_module.f90 atl_penalization_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_penalization_module.f90 sourcefile~atl_physfluxnvrstk_module.f90 atl_physFluxNvrstk_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_physfluxnvrstk_module.f90 sourcefile~atl_scheme_module.f90 atl_scheme_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_scheme_module.f90 sourcefile~atl_source_types_module.f90 atl_source_types_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_source_types_module.f90 sourcefile~atl_varsys_module.f90 atl_varSys_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_varsys_module.f90 sourcefile~atl_voltoface_module.f90 atl_volToFace_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~atl_voltoface_module.f90 sourcefile~ply_dof_module.f90 ply_dof_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~ply_dof_module.f90 sourcefile~ply_dynarray_project_module.f90 ply_dynArray_project_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~ply_dynarray_project_module.f90 sourcefile~ply_modg_basis_module.f90 ply_modg_basis_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~ply_modg_basis_module.f90 sourcefile~ply_poly_project_module.f90 ply_poly_project_module.f90 sourcefile~atl_modg_kernel_module.f90->sourcefile~ply_poly_project_module.f90

Files dependent on this one

sourcefile~~atl_modg_kernel_module.f90~~AfferentGraph sourcefile~atl_modg_kernel_module.f90 atl_modg_kernel_module.f90 sourcefile~atl_compute_local_module.f90 atl_compute_local_module.f90 sourcefile~atl_compute_local_module.f90->sourcefile~atl_modg_kernel_module.f90 sourcefile~atl_compute_module.f90 atl_compute_module.f90 sourcefile~atl_compute_module.f90->sourcefile~atl_modg_kernel_module.f90 sourcefile~atl_container_module.f90 atl_container_module.f90 sourcefile~atl_container_module.f90->sourcefile~atl_modg_kernel_module.f90 sourcefile~atl_global_time_integration_module.f90 atl_global_time_integration_module.f90 sourcefile~atl_container_module.f90->sourcefile~atl_global_time_integration_module.f90 sourcefile~ateles.f90 ateles.f90 sourcefile~ateles.f90->sourcefile~atl_container_module.f90 sourcefile~atl_program_module.f90 atl_program_module.f90 sourcefile~ateles.f90->sourcefile~atl_program_module.f90 sourcefile~atl_fwdeuler_module.f90 atl_fwdEuler_module.f90 sourcefile~atl_fwdeuler_module.f90->sourcefile~atl_compute_module.f90 sourcefile~atl_harvesting.f90 atl_harvesting.f90 sourcefile~atl_harvesting.f90->sourcefile~atl_container_module.f90 sourcefile~atl_initialize_module.f90 atl_initialize_module.f90 sourcefile~atl_harvesting.f90->sourcefile~atl_initialize_module.f90 sourcefile~atl_harvesting.f90->sourcefile~atl_program_module.f90 sourcefile~atl_imexrk_module.f90 atl_imexrk_module.f90 sourcefile~atl_imexrk_module.f90->sourcefile~atl_compute_module.f90 sourcefile~atl_initialize_module.f90->sourcefile~atl_container_module.f90 sourcefile~atl_predcor_cerk4_module.f90 atl_predcor_cerk4_module.f90 sourcefile~atl_predcor_cerk4_module.f90->sourcefile~atl_compute_local_module.f90 sourcefile~atl_predcor_cerk4_module.f90->sourcefile~atl_compute_module.f90 sourcefile~atl_program_module.f90->sourcefile~atl_container_module.f90 sourcefile~atl_program_module.f90->sourcefile~atl_initialize_module.f90 sourcefile~atl_program_module.f90->sourcefile~atl_global_time_integration_module.f90 sourcefile~atl_rk4_module.f90 atl_rk4_module.f90 sourcefile~atl_rk4_module.f90->sourcefile~atl_compute_module.f90 sourcefile~atl_rktaylor_module.f90 atl_rktaylor_module.f90 sourcefile~atl_rktaylor_module.f90->sourcefile~atl_compute_module.f90 sourcefile~atl_ssprk2_module.f90 atl_ssprk2_module.f90 sourcefile~atl_ssprk2_module.f90->sourcefile~atl_compute_module.f90 sourcefile~atl_global_time_integration_module.f90->sourcefile~atl_fwdeuler_module.f90 sourcefile~atl_global_time_integration_module.f90->sourcefile~atl_imexrk_module.f90 sourcefile~atl_global_time_integration_module.f90->sourcefile~atl_predcor_cerk4_module.f90 sourcefile~atl_global_time_integration_module.f90->sourcefile~atl_rk4_module.f90 sourcefile~atl_global_time_integration_module.f90->sourcefile~atl_rktaylor_module.f90 sourcefile~atl_global_time_integration_module.f90->sourcefile~atl_ssprk2_module.f90

Source Code

! Copyright (c) 2012-2014 Jens Zudrop <j.zudrop@grs-sim.de>
! Copyright (c) 2012-2013 Melven Zoellner <yameta@freenet.de>
! Copyright (c) 2012 Jan Hueckelheim <j.hueckelheim@grs-sim.de>
! Copyright (c) 2012 Vyacheslav Korchagin <v.korchagin@grs-sim.de>
! Copyright (c) 2012-2019 Harald Klimach <harald.klimach@uni-siegen.de>
! Copyright (c) 2013-2015 Verena Krupp <verena.krupp@uni-siegen.de>
! Copyright (c) 2013-2020 Peter Vitt <peter.vitt2@uni-siegen.de>
! Copyright (c) 2014-2015 Nikhil Anand <nikhil.anand@uni-siegen.de>
! Copyright (c) 2016 Parid Ndreka
! Copyright (c) 2016 Tobias Girresser <tobias.girresser@student.uni-siegen.de>
! Copyright (c) 2016 Kannan Masilamani <kannan.masilamani@uni-siegen.de>
! Copyright (c) 2016-2017 Jiaxing Qi <jiaxing.qi@uni-siegen.de>
! Copyright (c) 2017 Daniel PetrĂ³ <daniel.petro@student.uni-siegen.de>
! Copyright (c) 2018 Neda Ebrahimi Pour <neda.epour@uni-siegen.de>
!
! Permission to use, copy, modify, and distribute this software for any
! purpose with or without fee is hereby granted, provided that the above
! copyright notice and this permission notice appear in all copies.
!
! THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
! WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
! ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! **************************************************************************** !

! Copyright (c) 2014,2016-2017 Peter Vitt <peter.vitt2@uni-siegen.de>
! Copyright (c) 2014 Harald Klimach <harald.klimach@uni-siegen.de>
!
! Parts of this file were written by Peter Vitt and Harald Klimach for
! University of Siegen.
!
! Permission to use, copy, modify, and distribute this software for any
! purpose with or without fee is hereby granted, provided that the above
! copyright notice and this permission notice appear in all copies.
!
! THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
! WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
! ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! **************************************************************************** !
!
! Return the position of a given ansatz function combination in the
! linearized list of modal coefficients for Q-Tensor product polynomials.
! You must provide
! * Ansatzfunction index in x direction. Index starts with 1.
! * Ansatzfunction index in y direction. Index starts with 1.
! * Ansatzfunction index in z direction. Index starts with 1.
! * The maximal polynomial degree per spatial direction.
! * The variable to store the position of the modal coefficient in the list of
!   modal coefficients in.


! Return the position of a given ansatz function combination in the
! linearized list of modal coefficients for Q-Tensor product polynomials.
! You must provide
! * Ansatzfunction index in x direction. Index starts with 1.
! * Ansatzfunction index in y direction. Index starts with 1.
! * The maximal polynomial degree per spatial direction.
! * The variable to store the position of the modal coefficient in the list of
!   modal coefficients in.


! Return the position of a given ansatz function combination in the
! linearized list of modal coefficients for Q-Tensor product polynomials.
! You must provide
! * Ansatzfunction index in x direction. Index starts with 1.
! * The variable to store the position of the modal coefficient in the list of
!   modal coefficients in.


! Return the position of a given ansatz function combination in the
! linearized list of modal coefficients for P-Tensor product polynomials.
! You must provide
! * Ansatzfunction index in x direction. Index starts with 1.
! * Ansatzfunction index in y direction. Index starts with 1.
! * Ansatzfunction index in z direction. Index starts with 1.
! * The maximal polynomial degree per spatial direction.
! * The variable to store the position of the modal coefficient in the list of
!   modal coefficients in.


! Return the position of a given ansatz function combination in the
! linearized list of modal coefficients for P-Tensor product polynomials.
! You must provide
! * Ansatzfunction index in x direction. Index starts with 1.
! * Ansatzfunction index in y direction. Index starts with 1.
! * The maximal polynomial degree per spatial direction.
! * The variable to store the position of the modal coefficient in the list of
!   modal coefficients in.


! Return the position of a given ansatz function combination in the
! linearized list of modal coefficients for P-Tensor product polynomials.
! You must provide
! * Ansatzfunction index in x direction. Index starts with 1.
! * The variable to store the position of the modal coefficient in the list of
!   modal coefficients in.


! Return the number of degrees of freedom for Q polynomial space
! Your must provide:
! * The maximal polynomial degree per spatial direction
! * The variable to store the number of degrees of freedom for a Q tensor
!   product polynomial


! Return the number of degrees of freedom for broken polynomial space
! Your must provide:
! * The maximal polynomial degree per spatial direction (for P Tensor product
!   polynomials this assumed to be the same for each spatial direction).
! * The variable to store the number of degrees of freedom for a P tensor
!   product polynomial


! Return the number of degrees of freedom for Q polynomial space
! You must provide:
! * The maximal polynomial degree per spatial direction
! * The variable to store the number of degrees of freedom for a Q tensor
!   product polynomial


! Return the number of degrees of freedom for broken polynomial space
! You must provide:
! * The maximal polynomial degree per spatial direction (for P Tensor product
!   polynomials this assumed to be the same for each spatial direction).
! * A variable to store the number of degrees of freedom for a P tensor product
!   polynomial


! Return the number of degrees of freedom for Q polynomial space
! You must provide:
! * The maximal polynomial degree per spatial direction
! * The variable to store the number of degrees of freedom for a Q tensor
!   product polynomial


! Return the number of degrees of freedom for broken polynomial space
! You must provide:
! * The maximal polynomial degree per spatial direction (for P Tensor product
!   polynomials this assumed to be the same for each spatial direction).
! * The variable to store the number of degrees of freedom for a P tensor
!   product polynomial

! The x, y and z ansatz degrees are turned into the degrees of the next
! ansatz function in the layered P list
! You must provide:
! * Ansatz function index in x direction. First ansatz function has index 1.
! * Ansatz function index in y direction. First ansatz function has index 1.
! * Ansatz function index in z direction. First ansatz function has index 1.

! The x and y ansatz degrees are turned into the degrees of the next
! ansatz function in the layered P list
! You must provide:
! * Ansatz function index in x direction. First ansatz function has index 1.
! * Ansatz function index in y direction. First ansatz function has index 1.

! The x ansatz degree is turned into the degree of the next
! ansatz function in the layered P list
! You must provide:
! * Ansatz function index in x direction. First ansatz function has index 1.

! The x, y and z ansatz degrees are turned into the degrees of the next
! ansatz function in the linearized Q tensor
! You must provide:
! * Ansatz function index in x direction. First ansatz function has index 1.
! * Ansatz function index in y direction. First ansatz function has index 1.
! * Ansatz function index in z direction. First ansatz function has index 1.
! * Maximal polynomial degree

! The x and y ansatz degrees are turned into the degrees of the next
! ansatz function in the linearized Q tensor
! You must provide:
! * Ansatz function index in x direction. First ansatz function has index 1.
! * Ansatz function index in y direction. First ansatz function has index 1.
! * Maximal polynomial degree

! The x ansatz degree is turned into the degree of the next
! ansatz function in the linearized Q tensor
! You must provide:
! * Ansatz function index in x direction. First ansatz function has index 1.
!> Module for routines and datatypes of MOdal Discontinuous Galerkin (MODG)
!! scheme. This scheme is a spectral scheme for linear, purley hyperbolic
!! partial differential equation systems.
!!
!!@todo HK: Split this module into three: a common one, a Q part and a P part.
module atl_modg_kernel_module
  use env_module,                 only: rk, long_k, eps
  use tem_compileconf_module,     only: vlen

  use tem_aux_module,             only: tem_abort
  use treelmesh_module,           only: treelmesh_type
  use tem_element_module,         only: eT_fluid
  use tem_param_module,           only: qAxis
  use tem_time_module,            only: tem_time_type
  use tem_faceData_module,        only: tem_left, &
    &                                   tem_right
  use tem_logging_module,         only: logUnit
  use tem_comm_module,            only: tem_commPattern_type
  use tem_comm_env_module,        only: tem_comm_env_type
  use tem_element_module,         only: eT_fluid

  use ply_modg_basis_module,      only: ply_faceValLeftBndTest,      &
    &                                   ply_faceValRightBndTest,     &
    &                                   ply_faceValLeftBndGradTest,  &
    &                                   ply_faceValRightBndGradTest, &
    &                                   ply_scalProdDualLeg,         &
    &                                   ply_scalProdDualLegDiff,     &
    &                                   ply_faceValLeftBndTestGrad,  &
    &                                   ply_faceValRightBndTestGrad
  use ply_dof_module,             only: Q_space, P_space, &
    &                                   ply_change_poly_space
  use ply_poly_project_module,    only: ply_poly_project_type, assignment(=), &
    &                                   ply_poly_project_m2n, &
    &                                   ply_poly_project_n2m

  use atl_equation_module,        only: atl_equations_type
  use atl_cube_elem_module,       only: atl_cube_elem_type
  use atl_kerneldata_module,      only: atl_kerneldata_type, &
    &                                   atl_init_kerneldata, &
    &                                   atl_init_statedata,  &
    &                                   atl_statedata_type
  use atl_parallel_module,        only: atl_init_parallel_module
  use atl_scheme_module,          only: atl_scheme_type, atl_modg_scheme_type
  use atl_elemental_time_integration_module,                        &
    &                             only: atl_elemental_timestep_vec, &
    &                                   atl_timestep_type
  use atl_source_types_module,    only: atl_source_type

  use atl_facedata_module,        only: atl_facedata_type, &
    &                                   atl_faceRep_type,  &
    &                                   atl_elemfaceToNormal_prp
  use atl_boundary_module,        only: atl_level_boundary_type, &
    &                                   atl_get_numBndElems
  use atl_materialPrp_module,     only: atl_material_type
  use atl_penalization_module,    only: atl_penalizationData_type
  use atl_physFluxNvrstk_module,  only: atl_mult_nu11_NavierStokes, &
    &                                   atl_mult_nu21_NavierStokes, &
    &                                   atl_mult_nu31_NavierStokes, &
    &                                   atl_mult_nu12_NavierStokes, &
    &                                   atl_mult_nu22_NavierStokes, &
    &                                   atl_mult_nu32_NavierStokes, &
    &                                   atl_mult_nu13_NavierStokes, &
    &                                   atl_mult_nu23_NavierStokes, &
    &                                   atl_mult_nu33_NavierStokes
  use atl_materialIni_module,     only: atl_update_materialParams
  use atl_volToFace_module,       only: atl_modg_volToFace_grad_Q, &
    &                                   atl_modg_volToFace_P

  implicit none

  private

  public :: atl_init_modg_kernel, &
    & atl_modg_invMassMatrix,     &
    & atl_preprocess_modg_kernel, &
    & atl_modg_modalVolToModalFace

  public :: atl_modg_ensure_pos_facemean

  public :: atl_modg_scaledTransposedInvMassMatrix_Q, &
    & atl_modg_scaledTransposedInvMassMatrix_P,       &
    & atl_modg_scaledTransposedProject_physFlux_Q,    &
    & atl_modg_scaledTransposedProject_physFlux_P

  public :: atl_modg_project_source, &
    &       atl_modg_project_NumFlux

  ! only for the utests
  public :: atl_modg_kernel_utests


contains


  !> Initiate the MODG kernel for cubic elements on all levels.
  subroutine atl_init_modg_kernel(                                        &
    &          kerneldata_list, statedata_list, statedata_stab_list,      &
    &          mesh_list, scheme_list, boundary_list, boundary_stab_list, &
    &          equation, tree, time, commPattern,                         &
    &          need_deviation                                             )
    ! --------------------------------------------------------------------------
    !> Array of kerneldata_types, one for each level. They will be initialized.
    type(atl_kerneldata_type), allocatable :: kerneldata_list(:)
    !> Array of statedata_types, one for each level.
    type(atl_statedata_type), allocatable :: statedata_list(:)
    type(atl_statedata_type), allocatable :: statedata_stab_list(:,:)
    !> List of cubic meshes. One entry per level.
    type(atl_cube_elem_type), allocatable :: mesh_list(:)
    !> List of schemes on the levels.
    type(atl_scheme_type), allocatable :: scheme_list(:)
    !> The boundary description for the faces on the current level.
    type(atl_level_boundary_type), allocatable :: boundary_list(:)
    !> The boundary description for the faces on the current level.
    type(atl_level_boundary_type), allocatable :: boundary_stab_list(:)
    !> The equation type.
    type(atl_equations_type) :: equation
    !> The tree of the simulation domain
    type(treelmesh_type), intent(in) :: tree
    !> current time
    type(tem_time_type), intent(in) :: time
    !> mpi communication pattern type
    type(tem_commPattern_type), intent(in) :: commPattern
    !> Flag to indicate whether maximal polynomial deviations should be
    !! computed for the state.
    logical, intent(in) :: need_deviation
    ! --------------------------------------------------------------------------
    integer :: iList, iStab, iDir
    integer :: nScalars, nScalarsState_Comm, nScalarsFlux_Comm, nDer, nDof
    integer :: nTotal
    logical :: stab_reqNeigh
    integer :: nBndStabElems(tree%global%minLevel:tree%global%maxLevel,1:3)
    ! --------------------------------------------------------------------------

    ! the number of scalar variables of our equation system
    nScalars = equation%varSys%nScalars

    ! If the stabilization requires further neighbor information,
    ! we init the buffer for it
    stab_reqNeigh = .false.
    do iStab = 1, size(scheme_list(tree%global%minLevel)%stabilization)
      if ( scheme_list(tree%global%minLevel)%stabilization(iStab) &
        &                                   %reqNeigh             ) then
        stab_reqNeigh = .true.
      end if
    end do

    ! init the kerneldata on each level.
    do iList = tree%global%minLevel, tree%global%maxLevel
      ! The number of derived quantities is the number of polynomial degrees of
      ! freedoms per scalar variable.

      select case(scheme_list(iList)%modg%basisType)
      case(Q_space)
        nDer = (scheme_list(iList)%modg%maxPolyDegree+1)**3
        nDof = nDer
      case(P_space)
  nder = (((scheme_list(ilist)%modg%maxpolydegree) + 1) &
    &   * ((scheme_list(ilist)%modg%maxpolydegree) + 2) &
    &   * ((scheme_list(ilist)%modg%maxpolydegree) + 3)) &
    & / 6
        nDof = nDer
      end select

      call atl_init_kerneldata(                                             &
        &    kerneldata     = kerneldata_list(iList),                       &
        &    statedata      = statedata_list(iList),                        &
        &    nTotal         = mesh_list(iList)%descriptor%elem              &
        &                                                %nElems(eT_fluid), &
        &    nVars          = nScalars,                                     &
        &    nDofs          = nDof,                                         &
        &    nDervQuant     = nDer,                                         &
        &    time           = time,                                         &
        &    maxPolyDegree  = scheme_list(iList)%modg%maxPolyDegree,        &
        &    nDims          = 3,                                            &
        &    poly_space     = scheme_list(iList)%modg%basisType,            &
        &    need_deviation = need_deviation,                               &
        &    need_maxgrad   = equation%requires_gradmax                     )

      if(stab_reqNeigh) then
        nBndStabElems = atl_get_numBndElems(      &
          & minLevel      = tree%global%minLevel, &
          & maxLevel      = tree%global%maxLevel, &
          & boundary_list = boundary_stab_list    )
        do iDir = 1,3
          nTotal = mesh_list(iList)%faces%dimByDimDesc(iDir)%nElems &
            &    + nBndStabElems(iList,iDir)
          call atl_init_statedata(                                 &
            &    statedata      = statedata_stab_list(iList,iDir), &
            &    nTotal         = nTotal,                          &
            &    nVars          = nScalars,                        &
            &    nDofs          = nDof,                            &
            &    time           = time                             )
        end do

      end if
    end do

    ! Init the parallel module here, as well. DG requires only face values, so
    ! we init only the face buffers and do not init the cell buffers.
    ! ... for the state, we need the state and all its derivatives
    !     (in all directions)
    nScalarsState_Comm = nScalars*(1+equation%nDerivatives*3)
    ! ... for the flux, we need the numerical flux and the stabilization flux
    nScalarsFlux_Comm = nScalars*(1+equation%nDerivatives)
    call atl_init_parallel_module(                      &
      &    commPattern          = commPattern,          &
      &    scheme               = scheme_list,          &
      &    nValsElem            = nScalars,             &
      &    nValsStateFace       = nScalarsState_Comm,   &
      &    nValsFluxFace        = nScalarsFlux_Comm,    &
      &    cube                 = mesh_list,            &
      &    boundary             = boundary_list,        &
      &    createCellBuffer     = .false.,              &
      &    createFaceBuffer     = .true.,               &
      &    createStabFaceBuffer = .false.,              &
      &    createStabElemBuffer = stab_reqNeigh,        &
      &    nBndStabElems        = nBndStabElems,        &
      &    minLevel             = tree%global%minLevel, &
      &    maxLevel             = tree%global%maxLevel  )

  end subroutine atl_init_modg_kernel



  !> Subroutine to execute the preprocessing for the MODG kernels.
  !! Currently this includes: Convert external source terms to modal
  !! representation.
  subroutine atl_preprocess_modg_kernel( equation, statedata, mesh, scheme, &
    &                                    poly_proj_material, boundary,      &
  &                                      material, proc, commPattern        )
    ! --------------------------------------------------------------------------
    type(atl_equations_type), intent(inout) :: equation
    type(atl_statedata_type), intent(inout) :: statedata
    type(atl_cube_elem_type), intent(inout) :: mesh
    type(atl_scheme_type), intent(inout) :: scheme
    type(atl_material_type), intent(inout) :: material
    !> Global description of the boundary conditions.
    type(atl_level_boundary_type), intent(in) :: boundary
    type(ply_poly_project_type), intent(inout) :: poly_proj_material
    !> mpi communication environment with mpi communicator
    type(tem_comm_env_type) :: proc
    !> mpi communication pattern type
    type(tem_commPattern_type) :: commPattern
    ! --------------------------------------------------------------------------
    ! --------------------------------------------------------------------------

    call atl_update_materialParams( equation    = equation,             &
      &                             mesh        = mesh,                 &
      &                             scheme      = scheme,               &
      &                             boundary    = boundary,             &
      &                             material    = material,             &
      &                             time        = statedata%local_time, &
      &                             poly_proj   = poly_proj_material,   &
      &                             proc        = proc,                 &
      &                             commPattern = commPattern           )

  end subroutine atl_preprocess_modg_kernel




  !> Subroutine to project modal representations of physical flux, numerical
  !! flux and source terms onto test functions.
  subroutine atl_modg_project_NumFlux( mesh, equation, kerneldata, facedata, &
    &                              scheme, poly_proj, dl_prod, dl_prodDiff,  &
    &                              dirvec, penalizationdata, usePenalization )
    ! --------------------------------------------------------------------------
    !> Descritption of the cubical elements in the mesh
    type(atl_cube_elem_type), intent(in) :: mesh
    !> The equation description.
    type(atl_equations_type), intent(in) :: equation
    !> The data of the kernel. Holds the physical fluxes.
    type(atl_kerneldata_type), intent(inout) :: kerneldata
    !> The representation on the face + representation of the flux.
    type(atl_facedata_type), intent(inout) :: facedata
    !> The parameters of the MODG scheme
    type(atl_modg_scheme_type), intent(in) :: scheme
    !> Projection for the current level
    type(ply_poly_project_type), intent(inout) :: poly_proj
    !> stored scalar products of the testfunction and anstaz function
    real(kind=rk), intent(in) :: dl_prod(2, scheme%maxPolyDegree+1)
    real(kind=rk), intent(in) :: dl_prodDiff(2, scheme%maxPolyDegree+1)
    !> vector for direction indicators
    integer, intent(in) :: dirVec(3,3)
    !> Volumetric data for the penalization
    type(atl_penalizationData_type), intent(in) :: penalizationdata
    !> Flag to indicate, whether we need to take care of the penalization
    !! terms here or not.
    logical, intent(in) :: usePenalization
    ! --------------------------------------------------------------------------
    integer :: nScalars, nFaces, leftOrRight, nElems_fluid
    !> The direction
    integer :: iDir
    type(atl_faceRep_type), allocatable :: faceReP_Q(:)
    type(atl_faceRep_type), allocatable :: faceFlux_Q(:)
    real(kind=rk), allocatable ::state_der_Q(:,:,:)
    ! --------------------------------------------------------------------------
    nScalars = equation%varSys%nScalars
    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid)


    ! Projection of the numerical flux
    do iDir = 1, 3
      select case(scheme%basisType)
      case(Q_space)
        call modg_project_numFlux_Q(                                         &
          &    nTotalFaces   = size(facedata%faceFlux(iDir)%dat,1),          &
          &    nFaceDofs     = size(facedata%faceFlux(iDir)%dat,2),          &
          &    nScalars      = nScalars,                                     &
          &    faceFlux      = facedata%faceFlux(iDir)%dat(:,:,:nScalars,:), &
          &    maxPolyDegree = scheme%maxPolyDegree,                         &
          &    length        = mesh%length,                                  &
          &    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid),        &
          &    dl_prod       = dl_prod,                                      &
          &    projection    = kerneldata%state_der,                         &
          &    dirVec        = dirVec(:,iDir)                                )

      case(P_space)
        call modg_project_numFlux_P(                                         &
          &    nTotalFaces   = size(facedata%faceFlux(iDir)%dat,1),          &
          &    nFaceDofs     = size(facedata%faceFlux(iDir)%dat,2),          &
          &    nScalars      = nScalars,                                     &
          &    faceFlux      = facedata%faceFlux(iDir)%dat(:,:,:nScalars,:), &
          &    maxPolyDegree = scheme%maxPolyDegree,                         &
          &    length        = mesh%length,                                  &
          &    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid),        &
          &    dl_prod       = dl_prod,                                      &
          &    projection    = kerneldata%state_der,                         &
          &    dirVec        = dirVec(:,iDir)                                )

      end select
    end do

    if (equation%nDerivatives == 1) then
      select case(equation%eq_kind)
      case('heat')
        select case(scheme%basisType)
        case(Q_space)
        do iDir = 1, 3
          ! Projection of the appropriate  numerical flux onto the derivative
          ! of the test functions
          call modg_project_numFlux_diffTest_Q(                             &
            &    nScalars      = nScalars,                                  &
            &    faceFlux      = facedata%faceFlux(iDir)                    &
            &                            %dat(:,:,1+nScalars:2*nScalars,:), &
            &    maxPolyDegree = scheme%maxPolyDegree,                      &
            &    length        = mesh%length,                               &
            &    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid),     &
            &    dl_prod       = dl_prodDiff,                               &
            &    projection    = kerneldata%state_der,                      &
            &    dirVec        = dirVec(:,iDir)                             )
        end do

        case(P_space)
          allocate(faceFlux_Q(3))
          allocate(state_der_Q(nElems_fluid,(scheme%maxPolyDegree+1)**3, &
            &                    nScalars))
          do iDir = 1,3
            nFaces = size(facedata%faceflux(iDir)%dat,1)
            allocate(faceFlux_Q(iDir)%dat(nFaces,(scheme%maxPolyDegree+1)**2, &
              &                           2*nScalars,2))
            do leftOrRight = 1,2
              call ply_change_poly_space( inspace    = P_space,                 &
                &                         instate    = facedata%faceflux(iDir)  &
                &                                      %dat(:,:,:,leftOrRight), &
                &                         outstate   = faceFlux_Q(iDir)         &
                &                                      %dat(:,:,:,leftORRight), &
                &                         maxPolyDeg = scheme%maxPolyDegree,    &
                &                         nElems     = nElems_fluid,            &
                &                         nVars      = 2*nScalars,              &
                &                         nDims      = 2                        )
            end do
          end do

          call ply_change_poly_space( inspace    = P_space,              &
            &                         instate    = kerneldata%state_der, &
            &                         outstate   = state_der_Q,          &
            &                         maxPolyDeg = scheme%maxPolyDegree, &
            &                         nElems     = nElems_fluid,         &
            &                         nVars      = nScalars,             &
            &                         nDims      = 3                     )
          ! Projection of the appropriate  numerical flux onto the derivative
          ! of the test functions
          do iDir = 1,3
            call modg_project_numFlux_diffTest_Q(                         &
              &    nScalars      = nScalars,                              &
              &    faceFlux      = faceFlux_Q(iDir)                       &
              &                    %dat(:,:,1+nScalars:2*nScalars,:),     &
              &    maxPolyDegree = scheme%maxPolyDegree,                  &
              &    length        = mesh%length,                           &
              &    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid), &
              &    dl_prod       = dl_prodDiff,                           &
              &    projection    = state_der_Q,                           &
              &    dirVec        = dirVec(:,iDir)                         )
          end do

          do iDir = 1,3
            do leftOrRight = 1,2
              call ply_change_poly_space( inspace    = Q_space,                 &
                &                         instate    = faceFlux_Q(iDir)         &
                &                                      %dat(:,:,:,leftORRight), &
                &                         outstate   = facedata%faceflux(iDir)  &
                &                                      %dat(:,:,:,leftOrRight), &
                &                         maxPolyDeg = scheme%maxPolyDegree,    &
                &                         nElems     = nElems_fluid,            &
                &                         nVars      = 2*nScalars,              &
                &                         nDims      = 2                        )
            end do
          end do
          call ply_change_poly_space( inspace    = Q_space,              &
            &                         instate    = state_der_Q,          &
            &                         outstate   = kerneldata%state_der, &
            &                         maxPolyDeg = scheme%maxPolyDegree, &
            &                         nElems     = nElems_fluid,         &
            &                         nVars      = nScalars,             &
            &                         nDims      = 3                     )

          deallocate(faceFlux_Q)
          deallocate(state_der_Q)

        end select

      case('navier_stokes', 'filtered_navier_stokes')
        ! Projection of the numerical flux
        ! ... x faces
        select case(scheme%basisType)
        case(Q_space)
          call modg_project_stabViscNumFluxX_Q(                         &
            &    numFlux       = facedata%faceFlux(1)%dat,              &
            &    faceState     = facedata%faceRep(1)%dat,               &
            &    equation      = equation,                              &
            &    maxPolyDegree = scheme%maxPolyDegree,                  &
            &    length        = mesh%length,                           &
            &    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid), &
            &    projection    = kerneldata%state_der,                  &
            &    poly_proj     = poly_proj                              )
          ! ... y faces
          call modg_project_stabViscNumFluxY_Q(                         &
            &    numFlux       = facedata%faceFlux(2)%dat,              &
            &    faceState     = facedata%faceRep(2)%dat,               &
            &    equation      = equation,                              &
            &    maxPolyDegree = scheme%maxPolyDegree,                  &
            &    length        = mesh%length,                           &
            &    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid), &
            &    projection    = kerneldata%state_der,                  &
            &    poly_proj     = poly_proj                              )

          ! ... z faces
          call modg_project_stabViscNumFluxZ_Q(                         &
            &    numFlux       = facedata%faceFlux(3)%dat,              &
            &    faceState     = facedata%faceRep(3)%dat,               &
            &    equation      = equation,                              &
            &    maxPolyDegree = scheme%maxPolyDegree,                  &
            &    length        = mesh%length,                           &
            &    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid), &
            &    projection    = kerneldata%state_der,                  &
            &    poly_proj     = poly_proj                              )
        case(P_space)

          ! For P_space we need to change the polynomial space if we
          ! want to use the same routines that are used for the
          ! Q_space case.

          allocate(faceFlux_Q(3))
          allocate(faceRep_Q(3))
          allocate(state_der_Q(nElems_fluid,(scheme%maxPolyDegree+1)**3, &
            &                    nScalars))

          do iDir = 1,3
            nFaces = size(facedata%faceflux(iDir)%dat,1)
            allocate(faceFlux_Q(iDir)%dat(nFaces,(scheme%maxPolyDegree+1)**2, &
              &                             2*nScalars,2))
            allocate(faceRep_Q(iDir)%dat(nFaces,(scheme%maxPolyDegree+1)**2, &
              &                            2*nScalars,2))
            do leftOrRight = 1,2
              call ply_change_poly_space( inspace    = P_space,                 &
                &                         instate    = facedata%faceflux(iDir)  &
                &                                      %dat(:,:,:,leftOrRight), &
                &                         outstate   = faceFlux_Q(iDir)         &
                &                                      %dat(:,:,:,leftORRight), &
                &                         maxPolyDeg = scheme%maxPolyDegree,    &
                &                         nElems     = nElems_fluid,            &
                &                         nVars      = 2*nScalars,              &
                &                         nDims      = 2                        )

              call ply_change_poly_space( inspace    = P_space,                 &
                &                         instate    = facedata%faceRep(iDir)   &
                &                                      %dat(:,:,:,leftOrRight), &
                &                         outstate   = faceRep_Q(iDir)          &
                &                                      %dat(:,:,:,leftOrRight), &
                &                         maxPolyDeg = scheme%maxPolyDegree,    &
                &                         nElems     = nElems_fluid,            &
                &                         nVars      = 2*nScalars,              &
                &                         nDims      = 2                        )
            end do
          end do

          call ply_change_poly_space( inspace    = P_space,              &
            &                         instate    = kerneldata%state_der, &
            &                         outstate   = state_der_Q,          &
            &                         maxPolyDeg = scheme%maxPolyDegree, &
            &                         nElems     = nElems_fluid,         &
            &                         nVars      = nScalars,             &
            &                         nDims      = 3                     )

          call modg_project_stabViscNumFluxX_Q(                         &
            &    numFlux       = faceFlux_Q(1)%dat,                     &
            &    faceState     = faceRep_Q(1)%dat,                      &
            &    equation      = equation,                              &
            &    maxPolyDegree = scheme%maxPolyDegree,                  &
            &    length        = mesh%length,                           &
            &    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid), &
            &    projection    = state_der_Q,                           &
            &    poly_proj     = poly_proj                              )
          ! ... y faces
          call modg_project_stabViscNumFluxY_Q(                         &
            &    numFlux       = faceFlux_Q(2)%dat,                     &
            &    faceState     = faceRep_Q(2)%dat,                      &
            &    equation      = equation,                              &
            &    maxPolyDegree = scheme%maxPolyDegree,                  &
            &    length        = mesh%length,                           &
            &    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid), &
            &    projection    = state_der_Q,                           &
            &    poly_proj     = poly_proj                              )

          ! ... z faces
          call modg_project_stabViscNumFluxZ_Q(                         &
            &    numFlux       = faceFlux_Q(3)%dat,                     &
            &    faceState     = faceRep_Q(3)%dat,                      &
            &    equation      = equation,                              &
            &    maxPolyDegree = scheme%maxPolyDegree,                  &
            &    length        = mesh%length,                           &
            &    nElems_fluid  = mesh%descriptor%elem%nElems(eT_fluid), &
            &    projection    = state_der_Q,                           &
            &    poly_proj     = poly_proj                              )

          do iDir = 1,3
            do leftOrRight = 1,2
              call ply_change_poly_space( inspace    = Q_space,                 &
                &                         instate    = faceFlux_Q(iDir)         &
                &                                      %dat(:,:,:,leftORRight), &
                &                         outstate   = facedata%faceflux(iDir)  &
                &                                      %dat(:,:,:,leftOrRight), &
                &                         maxPolyDeg = scheme%maxPolyDegree,    &
                &                         nElems     = nElems_fluid,            &
                &                         nVars      = 2*nScalars,              &
                &                         nDims      = 2                        )

              call ply_change_poly_space( inspace    = Q_space,                 &
                &                         instate    = faceRep_Q(iDir)          &
                &                                      %dat(:,:,:,leftOrRight), &
                &                         outstate   = facedata%faceRep(iDir)   &
                &                                      %dat(:,:,:,leftOrRight), &
                &                         maxPolyDeg = scheme%maxPolyDegree,    &
                &                         nElems     = nElems_fluid,            &
                &                         nVars      = 2*nScalars,              &
                &                         nDims      = 2                        )
            end do
          end do
          call ply_change_poly_space( inspace    = Q_space,              &
            &                         instate    = state_der_Q,          &
            &                         outstate   = kerneldata%state_der, &
            &                         maxPolyDeg = scheme%maxPolyDegree, &
            &                         nElems     = nElems_fluid,         &
            &                         nVars      = nScalars,             &
            &                         nDims      = 3                     )

          deallocate(faceRep_Q)
          deallocate(faceFlux_Q)
          deallocate(state_der_Q)
        end select

      case default
        write(logUnit(1),*) 'ERROR in atl_modg_project_NumFlux:'
        write(logUnit(1),*) 'Unknown equation',equation%eq_kind
        write(logUnit(1),*) 'projections of viscous stabilization,'
        write(logUnit(1),*) ' stopping ... '
        call tem_abort()
      end select
    end if

    !> TODO NA - maybe move this call out of this routine ?
    ! Projection of the penalization terms if not computed elsewhere.
    if (penalizationdata%isActive .and. usePenalization) then
      select case(scheme%basisType)
      case(Q_space)
       call modg_project_penalization_Q(                   &
         &    mesh             = mesh,                     &
         &    maxPolyDegree    = scheme%maxPolyDegree,     &
         &    penalizationdata = penalizationdata,         &
         &    kerneldata       = kerneldata                )
      case(P_space)
        write(logUnit(1),*) 'ERROR in atl_modg_project_NumFlux: '
        write(logUnit(1),*) 'Penelization not yet implemented for P_space'
        call tem_abort
      end select
    end if
  end subroutine atl_modg_project_NumFlux


  !> Projection of the numerical flux in x direction onto the testfunctions.
  subroutine modg_project_stabViscNumFluxX_Q(                       &
    &          numFlux, faceState, equation, maxPolyDegree, length, &
    &          nElems_fluid, projection, poly_proj                  )
    ! -------------------------------------------------------------------- !
    !> The numerical flux on the faces in modal representations.
    !! Dimension is (maxPolyDegree+1)^2 , nScalars
    real(kind=rk), intent(inout) :: numFlux(:,:,:,:)
    !> The state on the faces in modal representations.
    !! Dimension is (maxPolyDegree+1)^2 , nScalars
    real(kind=rk), intent(inout) :: faceState(:,:,:,:)
    !> The equation system under consideration
    type(atl_equations_type), intent(in) :: equation
    !> The maximal polynomial degree in each spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The length of the cubes.
    real(kind=rk), intent(in) :: length
    !> The element index
    integer, intent(in) :: nElems_fluid
    !> The numerical flux projected onto the test functions.
    real(kind=rk), intent(inout) :: projection(:,:,:)
    !> Projection for the current level
    type(ply_poly_project_type), intent(inout) :: poly_proj
    ! -------------------------------------------------------------------- !
    integer :: iVar, iElem, iPoint, testPos, iVP
    integer :: iTestX, iTestY, iTestZ
    integer :: iOversamp, iOrig
    integer :: iDof, iDofY, iDofX
    integer :: nScalars, nPoints, nPVars, nOversamp

    real(kind=rk) :: p_a_left(equation%varSys%nScalars)
    real(kind=rk) :: p_b_left(equation%varSys%nScalars)
    real(kind=rk) :: p_c_left(equation%varSys%nScalars)

    real(kind=rk) :: p_a_right(equation%varSys%nScalars)
    real(kind=rk) :: p_b_right(equation%varSys%nScalars)
    real(kind=rk) :: p_c_right(equation%varSys%nScalars)

    real(kind=rk), allocatable :: flux_left(:,:)
    real(kind=rk), allocatable :: pointVal_flux_left(:,:)
    real(kind=rk), allocatable :: pointVal_left(:,:)
    real(kind=rk), allocatable :: state_left(:,:)
    real(kind=rk), allocatable :: nodal_a_left(:,:)
    real(kind=rk), allocatable :: nodal_b_left(:,:)
    real(kind=rk), allocatable :: nodal_c_left(:,:)
    real(kind=rk), allocatable :: modalA_left(:,:)
    real(kind=rk), allocatable :: modalB_left(:,:)
    real(kind=rk), allocatable :: modalC_left(:,:)

    real(kind=rk), allocatable :: flux_right(:,:)
    real(kind=rk), allocatable :: pointVal_flux_right(:,:)
    real(kind=rk), allocatable :: pointVal_right(:,:)
    real(kind=rk), allocatable :: state_right(:,:)
    real(kind=rk), allocatable :: nodal_a_right(:,:)
    real(kind=rk), allocatable :: nodal_b_right(:,:)
    real(kind=rk), allocatable :: nodal_c_right(:,:)
    real(kind=rk), allocatable :: modalA_right(:,:)
    real(kind=rk), allocatable :: modalB_right(:,:)
    real(kind=rk), allocatable :: modalC_right(:,:)

    real(kind=rk) :: velocity_left(3)
    real(kind=rk) :: velocity_right(3)
    real(kind=rk) :: jacobiDet
    real(kind=rk) :: testX_val_left, testX_grad_val_left
    real(kind=rk) :: testX_val_right, testX_grad_val_right
    real(kind=rk) :: outerNormalLeft, outerNormalRight
    ! -------------------------------------------------------------------- !

    nScalars = equation%varSys%nScalars
    nPoints = poly_proj%body_2D%nquadpoints
    nOversamp = poly_proj%body_2D%oversamp_dofs

    nPVars = (poly_proj%min_Degree+1)**2*equation%varSys%nScalars

    allocate( flux_left(nOversamp, nScalars), flux_right(nOversamp, nScalars) )
    allocate( state_left(nOversamp, nScalars) )
    allocate( state_right(nOversamp, nScalars) )
    allocate( pointVal_flux_left(nPoints, nScalars) )
    allocate( pointVal_flux_right(nPoints, nScalars) )
    allocate( pointVal_left(nPoints, nScalars) )
    allocate( pointVal_right(nPoints, nScalars) )
    allocate( nodal_a_left(nPoints, nScalars) )
    allocate( nodal_b_left(nPoints, nScalars) )
    allocate( nodal_c_left(nPoints, nScalars) )
    allocate( nodal_a_right(nPoints, nScalars) )
    allocate( nodal_b_right(nPoints, nScalars) )
    allocate( nodal_c_right(nPoints, nScalars) )
    allocate( modalA_left(nOversamp,nScalars) )
    allocate( modalB_left(nOversamp, nScalars) )
    allocate( modalC_left(nOversamp,nScalars) )
    allocate( modalA_right(nOversamp,nScalars) )
    allocate( modalB_right(nOversamp, nScalars) )
    allocate( modalC_right(nOversamp, nScalars) )

    ! The 1D Jacobi determinant
    jacobiDet = 0.5_rk * length

    ! The outer unit normals scaled by jacobiDet as this is a common factor
    outerNormalLeft = jacobiDet * atl_elemfaceToNormal_prp(tem_left)
    outerNormalRight = jacobiDet * atl_elemfaceToNormal_prp(tem_right)


    elementLoop: do iElem = 1,nElems_fluid

      state_left = 0.0_rk
      state_right = 0.0_rk
      flux_left = 0.0_rk
      flux_right = 0.0_rk

      do iVP = 1,nPVars
        iVar = (iVP-1)/((poly_proj%min_degree+1)**2) + 1
        iDof = iVP - (iVar-1)*((poly_proj%min_degree+1)**2)
        iDofY = (iDof-1) / (poly_proj%min_Degree+1)
        iDofX = mod(iDof-1,poly_proj%min_degree+1)
        iOversamp = iDofY*(poly_proj%oversamp_degree+1) + iDofX + 1
        iOrig = iDofY*(poly_proj%maxPolyDegree+1) + iDofX + 1

        ! Get u
        ! ... for left face
        state_left(iOversamp,iVar) = faceState(iElem,iOrig,iVar,1)
        ! ... for right face
        state_right(iOversamp,iVar) = faceState(iElem,iOrig,iVar,2)

        ! Caluclate (u^* - u)
        ! ... for the left face
        flux_left(iOversamp,iVar) = numFlux(iElem,iOrig,iVar+nScalars,1)  &
          &                       - state_left(iOversamp,iVar)
        ! ... for the right face
        flux_right(iOversamp,iVar) = numFlux(iElem,iOrig,iVar+nScalars,2) &
          &                        - state_right(iOversamp,iVar)
      end do

      ! Transform u to nodal representation
      ! ... for the left face
      call ply_poly_project_m2n(me = poly_proj,              &
        &                       dim = 2 ,                    &
        &                       nVars = nScalars,            &
        &                       nodal_data = pointVal_left,  &
        &                       modal_data = state_left      )
      ! ... for the right face
      call ply_poly_project_m2n(me = poly_proj,              &
        &                       dim = 2 ,                    &
        &                       nVars = nScalars,            &
        &                       nodal_data = pointVal_right, &
        &                       modal_data = state_right     )

      ! Transform (u^* - u) to nodal representation
      ! ... for the left face
      call ply_poly_project_m2n(me = poly_proj,                  &
        &                       dim = 2 ,                        &
        &                       nVars = nScalars,                &
        &                       nodal_data = pointVal_flux_left, &
        &                       modal_data = flux_left           )
      ! ... for the right face
      call ply_poly_project_m2n(me = poly_proj,                   &
        &                       dim = 2 ,                         &
        &                       nVars = nScalars,                 &
        &                       nodal_data = pointVal_flux_right, &
        &                       modal_data = flux_right           )

      ! Loop over all the points
      pointLoop: do iPoint = 1, nPoints

        ! Caculate velocity at this point
        ! ... for the left face
        velocity_left(1:3) = pointVal_left(iPoint,2:4) &
          &                  / pointVal_left(iPoint,1)
        ! ... for the right face
        velocity_right(1:3) = pointVal_right(iPoint,2:4) &
          &                   / pointVal_right(iPoint,1)

        ! Build matrix-vector product of nu_11 and values at current point
        ! ... for the left face
        nodal_a_left(iPoint,:)                                 &
          &  = atl_mult_nu11_NavierStokes(                     &
          &      density   = pointVal_left(iPoint,1),          &
          &      velocity  = velocity_left,                    &
          &      totEnergy = pointVal_left(iPoint,5),          &
          &      inVec     = pointVal_flux_left(iPoint,:),     &
          &      mu        = equation%NavierStokes%mu,         &
          &      lambda    = equation%NavierStokes%lambda,     &
          &      thermCond = equation%NavierStokes%therm_cond, &
          &      heatCap   = equation%euler%cv                 )

        ! ... for the right face
        nodal_a_right(iPoint,:)                                &
          &  = atl_mult_nu11_NavierStokes(                     &
          &      density   = pointVal_right(iPoint,1),         &
          &      velocity  = velocity_right,                   &
          &      totEnergy = pointVal_right(iPoint,5),         &
          &      inVec     = pointVal_flux_right(iPoint,:),    &
          &      mu        = equation%NavierStokes%mu,         &
          &      lambda    = equation%NavierStokes%lambda,     &
          &      thermCond = equation%NavierStokes%therm_cond, &
          &      heatCap   = equation%euler%cv                 )

        ! Build matrix-vector product of nu_21 and values at current point
        ! ... for the left face
        nodal_b_left(iPoint,:)                                 &
          &  = atl_mult_nu21_NavierStokes(                     &
          &      density   = pointVal_left(iPoint,1),          &
          &      velocity  = velocity_left,                    &
          &      inVec     = pointVal_flux_left(iPoint,:),     &
          &      mu        = equation%NavierStokes%mu,         &
          &      lambda    = equation%NavierStokes%lambda      )

        ! ... for the right face
        nodal_b_right(iPoint,:)                                &
          &  = atl_mult_nu21_NavierStokes(                     &
          &      density   = pointVal_right(iPoint,1),         &
          &      velocity  = velocity_right,                   &
          &      inVec     = pointVal_flux_right(iPoint,:),    &
          &      mu        = equation%NavierStokes%mu,         &
          &      lambda    = equation%NavierStokes%lambda      )

        ! Build matrix-vector product of nu_31 and values at current point
        ! ... for the left face
        nodal_c_left(iPoint,:)                                 &
          &  = atl_mult_nu31_NavierStokes(                     &
          &      density   = pointVal_left(iPoint,1),          &
          &      velocity  = velocity_left,                    &
          &      inVec     = pointVal_flux_left(iPoint,:),     &
          &      mu        = equation%NavierStokes%mu,         &
          &      lambda    = equation%NavierStokes%lambda      )

        ! ... for the right face
        nodal_c_right(iPoint,:)                                &
          &  = atl_mult_nu31_NavierStokes(                     &
          &      density   = pointVal_right(iPoint,1),         &
          &      velocity  = velocity_right,                   &
          &      inVec     = pointVal_flux_right(iPoint,:),    &
          &      mu        = equation%NavierStokes%mu,         &
          &      lambda    = equation%NavierStokes%lambda      )

      end do pointLoop

      ! Transform nodal_a and nodal_b back to modal space for projections
      ! ... for the left face
      call ply_poly_project_n2m(me         = poly_proj,    &
        &                       dim        = 2,            &
        &                       nVars      = nScalars,     &
        &                       nodal_data = nodal_a_left, &
        &                       modal_data = modalA_left   )
      call ply_poly_project_n2m(me         = poly_proj,    &
        &                       dim        = 2,            &
        &                       nVars      = nScalars,     &
        &                       nodal_data = nodal_b_left, &
        &                       modal_data = modalB_left   )
      call ply_poly_project_n2m(me         = poly_proj,    &
        &                       dim        = 2,            &
        &                       nVars      = nScalars,     &
        &                       nodal_data = nodal_c_left, &
        &                       modal_data = modalC_left   )

      ! ... for the right face
      call ply_poly_project_n2m(me         = poly_proj,     &
        &                       dim        = 2,             &
        &                       nVars      = nScalars,      &
        &                       nodal_data = nodal_a_right, &
        &                       modal_data = modalA_right   )
      call ply_poly_project_n2m(me         = poly_proj,     &
        &                       dim        = 2,             &
        &                       nVars      = nScalars,      &
        &                       nodal_data = nodal_b_right, &
        &                       modal_data = modalB_right   )
      call ply_poly_project_n2m(me         = poly_proj,     &
        &                       dim        = 2,             &
        &                       nVars      = nScalars,      &
        &                       nodal_data = nodal_c_right, &
        &                       modal_data = modalC_right   )

      ! Project onto all the test functions
      xTestLoop: do iTestX = 1, maxPolyDegree+1
        ! Evaluate test function in x direction at face coordinate
        ! ... for the left face
        testX_val_left = ply_faceValLeftBndTest(iTestX)
        testX_grad_val_left = ply_faceValLeftBndTestGrad(iTestX)
        ! ... for the right face
        testX_val_right = ply_faceValRightBndTest(iTestX)
        testX_grad_val_right = ply_faceValRightBndTestGrad(iTestX)

        ! Now we compute the projections. Since the test functions
        ! change when iTestY > 2 we moved the first two projections of
        ! of the projection loop.

        ! Project onto the first test function in y direction
        iTestY = 1
        iTestZ = 1
        testpos = iTestX
        ! ... for the left face
        p_a_left = jacobiDet * testX_grad_val_left  &
          &        * ply_scalProdDualLeg(iTestY,iTestY) &
          &        * ply_scalProdDualLeg(iTestZ,iTestZ) &
          &        * modalA_left(iTestY+(iTestZ-1)*(maxPolyDegree+1),:)
        projection(iElem,testPos,:) = projection(iElem,testPos,:)     &
          &                         - outerNormalLeft * ( p_a_left(:) )

        ! ... for the right face
        p_a_right = jacobiDet * testX_grad_val_right              &
          &         * ply_scalProdDualLeg(iTestY,iTestY) &
          &         * ply_scalProdDualLeg(iTestZ,iTestZ) &
          &         * modalA_right(iTestY+(iTestZ-1)*(maxPolyDegree+1),:)
        projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
          &                         - outerNormalRight * ( p_a_right(:) )

        if (maxPolyDegree+1 > 1) then

          ! Continue iTestY=1...
          iTestZ = 2
          testPos = iTestX &
            &     + ( (iTestY-1) + (iTestZ-1)*(maxPolyDegree+1) ) &
            &       * (maxPolyDegree+1)

          ! ... for the left face
          p_a_left = jacobiDet * testX_grad_val_left      &
            &        * ply_scalProdDualLeg(iTestY,iTestY) &
            &        * ply_scalProdDualLeg(iTestZ,iTestZ) &
            &        * modalA_left(iTestY+(iTestZ-1)*(maxPolyDegree+1),:)
          p_c_left = testX_val_left                             &
            &        * ply_scalProdDualLeg(iTestY,iTestY)       &
            &        * ply_scalProdDualLegDiff(iTestZ-1,iTestZ) &
            &        * modalC_left(iTestY+(iTestZ-2)*(maxPolyDegree+1),:)
          projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
            &                         - outerNormalLeft * ( p_a_left(:)   &
            &                                               + p_c_left(:) )

          ! ... for the right face
          p_a_right = jacobiDet * testX_grad_val_right &
            &         * ply_scalProdDualLeg(iTestY,iTestY) &
            &         * ply_scalProdDualLeg(iTestZ,iTestZ) &
            &         * modalA_right(iTestY+(iTestZ-1)*(maxPolyDegree+1),:)
          p_c_right = testX_val_right &
            &         * ply_scalProdDualLeg(iTestY,iTestY) &
            &         * ply_scalProdDualLegDiff(iTestZ-1,iTestZ) &
            &         * modalC_right(iTestY+(iTestZ-2)*(maxPolyDegree+1),:)
          projection(iElem,testPos,:) = projection(iElem,testPos,:)         &
            &                         - outerNormalRight * ( p_a_right(:)   &
            &                                                + p_c_right(:) )

          do iTestZ = 3, maxPolyDegree+1
            testPos = iTestX &
              &     + ( (iTestY-1) + (iTestZ-1)*(maxPolyDegree+1) ) &
              &       * (maxPolyDegree+1)

            ! ... for the left face
            p_a_left = jacobiDet * testX_grad_val_left                        &
              &        * ( ply_scalProdDualLeg(iTestY,iTestY)                 &
              &            * ply_scalProdDualLeg(iTestZ,iTestZ)               &
              &            * modalA_left(iTestY+(iTestZ-1)*(maxPolyDegree+1), &
              &                          :)                                   &
              &            + ply_scalProdDualLeg(iTestY,iTestY)               &
              &              * ply_scalProdDualLeg(iTestZ-2,iTestZ)           &
              &              * modalA_left(iTestY                             &
              &                            +(iTestZ-3)*(maxPolyDegree+1),:)   &
              &          )
            p_c_left = testX_val_left                                     &
              &        * ( ply_scalProdDualLeg(iTestY,iTestY)             &
              &            * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)     &
              &            * modalC_left(iTestY                           &
              &                          +(iTestZ-2)*(maxPolyDegree+1),:) &
              &          )
            projection(iElem,testPos,:) = projection(iElem,testPos,:)     &
              &                         - outerNormalLeft * ( p_a_left(:) &
              &                                             + p_c_left(:) )

            ! ... for the right face
            p_a_right = jacobiDet * testX_grad_val_right                    &
              &         * ( ply_scalProdDualLeg(iTestY,iTestY)              &
              &             * ply_scalProdDualLeg(iTestZ,iTestZ)            &
              &             * modalA_right(iTestY                           &
              &                            +(iTestZ-1)*(maxPolyDegree+1),:) &
              &             + ply_scalProdDualLeg(iTestY,iTestY)            &
              &               * ply_scalProdDualLeg(iTestZ-2,iTestZ)        &
              &             * modalA_right(iTestY                           &
              &                            +(iTestZ-3)*(maxPolyDegree+1),:) &
              &           )
            p_c_right = testX_val_right                                     &
              &         * ( ply_scalProdDualLeg(iTestY,iTestY)              &
              &             * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)      &
              &             * modalC_right(iTestY                           &
              &                            +(iTestZ-2)*(maxPolyDegree+1),:) &
              &           )
            projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
              &                         - outerNormalRight * ( p_a_right(:) &
              &                                              + p_c_right(:) )
          end do

          ! Next iTestY iteration
          iTestY = 2

          iTestZ = 1
          testPos = iTestX &
            &     + ( (iTestY-1) + (iTestZ-1)*(maxPolyDegree+1) ) &
            &       * (maxPolyDegree+1)

          ! ... for the left face
          p_a_left = jacobiDet * testX_grad_val_left      &
            &        * ply_scalProdDualLeg(iTestY,iTestY) &
            &        * ply_scalProdDualLeg(iTestZ,iTestZ) &
            &        * modalA_left(iTestY+(iTestZ-1)*(maxPolyDegree+1),:)

          p_b_left(:) = testX_val_left                             &
            &           * ply_scalProdDualLegDiff(iTestY-1,iTestY) &
            &           * ply_scalProdDualLeg(iTestZ,iTestZ)       &
            &           * modalB_left(iTestY-1+(iTestZ-1)*(maxPolyDegree+1),:)
          projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
            &                         - outerNormalLeft * ( p_a_left(:)   &
            &                                               + p_b_left(:) )

          ! ... for the right face
          p_a_right = jacobiDet * testX_grad_val_right     &
            &         * ply_scalProdDualLeg(iTestY,iTestY) &
            &         * ply_scalProdDualLeg(iTestZ,iTestZ) &
            &         * modalA_right(iTestY+(iTestZ-1)*(maxPolyDegree+1),:)
          p_b_right = testX_val_right                            &
            &         * ply_scalProdDualLegDiff(iTestY-1,iTestY) &
            &         * ply_scalProdDualLeg(iTestZ,iTestZ)       &
            &         * modalB_right(iTestY-1+(iTestZ-1)*(maxPolyDegree+1),:)
          projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
            &                         - outerNormalRight * ( p_a_right(:) &
            &                                              + p_b_right(:) )

          iTestZ = 2
          testPos = iTestX &
            &     + ( (iTestY-1) + (iTestZ-1)*(maxPolyDegree+1) ) &
            &       * (maxPolyDegree+1)

          ! ... for the left face
          p_a_left = jacobiDet * testX_grad_val_left      &
            &        * ply_scalProdDualLeg(iTestY,iTestY) &
            &        * ply_scalProdDualLeg(iTestZ,iTestZ) &
            &        * modalA_left(iTestY+(iTestZ-1)*(maxPolyDegree+1),:)
          p_b_left = testX_val_left                             &
            &        * ply_scalProdDualLegDiff(iTestY-1,iTestY) &
            &        * ply_scalProdDualLeg(iTestZ,iTestZ)       &
            &        * modalB_left(iTestY-1+(iTestZ-1)*(maxPolyDegree+1),:)
          p_c_left = testX_val_left                             &
            &        * ply_scalProdDualLeg(iTestY,iTestY)       &
            &        * ply_scalProdDualLegDiff(iTestZ-1,iTestZ) &
            &        * modalC_left(iTestY+(iTestZ-2)*(maxPolyDegree+1),:)
          projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
            &                         - outerNormalLeft * ( p_a_left(:)   &
            &                                               + p_b_left(:) &
            &                                               + p_c_left(:) )

          ! ... for the right face
          p_a_right = jacobiDet * testX_grad_val_right     &
            &         * ply_scalProdDualLeg(iTestY,iTestY) &
            &         * ply_scalProdDualLeg(iTestZ,iTestZ) &
            &         * modalA_right(iTestY+(iTestZ-1)*(maxPolyDegree+1),:)
          p_b_right = testX_val_right                            &
            &         * ply_scalProdDualLegDiff(iTestY-1,iTestY) &
            &         * ply_scalProdDualLeg(iTestZ,iTestZ)       &
            &         * modalB_right(iTestY-1+(iTestZ-1)*(maxPolyDegree+1),:)
          p_c_right = testX_val_right                            &
            &         * ply_scalProdDualLeg(iTestY,iTestY)       &
            &         * ply_scalProdDualLegDiff(iTestZ-1,iTestZ) &
            &         * modalC_right(iTestY+(iTestZ-2)*(maxPolyDegree+1),:)
          projection(iElem,testPos,:) = projection(iElem,testPos,:)         &
            &                         - outerNormalRight * ( p_a_right(:)   &
            &                                                + p_b_right(:) &
            &                                                + p_c_right(:) )
          do iTestZ = 3, maxPolyDegree+1
            testPos = iTestX &
              &     + ( (iTestY-1) + (iTestZ-1)*(maxPolyDegree+1) ) &
              &       * (maxPolyDegree+1)

            ! ... for the left face
            p_a_left = jacobiDet * testX_grad_val_left                      &
              &        * ( ply_scalProdDualLeg(iTestY,iTestY)               &
              &            * ply_scalProdDualLeg(iTestZ,iTestZ)             &
              &            * modalA_left(iTestY                             &
              &                          +(iTestZ-1)*(maxPolyDegree+1),:)   &
              &            + ply_scalProdDualLeg(iTestY,iTestY)             &
              &              * ply_scalProdDualLeg(iTestZ-2,iTestZ)         &
              &              * modalA_left(iTestY                           &
              &                            +(iTestZ-3)*(maxPolyDegree+1),:) &
              &          )
            p_b_left = testX_val_left                                       &
              &        * ( ply_scalProdDualLegDiff(iTestY-1,iTestY)         &
              &            * ply_scalProdDualLeg(iTestZ,iTestZ)             &
              &            * modalB_left(iTestY-1                           &
              &                          +(iTestZ-1)*(maxPolyDegree+1),:)   &
              &            + ply_scalProdDualLegDiff(iTestY-1,iTestY)       &
              &              * ply_scalProdDualLeg(iTestZ-2,iTestZ)         &
              &              * modalB_left(iTestY-1                         &
              &                            +(iTestZ-3)*(maxPolyDegree+1),:) &
              &          )
            p_c_left = testX_val_left                                     &
              &        * ( ply_scalProdDualLeg(iTestY,iTestY)             &
              &            * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)     &
              &            * modalC_left(iTestY                           &
              &                          +(iTestZ-2)*(maxPolyDegree+1),:) &
              &          )
            projection(iElem,testPos,:) = projection(iElem,testPos,:)     &
              &                         - outerNormalLeft * ( p_a_left(:) &
              &                                             + p_b_left(:) &
              &                                             + p_c_left(:) )

            ! ... for the right face
            p_a_right = jacobiDet * testX_grad_val_right                      &
              &         * ( ply_scalProdDualLeg(iTestY,iTestY)                &
              &             * ply_scalProdDualLeg(iTestZ,iTestZ)              &
              &             * modalA_right(iTestY                             &
              &                            +(iTestZ-1)*(maxPolyDegree+1),:)   &
              &             + ply_scalProdDualLeg(iTestY,iTestY)              &
              &               * ply_scalProdDualLeg(iTestZ-2,iTestZ)          &
              &               * modalA_right(iTestY                           &
              &                              +(iTestZ-3)*(maxPolyDegree+1),:) &
              &          )
            p_b_right = testX_val_right                                       &
              &         * ( ply_scalProdDualLegDiff(iTestY-1,iTestY)          &
              &             * ply_scalProdDualLeg(iTestZ,iTestZ)              &
              &             * modalB_right(iTestY-1                           &
              &                            +(iTestZ-1)*(maxPolyDegree+1),:)   &
              &             + ply_scalProdDualLegDiff(iTestY-1,iTestY)        &
              &               * ply_scalProdDualLeg(iTestZ-2,iTestZ)          &
              &               * modalB_right(iTestY-1                         &
              &                              +(iTestZ-3)*(maxPolyDegree+1),:) &
              &           )
            p_c_right = testX_val_right                                     &
              &         * ( ply_scalProdDualLeg(iTestY,iTestY)              &
              &             * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)      &
              &             * modalC_right(iTestY                           &
              &                            +(iTestZ-2)*(maxPolyDegree+1),:) &
              &           )
            projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
              &                         - outerNormalRight * ( p_a_right(:) &
              &                                              + p_b_right(:) &
              &                                              + p_c_right(:) )
          end do


          ! Now, we project onto all the other test functions in y direction
          yTestLoop: do iTestY = 3, maxPolyDegree+1
            iTestZ = 1
            testPos = iTestX                                        &
              &     + ( (iTestY-1) + (iTestZ-1)*(maxPolyDegree+1) ) &
              &       * (maxPolyDegree+1)

            ! ... for the left face
            p_a_left = jacobiDet * testX_grad_val_left                      &
              &        * ( ply_scalProdDualLeg(iTestY,iTestY)               &
              &            * ply_scalProdDualLeg(iTestZ,iTestZ)             &
              &            * modalA_left(iTestY                             &
              &                          +(iTestZ-1)*(maxPolyDegree+1),:)   &
              &            + ply_scalProdDualLeg(iTestY-2,iTestY)           &
              &              * ply_scalProdDualLeg(iTestZ,iTestZ)           &
              &              * modalA_left(iTestY-2                         &
              &                            +(iTestZ-1)*(maxPolyDegree+1),:) &
              &          )
            p_b_left = testX_val_left                             &
              &        * ply_scalProdDualLegDiff(iTestY-1,iTestY) &
              &        * ply_scalProdDualLeg(iTestZ,iTestZ)       &
              &        * modalB_left(iTestY-1+(iTestZ-1)*(maxPolyDegree+1),:)
            projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
              &                         - outerNormalLeft * ( p_a_left(:)   &
              &                                               + p_b_left(:) )

            ! ... for the right face
            p_a_right = jacobiDet * testX_grad_val_right                      &
              &         * ( ply_scalProdDualLeg(iTestY,iTestY)                &
              &             * ply_scalProdDualLeg(iTestZ,iTestZ)              &
              &             * modalA_right(iTestY                             &
              &                            +(iTestZ-1)*(maxPolyDegree+1),:)   &
              &             + ply_scalProdDualLeg(iTestY-2,iTestY)            &
              &               * ply_scalProdDualLeg(iTestZ,iTestZ)            &
              &               * modalA_right(iTestY-2                         &
              &                              +(iTestZ-1)*(maxPolyDegree+1),:) &
              &           )
            p_b_right = testX_val_right                            &
              &         * ply_scalProdDualLegDiff(iTestY-1,iTestY) &
              &         * ply_scalProdDualLeg(iTestZ,iTestZ)       &
              &         * modalB_right(iTestY-1+(iTestZ-1)*(maxPolyDegree+1),:)
            projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
              &                         - outerNormalRight * ( p_a_right(:) &
              &                                              + p_b_right(:) )

            iTestZ = 2
            testPos = iTestX                                        &
              &     + ( (iTestY-1) + (iTestZ-1)*(maxPolyDegree+1) ) &
              &       * (maxPolyDegree+1)

            ! ... for the left face
            p_a_left = jacobiDet * testX_grad_val_left                      &
              &        * ( ply_scalProdDualLeg(iTestY,iTestY)               &
              &            * ply_scalProdDualLeg(iTestZ,iTestZ)             &
              &            * modalA_left(iTestY                             &
              &                          +(iTestZ-1)*(maxPolyDegree+1),:)   &
              &            + ply_scalProdDualLeg(iTestY-2,iTestY)           &
              &              * ply_scalProdDualLeg(iTestZ,iTestZ)           &
              &              * modalA_left(iTestY-2                         &
              &                            +(iTestZ-1)*(maxPolyDegree+1),:) &
              &          )
            p_b_left = testX_val_left                             &
              &        * ply_scalProdDualLegDiff(iTestY-1,iTestY) &
              &        * ply_scalProdDualLeg(iTestZ,iTestZ)       &
              &        * modalB_left(iTestY-1+(iTestZ-1)*(maxPolyDegree+1),:)
            p_c_left = testX_val_left                                       &
              &        * ( ply_scalProdDualLeg(iTestY,iTestY)               &
              &            * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)       &
              &            * modalC_left(iTestY                             &
              &                          +(iTestZ-2)*(maxPolyDegree+1),:)   &
              &            + ply_scalProdDualLeg(iTestY-2,iTestY)           &
              &              * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)     &
              &              * modalC_left(iTestY-2                         &
              &                            +(iTestZ-2)*(maxPolyDegree+1),:) &
              &          )
            projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
              &                         - outerNormalLeft * ( p_a_left(:)   &
              &                                               + p_b_left(:) &
              &                                               + p_c_left(:) )

            ! ... for the right face
            p_a_right = jacobiDet * testX_grad_val_right                      &
              &         * ( ply_scalProdDualLeg(iTestY,iTestY)                &
              &             * ply_scalProdDualLeg(iTestZ,iTestZ)              &
              &             * modalA_right(iTestY                             &
              &                            +(iTestZ-1)*(maxPolyDegree+1),:)   &
              &             + ply_scalProdDualLeg(iTestY-2,iTestY)            &
              &               * ply_scalProdDualLeg(iTestZ,iTestZ)            &
              &               * modalA_right(iTestY-2                         &
              &                              +(iTestZ-1)*(maxPolyDegree+1),:) &
              &           )
            p_b_right = testX_val_right                            &
              &         * ply_scalProdDualLegDiff(iTestY-1,iTestY) &
              &         * ply_scalProdDualLeg(iTestZ,iTestZ)       &
              &         * modalB_right(iTestY-1+(iTestZ-1)*(maxPolyDegree+1),:)
            p_c_right = testX_val_right                                     &
              &         * ( ply_scalProdDualLeg(iTestY,iTestY)              &
              &             * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)      &
              &             * modalC_right(iTestY                           &
              &                            +(iTestZ-2)*(maxPolyDegree+1),:) &
              &             + ply_scalProdDualLeg(iTestY-2,iTestY)          &
              &               * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)    &
              &               * modalC_right(iTestY-2                       &
              &                              +(iTestZ-2)*(maxPolyDegree+1), &
              &                              :)                             &
              &           )
            projection(iElem,testPos,:) = projection(iElem,testPos,:)         &
              &                         - outerNormalRight * ( p_a_right(:)   &
              &                                                + p_b_right(:) &
              &                                                + p_c_right(:) )

            zTestLoop: do iTestZ = 3, maxPolyDegree+1
              testPos = iTestX                                        &
                &     + ( (iTestY-1) + (iTestZ-1)*(maxPolyDegree+1) ) &
                &       * (maxPolyDegree+1)

              ! ... for the left face
              p_a_left = jacobiDet * testX_grad_val_left                      &
                &        * ( ply_scalProdDualLeg(iTestY,iTestY)               &
                &            * ply_scalProdDualLeg(iTestZ,iTestZ)             &
                &            * modalA_left(iTestY                             &
                &                          +(iTestZ-1)*(maxPolyDegree+1),:)   &
                &            + ply_scalProdDualLeg(iTestY,iTestY)             &
                &              * ply_scalProdDualLeg(iTestZ-2,iTestZ)         &
                &              * modalA_left(iTestY                           &
                &                            +(iTestZ-3)*(maxPolyDegree+1),:) &
                &            + ply_scalProdDualLeg(iTestY-2,iTestY)           &
                &              * ply_scalProdDualLeg(iTestZ-2,iTestZ)         &
                &              * modalA_left(iTestY-2                         &
                &                            +(iTestZ-3)*(maxPolyDegree+1),:) &
                &            + ply_scalProdDualLeg(iTestY-2,iTestY)           &
                &              * ply_scalProdDualLeg(iTestZ,iTestZ)           &
                &              * modalA_left(iTestY-2                         &
                &                            +(iTestZ-1)*(maxPolyDegree+1),:) &
                &         )
              p_b_left = testX_val_left                                       &
                &        * ( ply_scalProdDualLegDiff(iTestY-1,iTestY)         &
                &            * ply_scalProdDualLeg(iTestZ,iTestZ)             &
                &            * modalB_left(iTestY-1                           &
                &                          +(iTestZ-1)*(maxPolyDegree+1),:)   &
                &            + ply_scalProdDualLegDiff(iTestY-1,iTestY)       &
                &              * ply_scalProdDualLeg(iTestZ-2,iTestZ)         &
                &              * modalB_left(iTestY-1                         &
                &                            +(iTestZ-3)*(maxPolyDegree+1),:) &
                &          )
              p_c_left = testX_val_left                                       &
                &        * ( ply_scalProdDualLeg(iTestY,iTestY)               &
                &            * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)       &
                &            * modalC_left(iTestY                             &
                &                          +(iTestZ-2)*(maxPolyDegree+1),:)   &
                &            + ply_scalProdDualLeg(iTestY-2,iTestY)           &
                &              * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)     &
                &              * modalC_left(iTestY-2                         &
                &                            +(iTestZ-2)*(maxPolyDegree+1),:) &
                &          )
              projection(iElem,testPos,:) = projection(iElem,testPos,:)     &
                &                         - outerNormalLeft * ( p_a_left(:) &
                &                                             + p_b_left(:) &
                &                                             + p_c_left(:) )

              ! ... for the right face
              p_a_right = jacobiDet * testX_grad_val_right                     &
                &         * ( ply_scalProdDualLeg(iTestY,iTestY)               &
                &             * ply_scalProdDualLeg(iTestZ,iTestZ)             &
                &             * modalA_right(iTestY                            &
                &                            +(iTestZ-1)*(maxPolyDegree+1),:)  &
                &             + ply_scalProdDualLeg(iTestY,iTestY)             &
                &               * ply_scalProdDualLeg(iTestZ-2,iTestZ)         &
                &               * modalA_right(iTestY                          &
                &                              +(iTestZ-3)*(maxPolyDegree+1),  &
                &                              :)                              &
                &             + ply_scalProdDualLeg(iTestY-2,iTestY)           &
                &               * ply_scalProdDualLeg(iTestZ-2,iTestZ)         &
                &               * modalA_right(iTestY-2                        &
                &                             +(iTestZ-3)*(maxPolyDegree+1),:) &
                &             + ply_scalProdDualLeg(iTestY-2,iTestY)           &
                &               * ply_scalProdDualLeg(iTestZ,iTestZ)           &
                &               * modalA_right(iTestY-2                        &
                &                              +(iTestZ-1)*(maxPolyDegree+1),  &
                &                              :)                              &
                &           )
              p_b_right = testX_val_right                                     &
                &         * ( ply_scalProdDualLegDiff(iTestY-1,iTestY)        &
                &             * ply_scalProdDualLeg(iTestZ,iTestZ)            &
                &             * modalB_right(iTestY-1                         &
                &                            +(iTestZ-1)*(maxPolyDegree+1),:) &
                &             + ply_scalProdDualLegDiff(iTestY-1,iTestY)      &
                &             * ply_scalProdDualLeg(iTestZ-2,iTestZ)          &
                &             * modalB_right(iTestY-1                         &
                &                            +(iTestZ-3)*(maxPolyDegree+1),:) )
              p_c_right = testX_val_right                                     &
                &         * ( ply_scalProdDualLeg(iTestY,iTestY)              &
                &             * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)      &
                &             * modalC_right(iTestY                           &
                &                            +(iTestZ-2)*(maxPolyDegree+1),:) &
                &             + ply_scalProdDualLeg(iTestY-2,iTestY)          &
                &               * ply_scalProdDualLegDiff(iTestZ-1,iTestZ)    &
                &               * modalC_right(iTestY-2                       &
                &                              +(iTestZ-2)*(maxPolyDegree+1), &
                &                              :)                             &
                &           )
              projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
                &                         - outerNormalRight * ( p_a_right(:) &
                &                                              + p_b_right(:) &
                &                                              + p_c_right(:) )
            end do zTestLoop
          end do yTestLoop
        end if
      end do xTestLoop


    end do elementLoop


  end subroutine modg_project_stabViscNumFluxX_Q


  !> Projection of the numerical flux in y direction onto the testfunctions.
  subroutine modg_project_stabViscNumFluxY_Q(                       &
    &          numFlux, faceState, equation, maxPolyDegree, length, &
    &          nElems_fluid, projection, poly_proj                  )
    ! -------------------------------------------------------------------- !
    !> The numerical flux on the faces in modal representations.
    !! Dimension is (maxPolyDegree+1)^2 , nScalars
    real(kind=rk), intent(inout) :: numFlux(:,:,:,:)
    !> The state on the faces in modal representations.
    !! Dimension is (maxPolyDegree+1)^2 , nScalars
    real(kind=rk), intent(inout) :: faceState(:,:,:,:)
    !> The equation system under consideration
    type(atl_equations_type), intent(in) :: equation
    !> The maximal polynomial degree in each spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The length of the cubes.
    real(kind=rk), intent(in) :: length
    !> The element index
    integer, intent(in) :: nElems_fluid
    !> The numerical flux projected onto the test functions.
    real(kind=rk), intent(inout) :: projection(:,:,:)
    !> Projection for the current level
    type(ply_poly_project_type), intent(inout) :: poly_proj
    ! -------------------------------------------------------------------- !
    integer :: iVar, iElem, iPoint, testPos, iVP
    integer :: iTestX, iTestY, iTestZ
    integer :: iTestFace
    integer :: iOversamp, iOrig
    integer :: iDof, iDofY, iDofX
    integer :: nScalars, nPoints, nPVars, nOversamp

    real(kind=rk) :: p_a_left(equation%varSys%nScalars)
    real(kind=rk) :: p_b_left(equation%varSys%nScalars)
    real(kind=rk) :: p_c_left(equation%varSys%nScalars)

    real(kind=rk) :: p_a_right(equation%varSys%nScalars)
    real(kind=rk) :: p_b_right(equation%varSys%nScalars)
    real(kind=rk) :: p_c_right(equation%varSys%nScalars)

    real(kind=rk), allocatable :: flux_left(:,:)
    real(kind=rk), allocatable :: pointVal_flux_left(:,:)
    real(kind=rk), allocatable :: pointVal_left(:,:)
    real(kind=rk), allocatable :: state_left(:,:)
    real(kind=rk), allocatable :: nodal_a_left(:,:)
    real(kind=rk), allocatable :: nodal_b_left(:,:)
    real(kind=rk), allocatable :: nodal_c_left(:,:)
    real(kind=rk), allocatable :: modalA_left(:,:)
    real(kind=rk), allocatable :: modalB_left(:,:)
    real(kind=rk), allocatable :: modalC_left(:,:)

    real(kind=rk), allocatable :: flux_right(:,:)
    real(kind=rk), allocatable :: pointVal_flux_right(:,:)
    real(kind=rk), allocatable :: pointVal_right(:,:)
    real(kind=rk), allocatable :: state_right(:,:)
    real(kind=rk), allocatable :: nodal_a_right(:,:)
    real(kind=rk), allocatable :: nodal_b_right(:,:)
    real(kind=rk), allocatable :: nodal_c_right(:,:)
    real(kind=rk), allocatable :: modalA_right(:,:)
    real(kind=rk), allocatable :: modalB_right(:,:)
    real(kind=rk), allocatable :: modalC_right(:,:)

    real(kind=rk) :: velocity_left(3)
    real(kind=rk) :: velocity_right(3)
    real(kind=rk) :: jacobiDet
    real(kind=rk) :: testY_val_left, testY_grad_val_left
    real(kind=rk) :: testY_val_right, testY_grad_val_right
    real(kind=rk) :: outerNormalLeft, outerNormalRight
    real(kind=rk) :: legprod
    real(kind=rk) :: legprod_square
    real(kind=rk) :: tmp_left(equation%varSys%nScalars)
    real(kind=rk) :: tmp_right(equation%varSys%nScalars)
    ! -------------------------------------------------------------------- !

    nScalars = equation%varSys%nScalars
    nPoints = poly_proj%body_2D%nquadpoints
    nOversamp = poly_proj%body_2D%oversamp_dofs

    nPVars = (poly_proj%min_degree+1)**2*equation%varSys%nScalars

    allocate( flux_left(nOversamp, nScalars), flux_right(nOversamp, nScalars) )
    allocate( state_left(nOversamp, nScalars) )
    allocate( state_right(nOversamp, nScalars) )
    allocate( pointVal_flux_left(nPoints, nScalars) )
    allocate( pointVal_flux_right(nPoints, nScalars) )
    allocate( pointVal_left(nPoints, nScalars) )
    allocate( pointVal_right(nPoints, nScalars) )
    allocate( nodal_a_left(nPoints, nScalars) )
    allocate( nodal_b_left(nPoints, nScalars) )
    allocate( nodal_c_left(nPoints, nScalars) )
    allocate( nodal_a_right(nPoints, nScalars) )
    allocate( nodal_b_right(nPoints, nScalars) )
    allocate( nodal_c_right(nPoints, nScalars) )
    allocate( modalA_left(nOversamp,nScalars) )
    allocate( modalB_left(nOversamp, nScalars) )
    allocate( modalC_left(nOversamp,nScalars) )
    allocate( modalA_right(nOversamp,nScalars) )
    allocate( modalB_right(nOversamp, nScalars) )
    allocate( modalC_right(nOversamp, nScalars) )

    ! The 1D Jacobi determinant
    jacobiDet = 0.5_rk * length

    ! The outer unit normals scaled by jacobiDet as this is a common factor
    outerNormalLeft = jacobiDet * atl_elemfaceToNormal_prp(tem_left)
    outerNormalRight = jacobiDet * atl_elemfaceToNormal_prp(tem_right)


    elementLoop: do iElem = 1,nElems_fluid

      state_left = 0.0_rk
      state_right = 0.0_rk
      flux_left = 0.0_rk
      flux_right = 0.0_rk

      do iVP = 1,nPVars
        iVar = (iVP-1)/((poly_proj%min_degree+1)**2) + 1
        iDof = iVP - (iVar-1)*((poly_proj%min_degree+1)**2)
        iDofY = (iDof-1) / (poly_proj%min_Degree+1)
        iDofX = mod(iDof-1,poly_proj%min_degree+1)
        iOversamp = iDofY*(poly_proj%oversamp_degree+1) + iDofX + 1
        iOrig = iDofY*(poly_proj%maxPolyDegree+1) + iDofX + 1

        ! Get u
        ! ... for left face
        state_left(iOversamp,iVar) = faceState(iElem,iOrig,iVar,1)
        ! ... for right face
        state_right(iOversamp,iVar) = faceState(iElem,iOrig,iVar,2)

        ! Caluclate (u^* - u)
        ! ... for the left face
        flux_left(iOversamp,iVar) = numFlux(iElem,iOrig,iVar+nScalars,1)  &
          &                       - state_left(iOversamp,iVar)
        ! ... for the right face
        flux_right(iOversamp,iVar) = numFlux(iElem,iOrig,iVar+nScalars,2) &
          &                        - state_right(iOversamp,iVar)
      end do

      ! Transform  u to nodal representation
      ! ... for the left face
      call ply_poly_project_m2n(me = poly_proj,             &
        &                       dim = 2 ,                   &
        &                       nVars = nScalars,           &
        &                       nodal_data = pointVal_left, &
        &                       modal_data = state_left     )
      ! ... for the right face
      call ply_poly_project_m2n(me = poly_proj,              &
        &                       dim = 2 ,                    &
        &                       nVars = nScalars,            &
        &                       nodal_data = pointVal_right, &
        &                       modal_data = state_right     )

      ! Transform (u^* - u) to nodal representation
      ! ... for the left face
      call ply_poly_project_m2n(me = poly_proj,                  &
        &                       dim = 2 ,                        &
        &                       nVars = nScalars,                &
        &                       nodal_data = pointVal_flux_left, &
        &                       modal_data = flux_left           )
      ! ... for the right face
      call ply_poly_project_m2n(me = poly_proj,                   &
        &                       dim = 2 ,                         &
        &                       nVars = nScalars,                 &
        &                       nodal_data = pointVal_flux_right, &
        &                       modal_data = flux_right           )

      ! Loop over all the points
      pointLoop: do iPoint = 1, nPoints

        ! Caculate velocity at this point
        ! ... for the left face
        velocity_left = pointVal_left(iPoint,2:4) &
          &             / pointVal_left(iPoint,1)
        ! ... for the right face
        velocity_right = pointVal_right(iPoint,2:4) &
          &              / pointVal_right(iPoint,1)

        ! Build matrix-vector product of nu_12 and values at current point
        ! ... for the left face
        nodal_a_left(iPoint,:)                            &
          &  = atl_mult_nu12_NavierStokes(                &
          &      density  = pointVal_left(iPoint,1),      &
          &      velocity = velocity_left,                &
          &      inVec    = pointVal_flux_left(iPoint,:), &
          &      mu       = equation%NavierStokes%mu,     &
          &      lambda   = equation%NavierStokes%lambda  )

        ! ... for the right face
        nodal_a_right(iPoint,:)                            &
          &  = atl_mult_nu12_NavierStokes(                 &
          &      density  = pointVal_right(iPoint,1),      &
          &      velocity = velocity_right,                &
          &      inVec    = pointVal_flux_right(iPoint,:), &
          &      mu       = equation%NavierStokes%mu,      &
          &      lambda   = equation%NavierStokes%lambda   )

        ! Build matrix-vector product of nu_22 and values at current point
        ! ... for the left face
        nodal_b_left(iPoint,:) &
          &  = atl_mult_nu22_NavierStokes(                     &
          &      density   = pointVal_left(iPoint,1),          &
          &      velocity  = velocity_left,                    &
          &      totEnergy = pointVal_left(iPoint,5),          &
          &      inVec     = pointVal_flux_left(iPoint,:),     &
          &      mu        = equation%NavierStokes%mu,         &
          &      lambda    = equation%NavierStokes%lambda,     &
          &      thermCond = equation%NavierStokes%therm_cond, &
          &      heatCap   = equation%euler%cv                 )

        ! ... for the right face
        nodal_b_right(iPoint,:) &
          &  = atl_mult_nu22_NavierStokes(                     &
          &      density   = pointVal_right(iPoint,1),         &
          &      velocity  = velocity_right,                   &
          &      totEnergy = pointVal_right(iPoint,5),         &
          &      inVec     = pointVal_flux_right(iPoint,:),    &
          &      mu        = equation%NavierStokes%mu,         &
          &      lambda    = equation%NavierStokes%lambda,     &
          &      thermCond = equation%NavierStokes%therm_cond, &
          &      heatCap   = equation%euler%cv                 )

        ! Build matrix-vector product of nu_22 and values at current point
        ! ... for the left face
        nodal_c_left(iPoint,:) &
          &  = atl_mult_nu32_NavierStokes(                &
          &      density  = pointVal_left(iPoint,1),      &
          &      velocity = velocity_left,                &
          &      inVec    = pointVal_flux_left(iPoint,:), &
          &      mu       = equation%NavierStokes%mu,     &
          &      lambda   = equation%NavierStokes%lambda  )

        ! ... for the right face
        nodal_c_right(iPoint,:) &
          &  = atl_mult_nu32_NavierStokes(                 &
          &      density  = pointVal_right(iPoint,1),      &
          &      velocity = velocity_right,                &
          &      inVec    = pointVal_flux_right(iPoint,:), &
          &      mu       = equation%NavierStokes%mu,      &
          &      lambda   = equation%NavierStokes%lambda   )

      end do pointLoop

      ! Transform nodal_a and nodal_b back to modal space for projections
      ! ... for the left face
      call ply_poly_project_n2m(me = poly_proj,            &
        &                       dim = 2 ,                  &
        &                       nVars = nScalars,          &
        &                       nodal_data = nodal_a_left, &
        &                       modal_data = modalA_left   )
      call ply_poly_project_n2m(me = poly_proj,            &
        &                       dim = 2 ,                  &
        &                       nVars = nScalars,          &
        &                       nodal_data = nodal_b_left, &
        &                       modal_data = modalB_left   )
      call ply_poly_project_n2m(me = poly_proj,            &
        &                       dim = 2 ,                  &
        &                       nVars = nScalars,          &
        &                       nodal_data = nodal_c_left, &
        &                       modal_data = modalC_left   )

      ! ... for the right face
      call ply_poly_project_n2m(me = poly_proj,             &
        &                       dim = 2 ,                   &
        &                       nVars = nScalars,           &
        &                       nodal_data = nodal_a_right, &
        &                       modal_data = modalA_right   )
      call ply_poly_project_n2m(me = poly_proj,             &
        &                       dim = 2 ,                   &
        &                       nVars = nScalars,           &
        &                       nodal_data = nodal_b_right, &
        &                       modal_data = modalB_right   )
      call ply_poly_project_n2m(me = poly_proj,             &
        &                       dim = 2 ,                   &
        &                       nVars = nScalars,           &
        &                       nodal_data = nodal_c_right, &
        &                       modal_data = modalC_right   )

      ! Project onto all the test functions
      yTestLoop: do iTestY = 1, maxPolyDegree+1
        ! Evaluate test function in y direction at face coordinate
        ! ... for the left face
        testY_val_left = ply_faceValLeftBndTest(iTestY)
        testY_grad_val_left = ply_faceValLeftBndTestGrad(iTestY)
        ! ... for the right face
        testY_val_right = ply_faceValRightBndTest(iTestY)
        testY_grad_val_right = ply_faceValRightBndTestGrad(iTestY)

        ! Now we compute the projections. Since the test functions
        ! change when iTestY > 2 we moved the first two projections of
        ! of the projection loop.

        xTestLoop: do iTestX = 1, maxPolyDegree+1
          zTestLoop: do iTestZ = 1, maxPolyDegree+1

            testPos = iTestX                                        &
              &     + ( (iTestY-1) + (iTestZ-1)*(maxPolyDegree+1) ) &
              &       * (maxPolyDegree+1)

            legprod_square = ply_scalProdDualLeg(iTestZ, iTestZ)
            legprod = ply_scalProdDualLeg(iTestX, iTestX) &
              &       * legprod_square
            iTestFace = iTestX+(iTestZ-1)*(maxPolyDegree+1)

            tmp_left = legprod * modalB_left(iTestFace,:)
            tmp_right = legprod * modalB_right(iTestFace,:)

            if (iTestX > 2) then
              legprod = ply_scalProdDualLeg(iTestX-2, iTestX) &
                &       * legprod_square
              iTestFace = iTestX+(iTestZ-1)*(maxPolyDegree+1) - 2

              tmp_left = tmp_left + legprod * modalB_left(iTestFace,:)
              tmp_right = tmp_right + legprod * modalB_right(iTestFace,:)
            end if
            if (iTestZ > 2) then
              legprod = ply_scalProdDualLeg(iTestX, iTestX) &
                &       * ply_scalProdDualLeg(iTestZ-2, iTestZ)
              iTestFace = iTestX+(iTestZ-3)*(maxPolyDegree+1)

              tmp_left = tmp_left &
                &      + legprod * modalB_left(iTestFace,:)
              tmp_right = tmp_right &
                &       + legprod * modalB_right(iTestFace,:)
              if (iTestX > 2) then
                legprod = ply_scalProdDualLeg(iTestX-2,iTestX) &
                   &      * ply_scalProdDualLeg(iTestZ-2,iTestZ)
                iTestFace = iTestX+(iTestZ-3)*(maxPolyDegree+1) - 2

                tmp_left = tmp_left &
                  &      + legprod * modalB_left(iTestFace,:)
                tmp_right = tmp_right &
                  &       + legprod * modalB_right(iTestFace,:)
              end if
            end if

            p_b_left = jacobiDet * testY_grad_val_left * tmp_left
            p_b_right = jacobiDet * testY_grad_val_right * tmp_right

            if (iTestX > 1) then
              legprod = ply_scalProdDualLegDiff(iTestX-1,iTestX) &
                &       * legprod_square
              iTestFace = iTestX+(iTestZ-1)*(maxPolyDegree+1) - 1

              tmp_left = legprod * modalA_left(iTestFace,:)
              tmp_right = legprod * modalA_right(iTestFace,:)

              if (iTestZ > 2) then
                legprod = ply_scalProdDualLegDiff(iTestX-1, iTestX) &
                  &       * ply_scalProdDualLeg(iTestZ-2,iTestZ)
                iTestFace = iTestX+(iTestZ-3)*(maxPolyDegree+1) - 1

                tmp_left = tmp_left + legprod * modalA_left(iTestFace,:)
                tmp_right = tmp_right + legprod * modalA_right(iTestFace,:)
              end if

              p_a_left = testY_val_left * tmp_left
              p_a_right = testY_val_right * tmp_right

            else

              p_a_left = 0.0_rk
              p_a_right = 0.0_rk

            end if

            if (iTestZ > 1) then
              legprod = ply_scalProdDualLeg(iTestX, iTestX) &
                &       * ply_scalProdDualLegDiff(iTestZ-1, iTestZ)
              iTestFace = iTestX+(iTestZ-2)*(maxPolyDegree+1)

              tmp_left = legprod * modalC_left(iTestFace,:)
              tmp_right = legprod * modalC_right(iTestFace,:)

              if (iTestX > 2) then
                legprod = ply_scalProdDualLeg(iTestX-2, iTestX) &
                  &       * ply_scalProdDualLegDiff(iTestZ-1, iTestZ)
                iTestFace = iTestX+(iTestZ-2)*(maxPolyDegree+1) - 2

                tmp_left = tmp_left + legprod * modalC_left(iTestFace,:)
                tmp_right = tmp_right + legprod * modalC_right(iTestFace,:)
              end if

              p_c_left = testY_val_left * tmp_left
              p_c_right = testY_val_right * tmp_right

            else

              p_c_left = 0.0_rk
              p_c_right = 0.0_rk

            end if

            projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
              &                         - outerNormalLeft * ( p_a_left(:)   &
              &                                             + p_b_left(:)   &
              &                                             + p_c_left(:) ) &
              &                         - outerNormalRight * ( p_a_right(:) &
              &                                              + p_b_right(:) &
              &                                              + p_c_right(:) )

          end do zTestLoop
        end do xTestLoop
      end do yTestLoop


    end do elementLoop


  end subroutine modg_project_stabViscNumFluxY_Q


  !> Projection of the numerical flux in y direction onto the testfunctions.
  subroutine modg_project_stabViscNumFluxZ_Q(                       &
    &          numFlux, faceState, equation, maxPolyDegree, length, &
    &          nElems_fluid, projection, poly_proj                  )
    ! -------------------------------------------------------------------- !
    !> The numerical flux on the faces in modal representations.
    !! Dimension is (maxPolyDegree+1)^2 , nScalars
    real(kind=rk), intent(inout) :: numFlux(:,:,:,:)
    !> The state on the faces in modal representations.
    !! Dimension is (maxPolyDegree+1)^2 , nScalars
    real(kind=rk), intent(inout) :: faceState(:,:,:,:)
    !> The equation system under consideration
    type(atl_equations_type), intent(in) :: equation
    !> The maximal polynomial degree in each spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The length of the cubes.
    real(kind=rk), intent(in) :: length
    !> The element index
    integer, intent(in) :: nElems_fluid
    !> The numerical flux projected onto the test functions.
    real(kind=rk), intent(inout) :: projection(:,:,:)
    !> Projection for the current level
    type(ply_poly_project_type), intent(inout) :: poly_proj
    ! -------------------------------------------------------------------- !
    integer :: iVar, iElem, iPoint, testPos, iVP
    integer :: iTestX, iTestY, iTestZ
    integer :: iTestFace
    integer :: iOversamp, iOrig
    integer :: iDof, iDofY, iDofX
    integer :: nScalars, nPoints, nPVars, nOversamp

    real(kind=rk) :: p_a_left(equation%varSys%nScalars)
    real(kind=rk) :: p_b_left(equation%varSys%nScalars)
    real(kind=rk) :: p_c_left(equation%varSys%nScalars)

    real(kind=rk) :: p_a_right(equation%varSys%nScalars)
    real(kind=rk) :: p_b_right(equation%varSys%nScalars)
    real(kind=rk) :: p_c_right(equation%varSys%nScalars)

    real(kind=rk), allocatable :: flux_left(:,:)
    real(kind=rk), allocatable :: pointVal_flux_left(:,:)
    real(kind=rk), allocatable :: pointVal_left(:,:)
    real(kind=rk), allocatable :: state_left(:,:)
    real(kind=rk), allocatable :: nodal_a_left(:,:)
    real(kind=rk), allocatable :: nodal_b_left(:,:)
    real(kind=rk), allocatable :: nodal_c_left(:,:)
    real(kind=rk), allocatable :: modalA_left(:,:)
    real(kind=rk), allocatable :: modalB_left(:,:)
    real(kind=rk), allocatable :: modalC_left(:,:)

    real(kind=rk), allocatable :: flux_right(:,:)
    real(kind=rk), allocatable :: pointVal_flux_right(:,:)
    real(kind=rk), allocatable :: pointVal_right(:,:)
    real(kind=rk), allocatable :: state_right(:,:)
    real(kind=rk), allocatable :: nodal_a_right(:,:)
    real(kind=rk), allocatable :: nodal_b_right(:,:)
    real(kind=rk), allocatable :: nodal_c_right(:,:)
    real(kind=rk), allocatable :: modalA_right(:,:)
    real(kind=rk), allocatable :: modalB_right(:,:)
    real(kind=rk), allocatable :: modalC_right(:,:)

    real(kind=rk) :: velocity_left(3)
    real(kind=rk) :: velocity_right(3)
    real(kind=rk) :: jacobiDet
    real(kind=rk) :: testZ_val_left, testZ_grad_val_left
    real(kind=rk) :: testZ_val_right, testZ_grad_val_right
    real(kind=rk) :: outerNormalLeft, outerNormalRight
    real(kind=rk) :: legprod
    real(kind=rk) :: legprod_square
    real(kind=rk) :: tmp_left(equation%varSys%nScalars)
    real(kind=rk) :: tmp_right(equation%varSys%nScalars)
    ! -------------------------------------------------------------------- !

    nScalars = equation%varSys%nScalars
    nPoints = poly_proj%body_2D%nquadpoints
    nOversamp = poly_proj%body_2D%oversamp_dofs

    nPVars = (poly_proj%min_degree+1)**2*equation%varSys%nScalars

    allocate( flux_left(nOversamp, nScalars), flux_right(nOversamp, nScalars) )
    allocate( state_left(nOversamp, nScalars) )
    allocate( state_right(nOversamp, nScalars) )
    allocate( pointVal_flux_left(nPoints, nScalars) )
    allocate( pointVal_flux_right(nPoints, nScalars) )
    allocate( pointVal_left(nPoints, nScalars) )
    allocate( pointVal_right(nPoints, nScalars) )
    allocate( nodal_a_left(nPoints, nScalars) )
    allocate( nodal_b_left(nPoints, nScalars) )
    allocate( nodal_c_left(nPoints, nScalars) )
    allocate( nodal_a_right(nPoints, nScalars) )
    allocate( nodal_b_right(nPoints, nScalars) )
    allocate( nodal_c_right(nPoints, nScalars) )
    allocate( modalA_left(nOversamp,nScalars) )
    allocate( modalB_left(nOversamp, nScalars) )
    allocate( modalC_left(nOversamp,nScalars) )
    allocate( modalA_right(nOversamp,nScalars) )
    allocate( modalB_right(nOversamp, nScalars) )
    allocate( modalC_right(nOversamp, nScalars) )

    ! The 1D Jacobi determinant
    jacobiDet = 0.5_rk * length

    ! The outer unit normals scaled by jacobiDet as this is a common factor
    outerNormalLeft = jacobiDet * atl_elemfaceToNormal_prp(tem_left)
    outerNormalRight = jacobiDet * atl_elemfaceToNormal_prp(tem_right)


    elementLoop: do iElem = 1,nElems_fluid

      state_left = 0.0_rk
      state_right = 0.0_rk
      flux_left = 0.0_rk
      flux_right = 0.0_rk

      do iVP = 1,nPVars
        iVar = (iVP-1)/((poly_proj%min_degree+1)**2) + 1
        iDof = iVP - (iVar-1)*((poly_proj%min_degree+1)**2)
        iDofY = (iDof-1) / (poly_proj%min_Degree+1)
        iDofX = mod(iDof-1,poly_proj%min_degree+1)
        iOversamp = iDofY*(poly_proj%oversamp_degree+1) + iDofX + 1
        iOrig = iDofY*(poly_proj%maxPolyDegree+1) + iDofX + 1

        ! Get u
        ! ... for left face
        state_left(iOversamp,iVar) = faceState(iElem,iOrig,iVar,1)
        ! ... for right face
        state_right(iOversamp,iVar) = faceState(iElem,iOrig,iVar,2)

        ! Caluclate (u^* - u)
        ! ... for the left face
        flux_left(iOversamp,iVar) = numFlux(iElem,iOrig,iVar+nScalars,1)  &
          &                       - state_left(iOversamp,iVar)
        ! ... for the right face
        flux_right(iOversamp,iVar) = numFlux(iElem,iOrig,iVar+nScalars,2) &
          &                        - state_right(iOversamp,iVar)
      end do

      ! Transform  u to nodal representation
      ! ... for the left face
      call ply_poly_project_m2n(me = poly_proj,             &
        &                       dim = 2 ,                   &
        &                       nVars = nScalars,           &
        &                       nodal_data = pointVal_left, &
        &                       modal_data = state_left     )
      ! ... for the right face
      call ply_poly_project_m2n(me = poly_proj,              &
        &                       dim = 2 ,                    &
        &                       nVars = nScalars,            &
        &                       nodal_data = pointVal_right, &
        &                       modal_data = state_right     )

      ! Transform (u^* - u) to nodal representation
      ! ... for the left face
      call ply_poly_project_m2n(me = poly_proj,                  &
        &                       dim = 2 ,                        &
        &                       nVars = nScalars,                &
        &                       nodal_data = pointVal_flux_left, &
        &                       modal_data = flux_left           )
      ! ... for the right face
      call ply_poly_project_m2n(me = poly_proj,                   &
        &                       dim = 2 ,                         &
        &                       nVars = nScalars,                 &
        &                       nodal_data = pointVal_flux_right, &
        &                       modal_data = flux_right           )

      ! Loop over all the points
      pointLoop: do iPoint = 1, nPoints

        ! Caculate velocity at this point
        ! ... for the left face
        velocity_left(1:3) = pointVal_left(iPoint,2:4) &
          &                  / pointVal_left(iPoint,1)
        ! ... for the right face
        velocity_right(1:3) = pointVal_right(iPoint,2:4) &
          &                   / pointVal_right(iPoint,1)

        ! Build matrix-vector product of nu_13 and values at current point
        ! ... for the left face
        nodal_a_left(iPoint,:)                             &
          &  = atl_mult_nu13_NavierStokes(                 &
          &      density   = pointVal_left(iPoint,1),      &
          &      velocity  = velocity_left,                &
          &      inVec     = pointVal_flux_left(iPoint,:), &
          &      mu        = equation%NavierStokes%mu,     &
          &      lambda    = equation%NavierStokes%lambda  )

        ! ... for the right face
        nodal_a_right(iPoint,:)                             &
          &  = atl_mult_nu13_NavierStokes(                  &
          &      density   = pointVal_right(iPoint,1),      &
          &      velocity  = velocity_right,                &
          &      inVec     = pointVal_flux_right(iPoint,:), &
          &      mu        = equation%NavierStokes%mu,      &
          &      lambda    = equation%NavierStokes%lambda   )

        ! Build matrix-vector product of nu_23 and values at current point
        ! ... for the left face
        nodal_b_left(iPoint,:)                             &
          &  = atl_mult_nu23_NavierStokes(                 &
          &      density   = pointVal_left(iPoint,1),      &
          &      velocity  = velocity_left,                &
          &      inVec     = pointVal_flux_left(iPoint,:), &
          &      mu        = equation%NavierStokes%mu,     &
          &      lambda    = equation%NavierStokes%lambda  )

        ! ... for the right face
        nodal_b_right(iPoint,:)                             &
          &  = atl_mult_nu23_NavierStokes(                  &
          &      density   = pointVal_right(iPoint,1),      &
          &      velocity  = velocity_right,                &
          &      inVec     = pointVal_flux_right(iPoint,:), &
          &      mu        = equation%NavierStokes%mu,      &
          &      lambda    = equation%NavierStokes%lambda   )

        ! Build matrix-vector product of nu_33 and values at current point
        ! ... for the left face
        nodal_c_left(iPoint,:)                                 &
          &  = atl_mult_nu33_NavierStokes(                     &
          &      density   = pointVal_left(iPoint,1),          &
          &      velocity  = velocity_left,                    &
          &      totEnergy = pointVal_left(iPoint,5),          &
          &      inVec     = pointVal_flux_left(iPoint,:),     &
          &      mu        = equation%NavierStokes%mu,         &
          &      lambda    = equation%NavierStokes%lambda,     &
          &      thermCond = equation%NavierStokes%therm_cond, &
          &      heatCap   = equation%euler%cv                 )

        ! ... for the right face
        nodal_c_right(iPoint,:)                                &
          &  = atl_mult_nu33_NavierStokes(                     &
          &      density   = pointVal_right(iPoint,1),         &
          &      velocity  = velocity_right,                   &
          &      totEnergy = pointVal_right(iPoint,5),         &
          &      inVec     = pointVal_flux_right(iPoint,:),    &
          &      mu        = equation%NavierStokes%mu,         &
          &      lambda    = equation%NavierStokes%lambda,     &
          &      thermCond = equation%NavierStokes%therm_cond, &
          &      heatCap   = equation%euler%cv                 )

      end do pointLoop


      ! Transform nodal_a and nodal_b back to modal space for projections
      ! ... for the left face
      call ply_poly_project_n2m(me         = poly_proj,    &
        &                       dim        = 2 ,           &
        &                       nVars      = nScalars,     &
        &                       nodal_data = nodal_a_left, &
        &                       modal_data = modalA_left   )
      call ply_poly_project_n2m(me         = poly_proj,    &
        &                       dim        = 2 ,           &
        &                       nVars      = nScalars,     &
        &                       nodal_data = nodal_b_left, &
        &                       modal_data = modalB_left   )
      call ply_poly_project_n2m(me         = poly_proj,    &
        &                       dim        = 2 ,           &
        &                       nVars      = nScalars,     &
        &                       nodal_data = nodal_c_left, &
        &                       modal_data = modalC_left   )

      ! ... for the right face
      call ply_poly_project_n2m(me         = poly_proj,     &
        &                       dim        = 2 ,            &
        &                       nVars      = nScalars,      &
        &                       nodal_data = nodal_a_right, &
        &                       modal_data = modalA_right   )
      call ply_poly_project_n2m(me         = poly_proj,     &
        &                       dim        = 2 ,            &
        &                       nVars      = nScalars,      &
        &                       nodal_data = nodal_b_right, &
        &                       modal_data = modalB_right   )
      call ply_poly_project_n2m(me         = poly_proj,     &
        &                       dim        = 2 ,            &
        &                       nVars      = nScalars,      &
        &                       nodal_data = nodal_c_right, &
        &                       modal_data = modalC_right   )

      ! Project onto all the test functions
      zTestLoop: do iTestZ = 1, maxPolyDegree+1
        ! Evaluate test function in x direction at face coordinate
        ! ... for the left face
        testZ_val_left = ply_faceValLeftBndTest(iTestZ)
        testZ_grad_val_left = ply_faceValLeftBndTestGrad(iTestZ)
        ! ... for the right face
        testZ_val_right = ply_faceValRightBndTest(iTestZ)
        testZ_grad_val_right = ply_faceValRightBndTestGrad(iTestZ)

        xTestLoop: do iTestX = 1, maxPolyDegree+1
          yTestLoop: do iTestY = 1, maxPolyDegree+1

            testPos = iTestX &
              &     + ( (iTestY-1) + (iTestZ-1)*(maxPolyDegree+1) ) &
              &       * (maxPolyDegree+1)

            legprod_square = ply_scalProdDualLeg(iTestY,iTestY)
            legprod = ply_scalProdDualLeg(iTestX,iTestX) &
              &       * legprod_square
            iTestFace = iTestX+(iTestY-1)*(maxPolyDegree+1)

            tmp_left = legprod * modalC_left(iTestFace,:)
            tmp_right = legprod * modalC_right(iTestFace,:)

            if (iTestX > 2) then
              legprod = ply_scalProdDualLeg(iTestX-2,iTestX) &
                &       * legprod_square
              iTestFace = iTestX+(iTestY-1)*(maxPolyDegree+1) - 2

              tmp_left = tmp_left + legprod * modalC_left(iTestFace,:)
              tmp_right = tmp_right + legprod * modalC_right(iTestFace,:)
            end if

            if (iTestY > 2) then
              legprod = ply_scalProdDualLeg(iTestX,iTestX) &
                &       * ply_scalProdDualLeg(iTestY-2,iTestY)
              iTestFace = iTestX+(iTestY-3)*(maxPolyDegree+1)

              tmp_left = tmp_left + legprod * modalC_left(iTestFace,:)
              tmp_right = tmp_right + legprod * modalC_right(iTestFace,:)

              if (iTestX > 2) then
                legprod = ply_scalProdDualLeg(iTestX-2,iTestX) &
                  &       * ply_scalProdDualLeg(iTestY-2,iTestY)
                iTestFace = iTestX+(iTestY-3)*(maxPolyDegree+1) - 2

                tmp_left = tmp_left + legprod * modalC_left(iTestFace,:)
                tmp_right = tmp_right + legprod * modalC_right(iTestFace,:)
              end if

            end if

            p_c_left = jacobiDet * testZ_grad_val_left * tmp_left
            p_c_right = jacobiDet * testZ_grad_val_right * tmp_right

            if (iTestX > 1) then
              legprod = ply_scalProdDualLegDiff(iTestX-1,iTestX) &
                &       * legprod_square
              iTestFace = iTestX+(iTestY-1)*(maxPolyDegree+1) - 1

              tmp_left = legprod * modalA_left(iTestFace,:)
              tmp_right = legprod * modalA_right(iTestFace,:)

              if (iTestY > 2) then
                legprod = ply_scalProdDualLegDiff(iTestX-1,iTestX) &
                  &       * ply_scalProdDualLeg(iTestY-2,iTestY)
                iTestFace = iTestX+(iTestY-3)*(maxPolyDegree+1) - 1

                tmp_left = tmp_left + legprod * modalA_left(iTestFace,:)
                tmp_right = tmp_right + legprod * modalA_right(iTestFace,:)
              end if

              p_a_left = testZ_val_left * tmp_left
              p_a_right = testZ_val_right * tmp_right

            else

              p_a_left = 0.0_rk
              p_a_right = 0.0_rk

            end if

            if (iTestY > 1) then
              legprod = ply_scalProdDualLeg(iTestX,iTestX) &
                &       * ply_scalProdDualLegDiff(iTestY-1,iTestY)
              iTestFace = iTestX+(iTestY-2)*(maxPolyDegree+1)

              tmp_left = legprod * modalB_left(iTestFace,:)
              tmp_right = legprod * modalB_right(iTestFace,:)

              if (iTestX > 2) then
                legprod = ply_scalProdDualLeg(iTestX-2,iTestX) &
                  &       * ply_scalProdDualLegDiff(iTestY-1,iTestY)
                iTestFace = iTestX+(iTestY-2)*(maxPolyDegree+1) - 2

                tmp_left = tmp_left + legprod * modalB_left(iTestFace,:)
                tmp_right = tmp_right + legprod * modalB_right(iTestFace,:)
              end if

              p_b_left = testZ_val_left * tmp_left
              p_b_right = testZ_val_right * tmp_right

            else

              p_b_left = 0.0_rk
              p_b_right = 0.0_rk

            end if

            projection(iElem,testPos,:) = projection(iElem,testPos,:)       &
              &                         - outerNormalLeft * ( p_a_left(:)   &
              &                                             + p_b_left(:)   &
              &                                             + p_c_left(:) ) &
              &                         - outerNormalRight * ( p_a_right(:) &
              &                                              + p_b_right(:) &
              &                                              + p_c_right(:) )

          end do yTestLoop
        end do xTestLoop
      end do zTestLoop

    end do elementLoop

  end subroutine modg_project_stabViscNumFluxZ_Q

  !> Projection of the penalization terms (in modal representation) to the
  !! test functions.
  subroutine modg_project_penalization_Q( mesh, maxPolyDegree, &
                                        & kerneldata, penalizationdata)
    ! --------------------------------------------------------------------------
    !> The maximal polynomial degree of the modg scheme
    integer, intent(in) :: maxPolyDegree
    !> The cubical elements.
    type(atl_cube_elem_type), intent(in) :: mesh
    !> The data of the kernel. This one is updated by the projection.
    type(atl_kerneldata_type), intent(inout) :: kerneldata
    !> Volumetric data for the penalization
    type(atl_penalizationData_type), intent(in) :: penalizationdata
    ! --------------------------------------------------------------------------
    integer :: iElem, xTestFunc, yTestFunc, zTestFunc, testPos, &
             & xAnsFuncMin, xAnsFunc, yAnsFuncMin, yAnsFunc, zAnsFuncMin, &
             & zAnsFunc, ansPos
    real(kind=rk) :: jacobiDet, xScalProd, yScalProd, zScalProd
    integer :: nDoFs
    ! --------------------------------------------------------------------------

    jacobiDet = (0.5_rk*mesh%length)**3
    nDoFs = (maxPolyDegree+1)**3


    do iElem = 1, kerneldata%nTotal

      ! Now, we loop over all the test functions for this element and calculate
      ! the projection of the penalization terms onto this test functions.
      do testpos = 1,nDoFs
        xTestFunc = mod(testpos-1, maxpolydegree+1) + 1
        zTestFunc = (testpos-1)/(maxPolyDegree+1)**2 + 1
        yTestFunc = (testpos-1 - (zTestFunc-1)*(maxPolyDegree+1)**2) &
          &       / (maxPolyDegree+1) + 1
        ! Loop over relevant ans functions
        xAnsFuncMin = xTestFunc-2
        if (xAnsFuncMin < 1) then
          xAnsFuncMin = xTestFunc
        end if
        do xAnsFunc = xAnsFuncMin,xTestFunc,2
          ! Loop over relevant ans functions
          yAnsFuncMin = yTestFunc-2
          if( yAnsFuncMin < 1 ) then
            yAnsFuncMin = yTestFunc
          end if
          do yAnsFunc = yAnsFuncMin,yTestFunc,2
            ! Loop over relevant ans functions
            zAnsFuncMin = zTestFunc-2
            if( zAnsFuncMin < 1 ) then
              zAnsFuncMin = zTestFunc
            end if
            do zAnsFunc = zAnsFuncMin,zTestFunc,2

              ! get position of ansatz functions in the serialized list
              ! of dofs.
  anspos = xansfunc                                      &
    &      + ( ( yansfunc-1)                             &
    &      + (zansfunc-1)*(maxpolydegree+1))*(maxpolydegree+1)

              ! project the current ansatz function onto the test function
              xScalProd = ply_scalProdDualLeg(xAnsFunc, xTestFunc)
              yScalProd = ply_scalProdDualLeg(yAnsFunc, yTestFunc)
              zScalProd = ply_scalProdDualLeg(zAnsFunc, zTestFunc)
              kernelData%state_der(iElem, testPos,  :) &
                               & = kernelData%state_der(iElem, testPos, :) &
                               & + xScalProd * yScalProd * zScalProd &
                               & * jacobiDet &
                               & * penalizationdata% &
                               &   penalization_data(iElem, ansPos,:)
            end do ! z ansatz functions
          end do ! y ansatz functions
        end do ! x ansatz functions

      end do
    end do ! elem loop


  end subroutine modg_project_penalization_Q


  !> Projection of the source terms (in modal representation) to the
  !! test functions.
  subroutine atl_modg_project_source( nScalars, sourcedata, scheme,  &
    &                                 mesh, kerneldata, currentLevel )
    ! --------------------------------------------------------------------------
    !> The number scalar variables in the equation system.
    integer, intent(in) :: nScalars
    !> The modal representation of the source
    type(atl_source_type), intent(in) :: sourcedata
    !> The parameters of the MODG scheme
    type(atl_modg_scheme_type), intent(in) :: scheme
    !> The cubical elements.
    type(atl_cube_elem_type), intent(in) :: mesh
    !> The data of the kernel. This one is updated by the projection.
    type(atl_kerneldata_type), intent(inout) :: kerneldata
    !> The current level
    integer, intent(in) :: currentLevel
    ! --------------------------------------------------------------------------
    ! --------------------------------------------------------------------------

    ! Projection of the source terms onto the test functions.
    select case(scheme%basisType)
    case(Q_space)
      call atl_modg_project_source_Q(           &
        & sourcedata    = sourcedata,           &
        & nScalars      = nScalars,             &
        & mesh          = mesh,                 &
        & maxPolyDegree = scheme%maxPolyDegree, &
        & kerneldata    = kerneldata,           &
        & currentLevel  = currentLevel          )
    case(P_space)
      call atl_modg_project_source_P(           &
        & sourcedata    = sourcedata,           &
        & nScalars      = nScalars,             &
        & mesh          = mesh,                 &
        & maxPolyDegree = scheme%maxPolyDegree, &
        & kerneldata    = kerneldata,           &
        & currentLevel  = currentLevel          )
    end select

  end subroutine atl_modg_project_source


  !> Projection of the source terms (in modal representation) to the
  !! test functions.
  subroutine atl_modg_project_source_Q( sourcedata, nScalars, mesh, &
    &                                   maxPolyDegree, kerneldata,  &
    &                                   currentLevel                )
    ! --------------------------------------------------------------------------
    !> The number scalar variables in the equation system.
    integer, intent(in) :: nScalars
    !> The modal representation of the source
    type(atl_source_type), intent(in) :: sourcedata
    !> The maximal polynomial degree of the modg scheme
    integer, intent(in) :: maxPolyDegree
    !> The cubical elements.
    type(atl_cube_elem_type), intent(in) :: mesh
    !> The data of the kernel. This one is updated by the projection.
    type(atl_kerneldata_type), intent(inout) :: kerneldata
    !> The current level
    integer, intent(in) :: currentLevel
    ! --------------------------------------------------------------------------
    integer :: iElem, elemPos, xTestFunc, yTestFunc, zTestFunc, testPos, &
             & xAnsFuncMin, xAnsFunc, yAnsFuncMin, yAnsFunc, zAnsFuncMin, &
             & zAnsFunc, ansPos, varPos, iSource, nSourceElems
    real(kind=rk) :: jacobiDet, xScalProd, yScalProd, zScalProd
    integer :: nDoFs
    ! --------------------------------------------------------------------------

    jacobiDet = (0.5_rk*mesh%length)**3
    nDoFs = (maxPolyDegree+1)**3

    do iSource = 1, size(sourcedata%method)

      nSourceElems = sourcedata%method(iSource)%elems(currentLevel)%nElems

      do varPos = 1, nScalars
        do iElem = 1, nSourceElems

          ! Position of the current element
          if (sourcedata%method(iSource)%isPermanent) then
            elempos = iElem
          else
            elempos = sourcedata%method(iSource)%elems(currentLevel) &
                                %posInTotal%val(iElem)
          end if

          ! Now, we loop over all the test functions for this element and
          ! calculate the projection of the source terms onto this
          ! test functions.
          do testpos = 1,nDoFs
            xTestFunc = mod(testpos-1, maxpolydegree+1) + 1
            zTestFunc = (testpos-1)/(maxPolyDegree+1)**2 + 1
            yTestFunc = (testpos-1 - (zTestFunc-1)*(maxPolyDegree+1)**2) &
              &       / (maxPolyDegree+1) + 1
            ! Loop over relevant ans functions
            xAnsFuncMin = xTestFunc-2
            if (xAnsFuncMin < 1) then
              xAnsFuncMin = xTestFunc
            end if
            do xAnsFunc = xAnsFuncMin,xTestFunc,2
              ! Loop over relevant ans functions
              yAnsFuncMin = yTestFunc-2
              if( yAnsFuncMin < 1 ) then
                yAnsFuncMin = yTestFunc
              end if
              do yAnsFunc = yAnsFuncMin,yTestFunc,2
                ! Loop over relevant ans functions
                zAnsFuncMin = zTestFunc-2
                if( zAnsFuncMin < 1 ) then
                  zAnsFuncMin = zTestFunc
                end if
                do zAnsFunc = zAnsFuncMin,zTestFunc,2

                  ! get position of ansatz functions in the serialized list
                  ! of dofs.
  anspos = xansfunc                                      &
    &      + ( ( yansfunc-1)                             &
    &      + (zansfunc-1)*(maxpolydegree+1))*(maxpolydegree+1)

                  ! project the current ansatz function onto the test function
                  xScalProd = ply_scalProdDualLeg(xAnsFunc, xTestFunc)
                  yScalProd = ply_scalProdDualLeg(yAnsFunc, yTestFunc)
                  zScalProd = ply_scalProdDualLeg(zAnsFunc, zTestFunc)
                  kernelData%state_der(elemPos, testPos,  varPos)             &
                    & = kernelData%state_der(elemPos, testPos,  varPos)       &
                    &   + xScalProd * yScalProd * zScalProd                   &
                    &   * jacobiDet                                           &
                    &   * sourcedata%method(iSource)%val(iElem, ansPos, varPos)
                end do ! z ansatz functions
              end do ! y ansatz functions
            end do ! x ansatz functions

          end do

        end do ! elem loop
      end do ! variable loop
    end do ! source loop


  end subroutine atl_modg_project_source_Q


  !> Projection of the source terms (in modal representation) to the
  !! test functions.
  subroutine atl_modg_project_source_P( sourcedata, nScalars, mesh, &
    &                                   maxPolyDegree, kerneldata,  &
    &                                   currentLevel                )
    ! --------------------------------------------------------------------------
    !> The number scalar variables in the equation system.
    integer, intent(in) :: nScalars
    !> The modal representation of the source
    type(atl_source_type), intent(in) :: sourcedata
    !> The maximal polynomial degree of the modg scheme
    integer, intent(in) :: maxPolyDegree
    !> The cubical elements.
    type(atl_cube_elem_type), intent(in) :: mesh
    !> The data of the kernel. This one is updated by the projection.
    type(atl_kerneldata_type), intent(inout) :: kerneldata
    !> The current level
    integer, intent(in) :: currentLevel
    ! --------------------------------------------------------------------------
    integer(kind=long_k) :: elemPos
    integer :: iElem, xTestFunc, yTestFunc, zTestFunc, testPos, xAnsFuncMin, &
      &        xAnsFunc, yAnsFuncMin, yAnsFunc, zAnsFuncMin, zAnsFunc,       &
      &        ansPos, varPos, iSource, nSourceElems, testPosMax
    real(kind=rk) :: jacobiDet, xScalProd, yScalProd, zScalProd
    ! --------------------------------------------------------------------------

    jacobiDet = (0.5_rk*mesh%length)**3

    do iSource = 1, size(sourcedata%method)

      nSourceElems = sourcedata%method(iSource)%elems(currentLevel)%nElems

      do varPos = 1, nScalars
        do iElem = 1, nSourceElems

          ! Position of the current element
          elempos = sourcedata%method(iSource)%elems(currentLevel) &
            &                 %posInTotal%val(iElem)

          ! Now, we loop over all the test functions for this element and
          ! calculate the projection of the source terms onto this
          ! test functions.
          xTestFunc = 1
          yTestFunc = 1
          zTestFunc = 1
  testposmax = (((maxpolydegree) + 1) &
    &   * ((maxpolydegree) + 2) &
    &   * ((maxpolydegree) + 3)) &
    & / 6
          do testPos = 1, testPosMax

            ! Loop over relevant ans functions
            xAnsFuncMin = xTestFunc-2
            if( xAnsFuncMin < 1 ) then
              xAnsFuncMin = xTestFunc
            end if
            do xAnsFunc = xAnsFuncMin,xTestFunc,2
              ! Loop over relevant ans functions
              yAnsFuncMin = yTestFunc-2
              if( yAnsFuncMin < 1 ) then
                yAnsFuncMin = yTestFunc
              end if
              do yAnsFunc = yAnsFuncMin,yTestFunc,2
                ! Loop over relevant ans functions
                zAnsFuncMin = zTestFunc-2
                if( zAnsFuncMin < 1 ) then
                  zAnsFuncMin = zTestFunc
                end if
                do zAnsFunc = zAnsFuncMin,zTestFunc,2

                  ! get position of ansatz functions in the serialized list
                  ! of dofs.
  ! integer divisions are no mistake here.
  anspos = (((xansfunc + yansfunc + zansfunc - 3) &
    &     * (xansfunc + yansfunc + zansfunc - 2) &
    &     * (xansfunc + yansfunc + zansfunc - 1)) &
    &   / 6 + 1)             &
    & + ((zansfunc-1) * (xansfunc + yansfunc + zansfunc -2) &
    &   - ((zansfunc-2) * (zansfunc-1)) / 2) &
    & + (yansfunc-1)

                  ! project the current ansatz function onto the test function
                  xScalProd = ply_scalProdDualLeg(xAnsFunc, xTestFunc)
                  yScalProd = ply_scalProdDualLeg(yAnsFunc, yTestFunc)
                  zScalProd = ply_scalProdDualLeg(zAnsFunc, zTestFunc)
                  kernelData%state_der(elemPos, testPos,  varPos) &
                      & = kernelData%state_der(elemPos, testPos,  varPos) &
                      & + xScalProd * yScalProd * zScalProd &
                      & * jacobiDet &
                      & * sourcedata%method(iSource)%val(iElem, ansPos, varPos)
                end do ! z ansatz functions
              end do ! y ansatz functions
            end do ! x ansatz functions

  ! - ansatz indices are arranged in layers. within each layer, the total
  !   degree remains constant.
  ! - within each layer, we have blocks. within a block, ansfuncz is
  !   constant, y counts up and x accordingly down.
  ! - within each block, we have items. each item represents one particular
  !   combination of ansfuncx, -y, and -z degrees.

  if (xtestfunc .ne. 1) then
    ! next item
    xtestfunc = xtestfunc - 1
    ytestfunc = ytestfunc + 1
  elseif (ytestfunc .ne. 1) then
    ! next block
    xtestfunc = ytestfunc - 1
    ytestfunc = 1
    ztestfunc = ztestfunc + 1
  else
    ! next layer
    xtestfunc = ztestfunc + 1
    ytestfunc = 1
    ztestfunc = 1
  end if
          end do ! test functions
        end do ! elem loop
      end do ! variable loop
    end do ! source loop

  end subroutine atl_modg_project_source_P


  !> Projection of the numerical flux onto the testfunctions for Q_space.
  subroutine modg_project_numFlux_Q( nTotalFaces, &
    &                                nFaceDofs, nScalars, faceFlux, &
    &                                maxPolyDegree, length, &
    &                                nElems_fluid, dl_prod, projection, dirVec )
    ! --------------------------------------------------------------------------
    !> dimensions
    integer, intent(in) :: nTotalFaces, nFaceDofs, nScalars
    !> The numerical flux on the left face in modal representations.
    !! Dimension is nTotalFaces, (maxPolyDegree+1)^2 , nScalars, 2
    real(kind=rk), intent(in) :: faceFlux(:,:,:,:)
    !> The maximal polynomial degree in each spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The length of the cubes.
    real(kind=rk), intent(in) :: length
    !> The element index
    integer, intent(in) :: nElems_fluid
    !> The numerical flux projected onto the test functions.
    real(kind=rk), intent(inout) :: projection(:,:,:)
    !> Precomputed dual Legendre products:
    real(kind=rk), intent(in) :: dl_prod(2, maxPolyDegree+1)
    !> direction (x,y,z)
    integer, intent(in) :: dirVec(3)
    ! --------------------------------------------------------------------------
    integer :: testFunc1, testFunc2, testFunc3, testPosFunc(3)
    integer :: testPos, ansPos(4)
    real(kind=rk) :: yScalProd, zScalProd(4)
    real(kind=rk) :: outerNormalLeft, outerNormalRight
    real(kind=rk) :: jacobiDetFaceProj
    real(kind=rk) :: faceValLeft, faceValRight
    integer :: iVar, min2mpd
    integer :: jk
    integer :: maxpd_m1
    ! --------------------------------------------------------------------------


    ! Jacobi determinant for the projections of the numerical fluxes onto the
    ! test functions
    jacobiDetFaceProj = (0.5_rk*length)**2
    outerNormalLeft = atl_elemfaceToNormal_prp(tem_left)
    outerNormalRight = atl_elemfaceToNormal_prp(tem_right)
    min2mpd = min(maxPolyDegree+1,2)
    maxpd_m1 = max(maxPolyDegree-1,0)


    ! Loop over all the test functions and project the numerical flux to them.
    do testFunc1 = 1,min2mpd
      faceValLeft  = ply_faceValLeftBndTest(testFunc1) * outerNormalLeft
      faceValRight = ply_faceValRightBndTest(testFunc1) * outerNormalRight

      do jk=1,min2mpd**2
        testFunc2 = mod(jk-1,min2mpd) + 1
        testFunc3 = (jk-1)/min2mpd + 1
        ! Just a single term to sum

        ! position of the test functions in the kerneldata
        testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  testpos = testposfunc(dirvec(1))                                      &
    &      + ( ( testposfunc(dirvec(2))-1)                             &
    &      + (testposfunc(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        ! calculate the projection of the ansatz and test function
        zScalProd(1) = dl_prod(2, testFunc3) &
          &            * dl_prod(2, testFunc2) * jacobiDetFaceProj

        ! the position of the modal coefficeint of this ansatz functions
  anspos(1) = testfunc2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)

        ! buffer the result in kernel data and take care of the outer
        ! surface unit normal
        do iVar=1,nScalars
          projection(:nElems_fluid,testPos,iVar) &
            &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
            ! ... for the left face
            &      * ( faceValLeft &
            &          * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
            ! ... for the right face
            &        + faceValRight &
            &          * faceFlux(:nElems_fluid,ansPos(1),iVar,2) )
        end do
      end do

      do jk=1,min2mpd*maxpd_m1
        testFunc2 = mod(jk-1,min2mpd) + 1
        testFunc3 = (jk-1)/min2mpd + 3
        ! Two terms to sum

        ! position of the test functions in the kerneldata
        testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  testpos = testposfunc(dirvec(1))                                      &
    &      + ( ( testposfunc(dirvec(2))-1)                             &
    &      + (testposfunc(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        ! calculate the projection of the ansatz and test function
        yScalProd = dl_prod(2, testFunc2) * jacobiDetFaceProj

        ! the position of the modal coefficeint of this ansatz functions
  anspos(1) = testfunc2                                      &
    &      + ( ( testfunc3-2-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)
  anspos(2) = testfunc2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)

        ! calculate the projection of the ansatz and test function
        zScalProd(1) = dl_prod(1, testFunc3) * yScalProd
        zScalProd(2) = dl_prod(2, testFunc3) * yScalProd

        ! buffer the result in kernel data and take care of the outer
        ! surface unit normal
        do iVar=1,nScalars
          projection(:nElems_fluid,testPos,iVar) &
            &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
            ! ... for the left face
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,2) ) &
            &  - zScalProd(2) &
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,2) )
        end do

      end do

      do jk=1,min2mpd*maxpd_m1
        testFunc3 = mod(jk-1,min2mpd) + 1
        testFunc2 = (jk-1)/min2mpd + 3
        ! Two terms to sum

        ! position of the test functions in the kerneldata
        testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  testpos = testposfunc(dirvec(1))                                      &
    &      + ( ( testposfunc(dirvec(2))-1)                             &
    &      + (testposfunc(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)


        ! calculate the projection of the ansatz and test function
        zScalProd(1) = dl_prod(2, testFunc3) &
          &            * dl_prod(1, testFunc2) * jacobiDetFaceProj
        zScalProd(2) = dl_prod(2, testFunc3) &
          &            * dl_prod(2, testFunc2) * jacobiDetFaceProj

        ! the position of the modal coefficeint of this ansatz functions
  anspos(1) = testfunc2-2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)
  anspos(2) = testfunc2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)

        ! buffer the result in kernel data and take care of the outer
        ! surface unit normal
        do iVar=1,nScalars
          projection(:nElems_fluid,testPos,iVar) &
            &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
            ! ... for the left face
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,2) ) &
            &  - zScalProd(2) &
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,2) )
        end do

      end do

      do jk=1,maxpd_m1**2
        testFunc2 = mod(jk-1,maxpd_m1) + 3
        testFunc3 = (jk-1)/maxpd_m1 + 3
        ! Four terms to sum

        ! position of the test functions in the kerneldata
        testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  testpos = testposfunc(dirvec(1))                                      &
    &      + ( ( testposfunc(dirvec(2))-1)                             &
    &      + (testposfunc(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)


        ! calculate the projection of the ansatz and test function
        zScalProd(1) = dl_prod(1, testFunc3) * dl_prod(1, testFunc2) &
          &                                  * jacobiDetFaceProj
        zScalProd(2) = dl_prod(1, testFunc3) * dl_prod(2, testFunc2) &
          &                                  * jacobiDetFaceProj
        zScalProd(3) = dl_prod(2, testFunc3) * dl_prod(1, testFunc2) &
          &                                  * jacobiDetFaceProj
        zScalProd(4) = dl_prod(2, testFunc3) * dl_prod(2, testFunc2) &
          &                                  * jacobiDetFaceProj

        ! the position of the modal coefficeint of this ansatz functions
  anspos(1) = testfunc2-2                                      &
    &      + ( ( testfunc3-2-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)
  anspos(2) = testfunc2                                      &
    &      + ( ( testfunc3-2-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)
  anspos(3) = testfunc2-2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)
  anspos(4) = testfunc2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)

        ! buffer the result in kernel data and take care of the outer
        ! surface unit normal
        do iVar=1,nScalars
          projection(:nElems_fluid,testPos,iVar) &
            &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
            ! ... for the left face
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,2) ) &
            &  - zScalProd(2) &
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,2) ) &
            &  - zScalProd(3) &
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(3),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(3),iVar,2) ) &
            &  - zScalProd(4) &
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(4),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(4),iVar,2) )
        end do

      end do

    end do
  end subroutine modg_project_numFlux_Q



  !> Projection of the numerical flux onto the differentiated testfunctions.
  subroutine modg_project_numFlux_diffTest_Q( nScalars, faceFlux,      &
    & maxPolyDegree, length, nElems_fluid, dl_prod, projection, dirVec )
    ! --------------------------------------------------------------------------
    !> dimensions
    integer, intent(in) :: nScalars
    !> The numerical flux on the left face in modal representations.
    !! Dimension is (maxPolyDegree+1)^2 , nScalars
    real(kind=rk), intent(inout) :: faceFlux(:,:,:,:)
    !> The maximal polynomial degree in each spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The length of the cubes.
    real(kind=rk), intent(in) :: length
    !> The element index
    integer, intent(in) :: nElems_fluid
    !> The numerical flux projected onto the test functions.
    real(kind=rk), intent(inout) :: projection(:,:,:)
    !> Precomputed dual Legendre products:
    real(kind=rk), intent(in) :: dl_prod(2, maxPolyDegree+1)
    !> direction (x,y,z)
    integer, intent(in) :: dirVec(3)
    ! --------------------------------------------------------------------------
    integer :: testFunc1, testFunc2, testFunc3, testPosFunc(3)
    integer :: testPos, ansPos(4)
    real(kind=rk) :: yScalProd, zScalProd(4)
    real(kind=rk) :: jacobiDetFaceProj
    real(kind=rk) :: faceValLeft, faceValRight
    integer :: iVar, min2mpd
    integer :: jk
    integer :: maxpd_m1
    ! --------------------------------------------------------------------------

    ! Jacobi determinant for the projections of the numerical fluxes onto the
    ! test functions

    jacobiDetFaceProj = (0.5_rk*length)**2
    zscalProd = 0.0_rk

    min2mpd = min(maxPolyDegree+1,5)
    maxpd_m1 = max(maxPolyDegree-4,0)

    do testFunc1 = 2,maxPolyDegree+1
      faceValLeft  = ply_faceValLeftBndGradTest(testFunc1)
      faceValRight = ply_faceValRightBndGradTest(testFunc1)

      do jk=1,(min2mpd)**2
        testFunc2 = mod(jk-1,min2mpd) + 1
        testFunc3 = (jk-1)/min2mpd + 1
        ! Just a single term to sum

        ! position of the test functions in the kerneldata
        testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  testpos = testposfunc(dirvec(1))                                      &
    &      + ( ( testposfunc(dirvec(2))-1)                             &
    &      + (testposfunc(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)


        ! calculate the projection of the ansatz and test function
        zScalProd(1) = dl_prod(2, testFunc3) &
        &            * dl_prod(2, testFunc2) * jacobiDetFaceProj

        ! the position of the modal coefficeint of this ansatz functions
  anspos(1) = testfunc2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)

        ! buffer the result in kernel data and take care of the outer
        ! surface unit normal
        do iVar=1,nScalars

          projection(:nElems_fluid,testPos,iVar) &
            &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
            ! ... for the left face
            &      * ( faceValLeft &
            &          * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
            ! ... for the right face
            &        + faceValRight &
            &          * faceFlux(:nElems_fluid,ansPos(1),iVar,2) )
        end do

      end do

      ! for the testfunct3 greater than 5 and testfunc2 less than 5
      do jk=1,maxpd_m1*5
        testFunc2 = mod(jk-1,min2mpd) + 1
        testFunc3 = (jk-1)/min2mpd + 6
        ! Two terms to sum

        ! position of the test functions in the kerneldata
        testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  testpos = testposfunc(dirvec(1))                                      &
    &      + ( ( testposfunc(dirvec(2))-1)                             &
    &      + (testposfunc(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)


        ! calculate the projection of the ansatz and test function
        yScalProd = dl_prod(2, testFunc2) * jacobiDetFaceProj

        ! the position of the modal coefficeint of this ansatz functions
  anspos(1) = testfunc2                                      &
    &      + ( ( testfunc3-2-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)
  anspos(2) = testfunc2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)

        ! calculate the projection of the ansatz and test function
        zScalProd(1) = dl_prod(1, testFunc3) * yScalProd
        zScalProd(2) = dl_prod(2, testFunc3) * yScalProd

       ! buffer the result in kernel data and take care of the outer
        ! surface unit normal
        do iVar=1,nScalars
          projection(:nElems_fluid,testPos,iVar) &
            &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
            ! ... for the left face
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,2) ) &
            &  - zScalProd(2) &
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,2) )
        end do
      end do

      ! for the testfunct2 greater than 5 and testfunc3 less than 5
      do jk=1,maxpd_m1*5
        testFunc3 = mod(jk-1,min2mpd) + 1
        testFunc2 = (jk-1)/min2mpd + 6
        ! Two terms to sum

        ! position of the test functions in the kerneldata
        testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  testpos = testposfunc(dirvec(1))                                      &
    &      + ( ( testposfunc(dirvec(2))-1)                             &
    &      + (testposfunc(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)


        ! calculate the projection of the ansatz and test function
        zScalProd(1) = dl_prod(2, testFunc3) &
          &            * dl_prod(1, testFunc2) * jacobiDetFaceProj
        zScalProd(2) = dl_prod(2, testFunc3) &
          &            * dl_prod(2, testFunc2) * jacobiDetFaceProj

        ! the position of the modal coefficeint of this ansatz functions
  anspos(1) = testfunc2-2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)
  anspos(2) = testfunc2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)

        ! buffer the result in kernel data and take care of the outer
        ! surface unit normal
        do iVar=1,nScalars
          projection(:nElems_fluid,testPos,iVar) &
            &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
            ! ... for the left face
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,2) ) &
            &  - zScalProd(2) &
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,2) )
        end do

      end do

      do jk=1,(maxpd_m1)**2
        testFunc2 = mod(jk-1,maxpd_m1) + 6
        testFunc3 = (jk-1)/maxpd_m1 + 6
        ! Four terms to sum
        ! position of the test functions in the kerneldata
        testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  testpos = testposfunc(dirvec(1))                                      &
    &      + ( ( testposfunc(dirvec(2))-1)                             &
    &      + (testposfunc(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)


        ! calculate the projection of the ansatz and test function
        zScalProd(1) = dl_prod(1, testFunc3) * dl_prod(1, testFunc2) &
          &                                  * jacobiDetFaceProj
        zScalProd(2) = dl_prod(1, testFunc3) * dl_prod(2, testFunc2) &
          &                                  * jacobiDetFaceProj
        zScalProd(3) = dl_prod(2, testFunc3) * dl_prod(1, testFunc2) &
          &                                  * jacobiDetFaceProj
        zScalProd(4) = dl_prod(2, testFunc3) * dl_prod(2, testFunc2) &
          &                                  * jacobiDetFaceProj

        ! the position of the modal coefficeint of this ansatz functions
  anspos(1) = testfunc2-2                                      &
    &      + ( ( testfunc3-2-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)
  anspos(2) = testfunc2                                      &
    &      + ( ( testfunc3-2-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)
  anspos(3) = testfunc2-2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)
  anspos(4) = testfunc2                                      &
    &      + ( ( testfunc3-1)                             &
    &      + (1-1)*(maxpolydegree+1))*(maxpolydegree+1)

        ! buffer the result in kernel data and take care of the outer
        ! surface unit normal
        do iVar=1,nScalars
          projection(:nElems_fluid,testPos,iVar) &
            &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
            ! ... for the left face
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(1),iVar,2) ) &
            &  - zScalProd(2) &
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(2),iVar,2) ) &
            &  - zScalProd(3) &
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(3),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(3),iVar,2) ) &
            &  - zScalProd(4) &
            &    * ( faceValLeft &
            &        * faceFlux(:nElems_fluid,ansPos(4),iVar,1) &
            ! ... for the right face
            &      + faceValRight &
            &        * faceFlux(:nElems_fluid,ansPos(4),iVar,2) )
        end do
      end do

    end do
  end subroutine modg_project_numFlux_difftest_Q


  !> Projection of the numerical flux onto the testfunctions for P_space.
  subroutine modg_project_numFlux_P( nTotalFaces, nFaceDofs, nScalars,    &
    & faceFlux, maxPolyDegree, length, nElems_fluid, dl_prod, projection, &
    & dirVec                                                              )
    ! --------------------------------------------------------------------------
    !> dimensions
    integer, intent(in) :: nTotalFaces, nFaceDofs, nScalars
    !> The numerical flux on the left face in modal representations.
    !! Dimension is (maxPolyDegree+1)^2 , nScalars
    real(kind=rk), intent(in) :: faceFlux(nTotalFaces,nFaceDofs,nScalars,2)
    !> The maximal polynomial degree in each spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The length of the cubes.
    real(kind=rk), intent(in) :: length
    !> The element index
    integer, intent(in) :: nElems_fluid
    !> The numerical flux projected onto the test functions.
    real(kind=rk), intent(inout) :: projection(:,:,:)
    !> Precomputed dual Legendre products:
    real(kind=rk), intent(in) :: dl_prod(2, maxPolyDegree+1)
    !> direction (x,y,z)
    integer, intent(in) :: dirVec(3)
    ! --------------------------------------------------------------------------
    integer :: testFunc1, testFunc2, testFunc3, testPosFunc(3)
    integer :: testPos, ansPos(4)
    real(kind=rk) :: yScalProd, zScalProd(4)
    real(kind=rk) :: outerNormalLeft, outerNormalRight
    real(kind=rk) :: jacobiDetFaceProj
    real(kind=rk) :: faceValLeft, faceValRight
    integer :: iVar
    integer :: min2mpd
    ! --------------------------------------------------------------------------


    ! Jacobi determinant for the projections of the numerical fluxes onto the
    ! test functions
    jacobiDetFaceProj = (0.5_rk*length)**2
    outerNormalLeft = atl_elemfaceToNormal_prp(tem_left)
    outerNormalRight = atl_elemfaceToNormal_prp(tem_right)
    min2mpd = min(maxPolyDegree+1,2)


    ! Loop over all the test functions and project the numerical flux to them.
    do testFunc1 = 1,min2mpd
      faceValLeft = ply_faceValLeftBndTest(testFunc1) * outerNormalLeft
      faceValRight = ply_faceValRightBndTest(testFunc1) * outerNormalRight

      do testFunc2 = 1,min(2, maxPolyDegree+1 - (testFunc1-1))
        do testFunc3 = 1,min(2, maxPolyDegree+1 - (testFunc1-1) - (testfunc2-1))
          ! Just a single term to sum

          ! position of the test functions in the kerneldata
          testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  ! integer divisions are no mistake here.
  testpos = (((testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 3) &
    &     * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 2) &
    &     * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((testposfunc(dirvec(3))-1) * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) -2) &
    &   - ((testposfunc(dirvec(3))-2) * (testposfunc(dirvec(3))-1)) / 2) &
    & + (testposfunc(dirvec(2))-1)

          ! calculate the projection of the ansatz and test function
          zScalProd(1) = dl_prod(2, testFunc3) &
            &            * dl_prod(2, testFunc2) * jacobiDetFaceProj

          ! the position of the modal coefficeint of this ansatz functions
  ! integer divisions are no mistake here.
  anspos(1) = ((((testfunc2 - 1) + (testfunc3 - 1))            &
    &   * (((testfunc2 - 1) + (testfunc3 - 1)) + 1)) / 2 + 1) &
    & + (testfunc3 - 1)

          ! buffer the result in kernel data and take care of the outer
          ! surface unit normal
          do iVar=1, nScalars
              projection(:nElems_fluid,testPos,iVar) &
                &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
                ! ... for the left face
                &      * ( faceValLeft &
                &          * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
                ! ... for the right face
                &        + faceValRight &
                &          * faceFlux(:nElems_fluid,ansPos(1),iVar,2) )
          end do

        end do
      end do

      do testFunc2 = 1,min(2, maxPolyDegree+1 - (testFunc1-1))
        do testFunc3 = 3,maxPolyDegree+1 - (testFunc1-1) - (testfunc2-1)
          ! Two terms to sum

          ! position of the test functions in the kerneldata
          testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  ! integer divisions are no mistake here.
  testpos = (((testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 3) &
    &     * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 2) &
    &     * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((testposfunc(dirvec(3))-1) * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) -2) &
    &   - ((testposfunc(dirvec(3))-2) * (testposfunc(dirvec(3))-1)) / 2) &
    & + (testposfunc(dirvec(2))-1)

          ! calculate the projection of the ansatz and test function
          yScalProd = dl_prod(2, testFunc2) * jacobiDetFaceProj

          ! the position of the modal coefficeint of this ansatz functions
  ! integer divisions are no mistake here.
  anspos(1) = ((((testfunc2 - 1) + (testfunc3-2 - 1))            &
    &   * (((testfunc2 - 1) + (testfunc3-2 - 1)) + 1)) / 2 + 1) &
    & + (testfunc3-2 - 1)
  ! integer divisions are no mistake here.
  anspos(2) = ((((testfunc2 - 1) + (testfunc3 - 1))            &
    &   * (((testfunc2 - 1) + (testfunc3 - 1)) + 1)) / 2 + 1) &
    & + (testfunc3 - 1)

          ! calculate the projection of the ansatz and test function
          zScalProd(1) = dl_prod(1, testFunc3) * yScalProd
          zScalProd(2) = dl_prod(2, testFunc3) * yScalProd

          ! buffer the result in kernel data and take care of the outer
          ! surface unit normal
          do iVar=1, nScalars
            projection(:nElems_fluid,testPos,iVar) &
              &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
              ! ... for the left face
              &    * ( faceValLeft &
              &        * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
              ! ... for the right face
              &      + faceValRight &
              &        * faceFlux(:nElems_fluid,ansPos(1),iVar,2) ) &
              &  - zScalProd(2) &
              &    * ( faceValLeft &
              &        * faceFlux(:nElems_fluid,ansPos(2),iVar,1) &
              ! ... for the right face
              &      + faceValRight &
              &        * faceFlux(:nElems_fluid,ansPos(2),iVar,2) )
          end do

        end do
      end do

      do testFunc2 = 3,maxPolyDegree+1 - (testFunc1-1)
        do testFunc3 = 1,min(2,maxPolyDegree+1 - (testFunc1-1) - (testfunc2-1))
          ! Two terms to sum

          ! position of the test functions in the kerneldata
          testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  ! integer divisions are no mistake here.
  testpos = (((testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 3) &
    &     * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 2) &
    &     * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((testposfunc(dirvec(3))-1) * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) -2) &
    &   - ((testposfunc(dirvec(3))-2) * (testposfunc(dirvec(3))-1)) / 2) &
    & + (testposfunc(dirvec(2))-1)


          ! calculate the projection of the ansatz and test function
          zScalProd(1) = dl_prod(2, testFunc3) &
            &            * dl_prod(1, testFunc2) * jacobiDetFaceProj
          zScalProd(2) = dl_prod(2, testFunc3) &
            &            * dl_prod(2, testFunc2) * jacobiDetFaceProj

          ! the position of the modal coefficeint of this ansatz functions
  ! integer divisions are no mistake here.
  anspos(1) = ((((testfunc2-2 - 1) + (testfunc3 - 1))            &
    &   * (((testfunc2-2 - 1) + (testfunc3 - 1)) + 1)) / 2 + 1) &
    & + (testfunc3 - 1)
  ! integer divisions are no mistake here.
  anspos(2) = ((((testfunc2 - 1) + (testfunc3 - 1))            &
    &   * (((testfunc2 - 1) + (testfunc3 - 1)) + 1)) / 2 + 1) &
    & + (testfunc3 - 1)

          ! buffer the result in kernel data and take care of the outer
          ! surface unit normal
          do iVar=1, nScalars
            projection(:nElems_fluid,testPos,iVar) &
              &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
              ! ... for the left face
              &    * ( faceValLeft &
              &        * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
              ! ... for the right face
              &      + faceValRight &
              &        * faceFlux(:nElems_fluid,ansPos(1),iVar,2) ) &
              &  - zScalProd(2) &
              &    * ( faceValLeft &
              &        * faceFlux(:nElems_fluid,ansPos(2),iVar,1) &
              ! ... for the right face
              &      + faceValRight &
              &        * faceFlux(:nElems_fluid,ansPos(2),iVar,2) )
          end do

        end do
      end do

      do testFunc2 = 3,maxPolyDegree+1 - (testFunc1-1)
        do testFunc3 = 3,maxPolyDegree+1 - (testFunc1-1) - (testfunc2-1)
          ! Four terms to sum

          ! position of the test functions in the kerneldata
          testPosFunc(:) = (/testFunc1, testFunc2, testFunc3/)
  ! integer divisions are no mistake here.
  testpos = (((testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 3) &
    &     * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 2) &
    &     * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((testposfunc(dirvec(3))-1) * (testposfunc(dirvec(1)) + testposfunc(dirvec(2)) + testposfunc(dirvec(3)) -2) &
    &   - ((testposfunc(dirvec(3))-2) * (testposfunc(dirvec(3))-1)) / 2) &
    & + (testposfunc(dirvec(2))-1)


          ! calculate the projection of the ansatz and test function
          zScalProd(1) = dl_prod(1, testFunc3) * dl_prod(1, testFunc2) &
            &                                  * jacobiDetFaceProj
          zScalProd(2) = dl_prod(1, testFunc3) * dl_prod(2, testFunc2) &
            &                                  * jacobiDetFaceProj
          zScalProd(3) = dl_prod(2, testFunc3) * dl_prod(1, testFunc2) &
            &                                  * jacobiDetFaceProj
          zScalProd(4) = dl_prod(2, testFunc3) * dl_prod(2, testFunc2) &
            &                                  * jacobiDetFaceProj

          ! the position of the modal coefficeint of this ansatz functions
  ! integer divisions are no mistake here.
  anspos(1) = ((((testfunc2-2 - 1) + (testfunc3-2 - 1))            &
    &   * (((testfunc2-2 - 1) + (testfunc3-2 - 1)) + 1)) / 2 + 1) &
    & + (testfunc3-2 - 1)
  ! integer divisions are no mistake here.
  anspos(2) = ((((testfunc2 - 1) + (testfunc3-2 - 1))            &
    &   * (((testfunc2 - 1) + (testfunc3-2 - 1)) + 1)) / 2 + 1) &
    & + (testfunc3-2 - 1)
  ! integer divisions are no mistake here.
  anspos(3) = ((((testfunc2-2 - 1) + (testfunc3 - 1))            &
    &   * (((testfunc2-2 - 1) + (testfunc3 - 1)) + 1)) / 2 + 1) &
    & + (testfunc3 - 1)
  ! integer divisions are no mistake here.
  anspos(4) = ((((testfunc2 - 1) + (testfunc3 - 1))            &
    &   * (((testfunc2 - 1) + (testfunc3 - 1)) + 1)) / 2 + 1) &
    & + (testfunc3 - 1)

          ! buffer the result in kernel data and take care of the outer
          ! surface unit normal
          do iVar=1, nScalars
            projection(:nElems_fluid,testPos,iVar) &
              &  = projection(:nElems_fluid,testPos,iVar) - zScalProd(1) &
              ! ... for the left face
              &    * ( faceValLeft &
              &        * faceFlux(:nElems_fluid,ansPos(1),iVar,1) &
              ! ... for the right face
              &      + faceValRight &
              &        * faceFlux(:nElems_fluid,ansPos(1),iVar,2) ) &
              &  - zScalProd(2) &
              &    * ( faceValLeft &
              &        * faceFlux(:nElems_fluid,ansPos(2),iVar,1) &
              ! ... for the right face
              &      + faceValRight &
              &        * faceFlux(:nElems_fluid,ansPos(2),iVar,2) ) &
              &  - zScalProd(3) &
              &    * ( faceValLeft &
              &        * faceFlux(:nElems_fluid,ansPos(3),iVar,1) &
              ! ... for the right face
              &      + faceValRight &
              &        * faceFlux(:nElems_fluid,ansPos(3),iVar,2) ) &
              &  - zScalProd(4) &
              &    * ( faceValLeft &
              &        * faceFlux(:nElems_fluid,ansPos(4),iVar,1) &
              ! ... for the right face
              &      + faceValRight &
              &        * faceFlux(:nElems_fluid,ansPos(4),iVar,2) )
          end do

        end do
      end do

    end do

  end subroutine modg_project_numFlux_P


  !> Lift the mean on the face to a positive value if necessary.
  !!
  !! In some equations, some variables may not be allowed to be negative,
  !! for example the density and energy in flow simulations.
  !! With this routine we enforce this limitation for the projected mean
  !! state on the element surfaces.
  subroutine atl_modg_ensure_pos_facemean(                                  &
    &          nelems_fluid, volState, faceRep, nScalars, ensure_positivity )
    ! -------------------------------------------------------------------- !
    !> Number of fluid elements
    integer, intent(in) :: nElems_fluid

    !> Volumetric modal states for each element
    real(kind=rk), intent(in) :: volState(:,:,:)

    !> Modal representation on the face that might need limiting.
    !!
    !! All means below 0 of variables where positivity is to be ensured will
    !! be increased to a small value above 0.
    type(atl_faceRep_type), intent(inout) :: faceRep(:)

    !> Number of variables
    integer, intent(in) :: nScalars

    !> Indication for each variable, whether positivity is to be ensured
    !! or not.
    !!
    !! Only variables where this is set to .true. will be modified by this
    !! routine.
    logical, intent(in) :: ensure_positivity(:)
    ! --------------------------------------------------------------------------
    integer :: iVar, iDir
    real(kind=rk) :: epsfact
    ! --------------------------------------------------------------------------

    epsfact = epsilon(volState(1,1,1))

    do iVar=1,nScalars
      if (ensure_positivity(iVar)) then

        ! We assume the integral mean of the element is positive for all
        ! elements. The integral mean on the surface will now be lifted to
        ! a small fraction of that element mean if it is below that threshold.
        do iDir=1,3
          facerep(iDir)%dat(1:nElems_fluid,1,iVar,1)             &
            & = max( facerep(iDir)%dat(1:nElems_fluid,1,iVar,1), &
            &        epsfact * volState(1:nElems_fluid,1,iVar)   )
          facerep(iDir)%dat(1:nElems_fluid,1,iVar,2)             &
            & = max( facerep(iDir)%dat(1:nElems_fluid,1,iVar,2), &
            &        epsfact * volState(1:nElems_fluid,1,iVar)   )
        end do

      end if
    end do

  end subroutine atl_modg_ensure_pos_facemean


  !> Projects modal representation of each cell to its faces, i.e.
  !! this subroutine creates a modal representation on the faces.
  subroutine atl_modg_modalVolToModalFace( nElems_fluid, length , volState, &
    &                                      faceRep, nScalars, nDerivatives, &
    &                                      modg                             )
    ! --------------------------------------------------------------------------
    !> Volumetric, modal states for each element.
    real(kind=rk), intent(in) :: volState(:,:,:)
    !> Modal representation on the face (will be updated by this routine for all
    !! fluid elements in mesh).
    type(atl_faceRep_type), intent(inout) :: faceRep(:)
    !> number of variables
    integer, intent(in) :: nScalars
    !> number of derivatives
    integer, intent(in) :: nDerivatives
    !> number of fluid elements
    integer, intent(in) :: nElems_fluid
    !> length of cubic element
    real(kind=rk), intent(in) :: length
    !> The parameters of your modg scheme.
    type(atl_modg_scheme_type), intent(in) :: modg
    ! --------------------------------------------------------------------------
    integer :: iDir, spaceDir
    integer :: nFaces
    real(kind=rk), allocatable :: volState_Q(:,:,:)
    real(kind=rk), allocatable :: faceState_Q(:,:,:,:)
    ! --------------------------------------------------------------------------

    do iDir = 1,3
      faceRep(iDir)%dat(1:nElems_fluid,:,:,:) = 0.0_rk
    end do

    ! Iterate over all the fluid elements and project its modal representations
    ! to the faces of the element.
    select case(modg%basisType)
      case(Q_space) ! Q tensor product ansatz functions

        ! Project the state on the faces in each direction.
        call modg_volToFace_Q_x(                &
          & volState      = volstate,           &
          & maxPolyDegree = modg%maxPolyDegree, &
          & nScalars      = nScalars,           &
          & nElems        = nElems_fluid,       &
          & faceState     = faceRep(1)%dat      )
        call modg_volToFace_Q_y(                &
          & volState      = volstate,           &
          & maxPolyDegree = modg%maxPolyDegree, &
          & nScalars      = nScalars,           &
          & nElems        = nElems_fluid,       &
          & faceState     = faceRep(2)%dat      )
        call modg_volToFace_Q_z(                &
          & volState      = volstate,           &
          & maxPolyDegree = modg%maxPolyDegree, &
          & nScalars      = nScalars,           &
          & nElems        = nElems_fluid,       &
          & faceState     = faceRep(3)%dat      )

        if (nDerivatives == 1) then
          do iDir = 1, 6
            ! Project to the face in the current direction.
            spaceDir = qAxis(iDir)
            nFaces = size(faceRep(spaceDir)%dat,1)

            ! Project derivatives to the faces
              call atl_modg_volToFace_grad_Q(           &
                & volState      = volstate,             &
                & maxPolyDegree = modg%maxPolyDegree,   &
                & faceDir       = iDir,                 &
                & nScalars      = nScalars,             &
                & nElems        = nElems_fluid,         &
                & elemLength    = length,               &
                & faceState     = faceRep(spaceDir)%dat )

          end do
        end if

      case(P_space) ! P-tensor product ansatz functions

        ! Now, iterate over all the faces and project to this face.
        do iDir = 1, 6
          ! Project to the face in the current direction.
          spaceDir = qAxis(iDir)

          call atl_modg_volToFace_P(                                  &
            & nTotalElems   = size(volstate,1),                       &
            & nTotalFaces   = size(faceRep(spaceDir)%dat,1),          &
            & nDofs         = size(volstate,2),                       &
            & nFaceDofs     = size(faceRep(spaceDir)%dat,2),          &
            & nScalars      = nScalars,                               &
            & volState      = volstate,                               &
            & maxPolyDegree = modg%maxPolyDegree,                     &
            & faceDir       = iDir,                                   &
            & nElems        = nElems_fluid,                           &
            & faceState     = faceRep(spaceDir)%dat(:,:,1:nScalars,:) )
        end do

        if (nDerivatives == 1) then

          ! For the projection of the derivatives to the faces in P_space
          ! we can use the same subroutine that we use for the projection
          ! in Q_space but we need to change the polynomial space to a
          ! Q_space representation and change it back to P_space afterwards.

          allocate(volstate_Q(nElems_fluid,(modg%maxPolyDegree+1)**3,nScalars))

          call ply_change_poly_space( inspace    = P_space,            &
            &                         instate    = volstate,           &
            &                         outstate   = volstate_Q,         &
            &                         maxPolyDeg = modg%maxPolyDegree, &
            &                         nElems     = nElems_fluid,       &
            &                         nVars      = nScalars,           &
            &                         nDims      = 3                   )

          do iDir = 1, 6
            ! Project to the face in the current direction.
            spaceDir = qAxis(iDir)

            nFaces = size(faceRep(spaceDir)%dat,1)

            allocate(faceState_Q(nFaces,(modg%maxPolyDegree+1)**2,4*nScalars,2))

            ! Change space for the left and for the right face seperatly
            call ply_change_poly_space( inspace    = P_space,              &
              &                         instate    = faceRep(spaceDir)     &
              &                                       %dat(:,:,:,1),       &
              &                         outstate   = faceState_Q(:,:,:,1), &
              &                         maxPolyDeg = modg%maxPolyDegree,   &
              &                         nElems     = nFaces,               &
              &                         nVars      = 4*nScalars,           &
              &                         nDims      = 2                     )

            call ply_change_poly_space( inspace    = P_space,              &
              &                         instate    = faceRep(spaceDir)     &
              &                                       %dat(:,:,:,2),       &
              &                         outstate   = faceState_Q(:,:,:,2), &
              &                         maxPolyDeg = modg%maxPolyDegree,   &
              &                         nElems     = nFaces,               &
              &                         nVars      = 4*nScalars,           &
              &                         nDims      = 2                     )

            ! Project derivatives to the faces
            call atl_modg_volToFace_grad_Q(         &
              & volState      = volstate_Q,         &
              & maxPolyDegree = modg%maxPolyDegree, &
              & faceDir       = iDir,               &
              & nScalars      = nScalars,           &
              & nElems        = nElems_fluid,       &
              & elemLength    = length,             &
              & faceState     = faceState_Q         )

            call ply_change_poly_space( inspace    = Q_space,              &
              &                         instate    = faceState_Q(:,:,:,1), &
              &                         outstate   = faceRep(spaceDir)     &
              &                                       %dat(:,:,:,1),       &
              &                         maxPolyDeg = modg%maxPolyDegree,   &
              &                         nElems     = nFaces,               &
              &                         nVars      = 4*nScalars,           &
              &                         nDims      = 2                     )

            call ply_change_poly_space( inspace    = Q_space,              &
              &                         instate    = faceState_Q(:,:,:,2), &
              &                         outstate   = faceRep(spaceDir)     &
              &                                       %dat(:,:,:,2),       &
              &                         maxPolyDeg = modg%maxPolyDegree,   &
              &                         nElems     = nFaces,               &
              &                         nVars      = 4*nScalars,           &
              &                         nDims      = 2                     )


            deallocate(faceState_Q)
          end do

          deallocate(volstate_Q)

        end if

      case default
        write(logUnit(1),*) 'ERROR in atl_modg_modalVolToModalFace:'
        write(logUnit(1),*) 'Unknown tensor product, stopping ...'
        call tem_abort()
    end select


  end subroutine atl_modg_modalVolToModalFace


  !> Project modal representation of an element to its two faces in X.
  subroutine modg_volToFace_Q_x( nScalars, volState, maxPolyDegree, nElems, &
    &                            faceState )
    ! --------------------------------------------------------------------------
    integer, intent(in) :: nScalars
    !> The modal representation in the volume. First dimension is the number of
    !! volumetric numbers of degrees of freedom and second dimension is the
    !! number of scalar variables in the equation system.
    real(kind=rk), intent(in) :: volState(:,:,:)
    !> The maximal polynomial degree per spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The number of elements
    integer, intent(in) :: nElems
    !> The modal representation on the faces in X direction
    real(kind=rk), intent(inout) :: faceState(:,:,:,:)
    ! --------------------------------------------------------------------------
    integer :: pos, facePos, iAnsX
    integer :: iVar
    integer :: mpd1, mpd1_square
    integer :: iElem
    integer :: iVEF, ipv
    integer :: nIndeps
    ! --------------------------------------------------------------------------

    mpd1 = maxpolydegree+1
    mpd1_square = mpd1**2
    nIndeps = nScalars*nElems*mpd1_square

    ! Split off first ansatz function, to avoid initialization with 0.
    iAnsX = 1

    !$NEC ivdep
    do iVEF=1,nIndeps
      iVar = (iVEF-1) / (nElems*mpd1_square) + 1
      ipv = iVEF - (iVar - 1) * (nElems*mpd1_square)
      facepos = (ipv-1) / nElems + 1
      iElem = mod(ipv-1, nElems) + 1
      ! get position of the current ansatz function
      pos = (facepos-1)*mpd1 + iAnsX

      faceState(iElem,facePos,iVar,1) = volState(iElem, pos, iVar)
      faceState(iElem,facePos,iVar,2) = volState(iElem, pos, iVar)
    end do

    ! Legendre polynomial on the right is just the sum of all modes,
    ! but on the left we alternatingly need to add and subtract the modes.
    ! Thus, we split the loop to allow the correct operation to be used for
    ! each mode.
    do iAnsX=3,maxPolyDegree+1,2

      !$NEC ivdep
      do iVEF=1,nIndeps
        iVar = (iVEF-1) / (nElems*mpd1_square) + 1
        ipv = iVEF - (iVar - 1) * (nElems*mpd1_square)
        facepos = (ipv-1) / nElems + 1
        iElem = mod(ipv-1, nElems) + 1
        ! get position of the current ansatz function
        pos = (facepos-1)*mpd1 + iAnsX

        faceState(iElem,facePos,iVar,1) = faceState(iElem,facePos,iVar,1) &
          &                             + volState(iElem, pos, iVar)
        faceState(iElem,facePos,iVar,2) = faceState(iElem,facePos,iVar,2) &
          &                             + volState(iElem, pos, iVar)
      end do

    end do
    do iAnsX=2,maxPolyDegree+1,2

      !$NEC ivdep
      do iVEF=1,nIndeps
        iVar = (iVEF-1) / (nElems*mpd1_square) + 1
        ipv = iVEF - (iVar - 1) * (nElems*mpd1_square)
        facepos = (ipv-1) / nElems + 1
        iElem = mod(ipv-1, nElems) + 1
        ! get position of the current ansatz function
        pos = (facepos-1)*mpd1 + iAnsX

        faceState(iElem,facePos,iVar,1) = faceState(iElem,facePos,iVar,1) &
          &                             - volState(iElem, pos, iVar)
        faceState(iElem,facePos,iVar,2) = faceState(iElem,facePos,iVar,2) &
          &                             + volState(iElem, pos, iVar)
      end do

    end do

  end subroutine modg_voltoface_Q_x


  !> Project modal representation of an element to its two faces in Y.
  subroutine modg_volToFace_Q_y( nScalars, volState, maxPolyDegree, nElems, &
    &                            faceState )
    ! --------------------------------------------------------------------------
    integer, intent(in) :: nScalars
    !> The modal representation in the volume. First dimension is the number of
    !! volumetric numbers of degrees of freedom and second dimension is the
    !! number of scalar variables in the equation system.
    real(kind=rk), intent(in) :: volState(:,:,:)
    !> The maximal polynomial degree per spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The number of elements
    integer, intent(in) :: nElems
    !> The modal representation on the faces in X direction
    real(kind=rk), intent(inout) :: faceState(:,:,:,:)
    ! --------------------------------------------------------------------------
    integer :: pos, facePos
    integer :: iAnsX, iAnsY, iAnsZ
    integer :: iVar
    integer :: mpd1, mpd1_square
    integer :: iElem
    integer :: iVEF, ipv
    integer :: nIndeps
    ! --------------------------------------------------------------------------

    mpd1 = maxpolydegree+1
    mpd1_square = mpd1**2
    nIndeps = nScalars*nElems*mpd1_square

    ! Split off first ansatz function, to avoid initialization with 0.
    iAnsY = 1

    !$NEC ivdep
    do iVEF=1,nIndeps
      iVar = (iVEF-1) / (nElems*mpd1_square) + 1
      ipv = iVEF - (iVar - 1) * (nElems*mpd1_square)
      facepos = (ipv-1) / nElems + 1
      iElem = mod(ipv-1, nElems) + 1
      iAnsX = mod(facepos-1, mpd1) + 1
      iAnsZ = (facepos-1)/mpd1 + 1
      ! get position of the current ansatz function
      pos = iAnsX + (iAnsY-1)*mpd1 + (iAnsZ-1)*mpd1_square

      faceState(iElem,facePos,iVar,1) = volState(iElem, pos, iVar)
      faceState(iElem,facePos,iVar,2) = volState(iElem, pos, iVar)
    end do

    ! Legendre polynomial on the right is just the sum of all modes,
    ! but on the left we alternatingly need to add and subtract the modes.
    ! Thus, we split the loop to allow the correct operation to be used for
    ! each mode.
    do iAnsY=3,maxPolyDegree+1,2

      !$NEC ivdep
      do iVEF=1,nIndeps
        iVar = (iVEF-1) / (nElems*mpd1_square) + 1
        ipv = iVEF - (iVar - 1) * (nElems*mpd1_square)
        facepos = (ipv-1) / nElems + 1
        iElem = mod(ipv-1, nElems) + 1
        iAnsX = mod(facepos-1, mpd1) + 1
        iAnsZ = (facepos-1)/mpd1 + 1
        ! get position of the current ansatz function
        pos = iAnsX + (iAnsY-1)*mpd1 + (iAnsZ-1)*mpd1_square

        faceState(iElem,facePos,iVar,1) = faceState(iElem,facePos,iVar,1) &
          &                             + volState(iElem, pos, iVar)
        faceState(iElem,facePos,iVar,2) = faceState(iElem,facePos,iVar,2) &
          &                             + volState(iElem, pos, iVar)
      end do

    end do
    do iAnsY=2,maxPolyDegree+1,2

      !$NEC ivdep
      do iVEF=1,nIndeps
        iVar = (iVEF-1) / (nElems*mpd1_square) + 1
        ipv = iVEF - (iVar - 1) * (nElems*mpd1_square)
        facepos = (ipv-1) / nElems + 1
        iElem = mod(ipv-1, nElems) + 1
        iAnsX = mod(facepos-1, mpd1) + 1
        iAnsZ = (facepos-1)/mpd1 + 1
        ! get position of the current ansatz function
        pos = iAnsX + (iAnsY-1)*mpd1 + (iAnsZ-1)*mpd1_square

        faceState(iElem,facePos,iVar,1) = faceState(iElem,facePos,iVar,1) &
          &                             - volState(iElem, pos, iVar)
        faceState(iElem,facePos,iVar,2) = faceState(iElem,facePos,iVar,2) &
          &                             + volState(iElem, pos, iVar)
      end do

    end do

  end subroutine modg_voltoface_Q_y


  !> Project modal representation of an element to its two faces in Y.
  subroutine modg_volToFace_Q_z( nScalars, volState, maxPolyDegree, nElems, &
    &                            faceState )
    ! --------------------------------------------------------------------------
    integer, intent(in) :: nScalars
    !> The modal representation in the volume. First dimension is the number of
    !! volumetric numbers of degrees of freedom and second dimension is the
    !! number of scalar variables in the equation system.
    real(kind=rk), intent(in) :: volState(:,:,:)
    !> The maximal polynomial degree per spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The number of elements
    integer, intent(in) :: nElems
    !> The modal representation on the faces in X direction
    real(kind=rk), intent(inout) :: faceState(:,:,:,:)
    ! --------------------------------------------------------------------------
    integer :: pos, facePos
    integer :: iAnsZ
    integer :: iVar
    integer :: mpd1, mpd1_square
    integer :: iElem
    integer :: iVEF, ipv
    integer :: nIndeps
    ! --------------------------------------------------------------------------

    mpd1 = maxpolydegree+1
    mpd1_square = mpd1**2
    nIndeps = nScalars*nElems*mpd1_square

    ! Split off first ansatz function, to avoid initialization with 0.
    iAnsZ = 1

    !$NEC ivdep
    do iVEF=1,nIndeps
      iVar = (iVEF-1) / (nElems*mpd1_square) + 1
      ipv = iVEF - (iVar - 1) * (nElems*mpd1_square)
      facepos = (ipv-1) / nElems + 1
      iElem = mod(ipv-1, nElems) + 1
      ! get position of the current ansatz function
      pos = facepos + (iAnsZ-1)*mpd1_square

      faceState(iElem,facePos,iVar,1) = volState(iElem, pos, iVar)
      faceState(iElem,facePos,iVar,2) = volState(iElem, pos, iVar)
    end do

    ! Legendre polynomial on the right is just the sum of all modes,
    ! but on the left we alternatingly need to add and subtract the modes.
    ! Thus, we split the loop to allow the correct operation to be used for
    ! each mode.
    do iAnsZ=3,maxPolyDegree+1,2

      !$NEC ivdep
      do iVEF=1,nIndeps
        iVar = (iVEF-1) / (nElems*mpd1_square) + 1
        ipv = iVEF - (iVar - 1) * (nElems*mpd1_square)
        facepos = (ipv-1) / nElems + 1
        iElem = mod(ipv-1, nElems) + 1
        ! get position of the current ansatz function
        pos = facepos + (iAnsZ-1)*mpd1_square

        faceState(iElem,facePos,iVar,1) = faceState(iElem,facePos,iVar,1) &
          &                             + volState(iElem, pos, iVar)
        faceState(iElem,facePos,iVar,2) = faceState(iElem,facePos,iVar,2) &
          &                             + volState(iElem, pos, iVar)
      end do

    end do
    do iAnsZ=2,maxPolyDegree+1,2

      !$NEC ivdep
      do iVEF=1,nIndeps
        iVar = (iVEF-1) / (nElems*mpd1_square) + 1
        ipv = iVEF - (iVar - 1) * (nElems*mpd1_square)
        facepos = (ipv-1) / nElems + 1
        iElem = mod(ipv-1, nElems) + 1
        ! get position of the current ansatz function
        pos = facepos + (iAnsZ-1)*mpd1_square

        faceState(iElem,facePos,iVar,1) = faceState(iElem,facePos,iVar,1) &
          &                             - volState(iElem, pos, iVar)
        faceState(iElem,facePos,iVar,2) = faceState(iElem,facePos,iVar,2) &
          &                             + volState(iElem, pos, iVar)
      end do

    end do

  end subroutine modg_voltoface_Q_z


  !> Applies the inverse of the mass matrix for a 3D scheme.
  subroutine atl_modg_invMassMatrix( mesh, kerneldata, statedata,        &
    &                                elementalTimestep, timestep, scheme )
    ! --------------------------------------------------------------------------
    !> The mesh you are working with.
    type(atl_cube_elem_type) :: mesh
    !> The data of the kernel.
    type(atl_kerneldata_type) :: kerneldata
    !> THe state if the equation
    type(atl_statedata_type) :: statedata
    !> The elemental timestepping routine, because of performance, this
    !! is constant.
    procedure(atl_elemental_timestep_vec), pointer, intent(in) &
      & :: elementalTimestep
    !> The timestepping data.
    type(atl_timestep_type) :: timestep
    !> Parameters of the modal dg scheme
    type(atl_modg_scheme_type), intent(in) :: scheme
    ! --------------------------------------------------------------------------
    integer :: nElems

    nElems = mesh%descriptor%elem%nElems(eT_fluid)

    select case(scheme%basisType)
      case(Q_space)
        call modg_invMassMatrix_Q( mesh, kerneldata, &
          &                        scheme, nElems )
      case(P_space)
        call modg_invMassMatrix_P( mesh, kerneldata, scheme, nElems )
    end select

    ! Since this is the last part of the MOdal Discontinuous Galerkin (MODG)
    ! algorithm we can make the final timestep, now.
    call elementalTimestep( timestep, statedata%state, kerneldata )

  end subroutine atl_modg_invMassMatrix

  !> Applies the inverse of the mass matrix for a 3D scheme.
  subroutine modg_invMassMatrix_Q( mesh, kerneldata,  &
    &                              scheme, nElems )
    ! --------------------------------------------------------------------------
    !> The mesh you are working with.
    type(atl_cube_elem_type) :: mesh
    !> The data of the kernel.
    type(atl_kerneldata_type) :: kerneldata
    !> Parameters of the modal dg scheme
    type(atl_modg_scheme_type), intent(in) :: scheme
    integer, intent(in) :: nElems
    ! --------------------------------------------------------------------------
    ! Loop indices for ansatz functions
    integer :: iAnsX, iAnsY, iAnsZ
    integer :: iAnsYZ, iAnsXZ, iAnsXY
    ! Inverse of the determintant of the jacobian of the mapping from reference
    ! to physical element.
    real(kind=rk) :: inv_jacobiDet
    real(kind=rk) :: tmp(vlen)
    integer :: posCoeff
    integer :: yz_offset, xz_offset
    integer :: iVar, iElem, iStrip
    integer :: i
    integer :: nVars, nEntries
    integer :: mp1, square_mp1
    ! --------------------------------------------------------------------------

    nVars = kerneldata%nVars
    mp1 = scheme%maxPolyDegree+1
    square_mp1 = mp1**2

    inv_jacobiDet = (2.0_rk/mesh%length)**3

    ! apply the 1D inverse of the mass matrix in X
    if (mp1 > 1) then
      do iAnsYZ=1,square_mp1
        yz_offset = (iAnsYZ-1)*mp1

        do iStrip=0,nElems-1,vlen
          nEntries = min(vlen, nElems - iStrip)
          do iVar=1,nVars

            do i=1,nEntries
              tmp(i) = kerneldata%state_der(iStrip + i, 1 + yz_offset,  iVar)
              kerneldata%state_der(iStrip+i, 1+yz_offset,  iVar) &
                  &  = tmp(i) * 0.5_rk
            end do
            do iAnsX=3,mp1,2
              do i=1,nEntries
                iElem = iStrip + i
                tmp(i) = kerneldata%state_der(iElem, iAnsX+yz_offset,  iVar) &
                  &      + tmp(i)
                kerneldata%state_der(iElem, iAnsX+yz_offset,  iVar) &
                  &  = tmp(i) * 0.5_rk*(2*iAnsX-1)
              end do
            end do

            do i=1,nEntries
              tmp(i) = kerneldata%state_der(iStrip + i, 2 + yz_offset,  iVar)
              kerneldata%state_der(iStrip+i, 2+yz_offset,  iVar) &
                  &  = tmp(i) * 1.5_rk
            end do
            do iAnsX=4,mp1,2
              do i=1,nEntries
                iElem = iStrip + i
                tmp(i) = kerneldata%state_der(iElem, iAnsX+yz_offset,  iVar) &
                  &      + tmp(i)
                kerneldata%state_der(iElem, iAnsX+yz_offset,  iVar) &
                  &  = tmp(i) * 0.5_rk*(2*iAnsX-1)
              end do
            end do

          end do
        end do

      end do


      ! apply the 2D inverse of the mass matrix (in Y)
      do iAnsXZ=1,square_mp1
        iAnsX = mod(iAnsXZ-1,mp1) + 1
        xz_offset = (iAnsXZ - iAnsX)*mp1 + iAnsX

        do iStrip=0,nElems-1,vlen
          nEntries = min(vlen, nElems - iStrip)
          do iVar=1,nVars

            do i=1,nEntries
              tmp(i) = kerneldata%state_der(iStrip+i, xz_offset,  iVar)
              kerneldata%state_der(iStrip+i, xz_offset,  iVar) &
                &  = tmp(i) * 0.5_rk
            end do
            do iAnsY=3,mp1,2
              posCoeff = xz_offset + (iAnsY-1)*mp1
              do i=1,nEntries
                iElem = iStrip + i
                tmp(i) = kerneldata%state_der(iElem, posCoeff,  iVar) + tmp(i)
                kerneldata%state_der(iElem, posCoeff,  iVar) &
                  &  = tmp(i) * 0.5_rk*(2*iAnsY-1)
              end do
            end do

            do i=1,nEntries
              tmp(i) = kerneldata%state_der(iStrip+i, mp1 + xz_offset,  iVar)
              kerneldata%state_der(iStrip+i, mp1 + xz_offset,  iVar) &
                &  = tmp(i) * 1.5_rk
            end do
            do iAnsY=4,mp1,2
              posCoeff = xz_offset + (iAnsY-1)*mp1
              do i=1,nEntries
                iElem = iStrip + i
                tmp(i) = kerneldata%state_der(iElem, posCoeff,  iVar) + tmp(i)
                kerneldata%state_der(iElem, posCoeff,  iVar) &
                  &  = tmp(i) * 0.5_rk*(2*iAnsY-1)
              end do
            end do

          end do
        end do

      end do


      ! apply the 3D inverse of the mass matrix (in Z)
      do iAnsXY=1,square_mp1

        do iStrip=0,nElems-1,vlen
          nEntries = min(vlen, nElems - iStrip)
          do iVar=1,nVars

            do i=1,nEntries
              tmp(i) = kerneldata%state_der(iStrip+i, iAnsXY,  iVar)
              kerneldata%state_der(iStrip+i, iAnsXY,  iVar) &
                &  = tmp(i) * 0.5_rk * inv_jacobiDet
            end do
            do iAnsZ=3,mp1,2
              posCoeff = iAnsXY + (iAnsZ-1)*square_mp1
              do i=1,nEntries
                iElem = iStrip + i
                tmp(i) = kerneldata%state_der(iElem, posCoeff,  iVar) + tmp(i)
                kerneldata%state_der(iElem, posCoeff,  iVar) &
                  &  = tmp(i) * 0.5_rk*(2*iAnsZ-1) * inv_jacobiDet
              end do
            end do

            do i=1,nEntries
              tmp(i) = kerneldata%state_der(iStrip+i, iAnsXY+square_mp1,  iVar)
              kerneldata%state_der(iStrip+i, iAnsXY+square_mp1,  iVar) &
                &  = tmp(i) * 1.5_rk * inv_jacobiDet
            end do
            do iAnsZ=4,mp1,2
              posCoeff = iAnsXY + (iAnsZ-1)*square_mp1
              do i=1,nEntries
                iElem = iStrip + i
                tmp(i) = kerneldata%state_der(iElem, posCoeff,  iVar) + tmp(i)
                kerneldata%state_der(iElem, posCoeff,  iVar) &
                  &  = tmp(i) * 0.5_rk*(2*iAnsZ-1) * inv_jacobiDet
              end do
            end do

          end do
        end do

      end do

    else

      ! Single degree of freedom.
      do iVar=1,nVars
        kerneldata%state_der(:, 1,  iVar) &
          &  = 0.125_rk * kerneldata%state_der(:, 1, iVar)
      end do

    end if

  end subroutine modg_invMassMatrix_Q


  !> Applies the inverse of the mass matrix for a 3D scheme.
  subroutine modg_invMassMatrix_P( mesh, kerneldata, scheme, nElems )
    ! --------------------------------------------------------------------------
    !> The mesh you are working with.
    type(atl_cube_elem_type) :: mesh
    !> The data of the kernel.
    type(atl_kerneldata_type) :: kerneldata
    !> Parameters of the modal dg scheme
    type(atl_modg_scheme_type), intent(in) :: scheme
    integer, intent(in) :: nElems
    ! --------------------------------------------------------------------------
    ! Loop indices for ansatz functions
    integer :: iAnsX, iAnsY, iAnsZ
    ! Positions for the given ansatz functions
    integer :: ansLow, ansPrevLow
    ! Inverse of the determintant of the jacobian of the mapping from reference
    ! to physical element.
    real(kind=rk) :: inv_jacobiDet
    ! --------------------------------------------------------------------------

    inv_jacobiDet = (2.0_rk/mesh%length)**3

    ! apply the 1D inverse of the mass matrix
    do iAnsZ = 1, scheme%maxPolyDegree+1
      do iAnsY = 1, scheme%maxPolyDegree+1 - (iAnsZ-1)
        do iAnsX = 3, scheme%maxPolyDegree+1 - (iAnsZ-1) - (iAnsY-1)
  ! integer divisions are no mistake here.
  anslow = (((iansx + iansy + iansz - 3) &
    &     * (iansx + iansy + iansz - 2) &
    &     * (iansx + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
  ! integer divisions are no mistake here.
  ansprevlow = (((iansx-2 + iansy + iansz - 3) &
    &     * (iansx-2 + iansy + iansz - 2) &
    &     * (iansx-2 + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx-2 + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
          kerneldata%state_der(:nElems, ansLow, :) &
            &        = kerneldata%state_der(:nElems, ansLow, :) &
            &        + kerneldata%state_der(:nElems, ansPrevLow, :)
        end do
      end do
    end do


    do iAnsZ = 1, scheme%maxPolyDegree+1
      do iAnsY = 1, scheme%maxPolyDegree+1 - (iAnsZ-1)
        do iAnsX = 1, scheme%maxPolyDegree+1 - (iAnsZ-1) - (iAnsY-1)
  ! integer divisions are no mistake here.
  anslow = (((iansx + iansy + iansz - 3) &
    &     * (iansx + iansy + iansz - 2) &
    &     * (iansx + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
          kerneldata%state_der(:nElems, ansLow,  :) &
            &    = kerneldata%state_der(:nElems, ansLow,  :) &
            &      * 0.5_rk*(2*iAnsX-1)
        end do
      end do
    end do


    ! apply the 2D inverse of the mass matrix
    do iAnsX = 1, scheme%maxPolyDegree+1
      do iAnsZ = 1, scheme%maxPolyDegree+1 - (iAnsX-1)
        do iAnsY = 3, scheme%maxPolyDegree+1 - (iAnsX-1) - (iAnsZ-1)
  ! integer divisions are no mistake here.
  anslow = (((iansx + iansy + iansz - 3) &
    &     * (iansx + iansy + iansz - 2) &
    &     * (iansx + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
  ! integer divisions are no mistake here.
  ansprevlow = (((iansx + iansy-2 + iansz - 3) &
    &     * (iansx + iansy-2 + iansz - 2) &
    &     * (iansx + iansy-2 + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy-2 + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-2-1)
          kerneldata%state_der(:nElems, ansLow,  :) &
            &    = kerneldata%state_der(:nElems, ansLow,  :) &
            &    + kerneldata%state_der(:nElems, ansPrevLow,  :)
        end do
      end do
    end do


    do iAnsZ = 1, scheme%maxPolyDegree+1
      do iAnsY = 1, scheme%maxPolyDegree+1 - (iAnsZ-1)
        do iAnsX = 1, scheme%maxPolyDegree+1 - (iAnsZ-1) - (iAnsY-1)
  ! integer divisions are no mistake here.
  anslow = (((iansx + iansy + iansz - 3) &
    &     * (iansx + iansy + iansz - 2) &
    &     * (iansx + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
          kerneldata%state_der(:nElems, ansLow,  :) &
            &    = kerneldata%state_der(:nElems, ansLow,  :) &
            &      * 0.5_rk*(2*iAnsY-1)
        end do
      end do
    end do


    ! apply the 3D inverse of the mass matrix
    do iAnsX = 1, scheme%maxPolyDegree+1
      do iAnsY = 1, scheme%maxPolyDegree+1 - (iAnsX-1)
        do iAnsZ = 3, scheme%maxPolyDegree+1 - (iAnsX-1) - (iAnsY-1)
  ! integer divisions are no mistake here.
  anslow = (((iansx + iansy + iansz - 3) &
    &     * (iansx + iansy + iansz - 2) &
    &     * (iansx + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
  ! integer divisions are no mistake here.
  ansprevlow = (((iansx + iansy + iansz-2 - 3) &
    &     * (iansx + iansy + iansz-2 - 2) &
    &     * (iansx + iansy + iansz-2 - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-2-1) * (iansx + iansy + iansz-2 -2) &
    &   - ((iansz-2-2) * (iansz-2-1)) / 2) &
    & + (iansy-1)
          kerneldata%state_der(:nElems, ansLow,:) &
            &        = kerneldata%state_der(:nElems, ansLow,  :) &
            &        + kerneldata%state_der(:nElems, ansPrevLow,  :)
        end do
      end do
    end do


    ! Apply also inverse of determinant of the jacobain of the mapping from
    ! reference to physical element.
    do iAnsZ = 1, scheme%maxPolyDegree+1
      do iAnsY = 1, scheme%maxPolyDegree+1 - (iAnsZ-1)
        do iAnsX = 1, scheme%maxPolyDegree+1 - (iAnsZ-1) - (iAnsY-1)
  ! integer divisions are no mistake here.
  anslow = (((iansx + iansy + iansz - 3) &
    &     * (iansx + iansy + iansz - 2) &
    &     * (iansx + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
          kerneldata%state_der(:nElems, ansLow,  :) &
            &    = kerneldata%state_der(:nElems, ansLow,  :) &
            &      * 0.5_rk*(2*iAnsZ-1) * inv_jacobiDet
        end do
      end do
    end do


  end subroutine modg_invMassMatrix_P


  !> Applies a scaled transposed inverse of the mass matrix for a 3D scheme.
  !! The result is a transformation of the polynomial basis from
  !! the ansatz- to the test-polynomials.
  !! This is useful for a local predictor
  subroutine atl_modg_scaledTransposedInvMassMatrix_Q( nTotalElems, nDofs, &
    &                                                  nScalars,           &
    &                                                  maxPolyDegree,      &
    &                                                  nElems, state       )
    ! --------------------------------------------------------------------------
    !> defines the dimensions of the state array
    integer, intent(in) :: nTotalElems, nDofs, nScalars
    !> polynomial degree of the modal dg scheme
    integer, intent(in) :: maxPolyDegree
    !> number of elements to work with
    integer, intent(in) :: nElems
    !> the state to transform
    real(kind=rk), intent(inout) :: state(nTotalElems,nDofs,nScalars)
    ! --------------------------------------------------------------------------
    ! Loop indices for ansatz functions
    integer :: iAnsX, iAnsY, iAnsZ
    integer :: ij, ik, jk
    ! Positions for the given ansatz functions
    integer :: ans, ansNext
    integer :: mpd1, mpd1_square
    ! --------------------------------------------------------------------------

    mpd1 = maxPolyDegree+1
    mpd1_square = mpd1**2

    ! apply the 1D inverse of the mass matrix
    do ij=1,mpd1_square
      do iAnsZ = maxPolyDegree-1, 1, -1
        ans = (iAnsZ-1)*mpd1_square + ij
        ansNext = ans + 2*mpd1_square
        state(:nElems, ans, :) &
          &        = state(:nElems, ans, :) &
          &        + state(:nElems, ansNext, :)
      end do
    end do

    ! apply the 2D inverse of the mass matrix
    do ik=1,mpd1_square
      iAnsX = mod(ik-1, mpd1) + 1
      ! iAnsZ = (ik-1)/mpd1 + 1
      iAnsZ = (ik-1)/mpd1 * mpd1
      do iAnsY = maxPolyDegree-1, 1, -1
        ans = (iAnsZ+(iAnsY-1))*mpd1 + iAnsX
        ansNext = ans + 2*mpd1
        state(:nElems, ans, :) &
          &    = state(:nElems, ans, :) &
          &    + state(:nElems, ansNext, :)
      end do
    end do

    ! apply the 3D inverse of the mass matrix
    do jk=1,mpd1_square
      do iAnsX = maxPolyDegree-1, 1, -1
        ans = (jk-1)*mpd1 + iAnsX
        ansNext = ans + 2
        state(:nElems, ans, :) &
          &        = state(:nElems, ans, :) &
          &        + state(:nElems, ansNext, :)
      end do
    end do


  end subroutine atl_modg_scaledTransposedInvMassMatrix_Q


  !> Applies a scaled transposed inverse of the mass matrix for a 3D scheme.
  !! The result is a transformation of the polynomial basis from the
  !! ansatz- to the test-polynomials.
  !! This is useful for a local predictor
  subroutine atl_modg_scaledTransposedInvMassMatrix_P( nTotalElems, nDofs, &
    &                                                  nScalars,           &
    &                                                  maxPolyDegree,      &
    &                                                  nElems, state       )
    ! --------------------------------------------------------------------------
    !> defines the dimensions of the state array
    integer, intent(in) :: nTotalElems, nDofs, nScalars
    !> polynomial degree of the modal dg scheme
    integer, intent(in) :: maxPolyDegree
    !> number of elements to work with
    integer, intent(in) :: nElems
    !> the state to transform
    real(kind=rk), intent(inout) :: state(nTotalElems,nDofs,nScalars)
    ! --------------------------------------------------------------------------
    ! Loop indices for ansatz functions
    integer :: iAnsX, iAnsY, iAnsZ
    ! Positions for the given ansatz functions
    integer :: ans, ansNext
    ! --------------------------------------------------------------------------


    ! apply the 1D inverse of the mass matrix
    do iAnsX = 1, maxPolyDegree+1, 1
      do iAnsY = 1, maxPolyDegree+1 - (iAnsX-1), 1
        do iAnsZ = maxPolyDegree-1 - (iAnsX-1) - (iAnsY-1), 1, -1
  ! integer divisions are no mistake here.
  ans = (((iansx + iansy + iansz - 3) &
    &     * (iansx + iansy + iansz - 2) &
    &     * (iansx + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
  ! integer divisions are no mistake here.
  ansnext = (((iansx + iansy + iansz+2 - 3) &
    &     * (iansx + iansy + iansz+2 - 2) &
    &     * (iansx + iansy + iansz+2 - 1)) &
    &   / 6 + 1)             &
    & + ((iansz+2-1) * (iansx + iansy + iansz+2 -2) &
    &   - ((iansz+2-2) * (iansz+2-1)) / 2) &
    & + (iansy-1)
          state(:nElems, ans, :) &
            &        = state(:nElems, ans, :) &
            &        + state(:nElems, ansNext, :)
        end do
      end do
    end do


    ! apply the 2D inverse of the mass matrix
    do iAnsX = 1, maxPolyDegree+1, 1
      do iAnsZ = 1, maxPolyDegree+1 - (iAnsX-1), 1
        do iAnsY = maxPolyDegree-1 - (iAnsX-1) - (iAnsZ-1), 1, -1
  ! integer divisions are no mistake here.
  ans = (((iansx + iansy + iansz - 3) &
    &     * (iansx + iansy + iansz - 2) &
    &     * (iansx + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
  ! integer divisions are no mistake here.
  ansnext = (((iansx + iansy+2 + iansz - 3) &
    &     * (iansx + iansy+2 + iansz - 2) &
    &     * (iansx + iansy+2 + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy+2 + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy+2-1)
          state(:nElems, ans, :) &
            &    = state(:nElems, ans, :) &
            &    + state(:nElems, ansNext, :)
        end do
      end do
    end do


    ! apply the 3D inverse of the mass matrix
    do iAnsZ = 1, maxPolyDegree+1, 1
      do iAnsY = 1, maxPolyDegree+1 - (iAnsZ-1), 1
        do iAnsX = maxPolyDegree-1 - (iAnsZ-1) - (iAnsY-1), 1, -1
  ! integer divisions are no mistake here.
  ans = (((iansx + iansy + iansz - 3) &
    &     * (iansx + iansy + iansz - 2) &
    &     * (iansx + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
  ! integer divisions are no mistake here.
  ansnext = (((iansx+2 + iansy + iansz - 3) &
    &     * (iansx+2 + iansy + iansz - 2) &
    &     * (iansx+2 + iansy + iansz - 1)) &
    &   / 6 + 1)             &
    & + ((iansz-1) * (iansx+2 + iansy + iansz -2) &
    &   - ((iansz-2) * (iansz-1)) / 2) &
    & + (iansy-1)
          state(:nElems, ans, :) &
            &        = state(:nElems, ans, :) &
            &        + state(:nElems, ansNext, :)
        end do
      end do
    end do


  end subroutine atl_modg_scaledTransposedInvMassMatrix_P


  !> Projection of the physical flux for the local predictor:
  !! This function expects the physical flux transformed in the basis of
  !! test-polynomials and projects it onto the ansatz-functions.
  !! Thus, the result is directly given in the basis of ansatz-polynomials
  !! (no invMassMatrix-call needed afterwards)
  !!
  !! This function implements the multiplication with the transposed stiffness
  !! matrix and some useful scaling factors
  subroutine atl_modg_scaledTransposedProject_physFlux_Q( nScalars, &
    & maxPolyDegree, length, nElems, state, iDir, dirVec            )
    ! --------------------------------------------------------------------------
    !> The number of scalar variables in your equation system.
    integer, intent(in) :: nScalars
    !> The maximal polynomial degree in each spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The length of the cubes.
    real(kind=rk), intent(in) :: length
    !> The element index to project for
    integer, intent(in) :: nElems
    !> The state to alter.
    real(kind=rk), intent(inout) :: state(:,:,:,:)
    integer, intent(in) :: iDir
    !> ordering of xyz for current direction
    integer, intent(in) :: dirVec(3)
    ! --------------------------------------------------------------------------
    real(kind=rk) :: scaledJacobiDetStiffProj, scalProd1(maxPolyDegree)
    integer :: testPos
    integer :: iTest2, iTest1, iTest3, iTestVec(3)
    integer :: iAnsVec(3)
    integer :: iVar
    integer :: ansPos(4)
    real(kind=rk) :: scalProd
    integer :: ij
    ! --------------------------------------------------------------------------

    ! Jacobi determinant for pojections of the physical fluxes in test basis
    ! onto the ansatz functions.
    ! This is the stiffness term!
    !
    ! We have cubic elements, so the determinant of the jacobian of the mapping
    ! from reference element to physical element is the same everywhere.
    ! Please notice, that the mapping of the element itself is usually
    ! (mesh%length/2.0)**3, but the derivative in the volume integral
    ! gives an additional prefactor of 2.0/mesh%length and therefore
    ! the following is the correct scaling factor for the volume integrals.
    !jacobiDetStiffProj = (0.5_rk*length)**2
    ! Here we apply some additional scaling with invJacobiDet = (2/length)**3,
    ! so the final scaling is (- because it's on the rhs)
    scaledJacobiDetStiffProj = - (2/length)


    ! we directly scale the result with the inverse of the diagonal matrix
    ! <ansatz_i,ansatz_j>
    do iTest1=1,maxPolyDegree
      scalProd1(iTest1) = (2.0_rk*iTest1-1)*scaledJacobiDetStiffProj
    end do


    ! unrolled loop

    do iTest3 = maxPolyDegree, maxPolyDegree+1
      do ij=1,2*maxPolyDegree
        iTest2 = (ij-1)/maxPolyDegree + maxPolyDegree
        iTest1 = mod(ij-1, maxPolyDegree) + 1

        ! one entry

        iTestVec = (/iTest1, iTest2, iTest3/)
  testpos = itestvec(dirvec(1))                                      &
    &      + ( ( itestvec(dirvec(2))-1)                             &
    &      + (itestvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        iAnsVec = (/iTest1+1, iTest2, iTest3/)
  anspos(1) = iansvec(dirvec(1))                                      &
    &      + ( ( iansvec(dirvec(2))-1)                             &
    &      + (iansvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        scalProd = scalProd1(iTest1)

        do iVar=1,nScalars
          state(:nElems,testPos,1,iVar) &
            & = state(:nElems,testPos,1,iVar) &
            & + state(:nElems,ansPos(1),iDir+1,iVar) * scalProd
        end do

      end do

      do ij=1,maxPolyDegree*(maxPolyDegree-1)
        iTest2 = (ij-1)/maxPolyDegree + 1
        iTest1 = mod(ij-1, maxPolyDegree) + 1

        ! two entries

        iTestVec = (/iTest1, iTest2, iTest3/)
  testpos = itestvec(dirvec(1))                                      &
    &      + ( ( itestvec(dirvec(2))-1)                             &
    &      + (itestvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)


        iAnsVec = (/iTest1+1, iTest2, iTest3/)
  anspos(1) = iansvec(dirvec(1))                                      &
    &      + ( ( iansvec(dirvec(2))-1)                             &
    &      + (iansvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        iAnsVec = (/iTest1+1, iTest2+2, iTest3/)
  anspos(2) = iansvec(dirvec(1))                                      &
    &      + ( ( iansvec(dirvec(2))-1)                             &
    &      + (iansvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        scalProd = scalProd1(iTest1)

        do iVar=1,nScalars
          state(:nElems,testPos,1,iVar) &
            & = state(:nElems,testPos,1,iVar) &
            & + ( state(:nElems,ansPos(1),iDir+1,iVar) &
            &   - state(:nElems,ansPos(2),iDir+1,iVar) ) * scalProd
        end do

      end do
    end do

    do iTest3 = 1, maxPolyDegree-1
      do ij=1,2*maxPolyDegree
        iTest2 = (ij-1)/maxPolyDegree + maxPolyDegree
        iTest1 = mod(ij-1, maxPolyDegree) + 1

        ! two entries

        iTestVec = (/iTest1, iTest2, iTest3/)
  testpos = itestvec(dirvec(1))                                      &
    &      + ( ( itestvec(dirvec(2))-1)                             &
    &      + (itestvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)


        iAnsVec = (/iTest1+1, iTest2, iTest3/)
  anspos(1) = iansvec(dirvec(1))                                      &
    &      + ( ( iansvec(dirvec(2))-1)                             &
    &      + (iansvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        iAnsVec = (/iTest1+1, iTest2, iTest3+2/)
  anspos(2) = iansvec(dirvec(1))                                      &
    &      + ( ( iansvec(dirvec(2))-1)                             &
    &      + (iansvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        scalProd = scalProd1(iTest1)

        do iVar=1,nScalars
          state(:nElems,testPos,1,iVar) &
            & = state(:nElems,testPos,1,iVar) &
            & + ( state(:nElems,ansPos(1),iDir+1,iVar) &
            &   - state(:nElems,ansPos(2),iDir+1,iVar) ) * scalProd
        end do

      end do


      do ij=1,maxPolyDegree*(maxPolyDegree-1)
        iTest2 = (ij-1)/maxPolyDegree + 1
        iTest1 = mod(ij-1, maxPolyDegree) + 1

        ! four entries

        iTestVec = (/iTest1, iTest2, iTest3/)
  testpos = itestvec(dirvec(1))                                      &
    &      + ( ( itestvec(dirvec(2))-1)                             &
    &      + (itestvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)


        iAnsVec = (/iTest1+1, iTest2, iTest3/)
  anspos(1) = iansvec(dirvec(1))                                      &
    &      + ( ( iansvec(dirvec(2))-1)                             &
    &      + (iansvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        iAnsVec = (/iTest1+1, iTest2+2, iTest3/)
  anspos(2) = iansvec(dirvec(1))                                      &
    &      + ( ( iansvec(dirvec(2))-1)                             &
    &      + (iansvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        iAnsVec = (/iTest1+1, iTest2+2, iTest3+2/)
  anspos(3) = iansvec(dirvec(1))                                      &
    &      + ( ( iansvec(dirvec(2))-1)                             &
    &      + (iansvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)

        iAnsVec = (/iTest1+1, iTest2, iTest3+2/)
  anspos(4) = iansvec(dirvec(1))                                      &
    &      + ( ( iansvec(dirvec(2))-1)                             &
    &      + (iansvec(dirvec(3))-1)*(maxpolydegree+1))*(maxpolydegree+1)


        scalProd = scalProd1(iTest1)


        do iVar=1,nScalars
          state(:nElems,testPos,1,iVar) &
            & = state(:nElems,testPos,1,iVar) &
            & + ( state(:nElems,ansPos(1),iDir+1,iVar) &
            &   - state(:nElems,ansPos(2),iDir+1,iVar) &
            &   + state(:nElems,ansPos(3),iDir+1,iVar) &
            &   - state(:nElems,ansPos(4),iDir+1,iVar) ) * scalProd
        end do

      end do
    end do

  end subroutine atl_modg_scaledTransposedProject_physFlux_Q


  !> Projection of the physical flux for the local predictor:
  !! This function expects the physical flux transformed in the basis of
  !! test-polynomials and projects it onto the ansatz-functions.
  !! Thus, the result is directly given in the basis of ansatz-polynomials
  !! (no invMassMatrix-call needed afterwards)
  !!
  !! This function implements the multiplication with the transposed stiffness
  !! matrix and some useful scaling factors
  subroutine atl_modg_scaledTransposedProject_physFlux_P( nScalars, &
    & maxPolyDegree, length, nElems, state, iDir, dirVec            )
    ! --------------------------------------------------------------------------
    !> The number of scalar variables in your equation system.
    integer, intent(in) :: nScalars
    !> The maximal polynomial degree in each spatial direction.
    integer, intent(in) :: maxPolyDegree
    !> The length of the cubes.
    real(kind=rk), intent(in) :: length
    !> The element index to project for
    integer, intent(in) :: nElems
    !> The state to alter.
    real(kind=rk), intent(inout) :: state(:,:,:,:)
    integer, intent(in) :: iDir
    !> ordering of xyz for current direction
    integer, intent(in) :: dirVec(3)
    ! --------------------------------------------------------------------------
    real(kind=rk) :: scaledJacobiDetStiffProj, scalProd1(maxPolyDegree)
    integer :: testPos
    integer :: iTest2, iTest1, iTest3, iTestVec(3)
    integer :: iAnsVec(3)
    integer :: iVar
    integer :: ansPos(4)
    real(kind=rk) :: scalProd
    integer :: maxDeg2, maxDeg1
    ! --------------------------------------------------------------------------

    ! Jacobi determinant for pojections of the physical fluxes in test basis
    ! onto the ansatz functions.
    ! This is the stiffness term!
    !
    ! We have cubic elements, so the determinant of the jacobian of the mapping
    ! from reference element to physical element is the same everywhere.
    ! Please notice, that the mapping of the element itself is usually
    ! (mesh%length/2.0)**3, but the derivative in the volume integral
    ! gives an additional prefactor of 2.0/mesh%length and therefore
    ! the following is the correct scaling factor for the volume integrals.
    !jacobiDetStiffProj = (0.5_rk*length)**2
    ! Here we apply some additional scaling with invJacobiDet = (2/length)**3,
    ! so the final scaling is (- because it's on the rhs)
    scaledJacobiDetStiffProj = - (2/length)


    ! we directly scale the result with the inverse of the diagonal matrix
    ! <ansatz_i,ansatz_j>
    do iTest1=1,maxPolyDegree
      scalProd1(iTest1) = (2.0_rk*iTest1-1)*scaledJacobiDetStiffProj
    end do


    ! unrolled loop

    do iTest3 = 1, maxPolyDegree+1,1
      maxDeg2 = max(1,maxPolyDegree - (iTest3-1))

      do iTest2 = 1, maxDeg2+1, 1
        maxDeg1 = maxPolyDegree - (iTest3-1) - (iTest2-1)

        do iTest1 = max(1,maxDeg1-1), maxDeg1, 1

          ! one entry

          iTestVec = (/iTest1, iTest2, iTest3/)
  ! integer divisions are no mistake here.
  testpos = (((itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 3) &
    &     * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 2) &
    &     * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((itestvec(dirvec(3))-1) * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) -2) &
    &   - ((itestvec(dirvec(3))-2) * (itestvec(dirvec(3))-1)) / 2) &
    & + (itestvec(dirvec(2))-1)

          iAnsVec = (/iTest1+1, iTest2, iTest3/)
  ! integer divisions are no mistake here.
  anspos(1) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)
          scalProd =   scalProd1(iTest1)

          do iVar=1,nScalars
            state(:nElems,testPos,1,iVar) &
              & = state(:nElems,testPos,1,iVar) &
              & + state(:nElems,ansPos(1),iDir+1,iVar) * scalProd
          end do

        end do
      end do
    end do


    do iTest3 = maxPolyDegree, maxPolyDegree+1
      maxDeg2 = max(1,maxPolyDegree - (iTest3-1))

      do iTest2 = 1, maxDeg2-1, 1
        maxDeg1 = maxPolyDegree - (iTest3-1) - (iTest2+1)

        do iTest1 = max(1, maxDeg1-1), maxDeg1, 1

          ! two entries

          iTestVec = (/iTest1, iTest2, iTest3/)
  ! integer divisions are no mistake here.
  testpos = (((itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 3) &
    &     * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 2) &
    &     * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((itestvec(dirvec(3))-1) * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) -2) &
    &   - ((itestvec(dirvec(3))-2) * (itestvec(dirvec(3))-1)) / 2) &
    & + (itestvec(dirvec(2))-1)


          iAnsVec = (/iTest1+1, iTest2, iTest3/)
  ! integer divisions are no mistake here.
  anspos(1) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)

          iAnsVec = (/iTest1+1, iTest2+2, iTest3/)
  ! integer divisions are no mistake here.
  anspos(2) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)
          scalProd = scalProd1(iTest1)

          do iVar=1,nScalars
            state(:nElems,testPos,1,iVar) &
              & = state(:nElems,testPos,1,iVar) &
              & + ( state(:nElems,ansPos(1),iDir+1,iVar) &
              &   - state(:nElems,ansPos(2),iDir+1,iVar) ) * scalProd
          end do

        end do
      end do
    end do

    do iTest3 = 1, maxPolyDegree-1, 1
      maxDeg2 = max(1,maxPolyDegree - (iTest3-1))

      do iTest2 = max(1,maxDeg2), maxDeg2+1, 1
        maxDeg1 = maxPolyDegree - (iTest3+1) - (iTest2-1)

        do iTest1 = max(1,maxDeg1-1), maxDeg1, 1

          ! two entries

          iTestVec = (/iTest1, iTest2, iTest3/)
  ! integer divisions are no mistake here.
  testpos = (((itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 3) &
    &     * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 2) &
    &     * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((itestvec(dirvec(3))-1) * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) -2) &
    &   - ((itestvec(dirvec(3))-2) * (itestvec(dirvec(3))-1)) / 2) &
    & + (itestvec(dirvec(2))-1)


          iAnsVec = (/iTest1+1, iTest2, iTest3/)
  ! integer divisions are no mistake here.
  anspos(1) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)

          iAnsVec = (/iTest1+1, iTest2, iTest3+2/)
  ! integer divisions are no mistake here.
  anspos(2) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)
          scalProd =   scalProd1(iTest1)


          do iVar=1,nScalars
            state(:nElems,testPos,1,iVar) &
              & = state(:nElems,testPos,1,iVar) &
              & + ( state(:nElems,ansPos(1),iDir+1,iVar) &
              &   - state(:nElems,ansPos(2),iDir+1,iVar) ) * scalProd
          end do

        end do
      end do


      do iTest2 = 1, maxDeg2-1, 1
        maxDeg1 = maxPolyDegree - (iTest3-1) - (iTest2-1) - 2

        do iTest1 = max(1,maxDeg1-1), maxDeg1, 1

          ! three entries


          iTestVec = (/iTest1, iTest2, iTest3/)
  ! integer divisions are no mistake here.
  testpos = (((itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 3) &
    &     * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 2) &
    &     * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((itestvec(dirvec(3))-1) * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) -2) &
    &   - ((itestvec(dirvec(3))-2) * (itestvec(dirvec(3))-1)) / 2) &
    & + (itestvec(dirvec(2))-1)


          iAnsVec = (/iTest1+1, iTest2, iTest3/)
  ! integer divisions are no mistake here.
  anspos(1) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)

          iAnsVec = (/iTest1+1, iTest2, iTest3+2/)
  ! integer divisions are no mistake here.
  anspos(2) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)

          iAnsVec = (/iTest1+1, iTest2+2, iTest3/)
  ! integer divisions are no mistake here.
  anspos(3) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)
          scalProd = scalProd1(iTest1)



          do iVar=1,nScalars
            state(:nElems,testPos,1,iVar) &
              & = state(:nElems,testPos,1,iVar) &
              & + ( state(:nElems,ansPos(1),iDir+1,iVar) &
              &   - state(:nElems,ansPos(2),iDir+1,iVar) &
              &   - state(:nElems,ansPos(3),iDir+1,iVar) ) * scalProd
          end do

        end do
      end do


      do iTest2 = 1, maxDeg2-1, 1
        maxDeg1 = maxPolyDegree - (iTest3+1) - (iTest2+1)

        do iTest1 = 1, maxDeg1, 1

          ! four entries

          iTestVec = (/iTest1, iTest2, iTest3/)
  ! integer divisions are no mistake here.
  testpos = (((itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 3) &
    &     * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 2) &
    &     * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((itestvec(dirvec(3))-1) * (itestvec(dirvec(1)) + itestvec(dirvec(2)) + itestvec(dirvec(3)) -2) &
    &   - ((itestvec(dirvec(3))-2) * (itestvec(dirvec(3))-1)) / 2) &
    & + (itestvec(dirvec(2))-1)


          iAnsVec = (/iTest1+1, iTest2, iTest3/)
  ! integer divisions are no mistake here.
  anspos(1) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)

          iAnsVec = (/iTest1+1, iTest2, iTest3+2/)
  ! integer divisions are no mistake here.
  anspos(2) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)

          iAnsVec = (/iTest1+1, iTest2+2, iTest3/)
  ! integer divisions are no mistake here.
  anspos(3) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)

          iAnsVec = (/iTest1+1, iTest2+2, iTest3+2/)
  ! integer divisions are no mistake here.
  anspos(4) = (((iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 3) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 2) &
    &     * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) - 1)) &
    &   / 6 + 1)             &
    & + ((iansvec(dirvec(3))-1) * (iansvec(dirvec(1)) + iansvec(dirvec(2)) + iansvec(dirvec(3)) -2) &
    &   - ((iansvec(dirvec(3))-2) * (iansvec(dirvec(3))-1)) / 2) &
    & + (iansvec(dirvec(2))-1)
          scalProd = scalProd1(iTest1)



          do iVar=1,nScalars
            state(:nElems,testPos,1,iVar) &
              & = state(:nElems,testPos,1,iVar) &
              & + ( state(:nElems,ansPos(1),iDir+1,iVar) &
              &   - state(:nElems,ansPos(2),iDir+1,iVar) &
              &   - state(:nElems,ansPos(3),iDir+1,iVar) &
              &   + state(:nElems,ansPos(4),iDir+1,iVar) ) * scalProd
          end do

        end do
      end do
    end do

  end subroutine atl_modg_scaledTransposedProject_physFlux_P


  !> Subroutine to test the various routines of this module.
  subroutine atl_modg_kernel_utests(passed)
    use atl_equation_module, only: atl_equations_type
    use atl_eqn_euler_derive_module, only: atl_eqn_euler_cons2prim_elems, &
      &                                    atl_eqn_euler_prim2cons_elems
    use atl_eqn_euler_var_module, only: atl_init_euler_vars
    use atl_varsys_module, only: atl_varsys_solverdata_type
    ! -------------------------------------------------------------------- !
    logical, intent(out) :: passed
    ! -------------------------------------------------------------------- !
    type(atl_equations_type) :: eqn
    type(atl_varSys_solverData_type) :: varsys_data
    integer :: iDir
    integer :: order
    real(kind=rk), allocatable :: resmom(:,:)
    ! -------------------------------------------------------------------- !

    eqn%eq_kind = 'navier_stokes'
    eqn%isNonlinear = .true.
    eqn%nDerivatives =  1
    eqn%adaptive_timestep = .true.

    eqn%cons2prim => atl_eqn_euler_cons2prim_elems
    eqn%prim2cons => atl_eqn_euler_prim2cons_elems

    call atl_init_euler_vars(    &
      & equation   = eqn,        &
      & solverData = varSys_data )

    eqn%euler%isen_coef = 1.4_rk
    eqn%euler%r = 1.0_rk
    eqn%euler%cv = eqn%euler%r / (eqn%euler%isen_coef - 1.0_rk)
    eqn%euler%porosity = 0.0_rk
    eqn%euler%viscous_permeability = 0.0_rk
    eqn%euler%thermal_permeability = 0.0_rk

    eqn%navierstokes%mu = 1.0e-3_rk
    eqn%navierStokes%lambda = 2.0_rk * eqn%navierstokes%mu / 3.0_rk
    eqn%navierstokes%therm_cond = 0.7_rk
    eqn%navierstokes%ip_param = 0.5_rk

    order = 5
    allocate(resmom(order**3,3))
    do iDir = 1,3
      write(*,*) '==========================================================='
      call test_project_stabViscNumFlux( polydegree       = order-1,       &
        &                                oversamplefactor = 1.0_rk,        &
        &                                dir              = iDir,          &
        &                                equation         = eqn,           &
        &                                length           = 1.0_rk,        &
        &                                rotated_mom      = resmom(:,iDir) )
      write(*,*) '==========================================================='
      write(*,*) ''
    end do

    passed = ( (maxval(abs(resmom(:,2) - resmom(:,1)))      &
      &         < epsilon(1.0_rk))                          &
      &       .and. (maxval(abs(resmom(:,3) - resmom(:,1))) &
      &              < epsilon(1.0_rk))                     )

  end subroutine atl_modg_kernel_utests


  subroutine test_project_stabViscNumFlux(polydegree, oversamplefactor, dir, &
    &                                     equation, length, rotated_mom      )
    use ply_dof_module, only: q_space
    use ply_poly_project_module, only: ply_poly_project_fillbody
    use ply_dynArray_project_module, only: ply_prj_init_type
    ! -------------------------------------------------------------------- !
    integer, intent(in) :: polydegree
    real(kind=rk), intent(in) :: oversamplefactor
    integer, intent(in) :: dir
    type(atl_equations_type), intent(in) :: equation
    real(kind=rk), intent(in) :: length
    real(kind=rk), intent(out) :: rotated_mom(:)
    ! -------------------------------------------------------------------- !
    integer :: nScalars
    integer :: idof, jdof, kdof
    integer :: signfact
    integer :: rotdof
    integer :: dofstart
    real(kind=rk), allocatable :: numFlux(:,:,:,:)
    real(kind=rk), allocatable :: faceState(:,:,:,:)
    real(kind=rk), allocatable :: projection(:,:,:)
    type(ply_poly_project_type) :: poly_proj
    type(ply_prj_init_type) :: proj_init
    ! -------------------------------------------------------------------- !

    nScalars = equation%varSys%nScalars

    ! Just use l2p here to not depend on fftw for this test.
    proj_init%header%kind = 'l2p'
    proj_init%maxpolydegree = polydegree
    proj_init%basisType = q_space
    proj_init%header%l2p_header%factor = oversamplefactor
    proj_init%header%l2p_header%nodes_header%nodes_kind = 'gauss-legendre'

    call ply_poly_project_fillbody( me         = poly_proj, &
      &                             proj_init  = proj_init, &
      &                             scheme_dim = 3          )

    ! Flux for:
    ! 1 element,
    ! ndofs degrees of freedom,
    ! 2*nScalars variables,
    ! 2 sides
    allocate(numflux(1, poly_proj%body_2D%ndofs, 2*nScalars, 2))

    ! State on the face for:
    ! 1 element,
    ! ndofs degrees of freedom,
    ! 2*nScalars variables,
    ! 2 sides
    allocate(facestate(1, poly_proj%body_2D%ndofs, nScalars, 2))

    ! Constant state with a velocity in the current direction.
    facestate = 0.0_rk
    facestate(:, 1, [1,dir+1,5], :) = 1.0_rk


    signfact = 1
    do jdof=0,polydegree
      do idof=0,polydegree
        dofstart = jdof*(polydegree+1) + iDof + 1
        numflux(:,dofstart,nScalars+1:2*nScalars,:) = signfact * 1.0_rk/dofstart
        signfact = -signfact
      end do
    end do

    numflux(:,:,nScalars+1:2*nScalars,:) &
      &  = numflux(:,:,nScalars+1:2*nScalars,:) + facestate

    ! Projection onto testfunctions for:
    ! 1 element,
    ! ndofs degrees of freedom,
    ! nScalars variables
    allocate(projection(1, poly_proj%body_3D%ndofs, nScalars))

    projection = 0.0_rk

    select case(dir)
    case(1)
      call modg_project_stabViscNumFluxX_Q( &
        &    numFlux       = numFlux,       &
        &    faceState     = faceState,     &
        &    equation      = equation,      &
        &    maxpolydegree = polydegree,    &
        &    length        = length,        &
        &    nElems_fluid  = 1,             &
        &    projection    = projection,    &
        &    poly_proj     = poly_proj      )
      rotated_mom = projection(1,:,2)

    case(2)
      call modg_project_stabViscNumFluxY_Q( &
        &    numFlux       = numFlux,       &
        &    faceState     = faceState,     &
        &    equation      = equation,      &
        &    maxpolydegree = polydegree,    &
        &    length        = length,        &
        &    nElems_fluid  = 1,             &
        &    projection    = projection,    &
        &    poly_proj     = poly_proj      )
      rotdof = 0
      do iDof=0,polydegree
        do kDof=0,polydegree
          do jDof=0,polydegree
            rotdof = rotdof + 1
            dofstart = 1 + kdof + ( idof*(polydegree+1) &
              &                     + jDof ) * (polydegree+1)
            rotated_mom(rotdof) = projection(1,dofstart,3)
          end do
        end do
      end do

    case(3)
      call modg_project_stabViscNumFluxZ_Q( &
        &    numFlux       = numFlux,       &
        &    faceState     = faceState,     &
        &    equation      = equation,      &
        &    maxpolydegree = polydegree,    &
        &    length        = length,        &
        &    nElems_fluid  = 1,             &
        &    projection    = projection,    &
        &    poly_proj     = poly_proj      )
      rotdof = 0
      do jDof=0,polydegree
        do iDof=0,polydegree
          do kDof=0,polydegree
            rotdof = rotdof + 1
            dofstart = 1 + idof + ( kdof*(polydegree+1) &
              &                     + jDof ) * (polydegree+1)
            rotated_mom(rotdof) = projection(1,dofstart,4)
          end do
        end do
      end do

    end select

    do kdof=0,polydegree
      write(*,*) '----------------------------------------------------------'
      do jdof=0,polydegree
        dofstart = (kdof*(polydegree+1) + jdof)*(polydegree+1) + 1
        write(*,*) rotated_mom(dofstart:dofstart+polydegree)
      end do
    end do
    write(*,*) '----------------------------------------------------------'

  end subroutine test_project_stabViscNumFlux


end module atl_modg_kernel_module