Storage.f90 Source File


Source Code

#include "Includes.h"
module Storage
   use SMConstants
   use PhysicsStorage
   use SolutionFile
   use VariableConversion
   implicit none

   private
   public Mesh_t, Element_t, Boundary_t
   public NVARS, NGRADVARS, hasMPIranks, hasBoundaries, isOldStats
   public partitionFileName, boundaryFileName, flowEq
   public hasExtraGradients, hasMu_NS, hasUt_NS, hasWallY, NSTAT, hasMu_sgs

   integer                          :: NVARS, NGRADVARS
   logical                          :: hasMPIranks, hasBoundaries, isOldStats
   logical                          :: hasExtraGradients = .false.
   logical                          :: hasUt_NS = .false.
   logical                          :: hasMu_NS = .false.
   logical                          :: hasWallY     = .false.
   logical                          :: hasMu_sgs = .false.
   character(len=LINE_LENGTH)       :: boundaryFileName, partitionFileName, flowEq
   integer, parameter               :: NSTAT = 9

   type Element_t
!                                /* Mesh quantities */
      integer                    :: eID
      integer                    :: Nmesh(NDIM)
      integer                    :: mpi_rank = 0
      real(kind=RP), pointer     :: x(:,:,:,:)
!                                /* Solution quantities */
      integer                    :: Nsol(NDIM)
      real(kind=RP), pointer     :: Q(:,:,:,:)
      real(kind=RP), pointer     :: QDot(:,:,:,:)
      real(kind=RP), pointer     :: U_x(:,:,:,:)
      real(kind=RP), pointer     :: U_y(:,:,:,:)
      real(kind=RP), pointer     :: U_z(:,:,:,:)
      real(kind=RP), pointer     :: Q_x(:,:,:,:)
      real(kind=RP), pointer     :: Q_y(:,:,:,:)
      real(kind=RP), pointer     :: Q_z(:,:,:,:)
      real(kind=RP), pointer     :: mu_NS(:,:,:,:)
      real(kind=RP), pointer     :: ut_NS(:,:,:,:)
      real(kind=RP), pointer     :: wallY(:,:,:,:)
      real(kind=RP), pointer     :: mu_sgs(:,:,:,:)
      real(kind=RP), pointer     :: stats(:,:,:,:)
      real(kind=RP)              :: sensor
!                                /* Output quantities */
      integer                    :: Nout(NDIM)
      real(kind=RP), pointer     :: xOut(:,:,:,:)
      real(kind=RP), pointer     :: Qout(:,:,:,:)
      real(kind=RP), pointer     :: QDot_out(:,:,:,:)
      real(kind=RP), pointer     :: U_xout(:,:,:,:)
      real(kind=RP), pointer     :: U_yout(:,:,:,:)
      real(kind=RP), pointer     :: U_zout(:,:,:,:)
      real(kind=RP), pointer     :: mu_NSout(:,:,:,:)
      real(kind=RP), pointer     :: ut_NSout(:,:,:,:)
      real(kind=RP), pointer     :: wallYout(:,:,:,:)
      real(kind=RP), pointer     :: mu_sgsout(:,:,:,:)
      real(kind=RP), pointer     :: statsout(:,:,:,:)

      real(kind=RP), allocatable :: outputVars(:,:,:,:)
   end type Element_t

   type Boundary_t
      character(len=LINE_LENGTH) :: Name
      integer                    :: no_of_faces
      integer, allocatable       :: elements(:)
      integer, allocatable       :: elementSides(:)
	  logical, allocatable       :: cornerDomain(:)
	  logical, allocatable       :: edgeDomain(:)	
   end type Boundary_t

   type Mesh_t
      integer  :: no_of_elements
      integer  :: nodeType
	  real (kind=RP) 	:: time
      type(Element_t),   allocatable    :: elements(:)
      type(Boundary_t),  allocatable    :: boundaries(:)
      character(len=LINE_LENGTH) :: meshName
      character(len=LINE_LENGTH) :: solutionName
      real(kind=RP)              :: refs(NO_OF_SAVED_REFS)
      logical                    :: hasGradients
      logical                    :: hasSensor
      logical                    :: isSurface
      logical                    :: hasTimeDeriv
      logical                    :: isStatistics
      logical                    :: is2D
      contains
         procedure   :: ReadMesh     => Mesh_ReadMesh
         procedure   :: ReadSolution => Mesh_ReadSolution
   end type Mesh_t

   contains
      subroutine Mesh_ReadMesh(self,meshName)
         use Headers
         implicit none
         class(Mesh_t)         :: self
         character(len=*), intent(in)     :: meshName
