!
! $Id: limit_read_mod.f90 5968 2025-12-19 10:46:41Z ymeurdesoif $
!
MODULE limit_read_mod
!
! This module reads the fichier "limit.nc" containing fields for surface forcing.
!
! Module subroutines :
!  limit_read_frac    : call limit_read_tot and return the fractions
!  limit_read_rug_alb : return rugosity and albedo, if coupled ocean call limit_read_tot first
!  limit_read_sst     : return sea ice temperature   
!  limit_read_tot     : read limit.nc and store the fields in local modules variables
!
  IMPLICIT NONE

  REAL, ALLOCATABLE, DIMENSION(:,:), SAVE, PRIVATE :: pctsrf
!$OMP THREADPRIVATE(pctsrf)
  REAL, ALLOCATABLE, DIMENSION(:),   SAVE, PRIVATE :: rugos
!$OMP THREADPRIVATE(rugos)
  REAL, ALLOCATABLE, DIMENSION(:),   SAVE, PRIVATE :: albedo
!$OMP THREADPRIVATE(albedo)  
  REAL, ALLOCATABLE, DIMENSION(:),   SAVE, PRIVATE :: sst
!$OMP THREADPRIVATE(sst) 
!GG
  REAL, ALLOCATABLE, DIMENSION(:),   SAVE, PRIVATE :: sih
!$OMP THREADPRIVATE(sih)
!GG
  LOGICAL,SAVE :: read_continents=.FALSE.
!$OMP THREADPRIVATE(read_continents) 

CONTAINS
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!
!! Public subroutines :
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


  SUBROUTINE init_limit_read(first_day)
  USE mod_grid_phy_lmdz
  USE surface_data
  USE mod_phys_lmdz_para
  USE lmdz_xios

  IMPLICIT NONE
    INTEGER, INTENT(IN) :: first_day
    
    
    IF ( type_ocean /= 'couple') THEN
      IF (grid_type==unstructured) THEN
        IF (is_omp_master) CALL xios_set_file_attr("limit_read",enabled=.TRUE.,record_offset=first_day)
      ENDIF  
    ENDIF

  END SUBROUTINE init_limit_read
  
  SUBROUTINE limit_read_frac(itime, dtime, jour, pctsrf_new, is_modified)
!
! This subroutine is called from "change_srf_frac" for case of 
! ocean=force or from ocean_slab_frac for ocean=slab.
! The fraction for all sub-surfaces at actual time step is returned.

    USE dimphy
    USE indice_sol_mod

! Input arguments
!****************************************************************************************
    INTEGER, INTENT(IN) :: itime   ! time step
    INTEGER, INTENT(IN) :: jour    ! current day
    REAL   , INTENT(IN) :: dtime   ! length of time step
  
! Output arguments
!****************************************************************************************
    REAL, DIMENSION(klon,nbsrf), INTENT(OUT) :: pctsrf_new  ! sub surface fractions
    LOGICAL, INTENT(OUT)                     :: is_modified ! true if pctsrf is modified at this time step

! End declaration
!****************************************************************************************

! 1) Read file limit.nc
    CALL limit_read_tot(itime, dtime, jour, is_modified)

! 2) Return the fraction read in limit_read_tot
    pctsrf_new(:,:) = pctsrf(:,:)
    
  END SUBROUTINE limit_read_frac

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  SUBROUTINE limit_read_rug_alb(itime, dtime, jour, &
       knon, knindex, &
       rugos_out, alb_out)
!$gpum horizontal knon klon
!
! This subroutine is called from surf_land_bucket. 
! The flag "ok_veget" must can not be true. If coupled run, "ocean=couple"
! then this routine will call limit_read_tot.
!
    USE surface_data, ONLY: type_ocean, version_ocean

! Input arguments
!****************************************************************************************
    INTEGER, INTENT(IN) :: itime                     ! numero du pas de temps courant
    INTEGER, INTENT(IN) :: jour                      ! jour a lire dans l'annee
    REAL   , INTENT(IN) :: dtime                     ! pas de temps de la physique (en s)
    INTEGER, INTENT(IN) :: knon                      ! nomber of points on compressed grid
    INTEGER, DIMENSION(knon), INTENT(IN) :: knindex  ! grid point number for compressed grid
! Output arguments
!****************************************************************************************
    REAL, DIMENSION(knon), INTENT(OUT) :: rugos_out
    REAL, DIMENSION(knon), INTENT(OUT) :: alb_out
    
