diagnostics_group_m.f90 Source File


Contents


Source Code

module diagnostics_group_m
    !! Base module for diagnostic groups
    use netcdf
    use precision_grillix_m, only : GP
    use comm_handler_m, only : comm_handler_t
    use screen_io_m, only :  get_stdout
    use parallelisation_setup_m, only : is_rank_info_writer
    use error_handling_grillix_m, only: handle_error_netcdf, handle_error, error_info_t
    use status_codes_grillix_m, only : GRILLIX_ERR_OTHER

    use diagnostic_variable_m, only : diagnostic_variable_t

    implicit none
    
    type, public :: diagnostics_group_t
        !! Base class containing diagnostics
        character(len=:), allocatable, public :: groupname
        !! Name of diagnostics group
        character(len=:), allocatable, public :: dirpath
        !! Path to diagnostics directory
        type(diagnostic_variable_t), public :: tau
        !! Time
        type(diagnostic_variable_t), dimension(:), allocatable, public :: diags
        !! Container of non-generic diagnostic variables
        integer, public :: n_diags
        !! Number of non-generic diagnostic variables
        logical, public :: file_exists
        !! Internal flag to track snapshot file status
        integer, private :: diag_index
        !! Internal count to track next diagnostic array to initiate
    contains
        procedure, public :: init_diagnostics_group
        procedure, public :: allocate_diags
        procedure, public :: write_diagnostics
        procedure, public :: init_next_diagnostic
        final :: destructor
    end type

contains

    subroutine init_diagnostics_group(self, comm_handler, groupname)
        !! Initialize diagnostic
        class(diagnostics_group_t), intent(inout) :: self
        !! Instance of class
        type(comm_handler_t), intent(in) :: comm_handler
        !! MPI communication handler
        character(len=*), intent(in) :: groupname
        !! Directory suffix to write snapsfiles into

        integer :: cmd_exit, cmd_err, ierr

        self%dirpath = 'diagnostics_'//groupname
        self%groupname = groupname
        self%file_exists = .false.
        
        ! Create diagnostics snapsdir
        if (is_rank_info_writer) then
            call EXECUTE_COMMAND_LINE('mkdir '//self%dirpath, exitstat=cmd_exit, &
                                      cmdstat=cmd_err)
            if (cmd_err /= 0) then
                write(get_stdout(),*)'error(init_diagnostics_group): cmd_err = ', &
                                     cmd_err
            endif
        endif
        call MPI_BARRIER(comm_handler%get_comm_cart(), ierr)

    end subroutine

    subroutine allocate_diags(self, n_diags)
        !! Initialize diagnostic
        class(diagnostics_group_t), intent(inout) :: self
        !! Instance of class
        integer, intent(in) :: n_diags
        !! Directory to write snapsfiles into

        self%n_diags = n_diags
        allocate(self%diags(self%n_diags))
        ! Prepare counter for individual diagnostic init calls
        self%diag_index = 0

    end subroutine

    subroutine init_next_diagnostic(self, ndim, dimname, varname, ind_out)
        !! Initialize single diagnostic and provide pointer to its vals field
        class(diagnostics_group_t), intent(inout) :: self
        !! Instance of class
        integer, intent(in) :: ndim
        !! Number of elements in diagnostic
        character(len=*), intent(in) :: dimname
        !! Name of dimension of diagnostic
        character(len=*), intent(in) :: varname
        !! Name of diagnostic
        integer, intent(out) :: ind_out
        !! Index of initiated diagnostic in diags array

        ! Advance internal diags index
        self%diag_index = self%diag_index + 1
        if (self%diag_index > self%n_diags) then
            call handle_error('Diagnostic count exceeded', &
                              GRILLIX_ERR_OTHER, __LINE__, __FILE__, &
                              error_info_t('n_diags: ', [self%n_diags]))
        endif
        
        call self%diags(self%diag_index)%init(ndim, dimname, varname)

        ind_out = self%diag_index
        
    end subroutine

    subroutine write_diagnostics(self, tau, isnaps, idiag, start_new_file)
        !! Write all diagnostics
        class(diagnostics_group_t), intent(inout) :: self
        !! Instance of class
        real(GP), intent(in) :: tau
        !! Time
        integer, intent(in) :: isnaps
        !! Snapshot file index
        integer, intent(in) :: idiag
        !! Diagnostic step index
        logical, intent(in) :: start_new_file
        !! If true, a new file will be created

        character(len=5) :: isnaps_c
        character(len=:), allocatable :: diagsfile
        integer :: nf90_id, nf90_stat, i

        write(isnaps_c, '(I5.5)')isnaps
        diagsfile = self%dirpath//'/diags_'//self%groupname//'_t'//isnaps_c//'.nc'

        ! Write full diagnostics group
        if (is_rank_info_writer) then
            write(get_stdout(),*)'Writing ', self%groupname,' diagnostics:'
            write(get_stdout(),147)tau, isnaps, idiag
147             FORMAT(1X,'  tau           = ', ES14.6E3 /, &
                       1X,'  isnaps, idiag = ', I8, I8      )
            write(get_stdout(),*)'---------------------------------------------'

            if (.not. self%file_exists) then
                nf90_stat = nf90_create(diagsfile, &
                                        NF90_NETCDF4 + NF90_CLOBBER, &
                                        nf90_id)
                call handle_error_netcdf(nf90_stat, __LINE__, __FILE__)
                self%file_exists = .true.
            else
                nf90_stat = nf90_open(diagsfile, &
                                      NF90_NETCDF4 + NF90_WRITE, &
                                      nf90_id)
                call handle_error_netcdf(nf90_stat, __LINE__, __FILE__)
                nf90_stat = nf90_redef(nf90_id)
                call handle_error_netcdf(nf90_stat, __LINE__, __FILE__)
            endif

            ! Writing loop over diagnostics
            do i = 1, self%n_diags
                call self%diags(i)%write_netcdf(nf90_id, idiag)
            enddo 
            
            nf90_stat = nf90_close(nf90_id)
            call handle_error_netcdf(nf90_stat, __LINE__, __FILE__)

            ! Reset snapshot flag
            if ( start_new_file ) then
                self%file_exists = .false.
            endif

        endif
    end subroutine

    subroutine destructor(self)
        type(diagnostics_group_t), intent(inout) :: self
        !! Instance of the type

        self%n_diags = -1
        self%groupname = 'NULL'
        self%dirpath = 'NULL'
        if (allocated(self%diags)) deallocate(self%diags)
        self%file_exists = .false.
        self%diag_index = -1
    end subroutine
    
end module