!
!        ---------------
!        Local variables
!        ---------------
!
         integer, dimension(:), allocatable     :: arrayDimensions
         integer                                :: fileType, dimensionsSize
         integer                                :: fid, eID, i, pos
         character(len=1024)                    :: msg

         self % meshName = trim(meshName)
!
!        Get mesh node type and type of file
!        -----------------------------------
         self % nodeType = getSolutionFileNodeType(meshName)
         fileType = getSolutionFileType(meshName)
!
!        Get number of elements
!        ----------------------
         self % no_of_elements = getSolutionFileNoOfElements(meshName)
!
!        Allocate elements
!        -----------------
         allocate(self % elements(self % no_of_elements))
!
!        Allocate dimension based on type of file
!        ----------------------------------------
         select case (fileType)
         case (ZONE_MESH_FILE)
             dimensionsSize = 3
         case default
             dimensionsSize = 4
         end select

         allocate(arrayDimensions(dimensionsSize))
!
!        Read coordinates
!        ----------------
         fid = putSolutionFileInReadDataMode(meshName)

         self % is2D = .false.
         do eID = 1, self % no_of_elements
            associate ( e => self % elements(eID) )
            e % eID = eID

            call getSolutionFileArrayDimensions(fid,arrayDimensions)
!
!           Allocate memory for the coordinates
!           -----------------------------------
            ! e % Nmesh(1:3) = arrayDimensions(2:4) - 1
            e % Nmesh(1:dimensionsSize-1) = arrayDimensions(2:dimensionsSize) - 1
!           Use a 0 index for the case of a surface mesh
            if (dimensionsSize .eq. 3) then
               e % Nmesh(3) = 0
            end if
            if (e % Nmesh(3) .eq. 0) then
               self % is2D = .true.
            end if
            allocate( e % x(NDIM,0:e % Nmesh(1),0:e % Nmesh(2),0:e % Nmesh(3)) )
!
!           Read data
!           ---------
            read(fid) e % x

            end associate
         end do
!
!        Close file
!        ----------
         close(fid)

!        Read boundary file (if present)
!        -------------------------------
         if (hasBoundaries) then
               call readBoundaryFile(self % boundaries)
         end if
!
!        Describe the mesh
!        -----------------
		 write(STD_OUT,'(/)')
         write(STD_OUT,'(10X,A,A)') "Loading Mesh File:"
         write(STD_OUT,'(10X,A,A)') "-----------------"
         write(msg,'(A,A,A)') 'Mesh file "',trim(meshName),'":'
         call SubSection_Header(trim(msg))

         write(STD_OUT,'(30X,A,A30,I0)') "->", "Number of elements: ", self % no_of_elements
         select case ( self % nodeType )
         case(1)
            write(STD_OUT,'(30X,A,A30,A)') "->","Discretization nodes: ","Gauss"
         case(2)
            write(STD_OUT,'(30X,A,A30,A)') "->","Discretization nodes: ","Gauss-Lobatto"
         end select

!
!        Describe the boundaries
!        -----------------
         if (hasBoundaries) then
             write(msg,'(A,A,A)') 'Boundary file "',trim(boundaryFileName),'":'
             write(STD_OUT,'(/)')
             call SubSection_Header(trim(msg))
             write(STD_OUT,'(30X,A,A30,I0)') "->","Number of Boundaries: ", size(self % boundaries)

         end if

      end subroutine Mesh_ReadMesh

      subroutine Mesh_ReadSolution(self,solutionName)
         use Headers
         implicit none
         class(Mesh_t)         :: self
         character(len=*), intent(in)     :: solutionName
