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