! Local variables
!****************************************************************************************
    INTEGER :: i
    LOGICAL :: is_modified
!****************************************************************************************

    IF (type_ocean == 'couple'.OR. &
         (type_ocean == 'slab' .AND. version_ocean == 'sicINT')) THEN
       ! limit.nc has not yet been read. Do it now!
!$gpum nocall
       CALL limit_read_tot(itime, dtime, jour, is_modified)
    END IF

    DO i=1,knon
       rugos_out(i) = rugos(knindex(i))
       alb_out(i)  = albedo(knindex(i))
    END DO

  END SUBROUTINE limit_read_rug_alb

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  SUBROUTINE limit_read_sst(knon, knindex, sst_out)
!$gpum horizontal knon klon
!
! This subroutine returns the sea surface temperature already read from limit.nc.
!
    USE dimphy, ONLY : klon

    INTEGER, INTENT(IN)                  :: knon     ! nomber of points on compressed grid
    INTEGER, DIMENSION(knon), INTENT(IN) :: knindex  ! grid point number for compressed grid
    REAL, DIMENSION(knon), INTENT(OUT)   :: sst_out

    INTEGER :: i

    DO i = 1, knon
       sst_out(i) = sst(knindex(i))
    END DO

  END SUBROUTINE limit_read_sst

!GG
  SUBROUTINE limit_read_hice(knon, knindex, hice_out)
!$gpum horizontal knon klon
!
! This subroutine returns the sea surface temperature already read from limit.nc.
!
    USE dimphy, ONLY : klon

    INTEGER, INTENT(IN)                  :: knon     ! nomber of points on compressed grid
    INTEGER, DIMENSION(knon), INTENT(IN) :: knindex  ! grid point number for compressed grid
    REAL, DIMENSION(knon), INTENT(OUT)   :: hice_out

    INTEGER :: i

    DO i = 1, knon
       hice_out(i) = sih(knindex(i))
    END DO

  END SUBROUTINE limit_read_hice
!GG
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!
!! Private subroutine :
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  SUBROUTINE limit_read_tot(itime, dtime, jour, is_modified)
!
! Read everything needed from limit.nc
!
! 0) Initialize
! 1) Open the file limit.nc, if it is time
! 2) Read fraction, if not type_ocean=couple
! 3) Read sea surface temperature, if not type_ocean=couple
! 4) Read albedo and rugosity for land surface, only in case of no vegetation model
! 5) Close file and distribuate variables to all processus

    USE dimphy
    USE mod_grid_phy_lmdz
    USE mod_phys_lmdz_para
    !GG USE surface_data, ONLY : type_ocean, ok_veget
    USE surface_data, ONLY : type_ocean, ok_veget, iflag_seaice, amax_n, amax_s
    !GG
    USE netcdf
    USE indice_sol_mod
    USE phys_cal_mod, ONLY : calend, year_len
    USE print_control_mod, ONLY: lunout, prt_level
    USE lmdz_xios, ONLY: xios_recv_field, using_xios
    
    IMPLICIT NONE
    
! In- and ouput arguments
!****************************************************************************************
    INTEGER, INTENT(IN) :: itime   ! numero du pas de temps courant
    INTEGER, INTENT(IN) :: jour    ! jour a lire dans l'annee
    REAL   , INTENT(IN) :: dtime   ! pas de temps de la physique (en s)

    LOGICAL, INTENT(OUT) :: is_modified  ! true if pctsrf is modified at this time step

! Locals variables with attribute SAVE
!****************************************************************************************
! frequence de lecture des conditions limites (en pas de physique) 
    INTEGER,SAVE                              :: lmt_pas
!$OMP THREADPRIVATE(lmt_pas) 
    LOGICAL, SAVE                             :: first_call=.TRUE.
!$OMP THREADPRIVATE(first_call)  
    INTEGER, SAVE                             :: jour_lu = -1
!$OMP THREADPRIVATE(jour_lu)  
! Locals variables
!****************************************************************************************
    INTEGER                                   :: nid, nvarid, ndimid, nn
    INTEGER                                   :: ii, ierr
    INTEGER, DIMENSION(2)                     :: start, epais
    REAL, DIMENSION(klon_glo,nbsrf)           :: pct_glo  ! fraction at global grid
    REAL, DIMENSION(klon_glo)                 :: sst_glo  ! sea-surface temperature at global grid
    REAL, DIMENSION(klon_glo)                 :: rug_glo  ! rugosity at global grid
    REAL, DIMENSION(klon_glo)                 :: alb_glo  ! albedo at global grid

    REAL, DIMENSION(klon_mpi,nbsrf)           :: pct_mpi  ! fraction at global grid
    REAL, DIMENSION(klon_mpi)                 :: sst_mpi  ! sea-surface temperature at global grid
    REAL, DIMENSION(klon_mpi)                 :: rug_mpi  ! rugosity at global grid
    REAL, DIMENSION(klon_mpi)                 :: alb_mpi  ! albedo at global grid