!
!        ---------------
!        Local variables
!        ---------------
!
         integer                        :: no_of_elements
         integer, allocatable           :: arrayDimensions(:)
         integer                        :: fileType, dimensionsSize
         integer                        :: fid, eID, pos
         integer                        :: i,j,k
         integer                        :: iter
         real(kind=RP)                  :: time
         real(kind=RP), allocatable     :: Qdot(:,:,:,:)
         character(len=1024)  :: msg

         self % solutionName = trim(solutionName)
		 write(STD_OUT,'(10X,A,A)') "Loading Solution File:"
         write(STD_OUT,'(10X,A,A)') "---------------------"
		 write(STD_OUT,'(30X,A,A30,A)') "->","Solution File: ", trim(solutionName)													  
!
!        Get the solution file type
!        --------------------------
         self % hasTimeDeriv = .false.
         self % isStatistics = .false.
         select case ( getSolutionFileType(trim(solutionName)) )

         case (SOLUTION_FILE)
            dimensionsSize = 4
            self % hasGradients = .false.
            self % hasSensor    = .false.

         case (SOLUTION_AND_GRADIENTS_FILE)
            dimensionsSize = 4
            self % hasGradients = .true.
            self % hasSensor    = .false.

         case (STATS_FILE)
            dimensionsSize = 4
            self % hasGradients = .false.
            self % hasSensor    = .false.
            self % isStatistics = .true.

         case (ZONE_SOLUTION_FILE)
            dimensionsSize = 3
            self % hasGradients = .false.
            self % hasSensor    = .false.

         case (ZONE_SOLUTION_AND_DOT_FILE)
            dimensionsSize = 3
            self % hasGradients = .false.
            self % hasSensor    = .false.
            self % hasTimeDeriv = .true.

         case (SOLUTION_AND_SENSOR_FILE)
            dimensionsSize = 4
            self % hasGradients = .false.
            self % hasSensor    = .true.

         case (SOLUTION_AND_GRADIENTS_AND_SENSOR_FILE)
            dimensionsSize = 4
            self % hasGradients = .true.
            self % hasSensor    = .true.

         case default
            print*, "File expected to be a solution file"
            errorMessage(STD_OUT)
            error stop
         end select

         self % isSurface = (dimensionsSize .eq. 3)

         self % hasGradients = self % hasGradients .or. hasExtraGradients
!
!        Get node type
!        -------------
         if ( getSolutionFileNodeType(solutionName) .ne. self % nodeType ) then
            print*, "Solution and Mesh node type differs"
            errorMessage(STD_OUT)
            error stop
         end if
!
!        Get number of elements
!        ----------------------
         no_of_elements = getSolutionFileNoOfElements(solutionName)
         if ( self % no_of_elements .ne. no_of_elements ) then
            write(STD_OUT,'(30X,A,I0,A,I0,A)') "The number of elements in the mesh (",self % no_of_elements,&
                                           ") differs to that of the solution (",no_of_elements,")."
            errorMessage(STD_OUT)
            error stop
         end if
         allocate(arrayDimensions(dimensionsSize))
!
!        Get time and iteration
!        ----------------------
         call getSolutionFileTimeAndIteration(trim(solutionName),iter,self % time)
!
!        Read reference values
!        ---------------------
         self % refs = getSolutionFileReferenceValues(trim(solutionName))
!
!        Read coordinates
!        ----------------
         fid = putSolutionFileInReadDataMode(solutionName)

         ! call set_getVelocityGradients(GRADVARS_STATE) ! FIXME: MIGHT BE NEEDED FOR HORSES2PLT
         ! write(STD_OUT,'(15X,A)') " WARNING horses2tecplot.90 :: Velocity Gradients set to default (GRADVARS_STATE)"
      
         if ( .not. isOldStats ) then
         ! if ( .not. self % isStatistics ) then
            do eID = 1, self % no_of_elements
               associate ( e => self % elements(eID) )
               call getSolutionFileArrayDimensions(fid,arrayDimensions)

               call getNVARS(arrayDimensions(1), self % isStatistics)
!   
               ! e % Nsol(1:3) = arrayDimensions(2:4) - 1
               e % Nsol(1:dimensionsSize-1) = arrayDimensions(2:dimensionsSize) - 1
               if (dimensionsSize .eq. 3) e % Nsol(3) = 0
!   
!
!              Allocate memory for the statistics
!              ----------------------------------
               if (self % isStatistics) then
                   allocate( e % stats(1:NSTAT,0:e % Nsol(1), 0:e % Nsol(2), 0:e % Nsol(3)) )
                   read(fid) e % stats
