MODULE iostart

PRIVATE
    ! WARNING the following variables, though SAVED, should not be put in a THREADPRIVATE statement
    INTEGER,SAVE :: nid_start 
    INTEGER,SAVE :: nid_restart
    INTEGER,SAVE :: idim1,idim2,idim3,idim4

    INTEGER,PARAMETER :: length=100
    
    INTERFACE get_field
      MODULE PROCEDURE Get_field_r1,Get_field_r2,Get_field_r3
    END INTERFACE get_field
    
    INTERFACE get_var
      MODULE PROCEDURE get_var_r0,Get_var_r1,Get_var_r2,Get_var_r3
    END INTERFACE get_var

    INTERFACE put_field
      MODULE PROCEDURE put_field_r1,put_field_r2,put_field_r3
    END INTERFACE put_field

    INTERFACE put_var
      MODULE PROCEDURE put_var_r0,put_var_r1,put_var_r2,put_var_r3
    END INTERFACE put_var

    PUBLIC get_field,get_var,put_field,put_var
    PUBLIC Open_startphy,close_startphy,open_restartphy,close_restartphy, enddef_restartphy
    
CONTAINS

  SUBROUTINE Open_startphy(filename)
  USE netcdf
  USE mod_phys_lmdz_para
  IMPLICIT NONE
    CHARACTER(LEN=*) :: filename
    INTEGER          :: ierr

    IF (is_mpi_root .AND. is_omp_root) THEN
      ierr = NF90_OPEN (filename, NF90_NOWRITE,nid_start)
      IF (ierr.NE.NF90_NOERR) THEN
        write(6,*)' Pb d''ouverture du fichier '//filename
        write(6,*)' ierr = ', ierr
        CALL abort_physic("", "", 1)
      ENDIF
    ENDIF
   
  END SUBROUTINE Open_startphy

  SUBROUTINE Close_startphy
  USE netcdf
  USE mod_phys_lmdz_para
  IMPLICIT NONE
    INTEGER          :: ierr

    IF (is_mpi_root .AND. is_omp_root) THEN
        ierr = NF90_CLOSE (nid_start)
    ENDIF

  END SUBROUTINE close_startphy


  FUNCTION Inquire_Field(Field_name)
  USE netcdf
  USE mod_phys_lmdz_para
  IMPLICIT NONE
    CHARACTER(LEN=*) :: Field_name
    LOGICAL :: inquire_field
    INTEGER :: varid
    INTEGER :: ierr
    
    IF (is_mpi_root .AND. is_omp_root) THEN
      ierr=NF90_INQ_VARID(nid_start,Field_name,varid)
      IF (ierr==NF90_NOERR) THEN
        Inquire_field=.TRUE.
      ELSE
        Inquire_field=.FALSE.
      ENDIF
    ENDIF

    CALL bcast(Inquire_field)

  END FUNCTION Inquire_Field
  
 
  SUBROUTINE Get_Field_r1(field_name,field,found)
  IMPLICIT NONE
    CHARACTER(LEN=*),INTENT(IN)    :: Field_name
    REAL,INTENT(INOUT)               :: Field(:)
    LOGICAL,INTENT(OUT),OPTIONAL   :: found 

    CALL Get_field_rgen(field_name,field,1,found)
      
  END SUBROUTINE Get_Field_r1
  
  SUBROUTINE Get_Field_r2(field_name,field,found)
  IMPLICIT NONE
    CHARACTER(LEN=*),INTENT(IN)    :: Field_name
    REAL,INTENT(INOUT)               :: Field(:,:)
    LOGICAL,INTENT(OUT),OPTIONAL   :: found 

    CALL Get_field_rgen(field_name,field,size(field,2),found)

      
  END SUBROUTINE Get_Field_r2
  
  SUBROUTINE Get_Field_r3(field_name,field,found)
  IMPLICIT NONE
    CHARACTER(LEN=*),INTENT(IN)    :: Field_name
    REAL,INTENT(INOUT)               :: Field(:,:,:)
    LOGICAL,INTENT(OUT),OPTIONAL   :: found 

    CALL Get_field_rgen(field_name,field,size(field,2)*size(field,3),found)
      
  END SUBROUTINE Get_Field_r3
  
  SUBROUTINE Get_field_rgen(field_name,field,field_size,found)
  USE netcdf
  USE dimphy
  USE geometry_mod
  USE mod_grid_phy_lmdz
  USE mod_phys_lmdz_para
  IMPLICIT NONE
    CHARACTER(LEN=*) :: Field_name
    INTEGER          :: field_size
    REAL             :: field(klon,field_size)
    LOGICAL,OPTIONAL :: found
    
    REAL,ALLOCATABLE    :: field_glo(:,:)
    REAL,ALLOCATABLE    :: field_glo_tmp(:,:)
    INTEGER,ALLOCATABLE :: ind_cell_glo_glo(:)
    LOGICAL :: tmp_found
    INTEGER :: varid
    INTEGER :: ierr,i

    IF (is_master) THEN 
      ALLOCATE(ind_cell_glo_glo(klon_glo))
      ALLOCATE(field_glo(klon_glo,field_size))
      ALLOCATE(field_glo_tmp(klon_glo,field_size))
    ELSE
      ALLOCATE(ind_cell_glo_glo(0))
      ALLOCATE(field_glo(0,0))
    ENDIF
    
    CALL gather(ind_cell_glo,ind_cell_glo_glo)
    
    IF (is_master) THEN
  
      ierr=NF90_INQ_VARID(nid_start,Field_name,varid)
      
      IF (ierr==NF90_NOERR) THEN
        CALL body(field_glo_tmp)
        tmp_found=.TRUE.
      ELSE
        tmp_found=.FALSE.
      ENDIF
    
    ENDIF
    
    CALL bcast(tmp_found)

    IF (tmp_found) THEN
      IF (is_master) THEN  
        DO i=1,klon_glo
         field_glo(i,:)=field_glo_tmp(ind_cell_glo_glo(i),:)
        ENDDO
      ENDIF
      CALL scatter(field_glo,field)
    ENDIF
    
    IF (PRESENT(found)) THEN
      found=tmp_found
    ELSE
      IF (.NOT. tmp_found) THEN
        PRINT*, 'phyetat0: Le champ <'//field_name//'> est absent'
        call abort_physic("", "", 1)
      ENDIF
    ENDIF
 
    
    CONTAINS
     
     SUBROUTINE body(field_glo)
       REAL :: field_glo(klon_glo*field_size)
         ierr=NF90_GET_VAR(nid_start,varid,field_glo)
         IF (ierr/=NF90_NOERR) THEN
           ! La variable exist dans le fichier mais la lecture a echouee. 
           PRINT*, 'phyetat0: Lecture echouee pour <'//field_name//'>'

           IF (field_name=='CLWCON' .OR. field_name=='RNEBCON' .OR. field_name=='RATQS') THEN
              ! Essaye de lire le variable sur surface uniqument, comme fait avant
              field_glo(:)=0.
              ierr=NF90_GET_VAR(nid_start,varid,field_glo(1:klon_glo))
              IF (ierr/=NF90_NOERR) THEN
                 PRINT*, 'phyetat0: Lecture echouee aussi en 2D pour <'//field_name//'>'
                 call abort_physic("", "", 1)
              ELSE
                 PRINT*, 'phyetat0: La variable <'//field_name//'> lu sur surface seulement'!, selon ancien format, le reste mis a zero'
              END IF
           ELSE
              call abort_physic("", "", 1)
           ENDIF
         ENDIF

     END SUBROUTINE body

  END SUBROUTINE Get_field_rgen
  

  SUBROUTINE get_var_r0(var_name,var,found)
  IMPLICIT NONE  
    CHARACTER(LEN=*),INTENT(IN)  :: var_name
    REAL,INTENT(INOUT)             :: var
    LOGICAL,OPTIONAL,INTENT(OUT) :: found

    REAL                         :: varout(1)
    
    CALL Get_var_rgen(var_name,varout,size(varout),found)
    var=varout(1)
 
  END SUBROUTINE get_var_r0

  SUBROUTINE get_var_r1(var_name,var,found)
  IMPLICIT NONE  
    CHARACTER(LEN=*),INTENT(IN)  :: var_name
    REAL,INTENT(INOUT)             :: var(:)
    LOGICAL,OPTIONAL,INTENT(OUT) :: found
    
    CALL Get_var_rgen(var_name,var,size(var),found)
  
  END SUBROUTINE get_var_r1

  SUBROUTINE get_var_r2(var_name,var,found)
  IMPLICIT NONE  
    CHARACTER(LEN=*),INTENT(IN)  :: var_name
    REAL,INTENT(OUT)             :: var(:,:)
    LOGICAL,OPTIONAL,INTENT(OUT) :: found
    
    CALL Get_var_rgen(var_name,var,size(var),found)
  
  END SUBROUTINE get_var_r2

  SUBROUTINE get_var_r3(var_name,var,found)
  IMPLICIT NONE  
    CHARACTER(LEN=*),INTENT(IN)  :: var_name
    REAL,INTENT(INOUT)             :: var(:,:,:)
    LOGICAL,OPTIONAL,INTENT(OUT) :: found
    
    CALL Get_var_rgen(var_name,var,size(var),found)
  
  END SUBROUTINE get_var_r3

  SUBROUTINE Get_var_rgen(var_name,var,var_size,found)
  USE netcdf
  USE dimphy
  USE mod_grid_phy_lmdz
  USE mod_phys_lmdz_para
  IMPLICIT NONE
    CHARACTER(LEN=*) :: var_name
    INTEGER          :: var_size
    REAL             :: var(var_size)
    LOGICAL,OPTIONAL :: found
    
    LOGICAL :: tmp_found
    INTEGER :: varid
    INTEGER :: ierr
    
    IF (is_mpi_root .AND. is_omp_root) THEN
  
      ierr=NF90_INQ_VARID(nid_start,var_name,varid)
      
      IF (ierr==NF90_NOERR) THEN
        ierr=NF90_GET_VAR(nid_start,varid,var)
        IF (ierr/=NF90_NOERR) THEN
          PRINT*, 'phyetat0: Lecture echouee pour <'//var_name//'>'
          call abort_physic("", "", 1)
        ENDIF
        tmp_found=.TRUE.
      ELSE
        tmp_found=.FALSE.
      ENDIF
    
    ENDIF
    
    CALL bcast(tmp_found)

    IF (tmp_found) THEN
      CALL bcast(var)
    ENDIF
    
    IF (PRESENT(found)) THEN
      found=tmp_found
    ELSE
      IF (.NOT. tmp_found) THEN
        PRINT*, 'phyetat0: La variable champ <'//var_name//'> est absente'
        call abort_physic("", "", 1)
      ENDIF
    ENDIF

  END SUBROUTINE Get_var_rgen


  SUBROUTINE open_restartphy(filename)
  USE netcdf
  USE mod_phys_lmdz_para, ONLY: is_master
  USE mod_grid_phy_lmdz, ONLY: klon_glo
  USE dimphy, ONLY: klev, klevp1
  USE print_control_mod, ONLY: lunout
  IMPLICIT NONE
    CHARACTER(LEN=*),INTENT(IN) :: filename
    INTEGER                     :: ierr
    
    IF (is_master) THEN
      ierr = NF90_CREATE(filename, IOR(NF90_CLOBBER,NF90_64BIT_OFFSET), &
                          nid_restart)
      IF (ierr/=NF90_NOERR) THEN
        write(lunout,*)'open_restartphy: problem creating file '//trim(filename)
        write(lunout,*)trim(nf90_strerror(ierr))
        CALL abort_physic("open_restartphy", trim(nf90_strerror(ierr)), 1)
      ENDIF

      ierr = NF90_PUT_ATT (nid_restart, NF90_GLOBAL, "title","Fichier redemmarage physique")

      ierr = NF90_DEF_DIM (nid_restart, "index", length, idim1)
      ierr = NF90_DEF_DIM (nid_restart, "points_physiques", klon_glo, idim2)
      ierr = NF90_DEF_DIM (nid_restart, "horizon_vertical", klon_glo*klev, idim3)
      ierr = NF90_DEF_DIM (nid_restart, "horizon_klevp1", klon_glo*klevp1, idim4)