!GG
    REAL, DIMENSION(klon_glo)                 :: sih_glo  ! albedo at global grid
    REAL, DIMENSION(klon_mpi)                 :: sih_mpi  ! albedo at global grid
!GG

    CHARACTER(len=20)                         :: modname='limit_read_mod'     
    CHARACTER(LEN=99)                         :: abort_message, calendar, str

! End declaration
!****************************************************************************************

!****************************************************************************************
! 0) Initialization
!
!****************************************************************************************
    IF (first_call) THEN
       first_call=.FALSE.
       ! calculate number of time steps for one day
       lmt_pas = NINT(86400./dtime * 1.0)
       
       ! Allocate module save variables
       IF ( type_ocean /= 'couple' ) THEN
          ALLOCATE(pctsrf(klon,nbsrf), sst(klon), stat=ierr)
          IF (ierr /= 0) CALL abort_physic(modname, 'PB in allocating pctsrf and sst',1)
       END IF

       !GG
       IF (iflag_seaice==1) THEN
             ALLOCATE(sih(klon), stat=ierr)
             IF (ierr /= 0) CALL abort_physic(modname, 'PB in allocating sih',1)
       ENDIF
       !GG

       IF ( .NOT. ok_veget ) THEN
          ALLOCATE(rugos(klon), albedo(klon), stat=ierr)
          IF (ierr /= 0) CALL abort_physic(modname, 'PB in allocating rugos and albedo',1)
       END IF

!$OMP MASTER  ! Only master thread
       IF (is_mpi_root) THEN ! Only master processus
          ierr = NF90_OPEN ('limit.nc', NF90_NOWRITE, nid)
          IF (ierr /= NF90_NOERR) CALL abort_physic(modname,&
               'Pb d''ouverture du fichier de conditions aux limites',1)

          !--- WARNING IF CALENDAR IS KNOWN AND DOES NOT MATCH THE ONE OF LMDZ
          ierr=NF90_INQ_VARID(nid, 'TEMPS', nvarid)
          ierr=NF90_GET_ATT(nid, nvarid, 'calendar', calendar)
          IF(ierr==NF90_NOERR.AND.calendar/=calend.AND.prt_level>=1) THEN
             WRITE(lunout,*)'BEWARE: gcm and limit.nc calendars differ: '
             WRITE(lunout,*)'  '//TRIM(calend)//' for gcm'
             WRITE(lunout,*)'  '//TRIM(calendar)//' for limit.nc file'
          END IF

          !--- ERROR IF FILE RECORDS NUMBER IS NOT EQUAL TO EXPECTED NUMBER OF DAYS          
          IF (grid_type==unstructured) THEN
            ierr=NF90_INQ_DIMID(nid,"time_year",ndimid)
          ELSE
            ierr=NF90_INQUIRE(nid, UnlimitedDimID=ndimid)
          ENDIF
          ierr=NF90_INQUIRE_DIMENSION(nid, ndimid, len=nn)
          WRITE(abort_message,'(a,2(i0,a))')'limit.nc records number (',nn,') does no'//&
            't match year length (',year_len,')'
          IF(nn/=year_len) CALL abort_physic(modname,abort_message,1)

          !--- ERROR IF FILES AND LMDZ HORIZONTAL RESOLUTIONS DO NOT MATCH
          IF (grid_type==unstructured) THEN
            ierr=NF90_INQ_DIMID(nid, 'cell', ndimid)
          ELSE
            ierr=NF90_INQ_DIMID(nid, 'points_physiques', ndimid)
          ENDIF
          ierr=NF90_INQUIRE_DIMENSION(nid, ndimid, len=nn)
          WRITE(abort_message,'(a,2(i0,a))')'limit.nc horizontal number of cells (',nn, &
            ') does not match LMDZ klon_glo (',klon_glo,')'
          IF(nn/=klon_glo) CALL abort_physic(modname,abort_message,1)

          ierr = NF90_CLOSE(nid)
          IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Pb when closing file', 1)
       END IF ! is_mpi_root
