Solution2Plt.f90 Source File


Source Code

module Solution2PltModule
   use SMConstants
   use SolutionFile
   use InterpolationMatrices
   use FileReadingUtilities      , only: getFileName
   use getTask
   implicit none

   private
   public   Solution2Plt, WriteBoundaryToTecplot

#define PRECISION_FORMAT "(E18.10)"

   contains
      subroutine Solution2Plt(meshName, solutionName, fixedOrder, basis, Nout, mode)
         use getTask
         use Headers
         implicit none  
         character(len=*), intent(in)     :: meshName
         character(len=*), intent(in)     :: solutionName
         integer,          intent(in)     :: basis
         logical,          intent(in)     :: fixedOrder
         integer,          intent(in)     :: Nout(3)
         integer,          intent(in)     :: mode

         write(STD_OUT,'(/)')
         call SubSection_Header("Job description")
         
         select case (mode)
         case(MODE_FINITEELM)
            write(STD_OUT,'(30X,A3,A)') "->", " Output mode: Tecplot FE"
         case(MODE_MULTIZONE)
            write(STD_OUT,'(30X,A3,A)') "->", " Output mode: Tecplot Multi-Zone"
         end select
         
         select case ( basis )

         case(EXPORT_GAUSS)

            if ( fixedOrder ) then
               write(STD_OUT,'(30X,A3,A)') "->", " Export to Gauss points with fixed order"
               write(STD_OUT,'(30X,A,A30,I0,A,I0,A,I0,A)') "->" , "Output order: [",&
                                                Nout(1),",",Nout(2),",",Nout(3),"]."
               call Solution2Plt_GaussPoints_FixedOrder(meshName, solutionName, Nout, mode)
   
            else
               write(STD_OUT,'(30X,A3,A)') "->", " Export to Gauss points"
               call Solution2Plt_GaussPoints(meshName, solutionName, mode)

            end if

         case(EXPORT_HOMOGENEOUS)
            
            write(STD_OUT,'(30X,A3,A)') "->", " Export to homogeneous points"
            write(STD_OUT,'(30X,A,A30,I0,A,I0,A,I0,A)') "->" , "Output order: [",&
                                        Nout(1),",",Nout(2),",",Nout(3),"]."
            call Solution2Plt_Homogeneous(meshName, solutionName, Nout, mode)

         end select

      end subroutine Solution2Plt
!
!//////////////////////////////////////////////////////////////////////////////////////////
!
!     Gauss Points procedures
!     -----------------------
!
!//////////////////////////////////////////////////////////////////////////////////////////
!
      subroutine Solution2Plt_GaussPoints(meshName, solutionName, mode)
         use Storage
         use NodalStorageClass
         use SharedSpectralBasis
         use OutputVariables
         implicit none  
         character(len=*), intent(in)     :: meshName
         character(len=*), intent(in)     :: solutionName
         integer,          intent(in)     :: mode
!
!        ---------------
!        Local variables
!        ---------------
!
         type(Mesh_t)                    :: mesh
         character(len=LINE_LENGTH)      :: meshPltName
         character(len=LINE_LENGTH)      :: solutionFile
         character(len=1024)             :: title
         integer                         :: no_of_elements, eID
         integer                         :: fid, bID
         integer                         :: Nmesh(4), Nsol(4)
!
!        Read the mesh and solution data
!        -------------------------------
         call mesh % ReadMesh(meshName)
         call mesh % ReadSolution(SolutionName)
         no_of_elements = mesh % no_of_elements
!
!        Transform zones to the output variables
!        ---------------------------------------
         do eID = 1, no_of_elements
            associate ( e => mesh % elements(eID) )
            e % Nout = e % Nsol
!
!           Construct spectral basis
!           ------------------------
            call addNewSpectralBasis(spA, e % Nmesh, mesh % nodeType)
            call addNewSpectralBasis(spA, e % Nsol , mesh % nodeType)
!
!           Project mesh and solution
!           -------------------------
            call ProjectStorageGaussPoints(e, spA, e % Nmesh, Nsol, mesh % hasGradients, mesh % isStatistics)

            end associate
         end do
!
!        Write the solution file name
!        ----------------------------
         solutionFile = trim(getFileName(solutionName)) // ".tec"
!
!        Create the file
!        ---------------
         open(newunit = fid, file = trim(solutionFile), action = "write", status = "unknown")
!
!        Add the title
!        -------------
         write(title,'(A,A,A,A,A)') '"Generated from ',trim(meshName),' and ',trim(solutionName),'"'
         write(fid,'(A,A)') "TITLE = ", trim(title)
!
!        Add the variables
!        -----------------
         call getOutputVariables()

         write(fid,'(A,A)') 'VARIABLES = "x","y","z"', trim(getOutputVariablesLabel())
!
!        Write elements
!        --------------
         if ( mode == MODE_FINITEELM) then
            call WriteSingleFluidZoneToTecplot(fid,mesh)
         else
            do eID = 1, no_of_elements
               associate ( e => mesh % elements(eID) )
!
!              Write the tecplot file
!              ----------------------
               call WriteElementToTecplot(fid, e, mesh % refs, &
                                          mesh % hasGradients, mesh % isStatistics, mesh % hasSensor)
               end associate
            end do
         end if
!
!        Write boundaries
!        ----------------
         if (hasBoundaries) then
            if ( mode == MODE_FINITEELM) then
               do bID=1, size (mesh % boundaries)
                  call WriteSingleBoundaryZoneToTecplot(fid, mesh % boundaries(bID), mesh % elements)
               end do
            else
               do bID=1, size (mesh % boundaries)
                  call WriteBoundaryToTecplot(fid, mesh % boundaries(bID), mesh % elements)
               end do
            end if
         end if