!      ierr = NF90_ENDDEF(nid_restart)
    ENDIF

  END SUBROUTINE open_restartphy
  
  SUBROUTINE enddef_restartphy
  USE netcdf
  USE mod_phys_lmdz_para
  IMPLICIT NONE
    INTEGER          :: ierr

    IF (is_master) ierr = NF90_ENDDEF(nid_restart)
 
  END SUBROUTINE enddef_restartphy

  SUBROUTINE close_restartphy
  USE netcdf
  USE mod_phys_lmdz_para
  IMPLICIT NONE
    INTEGER          :: ierr

    IF (is_master) ierr = NF90_CLOSE (nid_restart)
 
  END SUBROUTINE close_restartphy

  
  SUBROUTINE put_field_r1(pass, field_name,title,field)
  IMPLICIT NONE
  INTEGER, INTENT(IN)            :: pass
  CHARACTER(LEN=*),INTENT(IN)    :: field_name
  CHARACTER(LEN=*),INTENT(IN)    :: title
  REAL,INTENT(IN)                :: field(:)
    CALL put_field_rgen(pass, field_name,title,field,1)
  
  END SUBROUTINE put_field_r1

  SUBROUTINE put_field_r2(pass, field_name,title,field)
  IMPLICIT NONE
  INTEGER, INTENT(IN)            :: pass
  CHARACTER(LEN=*),INTENT(IN)    :: field_name
  CHARACTER(LEN=*),INTENT(IN)    :: title
  REAL,INTENT(IN)                :: field(:,:)
  
    CALL put_field_rgen(pass, field_name,title,field,size(field,2))
  
  END SUBROUTINE put_field_r2

  SUBROUTINE put_field_r3(pass, field_name,title,field)
  IMPLICIT NONE
  INTEGER, INTENT(IN)            :: pass
  CHARACTER(LEN=*),INTENT(IN)    :: field_name
  CHARACTER(LEN=*),INTENT(IN)    :: title
  REAL,INTENT(IN)                :: field(:,:,:)
  
    CALL put_field_rgen(pass, field_name,title,field,size(field,2)*size(field,3))
  
  END SUBROUTINE put_field_r3
  
  SUBROUTINE put_field_rgen(pass, field_name,title,field,field_size)
  USE netcdf
  USE dimphy
  USE geometry_mod
  USE mod_grid_phy_lmdz
  USE mod_phys_lmdz_para
  USE lmdz_cppkeys_wrapper, ONLY: nf90_format
  IMPLICIT NONE
  INTEGER, INTENT(IN)            :: pass
  CHARACTER(LEN=*),INTENT(IN)    :: field_name
  CHARACTER(LEN=*),INTENT(IN)    :: title
  INTEGER,INTENT(IN)             :: field_size
  REAL,INTENT(IN)                :: field(klon,field_size)
  
