Forked from
Méso-NH / Méso-NH code
41 commits behind the upstream repository.
-
WAUTELET Philippe authoredWAUTELET Philippe authored
mode_io_manage_struct.f90 78.27 KiB
!MNH_LIC Copyright 2016-2024 CNRS, Meteo-France and Universite Paul Sabatier
!MNH_LIC This is part of the Meso-NH software governed by the CeCILL-C licence
!MNH_LIC version 1. See LICENSE, CeCILL-C_V1-en.txt and CeCILL-C_V1-fr.txt
!MNH_LIC for details. version 1.
!-----------------------------------------------------------------
! Author(s)
! P. Wautelet 2016
! Modifications:
! P. Wautelet 05/2016-04/2018: new data structures and calls for I/O
! P. Wautelet 21/01/2019: add LIO_ALLOW_NO_BACKUP and LIO_NO_WRITE to modd_io_ll
! to allow to disable writes (for bench purposes)
! P. Wautelet 06/02/2019: simplify OPEN_ll and do somme assignments at a more logical place
! P. Wautelet 07/02/2019: force TYPE to a known value for IO_File_add2list
! P. Wautelet 07/02/2019: remove OPARALLELIO argument from open and close files subroutines
! (nsubfiles_ioz is now determined in IO_File_add2list)
! P. Wautelet 18/02/2019: bugfixes for nsubfiles_ioz
! P. Wautelet 05/03/2019: rename IO subroutines and modules
! P. Wautelet 12/03/2019: add TMAINFILE field in TFILEDATA
! P. Wautelet 11/02/2020: bugfix: TDADFILE was wrongly constructed for output files
! S. Donnier 28/02/2020: type STREAM needed for use of ECOCLIMAP SG
! P. Wautelet 08/01/2021: allow output files with empty variable list (useful if IO_Field_user_write is not empty)
! P. Wautelet 18/03/2022: minor bugfix in ISTEP_MAX computation + adapt diagnostics messages
! (change verbosity level and remove some unnecessary warnings)
! P. Wautelet 13/01/2023: set NMODEL field for backup and output files
! P. Wautelet 14/12/2023: add lossy compression for output files
! P. Wautelet 17/01/2024: add IO_File_remove_from_list subroutine
! P. Wautelet 02/02/2024: restructure backups/outputs lists
! P. Wautelet 07/02/2024: add compression for all netCDF files
! P. Wautelet 20/03/2024: add boxes for output files
!-----------------------------------------------------------------
MODULE MODE_IO_MANAGE_STRUCT
!
USE MODD_IO
use modd_precision, only: LFIINT
!
USE MODE_MSG
!
IMPLICIT NONE
!
private
!
public :: IO_Bakout_struct_prepare, IO_File_find_byname, IO_Filelist_print
public :: IO_File_add2list, IO_File_remove_from_list
public :: IO_Is_backup_time, IO_Is_output_time, IO_BakOut_file_create
!
! Integers for file stats
INTEGER, SAVE :: NFILE_STAT_NADD = 0 ! Number of files added to file list
INTEGER, SAVE :: NFILE_STAT_NREM = 0 ! Number of files removed from file list
INTEGER, SAVE :: NFILE_STAT_CURSIZE = 0 ! Current number of files in file list
INTEGER, SAVE :: NFILE_STAT_MAXSIZE = 0 ! Maximum number of files in file list
!
CONTAINS
!
!###################################################
SUBROUTINE IO_Bakout_struct_prepare( )
!###################################################
!
USE MODD_BAKOUT
USE MODD_CONF, ONLY: NMODEL
USE MODD_DYN, ONLY: XSEGLEN
USE MODD_DYN_n, ONLY: DYN_MODEL
USE MODD_FIELD, ONLY: TFIELDLIST
USE MODD_NESTING, ONLY: NDAD
USE MODD_SUB_MODEL_N, ONLY: NFILE_BACKUP_CURRENT, NFILE_OUTPUT_CURRENT, SUB_MODEL_MODEL
USE MODD_OUT_n, ONLY: OUT_GOTO_MODEL, OUT_MODEL, TOUT_BOXES
USE MODD_VAR_ll, ONLY: IP
USE MODN_BACKUP, ONLY: BACKUP_NML_DEALLOCATE
USE MODN_OUTPUT, ONLY: OUTPUT_NML_DEALLOCATE
IMPLICIT NONE
!
INTEGER, PARAMETER :: NSUPTS = 1 ! supp. time steps
!
INTEGER :: IBAK_NUM
INTEGER :: IOUT_NUM
INTEGER :: IMI ! Model number for loop
INTEGER :: IERR_LVL ! Level of error message
INTEGER :: ISTEP
INTEGER :: ISTEP_MAX ! Number of timesteps
INTEGER :: IPOS ! Index
INTEGER :: JOUT,IDX ! Loop indices
INTEGER :: JI
INTEGER :: IRESP
INTEGER :: ISTEPDADFIRST
INTEGER, DIMENSION(:,:), ALLOCATABLE :: IBAK_STEPLIST
INTEGER, DIMENSION(:,:), ALLOCATABLE :: IOUT_STEPLIST
! Arrays to store list of backup/output steps (intermediate array)
REAL :: ZTSTEP_RND
!
!
CALL PRINT_MSG(NVERB_DEBUG,'IO','IO_Bakout_struct_prepare','called')
!
! Special case if writes are forced to NO
IF (LIO_NO_WRITE) THEN
DO IMI = 1, NMODEL
OUT_MODEL(IMI)%NBAK_NUMB = 0
OUT_MODEL(IMI)%NOUT_NUMB = 0
END DO
RETURN
END IF
! Copy NBAK_STEP into IBAK_STEPLIST. All backup steps will be stored in it (except regular series)
ALLOCATE( IBAK_STEPLIST, SOURCE=NBAK_STEP )
ALLOCATE( IOUT_STEPLIST, SOURCE=NOUT_STEP )
! Treat regular series for all models before next loop on models
! This is necessary to have a first version of them before synchronizing them to the nested submodels
DO IMI = 1, NMODEL
! Backup regular series is provided with intervals in seconds
IF ( XBAK_TIME_FREQ(IMI) > 0. ) THEN
IF ( NBAK_STEP_FREQ(IMI) > 0 ) &
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', &
'XBAK_TIME_FREQ and NBAK_STEP_FREQ can not be provided simultaneously' )
! Check that frequency is at least equals to the model time step
IF ( XBAK_TIME_FREQ(IMI) < DYN_MODEL(IMI)%XTSTEP - 1.E-6 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', 'XBAK_TIME_FREQ smaller than model timestep' )
XBAK_TIME_FREQ(IMI) = DYN_MODEL(IMI)%XTSTEP
END IF
! Check that the frequency is a multiple of the model time step
ZTSTEP_RND = NINT( XBAK_TIME_FREQ(IMI) / DYN_MODEL(IMI)%XTSTEP ) * DYN_MODEL(IMI)%XTSTEP
IF ( ABS( ZTSTEP_RND - XBAK_TIME_FREQ(IMI) ) > 1.E-6 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', 'XBAK_TIME_FREQ is not a multiple of the model timestep' )
XBAK_TIME_FREQ(IMI) = ZTSTEP_RND
END IF
IF ( XBAK_TIME_FREQ_FIRST(IMI) > 0. ) THEN
! Check that the first write time of the series is a multiple of the model time step
ZTSTEP_RND = NINT( XBAK_TIME_FREQ_FIRST(IMI) / DYN_MODEL(IMI)%XTSTEP ) * DYN_MODEL(IMI)%XTSTEP
IF ( ABS( ZTSTEP_RND - XBAK_TIME_FREQ_FIRST(IMI) ) > 1.E-6 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', &
'XBAK_TIME_FREQ_FIRST is not a multiple of the model timestep' )
XBAK_TIME_FREQ_FIRST(IMI) = ZTSTEP_RND
END IF
END IF
! Check that the first write time of the series is not after the end of the segment
IF ( XBAK_TIME_FREQ_FIRST(IMI) > XSEGLEN + 1.E-6 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', &
'XBAK_TIME_FREQ_FIRST is after the end of the simulation segment' )
END IF
! Do not mix NBAK_STEP_FREQ_FIRST with XBAK_TIME_FREQ
IF ( NBAK_STEP_FREQ_FIRST(IMI) > 0 ) THEN
CMNHMSG(1) = 'NBAK_STEP_FREQ_FIRST is not allowed with XBAK_TIME_FREQ'
CMNHMSG(2) = 'use XBAK_TIME_FREQ_FIRST instead'
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare' )
END IF
! Set the backup frequency in timesteps
OUT_MODEL(IMI)%NBAK_STEPFREQ = NINT( XBAK_TIME_FREQ(IMI) / DYN_MODEL(IMI)%XTSTEP )
IF ( XBAK_TIME_FREQ_FIRST(IMI) >= 0. ) THEN
OUT_MODEL(IMI)%NBAK_STEPFREQFIRST = NINT( XBAK_TIME_FREQ_FIRST(IMI) / DYN_MODEL(IMI)%XTSTEP ) + 1
ELSE
! Set first backup to frequency
OUT_MODEL(IMI)%NBAK_STEPFREQFIRST = OUT_MODEL(IMI)%NBAK_STEPFREQ + 1
END IF
END IF
! Backup regular series is provided with intervals in timesteps
IF ( NBAK_STEP_FREQ(IMI) > 0 ) THEN
OUT_MODEL(IMI)%NBAK_STEPFREQ = NBAK_STEP_FREQ(IMI)
! Do not mix XBAK_TIME_FREQ_FIRST with NBAK_STEP_FREQ
IF ( XBAK_TIME_FREQ_FIRST(IMI) >= 0 ) THEN
CMNHMSG(1) = 'XBAK_TIME_FREQ_FIRST is not allowed with NBAK_STEP_FREQ'
CMNHMSG(2) = 'use NBAK_STEP_FREQ_FIRST instead'
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare' )
END IF
IF ( NBAK_STEP_FREQ_FIRST(IMI) > 0 ) THEN
OUT_MODEL(IMI)%NBAK_STEPFREQFIRST = NBAK_STEP_FREQ_FIRST(IMI)
ELSE
! Set first backup to frequency
OUT_MODEL(IMI)%NBAK_STEPFREQFIRST = OUT_MODEL(IMI)%NBAK_STEPFREQ + 1
END IF
END IF
! Output regular series is provided with intervals in seconds
IF ( XOUT_TIME_FREQ(IMI) > 0. ) THEN
IF ( NOUT_STEP_FREQ(IMI) > 0 ) &
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', &
'XOUT_TIME_FREQ and NOUT_STEP_FREQ can not be provided simultaneously' )
! Check that frequency is at least equals to the model time step
IF ( XOUT_TIME_FREQ(IMI) < DYN_MODEL(IMI)%XTSTEP - 1.E-6 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', 'XOUT_TIME_FREQ smaller than model timestep' )
XOUT_TIME_FREQ(IMI) = DYN_MODEL(IMI)%XTSTEP
END IF
! Check that the frequency is a multiple of the model time step
ZTSTEP_RND = NINT( XOUT_TIME_FREQ(IMI) / DYN_MODEL(IMI)%XTSTEP ) * DYN_MODEL(IMI)%XTSTEP
IF ( ABS( ZTSTEP_RND - XOUT_TIME_FREQ(IMI) ) > 1.E-6 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', 'XOUT_TIME_FREQ is not a multiple of the model timestep' )
XOUT_TIME_FREQ(IMI) = ZTSTEP_RND
END IF
IF ( XOUT_TIME_FREQ_FIRST(IMI) > 0. ) THEN
! Check that the first write time of the series is a multiple of the model time step
ZTSTEP_RND = NINT( XOUT_TIME_FREQ_FIRST(IMI) / DYN_MODEL(IMI)%XTSTEP ) * DYN_MODEL(IMI)%XTSTEP
IF ( ABS( ZTSTEP_RND - XOUT_TIME_FREQ_FIRST(IMI) ) > 1.E-6 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', &
'XOUT_TIME_FREQ_FIRST is not a multiple of the model timestep' )
XOUT_TIME_FREQ_FIRST(IMI) = ZTSTEP_RND
END IF
END IF
! Check that the first write time of the series is not after the end of the segment
IF ( XOUT_TIME_FREQ_FIRST(IMI) > XSEGLEN + 1.E-6 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', &
'XOUT_TIME_FREQ_FIRST is after the end of the simulation segment' )
END IF
! Do not mix NOUT_STEP_FREQ_FIRST with XOUT_TIME_FREQ
IF ( NOUT_STEP_FREQ_FIRST(IMI) > 0 ) THEN
CMNHMSG(1) = 'NOUT_STEP_FREQ_FIRST is not allowed with XOUT_TIME_FREQ'
CMNHMSG(2) = 'use XOUT_TIME_FREQ_FIRST instead'
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare' )
END IF
! Set the output frequency in timesteps
OUT_MODEL(IMI)%NOUT_STEPFREQ = NINT( XOUT_TIME_FREQ(IMI) / DYN_MODEL(IMI)%XTSTEP )
IF ( XOUT_TIME_FREQ_FIRST(IMI) > 0. ) THEN
OUT_MODEL(IMI)%NOUT_STEPFREQFIRST = NINT( XOUT_TIME_FREQ_FIRST(IMI) / DYN_MODEL(IMI)%XTSTEP ) + 1
ELSE
! Set first output to frequency
OUT_MODEL(IMI)%NOUT_STEPFREQFIRST = OUT_MODEL(IMI)%NOUT_STEPFREQ + 1
END IF
END IF
! Backup regular series is provided with intervals in timesteps
IF ( NOUT_STEP_FREQ(IMI) > 0 ) THEN
OUT_MODEL(IMI)%NOUT_STEPFREQ = NOUT_STEP_FREQ(IMI)
! Do not mix XOUT_TIME_FREQ_FIRST with NOUT_STEP_FREQ
IF ( XOUT_TIME_FREQ_FIRST(IMI) > 0 ) THEN
CMNHMSG(1) = 'XOUT_TIME_FREQ_FIRST is not allowed with NOUT_STEP_FREQ'
CMNHMSG(2) = 'use NOUT_STEP_FREQ_FIRST instead'
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare' )
END IF
IF ( NOUT_STEP_FREQ_FIRST(IMI) > 0 ) THEN
OUT_MODEL(IMI)%NOUT_STEPFREQFIRST = NOUT_STEP_FREQ_FIRST(IMI)
ELSE
! Set first output to frequency
OUT_MODEL(IMI)%NOUT_STEPFREQFIRST = OUT_MODEL(IMI)%NOUT_STEPFREQ + 1
END IF
END IF
END DO
! Synchronize regular series to nested models
DO IMI = 1, NMODEL
! Synchronize regular backup series to nested models
IF ( OUT_MODEL(IMI)%NBAK_STEPFREQ > 0 ) THEN
DO IDX = IMI+1, NMODEL
ISTEP = OUT_MODEL(IDX-1)%NBAK_STEPFREQ * NINT( DYN_MODEL(IDX-1)%XTSTEP / DYN_MODEL(IDX)%XTSTEP )
IF ( OUT_MODEL(IDX)%NBAK_STEPFREQ > 0 ) THEN
IF ( MOD(ISTEP,OUT_MODEL(IDX)%NBAK_STEPFREQ) /= 0 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', &
'backup frequency for parent model must be a multiple of child' )
OUT_MODEL(IDX)%NBAK_STEPFREQ = ISTEP
ELSE
! Nothing to do. We keep the model frequency
END IF
ELSE
! Propagate to child
OUT_MODEL(IDX)%NBAK_STEPFREQ = ISTEP
END IF
IF ( OUT_MODEL(IDX)%NBAK_STEPFREQFIRST > 0 ) THEN
IF ( OUT_MODEL(IDX-1)%NBAK_STEPFREQFIRST > 0 ) THEN
! Compute first step of dad in number of timesteps for THIS model
ISTEPDADFIRST = ( OUT_MODEL(IDX-1)%NBAK_STEPFREQFIRST - 1 ) * NINT( DYN_MODEL(IDX-1)%XTSTEP / DYN_MODEL(IDX)%XTSTEP ) + 1
! The first backup of a child model must be before or at the same time than its parent
IF ( OUT_MODEL(IDX)%NBAK_STEPFREQFIRST > ISTEPDADFIRST ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', &
'the first backup of a child model must be before or at the same time than its parent' )
OUT_MODEL(IDX)%NBAK_STEPFREQFIRST = ISTEPDADFIRST
END IF
! The backup times must be aligned with the one of the parent model (if it does also regular series)
IF ( MOD( ISTEPDADFIRST - OUT_MODEL(IDX)%NBAK_STEPFREQFIRST, OUT_MODEL(IDX)%NBAK_STEPFREQ ) /= 0 ) THEN
CMNHMSG(1) = 'times of series of backups must be aligned with the time of its parent'
CMNHMSG(2) = 'check that XBAK_TIME_FREQ_FIRST or NBAK_STEP_FREQ_FIRST are set correctly for all submodels'
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare' )
OUT_MODEL(IDX)%NBAK_STEPFREQFIRST = ( OUT_MODEL(IDX-1)%NBAK_STEPFREQFIRST - 1 ) &
* NINT( DYN_MODEL(IDX-1)%XTSTEP / DYN_MODEL(IDX)%XTSTEP ) + 1
END IF
ELSE
! Nothing to do (the parent does not do regular series)
END IF
ELSE
! Propagate first time (in timesteps)
OUT_MODEL(IDX)%NBAK_STEPFREQFIRST = ( OUT_MODEL(IDX-1)%NBAK_STEPFREQFIRST - 1 ) &
* NINT( DYN_MODEL(IDX-1)%XTSTEP / DYN_MODEL(IDX)%XTSTEP ) + 1
END IF
END DO
END IF
! Synchronize regular output series to nested models
IF ( OUT_MODEL(IMI)%NOUT_STEPFREQ > 0 ) THEN
DO IDX = IMI+1, NMODEL
ISTEP = OUT_MODEL(IDX-1)%NOUT_STEPFREQ * NINT( DYN_MODEL(IDX-1)%XTSTEP / DYN_MODEL(IDX)%XTSTEP )
IF ( OUT_MODEL(IDX)%NOUT_STEPFREQ > 0 ) THEN
IF ( MOD(ISTEP,OUT_MODEL(IDX)%NOUT_STEPFREQ) /= 0 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', &
'output frequency for parent model must be a multiple of child' )
OUT_MODEL(IDX)%NOUT_STEPFREQ = ISTEP
ELSE
! Nothing to do. We keep the model frequency
END IF
ELSE
! Propagate to child
OUT_MODEL(IDX)%NOUT_STEPFREQ = ISTEP
END IF
IF ( OUT_MODEL(IDX)%NOUT_STEPFREQFIRST > 0 ) THEN
IF ( OUT_MODEL(IDX-1)%NOUT_STEPFREQFIRST > 0 ) THEN
! Compute first step of dad in number of timesteps for THIS model
ISTEPDADFIRST = ( OUT_MODEL(IDX-1)%NOUT_STEPFREQFIRST - 1 ) * NINT( DYN_MODEL(IDX-1)%XTSTEP / DYN_MODEL(IDX)%XTSTEP ) + 1
! The first output of a child model must be before or at the same time than its parent
IF ( OUT_MODEL(IDX)%NOUT_STEPFREQFIRST > ISTEPDADFIRST ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', &
'the first output of a child model must be before or at the same time than its parent' )
OUT_MODEL(IDX)%NOUT_STEPFREQFIRST = ISTEPDADFIRST
END IF
! The output times must be aligned with the one of the parent model (if it does also regular series)
IF ( MOD( ISTEPDADFIRST - OUT_MODEL(IDX)%NOUT_STEPFREQFIRST, OUT_MODEL(IDX)%NOUT_STEPFREQ ) /= 0 ) THEN
CMNHMSG(1) = 'times of series of outputs must be aligned with the time of its parent'
CMNHMSG(2) = 'check that XOUT_TIME_FREQ_FIRST or NOUT_STEP_FREQ_FIRST are set correctly for all submodels'
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare' )
OUT_MODEL(IDX)%NOUT_STEPFREQFIRST = ( OUT_MODEL(IDX-1)%NOUT_STEPFREQFIRST - 1 ) &
* NINT( DYN_MODEL(IDX-1)%XTSTEP / DYN_MODEL(IDX)%XTSTEP ) + 1
END IF
ELSE
! Nothing to do (the parent does not do regular series)
END IF
ELSE
! Propagate first time (in timesteps)
OUT_MODEL(IDX)%NOUT_STEPFREQFIRST = ( OUT_MODEL(IDX-1)%NOUT_STEPFREQFIRST - 1 ) &
* NINT( DYN_MODEL(IDX-1)%XTSTEP / DYN_MODEL(IDX)%XTSTEP ) + 1
END IF
END DO
END IF
END DO
! Treat irregular backups/outputs
DO IMI = 1, NMODEL
IBAK_NUM = 0
IOUT_NUM = 0
!Reduce XSEGLEN by time added to XSEGLEN for 1st domain (see set_grid subroutine)
ISTEP_MAX = NINT( ( XSEGLEN - NSUPTS * DYN_MODEL(1)%XTSTEP ) / DYN_MODEL(IMI)%XTSTEP ) + 1
! Check that provided times are multiples of model timestep and not after end of segment
! After that, insert them in the lists (in timesteps instead of seconds)
DO JOUT = 1, NFILE_NUM_MAX
IF ( XBAK_TIME(IMI,JOUT) >= 0.) THEN
ZTSTEP_RND = NINT( XBAK_TIME(IMI,JOUT) / DYN_MODEL(IMI)%XTSTEP ) * DYN_MODEL(IMI)%XTSTEP
IF ( ABS( ZTSTEP_RND - XBAK_TIME(IMI,JOUT) ) > 1.E-6 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', 'XBAK_TIME is not a multiple of the model timestep' )
XBAK_TIME(IMI,JOUT) = ZTSTEP_RND
END IF
IF ( XBAK_TIME(IMI,JOUT) > XSEGLEN + 1.E-6 ) THEN
WRITE( CMNHMSG(1), '( "XBAK_TIME ", EN12.3 , " after end of simulation time segment => ignored" )' ) XBAK_TIME(IMI,JOUT)
CALL PRINT_MSG( NVERB_WARNING, 'IO', 'IO_Bakout_struct_prepare' )
XBAK_TIME(IMI,JOUT) = XNEGUNDEF
END IF
IF ( XBAK_TIME(IMI,JOUT) > 0.) THEN ! Check again because its value could have been modified just before if ignored
! Insert XBAK_TIME into IBAK_STEPLIST after conversion in timestep number (use insert because the list may be non-empty)
IBAK_NUM = IBAK_NUM + 1
CALL IO_INSERT_INT( IBAK_NUM, IBAK_STEPLIST(IMI,:), NINT( XBAK_TIME(IMI,JOUT) / DYN_MODEL(IMI)%XTSTEP ) + 1 )
END IF
END IF
IF ( XOUT_TIME(IMI,JOUT) >= 0.) THEN
ZTSTEP_RND = NINT( XOUT_TIME(IMI,JOUT) / DYN_MODEL(IMI)%XTSTEP ) * DYN_MODEL(IMI)%XTSTEP
IF ( ABS( ZTSTEP_RND - XOUT_TIME(IMI,JOUT) ) > 1.E-6 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_Bakout_struct_prepare', 'XOUT_TIME is not a multiple of the model timestep' )
XOUT_TIME(IMI,JOUT) = ZTSTEP_RND
END IF
IF ( XOUT_TIME(IMI,JOUT) > XSEGLEN + 1.E-6 ) THEN
WRITE( CMNHMSG(1), '( "XOUT_TIME ", EN12.3 , " after end of simulation time segment => ignored" )' ) XOUT_TIME(IMI,JOUT)
CALL PRINT_MSG( NVERB_WARNING, 'IO', 'IO_Bakout_struct_prepare' )
XOUT_TIME(IMI,JOUT) = XNEGUNDEF
END IF
IF ( XOUT_TIME(IMI,JOUT) > 0.) THEN ! Check again because its value could have been modified just before if ignored
! Insert XOUT_TIME into IOUT_STEPLIST after conversion in timestep number (use insert because the list may be non-empty)
IOUT_NUM = IOUT_NUM + 1
CALL IO_INSERT_INT( IOUT_NUM, IOUT_STEPLIST(IMI,:), NINT( XOUT_TIME(IMI,JOUT) / DYN_MODEL(IMI)%XTSTEP ) + 1 )
END IF
END IF
END DO
!
!* Synchronization between nested models through IBAK_STEPLIST/IOUT_STEPLIST arrays
!
CALL IO_SYNC_MODELS_INT(IBAK_NUM,IBAK_STEPLIST(:,:))
CALL IO_SYNC_MODELS_INT(IOUT_NUM,IOUT_STEPLIST(:,:))
IF ( LBAK_BEG ) THEN
IBAK_NUM = IBAK_NUM + 1
CALL IO_INSERT_INT( IBAK_NUM, IBAK_STEPLIST(IMI,:), 1 ) ! 1 is the 1st step number
END IF
IF ( LOUT_BEG ) THEN
IOUT_NUM = IOUT_NUM + 1
CALL IO_INSERT_INT( IOUT_NUM, IOUT_STEPLIST(IMI,:), 1 ) ! 1 is the 1st step number
END IF
IF ( LBAK_END ) THEN
IBAK_NUM = IBAK_NUM + 1
CALL IO_INSERT_INT( IBAK_NUM, IBAK_STEPLIST(IMI,:), ISTEP_MAX )
END IF
IF ( LOUT_END ) THEN
IOUT_NUM = IOUT_NUM + 1
CALL IO_INSERT_INT( IOUT_NUM, IOUT_STEPLIST(IMI,:), ISTEP_MAX )
END IF
!
!* Find and remove duplicated entries
!
CALL FIND_REMOVE_DUPLICATES( IBAK_NUM, IBAK_STEPLIST(IMI,:) )
CALL FIND_REMOVE_DUPLICATES( IOUT_NUM, IOUT_STEPLIST(IMI,:) )
!
!* Find and remove out of time range entries
!
CALL FIND_REMOVE_OUTOFTIMERANGE( IBAK_NUM, IBAK_STEPLIST(IMI,:) )
CALL FIND_REMOVE_OUTOFTIMERANGE( IOUT_NUM, IOUT_STEPLIST(IMI,:) )
! Remove entries in list if they are at the same time than regular entries
DO JOUT = OUT_MODEL(IMI)%NBAK_STEPFREQFIRST, ISTEP_MAX, OUT_MODEL(IMI)%NBAK_STEPFREQ
DO IDX = 1, IBAK_NUM
IF ( IBAK_STEPLIST(IMI,IDX) == JOUT ) THEN
CALL PRINT_MSG(NVERB_DEBUG,'IO','FIND_REMOVE_REGULAR','found duplicated backup step (removed extra one)')
IBAK_STEPLIST(IMI,IDX) = NNEGUNDEF
END IF
END DO
END DO
DO JOUT = OUT_MODEL(IMI)%NOUT_STEPFREQFIRST, ISTEP_MAX, OUT_MODEL(IMI)%NOUT_STEPFREQ
DO IDX = 1, IOUT_NUM
IF ( IOUT_STEPLIST(IMI,IDX) == JOUT ) THEN
CALL PRINT_MSG(NVERB_DEBUG,'IO','FIND_REMOVE_REGULAR','found duplicated output step (removed extra one)')
IOUT_STEPLIST(IMI,IDX) = NNEGUNDEF
END IF
END DO
END DO
!
!* Sort entries
!
CALL SORT_ENTRIES( IBAK_NUM, IBAK_STEPLIST(IMI,:) )
CALL SORT_ENTRIES( IOUT_NUM, IOUT_STEPLIST(IMI,:) )
!
!* Count the number of backups/outputs of model IMI and compact the list
!
IBAK_NUM = 0
DO JOUT = 1, SIZE( IBAK_STEPLIST(IMI,:) )
IF ( IBAK_STEPLIST(IMI,JOUT) >= 0 ) THEN
IBAK_NUM = IBAK_NUM + 1
END IF
END DO
ALLOCATE( OUT_MODEL(IMI)%NBAK_STEPLIST(IBAK_NUM) )
OUT_MODEL(IMI)%NBAK_STEPLIST(:) = IBAK_STEPLIST(IMI,1:IBAK_NUM)
OUT_MODEL(IMI)%NBAK_NUMB = IBAK_NUM
IOUT_NUM = 0
DO JOUT = 1, SIZE( IOUT_STEPLIST(IMI,:) )
IF ( IOUT_STEPLIST(IMI,JOUT) >= 0 ) THEN
IOUT_NUM = IOUT_NUM + 1
END IF
END DO
ALLOCATE( OUT_MODEL(IMI)%NOUT_STEPLIST(IOUT_NUM) )
OUT_MODEL(IMI)%NOUT_STEPLIST(:) = IOUT_STEPLIST(IMI,1:IOUT_NUM)
OUT_MODEL(IMI)%NOUT_NUMB = IOUT_NUM
! Count the number of regular backups/outputs
IBAK_NUM = MAX( ( ISTEP_MAX - OUT_MODEL(IMI)%NBAK_STEPFREQFIRST ) / OUT_MODEL(IMI)%NBAK_STEPFREQ + 1, 0 )
OUT_MODEL(IMI)%NBAK_NUMB = OUT_MODEL(IMI)%NBAK_NUMB + IBAK_NUM
IOUT_NUM = MAX( ( ISTEP_MAX - OUT_MODEL(IMI)%NOUT_STEPFREQFIRST ) / OUT_MODEL(IMI)%NOUT_STEPFREQ + 1, 0 )
OUT_MODEL(IMI)%NOUT_NUMB = OUT_MODEL(IMI)%NOUT_NUMB + IOUT_NUM
! Print message if there are no backups
IF ( OUT_MODEL(IMI)%NBAK_NUMB == 0 ) THEN
IF( LIO_ALLOW_NO_BACKUP ) THEN
IERR_LVL = NVERB_WARNING
ELSE
IERR_LVL = NVERB_ERROR
END IF
CALL PRINT_MSG( IERR_LVL, 'IO', 'IO_Bakout_struct_prepare', 'no (valid) backup time' )
END IF
IF ( OUT_MODEL(IMI)%NOUT_NUMB > 0 ) THEN
!Determine the list of the fields to write in each output
CALL IO_OUT_FIELDLIST_FILL( COUT_VAR(IMI,:), .TRUE., OUT_MODEL(IMI)%NOUT_FIELDLIST )
! Treat the boxes (sub-domains) for output files
CALL IO_BOX_PREPARE( )
END IF
!
IF ( IP == 1 ) THEN
! Backup information
WRITE( *, '( "-------------------------------------------------" )' )
WRITE( *, '( "Model number: ", I9 )' ) IMI
WRITE( *, '( "Number of backups: ", I9 )' ) OUT_MODEL(IMI)%NBAK_NUMB
IF (OUT_MODEL(IMI)%NBAK_STEPFREQ > 0 ) THEN
WRITE( *, '( " Regular: ", I9 )' ) &
( ISTEP_MAX - OUT_MODEL(IMI)%NBAK_STEPFREQFIRST ) / OUT_MODEL(IMI)%NBAK_STEPFREQ + 1
WRITE( *, '( " Frequency: ", I9, " timesteps (", F12.3, "s)" )' ) &
OUT_MODEL(IMI)%NBAK_STEPFREQ, OUT_MODEL(IMI)%NBAK_STEPFREQ * DYN_MODEL(IMI)%XTSTEP
WRITE( *, '( " First: ", I9, " timesteps (", F12.3, "s)" )' ) &
OUT_MODEL(IMI)%NBAK_STEPFREQFIRST, ( OUT_MODEL(IMI)%NBAK_STEPFREQFIRST - 1 ) * DYN_MODEL(IMI)%XTSTEP
ELSE
WRITE( *, '( " Regular: ", I9 )' ) 0
END IF
WRITE( *, '( " Iregular: ", I9 )' ) SIZE( OUT_MODEL(IMI)%NBAK_STEPLIST )
IF ( SIZE( OUT_MODEL(IMI)%NBAK_STEPLIST ) > 0 ) THEN
WRITE( *, '( " Timestep Time" )' )
DO JOUT = 1, SIZE( OUT_MODEL(IMI)%NBAK_STEPLIST )
WRITE(*,'( " ", I9,F12.3 )' ) OUT_MODEL(IMI)%NBAK_STEPLIST(JOUT), &
( OUT_MODEL(IMI)%NBAK_STEPLIST(JOUT) - 1 ) * DYN_MODEL(IMI)%XTSTEP
END DO
END IF
! Output information
WRITE( *, '( "-------------------------------------------------" )' )
WRITE( *, '( "Model number: ", I9 )' ) IMI
WRITE( *, '( "Number of outputs: ", I9 )' ) OUT_MODEL(IMI)%NOUT_NUMB
IF (OUT_MODEL(IMI)%NOUT_STEPFREQ > 0 ) THEN
WRITE( *, '( " Regular: ", I9 )' ) &
( ISTEP_MAX - OUT_MODEL(IMI)%NOUT_STEPFREQFIRST ) / OUT_MODEL(IMI)%NOUT_STEPFREQ + 1
WRITE( *, '( " Frequency: every ", I9, " timesteps (", F12.3, "s)" )' ) &
OUT_MODEL(IMI)%NOUT_STEPFREQ, OUT_MODEL(IMI)%NOUT_STEPFREQ * DYN_MODEL(IMI)%XTSTEP
WRITE( *, '( " First at timestep ", I9, " (", F12.3, "s)" )' ) &
OUT_MODEL(IMI)%NOUT_STEPFREQFIRST, ( OUT_MODEL(IMI)%NOUT_STEPFREQFIRST - 1 ) * DYN_MODEL(IMI)%XTSTEP
ELSE
WRITE( *, '( " Regular: ", I9 )' ) 0
END IF
WRITE( *, '( " Iregular: ", I9 )' ) SIZE( OUT_MODEL(IMI)%NOUT_STEPLIST )
IF ( SIZE( OUT_MODEL(IMI)%NOUT_STEPLIST ) > 0 ) THEN
WRITE( *, '( " Timestep Time" )' )
DO JOUT = 1, SIZE( OUT_MODEL(IMI)%NOUT_STEPLIST )
WRITE(*,'( " ", I9,F12.3 )' ) OUT_MODEL(IMI)%NOUT_STEPLIST(JOUT), &
( OUT_MODEL(IMI)%NOUT_STEPLIST(JOUT) - 1 ) * DYN_MODEL(IMI)%XTSTEP
END DO
END IF
IF ( OUT_MODEL(IMI)%NOUT_NUMB > 0 ) THEN
IF ( SIZE( OUT_MODEL(IMI)%NOUT_FIELDLIST ) > 0 ) THEN
WRITE( *, '( "List of fields:" )' )
DO JOUT = 1, SIZE( OUT_MODEL(IMI)%NOUT_FIELDLIST )
IDX = OUT_MODEL(IMI)%NOUT_FIELDLIST(JOUT)
WRITE(*, '( " ", A )' ) TRIM(TFIELDLIST(IDX)%CMNHNAME)
END DO
END IF
WRITE( *, '( "Number of boxes:", I9 )' ) OUT_MODEL(IMI)%NOUT_NBOXES
IF ( OUT_MODEL(IMI)%NOUT_NBOXES > 0 ) THEN
WRITE( *, '( " List of boxes:" )' )
DO JI = 1, OUT_MODEL(IMI)%NOUT_NBOXES
WRITE(*, '( " ", A )' ) TRIM(OUT_MODEL(IMI)%TOUT_BOXES(JI)%CNAME)
IF ( SIZE( OUT_MODEL(IMI)%TOUT_BOXES(JI)%NFIELDLIST_SUPP ) > 0 ) THEN
WRITE( *, '( " Specific fields:" )' )
DO JOUT = 1, SIZE( OUT_MODEL(IMI)%TOUT_BOXES(JI)%NFIELDLIST_SUPP )
IDX = OUT_MODEL(IMI)%TOUT_BOXES(JI)%NFIELDLIST_SUPP(JOUT)
WRITE(*, '( " ", A )' ) TRIM(TFIELDLIST(IDX)%CMNHNAME)
END DO
END IF
END DO
END IF
END IF
WRITE( *, '( "-------------------------------------------------" )' )
END IF
!
END DO ! IMI=1,NMODEL
!
CALL BACKUP_NML_DEALLOCATE()
CALL OUTPUT_NML_DEALLOCATE()
!
! Set/initialize the pointers (necessary to use them now without OUT_MODEL(1)%...)
CALL OUT_GOTO_MODEL( 1, 1 )
NFILE_BACKUP_CURRENT => SUB_MODEL_MODEL(1)%NFILE_BACKUP_CURRENT
NFILE_OUTPUT_CURRENT => SUB_MODEL_MODEL(1)%NFILE_OUTPUT_CURRENT
!
CONTAINS
!
!#########################################################################
SUBROUTINE IO_INSERT_INT( KPOS, KSTEPS, KVAL )
!#########################################################################
!
INTEGER, INTENT(INOUT) :: KPOS ! First position to try to insert KVAL
INTEGER,DIMENSION(:), INTENT(INOUT) :: KSTEPS ! Array in which to store KVAL
INTEGER, INTENT(IN) :: KVAL ! Value to store
!
CALL FIND_NEXT_AVAIL_SLOT_INT( KSTEPS, KPOS )
KSTEPS(KPOS) = KVAL
!
END SUBROUTINE IO_INSERT_INT
!
!#########################################################################
SUBROUTINE IO_INSERT_REGULAR_INT(KFIRST,KFREQ,KSTEPS)
!#########################################################################
!
INTEGER, INTENT(IN) :: KFIRST,KFREQ
INTEGER,DIMENSION(:), INTENT(INOUT) :: KSTEPS
!
IDX = 1
DO JOUT = KFIRST, ISTEP_MAX, KFREQ
CALL FIND_NEXT_AVAIL_SLOT_INT(KSTEPS,IDX)
KSTEPS(IDX) = JOUT
END DO
END SUBROUTINE IO_INSERT_REGULAR_INT
!
!#########################################################################
SUBROUTINE IO_SYNC_MODELS_INT(KNUMB,KSTEPS)
!#########################################################################
!
INTEGER, INTENT(INOUT) :: KNUMB
INTEGER,DIMENSION(:,:), INTENT(INOUT) :: KSTEPS
!
INTEGER :: JKLOOP ! Loop index
!
DO JOUT = 1, NFILE_NUM_MAX
IF (KSTEPS(IMI,JOUT) > 0) THEN
KNUMB = KNUMB + 1
!Output/backup time is propagated to nested models (with higher numbers)
DO JKLOOP = IMI+1,NMODEL
IDX = 1
CALL FIND_NEXT_AVAIL_SLOT_INT(KSTEPS(JKLOOP,:),IDX)
! Use of NINT and real to prevent rounding errors
! (STEP-1)* ... +1 because step numbers begin at 1
KSTEPS(JKLOOP,IDX) = (KSTEPS(IMI,JOUT)-1) * NINT( DYN_MODEL(IMI)%XTSTEP/DYN_MODEL(JKLOOP)%XTSTEP ) + 1
END DO
END IF
END DO
END SUBROUTINE IO_SYNC_MODELS_INT
!
!#########################################################################
SUBROUTINE FIND_NEXT_AVAIL_SLOT_INT(KSTEPS,KIDX)
!#########################################################################
!
INTEGER,DIMENSION(:), INTENT(IN) :: KSTEPS
INTEGER, INTENT(INOUT) :: KIDX
!
!Find next (starting from KIDX) non 'allocated' element
DO WHILE ( KSTEPS(KIDX) >= 0 )
KIDX = KIDX + 1
IF (KIDX > NFILE_NUM_MAX) CALL PRINT_MSG(NVERB_FATAL,'IO','FIND_NEXT_AVAIL_SLOT_INT','NFILE_NUM_MAX too small')
END DO
END SUBROUTINE FIND_NEXT_AVAIL_SLOT_INT
!
!#########################################################################
SUBROUTINE FIND_REMOVE_DUPLICATES(KNUMB,KSTEPS)
!#########################################################################
!
INTEGER, INTENT(IN) :: KNUMB
INTEGER,DIMENSION(:), INTENT(INOUT) :: KSTEPS
!
INTEGER :: JKLOOP ! Loop index
!
DO JOUT = 1,KNUMB
DO JKLOOP = JOUT+1,KNUMB
IF ( KSTEPS(JKLOOP) == KSTEPS(JOUT) .AND. KSTEPS(JKLOOP) > 0 ) THEN
CALL PRINT_MSG(NVERB_DEBUG,'IO','FIND_REMOVE_DUPLICATES','found duplicated backup/output step (removed extra one)')
KSTEPS(JKLOOP) = NNEGUNDEF
END IF
END DO
END DO
END SUBROUTINE FIND_REMOVE_DUPLICATES
!
!#########################################################################
SUBROUTINE FIND_REMOVE_OUTOFTIMERANGE(KNUMB,KSTEPS)
!#########################################################################
!
INTEGER, INTENT(IN) :: KNUMB
INTEGER,DIMENSION(:), INTENT(INOUT) :: KSTEPS
!
DO JOUT = 1,KNUMB
IF ( KSTEPS(JOUT) < 1 .OR. KSTEPS(JOUT) > ISTEP_MAX ) THEN
IF ( KSTEPS(JOUT) /= NNEGUNDEF ) &
CALL PRINT_MSG(NVERB_WARNING,'IO','FIND_REMOVE_OUTOFTIMERANGE','found backup/output step outside of time range')
KSTEPS(JOUT) = NNEGUNDEF
END IF
END DO
END SUBROUTINE FIND_REMOVE_OUTOFTIMERANGE
!
!#########################################################################
SUBROUTINE SORT_ENTRIES(KNUMB,KSTEPS)
!#########################################################################
!
INTEGER, INTENT(IN) :: KNUMB
INTEGER,DIMENSION(:), INTENT(INOUT) :: KSTEPS
!
INTEGER :: ITEMP ! Intermediate variable
INTEGER :: JKLOOP ! Loop index
!
DO JOUT = 1,KNUMB
ITEMP = KSTEPS(JOUT)
IF (ITEMP<=0) ITEMP = HUGE(ITEMP)
IPOS = -1
DO JKLOOP = JOUT+1,KNUMB
IF ( KSTEPS(JKLOOP) < ITEMP .AND. KSTEPS(JKLOOP) >= 0 ) THEN
ITEMP = KSTEPS(JKLOOP)
IPOS = JKLOOP
END IF
END DO
IF (IPOS >= JOUT) THEN
KSTEPS(IPOS) = KSTEPS(JOUT)
KSTEPS(JOUT) = ITEMP
END IF
END DO
END SUBROUTINE SORT_ENTRIES
!
!#########################################################################
SUBROUTINE IO_OUT_FIELDLIST_FILL( HVARLIST, OMAINBOX, KFIELDLIST )
!#########################################################################
use mode_field, only: Find_field_id_from_mnhname
CHARACTER(LEN=NMNHNAMELGTMAX), DIMENSION(:), INTENT(IN) :: HVARLIST
LOGICAL, INTENT(IN) :: OMAINBOX
INTEGER, DIMENSION(:), ALLOCATABLE, INTENT(OUT) :: KFIELDLIST
INTEGER :: IFIELD
INTEGER :: IVAR ! Number of variables
INTEGER :: JIDX
INTEGER, DIMENSION(:), ALLOCATABLE :: ICOMPACTFIELDLIST
!Count the number of fields to output
IVAR = 0
DO IPOS = 1, SIZE(HVARLIST)
IF ( HVARLIST(IPOS) /= '' ) IVAR = IVAR + 1
END DO
! Print warning message only if the main box has to be written
IF ( IVAR == 0 .AND. OMAINBOX .AND. ( NOUT_BOXES(IMI) == 0 .OR. LOUT_MAINDOMAIN_WRITE(IMI) ) ) &
CALL PRINT_MSG( NVERB_WARNING, 'IO', 'IO_OUT_FIELDLIST_FILL', 'no fields chosen for output' )
ALLOCATE( KFIELDLIST(IVAR) )
IF ( IVAR > 0 ) THEN
!Determine the list of the outputs to do (by field number)
IVAR = 0
DO IPOS = 1, SIZE(HVARLIST)
IF ( HVARLIST(IPOS) /= '' ) THEN
IVAR = IVAR + 1
CALL FIND_FIELD_ID_FROM_MNHNAME( HVARLIST(IPOS), IFIELD, IRESP )
KFIELDLIST(IVAR) = IFIELD
IF ( IRESP /= 0 ) THEN
CALL PRINT_MSG( NVERB_FATAL, 'IO', 'IO_OUT_FIELDLIST_FILL', 'unknown field for output: ' // TRIM(HVARLIST(IPOS)) )
!MNH is killed to prevent problems with wrong values in fieldlist
END IF
!
END IF
END DO
END IF
!Find and remove duplicated entries
DO IPOS = 1, SIZE( KFIELDLIST )
IFIELD = KFIELDLIST(IPOS)
IF ( IFIELD == -1 ) CYCLE
DO JIDX = IPOS + 1, SIZE( KFIELDLIST )
IF ( KFIELDLIST(JIDX) == IFIELD ) THEN
KFIELDLIST(JIDX) = -1
IVAR = IVAR - 1
END IF
END DO
END DO
! Compact the fieldlist (if some entries were duplicated and removed)
IF ( IVAR < SIZE( KFIELDLIST ) ) THEN
CALL PRINT_MSG( NVERB_WARNING, 'IO', 'IO_OUT_FIELDLIST_FILL', 'some output variable entries were duplicated => removed' )
ALLOCATE( ICOMPACTFIELDLIST(IVAR) )
JIDX = 1
DO IPOS = 1, SIZE( KFIELDLIST )
IF ( KFIELDLIST(IPOS) /= -1 ) THEN
ICOMPACTFIELDLIST(JIDX) = KFIELDLIST(IPOS)
JIDX = JIDX + 1
END IF
END DO
CALL MOVE_ALLOC( FROM = ICOMPACTFIELDLIST, TO = KFIELDLIST )
END IF
END SUBROUTINE IO_OUT_FIELDLIST_FILL
!#########################################################################
SUBROUTINE IO_BOX_PREPARE( )
!#########################################################################
USE MODD_DIM_n, ONLY: DIM_MODEL
USE MODD_DYN_n, ONLY: DYN_MODEL
USE MODD_OUT_n, ONLY: CMAINDOMAINNAME
INTEGER :: IFIELD
INTEGER :: IVAR
INTEGER :: JIDX1, JIDX2
INTEGER, DIMENSION(:), ALLOCATABLE :: ICOMPACTFIELDLIST
LOGICAL :: GKINF_PROVIDED, GKSUP_PROVIDED
! Force the writing of the main domain if there are no boxes
! Remark: default value of LOUT_MAINDOMAIN_WRITE is .FALSE.
IF ( NOUT_BOXES(IMI) == 0 ) LOUT_MAINDOMAIN_WRITE(IMI) = .TRUE.
OUT_MODEL(IMI)%LOUT_BIGBOX_WRITE = LOUT_MAINDOMAIN_WRITE(IMI)
! Remove the Bottom Absorbing Layer?
IF ( DYN_MODEL(IMI)%LVE_RELAX_GRD .AND. LOUT_BOTTOM_ABSORBING_LAYER_REMOVE(IMI) ) THEN
OUT_MODEL(IMI)%LOUT_BAL_REMOVE = .TRUE.
ELSE
OUT_MODEL(IMI)%LOUT_BAL_REMOVE = .FALSE.
END IF
! Remove the Top Absorbing Layer?
IF ( DYN_MODEL(IMI)%LVE_RELAX .AND. LOUT_TOP_ABSORBING_LAYER_REMOVE(IMI) ) THEN
OUT_MODEL(IMI)%LOUT_TAL_REMOVE = .TRUE.
ELSE
OUT_MODEL(IMI)%LOUT_TAL_REMOVE = .FALSE.
END IF
! Remove the horizontal unphysical points?
OUT_MODEL(IMI)%LOUT_HOR_BORDER_REMOVE = LOUT_UNPHYSICAL_HOR_CELLS_REMOVE(IMI)
! Remove the vertical unphysical points?
OUT_MODEL(IMI)%LOUT_VER_BORDER_REMOVE = LOUT_UNPHYSICAL_VER_CELLS_REMOVE(IMI)
OUT_MODEL(IMI)%NOUT_NBOXES = NOUT_BOXES(IMI)
! Allocate boxes
! Allocate also a special box for the main domain (box number 0)
! This is useful to store its boundaries (ie used if we remove unphysical boundaries or top absorbing layer)
ALLOCATE( OUT_MODEL(IMI)%TOUT_BOXES(0:NOUT_BOXES(IMI)) )
TOUT_BOXES => OUT_MODEL(IMI)%TOUT_BOXES
! Treat special box for main domain
TOUT_BOXES(0)%CNAME = CMAINDOMAINNAME
TOUT_BOXES(0)%NID = 0
ALLOCATE( TOUT_BOXES(0)%NFIELDLIST_SUPP(0) )
IF ( OUT_MODEL(IMI)%LOUT_HOR_BORDER_REMOVE ) THEN
TOUT_BOXES(0)%NIINF = 1
TOUT_BOXES(0)%NISUP = DIM_MODEL(IMI)%NIMAX_ll
TOUT_BOXES(0)%NJINF = 1
TOUT_BOXES(0)%NJSUP = DIM_MODEL(IMI)%NJMAX_ll
ELSE
! Set boundaries in physical domain coordinates, but must cover all the domain with non-physical values
TOUT_BOXES(0)%NIINF = 1 - JPHEXT
TOUT_BOXES(0)%NISUP = DIM_MODEL(IMI)%NIMAX_ll + JPHEXT
TOUT_BOXES(0)%NJINF = 1 - JPHEXT
TOUT_BOXES(0)%NJSUP = DIM_MODEL(IMI)%NJMAX_ll + JPHEXT
END IF
IF ( OUT_MODEL(IMI)%LOUT_VER_BORDER_REMOVE ) THEN
TOUT_BOXES(0)%NKINF = 1
TOUT_BOXES(0)%NKSUP = DIM_MODEL(IMI)%NKMAX
ELSE
TOUT_BOXES(0)%NKINF = 1 - JPVEXT
TOUT_BOXES(0)%NKSUP = DIM_MODEL(IMI)%NKMAX + JPVEXT
END IF
IF ( OUT_MODEL(IMI)%LOUT_BAL_REMOVE ) THEN
! Max to manage case when the BAL is in the unphysical domain (should not happen)
TOUT_BOXES(0)%NKINF = MAX( TOUT_BOXES(0)%NKINF, DYN_MODEL(IMI)%NALBAS - JPVEXT )
END IF
IF ( OUT_MODEL(IMI)%LOUT_TAL_REMOVE ) THEN
! Min to manage case when the TAL is in the unphysical domain (should not happen)
TOUT_BOXES(0)%NKSUP = MIN( TOUT_BOXES(0)%NKSUP, DYN_MODEL(IMI)%NALBOT - JPVEXT )
END IF
! Treat boxes
DO JI = 1, OUT_MODEL(IMI)%NOUT_NBOXES
IF ( LEN_TRIM(COUT_BOX_NAME(IMI,JI)) > 0 ) THEN
TOUT_BOXES(JI)%CNAME = COUT_BOX_NAME(IMI,JI)
ELSE
WRITE( TOUT_BOXES(JI)%CNAME, '( "Box_", I4.4 )' ) JI
END IF
TOUT_BOXES(JI)%NID = JI
TOUT_BOXES(JI)%NIINF = NOUT_BOX_IINF(IMI,JI)
TOUT_BOXES(JI)%NISUP = NOUT_BOX_ISUP(IMI,JI)
TOUT_BOXES(JI)%NJINF = NOUT_BOX_JINF(IMI,JI)
TOUT_BOXES(JI)%NJSUP = NOUT_BOX_JSUP(IMI,JI)
TOUT_BOXES(JI)%NKINF = NOUT_BOX_KINF(IMI,JI)
TOUT_BOXES(JI)%NKSUP = NOUT_BOX_KSUP(IMI,JI)
IF ( TOUT_BOXES(JI)%NKINF == NNEGUNDEF ) THEN
GKINF_PROVIDED = .FALSE.
ELSE
GKINF_PROVIDED = .TRUE.
END IF
IF ( TOUT_BOXES(JI)%NKSUP == NNEGUNDEF ) THEN
GKSUP_PROVIDED = .FALSE.
ELSE
GKSUP_PROVIDED = .TRUE.
END IF
!Set default values to physical domain boundaries
IF ( TOUT_BOXES(JI)%NIINF == NNEGUNDEF ) TOUT_BOXES(JI)%NIINF = 1
IF ( TOUT_BOXES(JI)%NISUP == NNEGUNDEF ) TOUT_BOXES(JI)%NISUP = DIM_MODEL(IMI)%NIMAX_ll
IF ( TOUT_BOXES(JI)%NJINF == NNEGUNDEF ) TOUT_BOXES(JI)%NJINF = 1
IF ( TOUT_BOXES(JI)%NJSUP == NNEGUNDEF ) TOUT_BOXES(JI)%NJSUP = DIM_MODEL(IMI)%NJMAX_ll
IF ( TOUT_BOXES(JI)%NKINF == NNEGUNDEF ) TOUT_BOXES(JI)%NKINF = 1
IF ( TOUT_BOXES(JI)%NKSUP == NNEGUNDEF ) TOUT_BOXES(JI)%NKSUP = DIM_MODEL(IMI)%NKMAX
IF ( OUT_MODEL(IMI)%LOUT_BAL_REMOVE ) THEN
IF ( GKINF_PROVIDED .AND. TOUT_BOXES(JI)%NKINF < ( DYN_MODEL(IMI)%NALBAS - JPVEXT ) ) &
CALL Print_msg( NVERB_WARNING, 'GEN', 'IO_BOX_PREPARE', 'provided NKINF increazed after removal of Bottom Absorbing Layer' )
TOUT_BOXES(JI)%NKINF = MAX( TOUT_BOXES(JI)%NKINF, DYN_MODEL(IMI)%NALBAS - JPVEXT )
END IF
IF ( OUT_MODEL(IMI)%LOUT_TAL_REMOVE ) THEN
IF ( GKSUP_PROVIDED .AND. TOUT_BOXES(JI)%NKSUP > ( DYN_MODEL(IMI)%NALBOT - JPVEXT ) ) &
CALL Print_msg( NVERB_WARNING, 'GEN', 'IO_BOX_PREPARE', 'provided NKSUP reduced after removal of Top Absorbing Layer' )
TOUT_BOXES(JI)%NKSUP = MIN( TOUT_BOXES(JI)%NKSUP, DYN_MODEL(IMI)%NALBOT - JPVEXT )
END IF
!Check that selected indices are in physical domain
IF ( TOUT_BOXES(JI)%NIINF < 1 ) CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_IINF too small (<1)' )
IF ( TOUT_BOXES(JI)%NIINF > DIM_MODEL(IMI)%NIMAX_ll ) &
CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_IINF too large (>NIMAX)')
IF ( TOUT_BOXES(JI)%NISUP < 1 ) CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_ISUP too small (<1)' )
IF ( TOUT_BOXES(JI)%NISUP > DIM_MODEL(IMI)%NIMAX_ll ) &
CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_ISUP too large (>NIMAX)')
IF ( TOUT_BOXES(JI)%NISUP < TOUT_BOXES(JI)%NIINF ) &
CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_ISUP < NOUT_BOX_IINF' )
IF ( TOUT_BOXES(JI)%NJINF < 1 ) CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_JINF too small (<1)' )
IF ( TOUT_BOXES(JI)%NJINF > DIM_MODEL(IMI)%NJMAX_ll ) &
CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_JINF too large (>NJMAX)')
IF ( TOUT_BOXES(JI)%NJSUP < 1 ) CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_JSUP too small (<1)' )
IF ( TOUT_BOXES(JI)%NJSUP > DIM_MODEL(IMI)%NJMAX_ll ) &
CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_JSUP too large (>NJMAX)')
IF ( TOUT_BOXES(JI)%NJSUP < TOUT_BOXES(JI)%NJINF ) &
CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_JSUP < NOUT_BOX_JINF' )
IF ( TOUT_BOXES(JI)%NKINF < 1 ) CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_KINF too small (<1)' )
IF ( TOUT_BOXES(JI)%NKINF > DIM_MODEL(IMI)%NKMAX ) &
CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_KINF too large (>NKMAX)' )
IF ( TOUT_BOXES(JI)%NKSUP < 1 ) CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_KSUP too small (<1)' )
IF ( TOUT_BOXES(JI)%NKSUP > DIM_MODEL(IMI)%NKMAX ) &
CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_KSUP too large (>NKMAX)' )
IF ( TOUT_BOXES(JI)%NKSUP < TOUT_BOXES(JI)%NKINF ) &
CALL Print_msg( NVERB_ERROR, 'GEN', 'IO_BOX_PREPARE', 'NOUT_BOX_KSUP < NOUT_BOX_KINF' )
! Field the list of variables to write for each box (in addition to the NOUTFIELDLIST which is common to all the boxes)
CALL IO_OUT_FIELDLIST_FILL( COUT_BOX_VAR_SUPP(IMI,JI,:), .FALSE., TOUT_BOXES(JI)%NFIELDLIST_SUPP )
! Check for duplicated entries between the general list and the box-specific one
IVAR = SIZE( TOUT_BOXES(JI)%NFIELDLIST_SUPP )
DO JIDX1 = 1, SIZE( OUT_MODEL(IMI)%NOUT_FIELDLIST )
IFIELD = OUT_MODEL(IMI)%NOUT_FIELDLIST(JIDX1)
DO JIDX2 = 1, SIZE( TOUT_BOXES(JI)%NFIELDLIST_SUPP )
IF ( TOUT_BOXES(JI)%NFIELDLIST_SUPP(JIDX2) == IFIELD ) THEN
TOUT_BOXES(JI)%NFIELDLIST_SUPP(JIDX2) = TOUT_BOXES(JI)%NFIELDLIST_SUPP(SIZE(TOUT_BOXES(JI)%NFIELDLIST_SUPP))
IVAR = IVAR - 1
END IF
END DO
END DO
! Compact the fieldlist (if some entries were duplicated and removed)
IF ( IVAR < SIZE( TOUT_BOXES(JI)%NFIELDLIST_SUPP ) ) THEN
CALL Print_msg( NVERB_WARNING, 'IO', 'IO_BOX_PREPARE', 'some box-specific output variables already in main list => removed' )
ICOMPACTFIELDLIST = TOUT_BOXES(JI)%NFIELDLIST_SUPP(1:IVAR)
CALL MOVE_ALLOC( FROM = ICOMPACTFIELDLIST, TO = TOUT_BOXES(JI)%NFIELDLIST_SUPP )
END IF
END DO
END SUBROUTINE IO_BOX_PREPARE
END SUBROUTINE IO_Bakout_struct_prepare
FUNCTION IO_Is_backup_time( KMI, KTCOUNT, KNUMBAK ) RESULT( OBAK )
USE MODD_OUT_n
USE MODD_SUB_MODEL_N, ONLY: NFILE_BACKUP_CURRENT
! Determine if it is a step when a backup is needed
INTEGER, INTENT(IN) :: KMI ! Model number
INTEGER, INTENT(IN) :: KTCOUNT ! Timestep
INTEGER, INTENT(INOUT) :: KNUMBAK ! Number of the backup
LOGICAL :: OBAK ! Result
INTEGER :: JI
WRITE( CMNHMSG(1), '( "called for timestep ", I0, " on model ", I0 )' ) KTCOUNT, KMI
CALL PRINT_MSG( NVERB_DEBUG, 'IO', 'IO_Is_backup_time' )
OBAK = .FALSE.
KNUMBAK = NNEGUNDEF
! No more backups to do
IF ( NFILE_BACKUP_CURRENT >= NBAK_NUMB ) RETURN
! Check if it is time for a regular backup
IF ( NBAK_STEPFREQ > 0 .AND. KTCOUNT >= NBAK_STEPFREQFIRST ) THEN
IF ( MOD( KTCOUNT - NBAK_STEPFREQFIRST, NBAK_STEPFREQ ) == 0 ) OBAK = .TRUE.
END IF
! Check if it is time for an irregular backup
IF ( .NOT. OBAK ) THEN
DO JI = 1, SIZE( NBAK_STEPLIST )
IF ( NBAK_STEPLIST(JI) == KTCOUNT ) THEN
OBAK = .TRUE.
EXIT
END IF
END DO
END IF
IF ( OBAK ) THEN
NFILE_BACKUP_CURRENT = NFILE_BACKUP_CURRENT + 1
KNUMBAK = NFILE_BACKUP_CURRENT
END IF
END FUNCTION IO_Is_backup_time
FUNCTION IO_Is_output_time( KMI, KTCOUNT, KNUMOUT ) RESULT( OOUT )
USE MODD_OUT_n
USE MODD_SUB_MODEL_N, ONLY: NFILE_OUTPUT_CURRENT
! Determine if it is a step when a output is needed
INTEGER, INTENT(IN) :: KMI ! Model number
INTEGER, INTENT(IN) :: KTCOUNT ! Timestep
INTEGER, INTENT(INOUT) :: KNUMOUT ! Number of the output
LOGICAL :: OOUT ! Result
INTEGER :: JI
WRITE( CMNHMSG(1), '( "called for timestep ", I0, " on model ", I0 )' ) KTCOUNT, KMI
CALL PRINT_MSG( NVERB_DEBUG, 'IO', 'IO_Is_output_time' )
OOUT = .FALSE.
KNUMOUT = NNEGUNDEF
! No more outputs to do
IF ( NFILE_OUTPUT_CURRENT >= NOUT_NUMB ) RETURN
! Check if it is time for a regular output
IF ( NOUT_STEPFREQ > 0 .AND. KTCOUNT >= NOUT_STEPFREQFIRST ) THEN
IF ( MOD( KTCOUNT - NOUT_STEPFREQFIRST, NOUT_STEPFREQ ) == 0 ) OOUT = .TRUE.
END IF
! Check if it is time for an irregular output
IF ( .NOT. OOUT ) THEN
DO JI = 1, SIZE( NOUT_STEPLIST )
IF ( NOUT_STEPLIST(JI) == KTCOUNT ) THEN
OOUT = .TRUE.
EXIT
END IF
END DO
END IF
IF ( OOUT ) THEN
NFILE_OUTPUT_CURRENT = NFILE_OUTPUT_CURRENT + 1
KNUMOUT = NFILE_OUTPUT_CURRENT
END IF
END FUNCTION IO_Is_output_time
SUBROUTINE IO_BakOut_file_create( TPFILE, HTYPE, KMI, KSTEP, KNUMBAK )
USE MODD_BAKOUT, ONLY: CBAK_DIR, COUT_DIR
USE MODD_CONF, ONLY: CEXP, CSEG, NMODEL, NVERB
USE MODD_CONF_n, ONLY: NRR
USE MODD_NSV, ONLY: NSV
USE MODD_PARAMETERS, ONLY: NMODELNUMLGTMAX
TYPE(TFILEDATA), POINTER, INTENT(INOUT) :: TPFILE ! File structure to return
CHARACTER(LEN=*), INTENT(IN) :: HTYPE ! File type
INTEGER, INTENT(IN) :: KMI ! Model number
INTEGER, INTENT(IN) :: KSTEP ! Timestep number
INTEGER, INTENT(IN) :: KNUMBAK ! Number of the backup
CHARACTER(LEN=:), ALLOCATABLE :: YDIRNAME
CHARACTER(LEN=:), ALLOCATABLE :: YFORMAT
CHARACTER(LEN=:), ALLOCATABLE :: YNAME, YNAMEPRE
CHARACTER(LEN=:), ALLOCATABLE :: YNUMBER ! Character string for the file number
INTEGER :: ILEN
INTEGER(KIND=LFIINT) :: ILFINPRAR
CALL PRINT_MSG( NVERB_DEBUG, 'IO', 'IO_BakOut_file_create', 'called' )
IF ( HTYPE /= 'MNHBACKUP' .AND. HTYPE /= 'MNHOUTPUT' ) &
CALL PRINT_MSG( NVERB_FATAL, 'IO', 'IO_BakOut_file_create', 'invalid HTYPE' )
IF ( NFILE_NUM_MAX < 1000 ) THEN
ALLOCATE( CHARACTER(LEN=3) :: YNUMBER )
WRITE ( YNUMBER, FMT = "(I3.3)" ) KNUMBAK
ELSE IF ( NFILE_NUM_MAX < 10000 ) THEN
ALLOCATE( CHARACTER(LEN=4) :: YNUMBER )
WRITE ( YNUMBER, FMT = "(I4.4)" ) KNUMBAK
ELSE IF ( NFILE_NUM_MAX < 100000 ) THEN
ALLOCATE( CHARACTER(LEN=5) :: YNUMBER )
WRITE ( YNUMBER, FMT = "(I5.5)" ) KNUMBAK
ELSE IF ( NFILE_NUM_MAX < 1000000 ) THEN
ALLOCATE( CHARACTER(LEN=6) :: YNUMBER )
WRITE ( YNUMBER, FMT = "(I6.6)" ) KNUMBAK
ELSE
CALL PRINT_MSG( NVERB_FATAL, 'IO', 'IO_BakOut_file_create', 'NFILE_NUM_MAX is too large' )
END IF
ILEN = LEN_TRIM(CEXP) + 1 + NMODELNUMLGTMAX + 1 + LEN_TRIM(CSEG)
ALLOCATE( CHARACTER(LEN=ILEN) :: YNAMEPRE )
IF ( NMODELNUMLGTMAX == 1 ) THEN
IF ( NMODEL > 9 ) CALL PRINT_MSG( NVERB_FATAL, 'GEN', 'IO_BakOut_file_create', 'NMODEL>9 and NMODELNUMLGTMAX=1' )
WRITE( YNAMEPRE, '( A, ".", I1, ".", A) ' ) TRIM(CEXP), KMI, TRIM(CSEG)
ELSE IF ( NMODELNUMLGTMAX == 2 ) THEN
IF ( NMODEL > 99 ) CALL PRINT_MSG( NVERB_FATAL, 'GEN', 'IO_BakOut_file_create', 'NMODEL>99 and NMODELNUMLGTMAX=2' )
WRITE( YNAMEPRE, '( A, ".", I2.2, ".", A) ' ) TRIM(CEXP), KMI, TRIM(CSEG)
ELSE
CALL PRINT_MSG( NVERB_FATAL, 'GEN', 'IO_BakOut_file_create', 'NMODELNUMLGTMAX>2 not implemented' )
END IF
IF ( HTYPE == 'MNHOUTPUT' ) THEN
! Add a "OUT" suffix for output files
YNAME = YNAMEPRE // '.OUT.' // YNUMBER
!Set output directory
IF (LEN_TRIM(COUT_DIR)>0) THEN
YDIRNAME = TRIM(COUT_DIR)
ELSE IF (LEN_TRIM(CIO_DIR)>0) THEN
YDIRNAME = TRIM(CIO_DIR)
ELSE
YDIRNAME = ''
END IF
ELSE IF ( HTYPE == 'MNHBACKUP' ) THEN
YNAME = YNAMEPRE // '.' // YNUMBER
IF (LEN_TRIM(CBAK_DIR)>0) THEN
YDIRNAME = TRIM(CBAK_DIR)
ELSE IF (LEN_TRIM(CIO_DIR)>0) THEN
YDIRNAME = TRIM(CIO_DIR)
ELSE
YDIRNAME = ''
END IF
END IF
IF ( LIOCDF4 ) THEN
IF ( .NOT.LLFIOUT ) THEN
YFORMAT = 'NETCDF4'
ELSE
YFORMAT = 'LFICDF4'
IF ( HTYPE == 'MNHBACKUP' ) ILFINPRAR = 22+2*(4+NRR+NSV)
END IF
ELSE IF ( LLFIOUT ) THEN
YFORMAT = 'LFI'
IF ( HTYPE == 'MNHBACKUP') ILFINPRAR = 22+2*(4+NRR+NSV)
ELSE
CALL PRINT_MSG( NVERB_FATAL, 'IO', 'IO_BakOut_file_create', 'unknown backup/output fileformat' )
ENDIF
CALL IO_File_add2list( TPFILE, HNAME=YNAME, HTYPE=HTYPE, HMODE='WRITE', HFORMAT=YFORMAT, HDIRNAME=YDIRNAME, &
KLFINPRAR=ILFINPRAR, KLFITYPE=1, KLFIVERB=NVERB, KMODEL=KMI, KSTEP=KSTEP )
END SUBROUTINE IO_BakOut_file_create
SUBROUTINE IO_File_add2list( TPFILE, HNAME, HTYPE, HMODE, &
HFORM, HACCESS, HFORMAT, HDIRNAME, &
KLFINPRAR, KLFITYPE, KLFIVERB, KRECL, KMODEL, KSTEP, &
TPDADFILE, TPDATAFILE, TPMAINFILE, OOLD, OSPLIT_IOZ )
!
#ifdef MNH_IOCDF4
USE NETCDF, ONLY: NF90_QUANTIZE_BITGROOM, NF90_QUANTIZE_BITROUND, NF90_QUANTIZE_GRANULARBR
!
#endif
USE MODD_BAKOUT, ONLY: LBAK_COMPRESS, NBAK_COMPRESS_LEVEL, LBAK_REDUCE_FLOAT_PRECISION, &
LOUT_COMPRESS, NOUT_COMPRESS_LEVEL, LOUT_REDUCE_FLOAT_PRECISION, &
COUT_COMPRESS_LOSSY_ALGO, LOUT_COMPRESS_LOSSY, NOUT_COMPRESS_LOSSY_NSD, &
LOUT_FILESPLIT_DISABLE
USE MODD_CONF, ONLY: CPROGRAM
USE MODD_CONFZ, ONLY: NB_PROCIO_R, NB_PROCIO_W
USE MODD_DYN_n, ONLY: DYN_MODEL
USE MODD_IO, ONLY: LDIAG_REDUCE_FLOAT_PRECISION, LIO_COMPRESS, NIO_COMPRESS_LEVEL
USE MODD_NESTING, ONLY: NDAD
USE MODD_OUT_n, ONLY: OUT_MODEL
!
USE MODE_MODELN_HANDLER, ONLY: GET_CURRENT_MODEL_INDEX
USE MODE_TOOLS, ONLY: UPCASE
!
TYPE(TFILEDATA),POINTER, INTENT(INOUT) :: TPFILE !File structure to return
CHARACTER(LEN=*), INTENT(IN) :: HNAME !Filename
CHARACTER(LEN=*), INTENT(IN) :: HTYPE !Filetype (backup, output, prepidealcase...)
CHARACTER(LEN=*), INTENT(IN) :: HMODE !Opening mode (read, write...)
CHARACTER(LEN=*), OPTIONAL,INTENT(IN) :: HFORM !Formatted/unformatted
CHARACTER(LEN=*), OPTIONAL,INTENT(IN) :: HACCESS !Direct/sequential/stream
CHARACTER(LEN=*), OPTIONAL,INTENT(IN) :: HFORMAT !Fileformat (NETCDF4, LFI, LFICDF4...)
CHARACTER(LEN=*), OPTIONAL,INTENT(IN) :: HDIRNAME !File directory
INTEGER(KIND=LFIINT), OPTIONAL,INTENT(IN) :: KLFINPRAR !Number of predicted articles of the LFI file (non crucial)
INTEGER, OPTIONAL,INTENT(IN) :: KLFITYPE !Type of the file (used to generate list of files to transfers)
INTEGER, OPTIONAL,INTENT(IN) :: KLFIVERB !LFI verbosity level
INTEGER, OPTIONAL,INTENT(IN) :: KRECL !Record length
INTEGER, OPTIONAL,INTENT(IN) :: KMODEL !Model number
INTEGER, OPTIONAL,INTENT(IN) :: KSTEP !Timestep number
TYPE(TFILEDATA),POINTER,OPTIONAL,INTENT(IN) :: TPDADFILE !Corresponding dad file
TYPE(TFILEDATA),POINTER,OPTIONAL,INTENT(IN) :: TPDATAFILE!Corresponding data file (used only for DES files)
TYPE(TFILEDATA),POINTER,OPTIONAL,INTENT(IN) :: TPMAINFILE!Corresponding main file (for subfiles)
LOGICAL, OPTIONAL,INTENT(IN) :: OOLD !FALSE if new file (should not be found)
!TRUE if the file could already be in the list
! (add it only if not yet present)
logical, optional,intent(in) :: osplit_ioz !Is the file split vertically
!
INTEGER :: IMI
INTEGER :: IRESP
INTEGER :: ILFITYPE
INTEGER :: ILFIVERB
INTEGER :: IMULT
INTEGER(KIND=LFIINT) :: ILFINPRAR
LOGICAL :: GOLD
logical :: gsplit_ioz
TYPE(TFILEDATA), POINTER :: TZFILE
!
CALL PRINT_MSG(NVERB_DEBUG,'IO','IO_File_add2list','called for '//TRIM(HNAME))
!
IMI = GET_CURRENT_MODEL_INDEX()
!
IF (PRESENT(OOLD)) THEN
GOLD = OOLD
ELSE
GOLD = .FALSE. !By default, we assume file is not yet in list
END IF
!
IF (ASSOCIATED(TPFILE)) THEN
IF (GOLD) THEN
CALL PRINT_MSG(NVERB_INFO,'IO','IO_File_add2list','file '//TRIM(HNAME)//' already associated. Pointer will be overwritten')
TPFILE => NULL()
ELSE
CALL PRINT_MSG(NVERB_FATAL,'IO','IO_File_add2list','file '//TRIM(HNAME)//' already associated')
END IF
END IF
!
CALL IO_File_find_byname(HNAME,TPFILE,IRESP,OOLD=GOLD)
IF (IRESP==0) THEN
!File has been found
!Check if really same one (LFI vs netCDF)
IF (PRESENT(HFORMAT)) THEN
IF ( (HFORMAT=='LFI' .AND. TPFILE%CFORMAT/='NETCDF4') .OR. (HFORMAT=='NETCDF4' .AND. TPFILE%CFORMAT/='LFI') ) THEN
CALL PRINT_MSG(NVERB_FATAL,'IO','IO_File_add2list','file '//TRIM(HNAME)//' already in filelist')
END IF
ELSE
IF (.NOT.GOLD) THEN
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','file '//TRIM(HNAME)//' already in filelist')
ELSE
CALL PRINT_MSG(NVERB_DEBUG,'IO','IO_File_add2list','file '//TRIM(HNAME)//' already in filelist (not unexpected)')
END IF
RETURN
END IF
END IF
!
IF( PRESENT(HFORM) .AND. TRIM(HTYPE)/='SURFACE_DATA') &
CALL PRINT_MSG(NVERB_WARNING,'IO','IO_File_add2list','optional argument HFORM is not used by '//TRIM(HTYPE)//' files')
IF(.NOT.PRESENT(HFORM) .AND. TRIM(HTYPE)=='SURFACE_DATA') &
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','optional argument HFORM is necessary for '//TRIM(HTYPE)//' files')
IF(PRESENT(HFORM)) THEN
IF(HFORM/='FORMATTED' .AND. HFORM/='UNFORMATTED') &
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','HFORM should be FORMATTED or UNFORMATTED and not '//TRIM(HFORM))
END IF
!
IF( PRESENT(HACCESS) .AND. TRIM(HTYPE)/='SURFACE_DATA') &
CALL PRINT_MSG(NVERB_WARNING,'IO','IO_File_add2list','optional argument HACCESS is not used by '//TRIM(HTYPE)//' files')
IF(.NOT.PRESENT(HACCESS) .AND. TRIM(HTYPE)=='SURFACE_DATA') &
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','optional argument HACCESS is necessary for '//TRIM(HTYPE)//' files')
IF(PRESENT(HACCESS)) THEN
IF(HACCESS/='DIRECT' .AND. HACCESS/='SEQUENTIAL' .AND. HACCESS/='STREAM') &
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','HACCESS should be DIRECT, SEQUENTIAL or STREAM and not '//TRIM(HACCESS))
END IF
!
IF (PRESENT(HFORMAT)) THEN
IF(CPROGRAM=='LFICDF') THEN
IF (HFORMAT/='LFI' .AND. HFORMAT/='NETCDF4') &
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','invalid HFORMAT ('//TRIM(HFORMAT)//')')
END IF
ELSE
IF(CPROGRAM=='LFICDF') &
CALL PRINT_MSG(NVERB_FATAL,'IO','IO_File_add2list','optional argument HFORMAT is necessary for CPROGRAM='//TRIM(CPROGRAM))
END IF
!
IF(PRESENT(KLFINPRAR)) THEN
ILFINPRAR = KLFINPRAR
ELSE
ILFINPRAR = 0
END IF
!
IF(PRESENT(KLFITYPE)) THEN
ILFITYPE = KLFITYPE
ELSE
ILFITYPE = -1
END IF
!
IF(PRESENT(KLFIVERB)) THEN
ILFIVERB = KLFIVERB
ELSE
ILFIVERB = -1
END IF
!
IF( PRESENT(KRECL) .AND. TRIM(HTYPE)/='SURFACE_DATA' .AND. TRIM(HTYPE)/='TXT') &
CALL PRINT_MSG(NVERB_WARNING,'IO','IO_File_add2list','optional argument KRECL is not used by '//TRIM(HTYPE)//' files')
IF(.NOT.PRESENT(KRECL) .AND. TRIM(HTYPE)=='SURFACE_DATA') THEN
IF(TRIM(HACCESS)=='DIRECT') &
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','optional argument KRECL is necessary for '//TRIM(HTYPE)// &
' files in DIRECT access')
END IF
!
IF (PRESENT(TPDATAFILE) .AND. TRIM(HTYPE)/='DES') &
CALL PRINT_MSG(NVERB_WARNING,'IO','IO_File_add2list','optional argument TPDATAFILE is not used by '//TRIM(HTYPE)//' files')
!
IF ( PRESENT( TPMAINFILE) ) THEN
IF ( LEN(HTYPE) >= 3 ) THEN
IF ( HTYPE(1:3) /= 'MNH' ) &
CALL PRINT_MSG(NVERB_WARNING,'IO','IO_File_add2list','optional argument TPMAINFILE is not used by '//TRIM(HTYPE)//' files')
ELSE
CALL PRINT_MSG(NVERB_WARNING,'IO','IO_File_add2list','optional argument TPMAINFILE is not used by '//TRIM(HTYPE)//' files')
END IF
END IF
!
IF (.NOT.ASSOCIATED(TFILE_LAST)) THEN
ALLOCATE(TFILE_LAST)
TFILE_FIRST => TFILE_LAST
ELSE
ALLOCATE(TFILE_LAST%TFILE_NEXT)
TFILE_LAST%TFILE_NEXT%TFILE_PREV => TFILE_LAST
TFILE_LAST => TFILE_LAST%TFILE_NEXT
END IF
!
TPFILE => TFILE_LAST
!
TPFILE%CNAME = HNAME
TPFILE%CTYPE = HTYPE
!
IF (PRESENT(HDIRNAME)) THEN
IF (LEN_TRIM(HDIRNAME)>0) TPFILE%CDIRNAME=TRIM(HDIRNAME)
END IF
!
IF (TRIM(HMODE)/='READ' .AND. TRIM(HMODE)/='WRITE') THEN
CALL PRINT_MSG(NVERB_FATAL,'IO','IO_File_add2list','unknown mode ('//TRIM(HMODE)//') for file '//TRIM(HNAME))
END IF
!
TPFILE%CMODE = HMODE
!
IF( PRESENT( KMODEL ) ) TPFILE%NMODEL = KMODEL
!
IF( PRESENT( KSTEP ) ) TPFILE%NSTEP = KSTEP
!
if ( present(osplit_ioz) ) then
gsplit_ioz = osplit_ioz
else
gsplit_ioz = .false.
if ( len_trim(htype) >= 3 ) then
if ( htype(1:3) == 'MNH' ) then
! MNH/MNHBACKUP/MNHOUTPUT
!Remark: 'MNH' is more general than MNHBACKUP and could be in fact a MNHBACKUP file
gsplit_ioz = .true.
! Disable Z-split files for output files with no write of the main domain
! Boxes/subdomains are assumed to be too small to be usefully split vertically
if ( htype == 'MNHOUTPUT' .and. .not. out_model(imi)%lout_bigbox_write ) gsplit_ioz = .false.
! Force no split if asked in NAM_OUTPUT
if ( htype == 'MNHOUTPUT' .and. lout_filesplit_disable(imi) ) gsplit_ioz = .false.
end if
end if
end if
if ( gsplit_ioz ) then
select case (hmode)
case('READ')
tpfile%nsubfiles_ioz = nb_procio_r
case('WRITE')
tpfile%nsubfiles_ioz = nb_procio_w
end select
if (tpfile%nsubfiles_ioz == 1) tpfile%nsubfiles_ioz = 0
else
tpfile%nsubfiles_ioz = 0
end if
IF ( PRESENT(TPMAINFILE) ) THEN
IF ( .NOT. ASSOCIATED( TPMAINFILE ) ) &
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list', 'TPMAINFILE is not associated for file ' // TRIM(HNAME) )
TPFILE%TMAINFILE => TPMAINFILE
ELSE
TPFILE%TMAINFILE => NULL()
END IF
SELECT CASE(TPFILE%CTYPE)
!Chemistry input files
CASE('CHEMINPUT')
IF (TRIM(HMODE)/='READ') & !Invalid because not (yet) necessary
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','invalid mode '//TRIM(HMODE)//' for file '//TRIM(HNAME))
TPFILE%CACCESS = 'SEQUENTIAL'
TPFILE%CFORM = 'FORMATTED'
TPFILE%CFORMAT = 'TEXT'
!Chemistry tabulation files
CASE('CHEMTAB')
IF (TRIM(HMODE)/='READ') & !Invalid because not (yet) necessary
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','invalid mode '//TRIM(HMODE)//' for file '//TRIM(HNAME))
TPFILE%CACCESS = 'SEQUENTIAL'
TPFILE%CFORM = 'FORMATTED'
TPFILE%CFORMAT = 'TEXT'
!DES files
CASE('DES')
TPFILE%CACCESS = 'SEQUENTIAL'
TPFILE%CFORM = 'FORMATTED'
TPFILE%CFORMAT = 'TEXT'
TPFILE%NRECL = 8*1024
IF (.NOT.PRESENT(TPDATAFILE)) THEN
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','missing TPDATAFILE argument for DES file '//TRIM(HNAME))
ELSE
IF (.NOT.ASSOCIATED(TPDATAFILE)) &
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','TPDATAFILE is not associated for DES file '//TRIM(HNAME))
TPFILE%TDATAFILE => TPDATAFILE
TPDATAFILE%TDESFILE => TPFILE
IF (PRESENT(HDIRNAME)) &
CALL PRINT_MSG(NVERB_WARNING,'IO','IO_File_add2list','HDIRNAME argument ignored for DES file '//TRIM(HNAME))
IF (ALLOCATED(TPDATAFILE%CDIRNAME)) TPFILE%CDIRNAME = TPDATAFILE%CDIRNAME
END IF
!GPS files
CASE('GPS')
IF (TRIM(HMODE)/='WRITE') & !Invalid because not (yet) necessary
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','invalid mode '//TRIM(HMODE)//' for file '//TRIM(HNAME))
TPFILE%CACCESS = 'SEQUENTIAL'
TPFILE%CFORM = 'FORMATTED'
TPFILE%CFORMAT = 'TEXT'
!Meteo files
CASE('METEO')
IF (TRIM(HMODE)/='WRITE') & !Invalid because not (yet) necessary
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','invalid mode '//TRIM(HMODE)//' for file '//TRIM(HNAME))
TPFILE%CACCESS = 'SEQUENTIAL'
TPFILE%CFORM = 'UNFORMATTED'
TPFILE%CFORMAT = 'BINARY'
TPFILE%NRECL = 100000000
!Namelist files
CASE('NML')
IF (TRIM(HMODE)/='READ') & !Invalid because not (yet) necessary
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','invalid mode '//TRIM(HMODE)//' for file '//TRIM(HNAME))
TPFILE%CACCESS = 'SEQUENTIAL'
TPFILE%CFORM = 'FORMATTED'
TPFILE%CFORMAT = 'TEXT'
!OUTPUTLISTING files
CASE('OUTPUTLISTING')
IF (TRIM(HMODE)/='WRITE') & !Invalid because not (yet) necessary
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','invalid mode '//TRIM(HMODE)//' for file '//TRIM(HNAME))
TPFILE%CACCESS = 'SEQUENTIAL'
TPFILE%CFORM = 'FORMATTED'
TPFILE%CFORMAT = 'TEXT'
!SURFACE_DATA files
CASE('SURFACE_DATA')
IF (TRIM(HMODE)/='READ') & !Invalid because not (yet) necessary
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_add2list','invalid mode '//TRIM(HMODE)//' for file '//TRIM(HNAME))
TPFILE%CFORMAT = 'SURFACE_DATA'
TPFILE%CFORM = HFORM
TPFILE%CACCESS = HACCESS
IF(TRIM(HACCESS)=='DIRECT') TPFILE%NRECL = KRECL
!Text files
CASE('TXT')
TPFILE%CACCESS = 'SEQUENTIAL'
TPFILE%CFORM = 'FORMATTED'
TPFILE%CFORMAT = 'TEXT'
IF(PRESENT(KRECL)) TPFILE%NRECL = KRECL
!MesoNH files
!Remark: 'MNH' is more general than MNHBACKUP and could be in fact a MNHBACKUP file
CASE ('MNH', 'MNHBACKUP', 'MNHDIACHRONIC', 'MNHDIAG', 'MNHOUTPUT', 'PGD')
IF (TRIM(HMODE)=='READ') THEN
IF (PRESENT(HFORMAT)) THEN
TPFILE%CFORMAT = TRIM(HFORMAT)
ELSE IF (LLFIREAD) THEN
TPFILE%CFORMAT = 'LFI'
TPFILE%NLFINPRAR = ILFINPRAR
ELSE IF (LIOCDF4) THEN
TPFILE%CFORMAT = 'NETCDF4'
ELSE
CALL PRINT_MSG(NVERB_FATAL,'IO','IO_File_add2list','invalid format for file '//TRIM(HNAME))
END IF
ELSE IF (TRIM(HMODE)=='WRITE') THEN
IF (PRESENT(HFORMAT)) THEN
TPFILE%CFORMAT = TRIM(HFORMAT)
ELSE IF (LLFIOUT .AND. LIOCDF4) THEN
TPFILE%CFORMAT = 'LFICDF4'
TPFILE%NLFINPRAR = ILFINPRAR
ELSE IF (LIOCDF4) THEN
TPFILE%CFORMAT = 'NETCDF4'
ELSE IF (LLFIOUT) THEN
TPFILE%CFORMAT = 'LFI'
TPFILE%NLFINPRAR = ILFINPRAR
ELSE
CALL PRINT_MSG(NVERB_FATAL,'IO','IO_File_add2list','invalid format for file '//TRIM(HNAME))
END IF
END IF
!
TPFILE%NLFITYPE = ILFITYPE
TPFILE%NLFIVERB = ILFIVERB
!
! Apply compression to all HTYPE='MNH*' files (if asked)
IF ( LIO_COMPRESS ) THEN
TPFILE%LNCCOMPRESS = LIO_COMPRESS
IF ( NIO_COMPRESS_LEVEL < 0 .OR. NIO_COMPRESS_LEVEL > 9 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list', &
'NIO_COMPRESS_LEVEL must be in the [0..9] range' )
NIO_COMPRESS_LEVEL = 4
END IF
TPFILE%NNCCOMPRESS_LEVEL = NIO_COMPRESS_LEVEL
END IF
!
IF (TRIM(HTYPE)=='MNHOUTPUT') THEN
#ifdef MNH_IOCDF4
TPFILE%LNCREDUCE_FLOAT_PRECISION = LOUT_REDUCE_FLOAT_PRECISION(IMI)
! Apply compression to output files if not already forced for all
IF ( .NOT. LIO_COMPRESS ) THEN
TPFILE%LNCCOMPRESS = LOUT_COMPRESS(IMI)
IF ( NOUT_COMPRESS_LEVEL(IMI) < 0 .OR. NOUT_COMPRESS_LEVEL(IMI) > 9 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list', &
'NOUT_COMPRESS_LEVEL must be in the [0..9] range' )
NOUT_COMPRESS_LEVEL(IMI) = 4
END IF
TPFILE%NNCCOMPRESS_LEVEL = NOUT_COMPRESS_LEVEL(IMI)
END IF
!Set lossy compression
TPFILE%LNCCOMPRESS_LOSSY = LOUT_COMPRESS_LOSSY(IMI)
IF ( LOUT_COMPRESS_LOSSY(IMI) ) THEN
!Force compression if lossy compression is enabled
TPFILE%LNCCOMPRESS = .TRUE.
!Set lossy compression algorithm
SELECT CASE ( UPCASE( COUT_COMPRESS_LOSSY_ALGO(IMI) ) )
CASE ( 'BITGROOM' )
TPFILE%NNCCOMPRESS_LOSSY_ALGO = NF90_QUANTIZE_BITGROOM
CASE ( 'GRANULARBR' )
TPFILE%NNCCOMPRESS_LOSSY_ALGO = NF90_QUANTIZE_GRANULARBR
CASE ( 'BITROUND' )
TPFILE%NNCCOMPRESS_LOSSY_ALGO = NF90_QUANTIZE_BITROUND
CASE DEFAULT
CMNHMSG(1) = 'invalid COUT_COMPRESS_LOSSY_ALGO'
CMNHMSG(2) = 'Accepted algorithms: BITGROOM, GRANULARBR (default choice), BITROUND'
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list' )
TPFILE%NNCCOMPRESS_LOSSY_ALGO = NF90_QUANTIZE_GRANULARBR
END SELECT
!Set number of significant digits/bits for lossy compression algorithm
#if (MNH_REAL == 4)
SELECT CASE ( TPFILE%NNCCOMPRESS_LOSSY_ALGO )
CASE ( NF90_QUANTIZE_BITROUND )
! For 32 bit reals, number of significant bits must be in the 1 to 23 range
IF ( NOUT_COMPRESS_LOSSY_NSD(IMI) < 1 .OR. NOUT_COMPRESS_LOSSY_NSD(IMI) > 23 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list', &
'NOUT_COMPRESS_LOSSY_NSD must be in the [1..23] range' )
NOUT_COMPRESS_LOSSY_NSD(IMI) = 7
END IF
CASE ( NF90_QUANTIZE_BITGROOM, NF90_QUANTIZE_GRANULARBR )
! For 32 bit reals, number of significant digits must be in the 1 to 7 range
IF ( NOUT_COMPRESS_LOSSY_NSD(IMI) < 1 .OR. NOUT_COMPRESS_LOSSY_NSD(IMI) > 7 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list', &
'NOUT_COMPRESS_LOSSY_NSD must be in the [1..7] range' )
NOUT_COMPRESS_LOSSY_NSD(IMI) = 3
END IF
CASE DEFAULT
CALL PRINT_MSG( NVERB_FATAL, 'IO', 'IO_File_add2list', 'invalid NNCCOMPRESS_LOSSY_ALGO (internal fatal error)' )
END SELECT
#elif (MNH_REAL == 8)
IF ( TPFILE%LNCREDUCE_FLOAT_PRECISION ) THEN
SELECT CASE ( TPFILE%NNCCOMPRESS_LOSSY_ALGO )
CASE ( NF90_QUANTIZE_BITROUND )
! For 32 bit reals, number of significant bits must be in the 1 to 23 range
IF ( NOUT_COMPRESS_LOSSY_NSD(IMI) < 1 .OR. NOUT_COMPRESS_LOSSY_NSD(IMI) > 23 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list', &
'NOUT_COMPRESS_LOSSY_NSD must be in the [1..23] range' )
NOUT_COMPRESS_LOSSY_NSD(IMI) = 7
END IF
CASE ( NF90_QUANTIZE_BITGROOM, NF90_QUANTIZE_GRANULARBR )
! For 32 bit reals, number of significant digits must be in the 1 to 7 range
IF ( NOUT_COMPRESS_LOSSY_NSD(IMI) < 1 .OR. NOUT_COMPRESS_LOSSY_NSD(IMI) > 7 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list', &
'NOUT_COMPRESS_LOSSY_NSD must be in the [1..7] range' )
NOUT_COMPRESS_LOSSY_NSD(IMI) = 3
END IF
CASE DEFAULT
CALL PRINT_MSG( NVERB_FATAL, 'IO', 'IO_File_add2list', 'invalid NNCCOMPRESS_LOSSY_ALGO (internal fatal error)' )
END SELECT
ELSE
SELECT CASE ( TPFILE%NNCCOMPRESS_LOSSY_ALGO )
CASE ( NF90_QUANTIZE_BITROUND )
! For 64 bit reals, number of significant bits must be in the 1 to 52 range
IF ( NOUT_COMPRESS_LOSSY_NSD(IMI) < 1 .OR. NOUT_COMPRESS_LOSSY_NSD(IMI) > 52 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list', &
'NOUT_COMPRESS_LOSSY_NSD must be in the [1..52] range' )
NOUT_COMPRESS_LOSSY_NSD(IMI) = 7
END IF
CASE ( NF90_QUANTIZE_BITGROOM, NF90_QUANTIZE_GRANULARBR )
! For 64 bit reals, number of significant digits must be in the 1 to 15 range
IF ( NOUT_COMPRESS_LOSSY_NSD(IMI) < 1 .OR. NOUT_COMPRESS_LOSSY_NSD(IMI) > 15 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list', &
'NOUT_COMPRESS_LOSSY_NSD must be in the [1..15] range')
NOUT_COMPRESS_LOSSY_NSD(IMI) = 3
END IF
CASE DEFAULT
CALL PRINT_MSG( NVERB_FATAL, 'IO', 'IO_File_add2list', 'invalid NNCCOMPRESS_LOSSY_ALGO (internal fatal error)' )
END SELECT
END IF
#else
#error "Invalid MNH_REAL"
#endif
TPFILE%NNCCOMPRESS_LOSSY_NSD = NOUT_COMPRESS_LOSSY_NSD(IMI)
END IF
#endif
ELSE IF (TRIM(HTYPE)=='MNHBACKUP' .OR. TRIM(HTYPE)=='MNHDIACHRONIC') THEN
TPFILE%LNCREDUCE_FLOAT_PRECISION = LBAK_REDUCE_FLOAT_PRECISION(IMI)
IF ( LBAK_REDUCE_FLOAT_PRECISION(IMI) ) THEN
IF ( .NOT. LIO_ALLOW_REDUCED_PRECISION_BACKUP ) THEN
cmnhmsg(1) = 'LBAK_REDUCE_FLOAT_PRECISION=T is dangerous'
cmnhmsg(2) = 'if needed, it must be forced with LIO_ALLOW_REDUCED_PRECISION_BACKUP=T in NAM_CONFIO'
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list' )
TPFILE%LNCREDUCE_FLOAT_PRECISION = .FALSE.
ELSE
IF ( .NOT. ASSOCIATED( TPFILE%TMAINFILE ) ) THEN
! Do not print warning for subfiles
CALL PRINT_MSG( NVERB_WARNING, 'IO', 'IO_File_add2list', trim(tpfile%cname) // &
' LBAK_REDUCE_FLOAT_PRECISION=T dangerous (forced by LIO_ALLOW_REDUCED_PRECISION_BACKUP=T)' )
END IF
END IF
END IF
! Apply compression to backup files if not already forced for all
IF ( .NOT. LIO_COMPRESS ) THEN
TPFILE%LNCCOMPRESS = LBAK_COMPRESS(IMI)
IF ( NBAK_COMPRESS_LEVEL(IMI) < 0 .OR. NBAK_COMPRESS_LEVEL(IMI) > 9 ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_add2list', &
'NBAK_COMPRESS_LEVEL must be in the [0..9] range' )
NBAK_COMPRESS_LEVEL(IMI) = 4
END IF
TPFILE%NNCCOMPRESS_LEVEL = NBAK_COMPRESS_LEVEL(IMI)
END IF
ELSE IF ( TRIM(HTYPE) == 'MNHDIAG' ) THEN
TPFILE%LNCREDUCE_FLOAT_PRECISION = LDIAG_REDUCE_FLOAT_PRECISION
END IF
!
IF ( TRIM(HTYPE) == 'MNHBACKUP' .OR. TRIM(HTYPE) == 'MNHOUTPUT' ) THEN
IF( PRESENT(TPDADFILE) ) THEN
CALL PRINT_MSG( NVERB_FATAL, 'IO', 'IO_File_add2list', &
'TPDADFILE should not be provided for backup or output file ' // TRIM(HNAME) )
END IF
! Find dad file (not for the subfiles)
IF ( .NOT. ASSOCIATED(TPFILE%TMAINFILE) ) THEN
TPFILE%TDADFILE => NULL()
! Security check (if it happens, this part of the code should be exported outside of the IMI loop)
IF ( NDAD(IMI) > IMI ) CALL PRINT_MSG( NVERB_FATAL, 'IO', 'IO_File_add2list', 'NDAD(IMI)>IMI' )
IF ( NDAD(IMI) == IMI .OR. IMI == 1 ) THEN
TPFILE%TDADFILE => TPFILE !Points to itself
ELSE
! Try to find the dad file: it must be of the same type (MNHBACKUP/MNHOUTPUT), to the dad and at the same time
IMULT = NINT( DYN_MODEL(NDAD(IMI))%XTSTEP / DYN_MODEL(IMI)%XTSTEP )
TZFILE => TFILE_FIRST
DO WHILE ( ASSOCIATED(TZFILE) )
IF ( TZFILE%CTYPE == TPFILE%CTYPE .AND. TZFILE%NMODEL == NDAD(IMI) ) THEN
! Check if at same time
IF ( TPFILE%NSTEP == ( TZFILE%NSTEP - 1 ) * IMULT + 1 ) THEN
TPFILE%TDADFILE => TZFILE
EXIT
END IF
END IF
TZFILE => TZFILE%TFILE_NEXT
END DO
END IF
ELSE
! Subfile
TPFILE%TDADFILE => NULL()
END IF
ELSE
IF(PRESENT(TPDADFILE)) THEN
IF (.NOT.ASSOCIATED(TPDADFILE)) CALL PRINT_MSG(NVERB_WARNING,'IO','IO_File_add2list', &
'TPDADFILE provided but not associated for file '//TRIM(HNAME))
TPFILE%TDADFILE => TPDADFILE
ELSE
TPFILE%TDADFILE => NULL()
END IF
END IF
CASE default
call print_msg(NVERB_FATAL,'IO','IO_File_add2list','invalid type '//trim(tpfile%ctype)//' for file '//trim(hname))
END SELECT
!
TPFILE%LOPENED = .FALSE.
TPFILE%NOPEN = 0
TPFILE%NCLOSE = 0
NFILE_STAT_NADD = NFILE_STAT_NADD + 1
NFILE_STAT_CURSIZE = NFILE_STAT_CURSIZE + 1
NFILE_STAT_MAXSIZE = MAX( NFILE_STAT_MAXSIZE, NFILE_STAT_CURSIZE )
END SUBROUTINE IO_File_add2list
RECURSIVE SUBROUTINE IO_File_remove_from_list( TPFILE )
! Remove a file from the file list and free its ressources
#ifdef MNH_IOCDF4
USE MODE_IO_TOOLS_NC4, ONLY: IO_Iocdf_dealloc_nc4
#endif
TYPE(TFILEDATA), POINTER, INTENT(INOUT) :: TPFILE !File structure to return
INTEGER :: JF
IF ( .NOT.ASSOCIATED(TPFILE) ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_remove_from_list', 'trying to remove a non existing file' )
RETURN
END IF
CALL PRINT_MSG( NVERB_DEBUG, 'IO', 'IO_File_remove_from_list', 'called for ' // TRIM(TPFILE%CNAME) )
! Check if the file is opened. If it is, print an error
! Do not do the close here, because there will be a circular dependency with the MODE_IO_FILE module
IF ( TPFILE%LOPENED ) THEN
CALL PRINT_MSG( NVERB_ERROR, 'IO', 'IO_File_remove_from_list', TRIM(TPFILE%CNAME) // &
': removing an opened file is not allowed' )
RETURN
END IF
! Print a warning if a file has never been opened
IF ( TPFILE%NOPEN == 0 ) CALL PRINT_MSG( NVERB_WARNING, 'IO', 'IO_File_remove_from_list', TRIM(TPFILE%CNAME) // &
': never been opened' )
! Are there sub-files? If yes, remove them first
! Do it only if not already removed (ie in IO_File_close)
IF ( TPFILE%NSUBFILES_IOZ > 0 .AND. ALLOCATED( TPFILE%TFILES_IOZ ) ) THEN
DO JF = 1, TPFILE%NSUBFILES_IOZ
CALL IO_File_remove_from_list( TPFILE%TFILES_IOZ(JF)%TFILE )
END DO
END IF
! Remove corresponding .des file
IF ( ASSOCIATED( TPFILE%TDESFILE) ) CALL IO_File_remove_from_list( TPFILE%TDESFILE )
! Remove file from list
IF ( ASSOCIATED(TPFILE%TFILE_PREV) ) THEN
TPFILE%TFILE_PREV%TFILE_NEXT => TPFILE%TFILE_NEXT
ELSE
! File was first in the list
TFILE_FIRST => TPFILE%TFILE_NEXT
END IF
IF ( ASSOCIATED(TPFILE%TFILE_NEXT) ) THEN
TPFILE%TFILE_NEXT%TFILE_PREV => TPFILE%TFILE_PREV
ELSE
! File was last in the list
TFILE_LAST => TPFILE%TFILE_PREV
END IF
#ifdef MNH_IOCDF4
IF ( ASSOCIATED( TPFILE%TNCDIMS ) ) THEN
CALL PRINT_MSG( NVERB_WARNING, 'IO', 'IO_File_remove_from_list', TRIM(TPFILE%CNAME) // &
': TNCDIMS should not be associated at this point' )
CALL IO_Iocdf_dealloc_nc4( TPFILE%TNCDIMS )
END IF
#endif
! Free file ressources
IF ( ASSOCIATED(TPFILE%TBOXNCDIMS) ) DEALLOCATE( TPFILE%TBOXNCDIMS )
DEALLOCATE( TPFILE )
TPFILE => NULL()
NFILE_STAT_NREM = NFILE_STAT_NREM + 1
NFILE_STAT_CURSIZE = NFILE_STAT_CURSIZE - 1
END SUBROUTINE IO_File_remove_from_list
SUBROUTINE IO_File_find_byname(HNAME,TPFILE,KRESP,OOLD)
!
USE MODD_PARAMETERS, ONLY: NFILENAMELGTMAX
!
CHARACTER(LEN=*), INTENT(IN) :: HNAME ! Name of the file to find
TYPE(TFILEDATA),POINTER,INTENT(OUT) :: TPFILE ! File structure to return
INTEGER, INTENT(OUT) :: KRESP ! Return value
LOGICAL, OPTIONAL, INTENT(IN) :: OOLD ! FALSE if new file (should not be found)
! TRUE if file may be in the list
!
TYPE(TFILEDATA),POINTER :: TZFILE ! File structure
LOGICAL :: GOLD
!
CALL PRINT_MSG(NVERB_DEBUG,'IO','IO_File_find_byname','looking for '//TRIM(HNAME))
!
NULLIFY(TPFILE)
KRESP = 0
!
IF (PRESENT(OOLD)) THEN
GOLD = OOLD
ELSE
GOLD = .TRUE.
END IF
!
IF (LEN_TRIM(HNAME)>NFILENAMELGTMAX) &
CALL PRINT_MSG(NVERB_WARNING,'IO','IO_File_find_byname','HNAME length is bigger than NFILENAMELGTMAX for '//TRIM(HNAME))
!
IF (.NOT.ASSOCIATED(TFILE_FIRST)) THEN
IF (GOLD) CALL PRINT_MSG(NVERB_WARNING,'IO','IO_File_find_byname','filelist is empty')
ELSE
!
TZFILE => TFILE_FIRST
!
DO
IF (TRIM(TZFILE%CNAME) == TRIM(HNAME(1:MIN(NFILENAMELGTMAX,LEN(HNAME)))) ) THEN
TPFILE => TZFILE
EXIT
END IF
IF (.NOT.ASSOCIATED(TZFILE%TFILE_NEXT)) EXIT
TZFILE => TZFILE%TFILE_NEXT
END DO
END IF
!
IF (.NOT.ASSOCIATED(TPFILE)) THEN
CALL PRINT_MSG(NVERB_DEBUG,'IO','IO_File_find_byname','file '//TRIM(HNAME)//' not found in list')
KRESP = -1 !File not found
ELSE
IF (GOLD) THEN
CALL PRINT_MSG(NVERB_DEBUG,'IO','IO_File_find_byname',TRIM(HNAME)//' was found')
ELSE !File should not be found
CALL PRINT_MSG(NVERB_ERROR,'IO','IO_File_find_byname',TRIM(HNAME)//' was found (unexpected)')
END IF
END IF
!
END SUBROUTINE IO_File_find_byname
!
SUBROUTINE IO_Filelist_print(TPFILE_FIRST)
!
USE MODD_VAR_ll, ONLY : IP
!
TYPE(TFILEDATA),POINTER,OPTIONAL,INTENT(IN) :: TPFILE_FIRST
!
TYPE(TFILEDATA),POINTER :: TZFILE ! File structure
!
IF (IP/=1 .AND. .NOT.LVERB_ALLPRC) RETURN
!
CALL PRINT_MSG(NVERB_DEBUG,'IO','IO_Filelist_print','called')
!
IF (PRESENT(TPFILE_FIRST)) THEN
IF (.NOT.ASSOCIATED(TPFILE_FIRST)) RETURN
TZFILE => TPFILE_FIRST
ELSE
IF (.NOT.ASSOCIATED(TFILE_FIRST)) RETURN
TZFILE => TFILE_FIRST
END IF
!
WRITE( *, '( /, "Filelist statistics:" )' )
WRITE( *, '( " Number of files added: ", I0 )' ) NFILE_STAT_NADD
WRITE( *, '( " Number of files removed: ", I0 )' ) NFILE_STAT_NREM
WRITE( *, '( " Current list size: ", I0 )' ) NFILE_STAT_CURSIZE
WRITE( *, '( " Maximum list size: ", I0 )' ) NFILE_STAT_MAXSIZE
WRITE( *, '( /, "Current filelist" )' )
WRITE( *, '( A28," ",A13," ",A7," ",A7," ",A7," ",A7," ",A6," ",A6," ",A5," ",A6," ",A13," ",A13)' ) &
'CNAME ', &
'CTYPE ','CFORMAT','CMODE ','LOPENED','NLFIFLU','NNCID','NLU','NOPEN','NCLOSE','NOPEN_CURRENT','NSUBFILES_IOZ'
WRITE (*,'( A,A )') '--------------------------------------------------------------------------------------------------------', &
'------------------------'
WRITE (*,'(A28," ",A13," ",A7," ",A7," ",L7," ",I7," ",I6," ",I6," ",I5," ",I6," ",I13," ",I13)' ) &
TZFILE%CNAME,TZFILE%CTYPE,TZFILE%CFORMAT,&
TZFILE%CMODE,TZFILE%LOPENED,TZFILE%NLFIFLU,TZFILE%NNCID,TZFILE%NLU,TZFILE%NOPEN,TZFILE%NCLOSE,TZFILE%NOPEN_CURRENT,&
TZFILE%NSUBFILES_IOZ
!
DO WHILE (ASSOCIATED(TZFILE%TFILE_NEXT))
TZFILE => TZFILE%TFILE_NEXT
WRITE (*,'(A28," ",A13," ",A7," ",A7," ",L7," ",I7," ",I6," ",I6," ",I5," ",I6," ",I13," ",I13)' ) &
TZFILE%CNAME,TZFILE%CTYPE,TZFILE%CFORMAT,&
TZFILE%CMODE,TZFILE%LOPENED,TZFILE%NLFIFLU,TZFILE%NNCID,TZFILE%NLU,TZFILE%NOPEN,TZFILE%NCLOSE,TZFILE%NOPEN_CURRENT,&
TZFILE%NSUBFILES_IOZ
END DO
WRITE (*,'(/)')
!
END SUBROUTINE IO_Filelist_print
!
END MODULE MODE_IO_MANAGE_STRUCT