!$OMP END MASTER
!$OMP BARRIER
    END IF

!****************************************************************************************
! 1) Open the file limit.nc if it is the right moment to read, once a day.
!    The file is read only by the master thread of the master mpi process(is_mpi_root)
!    Check by the way if the number of records is correct.
!
!****************************************************************************************

    is_modified = .FALSE.
!ym    IF (MOD(itime-1, lmt_pas) == 0 .OR. jour_lu /= jour ) THEN   ! time to read
!  not REALLY PERIODIC
    IF (MOD(itime-1, lmt_pas) == 0 .OR. (jour_lu /= jour .AND. grid_type /= unstructured)) THEN   ! time to read
!    IF (MOD(itime-1, lmt_pas) == 0) THEN   ! time to read
       jour_lu = jour
       is_modified = .TRUE.

      IF (grid_type==unstructured) THEN

        IF ( type_ocean /= 'couple') THEN

           IF (is_omp_master) CALL xios_recv_field("foce_limin",pct_mpi(:,is_oce))
           IF (is_omp_master) CALL xios_recv_field("fsic_limin",pct_mpi(:,is_sic))
  !         IF (read_continents .OR. itime == 1) THEN
           IF (is_omp_master) CALL xios_recv_field("fter_limin",pct_mpi(:,is_ter))
           IF (is_omp_master) CALL xios_recv_field("flic_limin",pct_mpi(:,is_lic))
  !         ENDIF
         ENDIF! type_ocean /= couple
         
         IF ( type_ocean /= 'couple') THEN                    
             IF (is_omp_master) CALL xios_recv_field("sst_limin",sst_mpi)
             !GG
             IF (iflag_seaice == 1) THEN
               IF (is_omp_master) CALL xios_recv_field("sih_limin",sih_mpi)
             ENDIF
             !GG
         ENDIF
       
         IF (.NOT. ok_veget) THEN
           IF (is_omp_master) CALL xios_recv_field("alb_limin",alb_mpi)
           IF (is_omp_master) CALL xios_recv_field("rug_limin",rug_mpi)
         ENDIF

       IF ( type_ocean /= 'couple') THEN
          CALL Scatter_omp(sst_mpi,sst)
          !GG
          IF (iflag_seaice==1) THEN
            CALL Scatter_omp(sih_mpi,sih)
          ENDIF
          !GG
          CALL Scatter_omp(pct_mpi(:,is_oce),pctsrf(:,is_oce))
          CALL Scatter_omp(pct_mpi(:,is_sic),pctsrf(:,is_sic))
!          IF (read_continents .OR. itime == 1) THEN
             CALL Scatter_omp(pct_mpi(:,is_ter),pctsrf(:,is_ter))
             CALL Scatter_omp(pct_mpi(:,is_lic),pctsrf(:,is_lic))
!          END IF
       END IF

       IF (.NOT. ok_veget) THEN
          CALL Scatter_omp(alb_mpi, albedo)
          CALL Scatter_omp(rug_mpi, rugos)
       END IF
 
     ELSE      ! grid_type==regular

!$OMP MASTER  ! Only master thread
       IF (is_mpi_root) THEN ! Only master processus!

          ierr = NF90_OPEN ('limit.nc', NF90_NOWRITE, nid)
          IF (ierr /= NF90_NOERR) CALL abort_physic(modname,&
               'Pb d''ouverture du fichier de conditions aux limites',1)

          ! La tranche de donnees a lire:
          start(1) = 1
          start(2) = jour
          epais(1) = klon_glo
          epais(2) = 1


!****************************************************************************************
! 2) Read fraction if not type_ocean=couple
!
!****************************************************************************************

          IF ( type_ocean /= 'couple') THEN
!
! Ocean fraction
             ierr = NF90_INQ_VARID(nid, 'FOCE', nvarid)
             IF (ierr /= NF90_NOERR) CALL abort_physic(modname, 'Le champ <FOCE> est absent',1)
             
             ierr = NF90_GET_VAR(nid,nvarid,pct_glo(:,is_oce),start,epais)
             IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Lecture echouee pour <FOCE>' ,1)
!
! Sea-ice fraction
             ierr = NF90_INQ_VARID(nid, 'FSIC', nvarid)
             IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Le champ <FSIC> est absent',1)

             ierr = NF90_GET_VAR(nid,nvarid,pct_glo(:,is_sic),start,epais)
             IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Lecture echouee pour <FSIC>' ,1)