!  REAL                           :: field_glo(klon_glo,field_size)
!  REAL                           :: field_glo_tmp(klon_glo,field_size)
  REAL ,ALLOCATABLE              :: field_glo(:,:)
  REAL ,ALLOCATABLE              :: field_glo_tmp(:,:)
  INTEGER,ALLOCATABLE            :: ind_cell_glo_glo(:)
!  INTEGER                        :: ind_cell_glo_glo(klon_glo)
  INTEGER                        :: ierr,i
  INTEGER                        :: nvarid
  INTEGER                        :: idim
   
! first pass : definition   
  IF (pass==1) THEN
    
    IF (is_master) THEN

      IF (field_size==1) THEN
        idim=idim2
      ELSE IF (field_size==klev) THEN
        idim=idim3
      ELSE IF (field_size==klevp1) THEN
        idim=idim4
      ELSE
        PRINT *, "erreur phyredem : probleme de dimension"
        CALL abort_physic("", "", 1)
      ENDIF
         
!      ierr = NF90_REDEF (nid_restart)
      ierr = NF90_DEF_VAR (nid_restart, field_name, nf90_format,(/ idim /),nvarid)
      IF (LEN_TRIM(title) > 0) ierr = NF90_PUT_ATT (nid_restart,nvarid,"title", title)
