MODULE diagnostics
  !   Module for diagnostic parameters

  USE prec_const
  IMPLICIT NONE
  PRIVATE

  LOGICAL, PUBLIC, PROTECTED :: write_doubleprecision = .FALSE.
  LOGICAL, PUBLIC, PROTECTED :: write_gamma, write_hf  ! output particle transport and heat flux
  LOGICAL, PUBLIC, PROTECTED :: write_phi,  write_Na00
  LOGICAL, PUBLIC, PROTECTED :: write_Napj, write_Sapj
  LOGICAL, PUBLIC, PROTECTED :: write_dens, write_fvel, write_temp

  INTEGER, PUBLIC, PROTECTED :: nsave_0d, nsave_1d, nsave_2d, nsave_3d, nsave_5d ! save data every n step
  REAL,    PUBLIC, PROTECTED :: dtsave_0d, dtsave_1d, dtsave_2d, dtsave_3d, dtsave_5d ! save data every dt time unit
  ! Change diagnostic mode (full/txtonly)
  CHARACTER(len=256), PUBLIC, PROTECTED :: diag_mode = "full"
  !  HDF5 file
  CHARACTER(len=256), PUBLIC :: resfile,resfile0 = "outputs"            ! Head of main result file name
  CHARACTER(len=256), PUBLIC :: momfile,momfile0 = "moments"   ! Head of the moment spectrum file (N_a(p,j,z))
  CHARACTER(len=256), PUBLIC :: mspfile,mspfile0 = "moments_spectrum"   ! Head of the moment spectrum file (N_a(p,j,z))
  CHARACTER(len=256), PUBLIC :: fldfile,fldfile0 = "fields"             ! Head of field (phi,A)
  CHARACTER(len=256), PUBLIC :: ttrfile,ttrfile0 = "time_traces"        ! Head of time traces (gamma_x,Q_x)
  CHARACTER(len=256), PUBLIC :: ggmfile,ggmfile0 = "grid_geometry"        ! Head of time traces (gamma_x,Q_x)
  CHARACTER(len=256), PUBLIC :: prffile,prffile0 = "profiler"        ! Head of time traces (gamma_x,Q_x)
  CHARACTER(len=256), PUBLIC :: input_fname
  INTEGER, PUBLIC            :: fidres,fidmsp,fidfld,fidttr ! FID for output
  INTEGER, PUBLIC            :: fidmom,fidggm, fidprf

  PUBLIC :: diag_readinputs, diag_outputinputs, diagnose, spit_snapshot_check