! GG
! Account for leads
             IF (iflag_seaice>0) THEN
               DO ii=1,klon_glo/2
                 if (pct_glo(ii,is_sic)>amax_n) THEN
                    pct_glo(ii,is_oce)=pct_glo(ii,is_oce)+(pct_glo(ii,is_sic)-amax_n)
                    pct_glo(ii,is_sic)=amax_n
                 end if
               ENDDO
               DO ii=klon_glo/2,klon_glo
               if (pct_glo(ii,is_sic)>amax_s) THEN
                    pct_glo(ii,is_oce)=pct_glo(ii,is_oce)+(pct_glo(ii,is_sic)-amax_s)
                    pct_glo(ii,is_sic)=amax_s
               end if
               ENDDO
             ENDIF
!GG

! Read land and continentals fraction only if asked for
             IF (read_continents .OR. itime == 1) THEN
!
! Land fraction
                ierr = NF90_INQ_VARID(nid, 'FTER', nvarid)
                IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Le champ <FTER> est absent',1)
                
                ierr = NF90_GET_VAR(nid,nvarid,pct_glo(:,is_ter),start,epais)
                IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Lecture echouee pour <FTER>',1)
!
! Continentale ice fraction
                ierr = NF90_INQ_VARID(nid, 'FLIC', nvarid)
                IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Le champ <FLIC> est absent',1)

                ierr = NF90_GET_VAR(nid,nvarid,pct_glo(:,is_lic),start,epais)
                IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Lecture echouee pour <FLIC>',1)
             END IF

          END IF ! type_ocean /= couple

!****************************************************************************************
! 3) Read sea-surface temperature, if not coupled ocean
!
!****************************************************************************************
          IF ( type_ocean /= 'couple') THEN

             ierr = NF90_INQ_VARID(nid, 'SST', nvarid)
             IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Le champ <SST> est absent',1)

             ierr = NF90_GET_VAR(nid,nvarid,sst_glo,start,epais)
             IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Lecture echouee pour <SST>',1)
           !GG 
             IF (iflag_seaice == 1) THEN
               ierr = NF90_INQ_VARID(nid, 'HICE', nvarid)
               IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Le champ <HICE> est absent',1)

               ierr = NF90_GET_VAR(nid,nvarid,sih_glo(:),start,epais)
               IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Lecture echouee pour <HICE>' ,1)
             ENDIF
            !GG
          END IF

!****************************************************************************************
! 4) Read albedo and rugosity for land surface, only in case of no vegetation model
!
!****************************************************************************************

          IF (.NOT. ok_veget) THEN
!
! Read albedo
             ierr = NF90_INQ_VARID(nid, 'ALB', nvarid)
             IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Le champ <ALB> est absent',1)

             ierr = NF90_GET_VAR(nid,nvarid,alb_glo,start,epais)
             IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Lecture echouee pour <ALB>',1)
!
! Read rugosity
             ierr = NF90_INQ_VARID(nid, 'RUG', nvarid)
             IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Le champ <RUG> est absent',1)

             ierr = NF90_GET_VAR(nid,nvarid,rug_glo,start,epais)
             IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Lecture echouee pour <RUG>',1)

          END IF

!****************************************************************************************
! 5) Close file and distribuate variables to all processus
!
!****************************************************************************************
          ierr = NF90_CLOSE(nid)
          IF (ierr /= NF90_NOERR) CALL abort_physic(modname,'Pb when closing file', 1)
       ENDIF ! is_mpi_root

!$OMP END MASTER
!$OMP BARRIER

       IF ( type_ocean /= 'couple') THEN
          CALL Scatter(sst_glo,sst)
          !GG
          IF (iflag_seaice==1) THEN
             CALL Scatter(sih_glo,sih)
          END IF
          !GG
          CALL Scatter(pct_glo(:,is_oce),pctsrf(:,is_oce))
          CALL Scatter(pct_glo(:,is_sic),pctsrf(:,is_sic))
          IF (read_continents .OR. itime == 1) THEN
             CALL Scatter(pct_glo(:,is_ter),pctsrf(:,is_ter))
             CALL Scatter(pct_glo(:,is_lic),pctsrf(:,is_lic))
          END IF
       END IF

       IF (.NOT. ok_veget) THEN
          CALL Scatter(alb_glo, albedo)
          CALL Scatter(rug_glo, rugos)
       END IF

      ENDIF ! Grid type

    ENDIF ! time to read

  END SUBROUTINE limit_read_tot

END MODULE limit_read_mod