!      ierr = NF90_ENDDEF(nid_restart)
     ENDIF

! second pass : write     
   ELSE IF (pass==2) THEN 
    
     IF (is_master) THEN
       ALLOCATE(ind_cell_glo_glo(klon_glo))
       ALLOCATE(field_glo(klon_glo,field_size))
       ALLOCATE(field_glo_tmp(klon_glo,field_size))
     ELSE
       ALLOCATE(ind_cell_glo_glo(0))
       ALLOCATE(field_glo_tmp(0,0))
     ENDIF
     
     CALL gather(ind_cell_glo,ind_cell_glo_glo) 

     CALL gather(field,field_glo_tmp)
    
     IF (is_master) THEN

       DO i=1,klon_glo
         field_glo(ind_cell_glo_glo(i),:)=field_glo_tmp(i,:)
       ENDDO

       ierr = NF90_INQ_VARID(nid_restart, field_name, nvarid)
       ierr = NF90_PUT_VAR(nid_restart,nvarid,RESHAPE(field_glo,(/klon_glo*field_size/)))
      ENDIF
   ENDIF
    
 END SUBROUTINE put_field_rgen  
  

 SUBROUTINE put_var_r0(pass, var_name,title,var)
   IMPLICIT NONE
     INTEGER, INTENT(IN)            :: pass
     CHARACTER(LEN=*),INTENT(IN) :: var_name
     CHARACTER(LEN=*),INTENT(IN) :: title
     REAL,INTENT(IN)             :: var
     REAL                        :: varin(1)
     
     varin(1)=var
     
     CALL put_var_rgen(pass, var_name,title,varin,size(varin))

  END SUBROUTINE put_var_r0


   SUBROUTINE put_var_r1(pass, var_name,title,var)
   IMPLICIT NONE
     INTEGER, INTENT(IN)            :: pass
     CHARACTER(LEN=*),INTENT(IN) :: var_name
     CHARACTER(LEN=*),INTENT(IN) :: title
     REAL,INTENT(IN)             :: var(:)
     
     CALL put_var_rgen(pass, var_name,title,var,size(var))

  END SUBROUTINE put_var_r1
 
  SUBROUTINE put_var_r2(pass, var_name,title,var)
   IMPLICIT NONE
     INTEGER, INTENT(IN)            :: pass
     CHARACTER(LEN=*),INTENT(IN) :: var_name
     CHARACTER(LEN=*),INTENT(IN) :: title
     REAL,INTENT(IN)             :: var(:,:)
     
     CALL put_var_rgen(pass, var_name,title,var,size(var))

  END SUBROUTINE put_var_r2     
  
  SUBROUTINE put_var_r3(pass, var_name,title,var)
   IMPLICIT NONE
     INTEGER, INTENT(IN)            :: pass
     CHARACTER(LEN=*),INTENT(IN) :: var_name
     CHARACTER(LEN=*),INTENT(IN) :: title
     REAL,INTENT(IN)             :: var(:,:,:)
     
     CALL put_var_rgen(pass, var_name,title,var,size(var))

  END SUBROUTINE put_var_r3

  SUBROUTINE put_var_rgen(pass, var_name,title,var,var_size)
  USE netcdf
  USE dimphy
  USE mod_phys_lmdz_para
  USE lmdz_cppkeys_wrapper, ONLY: nf90_format
  IMPLICIT NONE
    INTEGER, INTENT(IN)         :: pass
    CHARACTER(LEN=*),INTENT(IN) :: var_name
    CHARACTER(LEN=*),INTENT(IN) :: title
    INTEGER,INTENT(IN)          :: var_size
    REAL,INTENT(IN)             :: var(var_size)
    
    INTEGER :: ierr
    INTEGER :: nvarid
         
    IF (is_master) THEN

      IF (var_size/=length) THEN
        PRINT *, "erreur phyredem : probleme de dimension"
        call abort_physic("", "", 1)
      ENDIF

     ! first pass : definition   
      IF (pass==1) THEN
        
!      ierr = NF90_REDEF (nid_restart)
        ierr = NF90_DEF_VAR (nid_restart, var_name, nf90_format,(/ idim1 /),nvarid)
        IF (LEN_TRIM(title)>0) ierr = NF90_PUT_ATT (nid_restart,nvarid,"title", title)
!      ierr = NF90_ENDDEF(nid_restart)

    ! second pass : write     
      ELSE IF (pass==2) THEN 
        ierr = NF90_INQ_VARID(nid_restart, var_name, nvarid)
        ierr = NF90_PUT_VAR(nid_restart,nvarid,var)
      ENDIF
    ENDIF
    
  END SUBROUTINE put_var_rgen     
    
END MODULE iostart