CONTAINS

  SUBROUTINE diag_readinputs
    !    Read the input parameters

    USE basic, ONLY : lu_in, dt
    USE prec_const
    IMPLICIT NONE

    NAMELIST /DIAGNOSTICS/ dtsave_0d, dtsave_1d, dtsave_2d, dtsave_3d, dtsave_5d
    NAMELIST /DIAGNOSTICS/ write_doubleprecision, write_gamma, write_hf, write_phi
    NAMELIST /DIAGNOSTICS/ write_Na00, write_Napj, write_Sapj
    NAMELIST /DIAGNOSTICS/ write_dens, write_fvel, write_temp
    NAMELIST /DIAGNOSTICS/ diag_mode

    READ(lu_in,diagnostics)

    ! set nsave variables from dtsave ones (time unit to steps)
    nsave_0d = CEILING(dtsave_0d/dt)
    nsave_1d = CEILING(dtsave_1d/dt)
    nsave_2d = CEILING(dtsave_2d/dt)
    nsave_3d = CEILING(dtsave_3d/dt)
    nsave_5d = CEILING(dtsave_5d/dt)

  END SUBROUTINE diag_readinputs

  SUBROUTINE init_outfile(comm,file0,file,fid)
    USE basic,           ONLY: speak, jobnum, basic_outputinputs
    USE grid,            ONLY: grid_outputinputs
    USE geometry,        ONLY: geometry_outputinputs
    USE model,           ONLY: model_outputinputs
    USE closure,         ONLY: closure_outputinputs
    USE species,         ONLY: species_outputinputs
    USE collision,       ONLY: coll_outputinputs
    USE initial,         ONLY: initial_outputinputs
    USE time_integration,ONLY: time_integration_outputinputs
    USE parallel,        ONLY: parallel_outputinputs
    USE futils,          ONLY: creatf, creatg, creatd, attach, putfile
    IMPLICIT NONE
    !input
    INTEGER,            INTENT(IN)    :: comm
    CHARACTER(len=256), INTENT(IN)    :: file0
    CHARACTER(len=256), INTENT(OUT)   :: file
    INTEGER,            INTENT(OUT)   :: fid
    CHARACTER(len=256)                :: str
    INCLUDE 'srcinfo.h'
    ! Writing output filename
    WRITE(file,'(a,a1,i2.2,a3)') TRIM(file0)   ,'_',jobnum,'.h5'
    !                      1.1   Initial run
    ! Main output file creation
    IF (write_doubleprecision) THEN
      CALL creatf(file, fid, real_prec='d', mpicomm=comm)
    ELSE
      CALL creatf(file, fid, mpicomm=comm)
    END IF
    CALL speak(TRIM(file)//' created')
    !  basic data group
    CALL creatg(fid, "/data", "data")
    !  File group
    CALL creatg(fid, "/files", "files")
    CALL attach(fid, "/files",  "jobnum", jobnum)
    ! Add the code info and parameters to the file
    CALL creatg(fid, "/data/input", "input")
    CALL creatd(fid, 0,(/0/),"/data/input/codeinfo",'Code Information')
    CALL attach(fid, "/data/input/codeinfo",  "version",  VERSION) !defined in srcinfo.h
    CALL attach(fid, "/data/input/codeinfo",   "branch",   BRANCH) !defined in srcinfo.h
    CALL attach(fid, "/data/input/codeinfo",   "author",   AUTHOR) !defined in srcinfo.h
    CALL attach(fid, "/data/input/codeinfo", "execdate", EXECDATE) !defined in srcinfo.h
    CALL attach(fid, "/data/input/codeinfo",     "host",     HOST) !defined in srcinfo.h
    CALL            basic_outputinputs(fid)
    CALL             grid_outputinputs(fid)
    CALL         geometry_outputinputs(fid)
    CALL             diag_outputinputs(fid)
    CALL            model_outputinputs(fid)
    CALL          closure_outputinputs(fid)
    CALL          species_outputinputs(fid)
    CALL             coll_outputinputs(fid)
    CALL          initial_outputinputs(fid)
    CALL time_integration_outputinputs(fid)
    CALL         parallel_outputinputs(fid)
    !  Save STDIN (input file) of this run
    IF(jobnum .LE. 99) THEN
       WRITE(str,'(a,i2.2)') "/files/STDIN.",jobnum
    ELSE
       WRITE(str,'(a,i3.2)') "/files/STDIN.",jobnum
    END IF
    CALL putfile(fid, TRIM(str), TRIM(input_fname),ionode=0)
  END SUBROUTINE init_outfile

  SUBROUTINE diagnose(kstep)
    !   Diagnostics, writing simulation state to disk
    USE basic,           ONLY: lu_in, chrono_runt, cstep, time, display_h_min_s
    USE processing,      ONLY: pflux_x, hflux_x
    USE parallel,        ONLY: my_id
    IMPLICIT NONE
    INTEGER, INTENT(in) :: kstep
    !! Basic diagnose loop for reading input file, displaying advancement and ending
    IF ((kstep .EQ. 0)) THEN
      INQUIRE(unit=lu_in, name=input_fname)
      CLOSE(lu_in)
    ENDIF
    !! End diag
    IF (kstep .EQ. -1) THEN
       ! Display total run time
       CALL display_h_min_s(chrono_runt%ttot)
    END IF
    !! Specific diagnostic calls
    SELECT CASE(diag_mode)
    CASE('full')
      CALL diagnose_full(kstep)
    CASE('txtonly')
      CALL diagnose_txtonly(kstep)
    END SELECT
  
    ! Terminal info
    IF ((kstep .GE. 0) .AND. (MOD(cstep, nsave_0d) == 0) .AND. (my_id .EQ. 0)) THEN
      if(SIZE(pflux_x) .GT. 1) THEN
        WRITE(*,"(A,F8.2,A8,G10.3,A8,G10.3,A8,G10.3,A8,G10.3,A)")&
        '|t = ', time,'| Pxi = ',pflux_x(1),'| Qxi = ',hflux_x(1),'| Pxe = ',pflux_x(2),'| Qxe = ',hflux_x(2),'|'
      else
        WRITE(*,"(A,F8.2,A8,G10.3,A8,G10.3,A)")&
        '|t = ', time,'| Px  = ',pflux_x(1),'| Qx  = ',hflux_x(1),'|'
      endif
    ENDIF
  END SUBROUTINE diagnose
  
  SUBROUTINE diagnose_full(kstep)
    USE basic,           ONLY: speak,chrono_runt,&
                               cstep,iframe0d,iframe3d,iframe5d,crashed
    USE grid,            ONLY: &
      parray_full,jarray_full, kparray, &
      kyarray_full,kxarray_full,zarray_full, ngz, total_nz, local_nz, ieven,&
      local_Nky, total_nky, local_nkx, total_nkx
    USE geometry, ONLY: gxx, gxy, gxz, gyy, gyz, gzz, &
                        hatR, hatZ, hatB, dBdx, dBdy, dBdz, Jacobian, gradz_coeff
    USE futils,          ONLY: creatf, creatg, creatd, closef, putarr, putfile, attach, openf!, putarrnd ! Routine de merde, jamais l'utiliser
    USE array
    USE model,           ONLY: EM
    USE parallel,        ONLY: my_id, comm0, gather_z, gather_xyz_real
    USE collision,       ONLY: coll_outputinputs
    IMPLICIT NONE
    INTEGER, INTENT(in) :: kstep
    INTEGER, parameter  :: BUFSIZE = 2
    REAL(xp), DIMENSION(total_nz) :: Az_full ! full z array for metric output
    REAL(xp), DIMENSION(total_nky,total_nkx,total_nz) :: kp_full
    INTEGER :: rank = 0, ierr
    INTEGER :: dims(1) = (/0/)
    !____________________________________________________________________________
    !                   1.   Initial diagnostics
    IF ((kstep .EQ. 0)) THEN
      CALL init_outfile(comm0,   resfile0,resfile,fidres)
      ! Profiler time measurement
      CALL creatg(fidres, "/profiler", "performance analysis")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_rhs",        "cumulative rhs computation time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_poisson",    "cumulative poisson computation time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_Sapj",       "cumulative Sapj computation time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_coll",       "cumulative collision computation time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_grad",       "cumulative grad computation time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_nadiab",     "cumulative nadiab moments computation time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_ExBshear",   "cumulative ExB shear time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_adv_field",  "cumulative adv. fields computation time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_ghost",       "cumulative communication time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_clos",       "cumulative closure computation time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_checkfield", "cumulative checkfield computation time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_diag",       "cumulative diagnostic time")
      CALL creatd(fidres, 0, dims, "/profiler/Tc_step",       "cumulative total step computation time")
      CALL creatd(fidres, 0, dims, "/profiler/time",          "current simulation time")
#ifdef TEST_SVD
      CALL creatd(fidres, 0, (/0/), "/profiler/Tc_CLA", "cumulative total CLA computation time")
#endif
      ! Grid info
      CALL creatg(fidres, "/data/grid", "Grid data")
      CALL putarr(fidres, "/data/grid/coordkx",   kxarray_full,  "kx*rho_s0", ionode=0)
      CALL putarr(fidres, "/data/grid/coordky",   kyarray_full,  "ky*rho_s0", ionode=0)
      CALL putarr(fidres, "/data/grid/coordz",    zarray_full,   "z/R", ionode=0)
      CALL putarr(fidres, "/data/grid/coordp" ,   parray_full,   "p", ionode=0)
      CALL putarr(fidres, "/data/grid/coordj" ,   jarray_full,   "j", ionode=0)
      CALL gather_xyz_real(kparray(1:local_Nky,1:local_Nkx,1:local_nz,ieven),kp_full,local_nky,total_nky,total_nkx,local_nz,total_nz)
      CALL putarr(fidres, "/data/grid/coordkp" ,      kp_full,   "kp", ionode=0)
      ! Metric info
      CALL   creatg(fidres, "/data/metric", "Metric data")
      CALL gather_z(gxx((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/gxx", Az_full, "gxx", ionode =0)
      CALL gather_z(gxy((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/gxy", Az_full, "gxy", ionode =0)
      CALL gather_z(gxz((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/gxz", Az_full, "gxz", ionode =0)
      CALL gather_z(gyy((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/gyy", Az_full, "gyy", ionode =0)
      CALL gather_z(gyz((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/gyz", Az_full, "gyz", ionode =0)
      CALL gather_z(gzz((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/gzz", Az_full, "gzz", ionode =0)
      CALL gather_z(hatR((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/hatR", Az_full, "hatR", ionode =0)
      CALL gather_z(hatZ((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/hatZ", Az_full, "hatZ", ionode =0)
      CALL gather_z(hatB((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/hatB", Az_full, "hatB", ionode =0)
      CALL gather_z(dBdx((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/dBdx", Az_full, "dBdx", ionode =0)
      CALL gather_z(dBdy((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/dBdy", Az_full, "dBdy", ionode =0)
      CALL gather_z(dBdz((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/dBdz", Az_full, "dBdz", ionode =0)
      CALL gather_z(Jacobian((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/Jacobian", Az_full, "Jacobian", ionode =0)
      CALL gather_z(gradz_coeff((1+ngz/2):(local_nz+ngz/2),ieven),Az_full,local_nz,total_nz)
      CALL putarr(fidres, "/data/metric/gradz_coeff", Az_full, "gradz_coeff", ionode =0)
      !  var0d group (gyro transport)
      IF (nsave_0d .GT. 0) THEN
       CALL creatg(fidres, "/data/var0d", "0d profiles")
       CALL creatd(fidres, rank, dims,  "/data/var0d/time",     "Time t*c_s/R")
       CALL creatd(fidres, rank, dims, "/data/var0d/cstep", "iteration number")
       IF (write_gamma) THEN
        CALL creatd(fidres, rank, dims, "/data/var0d/gflux_x", "Radial gyro transport")
        CALL creatd(fidres, rank, dims, "/data/var0d/pflux_x", "Radial part transport")
       ENDIF
       IF (write_hf) THEN
        CALL creatd(fidres, rank, dims, "/data/var0d/hflux_x", "Radial part heat flux")
       ENDIF
       IF (cstep==0) THEN
         iframe0d=0
       ENDIF
       CALL attach(fidres,"/data/var0d/" , "frames", iframe0d)
      END IF
      !  var2d group (gyro transport)
      IF (nsave_0d .GT. 0) THEN
        CALL creatg(fidres, "/data/var2d", "2d profiles")
        CALL creatd(fidres, rank, dims,  "/data/var2d/time",     "Time t*c_s/R")
        CALL creatd(fidres, rank, dims, "/data/var2d/cstep", "iteration number")
#ifdef TEST_SVD
        CALL creatg(fidres, "/data/var2d/sv_ky_pj", "singular values of the moment ky/pj cut")
#endif
      ENDIF
      !  var3d group (phi,psi, fluid moments, Ni00, Napjz)
      IF (nsave_3d .GT. 0) THEN
       CALL creatg(fidres, "/data/var3d", "3d profiles")
       CALL creatd(fidres, rank, dims,  "/data/var3d/time",     "Time t*c_s/R")
       CALL creatd(fidres, rank, dims, "/data/var3d/cstep", "iteration number")
       IF (write_phi) CALL creatg(fidres, "/data/var3d/phi", "phi")
       IF (write_phi.AND.EM) CALL creatg(fidres, "/data/var3d/psi", "psi")
      IF (write_Na00) THEN
      CALL creatg(fidres, "/data/var3d/Na00", "gyrocenter density ")
      CALL creatg(fidres, "/data/var3d/Napjz", "pj(z) moment spectrum ")
      ENDIF
      IF (write_dens) THEN
      CALL creatg(fidres, "/data/var3d/dens", "density ")
      ENDIF
      IF (write_fvel) THEN
        CALL creatg(fidres, "/data/var3d/upar", "parallel fluid velocity ")
        CALL creatg(fidres, "/data/var3d/uper", "perpendicular fluid velocity ")
      ENDIF
      IF (write_temp) THEN
        CALL creatg(fidres, "/data/var3d/Tper", "perpendicular temperature ")
        CALL creatg(fidres, "/data/var3d/Tpar", "parallel temperature ")
        CALL creatg(fidres, "/data/var3d/temp", "tiotal temperature ")
      ENDIF
       IF (cstep==0) THEN
         iframe3d=0
       ENDIF
       CALL attach(fidres,"/data/var3d/" , "frames", iframe3d)
      END IF
      !  var5d group (moments)
      IF (nsave_5d .GT. 0) THEN
        CALL creatg(fidres, "/data/var5d", "5d profiles")
        CALL creatd(fidres, rank, dims,  "/data/var5d/time",     "Time t*c_s/R")
        CALL creatd(fidres, rank, dims, "/data/var5d/cstep", "iteration number")
        IF (write_Napj) THEN
         CALL creatg(fidres, "/data/var5d/moments", "full moments array")
        ENDIF
        IF (cstep==0) THEN
         iframe5d=0
        END IF
        CALL attach(fidres,"/data/var5d/" , "frames", iframe5d)
      END IF
    ENDIF
    !_____________________________________________________________________________
    !                   2.   Periodic diagnostics
    !
    IF (kstep .GE. 0) THEN
      !                       2.1   0d history arrays
      IF (nsave_0d .GT. 0) THEN
        IF ( MOD(cstep, nsave_0d) == 0 ) THEN
          CALL diagnose_0d
        END IF
      END IF
      !                       2.2   2d profiles
      IF (nsave_2d .GT. 0) THEN
        IF (MOD(cstep, nsave_2d) == 0) THEN
          CALL diagnose_2d
        ENDIF
      ENDIF
      !                       2.3   3d profiles
      IF (nsave_3d .GT. 0) THEN
        IF (MOD(cstep, nsave_3d) == 0) THEN
          CALL diagnose_3d
          ! Looks at the folder if the file check_phi exists and spits a snapshot
          ! of the current electrostatic potential in a basic text file
          CALL spit_snapshot_check
        ENDIF
      ENDIF
      !                       2.4   5d profiles
      IF (nsave_5d .GT. 0) THEN
        IF (MOD(cstep, nsave_5d) == 0) THEN
          CALL diagnose_5d
        END IF
      END IF
    !_____________________________________________________________________________
    !                   3.   Final diagnostics
    ELSEIF (kstep .EQ. -1) THEN
      CALL attach(fidres, "/data/input","cpu_time",chrono_runt%ttot)
      ! make a checkpoint at last timestep if not crashed
      IF(.NOT. crashed) THEN
        IF(my_id .EQ. 0) write(*,*) 'Saving last state'
        IF (nsave_5d .GT. 0) CALL diagnose_5d
      ENDIF
      !   Close all diagnostic files
      CALL mpi_barrier(MPI_COMM_WORLD, ierr)
      CALL closef(fidres)
    END IF
  END SUBROUTINE diagnose_full
  
  !! This routine outputs only txt file 0D data (flux and time)
  SUBROUTINE diagnose_txtonly(kstep)
    USE basic
    USE processing,      ONLY: pflux_x, hflux_x, compute_radial_transport, compute_radial_heatflux
    USE parallel,        ONLY: my_id, comm0
    USE futils,          ONLY: creatf, creatg, creatd, attach, putfile, closef
    IMPLICIT NONE
    INTEGER, INTENT(in) :: kstep
    INTEGER, parameter  :: BUFSIZE = 2
    INTEGER :: rank = 0
    INTEGER :: dims(1) = (/0/)
    IF (kstep .GE. 0) THEN
      ! output the transport in a txt file
      IF ( MOD(cstep, nsave_0d) == 0 ) THEN
        CALL compute_radial_transport
        CALL compute_radial_heatflux
        IF (my_id .EQ. 0) &
          WRITE(1,*) time, pflux_x(1), hflux_x(1)
      END IF
    !! Save the last state
    ELSEIF (kstep .EQ. -1) THEN
      CALL init_outfile(comm0,   resfile0,resfile,fidres)
      CALL creatg(fidres, "/data/var5d", "5d profiles")
      CALL creatd(fidres, rank, dims,  "/data/var5d/time",     "Time t*c_s/R")
      CALL creatd(fidres, rank, dims, "/data/var5d/cstep", "iteration number")
      CALL creatg(fidres, "/data/var5d/moments", "full moments array")
      CALL attach(fidres,"/data/var5d/" , "frames", iframe5d)
      CALL diagnose_5d
      CALL closef(fidres)
    ENDIF
  END SUBROUTINE diagnose_txtonly
  
  !!-------------- Auxiliary routines -----------------!!
  SUBROUTINE diagnose_0d
    USE basic
    USE futils, ONLY: append, attach, getatt, creatd
    USE prec_const
    USE processing
    IMPLICIT NONE
    ! Time measurement data
    CALL append(fidres, "/profiler/Tc_rhs",       REAL(chrono_mrhs%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_adv_field", REAL(chrono_advf%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_clos",      REAL(chrono_clos%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_ghost",     REAL(chrono_ghst%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_coll",      REAL(chrono_coll%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_poisson",   REAL(chrono_pois%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_Sapj",      REAL(chrono_sapj%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_checkfield",REAL(chrono_chck%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_diag",      REAL(chrono_diag%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_grad",      REAL(chrono_grad%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_nadiab",    REAL(chrono_napj%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_ExBshear",  REAL(chrono_ExBs%ttot,dp),ionode=0)
    CALL append(fidres, "/profiler/Tc_step",      REAL(chrono_step%ttot,dp),ionode=0)
#ifdef TEST_SVD
    CALL append(fidres, "/profiler/Tc_CLA",      REAL(chrono_CLA%ttot,dp),ionode=0) 
#endif
    CALL append(fidres, "/profiler/time",                REAL(time,dp),ionode=0)
    ! Processing data
    CALL append(fidres,  "/data/var0d/time",      REAL(time,dp),ionode=0)
    CALL append(fidres, "/data/var0d/cstep", real(cstep,dp),ionode=0)
    CALL getatt(fidres,      "/data/var0d/",       "frames",iframe0d)
    iframe0d=iframe0d+1
    CALL attach(fidres,"/data/var0d/" , "frames", iframe0d)
    ! Ion transport data
    IF (write_gamma) THEN
      CALL compute_radial_transport
      CALL append(fidres, "/data/var0d/gflux_x",REAL(gflux_x,dp),ionode=0)
      CALL append(fidres, "/data/var0d/pflux_x",REAL(pflux_x,dp),ionode=0)
    ENDIF
    IF (write_hf) THEN
      CALL compute_radial_heatflux
      CALL append(fidres, "/data/var0d/hflux_x",REAL(hflux_x,dp),ionode=0)
    ENDIF
  END SUBROUTINE diagnose_0d
  
  SUBROUTINE diagnose_2d
    USE prec_const
    USE basic
    USE futils, ONLY: putarr, append
#ifdef TEST_SVD
    USE CLA, ONLY: Sf
#endif
    IMPLICIT NONE
    iframe2d=iframe2d+1
    CALL append(fidres,"/data/var2d/time", REAL(time,dp), ionode=0)
    CALL append(fidres,"/data/var2d/cstep",REAL(cstep,dp),ionode=0)
#ifdef TEST_SVD
    WRITE(dset_name, "(A, '/', i6.6)") "/data/var2d/sv_ky_pj/", iframe2d
    CALL putarr(fidres, dset_name, Sf, ionode=0)
#endif
  END SUBROUTINE
  
  SUBROUTINE diagnose_3d
    USE basic
    USE futils, ONLY: append, getatt, attach, putarr!, putarrnd
    USE fields, ONLY: phi, psi, moments
    USE array,  ONLY: Napjz,dens,upar,uper,Tpar,Tper,temp
    USE grid, ONLY: CONTAINSp0, ip0,ij0, local_na, total_na,&
                    total_np, total_nj, total_nky, total_nkx, total_nz, &
                    local_np, local_nky, local_nkx, local_nz, &
                    ngz
    USE time_integration, ONLY: updatetlevel
    USE prec_const
    USE processing, ONLY: compute_fluid_moments, compute_Napjz_spectrum
    USE model,      ONLY: EM
    USE parallel,   ONLY: manual_3D_bcast
    IMPLICIT NONE
    COMPLEX(xp), DIMENSION(local_na,local_nky,local_nkx,local_nz) :: Na00_
    ! add current time, cstep and frame
    CALL append(fidres,  "/data/var3d/time",           REAL(time,dp),ionode=0)
    CALL append(fidres, "/data/var3d/cstep", real(cstep,dp),ionode=0)
    CALL getatt(fidres,      "/data/var3d/",       "frames",iframe3d)
    iframe3d=iframe3d+1
    CALL attach(fidres,"/data/var3d/" , "frames", iframe3d)
    ! Write current EM fields
    IF (write_phi)        CALL write_field3d_kykxz(phi (:,:,(1+ngz/2):(local_nz+ngz/2)), 'phi')
    IF (write_phi.AND.EM) CALL write_field3d_kykxz(psi (:,:,(1+ngz/2):(local_nz+ngz/2)), 'psi')
    IF (write_Na00) THEN
      CALL compute_Napjz_spectrum
      IF (CONTAINSp0) THEN
        ! gyrocenter density
        Na00_    = moments(:,ip0,ij0,:,:,(1+ngz/2):(local_nz+ngz/2),updatetlevel)
      ELSE
        Na00_    = 0._xp
      ENDIF
      CALL write_field3da_kykxz(Na00_, 'Na00')
      ! <<Napj>x>y spectrum
      CALL write_field3da_pjz(Napjz, 'Napjz')
    ENDIF
    !! Fuid moments
    IF (write_dens .OR. write_fvel .OR. write_temp) &
    CALL compute_fluid_moments
    IF (write_dens) THEN
      CALL write_field3da_kykxz(dens, 'dens')
    ENDIF
    IF (write_fvel) THEN
      CALL write_field3da_kykxz(upar, 'upar')
      CALL write_field3da_kykxz(uper, 'uper')
    ENDIF
    IF (write_temp) THEN
      CALL write_field3da_kykxz(Tpar, 'Tpar')
      CALL write_field3da_kykxz(Tper, 'Tper')
      CALL write_field3da_kykxz(temp, 'temp')
    ENDIF
    CONTAINS
      SUBROUTINE write_field3d_kykxz(field, text)
        USE parallel, ONLY : gather_xyz, num_procs
        IMPLICIT NONE
        COMPLEX(xp), DIMENSION(:,:,:), INTENT(IN) :: field
        CHARACTER(*), INTENT(IN) :: text
        COMPLEX(xp), DIMENSION(total_nky,total_nkx,total_nz) :: field_full
        CHARACTER(50) :: dset_name
        field_full = 0;
        WRITE(dset_name, "(A, '/', A, '/', i6.6)") "/data/var3d", TRIM(text), iframe3d
        IF (num_procs .EQ. 1) THEN ! no data distribution
          CALL putarr(fidres, dset_name, field, ionode=0)
        ELSE
          CALL gather_xyz(field,field_full,local_nky,total_nky,total_nkx,local_nz,total_nz)
          CALL putarr(fidres, dset_name, field_full, ionode=0)
        ENDIF
        END SUBROUTINE write_field3d_kykxz
  
        SUBROUTINE write_field3da_kykxz(field, text)
          USE parallel, ONLY : gather_xyz, num_procs
          IMPLICIT NONE
          COMPLEX(xp), DIMENSION(:,:,:,:), INTENT(IN) :: field
          CHARACTER(*), INTENT(IN) :: text
          COMPLEX(xp), DIMENSION(total_na,total_nky,total_nkx,total_nz) :: field_full
          COMPLEX(xp), DIMENSION(local_nky,total_nkx,local_nz) :: buff_local
          COMPLEX(xp), DIMENSION(total_nky,total_nkx,total_nz) :: buff_full
          CHARACTER(50) :: dset_name
          INTEGER :: ia
          field_full = 0;
          WRITE(dset_name, "(A, '/', A, '/', i6.6)") "/data/var3d", TRIM(text), iframe3d
          IF (num_procs .EQ. 1) THEN ! no data distribution
            CALL putarr(fidres, dset_name, field, ionode=0)
          ELSE
            DO ia = 1,total_Na
              buff_local = field(ia,:,:,:)
              CALL gather_xyz(buff_local,buff_full,local_nky,total_nky,total_nkx,local_nz,total_nz)
              field_full(ia,:,:,:) = buff_full
            ENDDO
            CALL putarr(fidres, dset_name, field_full, ionode=0)
          ENDIF
          END SUBROUTINE write_field3da_kykxz
  
      SUBROUTINE write_field3da_pjz(field, text)
        USE parallel, ONLY : gather_pjz, num_procs
        IMPLICIT NONE
        COMPLEX(xp), DIMENSION(:,:,:,:), INTENT(IN) :: field
        CHARACTER(*), INTENT(IN) :: text
        COMPLEX(xp), DIMENSION(total_na,total_np,total_nj,total_nz) :: field_full
        CHARACTER(LEN=50) :: dset_name
        field_full = 0;
        WRITE(dset_name, "(A, '/', A, '/', i6.6)") "/data/var3d", TRIM(text), iframe3d
        IF (num_procs .EQ. 1) THEN ! no data distribution
          CALL putarr(fidres, dset_name, field, ionode=0)
        ELSE
          CALL gather_pjz(field,field_full,total_na,local_np,total_np,total_nj,local_nz,total_nz)
          CALL putarr(fidres, dset_name, field_full, ionode=0)
        ENDIF
        CALL attach(fidres, dset_name, "time", time)
      END SUBROUTINE write_field3da_pjz
  
  END SUBROUTINE diagnose_3d
  
  SUBROUTINE diagnose_5d
  
    USE basic,  ONLY: time, iframe5d,cstep
    USE futils, ONLY: append, getatt, attach, putarr !,putarrnd
    USE fields, ONLY: moments
    USE grid,   ONLY:total_np, total_nj, total_nky, total_nkx, total_nz, &
                     local_np, local_nj, local_nky, local_nkx, local_nz, &
                     ngp, ngj, ngz, total_na
    USE prec_const, ONLY: xp,dp
    IMPLICIT NONE
  
    CALL append(fidres,  "/data/var5d/time",  REAL(time,dp),ionode=0)
    CALL append(fidres, "/data/var5d/cstep", REAL(cstep,dp),ionode=0)
    CALL getatt(fidres,      "/data/var5d/",       "frames",iframe5d)
    iframe5d=iframe5d+1
    CALL attach(fidres,"/data/var5d/" , "frames", iframe5d)
  
    IF (write_Napj) THEN
    CALL write_field5d(moments, 'moments')
    ENDIF
  
    CONTAINS
  
    SUBROUTINE write_field5d(field, text)
      USE basic,            ONLY: jobnum, dt
      USE futils,           ONLY: attach, putarr!, putarrnd
      USE parallel,         ONLY: gather_pjxyz, num_procs
      USE prec_const,       ONLY: xp
      USE time_integration, ONLY: ntimelevel
      IMPLICIT NONE
      COMPLEX(xp), DIMENSION(total_na,local_np+ngp,local_nj+ngj,local_Nky,local_Nkx,local_nz+ngz,ntimelevel), INTENT(IN) :: field
      CHARACTER(*), INTENT(IN) :: text
      COMPLEX(xp), DIMENSION(total_na,local_np,local_nj,local_nky,local_nkx,local_nz) :: field_sub
      COMPLEX(xp), DIMENSION(total_na,total_np,total_nj,total_nky,total_nkx,total_nz) :: field_full
      CHARACTER(LEN=50) :: dset_name
      field_sub  = field(1:total_na,(1+ngp/2):(local_np+ngp/2),(1+ngj/2):(local_nj+ngj/2),&
                            1:local_nky,1:local_nkx,(1+ngz/2):(local_nz+ngz/2),1)
      field_full = 0;
      WRITE(dset_name, "(A, '/', A, '/', i6.6)") "/data/var5d", TRIM(text), iframe5d
      IF (num_procs .EQ. 1) THEN
        CALL putarr(fidres, dset_name, field_sub, ionode=0)
      ELSE!IF(GATHERV_OUTPUT) THEN ! output using one node (gatherv)
        CALL gather_pjxyz(field_sub,field_full,total_na,local_np,total_np,total_nj,local_nky,total_nky,total_nkx,local_nz,total_nz)
        CALL putarr(fidres, dset_name, field_full, ionode=0)
      ! ELSE
      !   CALL putarrnd(fidres, dset_name, field_sub,  (/1,3,5/))
       ENDIF
      CALL attach(fidres, dset_name, 'cstep', cstep)
      CALL attach(fidres, dset_name, 'time', time)
      CALL attach(fidres, dset_name, 'jobnum', jobnum)
      CALL attach(fidres, dset_name, 'dt', dt)
      CALL attach(fidres, dset_name, 'iframe5d', iframe5d)
    END SUBROUTINE write_field5d
  END SUBROUTINE diagnose_5d
  
  SUBROUTINE spit_snapshot_check
    USE fields, ONLY: phi
    USE grid, ONLY: total_nkx,total_nky,total_nz,&
                    local_nky,local_nz, ngz
    USE parallel, ONLY: gather_xyz, my_id
    USE basic
    USE prec_const, ONLY: xp
    IMPLICIT NONE
    LOGICAL :: file_exist
    INTEGER :: fid_check, ikx, iky, iz
    CHARACTER(256) :: check_filename
    COMPLEX(xp), DIMENSION(total_nky,total_nkx,total_nz) :: field_to_check
    !! Spit a snapshot of PHI if requested (triggered by creating a file named "check_phi")
    INQUIRE(file='check_phi', exist=file_exist)
    IF( file_exist ) THEN
       IF(my_id.EQ. 0) WRITE(*,*) 'Check file found -> gather phi..'
       CALL gather_xyz(phi(:,:,(1+Ngz/2):(local_nz+Ngz/2)), field_to_check,local_nky,total_nky,total_nkx,local_nz,total_nz)
       IF(my_id.EQ. 0) THEN
         WRITE(check_filename,'(a16)') 'check_phi.out'
         OPEN(fid_check, file=check_filename, form='formatted')
         WRITE(*,*) 'Check file found -> output phi ..'
         WRITE(fid_check,*) total_nky, total_nkx, total_nz
         DO iky = 1,total_nky; DO ikx = 1, total_nkx; DO iz = 1,total_nz
           WRITE(fid_check,*) real(field_to_check(iky,ikx,iz)), ',' , imag(field_to_check(iky,ikx,iz))
         ENDDO; ENDDO; ENDDO
         CLOSE(fid_check)
         WRITE(*,*) 'Check file found -> done.'
         ! delete the check_phi flagfile
         OPEN(fid_check, file='check_phi')
         CLOSE(fid_check, status='delete')
       ENDIF
    ENDIF
  END SUBROUTINE spit_snapshot_check
  


  SUBROUTINE diag_outputinputs(fid)
    !
    !    Write the input parameters to the results_xx.h5 file
    !
    USE prec_const
    USE futils, ONLY: attach, creatd
    IMPLICIT NONE
    INTEGER, INTENT(in) :: fid
    CHARACTER(len=256)  :: str
    WRITE(str,'(a)') '/data/input/diagnostics'
    CALL creatd(fidres, 0,(/0/),TRIM(str),'Diagnostics Parameters Input')
    CALL attach(fid, TRIM(str), "write_doubleprecision", write_doubleprecision)
    CALL attach(fid, TRIM(str), "nsave_0d", nsave_0d)
    CALL attach(fid, TRIM(str), "nsave_1d", nsave_1d)
    CALL attach(fid, TRIM(str), "nsave_2d", nsave_2d)
    CALL attach(fid, TRIM(str), "nsave_3d", nsave_3d)
    CALL attach(fid, TRIM(str), "nsave_5d", nsave_5d)
    CALL attach(fid, TRIM(str), "write_gamma", write_gamma)
    CALL attach(fid, TRIM(str),    "write_hf",    write_hf)
    CALL attach(fid, TRIM(str),   "write_phi",   write_phi)
    CALL attach(fid, TRIM(str),  "write_Na00",  write_Na00)
    CALL attach(fid, TRIM(str),  "write_Napj",  write_Napj)
    CALL attach(fid, TRIM(str),  "write_Sapj",  write_Sapj)
    CALL attach(fid, TRIM(str),  "write_dens",  write_dens)
    CALL attach(fid, TRIM(str),  "write_fvel",  write_fvel)
    CALL attach(fid, TRIM(str),  "write_temp",  write_temp)

  END SUBROUTINE diag_outputinputs


END MODULE diagnostics