!
               end if
!
!              Allocate memory for the coordinates
!              -----------------------------------            
               allocate( e % Q(1:NVARS,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )
!
!              Read data
!              ---------
               read(fid) e % Q

              ! Qdot goes before gradients when present
              ! for now is not saved anywhere, just to be able to read gradients
               if (self % hasTimeDeriv) then
                   allocate( Qdot(1:NVARS,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )
                   read(fid) Qdot
                   deallocate(Qdot)
               end if

               if ( self % hasGradients ) then
!
!                 Allocate memory for the gradients
!                 ---------------------------------
                  allocate( e % Q_x(1:NVARS,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) ) ! TODO: Allocate depending on physics
                  allocate( e % Q_y(1:NVARS,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )
                  allocate( e % Q_z(1:NVARS,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )

                  allocate( e % U_x(1:NGRADVARS,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )
                  allocate( e % U_y(1:NGRADVARS,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )
                  allocate( e % U_z(1:NGRADVARS,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )
!
!                 Read data
!                 ---------
                  read(fid) e % Q_x
                  read(fid) e % Q_y
                  read(fid) e % Q_z

!                 Call set_getVelocityGradients to make the pointer to the actual subroutine, is needed only for the NS
!                 Set state as is the default option TODO point to the correct one if its possible (oscar note)
!                 ---------------------------
                  call set_getVelocityGradients(GRADVARS_STATE)

                  ! Following block works for NS, CH, NSCH and iNS .... but not iNSCH: change 5 by 6 to use iNSCH (NS won't work)
                  if (NVARS .ge. 5) then
                     do k = 0,e % Nsol(3) ; do j = 0, e % Nsol(2) ; do i = 0, e % Nsol(1)
                        call getVelocityGradients(e % Q(:,i,j,k), e % Q_x(1:5,i,j,k), e % Q_y(1:5,i,j,k), e % Q_z(1:5,i,j,k), &
                                                  e % U_x(1:3,i,j,k), e % U_y(1:3,i,j,k), e % U_z(1:3,i,j,k) )
                     end do               ; end do                ; end do
                     !if (NVARS == 6) then
                     !   e % U_x(6,:,:,:) = e % Q_x(6,:,:,:)
                     !   e % U_y(6,:,:,:) = e % Q_y(6,:,:,:)
                     !   e % U_z(6,:,:,:) = e % Q_z(6,:,:,:)
                     !end if
                  else
                     e % U_x = e % Q_x(1:NGRADVARS,:,:,:)
                     e % U_y = e % Q_y(1:NGRADVARS,:,:,:)
                     e % U_z = e % Q_z(1:NGRADVARS,:,:,:)
                  end if

               end if
               if (self % hasSensor) then
                   read(fid) e % sensor
               end if

               if (hasUt_NS) then
                   allocate( e % ut_NS(1,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )
                   read(fid) e % ut_NS
               end if 

               if (hasMu_NS) then
                   allocate( e % mu_NS(1,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )
                   read(fid) e % mu_NS
               end if 

               if (hasWallY) then
                   allocate( e % wallY(1,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )
                   read(fid) e % wallY
               end if 

               if (hasMu_sgs) then
                   allocate( e % mu_sgs(1,0:e % Nsol(1),0:e % Nsol(2),0:e % Nsol(3)) )
                   read(fid) e % mu_sgs
               end if

               end associate
            end do

         else

            do eID = 1, self % no_of_elements
               associate ( e => self % elements(eID) )
               call getSolutionFileArrayDimensions(fid,arrayDimensions)
!
!              Allocate memory for the statistics
!              ----------------------------------
               e % Nsol(1:3) = arrayDimensions(2:4) - 1
               allocate( e % stats(1:NSTAT,0:e % Nsol(1), 0:e % Nsol(2), 0:e % Nsol(3)) )
!
!              Read data
!              ---------
               read(fid) e % stats
               end associate
            end do
         end if
!
!        Close file
!        ----------
         close(fid)

!        Read mpi ranks (if present)
!        ---------------------------
         if (hasMPIranks) then
               call readPartitionFile(self)
         end if
         
!
!        Describe the solution
!        ---------------------
         write(msg,'(A,A,A)') 'Solution file "',trim(solutionName),'":'
         write(STD_OUT,'(/)')
         call SubSection_Header(trim(msg))

         if ( self % isStatistics ) then
            write(STD_OUT,'(30X,A,A30)') "->","File is statistics file."

         else
            if ( self % hasGradients ) then
               write(STD_OUT,'(30X,A,A40,A)') "->","Solution file contains gradients: ", "yes"
            else
               write(STD_OUT,'(30X,A,A40,A)') "->","Solution file contains gradients: ", "no"
            end if
         end if

         write(STD_OUT,'(30X,A,A30,I0)') "->","Iteration: ", iter
         write(STD_OUT,'(30X,A,A30,ES10.3)') "->","Time: ", self % time
         write(STD_OUT,'(30X,A,A30,F7.3)') "->","Reference velocity: ", self % refs(V_REF)
         write(STD_OUT,'(30X,A,A30,F7.3)') "->","Reference density: ", self % refs(RHO_REF)
         write(STD_OUT,'(30X,A,A30,F7.3)') "->","Reference Temperature: ", self % refs(T_REF)
         write(STD_OUT,'(30X,A,A30,F7.3)') "->","Reference Mach number: ", self % refs(MACH_REF)
      end subroutine Mesh_ReadSolution
      
      
      subroutine readPartitionFile(self)
         implicit none

         !-arguments-----------------------------------------------
         class(Mesh_t)   , intent(inout)  :: self

         !-local-variables-----------------------------------------
         integer                    :: pos, nelem, eID, fID
         !---------------------------------------------------------

         open(newunit = fID, file=trim(partitionFileName),action='read')

            read(fID,*) nelem

            do eID = 1, nelem
               read(fID,*) self % elements(eID) % mpi_rank
            end do

         close(fID)
      end subroutine readPartitionFile
      
      subroutine readBoundaryFile(boundaries)
         implicit none
         !-arguments-----------------------------------------------
         type(Boundary_t), allocatable, intent(inout)  :: boundaries(:)

         !-local-variables-----------------------------------------
         integer                    :: pos, fd, no_of_boundaries,bID
         !---------------------------------------------------------

         open(newunit = fd, file=trim(boundaryFileName),action='read')

            read(fd,*) no_of_boundaries
            allocate ( boundaries(no_of_boundaries) )

            do bID = 1, no_of_boundaries

               read(fd,*) boundaries(bID) % Name
               read(fd,*) boundaries(bID) % no_of_faces

               allocate ( boundaries(bID) % elements    (boundaries(bID) % no_of_faces) )
               allocate ( boundaries(bID) % elementSides(boundaries(bID) % no_of_faces) )
			   allocate ( boundaries(bID) % cornerDomain(boundaries(bID) % no_of_faces) )
			   allocate ( boundaries(bID) % edgeDomain  (boundaries(bID) % no_of_faces) )

               read(fd,*) boundaries(bID) % elements
               read(fd,*) boundaries(bID) % elementSides
            end do

         close(fd)

      end subroutine readBoundaryFile

      Subroutine getNVARS(originalDim, useFlowEq)
!     *****************************************************
!     get NVARS from the hsol array size
!     in case that the array size is not NCONS in horses3d
!     such as the stats, use the definitions from physics.
!     *****************************************************
          implicit none
          integer, intent(in)           :: originalDim
          logical, intent(in)           :: useFlowEq

      NVARS = originalDim

      ! use simple hardcoded values taken from physics.
      ! todo: link from horses3d code
      if (useFlowEq) then
          select case (trim(flowEq))
              case ("ns")
                  NVARS = 5
              case ("nssa")
                  NVARS = 6
              case ("ins")
                  NVARS = 5
              case ("ch")
                  NVARS = 1
              case ("mu")
                  NVARS = 5
              case ("slr")
                  NVARS = 1
              case default
                  NVARS = 5
          end select
      end if

      NGRADVARS = NVARS
      select case(NVARS)
      case(1)      ; NGRADVARS = 1
      case(6)      ; NGRADVARS = 3
      case default ; NGRADVARS = 3
      end select
          
      End Subroutine getNVARS
end module Storage