Ignore:
Timestamp:
Feb 12, 2026, 9:09:12 AM (2 weeks ago)
Author:
jbclement
Message:

PEM:
Major refactor following the previous ones (r3989 and r3991) completing the large structural reorganization and cleanup of the PEM codebase. This revision introduces newly designed modules, standardizes interfaces with explicit ini/end APIs and adds native NetCDF I/O together with explicit PCM/PEM adapters. In detail:

  • Some PEM models were corrected or improved:
    • Frost/perennial ice semantics are clarified via renaming;
    • Soil temperature remapping clarified, notably by removing the rescaling of temperature deviation;
    • Geothermal flux for the PCM is computed based on the PEM state;
  • New explicit PEM/PCM adapters ("set_*"/"build4PCM_*") to decouple PEM internal representation from PCM file layouts and reconstruct consistent fields returned to the PCM;
  • New explicit build/teardown routines that centralize allocation and initialization ordering, reducing accidental use of uninitialized data and making the model lifecycle explicit;
  • Add native read/write helpers for NetCDF that centralize all low-level NetCDF interactions with major improvements (and more simplicity) compared to legacy PEM/PCM I/O (see the modules "io_netcdf" and "output"). They support reading, creation and writing of "diagevol.nc" (renamed from "diagpem.nc") and start/restart files;
  • Provide well-focused modules ("numerics"/"maths"/"utility"/"display") to host commonly-used primitives:
    • "numerics" defines numerical types and constants for reproducibility, portability across compilers and future transitions (e.g. quadruple precision experiments);
    • "display" provides a single controlled interface for runtime messages, status output and diagnostics, avoiding direct 'print'/'write' to enable silent mode, log redirection, and MPI-safe output in the future.
    • "utility" (new module) hosts generic helpers used throughout the code (e.g. "int2str" or "real2str");
  • Add modules "clim_state_init"/"clim_state_rec" which provide robust read/write logic for "start/startfi/startpem", including 1D fallbacks, mesh conversions and dimension checks. NetCDF file creation is centralized and explicit. Restart files are now self-consistent and future-proof, requiring changes only to affected variables;
  • Add module "atmosphere" which computes pressure fields, reconstructs potential temperature and air mass. It also holds the whole logic to define sigma or hybrid coordinates for altitudes;
  • Add module "geometry" to centrilize dimensions logic and grid conversions routines (including 2 new ones "dyngrd2vect"/"vect2dyngrd");
  • Add module "slopes" to isolate slopes handling;
  • Add module "surface" to isolate surface management. Notably, albedo and emissivity are now fully reconstructed following the PCM settings;
  • Add module "allocation" to check the dimension initialization and centrilize allocation/deallocation;
  • Finalize module decomposition and renaming to consolidate domain-based modules, purpose-based routines and physics/process-based variables;
  • The main program now drives a clearer sequence of conceptual steps (initialization / reading / evolution / update / build / writing) and fails explicitly instead of silently defaulting;
  • Ice table logic is made restart-safe;
  • 'Open'/'read' intrinsic logic is made safe and automatic;
  • Improve discoverability and standardize the data handling (private vs protected vs public);
  • Apply consistent documentation/header style already introduced;
  • Update deftank scripts to reflect new names and launch wrappers;

This revision is a structural milestone aiming to be behavior-preserving where possible. It has been tested via compilation and short integration runs. However, due to extensive renames, moves, and API changes, full validation is still ongoing.
Note: the revision includes one (possibly two) easter egg hidden in the code for future archaeologists of the PEM. No physics were harmed.
JBC