!
!        Close the file
!        --------------
         close(fid)
      
      end subroutine Solution2Plt_GaussPoints

      subroutine ProjectStorageGaussPoints(e, spA, NM, NS, hasGradients, hasStats)
         use Storage
         use NodalStorageClass
         use ProlongMeshAndSolution
         implicit none
         type(Element_t)     :: e
         type(NodalStorage_t),  intent(in)  :: spA(0:)
         integer           ,  intent(in)  :: NM(3)
         integer           ,  intent(in)  :: NS(3)
         logical,             intent(in)  :: hasGradients
         logical,             intent(in)  :: hasStats
         
         e % Nout = e % Nsol
         if ( all(e % Nmesh .eq. e % Nout) ) then
            e % xOut(1:,0:,0:,0:) => e % x

         else
            allocate( e % xOut(1:3,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            call prolongMeshToGaussPoints(e, spA, NM, NS)

         end if

         e % Qout(1:,0:,0:,0:) => e % Q
         if ( hasGradients ) then
            e % U_xout(1:,0:,0:,0:) => e % U_x
            e % U_yout(1:,0:,0:,0:) => e % U_y
            e % U_zout(1:,0:,0:,0:) => e % U_z
         end if
         e % QDot_out(1:,0:,0:,0:) => e % QDot
         if (hasStats) e % statsout(1:,0:,0:,0:) => e % stats
         if (hasUt_NS) e % ut_NSout(1:,0:,0:,0:) => e % ut_NS
         if (hasMu_NS) e % mu_NSout(1:,0:,0:,0:) => e % mu_NS
         if (hasWallY) e % wallYout(1:,0:,0:,0:) => e % wallY
         if (hasMu_sgs) e % mu_sgsout(1:,0:,0:,0:) => e % mu_sgs

      end subroutine ProjectStorageGaussPoints
!
!//////////////////////////////////////////////////////////////////////////////////
!
!     Gauss points with fixed order procedures
!     ----------------------------------------
!
!//////////////////////////////////////////////////////////////////////////////////
!
      subroutine Solution2Plt_GaussPoints_FixedOrder(meshName, solutionName, Nout, mode)
         use Storage
         use NodalStorageClass
         use SharedSpectralBasis
         use OutputVariables
         implicit none  
         character(len=*), intent(in)     :: meshName
         character(len=*), intent(in)     :: solutionName
         integer,          intent(in)     :: Nout(3)
         integer,          intent(in)     :: mode
!
!        ---------------
!        Local variables
!        ---------------
!
         type(Mesh_t)                               :: mesh
         character(len=LINE_LENGTH)                 :: meshPltName
         character(len=LINE_LENGTH)                 :: solutionFile
         character(len=1024)                        :: title
         integer                                    :: no_of_elements, eID
         integer                                    :: fid, bID
!
!        Read the mesh and solution data
!        -------------------------------
         call mesh % ReadMesh(meshName)
         call mesh % ReadSolution(SolutionName)
!
!        Allocate the output spectral basis
!        ----------------------------------
         call spA(Nout(1)) % Construct(GAUSS, Nout(1))
         call spA(Nout(2)) % Construct(GAUSS, Nout(2))
         call spA(Nout(3)) % Construct(GAUSS, Nout(3))
!
!        Write each element zone
!        -----------------------
         do eID = 1, mesh % no_of_elements
            associate ( e => mesh % elements(eID) )
            e % Nout = Nout
!
!           Construct spectral basis
!           ------------------------
            call addNewSpectralBasis(spA, e % Nmesh, mesh % nodeType)
            call addNewSpectralBasis(spA, e % Nsol, mesh % nodeType)
!
!           Construct interpolation matrices
!           --------------------------------
            associate( spAoutXi   => spA(Nout(1)), &
                       spAoutEta  => spA(Nout(2)), &
                       spAoutZeta => spA(Nout(3)) )
            call addNewInterpolationMatrix(Tset, e % Nsol(1), spA(e % Nsol(1)), e % Nout(1), spAoutXi   % x)   ! TODO: check why it was Nsol(1)
            call addNewInterpolationMatrix(Tset, e % Nsol(2), spA(e % Nsol(2)), e % Nout(2), spAoutEta  % x)   ! TODO: check why it was Nsol(1)
            call addNewInterpolationMatrix(Tset, e % Nsol(3), spA(e % Nsol(3)), e % Nout(3), spAoutZeta % x)   ! TODO: check why it was Nsol(1)
            end associate
!
!           Perform interpolation
!           ---------------------
            call ProjectStorageGaussPoints_FixedOrder(e, spA, e % Nmesh, e % Nsol, e % Nout, &
                                                                    Tset(e % Nout(1), e % Nsol(1)) % T, &
                                                                    Tset(e % Nout(2), e % Nsol(2)) % T, &
                                                                    Tset(e % Nout(3), e % Nsol(3)) % T, &
                                                                    mesh % hasGradients, mesh % isStatistics )

            end associate
         end do
!
!        Write the solution file name
!        ----------------------------
         solutionFile = trim(getFileName(solutionName)) // ".tec"
!
!        Create the file
!        ---------------
         open(newunit = fid, file = trim(solutionFile), action = "write", status = "unknown")
!
!        Add the title
!        -------------
         write(title,'(A,A,A,A,A)') '"Generated from ',trim(meshName),' and ',trim(solutionName),'"'
         write(fid,'(A,A)') "TITLE = ", trim(title)
!
!        Add the variables
!        -----------------
         call getOutputVariables()
         write(fid,'(A,A)') 'VARIABLES = "x","y","z"', trim(getOutputVariablesLabel())
!
!        Write elements
!        --------------
         if ( mode == MODE_FINITEELM) then
            call WriteSingleFluidZoneToTecplot(fid,mesh)
         else
            do eID = 1, mesh % no_of_elements
               associate ( e => mesh % elements(eID) )

               call WriteElementToTecplot(fid, e, mesh % refs, &
                                          mesh % hasGradients, mesh % isStatistics, mesh % hasSensor)
               end associate
            end do
         end if
!
!        Write boundaries
!        ----------------
         if (hasBoundaries) then
            if ( mode == MODE_FINITEELM) then
               do bID=1, size (mesh % boundaries)
                  call WriteSingleBoundaryZoneToTecplot(fid, mesh % boundaries(bID), mesh % elements)
               end do
            else
               do bID=1, size (mesh % boundaries)
                  call WriteBoundaryToTecplot(fid, mesh % boundaries(bID), mesh % elements)
               end do
            end if
         end if
!
!        Close the file
!        --------------
         close(fid)

      end subroutine Solution2Plt_GaussPoints_FixedOrder

      subroutine ProjectStorageGaussPoints_FixedOrder(e, spA, NM, NS, Nout, Tx, Ty, Tz, hasGradients, hasStats)
         use Storage
         use NodalStorageClass
         use ProlongMeshAndSolution
         implicit none
         type(Element_t)     :: e
         type(NodalStorage_t),  intent(in)  :: spA(0:)
         integer           ,  intent(in)  :: NM(3)
         integer           ,  intent(in)  :: NS(3)
         integer           ,  intent(in)  :: Nout(3)
         real(kind=RP),       intent(in)  :: Tx(0:e % Nout(1), 0:e % Nsol(1))
         real(kind=RP),       intent(in)  :: Ty(0:e % Nout(2), 0:e % Nsol(2))
         real(kind=RP),       intent(in)  :: Tz(0:e % Nout(3), 0:e % Nsol(3))
         logical,             intent(in)  :: hasGradients
         logical,             intent(in)  :: hasStats
!
!        Project mesh
!        ------------         
         if ( all(e % Nmesh .eq. e % Nout) ) then
            e % xOut(1:,0:,0:,0:) => e % x

         else
            allocate( e % xOut(1:3,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            call prolongMeshToGaussPoints(e, spA, NM, Nout)

         end if
!
!        Project the solution
!        --------------------
         if ( all( e % Nsol .eq. e % Nout ) ) then
            e % Qout(1:,0:,0:,0:) => e % Q

            if ( hasGradients ) then
               e % U_xout(1:,0:,0:,0:) => e % U_x
               e % U_yout(1:,0:,0:,0:) => e % U_y
               e % U_zout(1:,0:,0:,0:) => e % U_z
            end if


            e % QDot_out(1:,0:,0:,0:) => e % QDot
            if (hasStats) e % statsout(1:,0:,0:,0:) => e % stats
            if (hasUt_NS) e % ut_NSout(1:,0:,0:,0:) => e % ut_NS
            if (hasMu_NS) e % mu_NSout(1:,0:,0:,0:) => e % mu_NS
            if (hasWallY) e % wallYout(1:,0:,0:,0:) => e % wallY
            if (hasMu_sgs) e % mu_sgsout(1:,0:,0:,0:) => e % mu_sgs

         else
            allocate( e % Qout(1:NVARS,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            call prolongSolutionToGaussPoints(NVARS, e % Nsol, e % Q, e % Nout, e % Qout, Tx, Ty, Tz)
   
            if ( hasGradients ) then
               allocate( e % U_xout(1:NGRADVARS,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
               allocate( e % U_yout(1:NGRADVARS,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
               allocate( e % U_zout(1:NGRADVARS,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
               call prolongSolutionToGaussPoints(NGRADVARS, e % Nsol, e % U_x, e % Nout, e % U_xout, Tx, Ty, Tz)
               call prolongSolutionToGaussPoints(NGRADVARS, e % Nsol, e % U_y, e % Nout, e % U_yout, Tx, Ty, Tz)
               call prolongSolutionToGaussPoints(NGRADVARS, e % Nsol, e % U_z, e % Nout, e % U_zout, Tx, Ty, Tz)
            end if

            if (hasStats) then
                allocate( e % statsout(1:NSTAT,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
                call prolongSolutionToGaussPoints(NSTAT, e % Nsol, e % stats, e % Nout, e % statsout, Tx, Ty, Tz)
            end if

            if (hasUt_NS) then
                allocate( e % ut_NSout(1:NSTAT,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
                call prolongSolutionToGaussPoints(1, e % Nsol, e % ut_NS, e % Nout, e % ut_NSout, Tx, Ty, Tz)
            end if

            if (hasMu_NS) then
                allocate( e % mu_NSout(1,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
                call prolongSolutionToGaussPoints(1, e % Nsol, e % mu_NS, e % Nout, e % mu_NSout, Tx, Ty, Tz)            
            end if

            if (hasWallY) then
                allocate( e % wallYout(1,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
                call prolongSolutionToGaussPoints(1, e % Nsol, e % WallY, e % Nout, e % wallYout, Tx, Ty, Tz)            
            end if

            if (hasMu_sgs) then
                allocate( e % mu_sgsout(1,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
                call prolongSolutionToGaussPoints(1, e % Nsol, e % mu_sgs, e % Nout, e % mu_sgsout, Tx, Ty, Tz)            
            end if

            allocate( e % QDot_out(1:NVARS,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            call prolongSolutionToGaussPoints(NVARS, e % Nsol, e % QDot, e % Nout, e % QDot_out, Tx, Ty, Tz)

         end if

      end subroutine ProjectStorageGaussPoints_FixedOrder
!
!////////////////////////////////////////////////////////////////////////////
!
!     Homogeneous procedures
!     ----------------------
!
!////////////////////////////////////////////////////////////////////////////
!
      subroutine Solution2Plt_Homogeneous(meshName, solutionName, Nout, mode)
         use Storage
         use NodalStorageClass
         use SharedSpectralBasis
         use OutputVariables
         implicit none  
         character(len=*), intent(in)     :: meshName
         character(len=*), intent(in)     :: solutionName
         integer,          intent(in)     :: Nout(3)
         integer,          intent(in)     :: mode
!
!        ---------------
!        Local variables
!        ---------------
!
         type(Mesh_t)                               :: mesh
         character(len=LINE_LENGTH)                 :: meshPltName
         character(len=LINE_LENGTH)                 :: solutionFile
         character(len=1024)                        :: title
         integer                                    :: no_of_elements, eID
         integer                                    :: fid, bID
         real(kind=RP)                              :: xi(0:Nout(1)), eta(0:Nout(2)), zeta(0:Nout(3))
         integer                                    :: i
!
!        Read the mesh and solution data
!        -------------------------------
         call mesh % ReadMesh(meshName)
         call mesh % ReadSolution(SolutionName)
!
!        Set homogeneous nodes
!        ---------------------
         xi   = RESHAPE( (/ (-1.0_RP + 2.0_RP*i/Nout(1),i=0,Nout(1)) /), (/ Nout(1)+1 /) )
         eta  = RESHAPE( (/ (-1.0_RP + 2.0_RP*i/Nout(2),i=0,Nout(2)) /), (/ Nout(2)+1 /) )
         zeta = RESHAPE( (/ (-1.0_RP + 2.0_RP*i/Nout(3),i=0,Nout(3)) /), (/ Nout(3)+1 /) )
!
!        Write each element zone
!        -----------------------
         do eID = 1, mesh % no_of_elements
            associate ( e => mesh % elements(eID) )
            e % Nout = Nout
!
!           Construct spectral basis for both mesh and solution
!           ---------------------------------------------------
            call addNewSpectralBasis(spA, e % Nmesh, mesh % nodeType)
            call addNewSpectralBasis(spA, e % Nsol , mesh % nodeType)
!
!           Construct interpolation matrices for the mesh
!           ---------------------------------------------
            call addNewInterpolationMatrix(Tset, e % Nmesh(1), spA(e % Nmesh(1)), e % Nout(1), xi)
            call addNewInterpolationMatrix(Tset, e % Nmesh(2), spA(e % Nmesh(2)), e % Nout(2), eta)      ! TODO: check why it was Nmesh(1) 
            call addNewInterpolationMatrix(Tset, e % Nmesh(3), spA(e % Nmesh(3)), e % Nout(3), zeta)     ! TODO: check why it was Nmesh(1)

!
!           Construct interpolation matrices for the solution
!           -------------------------------------------------
            call addNewInterpolationMatrix(Tset, e % Nsol(1), spA(e % Nsol(1)), e % Nout(1), xi)
            call addNewInterpolationMatrix(Tset, e % Nsol(2), spA(e % Nsol(2)), e % Nout(2), eta)        ! TODO: check why it was Nout(1)
            call addNewInterpolationMatrix(Tset, e % Nsol(3), spA(e % Nsol(3)), e % Nout(3), zeta)       ! TODO: check why it was Nout(1)
!
!           Perform interpolation
!           ---------------------
            call ProjectStorageHomogeneousPoints(e, Tset(e % Nout(1), e % Nmesh(1)) % T, &
                                                    Tset(e % Nout(2), e % Nmesh(2)) % T, &
                                                    Tset(e % Nout(3), e % Nmesh(3)) % T, &
                                                     Tset(e % Nout(1), e % Nsol(1)) % T, &
                                                     Tset(e % Nout(2), e % Nsol(2)) % T, &
                                                     Tset(e % Nout(3), e % Nsol(3)) % T, &
                                                     mesh % hasGradients, mesh % isStatistics )


            end associate
         end do
!
!        Write the solution file name
!        ----------------------------
         solutionFile = trim(getFileName(solutionName)) // ".tec"
!
!        Create the file
!        ---------------
         open(newunit = fid, file = trim(solutionFile), action = "write", status = "unknown")
!
!        Add the title
!        -------------
         write(title,'(A,A,A,A,A)') '"Generated from ',trim(meshName),' and ',trim(solutionName),'"'
         write(fid,'(A,A)') "TITLE = ", trim(title)
!
!        Add the variables
!        -----------------
         call getOutputVariables()
         write(fid,'(A,A)') 'VARIABLES = "x","y","z"', trim(getOutputVariablesLabel())
!
!        Write elements
!        --------------
         if ( mode == MODE_FINITEELM) then
            call WriteSingleFluidZoneToTecplot(fid,mesh)
         else
            do eID = 1, mesh % no_of_elements
               associate ( e => mesh % elements(eID) )

               call WriteElementToTecplot(fid, e, mesh % refs, &
                                          mesh % hasGradients, mesh % isStatistics, mesh % hasSensor)
               end associate
            end do
         end if
!
!        Write boundaries
!        ----------------
         if (hasBoundaries) then
            if ( mode == MODE_FINITEELM) then
               do bID=1, size (mesh % boundaries)
                  call WriteSingleBoundaryZoneToTecplot(fid, mesh % boundaries(bID), mesh % elements)
               end do
            else
               do bID=1, size (mesh % boundaries)
                  call WriteBoundaryToTecplot(fid, mesh % boundaries(bID), mesh % elements)
               end do
            end if
         end if

!
!        Close the file
!        --------------
         close(fid)

      end subroutine Solution2Plt_Homogeneous

      subroutine ProjectStorageHomogeneousPoints(e, TxMesh, TyMesh, TzMesh, TxSol, TySol, TzSol, hasGradients, hasStats)
         use Storage
         use NodalStorageClass
         implicit none
         type(Element_t)     :: e
         real(kind=RP),       intent(in)  :: TxMesh(0:e % Nout(1), 0:e % Nmesh(1))
         real(kind=RP),       intent(in)  :: TyMesh(0:e % Nout(2), 0:e % Nmesh(2))
         real(kind=RP),       intent(in)  :: TzMesh(0:e % Nout(3), 0:e % Nmesh(3))
         real(kind=RP),       intent(in)  :: TxSol(0:e % Nout(1), 0:e % Nsol(1))
         real(kind=RP),       intent(in)  :: TySol(0:e % Nout(2), 0:e % Nsol(2))
         real(kind=RP),       intent(in)  :: TzSol(0:e % Nout(3), 0:e % Nsol(3))
         logical,             intent(in)  :: hasGradients
         logical,             intent(in)  :: hasStats
!
!        ---------------
!        Local variables
!        ---------------
!
         integer     :: i, j, k, l, m, n
!
!        Project mesh
!        ------------         
         allocate( e % xOut(1:3,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
         e % xOut = 0.0_RP

         do n = 0, e % Nmesh(3) ; do m = 0, e % Nmesh(2) ; do l = 0, e % Nmesh(1)
            do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
               e % xOut(:,i,j,k) = e % xOut(:,i,j,k) + e % x(:,l,m,n) * TxMesh(i,l) * TyMesh(j,m) * TzMesh(k,n)
            end do            ; end do            ; end do
         end do            ; end do            ; end do

!
!        Project the solution
!        --------------------
         allocate( e % Qout(1:NVARS,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
         e % Qout = 0.0_RP

         do n = 0, e % Nsol(3) ; do m = 0, e % Nsol(2) ; do l = 0, e % Nsol(1)
            do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
               e % Qout(:,i,j,k) = e % Qout(:,i,j,k) + e % Q(:,l,m,n) * TxSol(i,l) * TySol(j,m) * TzSol(k,n)
            end do            ; end do            ; end do
         end do            ; end do            ; end do

         if ( hasGradients ) then
            allocate( e % U_xout(1:NGRADVARS,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)))
            e % U_xout = 0.0_RP
   
            do n = 0, e % Nsol(3) ; do m = 0, e % Nsol(2) ; do l = 0, e % Nsol(1)
               do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
                  e % U_xout(:,i,j,k) = e % U_xout(:,i,j,k) + e % U_x(:,l,m,n) * TxSol(i,l) * TySol(j,m) * TzSol(k,n)
               end do            ; end do            ; end do
            end do            ; end do            ; end do

          allocate( e % U_yout(1:NGRADVARS, 0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            e % U_yout = 0.0_RP
   
            do n = 0, e % Nsol(3) ; do m = 0, e % Nsol(2) ; do l = 0, e % Nsol(1)
               do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
                  e % U_yout(:,i,j,k) = e % U_yout(:,i,j,k) + e % U_y(:,l,m,n) * TxSol(i,l) * TySol(j,m) * TzSol(k,n)
               end do            ; end do            ; end do
            end do            ; end do            ; end do

          allocate( e % U_zout(1:NGRADVARS, 0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            e % U_zout = 0.0_RP
   
            do n = 0, e % Nsol(3) ; do m = 0, e % Nsol(2) ; do l = 0, e % Nsol(1)
               do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
                  e % U_zout(:,i,j,k) = e % U_zout(:,i,j,k) + e % U_z(:,l,m,n) * TxSol(i,l) * TySol(j,m) * TzSol(k,n)
               end do            ; end do            ; end do
            end do            ; end do            ; end do

         end if

         if (hasStats) then
            allocate( e % statsout(1:NSTAT,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            e % statsout = 0.0_RP
            do n = 0, e % Nsol(3) ; do m = 0, e % Nsol(2) ; do l = 0, e % Nsol(1)
               do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
                  e % statsout(:,i,j,k) = e % statsout(:,i,j,k) + e % stats(:,l,m,n) * TxSol(i,l) * TySol(j,m) * TzSol(k,n)
               end do            ; end do            ; end do
            end do            ; end do            ; end do
         end if
         
         if (hasUt_NS) then
            allocate( e % ut_NSout(1,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            e % ut_NSout = 0.0_RP
            do n = 0, e % Nsol(3) ; do m = 0, e % Nsol(2) ; do l = 0, e % Nsol(1)
               do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
                  e % ut_NSout(:,i,j,k) = e % ut_NSout(:,i,j,k) + e % ut_NS(:,l,m,n) * TxSol(i,l) * TySol(j,m) * TzSol(k,n)
               end do            ; end do            ; end do
            end do            ; end do            ; end do
         end if
         
         if (hasMu_NS) then
            allocate( e % mu_NSout(1,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            e % mu_NSout = 0.0_RP
            do n = 0, e % Nsol(3) ; do m = 0, e % Nsol(2) ; do l = 0, e % Nsol(1)
               do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
                  e % mu_NSout(:,i,j,k) = e % mu_NSout(:,i,j,k) + e % mu_NS(:,l,m,n) * TxSol(i,l) * TySol(j,m) * TzSol(k,n)
               end do            ; end do            ; end do
            end do            ; end do            ; end do  
         end if
         
         if (hasWallY) then
            allocate( e % wallYout(1,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            e % wallYout = 0.0_RP
            do n = 0, e % Nsol(3) ; do m = 0, e % Nsol(2) ; do l = 0, e % Nsol(1)
               do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
                  e % wallYout(:,i,j,k) = e % wallYout(:,i,j,k) + e % WallY(:,l,m,n) * TxSol(i,l) * TySol(j,m) * TzSol(k,n)
               end do            ; end do            ; end do
            end do            ; end do            ; end do  
         end if
         
         if (hasMu_sgs) then
            allocate( e % mu_sgsout(1,0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            e % mu_sgsout = 0.0_RP
            do n = 0, e % Nsol(3) ; do m = 0, e % Nsol(2) ; do l = 0, e % Nsol(1)
               do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
                  e % mu_sgsout(:,i,j,k) = e % mu_sgsout(:,i,j,k) + e % mu_sgs(:,l,m,n) * TxSol(i,l) * TySol(j,m) * TzSol(k,n)
               end do            ; end do            ; end do
            end do            ; end do            ; end do  
         end if

      end subroutine ProjectStorageHomogeneousPoints
!
!/////////////////////////////////////////////////////////////////////////////
!
!     Write solution
!     --------------
!
!/////////////////////////////////////////////////////////////////////////////
!
!     Writes a single fluid zone using the FE Tecplot format
!     -> This format is more efficiently read by paraview and tecplot.
!     ------------------------------------------------------
      subroutine WriteSingleFluidZoneToTecplot(fid,mesh)
         use Storage
         use OutputVariables
         implicit none
         integer     , intent(in)        :: fid
         type(Mesh_t), intent(inout)     :: mesh
         !---------
         integer :: numOfPoints  ! Number of plot points
         integer :: numOfFElems  ! Number of FINITE elements
         integer :: firstPoint(size(mesh % elements))
         integer :: eID          ! Spectral (not finite) element counter
         integer :: i, j, k
         integer :: N(3)
         integer :: corners(8), cornersFace(4)
         character(len=LINE_LENGTH) :: formatout
         !---------
         
!        Definitions
!        -----------
         formatout = getFormat() ! format for point data
         
         ! Count points and elements
         numOfPoints   = product(mesh % elements(1) % Nout + 1)
         if (mesh % isSurface) then
             numOfFElems   = product(mesh % elements(1) % Nout(1:2))
         else
             numOfFElems   = product(mesh % elements(1) % Nout)
         end if
         firstPoint(1) = 1
         do eID = 2, size(mesh % elements)
            associate ( e => mesh % elements(eID) )
            firstPoint(eID) = numOfPoints + 1
            numOfPoints = numOfPoints + product(e % Nout + 1)
            if (mesh % isSurface) then
                numOfFElems   = numOfFElems + product(e % Nout(1:2))
            else
                numOfFElems   = numOfFElems + product(e % Nout)
            end if
            ! numOfFElems = numOfFElems + product(e % Nout    )
            end associate
         end do
         
         if (mesh % isSurface) then
             write(fid,'(A,I0,A,I0,A)') 'ZONE T="FLUID" N=',numOfPoints,' E=',numOfFElems,' ET=QUADRILATERAL, F=FEPOINT'
         else
             write(fid,'(A,I0,A,I0,A)') 'ZONE T="FLUID" N=',numOfPoints,' E=',numOfFElems,' ET=BRICK, F=FEPOINT'
         end if
         
!        Write the points
!        ----------------
         do eID = 1, size(mesh % elements)
            associate ( e => mesh % elements(eID) )
            N = e % Nout
            allocate (e % outputVars(1:no_of_outputVariables, 0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
            call ComputeOutputVariables(no_of_outputVariables, outputVariableNames, e % Nout, e, e % outputVars, mesh % refs, &
                                        mesh % hasGradients, mesh % isStatistics, mesh % hasSensor)
            
            do k = 0, e % Nout(3) ; do j = 0, e % Nout(2) ; do i = 0, e % Nout(1)
               write(fid,trim(formatout)) e % xOut(:,i,j,k), e % outputVars(:,i,j,k)
            end do                ; end do                ; end do
            end associate
         end do
         
!        Write the elems connectivity
!        ----------------------------
         ! surface mesh case
         if (mesh % isSurface) then
             do eID = 1, size(mesh % elements)
                associate ( e => mesh % elements(eID) )

                do j = 0, e % Nout(2) - 1 ; do i = 0, e % Nout(1) - 1
                   cornersFace =  [ ij2localDOF(i,j,e%Nout(1:2)), ij2localDOF(i+1,j,e%Nout(1:2)), ij2localDOF(i+1,j+1,e%Nout(1:2)), ij2localDOF(i,j+1,e%Nout(1:2)) ] + firstPoint(eID)
                   write(fid,*) cornersFace
                end do                  ; end do

                end associate
             end do
         ! normal elements case
         else
             do eID = 1, size(mesh % elements)
                associate ( e => mesh % elements(eID) )
                
                do k = 0, e % Nout(3) - 1 ; do j = 0, e % Nout(2) - 1 ; do i = 0, e % Nout(1) - 1
                   corners =  [ ijk2localDOF(i,j,k  ,e%Nout), ijk2localDOF(i+1,j,k  ,e%Nout), ijk2localDOF(i+1,j+1,k  ,e%Nout), ijk2localDOF(i,j+1,k  ,e%Nout), &
                                ijk2localDOF(i,j,k+1,e%Nout), ijk2localDOF(i+1,j,k+1,e%Nout), ijk2localDOF(i+1,j+1,k+1,e%Nout), ijk2localDOF(i,j+1,k+1,e%Nout)  ] + firstPoint(eID)
                   write(fid,*) corners
                end do                    ; end do                    ; end do
                
                end associate
             end do
         end if
      end subroutine WriteSingleFluidZoneToTecplot
!
!////////////////////////////////////////////////////////////////////////////
!
!     ------------------------------------------------------------------
!     ijk2localDOF:
!     Returns the local DOF index for an element in zero-based numbering
!     ------------------------------------------------------------------
      function ijk2localDOF(i,j,k,Nout) result(idx)
         implicit none
         
         integer, intent(in)   :: i, j, k, Nout(3)
         integer               :: idx
         
         IF (i < 0 .OR. i > Nout(1))     error stop 'error in ijk2local, i has wrong value'
         IF (j < 0 .OR. j > Nout(2))     error stop 'error in ijk2local, j has wrong value'
         IF (k < 0 .OR. k > Nout(3))     error stop 'error in ijk2local, k has wrong value'
         
         idx = k*(Nout(1)+1)*(Nout(2)+1) + j*(Nout(1)+1) + i
      end function ijk2localDOF
!
!//////////////////////////////////////////////////////////////////////////////
!
!     Writes a single boundary zone using the FE Tecplot format
!     -> This format is more efficiently read by paraview and tecplot.
!     ------------------------------------------------------
      subroutine WriteSingleBoundaryZoneToTecplot(fd,boundary, elements)
         use Storage
         use NodalStorageClass
         use prolongMeshAndSolution
         use OutputVariables
         use SolutionFile
         implicit none
         !-arguments-------------------------------------------
         integer         , intent(in) :: fd
         type(Boundary_t), intent(in) :: boundary
         type(Element_t) , intent(in) :: elements(:)
         !-local-variables-------------------------------------
         integer :: numOfPoints  ! Number of plot points
         integer :: numOfFElems  ! Number of FINITE elements
         integer :: fID, side
         integer :: corners(4)
         integer :: i,j,k
         integer :: N(3)
         integer :: firstPoint(boundary % no_of_faces)
         integer :: Nf      (2,boundary % no_of_faces)
         character(len=LINE_LENGTH) :: formatout
         !-----------------------------------------------------
         
         formatout = getFormat()
         
         ! Count points and elements
         numOfPoints   = 0
         numOfFElems   = 0
         
         do fID = 1, boundary % no_of_faces
            associate (e => elements( boundary % elements(fID) ))
            side = boundary % elementSides(fID)
            
            select case (side)
               case(1,2) ; Nf(:,fID) = [e % Nout(1), e % Nout(3)]
               case(3,5) ; Nf(:,fID) = [e % Nout(1), e % Nout(2)]
               case(4,6) ; Nf(:,fID) = [e % Nout(2), e % Nout(3)]
            end select
            
            firstPoint(fID) = numOfPoints + 1
            numOfPoints     = numOfPoints + product(Nf(:,fID)+1)
            numOfFElems     = numOfFElems + product(Nf(:,fID)  )
            end associate
         end do

         ! don't write if boundary doesn't have elements associated, happens for periodic conditions
         if (numOfFElems .eq. 0) return
         
         write(fd,'(A,I0,A,I0,A,A,A)') "ZONE N=", numOfPoints,", E=", numOfFElems, &
                                                  ',ET=QUADRILATERAL, F=FEPOINT, T="boundary_', trim(boundary % Name), '"'
                  
!        Write the points
!        ----------------
         do fID=1, boundary % no_of_faces
            
            associate (e => elements( boundary % elements(fID) ))
            side = boundary % elementSides(fID)
            N = e % Nout
            select case (side)
            
               case(1)
                  do k = 0, e % Nout(3)    ; do i = 0, e % Nout(1)
                     write(fd,trim(formatout)) e % xOut(:,i,0,k), e % outputVars(:,i,0,k)
                  end do                ; end do
                  
               case(2)
                  do k = 0, e % Nout(3)    ; do i = 0, e % Nout(1)
                     write(fd,trim(formatout)) e % xOut(:,i,e % Nout(2),k), e % outputVars(:,i,e % Nout(2),k)
                  end do                ; end do
               
               case(3)
                  do j = 0, e % Nout(2)    ; do i = 0, e % Nout(1)
                     write(fd,trim(formatout)) e % xOut(:,i,j,0), e % outputVars(:,i,j,0)
                  end do                ; end do
                  
               case(4)
                  do k = 0, e % Nout(3)    ; do j = 0, e % Nout(2)
                     write(fd,trim(formatout)) e % xOut(:,e % Nout(1),j,k), e % outputVars(:,e % Nout(1),j,k)
                  end do                ; end do
                  
               case(5)
                  do j = 0, e % Nout(2)    ; do i = 0, e % Nout(1)
                     write(fd,trim(formatout)) e % xOut(:,i,j,e % Nout(3)), e % outputVars(:,i,j,e % Nout(3))
                  end do                ; end do
                  
               case(6)
                  do k = 0, e % Nout(3)    ; do j = 0, e % Nout(2)
                     write(fd,trim(formatout)) e % xOut(:,0,j,k), e % outputVars(:,0,j,k)
                  end do                ; end do
                  
            end select
            
            end associate
         end do
         
!        Write the elems connectivity
!        ----------------------------
         do fID = 1, boundary % no_of_faces
            
            do j = 0, Nf(2,fID) - 1 ; do i = 0, Nf(1,fID) - 1
               corners =  [ ij2localDOF(i,j,Nf(:,fID)), ij2localDOF(i+1,j,Nf(:,fID)), ij2localDOF(i+1,j+1,Nf(:,fID)), ij2localDOF(i,j+1,Nf(:,fID)) ] + firstPoint(fID)
               write(fd,*) corners
            end do                  ; end do
            
         end do
         
      end subroutine WriteSingleBoundaryZoneToTecplot
!
!////////////////////////////////////////////////////////////////////////////
!
!     --------------------------------------------------------------
!     ij2localDOF:
!     Returns the local DOF index for a face in zero-based numbering
!     --------------------------------------------------------------
      function ij2localDOF(i,j,Nout) result(idx)
         implicit none
         
         integer, intent(in)   :: i, j, Nout(2)
         integer               :: idx
         
         IF (i < 0 .OR. i > Nout(1))     error stop 'error in ijk2local, i has wrong value'
         IF (j < 0 .OR. j > Nout(2))     error stop 'error in ijk2local, j has wrong value'
         
         idx = j*(Nout(1)+1) + i
      end function ij2localDOF
!
!//////////////////////////////////////////////////////////////////////////////
!
      subroutine WriteElementToTecplot(fid,e,refs, hasGradients, hasStats, hasSensor)
         use Storage
         use NodalStorageClass
         use prolongMeshAndSolution
         use OutputVariables
         use SolutionFile
         implicit none
         integer,            intent(in)    :: fid
         type(Element_t),    intent(inout) :: e 
         real(kind=RP),      intent(in)    :: refs(NO_OF_SAVED_REFS)
         logical,            intent(in)    :: hasGradients
         logical,            intent(in)    :: hasStats
         logical,            intent(in)    :: hasSensor
!
!        ---------------
!        Local variables
!        ---------------
!
         integer                    :: i,j,k,var
         character(len=LINE_LENGTH) :: formatout
!
!        Get output variables
!        --------------------
         
         allocate (e % outputVars(1:no_of_outputVariables, 0:e % Nout(1), 0:e % Nout(2), 0:e % Nout(3)) )
         call ComputeOutputVariables(no_of_outputVariables, outputVariableNames, e % Nout, e, e % outputVars, refs, hasGradients, hasStats, hasSensor)
!
!        Write variables
!        ---------------        
         write(fid,'(A,I0,A,I0,A,I0,A)') "ZONE I=",e % Nout(1)+1,", J=",e % Nout(2)+1, &
                                            ", K=",e % Nout(3)+1,", F=POINT"

         formatout = getFormat()

         do k = 0, e % Nout(3)   ; do j = 0, e % Nout(2)    ; do i = 0, e % Nout(1)
            write(fid,trim(formatout)) e % xOut(:,i,j,k), e % outputVars(:,i,j,k)
         end do               ; end do                ; end do

      end subroutine WriteElementToTecplot
      
      subroutine WriteBoundaryToTecplot(fd,boundary, elements)
         use Storage
         use NodalStorageClass
         use prolongMeshAndSolution
         use OutputVariables
         use SolutionFile
         implicit none
         !-arguments-------------------------------------------
         integer         , intent(in) :: fd
         type(Boundary_t), intent(in) :: boundary
         type(Element_t) , intent(in) :: elements(:)
         !-local-variables-------------------------------------
         integer :: fID, side
         integer :: i,j,k
         character(len=LINE_LENGTH) :: formatout
         !-----------------------------------------------------
         
         formatout = getFormat()
         
         do fID=1, boundary % no_of_faces
            
            associate (e => elements( boundary % elements(fID) ))
            side = boundary % elementSides(fID)
            
            select case (side)
            
               case(1)
                  
                  write(fd,'(A,I0,A,I0,A,I0,A,A,I0,A)') "ZONE I=",e % Nout(1)+1,", J=",e % Nout(3)+1, &
                                                  ", K=",1,', F=POINT, T="boundary_', trim(boundary % Name), fID, '"'
                  
                  do k = 0, e % Nout(3)    ; do i = 0, e % Nout(1)
                     write(fd,trim(formatout)) e % xOut(:,i,0,k), e % outputVars(:,i,0,k)
                  end do                ; end do
                  
               case(2)
                  
                  write(fd,'(A,I0,A,I0,A,I0,A,A,I0,A)') "ZONE I=",e % Nout(1)+1,", J=",e % Nout(3)+1, &
                                                  ", K=",1,', F=POINT, T="boundary_', trim(boundary % Name), fID, '"'
                  
                  do k = 0, e % Nout(3)    ; do i = 0, e % Nout(1)
                     write(fd,trim(formatout)) e % xOut(:,i,e % Nout(2),k), e % outputVars(:,i,e % Nout(2),k)
                  end do                ; end do
               
               case(3)
                  
                  write(fd,'(A,I0,A,I0,A,I0,A,A,I0,A)') "ZONE I=",e % Nout(1)+1,", J=",e % Nout(2)+1, &
                                                  ", K=",1,', F=POINT, T="boundary_', trim(boundary % Name), fID, '"'
                  
                  do j = 0, e % Nout(2)    ; do i = 0, e % Nout(1)
                     write(fd,trim(formatout)) e % xOut(:,i,j,0), e % outputVars(:,i,j,0)
                  end do                ; end do
                  
               case(4)
                  
                  write(fd,'(A,I0,A,I0,A,I0,A,A,I0,A)') "ZONE I=",e % Nout(2)+1,", J=",e % Nout(3)+1, &
                                                  ", K=",1,', F=POINT, T="boundary_', trim(boundary % Name), fID, '"'
                  
                  do k = 0, e % Nout(3)    ; do j = 0, e % Nout(2)
                     write(fd,trim(formatout)) e % xOut(:,e % Nout(1),j,k), e % outputVars(:,e % Nout(1),j,k)
                  end do                ; end do
                  
               case(5)
                  
                  write(fd,'(A,I0,A,I0,A,I0,A,A,I0,A)') "ZONE I=",e % Nout(1)+1,", J=",e % Nout(2)+1, &
                                                  ", K=",1,', F=POINT, T="boundary_', trim(boundary % Name), fID, '"'
                  
                  do j = 0, e % Nout(2)    ; do i = 0, e % Nout(1)
                     write(fd,trim(formatout)) e % xOut(:,i,j,e % Nout(3)), e % outputVars(:,i,j,e % Nout(3))
                  end do                ; end do
                  
               case(6)
                  
                  write(fd,'(A,I0,A,I0,A,I0,A,A,I0,A)') "ZONE I=",e % Nout(2)+1,", J=",e % Nout(3)+1, &
                                                  ", K=",1,', F=POINT, T="boundary_', trim(boundary % Name), fID, '"'
                  
                  do k = 0, e % Nout(3)    ; do j = 0, e % Nout(2)
                     write(fd,trim(formatout)) e % xOut(:,0,j,k), e % outputVars(:,0,j,k)
                  end do                ; end do
                  
            end select
            
            end associate
         end do
         
      end subroutine WriteBoundaryToTecplot
      

      character(len=LINE_LENGTH) function getFormat()
         use OutputVariables
         implicit none

         getFormat = ""

         write(getFormat,'(A,I0,A,A)') "(",3+no_of_outputVariables,PRECISION_FORMAT,")"

      end function getFormat

end module Solution2PltModule