File:
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/LMDZ.COMMON/libf/evolution/io_netcdf.F90

    r4064 r4065  
    1 MODULE iostart_pem
    2 !-----------------------------------------------------------------------
    3 ! NAME
    4 !     iostart_pem
    5 !
    6 ! DESCRIPTION
    7 !     Read and write specific netcdf files for the PEM (start and restart)
    8 !     Provides interfaces for field and variable I/O operations
    9 !
    10 ! AUTHORS & DATE
    11 !     L. Lange
    12 !     JB Clement, 2023-2025
    13 !
    14 ! NOTES
    15 !     Inspired by iostart from the PCM.
    16 !
    17 !-----------------------------------------------------------------------
    18 
    19     ! DECLARATION
    20 ! -----------
    21 implicit none
    22 
    23 ! MODULE VARIABLES
    24 ! ----------------
    25 PRIVATE
    26     INTEGER,SAVE :: nid_start ! NetCDF file identifier for startfi.nc file
    27     INTEGER,SAVE :: nid_restart ! NetCDF file identifier for restartfi.nc file
    28 
    29     ! restartfi.nc file dimension identifiers: (see open_restartphy())
    30     INTEGER,SAVE :: idim1  ! "index" dimension
    31     INTEGER,SAVE :: idim2  ! "physical_points" dimension
    32     INTEGER,SAVE :: idim3  ! "subsurface_layers" dimension
    33     INTEGER,SAVE :: idim4  ! "nlayer_plus_1" dimension
    34     INTEGER,SAVE :: idim5  ! "number_of_advected_fields" dimension
    35     INTEGER,SAVE :: idim6  ! "nlayer" dimension
    36     INTEGER,SAVE :: idim7  ! "Time" dimension
    37     INTEGER,SAVE :: idim8  ! "nslope" dimension
    38     INTEGER,SAVE :: idim9  ! "inter slope" dimension
    39     INTEGER,SAVE :: idim10 ! "nb_str_max" dimension
    40     INTEGER,SAVE :: timeindex ! current time index (for time-dependent fields)
    41     INTEGER,PARAMETER :: length=100 ! size of tab_cntrl array
    42 
    43     INTERFACE get_field
    44       MODULE PROCEDURE Get_field_r1,Get_field_r2,Get_field_r3
    45     END INTERFACE get_field
    46 
    47     INTERFACE get_var
    48       MODULE PROCEDURE get_var_r0,Get_var_r1,Get_var_r2,Get_var_r3
    49     END INTERFACE get_var
    50 
    51     INTERFACE put_field
    52       MODULE PROCEDURE put_field_r1,put_field_r2,put_field_r3
    53     END INTERFACE put_field
    54 
    55     INTERFACE put_var
    56       MODULE PROCEDURE put_var_r0,put_var_r1,put_var_r2,put_var_r3
    57     END INTERFACE put_var
    58 
    59     PUBLIC nid_start, length
    60     PUBLIC get_field,get_var,put_field,put_var
    61     PUBLIC inquire_dimension, inquire_dimension_length
    62     PUBLIC inquire_field, inquire_field_ndims
    63     PUBLIC open_startphy,close_startphy,open_restartphy,close_restartphy
    64 
    65 CONTAINS
     1MODULE io_netcdf
     2!-----------------------------------------------------------------------
     3! NAME
     4!     io_netcdf
     5!
     6! DESCRIPTION
     7!     Provides input/output procedures and parameters for the PEM.
     8!
     9! AUTHORS & DATE
     10!     JB Clement, 12/2025
     11!
     12! NOTES
     13!
     14!-----------------------------------------------------------------------
     15
     16! DEPEDENCIES
     17! -----------
     18use numerics, only: dp, di, k4
     19use netcdf,   only: nf90_double, nf90_noerr, nf90_strerror, nf90_write, nf90_nowrite,                &
     20                    nf90_open, nf90_close, nf90_redef, nf90_enddef, nf90_inquire, nf90_max_var_dims, &
     21                    nf90_inq_dimid, nf90_inquire_dimension, nf90_inq_varid, nf90_inquire_variable,   &
     22                    nf90_def_var, nf90_get_var, nf90_put_var, nf90_get_att, nf90_put_att
     23use stoppage, only: stop_clean
     24
     25! DECLARATION
     26! -----------
     27implicit none
     28
     29! PARAMETERS
     30! ----------
     31character(8),  parameter :: start_name     = 'start.nc'
     32character(11), parameter :: start1D_name   = 'start1D.txt'
     33character(10), parameter :: startfi_name   = 'startfi.nc'
     34character(11), parameter :: startpem_name  = 'startpem.nc'
     35character(19), parameter :: xios_day_name1 = 'Xoutdaily4pem_Y1.nc'  ! XIOS daily output file, second to last PCM year
     36character(19), parameter :: xios_day_name2 = 'Xoutdaily4pem_Y2.nc'  ! XIOS daily output file, last PCM year
     37character(20), parameter :: xios_yr_name1  = 'Xoutyearly4pem_Y1.nc' ! XIOS yearly output file, second to last PCM year
     38character(20), parameter :: xios_yr_name2  = 'Xoutyearly4pem_Y2.nc' ! XIOS yearly output file, last PCM year
     39character(11), parameter :: diagevol_name  = 'diagevol.nc'
     40
     41! VARIABLES
     42! ---------
     43logical(k4), private :: open_ncfile = .false.   ! Flag to true if a NetCDF file is already open
     44logical(k4), private :: open_diagevol = .false. ! Flag to true if "diagevol.nc" is already open
     45integer(di), private :: ncid_file               ! File ID
     46integer(di), private :: varid                   ! Variable ID
     47integer(di)          :: ncid_diagevol           ! File ID specific to "diagevol.nc"
     48
     49! INTERFACES
     50! ----------
     51interface get_var_nc
     52    module procedure get_var0d_nc, get_var1d_nc, get_var2d_nc, get_var3d_nc, get_var4d_nc
     53end interface get_var_nc
     54
     55interface put_var_nc
     56    module procedure put_var0d_nc, put_var1d_nc, put_var2d_nc, put_var3d_nc, put_var4d_nc
     57end interface put_var_nc
     58
     59interface check_valid_var_nc
     60    module procedure check_valid_var0d_nc, check_valid_var1d_nc, check_valid_var2d_nc, check_valid_var3d_nc, check_valid_var4d_nc
     61end interface check_valid_var_nc
     62
     63contains
    6664!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    6765
    6866!=======================================================================
    69   SUBROUTINE open_startphy(filename)
    70 !-----------------------------------------------------------------------
    71 ! NAME
    72 !     open_startphy
    73 !
    74 ! DESCRIPTION
    75 !     Open the startphy netCDF file for reading.
    76 
    77 ! AUTHORS & DATE
    78 !     L. Lange
    79 !     JB Clement, 2023-2025
    80 
     67SUBROUTINE check_nc(ierr,msg,found)
     68!-----------------------------------------------------------------------
     69! NAME
     70!     check_nc
     71!
     72! DESCRIPTION
     73!     NetCDF error handler.
     74!
     75! AUTHORS & DATE
     76!     JB Clement, 12/2025
     77!
    8178! NOTES
    8279!
     
    8582! DEPENDENCIES
    8683! ------------
    87   USE netcdf,             only: NF90_OPEN, NF90_NOERR, NF90_NOWRITE, nf90_strerror
    88   USE mod_phys_lmdz_para, only: is_master, bcast
    89 
    90 ! DECLARATION
    91 ! -----------
    92 implicit none
    93 
    94 ! ARGUMENTS
    95 ! ---------
    96     CHARACTER(LEN=*) :: filename
     84use display, only: print_err
     85
     86! ARGUMENTS
     87! ---------
     88integer(di),  intent(in)            :: ierr
     89character(*), intent(in)            :: msg
     90logical(k4),  intent(out), optional :: found
    9791
    9892! LOCAL VARIABLES
    9993! ---------------
    100     INTEGER :: ierr
    101 
    102 ! CODE
    103 ! ----
    104     IF (is_master) THEN
    105       ierr = NF90_OPEN (filename, NF90_NOWRITE, nid_start)
    106       IF (ierr.NE.NF90_NOERR) THEN
    107         write(*,*)'open_startphy: problem opening file '//trim(filename)
    108         write(*,*)trim(nf90_strerror(ierr))
    109         CALL abort_physic("open_startphy","Cannot open file",1)
    110       ENDIF
    111     ENDIF
    112 
    113     CALL bcast(nid_start) ! tell all procs about nid_start
    114 
    115   END SUBROUTINE open_startphy
    116 !=======================================================================
    117 
    118 !=======================================================================
    119   SUBROUTINE close_startphy
    120 !-----------------------------------------------------------------------
    121 ! NAME
    122 !     close_startphy
    123 !
    124 ! DESCRIPTION
    125 !     Close the startphy netCDF file.
    126 
    127 ! AUTHORS & DATE
    128 !     L. Lange
    129 !     JB Clement, 2023-2025
    130 
     94logical(k4) :: tmp_found
     95
     96! CODE
     97! ----
     98if (ierr == nf90_noerr) then
     99    tmp_found = .true.
     100else
     101    tmp_found = .false.
     102end if
     103
     104if (present(found)) then
     105    found = tmp_found
     106else
     107    if (.not. tmp_found) then
     108        call print_err(trim(nf90_strerror(ierr)))
     109        call stop_clean(__FILE__,__LINE__,'NetCDF error when '//trim(msg),ierr)
     110    end if
     111end if
     112
     113END SUBROUTINE check_nc
     114!=======================================================================
     115
     116!=======================================================================
     117SUBROUTINE open_nc(filename,mode,itime)
     118!-----------------------------------------------------------------------
     119! NAME
     120!     open_nc
     121!
     122! DESCRIPTION
     123!     Open a netCDF file for reading.
     124
     125! AUTHORS & DATE
     126!     JB Clement, 12/2025
     127
     128! NOTES
     129!
     130!-----------------------------------------------------------------------
     131
     132! DECLARATION
     133! -----------
     134implicit none
     135
     136! ARGUMENTS
     137! ---------
     138character(*), intent(in)            :: filename, mode
     139integer(di),  intent(out), optional :: itime
     140
     141! CODE
     142! ----
     143! Diagevol logic
     144if (adjustl(trim(filename)) == diagevol_name) then
     145    if (adjustl(trim(mode)) == 'read') then
     146        call stop_clean(__FILE__,__LINE__,'opening mode "read" cannot be used with '//trim(filename)//'"!',1)
     147    else if (adjustl(trim(mode)) == 'write') then
     148        if (.not. open_diagevol) then
     149            call check_nc(nf90_open(trim(filename),nf90_write,ncid_diagevol),'opening file '//trim(filename)//' to write')
     150            if (present(itime)) call get_next_itime_nc('Time',itime) ! Next time index
     151            open_diagevol = .true.
     152        end if
     153        return
     154    else
     155        call stop_clean(__FILE__,__LINE__,'opening mode "'//mode//'" unknown!',1)
     156    end if
     157end if
     158
     159! Standard logic
     160if (open_ncfile) then ! A file is already opened
     161    call stop_clean(__FILE__,__LINE__,'a NetCDF file is already opened!',1)
     162else
     163    if (adjustl(trim(mode)) == 'read') then
     164        call check_nc(nf90_open(trim(filename),nf90_nowrite,ncid_file),'opening file '//trim(filename)//' to read')
     165    else if (adjustl(trim(mode)) == 'write') then
     166        call check_nc(nf90_open(trim(filename),nf90_write,ncid_file),'opening file '//trim(filename)//' to write')
     167        if (present(itime)) call get_next_itime_nc('Time',itime) ! Next time index
     168    else
     169        call stop_clean(__FILE__,__LINE__,'opening mode "'//mode//'" unknown!',1)
     170    end if
     171    open_ncfile = .true.
     172end if
     173
     174END SUBROUTINE open_nc
     175!=======================================================================
     176
     177!=======================================================================
     178SUBROUTINE close_nc(filename)
     179!-----------------------------------------------------------------------
     180! NAME
     181!     close_nc
     182!
     183! DESCRIPTION
     184!     Open a netCDF file.
     185
     186! AUTHORS & DATE
     187!     JB Clement, 12/2025
     188
     189! NOTES
     190!
     191!-----------------------------------------------------------------------
     192
     193! DECLARATION
     194! -----------
     195implicit none
     196
     197! ARGUMENTS
     198! ---------
     199character(*), intent(in) :: filename
     200
     201! CODE
     202! ----
     203if (adjustl(trim(filename)) == diagevol_name) then ! Diagevol logic
     204    call check_nc(nf90_close(ncid_diagevol),'closing file '//trim(filename))
     205    open_diagevol = .false.
     206else ! Standard logic
     207    call check_nc(nf90_close(ncid_file),'closing file '//trim(filename))
     208    open_ncfile = .false.
     209end if
     210
     211END SUBROUTINE close_nc
     212!=======================================================================
     213
     214!=======================================================================
     215SUBROUTINE get_dim_nc(dim_name,d,found)
     216!-----------------------------------------------------------------------
     217! NAME
     218!     get_dim_nc
     219!
     220! DESCRIPTION
     221!     Read a 0D variable from open NetCDF file.
     222!
     223! AUTHORS & DATE
     224!     JB Clement, 12/2025
     225!
     226! NOTES
     227!
     228!-----------------------------------------------------------------------
     229
     230! DECLARATION
     231! -----------
     232implicit none
     233
     234! ARGUMENTS
     235! ---------
     236character(*), intent(in)            :: dim_name ! Variable name
     237integer(di),  intent(out)           :: d        ! Output dimension
     238logical(k4),  intent(out), optional :: found
     239
     240! LOCAL VARIABLES
     241! ---------------
     242integer(di) :: dimid ! Dimension ID
     243
     244! CODE
     245! ----
     246if (present(found)) then
     247    call check_nc(nf90_inq_dimid(ncid_file,dim_name,dimid),'inquiring '//dim_name,found)
     248    call check_nc(nf90_inquire_dimension(ncid_file,dimid,len = d),'getting '//dim_name,found)
     249else
     250    call check_nc(nf90_inq_dimid(ncid_file,dim_name,dimid),'inquiring '//dim_name)
     251    call check_nc(nf90_inquire_dimension(ncid_file,dimid,len = d),'getting '//dim_name)
     252end if
     253
     254END SUBROUTINE get_dim_nc
     255!=======================================================================
     256
     257!=======================================================================
     258SUBROUTINE def_var_nc(var_name,title,units,dimids)
     259!-----------------------------------------------------------------------
     260! NAME
     261!     def_var_nc
     262!
     263! DESCRIPTION
     264!     Define a variable into open NetCDF file.
     265!
     266! AUTHORS & DATE
     267!     JB Clement, 01/2026
     268!
     269! NOTES
     270!
     271!-----------------------------------------------------------------------
     272
     273! DECLARATION
     274! -----------
     275implicit none
     276
     277! ARGUMENTS
     278! ---------
     279character(*),              intent(in) :: var_name, title, units ! Variable|title|units name
     280integer(di), dimension(:), intent(in) :: dimids                 ! Dimensions IDs
     281
     282! LOCAL VARIABLES
     283! ---------------
     284logical(k4) :: found
     285integer(di) :: ncid
     286
     287! CODE
     288! ----
     289! Diagevol logic priority over standard logic
     290if (open_diagevol) then
     291    ncid = ncid_diagevol
     292else
     293    ncid = ncid_file
     294end if
     295
     296! Check if the variable exists
     297call check_nc(nf90_inq_varid(ncid,var_name,varid),'inquiring '//var_name,found)
     298if (found) return ! Variable is already defined
     299
     300! Enter define mode
     301call check_nc(nf90_redef(ncid),'entering define mode')
     302
     303! Define variable
     304call check_nc(nf90_def_var(ncid,var_name,NF90_DOUBLE,dimids,varid),'defining variable '//var_name)
     305call check_nc(nf90_put_att(ncid,varid,'title',title),'putting title attribute for '//var_name)
     306call check_nc(nf90_put_att(ncid,varid,'units',units),'putting units attribute for '//var_name)
     307
     308! Leave define mode
     309call check_nc(nf90_enddef(ncid),'leaving define mode')
     310
     311END SUBROUTINE def_var_nc
     312!=======================================================================
     313
     314!=======================================================================
     315SUBROUTINE get_var0d_nc(var_name,var,found)
     316!-----------------------------------------------------------------------
     317! NAME
     318!     get_var0d_nc
     319!
     320! DESCRIPTION
     321!     Read a 0D variable from open NetCDF file.
     322!
     323! AUTHORS & DATE
     324!     JB Clement, 12/2025
     325!
     326! NOTES
     327!
     328!-----------------------------------------------------------------------
     329
     330! DECLARATION
     331! -----------
     332implicit none
     333
     334! ARGUMENTS
     335! ---------
     336character(*), intent(in)            :: var_name ! Variable name
     337real(dp),     intent(out)           :: var      ! Output variable
     338logical(k4),  intent(out), optional :: found
     339
     340! CODE
     341! ----
     342if (present(found)) then
     343    call check_nc(nf90_inq_varid(ncid_file,var_name,varid),'inquiring '//var_name,found)
     344    call check_nc(nf90_get_var(ncid_file,varid,var),'getting '//var_name,found)
     345else
     346    call check_nc(nf90_inq_varid(ncid_file,var_name,varid),'inquiring '//var_name)
     347    call check_nc(nf90_get_var(ncid_file,varid,var),'getting '//var_name)
     348end if
     349call check_valid_var_nc(var_name,var)
     350
     351END SUBROUTINE get_var0d_nc
     352!=======================================================================
     353
     354!=======================================================================
     355SUBROUTINE get_var1d_nc(var_name,var,found)
     356!-----------------------------------------------------------------------
     357! NAME
     358!     get_var1d_nc
     359!
     360! DESCRIPTION
     361!     Read a 1D variable from open NetCDF file.
     362!
     363! AUTHORS & DATE
     364!     JB Clement, 12/2025
     365!
     366! NOTES
     367!
     368!-----------------------------------------------------------------------
     369
     370! DECLARATION
     371! -----------
     372implicit none
     373
     374! ARGUMENTS
     375! ---------
     376character(*),           intent(in)            :: var_name ! Variable name
     377real(dp), dimension(:), intent(out)           :: var      ! Output variable
     378logical(k4),            intent(out), optional :: found
     379
     380! CODE
     381! ----
     382if (present(found)) then
     383    call check_nc(nf90_inq_varid(ncid_file,var_name,varid),'inquiring '//var_name,found)
     384    call check_nc(nf90_get_var(ncid_file,varid,var),'getting '//var_name,found)
     385else
     386    call check_nc(nf90_inq_varid(ncid_file,var_name,varid),'inquiring '//var_name)
     387    call check_nc(nf90_get_var(ncid_file,varid,var),'getting '//var_name)
     388end if
     389call check_valid_var_nc(var_name,var)
     390
     391END SUBROUTINE get_var1d_nc
     392!=======================================================================
     393
     394!=======================================================================
     395SUBROUTINE get_var2d_nc(var_name,var,found)
     396!-----------------------------------------------------------------------
     397! NAME
     398!     get_var2d_nc
     399!
     400! DESCRIPTION
     401!     Read a 2D variable from open NetCDF file.
     402!
     403! AUTHORS & DATE
     404!     JB Clement, 12/2025
     405!
     406! NOTES
     407!
     408!-----------------------------------------------------------------------
     409
     410! DECLARATION
     411! -----------
     412implicit none
     413
     414! ARGUMENTS
     415! ---------
     416character(*),             intent(in)            :: var_name ! Variable name
     417real(dp), dimension(:,:), intent(out)           :: var      ! Output variable
     418logical(k4),              intent(out), optional :: found
     419
     420! CODE
     421! ----
     422if (present(found)) then
     423    call check_nc(nf90_inq_varid(ncid_file,var_name,varid),'inquiring '//var_name,found)
     424    call check_nc(nf90_get_var(ncid_file,varid,var),'getting '//var_name,found)
     425else
     426    call check_nc(nf90_inq_varid(ncid_file,var_name,varid),'inquiring '//var_name)
     427    call check_nc(nf90_get_var(ncid_file,varid,var),'getting '//var_name)
     428end if
     429call check_valid_var_nc(var_name,var)
     430
     431END SUBROUTINE get_var2d_nc
     432!=======================================================================
     433
     434!=======================================================================
     435SUBROUTINE get_var3d_nc(var_name,var,found)
     436!-----------------------------------------------------------------------
     437! NAME
     438!     get_var3d_nc
     439!
     440! DESCRIPTION
     441!     Read a 3D variable from open NetCDF file.
     442!
     443! AUTHORS & DATE
     444!     JB Clement, 12/2025
     445!
     446! NOTES
     447!
     448!-----------------------------------------------------------------------
     449
     450! DECLARATION
     451! -----------
     452implicit none
     453
     454! ARGUMENTS
     455! ---------
     456character(*),               intent(in)            :: var_name ! Variable name
     457real(dp), dimension(:,:,:), intent(out)           :: var      ! Output variable
     458logical(k4),                intent(out), optional :: found
     459
     460! CODE
     461! ----
     462if (present(found)) then
     463    call check_nc(nf90_inq_varid(ncid_file,var_name,varid),'inquiring '//var_name,found)
     464    call check_nc(nf90_get_var(ncid_file,varid,var),'getting '//var_name,found)
     465else
     466    call check_nc(nf90_inq_varid(ncid_file,var_name,varid),'inquiring '//var_name)
     467    call check_nc(nf90_get_var(ncid_file,varid,var),'getting '//var_name)
     468end if
     469call check_valid_var_nc(var_name,var)
     470
     471END SUBROUTINE get_var3d_nc
     472!=======================================================================
     473
     474!=======================================================================
     475SUBROUTINE get_var4d_nc(var_name,var,found)
     476!-----------------------------------------------------------------------
     477! NAME
     478!     get_var4d_nc
     479!
     480! DESCRIPTION
     481!     Read a 4D variable from open NetCDF file.
     482!
     483! AUTHORS & DATE
     484!     JB Clement, 12/2025
     485!
     486! NOTES
     487!
     488!-----------------------------------------------------------------------
     489
     490! DECLARATION
     491! -----------
     492implicit none
     493
     494! ARGUMENTS
     495! ---------
     496character(*),                 intent(in)            :: var_name ! Variable name
     497real(dp), dimension(:,:,:,:), intent(out)           :: var      ! Output variable
     498logical(k4),                  intent(out), optional :: found
     499
     500! CODE
     501! ----
     502if (present(found)) then
     503    call check_nc(nf90_inq_varid(ncid_file,var_name,varid),'inquiring '//var_name,found)
     504    call check_nc(nf90_get_var(ncid_file,varid,var),'getting '//var_name,found)
     505else
     506    call check_nc(nf90_inq_varid(ncid_file,var_name,varid),'inquiring '//var_name)
     507    call check_nc(nf90_get_var(ncid_file,varid,var),'getting '//var_name)
     508end if
     509call check_valid_var_nc(var_name,var)
     510
     511END SUBROUTINE get_var4d_nc
     512!=======================================================================
     513
     514!=======================================================================
     515SUBROUTINE put_var0d_nc(var_name,var,itime)
     516!-----------------------------------------------------------------------
     517! NAME
     518!     put_var0d_nc
     519!
     520! DESCRIPTION
     521!     Write a 0D variable into open NetCDF file.
     522!
     523! AUTHORS & DATE
     524!     JB Clement, 01/2026
     525!
     526! NOTES
     527!
     528!-----------------------------------------------------------------------
     529
     530! DECLARATION
     531! -----------
     532implicit none
     533
     534! ARGUMENTS
     535! ---------
     536character(*), intent(in)           :: var_name ! Variable name
     537real(dp),     intent(in)           :: var      ! Input variable
     538integer(di),  intent(in), optional :: itime    ! Current time index
     539
     540! LOCAL VARIABLES
     541! ---------------
     542integer(di)                               :: ndims, unlimdimid, ncid
     543integer(di), dimension(nf90_max_var_dims) :: dimids
     544logical(k4)                               :: has_time
     545
     546! CODE
     547! ----
     548! Diagevol logic priority over standard logic
     549if (open_diagevol) then
     550    ncid = ncid_diagevol
     551else
     552    ncid = ncid_file
     553end if
     554
     555! Check if the variable holds a Time dimension (unlimited dim)
     556call check_nc(nf90_inq_varid(ncid,var_name,varid),'inquiring '//var_name)
     557call check_nc(nf90_inquire_variable(ncid,varid,ndims = ndims,dimids = dimids),'inquiring dims of '//var_name)
     558if (ndims > 0) then
     559    call check_nc(nf90_inquire(ncid,unlimitedDimId = unlimdimid),'inquiring unlimited dim')
     560    has_time = (dimids(ndims) == unlimdimid)
     561else
     562    has_time = .false.
     563end if
     564
     565if (present(itime)) then ! Time-dependent write
     566    if (.not. has_time) call stop_clean(__FILE__,__LINE__,'itime present but variable has no Time dimension: '//var_name,1)
     567    ! For 0D variable with time, just write at the time index
     568    call check_nc(nf90_put_var(ncid,varid,var,start = (/itime/)),'putting '//var_name)
     569else ! Static write
     570    if (has_time) call stop_clean(__FILE__,__LINE__,'itime absent but variable is time-dependent: '//var_name,1)
     571    call check_nc(nf90_put_var(ncid,varid,var),'putting '//var_name)
     572end if
     573
     574END SUBROUTINE put_var0d_nc
     575!=======================================================================
     576
     577!=======================================================================
     578SUBROUTINE put_var1d_nc(var_name,var,itime)
     579!-----------------------------------------------------------------------
     580! NAME
     581!     put_var1d_nc
     582!
     583! DESCRIPTION
     584!     Write a 1D variable into open NetCDF file.
     585!
     586! AUTHORS & DATE
     587!     JB Clement, 01/2026
     588!
     589! NOTES
     590!
     591!-----------------------------------------------------------------------
     592
     593! DECLARATION
     594! -----------
     595implicit none
     596
     597! ARGUMENTS
     598! ---------
     599character(*),           intent(in)           :: var_name ! Variable name
     600real(dp), dimension(:), intent(in)           :: var      ! Input variable
     601integer(di),            intent(in), optional :: itime    ! Current time index
     602
     603! LOCAL VARIABLES
     604! ---------------
     605integer(di)                               :: i, ndims, unlimdimid, ncid
     606integer(di), dimension(nf90_max_var_dims) :: dimids
     607integer(di), dimension(:), allocatable    :: strt, cnt
     608logical(k4)                               :: has_time
     609
     610! CODE
     611! ----
     612! Diagevol logic priority over standard logic
     613if (open_diagevol) then
     614    ncid = ncid_diagevol
     615else
     616    ncid = ncid_file
     617end if
     618
     619! Check if the variable holds a Time dimension (unlimited dim)
     620call check_nc(nf90_inq_varid(ncid,var_name,varid),'inquiring '//var_name)
     621call check_nc(nf90_inquire_variable(ncid,varid,ndims = ndims,dimids = dimids),'inquiring dims of '//var_name)
     622if (ndims > 0) then
     623    call check_nc(nf90_inquire(ncid,unlimitedDimId = unlimdimid),'inquiring unlimited dim')
     624    has_time = (dimids(ndims) == unlimdimid)
     625else
     626    has_time = .false.
     627end if
     628
     629if (present(itime)) then ! Time-dependent write
     630    if (.not. has_time) call stop_clean(__FILE__,__LINE__,'itime present but variable has no Time dimension: '//var_name,1)
     631    allocate(strt(ndims),cnt(ndims))
     632    strt(:) = 1
     633    cnt(:) = 1
     634    strt(ndims) = itime
     635    do i = 1,ndims - 1
     636        cnt(i) = size(var,i)
     637    end do
     638    call check_nc(nf90_put_var(ncid,varid,var,start = strt,count = cnt),'putting '//var_name)
     639    deallocate(strt,cnt)
     640else ! Static write
     641    if (has_time) call stop_clean(__FILE__,__LINE__,'itime absent but variable is time-dependent: '//var_name,1)
     642    call check_nc(nf90_put_var(ncid,varid,var),'putting '//var_name)
     643end if
     644
     645END SUBROUTINE put_var1d_nc
     646!=======================================================================
     647
     648!=======================================================================
     649SUBROUTINE put_var2d_nc(var_name,var,itime)
     650!-----------------------------------------------------------------------
     651! NAME
     652!     put_var2d_nc
     653!
     654! DESCRIPTION
     655!     Write a 2D variable into open NetCDF file.
     656!
     657! AUTHORS & DATE
     658!     JB Clement, 01/2026
     659!
     660! NOTES
     661!
     662!-----------------------------------------------------------------------
     663
     664! DECLARATION
     665! -----------
     666implicit none
     667
     668! ARGUMENTS
     669! ---------
     670character(*),             intent(in)           :: var_name ! Variable name
     671real(dp), dimension(:,:), intent(in)           :: var      ! Input variable
     672integer(di),              intent(in), optional :: itime    ! Current time index
     673
     674! LOCAL VARIABLES
     675! ---------------
     676integer(di)                               :: i, ndims, unlimdimid, ncid
     677integer(di), dimension(nf90_max_var_dims) :: dimids
     678integer(di), dimension(:), allocatable    :: strt, cnt
     679logical(k4)                               :: has_time
     680
     681! CODE
     682! ----
     683! Diagevol logic priority over standard logic
     684if (open_diagevol) then
     685    ncid = ncid_diagevol
     686else
     687    ncid = ncid_file
     688end if
     689
     690! Check if the variable holds a Time dimension (unlimited dim)
     691call check_nc(nf90_inq_varid(ncid,var_name,varid),'inquiring '//var_name)
     692call check_nc(nf90_inquire_variable(ncid,varid,ndims = ndims,dimids = dimids),'inquiring dims of '//var_name)
     693if (ndims > 0) then
     694    call check_nc(nf90_inquire(ncid,unlimitedDimId = unlimdimid),'inquiring unlimited dim')
     695    has_time = (dimids(ndims) == unlimdimid)
     696else
     697    has_time = .false.
     698end if
     699
     700if (present(itime)) then ! Time-dependent write
     701    if (.not. has_time) call stop_clean(__FILE__,__LINE__,'itime present but variable has no Time dimension: '//var_name,1)
     702    allocate(strt(ndims),cnt(ndims))
     703    strt(:) = 1
     704    cnt(:) = 1
     705    strt(ndims) = itime
     706    do i = 1,ndims - 1
     707        cnt(i) = size(var,i)
     708    end do
     709    call check_nc(nf90_put_var(ncid,varid,var,start = strt,count = cnt),'putting '//var_name)
     710    deallocate(strt,cnt)
     711else ! Static write
     712    if (has_time) call stop_clean(__FILE__,__LINE__,'itime absent but variable is time-dependent: '//var_name,1)
     713    call check_nc(nf90_put_var(ncid,varid,var),'putting '//var_name)
     714end if
     715
     716END SUBROUTINE put_var2d_nc
     717!=======================================================================
     718
     719!=======================================================================
     720SUBROUTINE put_var3d_nc(var_name,var,itime)
     721!-----------------------------------------------------------------------
     722! NAME
     723!     put_var3d_nc
     724!
     725! DESCRIPTION
     726!     Write a 3D variable into open NetCDF file.
     727!
     728! AUTHORS & DATE
     729!     JB Clement, 01/2026
     730!
     731! NOTES
     732!
     733!-----------------------------------------------------------------------
     734
     735! DECLARATION
     736! -----------
     737implicit none
     738
     739! ARGUMENTS
     740! ---------
     741character(*),               intent(in)           :: var_name ! Variable name
     742real(dp), dimension(:,:,:), intent(in)           :: var      ! Input variable
     743integer(di),                intent(in), optional :: itime    ! Current time index
     744
     745! LOCAL VARIABLES
     746! ---------------
     747integer(di)                               :: i, ndims, unlimdimid, ncid
     748integer(di), dimension(nf90_max_var_dims) :: dimids
     749integer(di), dimension(:), allocatable    :: strt, cnt
     750logical(k4)                               :: has_time
     751
     752! CODE
     753! ----
     754! Diagevol logic priority over standard logic
     755if (open_diagevol) then
     756    ncid = ncid_diagevol
     757else
     758    ncid = ncid_file
     759end if
     760
     761! Check if the variable holds a Time dimension (unlimited dim)
     762call check_nc(nf90_inq_varid(ncid,var_name,varid),'inquiring '//var_name)
     763call check_nc(nf90_inquire_variable(ncid,varid,ndims = ndims,dimids = dimids),'inquiring dims of '//var_name)
     764if (ndims > 0) then
     765    call check_nc(nf90_inquire(ncid,unlimitedDimId = unlimdimid),'inquiring unlimited dim')
     766    has_time = (dimids(ndims) == unlimdimid)
     767else
     768    has_time = .false.
     769end if
     770
     771if (present(itime)) then ! Time-dependent write
     772    if (.not. has_time) call stop_clean(__FILE__,__LINE__,'itime present but variable has no Time dimension: '//var_name,1)
     773    allocate(strt(ndims),cnt(ndims))
     774    strt(:) = 1
     775    cnt(:) = 1
     776    strt(ndims) = itime
     777    do i = 1,ndims - 1
     778        cnt(i) = size(var,i)
     779    end do
     780    call check_nc(nf90_put_var(ncid,varid,var,start = strt,count = cnt),'putting '//var_name)
     781    deallocate(strt,cnt)
     782else ! Static write
     783    if (has_time) call stop_clean(__FILE__,__LINE__,'itime absent but variable is time-dependent: '//var_name,1)
     784    call check_nc(nf90_put_var(ncid,varid,var),'putting '//var_name)
     785end if
     786
     787END SUBROUTINE put_var3d_nc
     788!=======================================================================
     789
     790!=======================================================================
     791SUBROUTINE put_var4d_nc(var_name,var,itime)
     792!-----------------------------------------------------------------------
     793! NAME
     794!     put_var4d_nc
     795!
     796! DESCRIPTION
     797!     Write a 4D variable into open NetCDF file.
     798!
     799! AUTHORS & DATE
     800!     JB Clement, 01/2026
     801!
     802! NOTES
     803!
     804!-----------------------------------------------------------------------
     805
     806! DECLARATION
     807! -----------
     808implicit none
     809
     810! ARGUMENTS
     811! ---------
     812character(*),                 intent(in)           :: var_name ! Variable name
     813real(dp), dimension(:,:,:,:), intent(in)           :: var      ! Input variable
     814integer(di),                  intent(in), optional :: itime    ! Current time index
     815
     816! LOCAL VARIABLES
     817! ---------------
     818integer(di)                               :: i, ndims, unlimdimid, ncid
     819integer(di), dimension(nf90_max_var_dims) :: dimids
     820integer(di), dimension(:), allocatable    :: strt, cnt
     821logical(k4)                               :: has_time
     822
     823! CODE
     824! ----
     825! Diagevol logic priority over standard logic
     826if (open_diagevol) then
     827    ncid = ncid_diagevol
     828else
     829    ncid = ncid_file
     830end if
     831
     832! Check if the variable holds a Time dimension (unlimited dim)
     833call check_nc(nf90_inq_varid(ncid,var_name,varid),'inquiring '//var_name)
     834call check_nc(nf90_inquire_variable(ncid,varid,ndims = ndims,dimids = dimids),'inquiring dims of '//var_name)
     835if (ndims > 0) then
     836    call check_nc(nf90_inquire(ncid,unlimitedDimId = unlimdimid),'inquiring unlimited dim')
     837    has_time = (dimids(ndims) == unlimdimid)
     838else
     839    has_time = .false.
     840end if
     841
     842if (present(itime)) then ! Time-dependent write
     843    if (.not. has_time) call stop_clean(__FILE__,__LINE__,'itime present but variable has no Time dimension: '//var_name,1)
     844    allocate(strt(ndims),cnt(ndims))
     845    strt(:) = 1
     846    cnt(:) = 1
     847    strt(ndims) = itime
     848    do i = 1,ndims - 1
     849        cnt(i) = size(var,i)
     850    end do
     851    call check_nc(nf90_put_var(ncid,varid,var,start = strt,count = cnt),'putting '//var_name)
     852    deallocate(strt,cnt)
     853else ! Static write
     854    if (has_time) call stop_clean(__FILE__,__LINE__,'itime absent but variable is time-dependent: '//var_name,1)
     855    call check_nc(nf90_put_var(ncid,varid,var),'putting '//var_name)
     856end if
     857
     858END SUBROUTINE put_var4d_nc
     859!=======================================================================
     860
     861!=======================================================================
     862SUBROUTINE get_next_itime_nc(dim_name,itime)
     863!-----------------------------------------------------------------------
     864! NAME
     865!     get_next_itime_nc
     866!
     867! DESCRIPTION
     868!     Get the next time index in a NetCDF file to record variables.
     869!
     870! AUTHORS & DATE
     871!     JB Clement, 01/2026
     872!
     873! NOTES
     874!     In most cases, dim_name = 'Time'.
     875!-----------------------------------------------------------------------
     876
     877! DECLARATION
     878! -----------
     879implicit none
     880
     881! ARGUMENTS
     882! ---------
     883character(*), intent(in)  :: dim_name ! Dimension name
     884integer(di),  intent(out) :: itime    ! Time index
     885
     886! LOCAL VARIABLES
     887! ---------------
     888integer(di) :: length
     889logical(k4) :: found
     890
     891! CODE
     892! ----
     893! Get dimension length
     894call get_dim_nc(dim_name,length,found)
     895if (.not. found) call stop_clean(__FILE__,__LINE__,'dimension '//dim_name//' not found',1)
     896
     897! Next time index
     898itime = length + 1
     899
     900END SUBROUTINE get_next_itime_nc
     901!=======================================================================
     902
     903!=======================================================================
     904SUBROUTINE check_valid_var0d_nc(var_name,var)
     905!-----------------------------------------------------------------------
     906! NAME
     907!     check_valid_var0d_nc
     908!
     909! DESCRIPTION
     910!     Check the validity of a 0D variable read from a NetCDF file.
     911!
     912! AUTHORS & DATE
     913!     JB Clement, 02/2026
     914!
    131915! NOTES
    132916!
     
    135919! DEPENDENCIES
    136920! ------------
    137   USE netcdf,             only: NF90_CLOSE
    138   USE mod_phys_lmdz_para, only: is_master
    139 
    140 ! DECLARATION
    141 ! -----------
    142 implicit none
     921use numerics, only: largest_nb
     922use stoppage, only: stop_clean
     923use utility,  only: real2str
     924
     925! DECLARATION
     926! -----------
     927implicit none
     928
     929! ARGUMENTS
     930! ---------
     931character(*), intent(in) :: var_name ! Variable name
     932real(dp),     intent(in) :: var      ! Input variable
    143933
    144934! LOCAL VARIABLES
    145935! ---------------
    146     INTEGER :: ierr
    147 
    148 ! CODE
    149 ! ----
    150     IF (is_master) THEN
    151         ierr = NF90_CLOSE (nid_start)
    152     ENDIF
    153 
    154   END SUBROUTINE close_startphy
    155 !=======================================================================
    156 
    157 !=======================================================================
    158   FUNCTION inquire_field(Field_name)
    159 !-----------------------------------------------------------------------
    160 ! NAME
    161 !     inquire_field
    162 !
    163 ! DESCRIPTION
    164 !     Check if a given field is present in the input file.
    165 
    166 ! AUTHORS & DATE
    167 !     L. Lange
    168 !     JB Clement, 2023-2025
    169 
     936logical(k4)            :: has_fill, has_range, has_valid_min, has_valid_max
     937real(dp)               :: fill_value, valid_min, valid_max
     938real(dp), dimension(2) :: valid_range
     939integer(di)            :: ncid
     940
     941! CODE
     942! ----
     943! Diagevol logic priority over standard logic
     944if (open_diagevol) then
     945    ncid = ncid_diagevol
     946else
     947    ncid = ncid_file
     948end if
     949
     950! NaN
     951if (var /= var) call stop_clean(__FILE__,__LINE__,'NaN detected in variable '//var_name//'!',1)
     952
     953! Infinite
     954if (abs(var) > largest_nb) call stop_clean(__FILE__,__LINE__,'Infs detected in variable '//var_name//'!',1)
     955
     956! Fill value
     957has_fill = .false.
     958call check_nc(nf90_get_att(ncid,varid,"_FillValue",fill_value),'getting fill value',has_fill)
     959if (.not. has_fill) call check_nc(nf90_get_att(ncid,varid,"missing_value",fill_value),'getting missing value',has_fill)
     960if (has_fill .and. var == fill_value) call stop_clean(__FILE__,__LINE__,'Fill values ('//real2str(fill_value)//') detected in '//var_name//'!',1)
     961
     962! Valid range
     963has_range = .false.
     964call check_nc(nf90_get_att(ncid,varid,"valid_range",valid_range),'getting valid range',has_range)
     965if (has_range) then
     966    valid_min = valid_range(1)
     967    valid_max = valid_range(2)
     968else! valid_min / valid_max (fallback)
     969    has_valid_min = .false.
     970    has_valid_max = .false.
     971    call check_nc(nf90_get_att(ncid,varid,"valid_min",valid_min),'getting valid min',has_valid_min)
     972    call check_nc(nf90_get_att(ncid,varid,"valid_max",valid_max),'getting valid max',has_valid_max)
     973    if (has_valid_min .or. has_valid_max) then
     974        has_range = .true.
     975        if (.not. has_valid_min) valid_min = -largest_nb
     976        if (.not. has_valid_max) valid_max = largest_nb
     977    end if
     978end if
     979if (has_range .and. (var < valid_min .or. var > valid_max)) call stop_clean(__FILE__,__LINE__,'Values outside valid range ('//real2str(valid_min)//','//real2str(valid_max)//') detected in '//var_name//'!',1)
     980
     981END SUBROUTINE check_valid_var0d_nc
     982!=======================================================================
     983
     984!=======================================================================
     985SUBROUTINE check_valid_var1d_nc(var_name,var)
     986!-----------------------------------------------------------------------
     987! NAME
     988!     check_valid_var1d_nc
     989!
     990! DESCRIPTION
     991!     Check the validity of a 1D variable read from a NetCDF file.
     992!
     993! AUTHORS & DATE
     994!     JB Clement, 02/2026
     995!
    170996! NOTES
    171997!
     
    1741000! DEPENDENCIES
    1751001! ------------
    176   USE netcdf,             only: NF90_INQ_VARID, NF90_NOERR
    177   USE mod_phys_lmdz_para, only: is_master, bcast
    178 
    179 ! DECLARATION
    180 ! -----------
    181 implicit none
    182 
    183 ! ARGUMENTS
    184 ! ---------
    185     CHARACTER(LEN=*), INTENT(IN) :: Field_name
     1002use numerics, only: largest_nb
     1003use stoppage, only: stop_clean
     1004use utility,  only: real2str
     1005
     1006! DECLARATION
     1007! -----------
     1008implicit none
     1009
     1010! ARGUMENTS
     1011! ---------
     1012character(*),           intent(in) :: var_name ! Variable name
     1013real(dp), dimension(:), intent(in) :: var      ! Input variable
    1861014
    1871015! LOCAL VARIABLES
    1881016! ---------------
    189     LOGICAL :: inquire_field
    190     INTEGER :: varid
    191     INTEGER :: ierr
    192 
    193 ! CODE
    194 ! ----
    195     IF (is_master) THEN
    196       ierr=NF90_INQ_VARID(nid_start,Field_name,varid)
    197       IF (ierr==NF90_NOERR) THEN
    198         Inquire_field=.TRUE.
    199       ELSE
    200         Inquire_field=.FALSE.
    201       ENDIF
    202     ENDIF
    203 
    204     CALL bcast(inquire_field)
    205 
    206   END FUNCTION inquire_field
    207 !=======================================================================
    208 
    209 !=======================================================================
    210   FUNCTION inquire_field_ndims(Field_name)
    211 !-----------------------------------------------------------------------
    212 ! NAME
    213 !     inquire_field_ndims
    214 !
    215 ! DESCRIPTION
    216 !     Give the number of dimensions of "Field_name" stored in the input file.
    217 
    218 ! AUTHORS & DATE
    219 !     L. Lange
    220 !     JB Clement, 2023-2025
    221 
     1017logical(k4)            :: has_fill, has_range, has_valid_min, has_valid_max
     1018real(dp)               :: fill_value, valid_min, valid_max
     1019real(dp), dimension(2) :: valid_range
     1020integer(di)            :: ncid
     1021
     1022! CODE
     1023! ----
     1024! Diagevol logic priority over standard logic
     1025if (open_diagevol) then
     1026    ncid = ncid_diagevol
     1027else
     1028    ncid = ncid_file
     1029end if
     1030
     1031! NaN
     1032if (any(var /= var)) call stop_clean(__FILE__,__LINE__,'NaN detected in variable '//var_name//'!',1)
     1033
     1034! Infinite
     1035if (any(abs(var) > largest_nb)) call stop_clean(__FILE__,__LINE__,'Infs detected in variable '//var_name//'!',1)
     1036
     1037! Fill value
     1038has_fill = .false.
     1039call check_nc(nf90_get_att(ncid,varid,"_FillValue",fill_value),'getting fill value',has_fill)
     1040if (.not. has_fill) call check_nc(nf90_get_att(ncid,varid,"missing_value",fill_value),'getting missing value',has_fill)
     1041if (has_fill) then
     1042    if (any(var == fill_value)) call stop_clean(__FILE__,__LINE__,'Fill values ('//real2str(fill_value)//') detected in '//var_name//'!',1)
     1043end if
     1044
     1045! Valid range
     1046has_range = .false.
     1047call check_nc(nf90_get_att(ncid,varid,"valid_range",valid_range),'getting valid range',has_range)
     1048if (has_range) then
     1049    valid_min = valid_range(1)
     1050    valid_max = valid_range(2)
     1051else! valid_min / valid_max (fallback)
     1052    has_valid_min = .false.
     1053    has_valid_max = .false.
     1054    call check_nc(nf90_get_att(ncid,varid,"valid_min",valid_min),'getting valid min',has_valid_min)
     1055    call check_nc(nf90_get_att(ncid,varid,"valid_max",valid_max),'getting valid max',has_valid_max)
     1056    if (has_valid_min .or. has_valid_max) then
     1057        has_range = .true.
     1058        if (.not. has_valid_min) valid_min = -largest_nb
     1059        if (.not. has_valid_max) valid_max = largest_nb
     1060    end if
     1061end if
     1062if (has_range) then
     1063    if (any(var < valid_min) .or. any(var > valid_max)) call stop_clean(__FILE__,__LINE__,'Values outside valid range ('//real2str(valid_min)//','//real2str(valid_max)//') detected in '//var_name//'!',1)
     1064end if
     1065
     1066END SUBROUTINE check_valid_var1d_nc
     1067!=======================================================================
     1068
     1069!=======================================================================
     1070SUBROUTINE check_valid_var2d_nc(var_name,var)
     1071!-----------------------------------------------------------------------
     1072! NAME
     1073!     check_valid_var2d_nc
     1074!
     1075! DESCRIPTION
     1076!     Check the validity of a 1D variable read from a NetCDF file.
     1077!
     1078! AUTHORS & DATE
     1079!     JB Clement, 02/2026
     1080!
    2221081! NOTES
    2231082!
     
    2261085! DEPENDENCIES
    2271086! ------------
    228   USE netcdf,             only: nf90_inq_varid, nf90_inquire_variable, &
    229                                 NF90_NOERR, nf90_strerror
    230   USE mod_phys_lmdz_para, only: is_master, bcast
    231 
    232 ! DECLARATION
    233 ! -----------
    234 implicit none
    235 
    236 ! ARGUMENTS
    237 ! ---------
    238     CHARACTER(LEN=*), INTENT(IN) :: Field_name
     1087use numerics, only: largest_nb
     1088use stoppage, only: stop_clean
     1089use utility,  only: real2str
     1090
     1091! DECLARATION
     1092! -----------
     1093implicit none
     1094
     1095! ARGUMENTS
     1096! ---------
     1097character(*),             intent(in) :: var_name ! Variable name
     1098real(dp), dimension(:,:), intent(in) :: var      ! Input variable
    2391099
    2401100! LOCAL VARIABLES
    2411101! ---------------
    242     INTEGER :: inquire_field_ndims
    243     INTEGER :: varid
    244     INTEGER :: ierr
    245 
    246 ! CODE
    247 ! ----
    248     IF (is_master) THEN
    249       ierr=nf90_inq_varid(nid_start,Field_name,varid)
    250       ierr=nf90_inquire_variable(nid_start,varid,&
    251                                   ndims=inquire_field_ndims)
    252       IF (ierr.NE.NF90_NOERR) THEN
    253         write(*,*)'inquire_field_ndims: problem obtaining ndims of '&
    254                   //trim(field_name)
    255         write(*,*)trim(nf90_strerror(ierr))
    256         CALL abort_physic("inquire_field_ndims","Failed to get ndims",1)
    257       ENDIF
    258     ENDIF
    259 
    260     CALL bcast(inquire_field_ndims)
    261 
    262   END FUNCTION inquire_field_ndims
    263 !=======================================================================
    264 
    265 !=======================================================================
    266   FUNCTION inquire_dimension(Field_name)
    267 !-----------------------------------------------------------------------
    268 ! NAME
    269 !     inquire_dimension
    270 !
    271 ! DESCRIPTION
    272 !     Check if a given dimension is present in the input file.
    273 
    274 ! AUTHORS & DATE
    275 !     L. Lange
    276 !     JB Clement, 2023-2025
    277 
     1102logical(k4)            :: has_fill, has_range, has_valid_min, has_valid_max
     1103real(dp)               :: fill_value, valid_min, valid_max
     1104real(dp), dimension(2) :: valid_range
     1105integer(di)            :: ncid
     1106
     1107! CODE
     1108! ----
     1109! Diagevol logic priority over standard logic
     1110if (open_diagevol) then
     1111    ncid = ncid_diagevol
     1112else
     1113    ncid = ncid_file
     1114end if
     1115
     1116! NaN
     1117if (any(var /= var)) call stop_clean(__FILE__,__LINE__,'NaN detected in variable '//var_name//'!',1)
     1118
     1119! Infinite
     1120if (any(abs(var) > largest_nb)) call stop_clean(__FILE__,__LINE__,'Infs detected in variable '//var_name//'!',1)
     1121
     1122! Fill value
     1123has_fill = .false.
     1124call check_nc(nf90_get_att(ncid,varid,"_FillValue",fill_value),'getting fill value',has_fill)
     1125if (.not. has_fill) call check_nc(nf90_get_att(ncid,varid,"missing_value",fill_value),'getting missing value',has_fill)
     1126if (has_fill .and. any(var == fill_value)) call stop_clean(__FILE__,__LINE__,'Fill values ('//real2str(fill_value)//') detected in '//var_name//'!',1)
     1127
     1128! Valid range
     1129has_range = .false.
     1130call check_nc(nf90_get_att(ncid,varid,"valid_range",valid_range),'getting valid range',has_range)
     1131if (has_range) then
     1132    valid_min = valid_range(1)
     1133    valid_max = valid_range(2)
     1134else! valid_min / valid_max (fallback)
     1135    has_valid_min = .false.
     1136    has_valid_max = .false.
     1137    call check_nc(nf90_get_att(ncid,varid,"valid_min",valid_min),'getting valid min',has_valid_min)
     1138    call check_nc(nf90_get_att(ncid,varid,"valid_max",valid_max),'getting valid max',has_valid_max)
     1139    if (has_valid_min .or. has_valid_max) then
     1140        has_range = .true.
     1141        if (.not. has_valid_min) valid_min = -largest_nb
     1142        if (.not. has_valid_max) valid_max = largest_nb
     1143    end if
     1144end if
     1145if (has_range .and. (any(var < valid_min) .or. any(var > valid_max))) call stop_clean(__FILE__,__LINE__,'Values outside valid range ('//real2str(valid_min)//','//real2str(valid_max)//') detected in '//var_name//'!',1)
     1146
     1147END SUBROUTINE check_valid_var2d_nc
     1148!=======================================================================
     1149
     1150!=======================================================================
     1151SUBROUTINE check_valid_var3d_nc(var_name,var)
     1152!-----------------------------------------------------------------------
     1153! NAME
     1154!     check_valid_var3d_nc
     1155!
     1156! DESCRIPTION
     1157!     Check the validity of a 1D variable read from a NetCDF file.
     1158!
     1159! AUTHORS & DATE
     1160!     JB Clement, 02/2026
     1161!
    2781162! NOTES
    2791163!
     
    2821166! DEPENDENCIES
    2831167! ------------
    284   USE netcdf, only: nf90_inq_dimid, NF90_NOERR
    285   USE mod_phys_lmdz_para, only: is_master, bcast
    286 
    287 ! DECLARATION
    288 ! -----------
    289 implicit none
    290 
    291 ! ARGUMENTS
    292 ! ---------
    293     CHARACTER(LEN=*), INTENT(IN) :: Field_name
     1168use numerics, only: largest_nb
     1169use stoppage, only: stop_clean
     1170use utility,  only: real2str
     1171
     1172! DECLARATION
     1173! -----------
     1174implicit none
     1175
     1176! ARGUMENTS
     1177! ---------
     1178character(*),               intent(in) :: var_name ! Variable name
     1179real(dp), dimension(:,:,:), intent(in) :: var      ! Input variable
    2941180
    2951181! LOCAL VARIABLES
    2961182! ---------------
    297     LOGICAL :: inquire_dimension
    298     INTEGER :: varid
    299     INTEGER :: ierr
    300 
    301 ! CODE
    302 ! ----
    303     IF (is_master) THEN
    304       ierr=NF90_INQ_DIMID(nid_start,Field_name,varid)
    305       IF (ierr==NF90_NOERR) THEN
    306         Inquire_dimension=.TRUE.
    307       ELSE
    308         Inquire_dimension=.FALSE.
    309       ENDIF
    310     ENDIF
    311 
    312     CALL bcast(inquire_dimension)
    313 
    314   END FUNCTION inquire_dimension
    315 !=======================================================================
    316 
    317 !=======================================================================
    318   FUNCTION inquire_dimension_length(Field_name)
    319 !-----------------------------------------------------------------------
    320 ! NAME
    321 !     inquire_dimension_length
    322 !
    323 ! DESCRIPTION
    324 !     Give the length of the "Field_name" dimension stored in the input file.
    325 
    326 ! AUTHORS & DATE
    327 !     L. Lange
    328 !     JB Clement, 2023-2025
    329 
     1183logical(k4)            :: has_fill, has_range, has_valid_min, has_valid_max
     1184real(dp)               :: fill_value, valid_min, valid_max
     1185real(dp), dimension(2) :: valid_range
     1186integer(di)            :: ncid
     1187
     1188! CODE
     1189! ----
     1190! Diagevol logic priority over standard logic
     1191if (open_diagevol) then
     1192    ncid = ncid_diagevol
     1193else
     1194    ncid = ncid_file
     1195end if
     1196
     1197! NaN
     1198if (any(var /= var)) call stop_clean(__FILE__,__LINE__,'NaN detected in variable '//var_name//'!',1)
     1199
     1200! Infinite
     1201if (any(abs(var) > largest_nb)) call stop_clean(__FILE__,__LINE__,'Infs detected in variable '//var_name//'!',1)
     1202
     1203! Fill value
     1204has_fill = .false.
     1205call check_nc(nf90_get_att(ncid,varid,"_FillValue",fill_value),'getting fill value',has_fill)
     1206if (.not. has_fill) call check_nc(nf90_get_att(ncid,varid,"missing_value",fill_value),'getting missing value',has_fill)
     1207if (has_fill .and. any(var == fill_value)) call stop_clean(__FILE__,__LINE__,'Fill values ('//real2str(fill_value)//') detected in '//var_name//'!',1)
     1208
     1209! Valid range
     1210has_range = .false.
     1211call check_nc(nf90_get_att(ncid,varid,"valid_range",valid_range),'getting valid range',has_range)
     1212if (has_range) then
     1213    valid_min = valid_range(1)
     1214    valid_max = valid_range(2)
     1215else! valid_min / valid_max (fallback)
     1216    has_valid_min = .false.
     1217    has_valid_max = .false.
     1218    call check_nc(nf90_get_att(ncid,varid,"valid_min",valid_min),'getting valid min',has_valid_min)
     1219    call check_nc(nf90_get_att(ncid,varid,"valid_max",valid_max),'getting valid max',has_valid_max)
     1220    if (has_valid_min .or. has_valid_max) then
     1221        has_range = .true.
     1222        if (.not. has_valid_min) valid_min = -largest_nb
     1223        if (.not. has_valid_max) valid_max = largest_nb
     1224    end if
     1225end if
     1226if (has_range .and. (any(var < valid_min) .or. any(var > valid_max))) call stop_clean(__FILE__,__LINE__,'Values outside valid range ('//real2str(valid_min)//','//real2str(valid_max)//') detected in '//var_name//'!',1)
     1227
     1228END SUBROUTINE check_valid_var3d_nc
     1229!=======================================================================
     1230
     1231!=======================================================================
     1232SUBROUTINE check_valid_var4d_nc(var_name,var)
     1233!-----------------------------------------------------------------------
     1234! NAME
     1235!     check_valid_var4d_nc
     1236!
     1237! DESCRIPTION
     1238!     Check the validity of a 1D variable read from a NetCDF file.
     1239!
     1240! AUTHORS & DATE
     1241!     JB Clement, 02/2026
     1242!
    3301243! NOTES
    3311244!
     
    3341247! DEPENDENCIES
    3351248! ------------
    336   USE netcdf, only: nf90_inquire_dimension, nf90_inq_dimid, &
    337                     NF90_NOERR, nf90_strerror
    338   USE mod_phys_lmdz_para, only: is_master, bcast
    339 
    340 ! DECLARATION
    341 ! -----------
    342 implicit none
    343 
    344 ! ARGUMENTS
    345 ! ---------
    346     CHARACTER(LEN=*), INTENT(IN) :: Field_name
     1249use numerics, only: largest_nb
     1250use stoppage, only: stop_clean
     1251use utility,  only: real2str
     1252
     1253! DECLARATION
     1254! -----------
     1255implicit none
     1256
     1257! ARGUMENTS
     1258! ---------
     1259character(*),                 intent(in) :: var_name ! Variable name
     1260real(dp), dimension(:,:,:,:), intent(in) :: var      ! Input variable
    3471261
    3481262! LOCAL VARIABLES
    3491263! ---------------
    350     INTEGER :: inquire_dimension_length
    351     INTEGER :: varid
    352     INTEGER :: ierr
    353 
    354 ! CODE
    355 ! ----
    356     IF (is_master) THEN
    357       ierr=nf90_inq_dimid(nid_start,Field_name,varid)
    358       ierr=nf90_inquire_dimension(nid_start,varid,&
    359                                   len=inquire_dimension_length)
    360       IF (ierr.NE.NF90_NOERR) THEN
    361         write(*,*)'inquire_field_length: problem obtaining length of '&
    362                   //trim(field_name)
    363         write(*,*)trim(nf90_strerror(ierr))
    364         CALL abort_physic("inquire_field_ndims","Failed to get length",1)
    365       ENDIF
    366     ENDIF
    367 
    368     CALL bcast(inquire_dimension_length)
    369 
    370   END FUNCTION inquire_dimension_length
    371 !=======================================================================
    372 
    373 !=======================================================================
    374   SUBROUTINE Get_Field_r1(field_name,field,found,timeindex)
    375 !-----------------------------------------------------------------------
    376 ! NAME
    377 !     Get_Field_r1
    378 !
    379 ! DESCRIPTION
    380 !     Read a surface field (1D) from the start file.
    381 
    382 ! AUTHORS & DATE
    383 !     L. Lange
    384 !     JB Clement, 2023-2025
    385 
    386 ! NOTES
    387 !
    388 !-----------------------------------------------------------------------
    389 
    390 ! DEPENDENCIES
    391 ! ------------
    392   use mod_grid_phy_lmdz, only: klon_glo
    393 
    394 ! DECLARATION
    395 ! -----------
    396 implicit none
    397 
    398 ! ARGUMENTS
    399 ! ---------
    400   CHARACTER(LEN=*), INTENT(IN)            :: field_name
    401   REAL,             INTENT(INOUT)         :: field(:)
    402   LOGICAL,          INTENT(OUT), OPTIONAL :: found
    403   INTEGER,          INTENT(IN),  OPTIONAL :: timeindex
    404 
    405 ! LOCAL VARIABLES
    406 ! ---------------
    407     integer :: edges(4), corners(4)
    408 
    409 ! CODE
    410 ! ----
    411     edges(1)=klon_glo
    412     edges(2:4)=1
    413     corners(1:4)=1
    414     if (PRESENT(timeindex)) then
    415       corners(2)=timeindex
    416     endif
    417 
    418     IF (PRESENT(found)) THEN
    419       CALL Get_field_rgen(field_name,field,1,corners,edges,found)
    420     ELSE
    421       CALL Get_field_rgen(field_name,field,1,corners,edges)
    422     ENDIF
    423 
    424   END SUBROUTINE Get_Field_r1
    425 !=======================================================================
    426 
    427 !=======================================================================
    428   SUBROUTINE Get_Field_r2(field_name,field,found,timeindex)
    429 !-----------------------------------------------------------------------
    430 ! NAME
    431 !     Get_Field_r2
    432 !
    433 ! DESCRIPTION
    434 !     Read a "3D" horizontal-vertical field (2D) from the start file.
    435 
    436 ! AUTHORS & DATE
    437 !     L. Lange
    438 !     JB Clement, 2023-2025
    439 
    440 ! NOTES
    441 !
    442 !-----------------------------------------------------------------------
    443 
    444 ! DEPENDENCIES
    445 ! ------------
    446   use mod_grid_phy_lmdz, only: klon_glo
    447 
    448 ! DECLARATION
    449 ! -----------
    450 implicit none
    451 
    452 ! ARGUMENTS
    453 ! ---------
    454   CHARACTER(LEN=*), INTENT(IN)            :: field_name
    455   REAL,             INTENT(INOUT)         :: field(:,:)
    456   LOGICAL,          INTENT(OUT), OPTIONAL :: found
    457   INTEGER,          INTENT(IN),  OPTIONAL :: timeindex
    458 
    459 ! LOCAL VARIABLES
    460 ! ---------------
    461     integer :: edges(4), corners(4)
    462 
    463 ! CODE
    464 ! ----
    465     edges(1)=klon_glo
    466     edges(2)=size(field,2)
    467     edges(3:4)=1
    468     corners(1:4)=1
    469     if (PRESENT(timeindex)) then
    470       corners(3)=timeindex
    471     endif
    472 
    473     IF (PRESENT(found)) THEN
    474       CALL Get_field_rgen(field_name,field,size(field,2),&
    475                           corners,edges,found)
    476     ELSE
    477       CALL Get_field_rgen(field_name,field,size(field,2),&
    478                           corners,edges)
    479     ENDIF
    480 
    481 
    482   END SUBROUTINE Get_Field_r2
    483 !=======================================================================
    484 
    485 !=======================================================================
    486   SUBROUTINE Get_Field_r3(field_name,field,found,timeindex)
    487 !-----------------------------------------------------------------------
    488 ! NAME
    489 !     Get_Field_r3
    490 !
    491 ! DESCRIPTION
    492 !     Read a "4D" field surf/alt/?? (3D) from the start file.
    493 
    494 ! AUTHORS & DATE
    495 !     L. Lange
    496 !     JB Clement, 2023-2025
    497 
    498 ! NOTES
    499 !
    500 !-----------------------------------------------------------------------
    501 
    502 ! DEPENDENCIES
    503 ! ------------
    504   use mod_grid_phy_lmdz, only: klon_glo
    505 
    506 ! DECLARATION
    507 ! -----------
    508 implicit none
    509 
    510 ! ARGUMENTS
    511 ! ---------
    512   CHARACTER(LEN=*), INTENT(IN)            :: field_name
    513   REAL,             INTENT(INOUT)         :: field(:,:,:)
    514   LOGICAL,          INTENT(OUT), OPTIONAL :: found
    515   INTEGER,          INTENT(IN),  OPTIONAL :: timeindex
    516 
    517 ! LOCAL VARIABLES
    518 ! ---------------
    519     integer :: edges(4), corners(4)
    520 
    521 ! CODE
    522 ! ----
    523     edges(1)=klon_glo
    524     edges(2)=size(field,2)
    525     edges(3)=size(field,3)
    526     edges(4)=1
    527     corners(1:4)=1
    528     if (PRESENT(timeindex)) then
    529       corners(4)=timeindex
    530     endif
    531 
    532     IF (PRESENT(found)) THEN
    533       CALL Get_field_rgen(field_name,field,size(field,2)*size(field,3),&
    534                           corners,edges,found)
    535     ELSE
    536       CALL Get_field_rgen(field_name,field,size(field,2)*size(field,3),&
    537                           corners,edges)
    538     ENDIF
    539 
    540   END SUBROUTINE Get_Field_r3
    541 !=======================================================================
    542 
    543 !=======================================================================
    544   SUBROUTINE Get_field_rgen(field_name,field,field_size, &
    545                             corners,edges,found)
    546 !-----------------------------------------------------------------------
    547 ! NAME
    548 !     Get_field_rgen
    549 !
    550 ! DESCRIPTION
    551 !     Generic subroutine to read fields from start file with scatter to processes.
    552 
    553 ! AUTHORS & DATE
    554 !     L. Lange
    555 !     JB Clement, 2023-2025
    556 
    557 ! NOTES
    558 !
    559 !-----------------------------------------------------------------------
    560 
    561 ! DEPENDENCIES
    562 ! ------------
    563   USE netcdf
    564   USE dimphy
    565   USE mod_grid_phy_lmdz
    566   USE mod_phys_lmdz_para
    567 
    568 ! DECLARATION
    569 ! -----------
    570 implicit none
    571 
    572 ! ARGUMENTS
    573 ! ---------
    574     CHARACTER(LEN=*), INTENT(IN)            :: field_name
    575     INTEGER,          INTENT(IN)            :: field_size
    576     REAL,             INTENT(OUT)           :: field(klon,field_size)
    577     INTEGER,          INTENT(IN)            :: corners(4)
    578     INTEGER,          INTENT(IN)            :: edges(4)
    579     LOGICAL,          INTENT(OUT), OPTIONAL :: found
    580 
    581 ! LOCAL VARIABLES
    582 ! ---------------
    583     REAL    :: field_glo(klon_glo,field_size)
    584     LOGICAL :: tmp_found
    585     INTEGER :: varid
    586     INTEGER :: ierr
    587 
    588 ! CODE
    589 ! ----
    590     IF (is_master) THEN
    591 
    592       ierr=NF90_INQ_VARID(nid_start,field_name,varid)
    593 
    594       IF (ierr==NF90_NOERR) THEN
    595         CALL body(field_glo)
    596         tmp_found=.TRUE.
    597       ELSE
    598         tmp_found=.FALSE.
    599       ENDIF
    600 
    601     ENDIF
    602 
    603     CALL bcast(tmp_found)
    604 
    605     IF (tmp_found) THEN
    606       CALL scatter(field_glo,field)
    607     ENDIF
    608 
    609     IF (PRESENT(found)) THEN
    610       found=tmp_found
    611     ELSE
    612       IF (.NOT. tmp_found) THEN
    613         write(*,*) 'get_field_rgen: Field <'//field_name//'> not found'
    614         CALL abort_physic("get_field_rgen","Failed to get field",1)
    615       ENDIF
    616     ENDIF
    617 
    618 
    619         CONTAINS
    620     !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    621 
    622     !=======================================================================
    623       SUBROUTINE body(field_glo)
    624     !-----------------------------------------------------------------------
    625     ! NAME
    626     !     body
    627     !
    628     ! DESCRIPTION
    629     !     Read the requested field from the start file into the global array.
    630     !
    631     ! AUTHORS & DATE
    632     !     L. Lange
    633     !     JB Clement, 2023-2025
    634     !
    635     ! NOTES
    636     !     Helper contained in Get_field_rgen.
    637     !-----------------------------------------------------------------------
    638 
    639     ! DECLARATION
    640     ! -----------
    641       ! DECLARATION
    642 ! -----------
    643 implicit none
    644 
    645     ! ARGUMENTS
    646     ! ---------
    647       REAL, INTENT(OUT) :: field_glo(klon_glo*field_size) ! Buffer for global field
    648 
    649     ! CODE
    650     ! ----
    651           ierr=NF90_GET_VAR(nid_start,varid,field_glo,corners,edges)
    652           IF (ierr/=NF90_NOERR) THEN
    653          ! La variable exist dans le fichier mais la lecture a echouee.
    654          write(*,*) 'get_field_rgen: Failed reading <'//field_name//'>'
    655 
    656 !           IF (field_name=='CLWCON' .OR. field_name=='RNEBCON' .OR. field_name=='RATQS') THEN
    657 !              ! Essaye de lire le variable sur surface uniqument, comme fait avant
    658 !              field_glo(:)=0.
    659 !              ierr=NF90_GET_VAR(nid_start,varid,field_glo(1:klon_glo))
    660 !              IF (ierr/=NF90_NOERR) THEN
    661 !                 write(*,*) 'phyetat0: Lecture echouee aussi en 2D pour <'//field_name//'>'
    662 !                 CALL stop_clean
    663 !              ELSE
    664 !                 write(*,*) 'phyetat0: La variable <'//field_name//'> lu sur surface seulement'!, selon ancien format, le reste mis a zero'
    665 !              END IF
    666 !           ELSE
    667               CALL abort_physic("get_field_rgen","Failed to read field",1)
    668 !           ENDIF
    669          ENDIF
    670 
    671      END SUBROUTINE body
    672 
    673   END SUBROUTINE Get_field_rgen
    674 !=======================================================================
    675 
    676 !=======================================================================
    677   SUBROUTINE get_var_r0(var_name,var,found)
    678 !-----------------------------------------------------------------------
    679 ! NAME
    680 !     get_var_r0
    681 !
    682 ! DESCRIPTION
    683 !     Get a scalar variable from input file.
    684 !
    685 ! AUTHORS & DATE
    686 !     L. Lange
    687 !     JB Clement, 2023-2025
    688 !
    689 ! NOTES
    690 !
    691 !-----------------------------------------------------------------------
    692 
    693 ! DECLARATION
    694 ! -----------
    695 implicit none
    696 
    697 ! ARGUMENTS
    698 ! ---------
    699   CHARACTER(LEN=*), INTENT(IN)            :: var_name
    700   REAL,             INTENT(INOUT)         :: var
    701   LOGICAL,          INTENT(OUT), OPTIONAL :: found
    702 
    703 ! LOCAL VARIABLES
    704 ! ---------------
    705     REAL :: varout(1)
    706 
    707 ! CODE
    708 ! ----
    709     IF (PRESENT(found)) THEN
    710       CALL Get_var_rgen(var_name,varout,size(varout),found)
    711     ELSE
    712       CALL Get_var_rgen(var_name,varout,size(varout))
    713     ENDIF
    714     var=varout(1)
    715 
    716   END SUBROUTINE get_var_r0
    717 !=======================================================================
    718 
    719 !=======================================================================
    720   SUBROUTINE get_var_r1(var_name,var,found)
    721 !-----------------------------------------------------------------------
    722 ! NAME
    723 !     get_var_r1
    724 !
    725 ! DESCRIPTION
    726 !     Get a vector (1D) variable from input file.
    727 !
    728 ! AUTHORS & DATE
    729 !     L. Lange
    730 !     JB Clement, 2023-2025
    731 !
    732 ! NOTES
    733 !
    734 !-----------------------------------------------------------------------
    735 
    736 ! DECLARATION
    737 ! -----------
    738 implicit none
    739 
    740 ! ARGUMENTS
    741 ! ---------
    742   CHARACTER(LEN=*), INTENT(IN)            :: var_name
    743   REAL,             INTENT(INOUT)         :: var(:)
    744   LOGICAL,          INTENT(OUT), OPTIONAL :: found
    745 
    746 ! CODE
    747 ! ----
    748     IF (PRESENT(found)) THEN
    749       CALL Get_var_rgen(var_name,var,size(var),found)
    750     ELSE
    751       CALL Get_var_rgen(var_name,var,size(var))
    752     ENDIF
    753 
    754   END SUBROUTINE get_var_r1
    755 !=======================================================================
    756 
    757 !=======================================================================
    758   SUBROUTINE get_var_r2(var_name,var,found)
    759 !-----------------------------------------------------------------------
    760 ! NAME
    761 !     get_var_r2
    762 !
    763 ! DESCRIPTION
    764 !     Get a 2D field variable from input file.
    765 !
    766 ! AUTHORS & DATE
    767 !     L. Lange
    768 !     JB Clement, 2023-2025
    769 !
    770 ! NOTES
    771 !
    772 !-----------------------------------------------------------------------
    773 
    774 ! DECLARATION
    775 ! -----------
    776 implicit none
    777 
    778 ! ARGUMENTS
    779 ! ---------
    780   CHARACTER(LEN=*), INTENT(IN)            :: var_name
    781   REAL,             INTENT(OUT)           :: var(:,:)
    782   LOGICAL,          INTENT(OUT), OPTIONAL :: found
    783 
    784 ! CODE
    785 ! ----
    786     IF (PRESENT(found)) THEN
    787       CALL Get_var_rgen(var_name,var,size(var),found)
    788     ELSE
    789       CALL Get_var_rgen(var_name,var,size(var))
    790     ENDIF
    791 
    792   END SUBROUTINE get_var_r2
    793 !=======================================================================
    794 
    795 !=======================================================================
    796   SUBROUTINE get_var_r3(var_name,var,found)
    797 !-----------------------------------------------------------------------
    798 ! NAME
    799 !     get_var_r3
    800 !
    801 ! DESCRIPTION
    802 !     Get a 3D field variable from input file.
    803 !
    804 ! AUTHORS & DATE
    805 !     L. Lange
    806 !     JB Clement, 2023-2025
    807 !
    808 ! NOTES
    809 !
    810 !-----------------------------------------------------------------------
    811 
    812 ! DECLARATION
    813 ! -----------
    814 implicit none
    815 
    816 ! ARGUMENTS
    817 ! ---------
    818   CHARACTER(LEN=*), INTENT(IN)            :: var_name
    819   REAL,             INTENT(INOUT)         :: var(:,:,:)
    820   LOGICAL,          INTENT(OUT), OPTIONAL :: found
    821 
    822 ! CODE
    823 ! ----
    824     IF (PRESENT(found)) THEN
    825       CALL Get_var_rgen(var_name,var,size(var),found)
    826     ELSE
    827       CALL Get_var_rgen(var_name,var,size(var))
    828     ENDIF
    829 
    830   END SUBROUTINE get_var_r3
    831 !=======================================================================
    832 
    833 !=======================================================================
    834   SUBROUTINE Get_var_rgen(var_name,var,var_size,found)
    835 !-----------------------------------------------------------------------
    836 ! NAME
    837 !     Get_var_rgen
    838 !
    839 ! DESCRIPTION
    840 !     Generic subroutine to read variables from input file.
    841 !
    842 ! AUTHORS & DATE
    843 !     L. Lange
    844 !     JB Clement, 2023-2025
    845 !
    846 ! NOTES
    847 !
    848 !-----------------------------------------------------------------------
    849 
    850 ! DEPENDENCIES
    851 ! ------------
    852   USE netcdf
    853   USE dimphy
    854   USE mod_grid_phy_lmdz
    855   USE mod_phys_lmdz_para
    856 
    857 ! DECLARATION
    858 ! -----------
    859 implicit none
    860 
    861 ! ARGUMENTS
    862 ! ---------
    863     CHARACTER(LEN=*), INTENT(IN)            :: var_name
    864     INTEGER,          INTENT(IN)            :: var_size
    865     REAL,             INTENT(OUT)           :: var(var_size)
    866     LOGICAL,          INTENT(OUT), OPTIONAL :: found
    867 
    868 ! LOCAL VARIABLES
    869 ! ---------------
    870     LOGICAL :: tmp_found
    871     INTEGER :: varid
    872     INTEGER :: ierr
    873 
    874 ! CODE
    875 ! ----
    876     IF (is_mpi_root .AND. is_omp_root) THEN
    877 
    878       ierr=NF90_INQ_VARID(nid_start,var_name,varid)
    879 
    880       IF (ierr==NF90_NOERR) THEN
    881         ierr=NF90_GET_VAR(nid_start,varid,var)
    882         IF (ierr/=NF90_NOERR) THEN
    883           write(*,*) 'phyetat0: Failed loading <'//trim(var_name)//'>'
    884           CALL abort_physic("get_var_rgen","Failed to read variable",1)
    885         ENDIF
    886         tmp_found=.TRUE.
    887       ELSE
    888         tmp_found=.FALSE.
    889       ENDIF
    890 
    891     ENDIF
    892 
    893     CALL bcast(tmp_found)
    894 
    895     IF (tmp_found) THEN
    896       CALL bcast(var)
    897     ENDIF
    898 
    899     IF (PRESENT(found)) THEN
    900       found=tmp_found
    901     ELSE
    902       IF (.NOT. tmp_found) THEN
    903         write(*,*) 'phyetat0: Variable <'//trim(var_name)//'> not found'
    904         CALL abort_physic("get_var_rgen","Failed to read variable",1)
    905       ENDIF
    906     ENDIF
    907 
    908   END SUBROUTINE Get_var_rgen
    909 !=======================================================================
    910 
    911 !=======================================================================
    912   SUBROUTINE open_restartphy(filename)
    913 !-----------------------------------------------------------------------
    914 ! NAME
    915 !     open_restartphy
    916 !
    917 ! DESCRIPTION
    918 !     Open (or create) the restart netCDF file for writing.
    919 !     Define dimensions and global attributes on first call.
    920 !
    921 ! AUTHORS & DATE
    922 !     L. Lange
    923 !     JB Clement, 2023-2025
    924 !
    925 ! NOTES
    926 !
    927 !-----------------------------------------------------------------------
    928 
    929 ! DEPENDENCIES
    930 ! ------------
    931   USE netcdf, only: NF90_CREATE, NF90_CLOBBER, NF90_64BIT_OFFSET, &
    932                     NF90_NOERR, nf90_strerror, &
    933                     NF90_PUT_ATT, NF90_GLOBAL, NF90_DEF_DIM, &
    934                     NF90_UNLIMITED, NF90_ENDDEF, &
    935                     NF90_WRITE, NF90_OPEN
    936   USE mod_phys_lmdz_para, only: is_master
    937   USE mod_grid_phy_lmdz,  only: klon_glo
    938   USE dimphy,             only: klev, klevp1
    939   USE tracer_mod,         only: nqmx
    940   USE soil,               only:  nsoilmx_PEM
    941   USE comslope_mod,       only: nslope
    942   use layered_deposits,   only: nb_str_max
    943 
    944 ! DECLARATION
    945 ! -----------
    946 implicit none
    947 
    948 ! ARGUMENTS
    949 ! ---------
    950   CHARACTER(LEN=*), INTENT(IN) :: filename
    951 
    952 ! LOCAL VARIABLES
    953 ! ---------------
    954     INTEGER           :: ierr
    955     LOGICAL,SAVE      :: already_created=.false.
    956 
    957 ! CODE
    958 ! ----
    959     IF (is_master) THEN
    960       if (.not.already_created) then
    961         ! At the very first call, create the file
    962         ierr=NF90_CREATE(filename,IOR(NF90_CLOBBER,NF90_64BIT_OFFSET), &
    963                           nid_restart)
    964         IF (ierr/=NF90_NOERR) THEN
    965           write(*,*)'open_restartphy: problem creating file '//trim(filename)
    966           write(*,*)trim(nf90_strerror(ierr))
    967           CALL abort_physic("open_restartphy","Failed creating file",1)
    968         ENDIF
    969         already_created=.true.
    970       else
    971         ! Just open the file
    972         ierr=NF90_OPEN(filename,NF90_WRITE,nid_restart)
    973         IF (ierr/=NF90_NOERR) THEN
    974           write(*,*)'open_restartphy: problem opening file '//trim(filename)
    975           write(*,*)trim(nf90_strerror(ierr))
    976           CALL abort_physic("open_restartphy","Failed opening file",1)
    977         ENDIF
    978         return
    979       endif ! of if (.not.already_created)
    980 
    981       ierr=NF90_PUT_ATT(nid_restart,NF90_GLOBAL,"title",&
    982                         "Physics start file")
    983       IF (ierr/=NF90_NOERR) THEN
    984         write(*,*)'open_restartphy: problem writing title '
    985         write(*,*)trim(nf90_strerror(ierr))
    986       ENDIF
    987 
    988       ierr=NF90_DEF_DIM(nid_restart,"index",length,idim1)
    989       IF (ierr/=NF90_NOERR) THEN
    990         write(*,*)'open_restartphy: problem defining index dimension '
    991         write(*,*)trim(nf90_strerror(ierr))
    992         CALL abort_physic("open_restartphy","Failed defining index",1)
    993       ENDIF
    994 
    995       ierr=NF90_DEF_DIM(nid_restart,"physical_points",klon_glo,idim2)
    996       IF (ierr/=NF90_NOERR) THEN
    997         write(*,*)'open_restartphy: problem defining physical_points dimension '
    998         write(*,*)trim(nf90_strerror(ierr))
    999         CALL abort_physic("open_restartphy","Failed defining physical_points",1)
    1000       ENDIF
    1001 
    1002       ierr=NF90_DEF_DIM(nid_restart,"subsurface_layers",nsoilmx_PEM,idim3)
    1003       IF (ierr/=NF90_NOERR) THEN
    1004         write(*,*)'open_restartphy: problem defining subsurface_layers dimension '
    1005         write(*,*)trim(nf90_strerror(ierr))
    1006         CALL abort_physic("open_restartphy","Failed defining subsurface_layers",1)
    1007       ENDIF
    1008 
    1009       ierr=NF90_DEF_DIM(nid_restart,"nlayer_plus_1",klevp1,idim4)
    1010       IF (ierr/=NF90_NOERR) THEN
    1011         write(*,*)'open_restartphy: problem defining nlayer_plus_1 dimension '
    1012         write(*,*)trim(nf90_strerror(ierr))
    1013         CALL abort_physic("open_restartphy","Failed defining nlayer_plus_1",1)
    1014       ENDIF
    1015 
    1016       if (nqmx.ne.0) then
    1017         ierr=NF90_DEF_DIM(nid_restart,"number_of_advected_fields",nqmx,idim5)
    1018       else
    1019         ! pretend nqmx=1 because 0 size implies unlimited dimension for NetCDF
    1020         ierr=NF90_DEF_DIM(nid_restart,"number_of_advected_fields",1,idim5)
    1021       endif
    1022       IF (ierr/=NF90_NOERR) THEN
    1023         write(*,*)'open_restartphy: problem defining number_of_advected_fields dimension '
    1024         write(*,*)trim(nf90_strerror(ierr))
    1025         CALL abort_physic("open_restartphy","Failed defining number_of_advected_fields",1)
    1026       ENDIF
    1027 
    1028       ierr=NF90_DEF_DIM(nid_restart,"nlayer",klev,idim6)
    1029       IF (ierr/=NF90_NOERR) THEN
    1030         write(*,*)'open_restartphy: problem defining nlayer dimension '
    1031         write(*,*)trim(nf90_strerror(ierr))
    1032         CALL abort_physic("open_restartphy","Failed defining nlayer",1)
    1033       ENDIF
    1034 
    1035       ierr=NF90_DEF_DIM(nid_restart,"Time",NF90_UNLIMITED,idim7)
    1036       IF (ierr/=NF90_NOERR) THEN
    1037         write(*,*)'open_restartphy: problem defining Time dimension '
    1038         write(*,*)trim(nf90_strerror(ierr))
    1039         CALL abort_physic("open_restartphy","Failed defining Time",1)
    1040       ENDIF
    1041 
    1042       ierr=NF90_DEF_DIM(nid_restart,"nslope",nslope,idim8)
    1043       IF (ierr/=NF90_NOERR) THEN
    1044         write(*,*)'phyredem: problem defining nslope dimension'
    1045         write(*,*)trim(nf90_strerror(ierr))
    1046         CALL ABORT
    1047       ENDIF
    1048 
    1049       ierr=NF90_DEF_DIM(nid_restart,"inter slope",nslope+1,idim9)
    1050       IF (ierr/=NF90_NOERR) THEN
    1051         write(*,*)'phyredem: problem defining inter slope dimension'
    1052         write(*,*)trim(nf90_strerror(ierr))
    1053         CALL ABORT
    1054       ENDIF
    1055 
    1056       ierr=NF90_DEF_DIM(nid_restart,"nb_str_max",nb_str_max,idim10)
    1057       IF (ierr/=NF90_NOERR) THEN
    1058         write(*,*)'phyredem: problem defining nb_str_max dimension'
    1059         write(*,*)trim(nf90_strerror(ierr))
    1060         CALL ABORT
    1061       ENDIF
    1062 
    1063       ierr=NF90_ENDDEF(nid_restart)
    1064       IF (ierr/=NF90_NOERR) THEN
    1065         write(*,*)'open_restartphy: problem ending definition mode '
    1066         write(*,*)trim(nf90_strerror(ierr))
    1067         CALL abort_physic("open_restartphy","Failed ending definition mode",1)
    1068       ENDIF
    1069     ENDIF
    1070 
    1071   END SUBROUTINE open_restartphy
    1072 !=======================================================================
    1073 
    1074 !=======================================================================
    1075   SUBROUTINE close_restartphy
    1076 !-----------------------------------------------------------------------
    1077 ! NAME
    1078 !     close_restartphy
    1079 !
    1080 ! DESCRIPTION
    1081 !     Close the restart netCDF file.
    1082 !
    1083 ! AUTHORS & DATE
    1084 !     L. Lange
    1085 !     JB Clement, 2023-2025
    1086 !
    1087 ! NOTES
    1088 !
    1089 !-----------------------------------------------------------------------
    1090 
    1091 ! DEPENDENCIES
    1092 ! ------------
    1093   USE netcdf,             only: NF90_CLOSE
    1094   USE mod_phys_lmdz_para, only: is_master
    1095 
    1096 ! DECLARATION
    1097 ! -----------
    1098 implicit none
    1099 
    1100 ! LOCAL VARIABLES
    1101 ! ---------------
    1102     INTEGER :: ierr
    1103 
    1104 ! CODE
    1105 ! ----
    1106     IF (is_master) THEN
    1107       ierr = NF90_CLOSE (nid_restart)
    1108     ENDIF
    1109 
    1110   END SUBROUTINE close_restartphy
    1111 !=======================================================================
    1112 
    1113 !=======================================================================
    1114   SUBROUTINE put_field_r1(field_name,title,field,time)
    1115 !-----------------------------------------------------------------------
    1116 ! NAME
    1117 !     put_field_r1
    1118 !
    1119 ! DESCRIPTION
    1120 !     Write a surface field to the restart file.
    1121 !
    1122 ! AUTHORS & DATE
    1123 !     L. Lange
    1124 !     JB Clement, 2023-2025
    1125 !
    1126 ! NOTES
    1127 !
    1128 !-----------------------------------------------------------------------
    1129 
    1130 ! DECLARATION
    1131 ! -----------
    1132 implicit none
    1133 
    1134 
    1135 ! ARGUMENTS
    1136 ! ---------
    1137   CHARACTER(LEN=*), INTENT(IN)           :: field_name
    1138   CHARACTER(LEN=*), INTENT(IN)           :: title
    1139   REAL,             INTENT(IN)           :: field(:)
    1140   REAL,             INTENT(IN), OPTIONAL :: time
    1141 
    1142 ! CODE
    1143 ! ----
    1144   IF (present(time)) THEN
    1145     ! if timeindex is present, it is a time-dependent variable
    1146     CALL put_field_rgen(field_name,title,field,1,time)
    1147   ELSE
    1148     CALL put_field_rgen(field_name,title,field,1)
    1149   ENDIF
    1150 
    1151   END SUBROUTINE put_field_r1
    1152 !=======================================================================
    1153 
    1154 !=======================================================================
    1155   SUBROUTINE put_field_r2(field_name,title,field,time)
    1156 !-----------------------------------------------------------------------
    1157 ! NAME
    1158 !     put_field_r2
    1159 !
    1160 ! DESCRIPTION
    1161 !     Write a "3D" horizontal-vertical field (2D) to the restart file.
    1162 
    1163 ! AUTHORS & DATE
    1164 !     L. Lange
    1165 !     JB Clement, 2023-2025
    1166 
    1167 ! NOTES
    1168 !
    1169 !-----------------------------------------------------------------------
    1170 
    1171 ! DECLARATION
    1172 ! -----------
    1173 implicit none
    1174 
    1175 ! ARGUMENTS
    1176 ! ---------
    1177   CHARACTER(LEN=*), INTENT(IN)           :: field_name
    1178   CHARACTER(LEN=*), INTENT(IN)           :: title
    1179   REAL,             INTENT(IN)           :: field(:,:)
    1180   REAL,             INTENT(IN), OPTIONAL :: time
    1181 
    1182 ! CODE
    1183 ! ----
    1184   IF (present(time)) THEN
    1185     ! if timeindex is present, it is a time-dependent variable
    1186     CALL put_field_rgen(field_name,title,field,size(field,2),time)
    1187   ELSE
    1188     CALL put_field_rgen(field_name,title,field,size(field,2))
    1189   ENDIF
    1190 
    1191   END SUBROUTINE put_field_r2
    1192 !=======================================================================
    1193 
    1194 !=======================================================================
    1195   SUBROUTINE put_field_r3(field_name,title,field,time)
    1196 !-----------------------------------------------------------------------
    1197 ! NAME
    1198 !     put_field_r3
    1199 !
    1200 ! DESCRIPTION
    1201 !     Write a "4D" field surf/alt/?? (3D) to the restart file.
    1202 
    1203 ! AUTHORS & DATE
    1204 !     L. Lange
    1205 !     JB Clement, 2023-2025
    1206 
    1207 ! NOTES
    1208 !
    1209 !-----------------------------------------------------------------------
    1210 
    1211 ! DECLARATION
    1212 ! -----------
    1213 implicit none
    1214 
    1215 ! ARGUMENTS
    1216 ! ---------
    1217   CHARACTER(LEN=*), INTENT(IN)           :: field_name
    1218   CHARACTER(LEN=*), INTENT(IN)           :: title
    1219   REAL,             INTENT(IN)           :: field(:,:,:)
    1220   REAL,             INTENT(IN), OPTIONAL :: time
    1221 
    1222 ! CODE
    1223 ! ----
    1224   IF (present(time)) THEN
    1225     ! if timeindex is present, it is a time-dependent variable
    1226     CALL put_field_rgen(field_name,title,field,size(field,2)*size(field,3),&
    1227                         time)
    1228   ELSE
    1229     CALL put_field_rgen(field_name,title,field,size(field,2)*size(field,3))
    1230   ENDIF
    1231 
    1232   END SUBROUTINE put_field_r3
    1233 !=======================================================================
    1234 
    1235 !=======================================================================
    1236   SUBROUTINE put_field_rgen(field_name,title,field,field_size,time)
    1237 !-----------------------------------------------------------------------
    1238 ! NAME
    1239 !     put_field_rgen
    1240 !
    1241 ! DESCRIPTION
    1242 !     Generic subroutine to write fields to restart file with gather from processes
    1243 
    1244 ! AUTHORS & DATE
    1245 !     L. Lange
    1246 !     JB Clement, 2023-2025
    1247 
    1248 ! NOTES
    1249 !
    1250 !-----------------------------------------------------------------------
    1251 
    1252 ! DEPENDENCIES
    1253 ! ------------
    1254   USE netcdf
    1255   USE dimphy
    1256   USE soil,         only: nsoilmx_PEM
    1257   USE comslope_mod, ONLY: nslope
    1258   USE mod_grid_phy_lmdz
    1259   USE mod_phys_lmdz_para
    1260 
    1261 ! DECLARATION
    1262 ! -----------
    1263 implicit none
    1264 
    1265 ! ARGUMENTS
    1266 ! ---------
    1267   CHARACTER(LEN=*),INTENT(IN)    :: field_name
    1268   CHARACTER(LEN=*),INTENT(IN)    :: title
    1269   INTEGER,INTENT(IN)             :: field_size
    1270   REAL,INTENT(IN)                :: field(klon,field_size)
    1271   REAL,OPTIONAL,INTENT(IN)       :: time
    1272 
    1273 ! LOCAL VARIABLES
    1274 ! ---------------
    1275   REAL                           :: field_glo(klon_glo,field_size)
    1276   INTEGER                        :: ierr
    1277   INTEGER                        :: nvarid
    1278 
    1279 ! CODE
    1280 ! ----
    1281     CALL gather(field,field_glo)
    1282 
    1283     IF (is_master) THEN
    1284 
    1285      IF (index(field_name,"stratif") == 0) then
    1286       IF (field_size==1) THEN
    1287         ! input is a 1D "surface field" array
    1288         if (.not.present(time)) then ! for a time-independent field
    1289           ierr=NF90_REDEF(nid_restart)
    1290 #ifdef NC_DOUBLE
    1291           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1292                             (/idim2/),nvarid)
    1293 #else
    1294           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1295                             (/idim2/),nvarid)
    1296 #endif
    1297           if (ierr.ne.NF90_NOERR) then
    1298             write(*,*)"put_field_rgen error: failed to define "//trim(field_name)
    1299             write(*,*)trim(nf90_strerror(ierr))
    1300           endif
    1301           IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1302           ierr=NF90_ENDDEF(nid_restart)
    1303           ierr=NF90_PUT_VAR(nid_restart,nvarid,field_glo)
    1304         else
    1305           ! check if the variable has already been defined:
    1306           ierr=NF90_INQ_VARID(nid_restart,field_name,nvarid)
    1307           if (ierr/=NF90_NOERR) then ! variable not found, define it
    1308             ierr=NF90_REDEF(nid_restart)
    1309 #ifdef NC_DOUBLE
    1310             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1311                               (/idim2,idim7/),nvarid)
    1312 #else
    1313             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1314                               (/idim2,idim7/),nvarid)
    1315 #endif
    1316             if (ierr.ne.NF90_NOERR) then
    1317               write(*,*)"put_field_rgen error: failed to define "//trim(field_name)
    1318               write(*,*)trim(nf90_strerror(ierr))
    1319             endif
    1320             IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1321             ierr=NF90_ENDDEF(nid_restart)
    1322           endif
    1323           ! Write the variable
    1324           ierr=NF90_PUT_VAR(nid_restart,nvarid,field_glo,&
    1325                             start=(/1,timeindex/))
    1326         endif ! of if (.not.present(timeindex))
    1327 
    1328       ELSE IF (field_size==klev) THEN
    1329         ! input is a 2D "atmospheric field" array
    1330         if (.not.present(time)) then ! for a time-independent field
    1331           ierr=NF90_REDEF(nid_restart)
    1332 #ifdef NC_DOUBLE
    1333           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1334                             (/idim2,idim6/),nvarid)
    1335 #else
    1336           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1337                             (/idim2,idim6/),nvarid)
    1338 #endif
    1339           if (ierr.ne.NF90_NOERR) then
    1340             write(*,*)"put_field_rgen error: failed to define "//trim(field_name)
    1341             write(*,*)trim(nf90_strerror(ierr))
    1342           endif
    1343           IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1344           ierr=NF90_ENDDEF(nid_restart)
    1345           ierr=NF90_PUT_VAR(nid_restart,nvarid,field_glo)
    1346         else
    1347           ! check if the variable has already been defined:
    1348           ierr=NF90_INQ_VARID(nid_restart,field_name,nvarid)
    1349           if (ierr/=NF90_NOERR) then ! variable not found, define it
    1350             ierr=NF90_REDEF(nid_restart)
    1351 #ifdef NC_DOUBLE
    1352             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1353                               (/idim2,idim6,idim7/),nvarid)
    1354 #else
    1355             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1356                               (/idim2,idim6,idim7/),nvarid)
    1357 #endif
    1358             if (ierr.ne.NF90_NOERR) then
    1359               write(*,*)"put_field_rgen error: failed to define "//trim(field_name)
    1360               write(*,*)trim(nf90_strerror(ierr))
    1361             endif
    1362             IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1363             ierr=NF90_ENDDEF(nid_restart)
    1364           endif
    1365           ! Write the variable
    1366           ierr=NF90_PUT_VAR(nid_restart,nvarid,field_glo,&
    1367                             start=(/1,1,timeindex/))
    1368         endif ! of if (.not.present(time))
    1369 
    1370       ELSE IF (field_size==klevp1) THEN
    1371         ! input is a 2D "interlayer atmospheric field" array
    1372         if (.not.present(time)) then ! for a time-independent field
    1373           ierr=NF90_REDEF(nid_restart)
    1374 #ifdef NC_DOUBLE
    1375           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1376                             (/idim2,idim4/),nvarid)
    1377 #else
    1378           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1379                             (/idim2,idim4/),nvarid)
    1380 #endif
    1381           if (ierr.ne.NF90_NOERR) then
    1382             write(*,*)"put_field_rgen error: failed to define "//trim(field_name)
    1383             write(*,*)trim(nf90_strerror(ierr))
    1384           endif
    1385           IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1386           ierr = NF90_ENDDEF(nid_restart)
    1387           ierr = NF90_PUT_VAR(nid_restart,nvarid,field_glo)
    1388         else
    1389           ! check if the variable has already been defined:
    1390           ierr=NF90_INQ_VARID(nid_restart,field_name,nvarid)
    1391           if (ierr/=NF90_NOERR) then ! variable not found, define it
    1392             ierr=NF90_REDEF(nid_restart)
    1393 #ifdef NC_DOUBLE
    1394             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1395                               (/idim2,idim4,idim7/),nvarid)
    1396 #else
    1397             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1398                               (/idim2,idim4,idim7/),nvarid)
    1399 #endif
    1400             if (ierr.ne.NF90_NOERR) then
    1401               write(*,*)"put_field_rgen error: failed to define "//trim(field_name)
    1402               write(*,*)trim(nf90_strerror(ierr))
    1403             endif
    1404             IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1405             ierr=NF90_ENDDEF(nid_restart)
    1406           endif
    1407           ! Write the variable
    1408           ierr=NF90_PUT_VAR(nid_restart,nvarid,field_glo,&
    1409                             start=(/1,1,timeindex/))
    1410         endif ! of if (.not.present(timeindex))
    1411 
    1412       ELSE IF (field_size==nsoilmx_PEM) THEN
    1413         ! input is a 2D "subsurface field" array
    1414         if (.not.present(time)) then ! for a time-independent field
    1415           ierr = NF90_REDEF(nid_restart)
    1416 #ifdef NC_DOUBLE
    1417           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1418                             (/idim2,idim3/),nvarid)
    1419 #else
    1420           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1421                             (/idim2,idim3/),nvarid)
    1422 #endif
    1423           if (ierr.ne.NF90_NOERR) then
    1424             write(*,*)"put_field_rgen error: failed to define "//trim(field_name)
    1425             write(*,*)trim(nf90_strerror(ierr))
    1426           endif
    1427           IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1428           ierr = NF90_ENDDEF(nid_restart)
    1429           ierr = NF90_PUT_VAR(nid_restart,nvarid,field_glo)
    1430         else
    1431           ! check if the variable has already been defined:
    1432           ierr=NF90_INQ_VARID(nid_restart,field_name,nvarid)
    1433           if (ierr/=NF90_NOERR) then ! variable not found, define it
    1434             ierr=NF90_REDEF(nid_restart)
    1435 #ifdef NC_DOUBLE
    1436             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1437                               (/idim2,idim3,idim7/),nvarid)
    1438 #else
    1439             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1440                               (/idim2,idim3,idim7/),nvarid)
    1441 #endif
    1442            if (ierr.ne.NF90_NOERR) then
    1443               write(*,*)"put_field_rgen error: failed to define "//trim(field_name)
    1444               write(*,*)trim(nf90_strerror(ierr))
    1445             endif
    1446             IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1447             ierr=NF90_ENDDEF(nid_restart)
    1448           endif
    1449           ! Write the variable
    1450           ierr=NF90_PUT_VAR(nid_restart,nvarid,field_glo,&
    1451                             start=(/1,1,timeindex/))
    1452 
    1453         endif ! of if (.not.present(time))
    1454 
    1455 
    1456       ELSE IF (field_size==nslope) THEN
    1457         ! input is a 2D "subsurface field" array
    1458         if (.not.present(time)) then ! for a time-independent field
    1459           ierr = NF90_REDEF(nid_restart)
    1460 #ifdef NC_DOUBLE
    1461           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1462                             (/idim2,idim8/),nvarid)
    1463 #else
    1464           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1465                             (/idim2,idim8/),nvarid)
    1466 #endif
    1467           if (ierr.ne.NF90_NOERR) then
    1468             write(*,*)"put_field_rgen error: failed to define"//trim(field_name)
    1469             write(*,*)trim(nf90_strerror(ierr))
    1470           endif
    1471           IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1472           ierr = NF90_ENDDEF(nid_restart)
    1473           ierr = NF90_PUT_VAR(nid_restart,nvarid,field_glo)
    1474         else
    1475           ! check if the variable has already been defined:
    1476           ierr=NF90_INQ_VARID(nid_restart,field_name,nvarid)
    1477           if (ierr/=NF90_NOERR) then ! variable not found, define it
    1478             ierr=NF90_REDEF(nid_restart)
    1479 #ifdef NC_DOUBLE
    1480             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1481                               (/idim2,idim8,idim7/),nvarid)
    1482 #else
    1483             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1484                               (/idim2,idim8,idim7/),nvarid)
    1485 #endif
    1486            if (ierr.ne.NF90_NOERR) then
    1487               write(*,*)"put_field_rgen error: failed to define"//trim(field_name)
    1488               write(*,*)trim(nf90_strerror(ierr))
    1489             endif
    1490             IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1491             ierr=NF90_ENDDEF(nid_restart)
    1492           endif
    1493           ! Write the variable
    1494           ierr=NF90_PUT_VAR(nid_restart,nvarid,field_glo,&
    1495                             start=(/1,1,timeindex/))
    1496 
    1497         endif ! of if (.not.present(time))
    1498 
    1499       ELSE
    1500         write(*,*) "Error phyredem(put_field_rgen) : wrong dimension for ",trim(field_name)
    1501         write(*,*) "  field_size =",field_size
    1502         CALL abort_physic("put_field_rgen","wrong field dimension",1)
    1503       ENDIF
    1504      ELSE
    1505         ! input is a 2D "stratification" array
    1506         if (.not.present(time)) then ! for a time-independent field
    1507           ierr = NF90_REDEF(nid_restart)
    1508 #ifdef NC_DOUBLE
    1509           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1510                             (/idim2,idim8/),nvarid)
    1511 #else
    1512           ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1513                             (/idim2,idim8/),nvarid)
    1514 #endif
    1515           if (ierr.ne.NF90_NOERR) then
    1516             write(*,*)"put_field_rgen error: failed to define"//trim(field_name)
    1517             write(*,*)trim(nf90_strerror(ierr))
    1518           endif
    1519           IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1520           ierr = NF90_ENDDEF(nid_restart)
    1521           ierr = NF90_PUT_VAR(nid_restart,nvarid,field_glo)
    1522         else
    1523           ! check if the variable has already been defined:
    1524           ierr=NF90_INQ_VARID(nid_restart,field_name,nvarid)
    1525           if (ierr/=NF90_NOERR) then ! variable not found, define it
    1526             ierr=NF90_REDEF(nid_restart)
    1527 #ifdef NC_DOUBLE
    1528             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_DOUBLE,&
    1529                               (/idim2,idim10,idim7/),nvarid)
    1530 #else
    1531             ierr=NF90_DEF_VAR(nid_restart,field_name,NF90_FLOAT,&
    1532                               (/idim2,idim10,idim7/),nvarid)
    1533 #endif
    1534            if (ierr.ne.NF90_NOERR) then
    1535               write(*,*)"put_field_rgen error: failed to define"//trim(field_name)
    1536               write(*,*)trim(nf90_strerror(ierr))
    1537             endif
    1538             IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1539             ierr=NF90_ENDDEF(nid_restart)
    1540           endif
    1541           ! Write the variable
    1542           ierr=NF90_PUT_VAR(nid_restart,nvarid,field_glo,&
    1543                             start=(/1,1,timeindex/))
    1544 
    1545         endif ! of if (.not.present(time))
    1546      ENDIF
    1547 
    1548       ! Check the writting of field to file went OK
    1549       if (ierr.ne.NF90_NOERR) then
    1550         write(*,*) " Error phyredem(put_field_rgen) : failed writing ",trim(field_name)
    1551         write(*,*)trim(nf90_strerror(ierr))
    1552         CALL abort_physic("put_field_rgen","Failed writing field",1)
    1553       endif
    1554 
    1555     ENDIF ! of IF (is_master)
    1556 
    1557   END SUBROUTINE put_field_rgen
    1558 !=======================================================================
    1559 
    1560 !=======================================================================
    1561   SUBROUTINE put_var_r0(var_name,title,var)
    1562 !-----------------------------------------------------------------------
    1563 ! NAME
    1564 !     put_var_r0
    1565 !
    1566 ! DESCRIPTION
    1567 !     Write a scalar variable to the restart file
    1568 
    1569 ! AUTHORS & DATE
    1570 !     L. Lange
    1571 !     JB Clement, 2023-2025
    1572 
    1573 ! NOTES
    1574 !
    1575 !-----------------------------------------------------------------------
    1576 
    1577 ! DECLARATION
    1578 ! -----------
    1579 implicit none
    1580 
    1581 ! ARGUMENTS
    1582 ! ---------
    1583   CHARACTER(LEN=*), INTENT(IN) :: var_name
    1584   CHARACTER(LEN=*), INTENT(IN) :: title
    1585   REAL,             INTENT(IN) :: var
    1586 
    1587 ! LOCAL VARIABLES
    1588 ! ---------------
    1589      REAL :: varin(1)
    1590 
    1591 ! CODE
    1592 ! ----
    1593      varin(1)=var
    1594 
    1595      CALL put_var_rgen(var_name,title,varin,size(varin))
    1596 
    1597   END SUBROUTINE put_var_r0
    1598 !=======================================================================
    1599 
    1600 !=======================================================================
    1601   SUBROUTINE put_var_r1(var_name,title,var)
    1602 !-----------------------------------------------------------------------
    1603 ! NAME
    1604 !    put_var_r1
    1605 !
    1606 ! DESCRIPTION
    1607 !     Write a vector (1D) variable to the restart file
    1608 
    1609 ! AUTHORS & DATE
    1610 !     L. Lange
    1611 !     JB Clement, 2023-2025
    1612 
    1613 ! NOTES
    1614 !
    1615 !-----------------------------------------------------------------------
    1616 
    1617 ! DECLARATION
    1618 ! -----------
    1619 implicit none
    1620 
    1621 ! ARGUMENTS
    1622 ! ---------
    1623   CHARACTER(LEN=*), INTENT(IN) :: var_name
    1624   CHARACTER(LEN=*), INTENT(IN) :: title
    1625   REAL,             INTENT(IN) :: var(:)
    1626 
    1627 ! CODE
    1628 ! ----
    1629   CALL put_var_rgen(var_name,title,var,size(var))
    1630 
    1631   END SUBROUTINE put_var_r1
    1632 !=======================================================================
    1633 
    1634 !=======================================================================
    1635   SUBROUTINE put_var_r2(var_name,title,var)
    1636 !-----------------------------------------------------------------------
    1637 ! NAME
    1638 !     put_var_r2
    1639 !
    1640 ! DESCRIPTION
    1641 !     Write a 3D field variable to the restart file
    1642 
    1643 ! AUTHORS & DATE
    1644 !     L. Lange
    1645 !     JB Clement, 2023-2025
    1646 
    1647 ! NOTES
    1648 !
    1649 !-----------------------------------------------------------------------
    1650 
    1651 ! DECLARATION
    1652 ! -----------
    1653 implicit none
    1654 
    1655 ! ARGUMENTS
    1656 ! ---------
    1657   CHARACTER(LEN=*), INTENT(IN) :: var_name
    1658   CHARACTER(LEN=*), INTENT(IN) :: title
    1659   REAL,             INTENT(IN) :: var(:,:)
    1660 
    1661 ! CODE
    1662 ! ----
    1663   CALL put_var_rgen(var_name,title,var,size(var))
    1664 
    1665   END SUBROUTINE put_var_r2
    1666 !=======================================================================
    1667 
    1668 !=======================================================================
    1669   SUBROUTINE put_var_r3(var_name,title,var)
    1670 !-----------------------------------------------------------------------
    1671 ! NAME
    1672 !     put_var_r3
    1673 !
    1674 ! DESCRIPTION
    1675 !     Write a 3D field variable to the restart file
    1676 !
    1677 ! AUTHORS & DATE
    1678 !     L. Lange
    1679 !     JB Clement, 2023-2025
    1680 !
    1681 ! NOTES
    1682 !
    1683 !-----------------------------------------------------------------------
    1684 
    1685 ! DECLARATION
    1686 ! -----------
    1687 implicit none
    1688 
    1689 ! ARGUMENTS
    1690 ! ---------
    1691   CHARACTER(LEN=*), INTENT(IN) :: var_name
    1692   CHARACTER(LEN=*), INTENT(IN) :: title
    1693   REAL,             INTENT(IN) :: var(:,:,:)
    1694 
    1695 ! CODE
    1696 ! ----
    1697   CALL put_var_rgen(var_name,title,var,size(var))
    1698 
    1699   END SUBROUTINE put_var_r3
    1700 !=======================================================================
    1701 
    1702 !=======================================================================
    1703   SUBROUTINE put_var_rgen(var_name,title,var,var_size)
    1704 !-----------------------------------------------------------------------
    1705 ! NAME
    1706 !     put_var_rgen
    1707 !
    1708 ! DESCRIPTION
    1709 !     Generic subroutine to write variables to the restart file
    1710 
    1711 ! AUTHORS & DATE
    1712 !     L. Lange
    1713 !     JB Clement, 2023-2025
    1714 
    1715 ! NOTES
    1716 !
    1717 !-----------------------------------------------------------------------
    1718 
    1719 ! DEPENDENCIES
    1720 ! ------------
    1721   USE netcdf, only: NF90_REDEF, NF90_DEF_VAR, NF90_ENDDEF, NF90_PUT_VAR, &
    1722                     NF90_FLOAT, NF90_DOUBLE, &
    1723                     NF90_PUT_ATT, NF90_NOERR, nf90_strerror, &
    1724                     nf90_inq_dimid, nf90_inquire_dimension, NF90_INQ_VARID
    1725   USE soil,               only:  nsoilmx_PEM
    1726   USE comslope_mod,       only: nslope
    1727   USE mod_phys_lmdz_para, only: is_master
    1728 
    1729 ! DECLARATION
    1730 ! -----------
    1731 implicit none
    1732 
    1733 ! ARGUMENTS
    1734 ! ---------
    1735   CHARACTER(LEN=*), INTENT(IN) :: var_name
    1736   CHARACTER(LEN=*), INTENT(IN) :: title
    1737   INTEGER,          INTENT(IN) :: var_size
    1738   REAL,             INTENT(IN) :: var(var_size)
    1739 
    1740 ! LOCAL VARIABLES
    1741 ! ---------------
    1742   INTEGER       :: ierr
    1743   INTEGER       :: nvarid
    1744   INTEGER       :: idim1d
    1745   LOGICAL, SAVE :: firsttime = .true.
    1746 
    1747 ! CODE
    1748 ! ----
    1749     IF (is_master) THEN
    1750 
    1751       IF (var_name=="Time") THEN
    1752         ! Very specific case of "Time" variable
    1753         if (firsttime) then
    1754           ! Create the "Time variable"
    1755           ierr=NF90_REDEF(nid_restart)
    1756 #ifdef NC_DOUBLE
    1757           ierr=NF90_DEF_VAR(nid_restart,var_name,NF90_DOUBLE,&
    1758                             (/idim7/),nvarid)
    1759 #else
    1760           ierr=NF90_DEF_VAR(nid_restart,var_name,NF90_FLOAT,&
    1761                             (/idim7/),nvarid)
    1762 #endif
    1763           IF (LEN_TRIM(title) > 0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1764           ierr=NF90_ENDDEF(nid_restart)
    1765 
    1766           firsttime=.false.
    1767         endif
    1768         ! Append "time" value
    1769         ! get current length of "Time" dimension
    1770         ierr=nf90_inq_dimid(nid_restart,var_name,idim1d)
    1771         ierr=nf90_inquire_dimension(nid_restart,idim1d,len=timeindex)
    1772         timeindex=timeindex+1
    1773         ierr=NF90_INQ_VARID(nid_restart,var_name,nvarid)
    1774         ierr=NF90_PUT_VAR(nid_restart,nvarid,var,&
    1775                            start=(/timeindex/))
    1776         IF (ierr/=NF90_NOERR) THEN
    1777           write(*,*)'put_var_rgen: problem writing Time'
    1778           write(*,*)trim(nf90_strerror(ierr))
    1779           CALL abort_physic("get_var_rgen","Failed to write Time",1)
    1780         ENDIF
    1781         return ! nothing left to do
    1782       ELSEIF (var_size==length) THEN
    1783         ! We know it is a "controle" kind of 1D array
    1784         idim1d=idim1
    1785       ELSEIF (var_size==nsoilmx_PEM) THEN
    1786         ! We know it is an "mlayer" kind of 1D array
    1787         idim1d=idim3
    1788       ELSEIF (var_size==nslope+1) THEN
    1789         ! We know it is an "inter slope" kind of 1D array
    1790         idim1d=idim9
    1791       ELSEIF (var_name == "nb_str_max") THEN
    1792         ! We know it is a kind of stratification array
    1793         idim1d = idim10
    1794       ELSE
    1795         write(*,*) "put_var_rgen error : wrong dimension"
    1796         write(*,*) "  var_size =",var_size
    1797         CALL abort_physic("get_var_rgen","Wrong variable dimension",1)
    1798 
    1799       ENDIF ! of IF (var_size==length) THEN
    1800 
    1801       ! Swich to NetCDF define mode
    1802       ierr=NF90_REDEF (nid_restart)
    1803       ! Define the variable
    1804 #ifdef NC_DOUBLE
    1805       ierr=NF90_DEF_VAR(nid_restart,var_name,NF90_DOUBLE,(/idim1d/),nvarid)
    1806 #else
    1807       ierr=NF90_DEF_VAR(nid_restart,var_name,NF90_FLOAT,(/idim1d/),nvarid)
    1808 #endif
    1809       ! Add a "title" attribute
    1810       IF (LEN_TRIM(title)>0) ierr=NF90_PUT_ATT(nid_restart,nvarid,"title",title)
    1811       ! Swich out of define mode
    1812       ierr=NF90_ENDDEF(nid_restart)
    1813       ! Write variable to file
    1814       ierr=NF90_PUT_VAR(nid_restart,nvarid,var)
    1815       IF (ierr/=NF90_NOERR) THEN
    1816         write(*,*)'put_var_rgen: problem writing '//trim(var_name)
    1817         write(*,*)trim(nf90_strerror(ierr))
    1818         CALL abort_physic("get_var_rgen","Failed writing variable",1)
    1819       ENDIF
    1820     ENDIF ! of IF (is_master)
    1821 
    1822   END SUBROUTINE put_var_rgen
    1823 !=======================================================================
    1824 
    1825 END MODULE iostart_pem
     1264logical(k4)            :: has_fill, has_range, has_valid_min, has_valid_max
     1265real(dp)               :: fill_value, valid_min, valid_max
     1266real(dp), dimension(2) :: valid_range
     1267integer(di)            :: ncid
     1268
     1269! CODE
     1270! ----
     1271! Diagevol logic priority over standard logic
     1272if (open_diagevol) then
     1273    ncid = ncid_diagevol
     1274else
     1275    ncid = ncid_file
     1276end if
     1277
     1278! NaN
     1279if (any(var /= var)) call stop_clean(__FILE__,__LINE__,'NaN detected in variable '//var_name//'!',1)
     1280
     1281! Infinite
     1282if (any(abs(var) > largest_nb)) call stop_clean(__FILE__,__LINE__,'Infs detected in variable '//var_name//'!',1)
     1283
     1284! Fill value
     1285has_fill = .false.
     1286call check_nc(nf90_get_att(ncid,varid,"_FillValue",fill_value),'getting fill value',has_fill)
     1287if (.not. has_fill) call check_nc(nf90_get_att(ncid,varid,"missing_value",fill_value),'getting missing value',has_fill)
     1288if (has_fill .and. any(var == fill_value)) call stop_clean(__FILE__,__LINE__,'Fill values ('//real2str(fill_value)//') detected in '//var_name//'!',1)
     1289
     1290! Valid range
     1291has_range = .false.
     1292call check_nc(nf90_get_att(ncid,varid,"valid_range",valid_range),'getting valid range',has_range)
     1293if (has_range) then
     1294    valid_min = valid_range(1)
     1295    valid_max = valid_range(2)
     1296else! valid_min / valid_max (fallback)
     1297    has_valid_min = .false.
     1298    has_valid_max = .false.
     1299    call check_nc(nf90_get_att(ncid,varid,"valid_min",valid_min),'getting valid min',has_valid_min)
     1300    call check_nc(nf90_get_att(ncid,varid,"valid_max",valid_max),'getting valid max',has_valid_max)
     1301    if (has_valid_min .or. has_valid_max) then
     1302        has_range = .true.
     1303        if (.not. has_valid_min) valid_min = -largest_nb
     1304        if (.not. has_valid_max) valid_max = largest_nb
     1305    end if
     1306end if
     1307if (has_range .and. (any(var < valid_min) .or. any(var > valid_max))) call stop_clean(__FILE__,__LINE__,'Values outside valid range ('//real2str(valid_min)//','//real2str(valid_max)//') detected in '//var_name//'!',1)
     1308
     1309END SUBROUTINE check_valid_var4d_nc
     1310!=======================================================================
     1311
     1312END MODULE io_netcdf
Note: See TracChangeset for help on using the changeset viewer.