# 1 "../fms/fms_io.F90"
!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
module fms_io_mod
# 1 "../include/fms_platform.h" 1
! -*-f90-*-*
!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
!Set type kinds.
# 37
!These values are not necessarily portable.
!DEC$ MESSAGE:'Using 8-byte addressing'
!Control "pure" functions.
# 54
!DEC$ MESSAGE:'Using pure routines.'
!Control array members of derived types.
# 66
!DEC$ MESSAGE:'Using allocatable derived type array members.'
!Control use of cray pointers.
# 78
!DEC$ MESSAGE:'Using cray pointers.'
!Control size of integers that will hold address values.
!Appears for legacy reasons, but seems rather dangerous.
# 89
!If you do not want to use 64-bit integers.
# 95
!If you do not want to use 32-bit floats.
# 106
!If you want to use quad-precision.
# 115
# 22 "../fms/fms_io.F90" 2
!
!
!
! Zhi Liang
!
!
! M.J. Harrison
!
!
!
! M.J. Harrison
!
!
! B. Wyman
!
!
! This module is for writing and reading restart data in NetCDF format.
! fms_io_init must be called before the first write_data/read_data call
! For writing, fms_io_exit must be called after ALL write calls have
! been made. Typically, fms_io_init and fms_io_exit are placed in the
! main (driver) program while read_data and write_data can be called where needed.
! Presently, two combinations of threading and fileset are supported, users can choose
! one line of the following by setting namelist:
!
! With the introduction of netCDF restart files, there is a need for a global
! switch to turn on/off netCDF restart options in all of the modules that deal with
! restart files. Here two more namelist variables (logical type) are introduced to fms_io
!
! fms_netcdf_override
! fms_netcdf_restart
!
! because default values of both flags are .true., the default behavior of the entire model is
! to use netCDF IO mode. To turn off netCDF restart, simply set fms_netcdf_restart to .false.
!
!
!
!
! threading_read can be 'single' or 'multi'
!
!
! .true. : fms_netcdf_restart overrides individual do_netcdf_restart value (default behavior)
! .false.: individual module settings has a precedence over the global setting, therefore fms_netcdf_restart is ignored
!
!
! .true. : all modules deal with restart files will operate under netCDF mode (default behavior)
! .false.: all modules deal with restart files will operate under binary mode
! This flag is effective only when fms_netcdf_override is .true. When fms_netcdf_override is .false., individual
! module setting takes over.
!
!
! .true. : time_stamp will be added to the restart file name as a prefix when
! optional argument time_stamp is passed into routine save_restart.
! .false.: time_stmp will not be added to the restart file name even though
! time_stamp is passed into save_restart.
! default is true.
!
!
! set print_chksum (default is false) to true to print out chksum of fields that are
! read and written through save_restart/restore_state. The chksum is accross all the
! processors, so there will be only one chksum even there are multiple-tiles in the
! grid. For the multiple case, the filename appeared in the message will contain
! tile1 because the message is print out from root pe and on root pe the tile id is tile1.
!
!
! set debug_mask_list (default is false) to true to print out mask_list reading from mask_table.
!
!
! Set checksum_required (default is true) to true to compare checksums stored in the attribute of a
! field against the checksum after reading in the data. This check mitigates the possibility of data
! that gets corrupted on write or read from being used in a n ongoing fashion. The checksum is across
! all the processors, so there will be only one checksum even if there are multiple-tiles in the
! grid. For the decomposed file case, the filename appearing in the message will contain tile1
! because the message is printed out from the root pe and on root pe the tile id is tile1.
!
! Set checksum_required to false if you do not want to compare checksums.
!
!
use mpp_io_mod, only: mpp_open, mpp_close, mpp_io_init, mpp_io_exit, mpp_read, mpp_write
use mpp_io_mod, only: mpp_write_meta, mpp_get_info, mpp_get_atts, mpp_get_fields
use mpp_io_mod, only: mpp_read_compressed, mpp_write_compressed, mpp_def_dim
use mpp_io_mod, only: mpp_write_unlimited_axis, mpp_read_distributed_ascii
use mpp_io_mod, only: mpp_get_axes, mpp_get_axis_data, mpp_get_att_char, mpp_get_att_name
use mpp_io_mod, only: mpp_get_att_real_scalar, mpp_attribute_exist, mpp_is_dist_ioroot
use mpp_io_mod, only: fieldtype, axistype, atttype, default_field, default_axis, default_att
use mpp_io_mod, only: MPP_NETCDF, MPP_ASCII, MPP_MULTI, MPP_SINGLE, MPP_OVERWR, MPP_RDONLY
use mpp_io_mod, only: MPP_IEEE32, MPP_NATIVE, MPP_DELETE, MPP_APPEND, MPP_SEQUENTIAL, MPP_DIRECT
use mpp_io_mod, only: MAX_FILE_SIZE, mpp_get_att_value
use mpp_io_mod, only: mpp_get_dimension_length
use mpp_domains_mod, only: domain2d, domain1d, NULL_DOMAIN1D, NULL_DOMAIN2D, operator( .EQ. )
use mpp_domains_mod, only: CENTER, EAST, WEST, NORTH, SOUTH, CORNER
use mpp_domains_mod, only: mpp_get_domain_components, mpp_get_compute_domain, mpp_get_data_domain
use mpp_domains_mod, only: mpp_get_domain_shift, mpp_get_global_domain, mpp_global_field, mpp_domain_is_tile_root_pe
use mpp_domains_mod, only: mpp_get_ntile_count, mpp_get_current_ntile, mpp_get_tile_id
use mpp_domains_mod, only: mpp_get_pelist, mpp_get_io_domain, mpp_get_domain_npes
use mpp_domains_mod, only: domainUG, mpp_pass_SG_to_UG, mpp_get_UG_domain_ntiles, mpp_get_UG_domain_tile_id
use mpp_mod, only: mpp_error, FATAL, NOTE, WARNING, mpp_pe, mpp_root_pe, mpp_npes, stdlog, stdout
use mpp_mod, only: mpp_broadcast, ALL_PES, mpp_chksum, mpp_get_current_pelist, mpp_npes, lowercase
use mpp_mod, only: input_nml_file, mpp_get_current_pelist_name, uppercase
use mpp_mod, only: mpp_gather, mpp_scatter, mpp_send, mpp_recv, mpp_sync_self, COMM_TAG_1, EVENT_RECV
use mpp_mod, only: MPP_FILL_DOUBLE,MPP_FILL_INT
use platform_mod, only: r8_kind
!----------
!ug support
use mpp_parameter_mod, only: COMM_TAG_2
use mpp_domains_mod, only: mpp_get_UG_io_domain
use mpp_domains_mod, only: mpp_domain_UG_is_tile_root_pe
use mpp_domains_mod, only: mpp_get_UG_domain_npes
use mpp_domains_mod, only: mpp_get_UG_domain_pelist
use mpp_io_mod, only: mpp_io_unstructured_write
use mpp_io_mod, only: mpp_io_unstructured_read
use mpp_io_mod, only: mpp_file_is_opened
!----------
implicit none
private
integer, parameter, private :: max_split_file = 50
integer, parameter, private :: max_fields=400
integer, parameter, private :: max_axes=40
integer, parameter, private :: max_atts=20
integer, parameter, private :: max_domains = 10
integer, parameter, private :: MAX_TIME_LEVEL_REGISTER = 2
integer, parameter, private :: MAX_TIME_LEVEL_WRITE = 20
integer, parameter :: max_axis_size=10000
! Index postions for axes in restart_file_type
! This is done so the user may define the axes
! in any order but a check can be performed
! to ensure no registration of duplicate axis
!----------
!ug support
integer(4),parameter,public :: XIDX = 1
integer(4),parameter,public :: YIDX = 2
integer(4),parameter,public :: CIDX = 3
integer(4),parameter,public :: ZIDX = 4
integer(4),parameter,public :: HIDX = 5
integer(4),parameter,public :: TIDX = 6
integer(4),parameter,public :: UIDX = 7
integer(4),parameter,public :: CCIDX = 8
!---------
integer, parameter, private :: NIDX=8
type meta_type
type(meta_type), pointer :: prev=>null(), next=>null()
!!$ Gfortran on gaea does not yet support deferred length character strings
!!$ character(len=:),allocatable :: name
character(len=256) :: name
real, allocatable :: rval(:)
integer, allocatable :: ival(:)
!!$ Gfortran on gaea does not yet support deferred length character strings
!!$ character(len=:), allocatable :: cval
character(len=256) :: cval
end type meta_type
type ax_type
private
character(len=128) :: name = ''
character(len=128) :: units = ''
character(len=128) :: longname = ''
character(len=8) :: cartesian = ''
character(len=256) :: compressed = ''
character(len=128) :: dimlen_name = ''
character(len=128) :: dimlen_lname = ''
character(len=128) :: calendar = ''
integer :: sense !Orientation of z axis definition
integer :: dimlen !max dim of elements across global domain
real :: min !valid min for real axis data
integer :: imin !valid min for integer axis data
integer,allocatable :: idx(:) !compressed io-domain index vector
integer,allocatable :: nelems(:) !num elements for each rank in io domain
real, pointer :: data(:) =>NULL() !real axis values (not used if time axis)
type(domain2d),pointer :: domain =>NULL() ! domain associated with compressed axis
!----------
!ug support
type(domainUG),pointer :: domain_ug => null() ! null() ! NULL()
end type Ptr0Dr
type Ptr1Dr
real, dimension(:), pointer :: p => NULL()
end type Ptr1Dr
type Ptr2Dr
real, dimension(:,:), pointer :: p => NULL()
end type Ptr2Dr
type Ptr3Dr
real, dimension(:,:,:), pointer :: p => NULL()
end type Ptr3Dr
type Ptr2Dr8
real(8), dimension(:,:), pointer :: p => NULL()
end type Ptr2Dr8
type Ptr3Dr8
real(8), dimension(:,:,:), pointer :: p => NULL()
end type Ptr3Dr8
type Ptr4Dr
real, dimension(:,:,:,:), pointer :: p => NULL()
end type Ptr4Dr
type Ptr0Di
integer, pointer :: p => NULL()
end type Ptr0Di
type Ptr1Di
integer, dimension(:), pointer :: p => NULL()
end type Ptr1Di
type Ptr2Di
integer, dimension(:,:), pointer :: p => NULL()
end type Ptr2Di
type Ptr3Di
integer, dimension(:,:,:), pointer :: p => NULL()
end type Ptr3Di
type restart_file_type
private
integer :: unit = -1 ! mpp_io unit for netcdf file
character(len=128) :: name = ''
integer :: register_id = 0
integer :: nvar = 0
integer :: natt = 0
integer :: max_ntime = 0
logical :: is_root_pe = .FALSE.
logical :: is_compressed = .FALSE.
logical :: unlimited_axis = .FALSE.
integer :: tile_count = 1
type(ax_type), allocatable :: axes(:) ! Currently define X,Y,Compressed, unlimited and maybe Z
type(meta_type), pointer :: first =>NULL() ! pointer to first additional global metadata element
type(var_type), dimension(:), pointer :: var => NULL()
type(Ptr0Dr), dimension(:,:), pointer :: p0dr => NULL()
type(Ptr1Dr), dimension(:,:), pointer :: p1dr => NULL()
type(Ptr2Dr), dimension(:,:), pointer :: p2dr => NULL()
type(Ptr3Dr), dimension(:,:), pointer :: p3dr => NULL()
type(Ptr2Dr8), dimension(:,:), pointer :: p2dr8 => NULL()
type(Ptr3Dr8), dimension(:,:), pointer :: p3dr8 => NULL()
type(Ptr4Dr), dimension(:,:), pointer :: p4dr => NULL()
type(Ptr0Di), dimension(:,:), pointer :: p0di => NULL()
type(Ptr1Di), dimension(:,:), pointer :: p1di => NULL()
type(Ptr2Di), dimension(:,:), pointer :: p2di => NULL()
type(Ptr3Di), dimension(:,:), pointer :: p3di => NULL()
end type restart_file_type
interface read_data
module procedure read_data_4d_new
module procedure read_data_3d_new
module procedure read_data_2d_new
module procedure read_data_2d_UG
module procedure read_data_1d_new
module procedure read_data_scalar_new
module procedure read_data_i3d_new
module procedure read_data_i2d_new
module procedure read_data_i1d_new
module procedure read_data_iscalar_new
module procedure read_data_2d, read_ldata_2d, read_idata_2d
module procedure read_data_3d, read_data_4d
# 338
module procedure read_data_text
module procedure read_data_2d_region
module procedure read_data_3d_region
module procedure read_data_2d_region_r8
module procedure read_data_3d_region_r8
end interface
interface read_distributed
module procedure read_distributed_r1D
module procedure read_distributed_r3D
module procedure read_distributed_r5D
module procedure read_distributed_i1D
module procedure read_distributed_iscalar
module procedure read_distributed_a1D
end interface
! Only need read compressed att; write is handled in with
! mpp_io calls in save_compressed_restart
interface read_compressed
module procedure read_compressed_i1d
module procedure read_compressed_i2d
module procedure read_compressed_1d
module procedure read_compressed_2d
module procedure read_compressed_3d
end interface read_compressed
interface write_data
module procedure write_data_4d_new
module procedure write_data_3d_new
module procedure write_data_2d_new
module procedure write_data_1d_new
module procedure write_data_scalar_new
module procedure write_data_i3d_new
module procedure write_data_i2d_new
module procedure write_data_i1d_new
module procedure write_data_iscalar_new
module procedure write_data_2d, write_ldata_2d, write_idata_2d
module procedure write_data_3d, write_data_4d
# 381
end interface
interface register_restart_field
module procedure register_restart_field_r0d
module procedure register_restart_field_r1d
module procedure register_restart_field_r2d
module procedure register_restart_field_r3d
module procedure register_restart_field_r2d8
module procedure register_restart_field_r3d8
module procedure register_restart_field_r2d8_2level
module procedure register_restart_field_r3d8_2level
module procedure register_restart_field_r4d
module procedure register_restart_field_i0d
module procedure register_restart_field_i1d
module procedure register_restart_field_i2d
module procedure register_restart_field_i3d
module procedure register_restart_field_r0d_2level
module procedure register_restart_field_r1d_2level
module procedure register_restart_field_r2d_2level
module procedure register_restart_field_r3d_2level
module procedure register_restart_field_i0d_2level
module procedure register_restart_field_i1d_2level
module procedure register_restart_field_i2d_2level
module procedure register_restart_field_i3d_2level
module procedure register_restart_region_r2d
module procedure register_restart_region_r3d
end interface
interface register_restart_axis
module procedure register_restart_axis_r1d
module procedure register_restart_axis_i1d
module procedure register_restart_axis_unlimited
end interface
interface reset_field_pointer
module procedure reset_field_pointer_r0d
module procedure reset_field_pointer_r1d
module procedure reset_field_pointer_r2d
module procedure reset_field_pointer_r3d
module procedure reset_field_pointer_r4d
module procedure reset_field_pointer_i0d
module procedure reset_field_pointer_i1d
module procedure reset_field_pointer_i2d
module procedure reset_field_pointer_i3d
module procedure reset_field_pointer_r0d_2level
module procedure reset_field_pointer_r1d_2level
module procedure reset_field_pointer_r2d_2level
module procedure reset_field_pointer_r3d_2level
module procedure reset_field_pointer_i0d_2level
module procedure reset_field_pointer_i1d_2level
module procedure reset_field_pointer_i2d_2level
module procedure reset_field_pointer_i3d_2level
end interface
interface restore_state
module procedure restore_state_all
module procedure restore_state_one_field
end interface
interface query_initialized
module procedure query_initialized_id
module procedure query_initialized_name
module procedure query_initialized_r2d
module procedure query_initialized_r3d
module procedure query_initialized_r4d
end interface
interface set_initialized
module procedure set_initialized_id
module procedure set_initialized_name
module procedure set_initialized_r2d
module procedure set_initialized_r3d
module procedure set_initialized_r4d
end interface
interface get_global_att_value
module procedure get_global_att_value_text
module procedure get_global_att_value_real
end interface
interface get_var_att_value
module procedure get_var_att_value_text
end interface
interface parse_mask_table
module procedure parse_mask_table_2d
module procedure parse_mask_table_3d
end interface
interface get_mosaic_tile_file
module procedure get_mosaic_tile_file_sg
module procedure get_mosaic_tile_file_ug
end interface
integer :: num_files_r = 0 ! number of currently opened files for reading
integer :: num_files_w = 0 ! number of currently opened files for writing
integer :: num_domains = 0 ! number of domains in array_domain
integer :: num_registered_files = 0 ! mumber of files registered by calling register_restart_file
integer :: thread_r, form
logical :: module_is_initialized = .FALSE.
character(len=128):: error_msg
logical :: great_circle_algorithm=.FALSE.
!------ private data, pointer to current 2d domain ------
! entrained from fms_mod. This will be deprecated in the future.
type(domain2D), pointer, private :: Current_domain =>NULL()
integer, private :: is,ie,js,je ! compute domain
integer, private :: isd,ied,jsd,jed ! data domain
integer, private :: isg,ieg,jsg,jeg ! global domain
character(len=128), dimension(:), allocatable :: registered_file ! file names registered through register_restart_file
type(restart_file_type), dimension(:), allocatable :: files_read ! store files that are read through read_data
type(restart_file_type), dimension(:), allocatable, target :: files_write ! store files that are written through write_data
type(domain2d), dimension(max_domains), target, save :: array_domain
type(domain1d), dimension(max_domains), save :: domain_x, domain_y
public :: read_data, read_compressed, write_data, read_distributed
public :: fms_io_init, fms_io_exit, field_size, get_field_size
public :: open_namelist_file, open_restart_file, open_ieee32_file, close_file
public :: set_domain, nullify_domain, get_domain_decomp, return_domain
public :: open_file, open_direct_file
public :: get_restart_io_mode, get_tile_string, string
public :: get_mosaic_tile_grid, get_mosaic_tile_file, get_file_name, get_mosaic_tile_file_ug
public :: get_global_att_value, get_var_att_value
public :: file_exist, field_exist
public :: register_restart_field, register_restart_axis, save_restart, restore_state
public :: set_meta_global
public :: save_restart_border, restore_state_border
public :: restart_file_type, query_initialized, set_initialized, free_restart_type
public :: reset_field_name, reset_field_pointer
private :: lookup_field_r, lookup_axis, unique_axes
public :: dimension_size
public :: set_filename_appendix, get_instance_filename
public :: get_filename_appendix, nullify_filename_appendix
public :: parse_mask_table
public :: get_great_circle_algorithm
public :: write_version_number
character(len=32), save :: filename_appendix = ''
!--- public interface ---
interface string
module procedure string_from_integer
module procedure string_from_real
end interface
!--- namelist interface
logical :: fms_netcdf_override = .true.
logical :: fms_netcdf_restart = .true.
character(len=32) :: threading_read = 'multi'
character(len=32) :: format = 'netcdf'
logical :: read_all_pe = .TRUE.
character(len=64) :: iospec_ieee32 = '-N ieee_32'
integer :: max_files_w = 40
integer :: max_files_r = 40
integer :: dr_set_size = 10
logical :: read_data_bug = .false.
logical :: time_stamp_restart = .true.
logical :: print_chksum = .false.
logical :: show_open_namelist_file_warning = .false.
logical :: debug_mask_list = .false.
logical :: checksum_required = .true.
namelist /fms_io_nml/ fms_netcdf_override, fms_netcdf_restart, &
threading_read, format, read_all_pe, iospec_ieee32,max_files_w,max_files_r, &
read_data_bug, time_stamp_restart, print_chksum, show_open_namelist_file_warning, &
debug_mask_list, checksum_required, dr_set_size
integer :: pack_size ! = 1 for double = 2 for float
! Include variable "version" to be written to log file.
# 1 "../include/file_version.h" 1
! -*-f90-*-
!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
# 23
character(len=*), parameter :: version = 'unknown'
# 556 "../fms/fms_io.F90" 2
!----------
!ug support
public :: fms_io_unstructured_register_restart_axis
public :: fms_io_unstructured_register_restart_field
public :: fms_io_unstructured_save_restart
public :: fms_io_unstructured_read
public :: fms_io_unstructured_get_field_size
public :: fms_io_unstructured_file_unit
public :: fms_io_unstructured_field_exist
interface fms_io_unstructured_register_restart_axis
module procedure fms_io_unstructured_register_restart_axis_r1D
module procedure fms_io_unstructured_register_restart_axis_i1D
module procedure fms_io_unstructured_register_restart_axis_u
end interface fms_io_unstructured_register_restart_axis
interface fms_io_unstructured_register_restart_field
module procedure fms_io_unstructured_register_restart_field_r_0d
module procedure fms_io_unstructured_register_restart_field_r_1d
module procedure fms_io_unstructured_register_restart_field_r_2d
module procedure fms_io_unstructured_register_restart_field_r_3d
module procedure fms_io_unstructured_register_restart_field_r8_2d
module procedure fms_io_unstructured_register_restart_field_r8_3d
module procedure fms_io_unstructured_register_restart_field_i_0d
module procedure fms_io_unstructured_register_restart_field_i_1d
module procedure fms_io_unstructured_register_restart_field_i_2d
end interface fms_io_unstructured_register_restart_field
interface fms_io_unstructured_read
module procedure fms_io_unstructured_read_r_scalar
module procedure fms_io_unstructured_read_r_1D
module procedure fms_io_unstructured_read_r_2D
module procedure fms_io_unstructured_read_r_3D
module procedure fms_io_unstructured_read_i_scalar
module procedure fms_io_unstructured_read_i_1D
module procedure fms_io_unstructured_read_i_2D
end interface fms_io_unstructured_read
!----------
contains
!
!
! With the introduction of netCDF restart files, there is a need for a global
! switch to turn on/off netCDF restart options in all of the modules that deal with
! restart files. Here two more namelist variables (logical type) are introduced to fms_io
!
! fms_netcdf_override
! fms_netcdf_restart
!
! because default values of both flags are .true., the default behavior of the entire model is
! to use netCDF IO mode. To turn off netCDF restart, simply set fms_netcdf_restart to .false.
!
!
!
! call get_fms_io_mode(do_netcdf_restart)
!
!
! This the input argument that contains the individual module setting of restart IO mode.
! Upon return from this subroutine, this output argument contains the actual setting of restart IO mode
! the calling module will be using
!
!
subroutine get_restart_io_mode(do_netcdf_restart)
logical, intent(inout) :: do_netcdf_restart
if(fms_netcdf_override) do_netcdf_restart = fms_netcdf_restart
end subroutine get_restart_io_mode
!.....................................................................
!
!
! Initialize fms_io module
!
!
! call fms_io_init()
!
subroutine fms_io_init()
integer :: i, unit, io_status, logunit
integer, allocatable, dimension(:) :: pelist
real(8) :: doubledata = 0
real :: realarray(4)
character(len=256) :: grd_file, filename
logical :: is_mosaic_grid
character(len=4096) :: attvalue
if (module_is_initialized) return
call mpp_io_init()
read (input_nml_file, fms_io_nml, iostat=io_status)
if (io_status > 0) then
call mpp_error(FATAL,'=>fms_io_init: Error reading input.nml')
endif
# 662
! take namelist options if present
! determine packsize
pack_size = size(transfer(doubledata, realarray))
if( pack_size .NE. 1 .AND. pack_size .NE. 2) call mpp_error(FATAL,'=>fms_io_init: pack_size should be 1 or 2')
select case (threading_read)
case ('multi')
thread_r = MPP_MULTI
case ('single')
thread_r = MPP_SINGLE
case default
call mpp_error(FATAL,'fms_io_init: threading_read should be multi/single but you chose'//trim(threading_read))
end select
! take namelist options if present
select case(format)
case ('netcdf')
form=MPP_NETCDF
case default
call mpp_error(FATAL,'fms_io_init: only NetCDF format currently supported in fms_io')
end select
! Initially allocate files_write and files_read
allocate(files_write(max_files_w),files_read(max_files_r))
allocate(registered_file(max_files_w))
do i = 1, max_domains
array_domain(i) = NULL_DOMAIN2D
enddo
!---- initialize module domain2d pointer ----
nullify (Current_domain)
!This is set here instead of at the end of the routine to prevent the read_data call below from stopping the model
module_is_initialized = .TRUE.
! Record the version number in the log file
call write_version_number("FMS_IO_MOD", version)
!--- read INPUT/grid_spec.nc to decide the value of great_circle_algorithm
!--- great_circle_algorithm could be true only for mosaic grid.
great_circle_algorithm = .false.
grd_file = "INPUT/grid_spec.nc"
is_mosaic_grid = .FALSE.
if (file_exist(grd_file)) then
if(field_exist(grd_file, 'atm_mosaic_file')) then ! coupled grid
is_mosaic_grid = .TRUE.
else if(field_exist(grd_file, "gridfiles")) then
call read_data(grd_file, "gridfiles", filename, level=1)
grd_file = 'INPUT/'//trim(filename)
is_mosaic_grid = .TRUE.
endif
endif
if(is_mosaic_grid) then
if( get_global_att_value(grd_file, "great_circle_algorithm", attvalue) ) then
if(trim(attvalue) == "TRUE") then
great_circle_algorithm = .true.
else if(trim(attvalue) == "FALSE") then
great_circle_algorithm = .false.
else
call mpp_error(FATAL, "fms_io(fms_io_init: value of global attribute great_circle_algorithm in file"// &
trim(grd_file)//" should be TRUE of FALSE")
endif
endif
endif
if(great_circle_algorithm .AND. (mpp_pe() == mpp_root_pe()) ) then
call mpp_error(NOTE,"fms_io_mod: great_circle algorithm will be used in the model run")
endif
end subroutine fms_io_init
!
!
!
! This routine is called after ALL fields have been written to temporary files
! The result NETCDF files are created here.
!
!
! call fms_io_exit
!
subroutine fms_io_exit()
integer :: num_x_axes, num_y_axes, num_z_axes
integer :: unit
real, dimension(max_axis_size) :: axisdata
real :: tlev
integer, dimension(max_axes) :: id_x_axes, siz_x_axes
integer, dimension(max_axes) :: id_y_axes, siz_y_axes
integer, dimension(max_axes) :: id_z_axes, siz_z_axes
type(axistype), dimension(max_axes) :: x_axes, y_axes, z_axes
type(axistype) :: t_axes
type(var_type), pointer, save :: cur_var=>NULL()
integer :: i, j, k, kk
character(len=256) :: filename
character(len=10) :: axisname
logical :: domain_present
logical :: write_on_this_pe
type(domain2d), pointer :: io_domain =>NULL()
if( .NOT.module_is_initialized )return !make sure it's only called once per PE
do i=1,max_axis_size
axisdata(i) = i
enddo
! each field has an associated domain type (may be undefined).
! each file only needs to write unique axes (i.e. if 2 fields share an identical axis, then only write the axis once)
! unique axes are defined by the global size and domain decomposition (i.e. can support identical axis sizes with
! different domain decomposition)
do i = 1, num_files_w
filename = files_write(i)%name
!--- check if any field in this file present domain.
domain_present = .false.
do j = 1, files_write(i)%nvar
if (files_write(i)%var(j)%domain_present) then
domain_present = .true.
exit
end if
end do
!--- get the unique axes for all the fields.
num_x_axes = unique_axes(files_write(i), 1, id_x_axes, siz_x_axes, domain_x)
num_y_axes = unique_axes(files_write(i), 2, id_y_axes, siz_y_axes, domain_y)
num_z_axes = unique_axes(files_write(i), 3, id_z_axes, siz_z_axes )
if( domain_present ) then
call mpp_open(unit,trim(filename),action=MPP_OVERWR,form=form, &
is_root_pe=files_write(i)%is_root_pe, domain=array_domain(files_write(i)%var(j)%domain_idx))
else ! global data
call mpp_open(unit,trim(filename),action=MPP_OVERWR,form=form,threading=MPP_SINGLE,&
fileset=MPP_SINGLE, is_root_pe=files_write(i)%is_root_pe)
end if
write_on_this_pe = .false.
if(domain_present) then
io_domain => mpp_get_io_domain(array_domain(files_write(i)%var(j)%domain_idx))
if(associated(io_domain)) then
if(mpp_domain_is_tile_root_pe(io_domain)) write_on_this_pe = .true.
endif
endif
!--- always write out from root pe
if( files_write(i)%is_root_pe ) write_on_this_pe = .true.
do j = 1, num_x_axes
if (j < 10) then
write(axisname,'(a,i1)') 'xaxis_',j
else
write(axisname,'(a,i2)') 'xaxis_',j
endif
if(id_x_axes(j) > 0) then
call mpp_write_meta(unit,x_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_x_axes(j)),domain=domain_x(id_x_axes(j)),cartesian='X')
else
call mpp_write_meta(unit,x_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_x_axes(j)),cartesian='X')
endif
end do
do j = 1, num_y_axes
if (j < 10) then
write(axisname,'(a,i1)') 'yaxis_',j
else
write(axisname,'(a,i2)') 'yaxis_',j
endif
if(id_y_axes(j) > 0) then
call mpp_write_meta(unit,y_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_y_axes(j)),domain=domain_y(id_y_axes(j)),cartesian='Y')
else
call mpp_write_meta(unit,y_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_y_axes(j)),cartesian='Y')
endif
end do
do j = 1, num_z_axes
if (j < 10) then
write(axisname,'(a,i1)') 'zaxis_',j
else
write(axisname,'(a,i2)') 'zaxis_',j
endif
call mpp_write_meta(unit,z_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_z_axes(j)),cartesian='Z')
end do
! write time axis (comment out if no time axis)
call mpp_write_meta(unit,t_axes,&
'Time','time level','Time',cartesian='T')
! write metadata for fields
do j = 1, files_write(i)%nvar
cur_var => files_write(i)%var(j)
call mpp_write_meta(unit,cur_var%field, (/x_axes(cur_var%id_axes(1)), &
y_axes(cur_var%id_axes(2)), z_axes(cur_var%id_axes(3)), t_axes/), cur_var%name, &
'none',cur_var%name,pack=pack_size)
enddo
! write values for ndim of spatial axes
do j = 1, num_x_axes
call mpp_write(unit,x_axes(j))
enddo
do j = 1, num_y_axes
call mpp_write(unit,y_axes(j))
enddo
do j = 1, num_z_axes
call mpp_write(unit,z_axes(j))
enddo
! write data of each field
do k = 1, files_write(i)%max_ntime
do j = 1, files_write(i)%nvar
cur_var => files_write(i)%var(j)
tlev=k
! If some fields only have one time level, we do not need to write the second level, just keep
! the data missing.
! If some fields only have one time level, we just write out 0 to the other level
if(k > cur_var%siz(4)) then
cur_var%buffer(:,:,:,1) = 0.0
kk = 1
else
kk = k
end if
if(cur_var%domain_present) then
call mpp_write(unit, cur_var%field,array_domain(cur_var%domain_idx), cur_var%buffer(:,:,:,kk), tlev, &
default_data=cur_var%default_data)
else if (write_on_this_pe) then
call mpp_write(unit, cur_var%field, cur_var%buffer(:,:,:,kk), tlev)
end if
enddo ! end j loop
enddo ! end k loop
call mpp_close(unit)
enddo ! end i loop
!--- release the memory
do i = 1, num_files_w
do j = 1, files_write(i)%nvar
deallocate(files_write(i)%var(j)%buffer)
end do
end do
cur_var=>NULL()
module_is_initialized = .false.
num_files_w = 0
num_files_r = 0
end subroutine fms_io_exit
!.....................................................................
!
!
!
! This subroutine performs writing "fieldname" to file "filename". All values of "fieldname"
! will be written to a temporary file. The final NETCDF file will be created only at a later step
! when the user calls fms_io_exit. Therefore, make sure that fms_io_exit is called after all
! fields have been written by this subroutine.
!
!
! call write_data(filename, fieldname, data, domain)
!
!
! File name
!
!
! Field name
!
!
! array containing data of fieldname
!
!
! domain of fieldname
!
!=================================================================================
subroutine write_data_i3d_new(filename, fieldname, data, domain, &
no_domain, position, tile_count, data_default)
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:,:,:), intent(in) :: data
type(domain2d), intent(in), optional :: domain
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: position, tile_count, data_default
real :: default_data
default_data = TRANSFER(MPP_FILL_INT,default_data)
if(present(data_default)) default_data = real(data_default)
call write_data_3d_new(filename, fieldname, real(data), domain, &
no_domain, .false., position, tile_count, data_default=default_data)
end subroutine write_data_i3d_new
!.....................................................................
subroutine write_data_i2d_new(filename, fieldname, data, domain, &
no_domain, position, tile_count, data_default)
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:,:), intent(in) :: data
type(domain2d), intent(in), optional :: domain
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: position, tile_count, data_default
real :: default_data
default_data = TRANSFER(MPP_FILL_INT,default_data)
if(present(data_default)) default_data = real(data_default)
call write_data_2d_new(filename, fieldname, real(data), domain, &
no_domain, position, tile_count, data_default=default_data)
end subroutine write_data_i2d_new
!.....................................................................
subroutine write_data_i1d_new(filename, fieldname, data, domain, &
no_domain, tile_count, data_default)
type(domain2d), intent(in), optional :: domain
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:), intent(in) :: data
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: tile_count, data_default
real :: default_data
default_data = TRANSFER(MPP_FILL_INT,default_data)
if(present(data_default)) default_data = real(data_default)
call write_data_1d_new(filename, fieldname, real(data), domain, &
no_domain, tile_count, data_default=default_data)
end subroutine write_data_i1d_new
!.....................................................................
subroutine write_data_iscalar_new(filename, fieldname, data, domain, &
no_domain, tile_count, data_default)
type(domain2d), intent(in), optional :: domain
character(len=*), intent(in) :: filename, fieldname
integer, intent(in) :: data
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: tile_count, data_default
real :: default_data
default_data = TRANSFER(MPP_FILL_INT,default_data)
if(present(data_default)) default_data = real(data_default)
call write_data_scalar_new(filename, fieldname, real(data), domain, &
no_domain, tile_count, data_default=default_data)
end subroutine write_data_iscalar_new
!.....................................................................
subroutine write_data_3d_new(filename, fieldname, data, domain, no_domain, scalar_or_1d, &
position, tile_count, data_default)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:,:), intent(in) :: data
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
logical, optional, intent(in) :: scalar_or_1d
integer, optional, intent(in) :: position, tile_count
!--- local variables
real, allocatable :: tmp_buffer(:,:,:,:)
integer :: index_field ! position of the fieldname in the list of fields
integer :: index_file ! position of the filename in the list of files_write
logical :: append_pelist, is_no_domain, is_scalar_or_1d
character(len=256) :: fname, filename2,append_string
real :: default_data
integer :: length, i, domain_idx
integer :: ishift, jshift
integer :: gxsize, gysize
integer :: cxsize, cysize
integer :: dxsize, dysize
type(domain2d), pointer, save :: d_ptr =>NULL()
type(var_type), pointer, save :: cur_var =>NULL()
type(restart_file_type), pointer, save :: cur_file =>NULL()
! Initialize files to default values
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(write_data_3d_new): need to call fms_io_init')
if(PRESENT(data_default))then
default_data=data_default
else
default_data=MPP_FILL_DOUBLE
endif
if(present(tile_count) .AND. .not. present(domain)) call mpp_error(FATAL, &
'fms_io write_data: when tile_count is present, domain must be present')
is_scalar_or_1d = .false.
if(PRESENT(scalar_or_1d)) is_scalar_or_1d = scalar_or_1d
is_no_domain = .false.
if (PRESENT(no_domain)) THEN
is_no_domain = no_domain
end if
if(is_no_domain) then
if(PRESENT(domain)) &
call mpp_error(FATAL, 'fms_io(write_data_3d_new): no_domain cannot be .true. when optional argument domain is present.')
else if(PRESENT(domain))then
d_ptr => domain
else if (ASSOCIATED(Current_domain)) then
d_ptr => Current_domain
endif
!--- remove .nc from file name
length = len_trim(filename)
if(filename(length-2:length) == '.nc') then
filename2 = filename(1:length-3)
else
filename2 = filename(1:length)
end if
!Logical append_pelist decides whether to append the pelist_name to file name
append_pelist = .false.
!Append a string to the file name
append_string=''
!If the filename_appendix is set override the passed argument.
if(len_trim(filename_appendix) > 0) then
append_pelist = .true.
append_string = filename_appendix
endif
if(append_pelist) filename2 = trim(filename2)//'.'//trim(append_string)
!JWD: This is likely a temporary fix. Since fms_io needs to know tile_count,
!JWD: I just don't see how the physics can remain "tile neutral"
!z1l: one solution is add one more public interface called set_tile_count
call get_mosaic_tile_file(filename2, fname, is_no_domain, domain, tile_count)
! Check if filename has been open or not
index_file = -1
do i=1,num_files_w
if (trim(files_write(i)%name) == trim(fname)) then
index_file = i
cur_file => files_write(index_file)
exit
endif
enddo
if (index_file < 0) then
if(num_files_w == max_files_w) & ! need to have bigger max_files_w
call mpp_error(FATAL,'fms_io(write_data_3d_new): max_files_w exceeded, increase it via fms_io_nml')
! record the file name in array files_write
num_files_w=num_files_w + 1
index_file = num_files_w
cur_file => files_write(index_file)
cur_file%name = trim(fname)
cur_file%tile_count=1
if(present(tile_count)) cur_file%tile_count = tile_count
if(ASSOCIATED(d_ptr))then
cur_file%is_root_pe = mpp_domain_is_tile_root_pe(d_ptr)
else
cur_file%is_root_pe = mpp_pe() == mpp_root_pe()
endif
cur_file%max_ntime = 1
!-- allocate memory
allocate(cur_file%var(max_fields) )
cur_file%nvar = 0
do i = 1, max_fields
cur_file%var(i)%name = 'none'
cur_file%var(i)%domain_present = .false.
cur_file%var(i)%read_only = .false.
cur_file%var(i)%domain_idx = -1
cur_file%var(i)%is_dimvar = .false.
cur_file%var(i)%position = CENTER
cur_file%var(i)%siz(:) = 0
cur_file%var(i)%gsiz(:) = 0
cur_file%var(i)%id_axes(:) = -1
end do
endif
! check if the field is new or not and get position and dimension of the field
index_field = -1
do i = 1, cur_file%nvar
if(trim(cur_file%var(i)%name) == trim(fieldname)) then
index_field = i
exit
end if
end do
if(index_field > 0) then
cur_var => cur_file%var(index_field)
cur_var%siz(4) = cur_var%siz(4) + 1
if(cur_file%max_ntime < cur_var%siz(4) ) cur_file%max_ntime = cur_var%siz(4)
! the time level should be no larger than MAX_TIME_LEVEL_WRITE ( =20) for write_data.
if( cur_var%siz(4) > MAX_TIME_LEVEL_WRITE ) call mpp_error(FATAL, 'fms_io(write_data_3d_new): ' // &
'the time level of field '//trim(cur_var%name)//' in file '//trim(cur_file%name)// &
' is greater than MAX_TIME_LEVEL_WRITE(=20), increase MAX_TIME_LEVEL_WRITE or check your code')
else
cur_file%nvar = cur_file%nvar +1
if(cur_file%nvar>max_fields) then
write(error_msg,'(I3,"/",I3)') cur_file%nvar, max_fields
call mpp_error(FATAL,'fms_io(write_data_3d_new): max_fields exceeded, needs increasing, nvar/max_fields=' &
//trim(error_msg))
endif
index_field = cur_file%nvar
cur_var => cur_file%var(index_field)
cur_var%siz(1) = size(data,1)
cur_var%siz(2) = size(data,2)
cur_var%siz(3) = size(data,3)
cur_var%siz(4) = 1
cur_var%gsiz(3) = cur_var%siz(3)
cur_var%name = fieldname
cur_var%default_data = default_data
cur_var%ndim = 3
if(present(position)) cur_var%position = position
if(ASSOCIATED(d_ptr) .AND. .NOT. is_scalar_or_1d)then
cur_var%domain_present = .true.
domain_idx = lookup_domain(d_ptr)
if(domain_idx == -1) then
num_domains = num_domains + 1
if(num_domains > max_domains) call mpp_error(FATAL,'fms_io(write_data_3d_new), 1: max_domains exceeded,' &
//' needs increasing')
domain_idx = num_domains
array_domain(domain_idx) = d_ptr
call mpp_get_domain_components(array_domain(domain_idx), domain_x(domain_idx), domain_y(domain_idx), &
tile_count=tile_count)
endif
cur_var%domain_idx = domain_idx
call mpp_get_domain_shift ( array_domain(domain_idx), ishift, jshift, position)
call mpp_get_global_domain(array_domain(domain_idx), xsize=gxsize,ysize=gysize,tile_count=tile_count)
call mpp_get_compute_domain(array_domain(domain_idx), xsize = cxsize, ysize = cysize, tile_count=tile_count)
call mpp_get_data_domain (array_domain(domain_idx), xsize = dxsize, ysize = dysize, tile_count=tile_count)
if (ishift .NE. 0) then
cxsize = cxsize+ishift; dxsize = dxsize+ishift; gxsize = gxsize + ishift
end if
if (jshift .NE. 0) then
cysize = cysize+jshift; dysize = dysize+jshift; gysize = gysize + jshift
endif
if( (cur_var%siz(1) .NE. cxsize .AND. cur_var%siz(1) .NE. dxsize ) .OR. &
(cur_var%siz(2) .NE. cysize .AND. cur_var%siz(2) .NE. dysize ) ) then
call mpp_error(FATAL, 'fms_io(write_data_3d_new): data should be on either compute domain '//&
'or data domain when domain is present for field '//trim(fieldname)//' of file '//trim(filename) )
end if
cur_var%gsiz(1) = gxsize
cur_var%gsiz(2) = gysize
else
cur_var%domain_present=.false.
cur_var%gsiz(1) = size(data,1)
cur_var%gsiz(2) = size(data,2)
endif
end if
! copy the data to the buffer
! if the time level is greater than the size(cur_var%buffer,4),
! need to increase the buffer size
if(cur_var%siz(4) == 1) then
allocate(cur_var%buffer(cur_var%siz(1), cur_var%siz(2), cur_var%siz(3), cur_var%siz(4)) )
else
allocate(tmp_buffer(cur_var%siz(1), cur_var%siz(2), cur_var%siz(3), size(cur_var%buffer,4)) )
tmp_buffer = cur_var%buffer
deallocate(cur_var%buffer)
allocate(cur_var%buffer(cur_var%siz(1), cur_var%siz(2), cur_var%siz(3), cur_var%siz(4)) )
cur_var%buffer(:,:,:,1:size(tmp_buffer,4)) = tmp_buffer
deallocate(tmp_buffer)
endif
cur_var%buffer(:,:,:,cur_var%siz(4)) = data ! copy current data to buffer for future write out
d_ptr =>NULL()
cur_var =>NULL()
cur_file =>NULL()
end subroutine write_data_3d_new
!
!-------------------------------------------------------------------------------
!
! This routine will register an integer restart file axis
!
!-------------------------------------------------------------------------------
subroutine register_restart_axis_r1d(fileObj,filename,fieldname,data,cartesian,units,longname,sense,min,calendar)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, intent(in), target :: data(:)
character(len=*), intent(in) :: cartesian
character(len=*), optional, intent(in) :: units, longname
integer, optional, intent(in) :: sense
real, optional, intent(in) :: min !valid min for real axis data
character(len=*), optional, intent(in) :: calendar
integer :: idx
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_axis_r1d): need to call fms_io_init')
select case(trim(cartesian))
case('X')
idx = XIDX
case('Y')
idx = YIDX
case('Z')
idx = ZIDX
case('T')
idx = TIDX
case('CC')
idx = CCIDX
case default
call mpp_error(FATAL,'fms_io(register_restart_axis_r1d): Axis must be one of X,Y,Z,T or CC ' // &
'but has value '//trim(cartesian))
end select
if(.not. ALLOCATED(fileObj%axes)) allocate(fileObj%axes(NIDX))
if(ASSOCIATED(fileObj%axes(idx)%data)) &
call mpp_error(FATAL,'fms_io(register_restart_axis_r1d): '//trim(cartesian)//' axis has already been defined')
!Why do we do this?
! fileObj%name = filename
fileObj%axes(idx)%name = fieldname
fileObj%axes(idx)%data =>data
fileObj%axes(idx)%cartesian = cartesian
fileObj%axes(idx)%dimlen = -1 ! This is not a compressed axis
if(PRESENT(units)) fileObj%axes(idx)%units = units
if(PRESENT(longname)) fileObj%axes(idx)%longname = longname
if(PRESENT(min)) fileObj%axes(idx)%min = min
if(idx == TIDX) then
if(PRESENT(calendar)) fileObj%axes(idx)%calendar = trim(calendar)
endif
if(PRESENT(sense)) then
if(idx /= ZIDX) call mpp_error(FATAL,'fms_io(register_restart_axis_r1d): Only the Z axis may define sense; ' // &
'Axis = '//trim(cartesian))
if(abs(sense) /= 1) call mpp_error(FATAL,'fms_io(register_restart_axis_r1d): Value of sense must be +/- 1')
fileObj%axes(idx)%sense = sense
endif
end subroutine register_restart_axis_r1d
!-------------------------------------------------------------------------------
!
! This routine will register the compressed index restart file axis
!
!-------------------------------------------------------------------------------
subroutine register_restart_axis_i1d(fileObj,filename,fieldname,data,compressed, &
compressed_axis,dimlen,dimlen_name,dimlen_lname,units,longname,imin)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer, intent(in) :: data(:)
character(len=*), intent(in) :: compressed
character(len=*), intent(in) :: compressed_axis !< which compressed axis (C or H)
integer, intent(in) :: dimlen
character(len=*), optional, intent(in) :: dimlen_name, dimlen_lname !< dimlen axis name and longname
character(len=*), optional, intent(in) :: units, longname
integer, optional, intent(in) :: imin !valid min for integer axis data
integer :: ssize,rsize,npes
integer :: idx
integer, allocatable :: pelist(:)
type(domain2d), pointer :: io_domain=>NULL()
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_axis_i1d): need to call fms_io_init')
select case(trim(compressed_axis))
case('C')
idx = CIDX
case('H')
idx = HIDX
case default
call mpp_error(FATAL,'fms_io(register_restart_axis_r1d): Axis must be one of C or H ' // &
'but has value '//trim(compressed_axis))
end select
if(.not. ALLOCATED(fileObj%axes)) allocate(fileObj%axes(NIDX))
if(ALLOCATED(fileObj%axes(idx)%idx)) &
call mpp_error(FATAL,'fms_io(register_restart_axis_i1d): Compressed axis ' //&
trim(compressed_axis) // ' has already been defined')
!Why do we do this?
! fileObj%name = filename
fileObj%is_compressed = .true.
fileObj%unlimited_axis = .false.
fileObj%axes(idx)%name = fieldname
if(ASSOCIATED(current_domain)) then
fileObj%axes(idx)%domain =>current_domain
io_domain =>mpp_get_io_domain(current_domain)
if(.not. ASSOCIATED(io_domain)) &
call mpp_error(FATAL,'fms_io(register_restart_axis_i1d): The io domain must be defined')
npes = mpp_get_domain_npes(io_domain)
allocate(fileObj%axes(idx)%nelems(npes)); fileObj%axes(idx)%nelems = 0
allocate(pelist(npes))
call mpp_get_pelist(io_domain,pelist)
ssize = size(data)
call mpp_gather((/ssize/),fileObj%axes(idx)%nelems,pelist)
rsize = sum(fileObj%axes(idx)%nelems)
allocate( fileObj%axes(idx)%idx(rsize) )
! Note that the gatherV implied here is asymmetric; only root needs to know the vector of recv sizes
call mpp_gather(data,ssize,fileObj%axes(idx)%idx,fileObj%axes(idx)%nelems,pelist)
deallocate(pelist); io_domain=>NULL()
else
call mpp_error(FATAL,'fms_io(register_restart_axis_i1d): The domain must be defined through set_domain')
endif
fileObj%axes(idx)%compressed = compressed
fileObj%axes(idx)%dimlen = dimlen
if(PRESENT(dimlen_name)) fileObj%axes(idx)%dimlen_name = dimlen_name
if(PRESENT(dimlen_lname)) fileObj%axes(idx)%dimlen_lname = dimlen_lname
if(PRESENT(units)) fileObj%axes(idx)%units = units
if(PRESENT(longname)) fileObj%axes(idx)%longname = longname
if(PRESENT(imin)) fileObj%axes(idx)%imin = imin
end subroutine register_restart_axis_i1d
!-------------------------------------------------------------------------------
subroutine register_restart_axis_unlimited(fileObj,filename,fieldname,nelem,units,longname)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer :: nelem ! Number of elements on rank
character(len=*), optional, intent(in) :: units, longname
integer :: idx,npes
integer, allocatable :: pelist(:)
type(domain2d), pointer :: io_domain=>NULL()
if(.not.module_is_initialized) &
call mpp_error(FATAL,'fms_io(register_restart_axis_unlimited): need to call fms_io_init')
idx = UIDX
if(.not. ALLOCATED(fileObj%axes)) allocate(fileObj%axes(NIDX))
if(ALLOCATED(fileObj%axes(idx)%idx)) &
call mpp_error(FATAL,'fms_io(register_restart_axis_unlimited): Unlimited axis has already been defined')
!Why do we do this?
! fileObj%name = filename
fileObj%is_compressed = .false.
fileObj%unlimited_axis = .true.
fileObj%axes(idx)%name = fieldname
if(ASSOCIATED(current_domain)) then
fileObj%axes(idx)%domain =>current_domain
io_domain =>mpp_get_io_domain(current_domain)
if(.not. ASSOCIATED(io_domain)) &
call mpp_error(FATAL,'fms_io(register_restart_axis_i1d): The io domain must be defined')
npes = mpp_get_domain_npes(io_domain)
allocate(fileObj%axes(idx)%nelems(npes)); fileObj%axes(idx)%nelems = 0
allocate(pelist(npes))
call mpp_get_pelist(io_domain,pelist)
call mpp_gather((/nelem/),fileObj%axes(idx)%nelems,pelist)
deallocate(pelist); io_domain=>NULL()
else
call mpp_error(FATAL,'fms_io(register_restart_axis_unlimited): The domain must be defined through set_domain')
endif
if(PRESENT(units)) fileObj%axes(idx)%units = units
if(PRESENT(longname)) fileObj%axes(idx)%longname = longname
end subroutine register_restart_axis_unlimited
!
! This routine is the destructor for the file object
!
!-------------------------------------------------------------------------------
subroutine free_restart_type(fileObj)
type(restart_file_type), intent(inout) :: fileObj
type(meta_type),pointer :: this
type(meta_type),pointer :: this_p
integer :: id, n, j, k
!--- remove file name from registered_file
id = 0
do n = 1, num_registered_files
if( trim(fileObj%name) == trim(registered_file(n)) ) then
id = n
exit
endif
enddo
if( id < 0) &
call mpp_error(FATAL, 'fms_io(free_restart_type): fileObj%name is not found in registered_files')
do n = id+1, num_registered_files
registered_file(n-1) = trim(registered_file(n))
enddo
registered_file(num_registered_files) = ''
num_registered_files = num_registered_files - 1
fileObj%register_id = 0
fileObj%unit = -1
fileObj%name = ''
fileObj%nvar = -1
fileObj%natt = -1
fileObj%max_ntime = -1
fileObj%tile_count = -1
if(ALLOCATED(fileObj%axes)) deallocate(fileObj%axes)
! deallocate all the data that restart owns
do k = 1,size(fileObj%var)
if (fileObj%var(k)%owns_data) then
do j = 1,size(fileObj%p0dr,1)
if(ASSOCIATED(fileObj%p0dr(j,k)%p)) deallocate(fileObj%p0dr(j,k)%p)
if(ASSOCIATED(fileObj%p1dr(j,k)%p)) deallocate(fileObj%p1dr(j,k)%p)
if(ASSOCIATED(fileObj%p2dr(j,k)%p)) deallocate(fileObj%p2dr(j,k)%p)
if(ASSOCIATED(fileObj%p3dr(j,k)%p)) deallocate(fileObj%p3dr(j,k)%p)
if(ASSOCIATED(fileObj%p2dr8(j,k)%p)) deallocate(fileObj%p2dr8(j,k)%p)
if(ASSOCIATED(fileObj%p3dr8(j,k)%p)) deallocate(fileObj%p3dr8(j,k)%p)
if(ASSOCIATED(fileObj%p0di(j,k)%p)) deallocate(fileObj%p0di(j,k)%p)
if(ASSOCIATED(fileObj%p1di(j,k)%p)) deallocate(fileObj%p1di(j,k)%p)
if(ASSOCIATED(fileObj%p2di(j,k)%p)) deallocate(fileObj%p2di(j,k)%p)
if(ASSOCIATED(fileObj%p3di(j,k)%p)) deallocate(fileObj%p3di(j,k)%p)
enddo
endif
enddo
if(ASSOCIATED(fileObj%var)) deallocate(fileObj%var)
if(ASSOCIATED(fileObj%p0dr)) deallocate(fileObj%p0dr)
if(ASSOCIATED(fileObj%p1dr)) deallocate(fileObj%p1dr)
if(ASSOCIATED(fileObj%p2dr)) deallocate(fileObj%p2dr)
if(ASSOCIATED(fileObj%p3dr)) deallocate(fileObj%p3dr)
if(ASSOCIATED(fileObj%p2dr8)) deallocate(fileObj%p2dr8)
if(ASSOCIATED(fileObj%p3dr8)) deallocate(fileObj%p3dr8)
if(ASSOCIATED(fileObj%p0di)) deallocate(fileObj%p0di)
if(ASSOCIATED(fileObj%p1di)) deallocate(fileObj%p1di)
if(ASSOCIATED(fileObj%p2di)) deallocate(fileObj%p2di)
if(ASSOCIATED(fileObj%p3di)) deallocate(fileObj%p3di)
if(ASSOCIATED(fileObj%first)) then
this =>fileObj%first
do while(associated(this%next))
this =>this%next ! Find the last element
enddo
do while(associated(this)) ! Deallocate from the last element to the first
this_p =>this%prev
!!$ Gfortran on gaea does not yet support deferred length character strings
!!$ deallocate(this%name)
this%name='' ! Remove this line when Gfortran supports deferred length character strings
if(allocated(this%rval)) deallocate(this%rval)
if(allocated(this%ival)) deallocate(this%ival)
!!$ Gfortran on gaea does not yet support deferred length character strings
!!$ if(allocated(this%cval)) deallocate(this%cval)
this%cval='' ! Remove this line when Gfortran supports deferred length character strings
deallocate(this)
this =>this_p
enddo
fileObj%first =>NULL()
endif
end subroutine free_restart_type
!-------------------------------------------------------------------------------
!
! The routine sets up a list of global metadata expressions for save_restart
!
!-------------------------------------------------------------------------------
subroutine set_meta_global(fileObj, name, rval, ival, cval)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: name
real, intent(in), optional :: rval(:)
integer, intent(in), optional :: ival(:)
character(len=*), intent(in), optional :: cval
type(meta_type),pointer :: this
type(meta_type),pointer :: this_n
this =>fileObj%first
if(associated(this))then
do while(associated(this%next))
this =>this%next
enddo
allocate(this_n); this%next =>this_n; this_n%prev =>this; this =>this_n
else
allocate(this)
fileObj%first =>this
endif
! Per mpp_write_meta_global, only one type of data can be associated with the metadata
!!$ Gfortran on gaea does not yet support deferred length character strings
!!$ allocate(character(len(name)) :: this%name); this%name = name
this%name = name ! Remove this line when Gfortran supports deferred length character stings
if(present(rval))then
allocate(this%rval(size(rval))); this%rval=rval
elseif(present(ival))then
allocate(this%ival(size(ival))); this%ival=ival
elseif(present(cval))then
!!$ Gfortran on gaea does not yet support deferred length character strings
!!$ allocate(character(len(cval)) :: this%cval); this%cval = cval
this%cval=cval ! Remove this line when Gfortran supports deferred length character stings
endif
end subroutine set_meta_global
!-------------------------------------------------------------------------------
!
! The routine writes the global metadata
!
!-------------------------------------------------------------------------------
subroutine write_meta_global(unit,fileObj)
integer, intent(in) :: unit
type(restart_file_type), intent(in) :: fileObj
type(meta_type), pointer :: this
this =>fileObj%first
do while(associated(this))
if(allocated(this%rval))then
call mpp_write_meta(unit,this%name,rval=this%rval)
elseif(allocated(this%ival))then
call mpp_write_meta(unit,this%name,ival=this%ival)
!!$ Gfortran on gaea does not yet support deferred length character strings
!!$ elseif(allocated(this%cval))then
elseif(len_trim(this%cval).GT.0)then ! Remove this line when Gfortran supports deferred length character stings
call mpp_write_meta(unit,this%name,cval=this%cval)
else
call mpp_write_meta(unit,this%name)
endif
this =>this%next
enddo
end subroutine write_meta_global
!-------------------------------------------------------------------------------
!
! The routine will register a scalar real restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r0d(fileObj, filename, fieldname, data, domain, mandatory, &
no_domain, position, tile_count, data_default, &
longname, units, read_only, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
logical, optional, intent(in) :: no_domain
real, optional, intent(in) :: data_default
logical, optional, intent(in) :: mandatory
integer, optional, intent(in) :: position, tile_count
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: restart_owns_data
integer :: index_field
integer :: register_restart_field_r0d
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_r0d): need to call fms_io_init')
call setup_one_field(fileObj, filename, fieldname, (/1, 1, 1, 1/), index_field, domain, mandatory, &
no_domain, scalar_or_1d=.true., position=position, tile_count=tile_count, &
data_default=data_default, longname=longname, units=units, read_only=read_only,&
owns_data=restart_owns_data)
fileObj%p0dr(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 0
register_restart_field_r0d = index_field
end function register_restart_field_r0d
!-------------------------------------------------------------------------------
!
! The routine will register a 1-D real restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r1d(fileObj, filename, fieldname, data, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, &
compressed_axis, read_only, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, dimension(:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
logical, optional, intent(in) :: no_domain
real, optional, intent(in) :: data_default
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units, compressed_axis
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: restart_owns_data
integer :: index_field
integer :: register_restart_field_r1d
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_r1d): need to call fms_io_init')
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), 1, 1, 1/), index_field, domain, mandatory, &
no_domain, scalar_or_1d=.true., position=position, tile_count=tile_count, &
data_default=data_default, longname=longname, units=units, compressed_axis=compressed_axis, &
read_only=read_only, owns_data=restart_owns_data)
fileObj%p1dr(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 1
register_restart_field_r1d = index_field
end function register_restart_field_r1d
!-------------------------------------------------------------------------------
!
! The routine will register a 2-D real restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r2d(fileObj, filename, fieldname, data, domain, mandatory, no_domain, &
compressed, position, tile_count, data_default, longname, units, &
compressed_axis, read_only, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
logical, optional, intent(in) :: compressed
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units, compressed_axis
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: restart_owns_data
logical :: is_compressed
integer :: index_field
integer :: register_restart_field_r2d
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_r2d): need to call fms_io_init')
is_compressed = .false.
if(present(compressed)) is_compressed=compressed
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), 1, 1/), &
index_field, domain, mandatory, no_domain, is_compressed, &
position, tile_count, data_default, longname, units, compressed_axis, &
read_only=read_only, owns_data=restart_owns_data)
fileObj%p2dr(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 2
register_restart_field_r2d = index_field
end function register_restart_field_r2d
!-------------------------------------------------------------------------------
!
! The routine will register a 3-D real restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r3d(fileObj, filename, fieldname, data, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only, &
compressed, compressed_axis, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:,:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units, compressed_axis
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: compressed
logical, optional, intent(in) :: restart_owns_data
logical :: is_compressed
integer :: index_field
integer :: register_restart_field_r3d
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_r3d): need to call fms_io_init')
if(present(compressed)) then
is_compressed=compressed
else
is_compressed = .false.
endif
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), size(data,3), 1/), &
index_field, domain, mandatory, no_domain, is_compressed, &
position, tile_count, data_default, longname, units, compressed_axis, &
read_only=read_only, owns_data=restart_owns_data)
fileObj%p3dr(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 3
register_restart_field_r3d = index_field
end function register_restart_field_r3d
!-------------------------------------------------------------------------------
!
! The routine will register a 2-D double_kind restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r2d8(fileObj, filename, fieldname, data, domain, mandatory, no_domain, &
compressed, position, tile_count, data_default, longname, units, &
compressed_axis, read_only, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real(8), dimension(:,:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
real(8), optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
logical, optional, intent(in) :: compressed
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units, compressed_axis
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: restart_owns_data
logical :: is_compressed
integer :: index_field
integer :: register_restart_field_r2d8
real(4) :: data_default_r4
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_r2d8): need to call fms_io_init')
is_compressed = .false.
if(present(compressed)) is_compressed=compressed
if(present(data_default)) then
data_default_r4=REAL(data_default, 4)
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), 1, 1/), &
index_field, domain, mandatory, no_domain, is_compressed, &
position, tile_count, data_default_r4, longname, units, compressed_axis, &
read_only=read_only, owns_data=restart_owns_data)
else
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), 1, 1/), &
index_field, domain, mandatory, no_domain, is_compressed, &
position, tile_count, longname=longname, units=units, compressed_axis=compressed_axis, &
read_only=read_only, owns_data=restart_owns_data)
endif
fileObj%p2dr8(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 2
register_restart_field_r2d8 = index_field
end function register_restart_field_r2d8
!-------------------------------------------------------------------------------
!
! The routine will register a 3-D double_kind restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r3d8(fileObj, filename, fieldname, data, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only, &
compressed, compressed_axis, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real(8), dimension(:,:,:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
real(8), optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units, compressed_axis
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: compressed
logical, optional, intent(in) :: restart_owns_data
logical :: is_compressed
integer :: index_field
integer :: register_restart_field_r3d8
real(4) :: data_default_r4
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_r3d8): need to call fms_io_init')
is_compressed = .false.
if(present(compressed)) is_compressed=compressed
if(present(data_default)) then
data_default_r4=REAL(data_default, 4)
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), size(data,3), 1/), &
index_field, domain, mandatory, no_domain, is_compressed, &
position, tile_count, data_default_r4, longname, units, compressed_axis, &
read_only=read_only, owns_data=restart_owns_data)
else
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), size(data,3), 1/), &
index_field, domain, mandatory, no_domain, is_compressed, &
position, tile_count, longname=longname, units=units, compressed_axis=compressed_axis, &
read_only=read_only, owns_data=restart_owns_data)
endif
fileObj%p3dr8(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 3
register_restart_field_r3d8 = index_field
end function register_restart_field_r3d8
!-------------------------------------------------------------------------------
!
! The routine will register a 4-D real restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r4d(fileObj, filename, fieldname, data, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, &
read_only, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:,:,:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: restart_owns_data
integer :: index_field
integer :: register_restart_field_r4d
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_r4d): need to call fms_io_init')
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), size(data,3), 1, size(data,4)/), &
index_field, domain, mandatory, no_domain, .false., &
position, tile_count, data_default, longname, units, &
read_only=read_only, owns_data=restart_owns_data)
fileObj%p4dr(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 4
register_restart_field_r4d = index_field
end function register_restart_field_r4d
!-------------------------------------------------------------------------------
!
! The routine will register a scalar integer restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_i0d(fileObj, filename, fieldname, data, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, &
read_only, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer, intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
integer, optional, intent(in) :: data_default
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
logical, optional, intent(in) :: no_domain
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: restart_owns_data
integer :: index_field
integer :: register_restart_field_i0d
real :: data_default_r
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_i0d): need to call fms_io_init')
if (KIND(data_default)/=KIND(data)) call mpp_error(FATAL,'fms_io(register_restart_field_i0d): data_default and data different KIND()')
data_default_r = TRANSFER(MPP_FILL_INT,data_default_r)
if (present(data_default)) data_default_r = TRANSFER(data_default ,data_default_r)
call setup_one_field(fileObj, filename, fieldname, (/1, 1, 1, 1/), index_field, domain, &
mandatory, no_domain=no_domain, scalar_or_1d=.true., position=position, tile_count=tile_count, &
data_default=data_default_r, longname=longname, units=units, &
read_only=read_only, owns_data=restart_owns_data)
fileObj%p0di(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 0
register_restart_field_i0d = index_field
end function register_restart_field_i0d
!-------------------------------------------------------------------------------
!
! The routine will register a 1-D integer restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_i1d(fileObj, filename, fieldname, data, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, &
compressed_axis, read_only, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
integer, optional, intent(in) :: data_default
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
logical, optional, intent(in) :: no_domain
character(len=*), optional, intent(in) :: longname, units, compressed_axis
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: restart_owns_data
integer :: index_field
integer :: register_restart_field_i1d
real :: data_default_r
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_i1d): need to call fms_io_init')
if (KIND(data_default)/=KIND(data)) call mpp_error(FATAL,'fms_io(register_restart_field_i1d): data_default and data different KIND()')
data_default_r = TRANSFER(MPP_FILL_INT,data_default_r)
if (present(data_default)) data_default_r = TRANSFER(data_default ,data_default_r)
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), 1, 1, 1/), index_field, domain, &
mandatory, no_domain=no_domain, scalar_or_1d=.true., position=position, tile_count=tile_count, &
data_default=data_default_r, longname=longname, units=units, compressed_axis=compressed_axis, &
read_only=read_only, owns_data=restart_owns_data)
fileObj%p1di(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 1
register_restart_field_i1d = index_field
end function register_restart_field_i1d
!-------------------------------------------------------------------------------
!
! The routine will register a 2-D real restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_i2d(fileObj, filename, fieldname, data, domain, mandatory, no_domain, &
compressed, position, tile_count, data_default, longname, units, &
compressed_axis, read_only, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:,:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
integer, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
logical, optional, intent(in) :: compressed
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units, compressed_axis
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: restart_owns_data
logical :: is_compressed
integer :: index_field
integer :: register_restart_field_i2d
real :: data_default_r
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_i2d): need to call fms_io_init')
is_compressed = .false.
if(present(compressed)) is_compressed=compressed
if (KIND(data_default)/=KIND(data)) call mpp_error(FATAL,'fms_io(register_restart_field_i2d): data_default and data different KIND()')
data_default_r = TRANSFER(MPP_FILL_INT,data_default_r)
if (present(data_default)) data_default_r = TRANSFER(data_default ,data_default_r)
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), 1, 1/), &
index_field, domain, mandatory, no_domain, is_compressed, &
position, tile_count, data_default_r, longname, units, compressed_axis, &
read_only=read_only, owns_data=restart_owns_data)
fileObj%p2di(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 2
register_restart_field_i2d = index_field
end function register_restart_field_i2d
!-------------------------------------------------------------------------------
!
! The routine will register a 3-D real restart file field with one time level
!
!-------------------------------------------------------------------------------
function register_restart_field_i3d(fileObj, filename, fieldname, data, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, &
read_only, restart_owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:,:,:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
integer, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: restart_owns_data
integer :: index_field
integer :: register_restart_field_i3d
real :: data_default_r
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_field_i3d): need to call fms_io_init')
if (KIND(data_default)/=KIND(data)) call mpp_error(FATAL,'fms_io(register_restart_field_i3d): data_default and data different KIND()')
data_default_r = TRANSFER(MPP_FILL_INT,data_default_r)
if (present(data_default)) data_default_r = TRANSFER(data_default ,data_default_r)
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), size(data,3), 1/), &
index_field, domain, mandatory, no_domain, .false., &
position, tile_count, data_default_r, longname, units, &
read_only=read_only, owns_data=restart_owns_data)
fileObj%p3di(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 3
register_restart_field_i3d = index_field
end function register_restart_field_i3d
!-------------------------------------------------------------------------------
!
! The routine will register a scalar real restart file field with two time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r0d_2level(fileObj, filename, fieldname, data1, data2, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, intent(in), target :: data1, data2
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
logical, optional, intent(in) :: no_domain
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer :: index_field
integer :: register_restart_field_r0d_2level
if(.not.module_is_initialized) call mpp_error(FATAL, &
'fms_io(register_restart_field_r0d_2level): need to call fms_io_init')
call setup_one_field(fileObj, filename, fieldname, (/1, 1, 1, 2/), index_field, domain, &
mandatory, no_domain=no_domain, scalar_or_1d=.true., position=position, tile_count=tile_count, &
data_default=data_default, longname=longname, units=units, read_only=read_only)
fileObj%p0dr(1, index_field)%p => data1
fileObj%p0dr(2, index_field)%p => data2
fileObj%var(index_field)%ndim = 0
register_restart_field_r0d_2level = index_field
end function register_restart_field_r0d_2level
!-------------------------------------------------------------------------------
!
! The routine will register a 1-D real restart file field with two time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r1d_2level(fileObj, filename, fieldname, data1, data2, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, dimension(:), intent(in), target :: data1, data2
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
logical, optional, intent(in) :: no_domain
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer :: index_field
integer :: register_restart_field_r1d_2level
if(.not.module_is_initialized) call mpp_error(FATAL, &
'fms_io(register_restart_field_r1d_2level): need to call fms_io_init')
call setup_one_field(fileObj, filename, fieldname, (/size(data1,1), 1, 1, 2/), index_field, domain, &
mandatory, no_domain=no_domain, scalar_or_1d=.true., position=position, tile_count=tile_count, &
data_default=data_default, longname=longname, units=units, read_only=read_only)
fileObj%p1dr(1, index_field)%p => data1
fileObj%p1dr(2, index_field)%p => data2
fileObj%var(index_field)%ndim = 1
register_restart_field_r1d_2level = index_field
return
end function register_restart_field_r1d_2level
!-------------------------------------------------------------------------------
!
! The routine will register a 3-D real restart file field with two time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r2d_2level(fileObj, filename, fieldname, data1, data2, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:), intent(in), target :: data1, data2
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer :: index_field
integer :: register_restart_field_r2d_2level
if(.not.module_is_initialized) call mpp_error(FATAL, &
'fms_io(register_restart_field_r2d_2level): need to call fms_io_init')
call setup_one_field(fileObj, filename, fieldname, (/size(data1,1), size(data1,2), 1, 2/), &
index_field, domain, mandatory, no_domain, .false., &
position, tile_count, data_default, longname, units, read_only=read_only)
fileObj%p2dr(1, index_field)%p => data1
fileObj%p2dr(2, index_field)%p => data2
fileObj%var(index_field)%ndim = 2
register_restart_field_r2d_2level = index_field
return
end function register_restart_field_r2d_2level
!-------------------------------------------------------------------------------
!
! The routine will register a 3-D real restart file field with two time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r3d_2level(fileObj, filename, fieldname, data1, data2, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:,:), intent(in), target :: data1, data2
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer :: index_field
integer :: register_restart_field_r3d_2level
if(.not.module_is_initialized) call mpp_error(FATAL, &
'fms_io(register_restart_field_r3d_2level): need to call fms_io_init')
call setup_one_field(fileObj, filename, fieldname, (/size(data1,1), size(data1,2), size(data1,3), 2/), &
index_field, domain, mandatory, no_domain, .false., &
position, tile_count, data_default, longname, units, read_only=read_only)
fileObj%p3dr(1, index_field)%p => data1
fileObj%p3dr(2, index_field)%p => data2
fileObj%var(index_field)%ndim = 3
register_restart_field_r3d_2level = index_field
return
end function register_restart_field_r3d_2level
!-------------------------------------------------------------------------------
!
! The routine will register a 2-D double_kind restart file field with two time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r2d8_2level(fileObj, filename, fieldname, data1, data2, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real(8), dimension(:,:), intent(in), target :: data1, data2
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer :: index_field
integer :: register_restart_field_r2d8_2level
if(.not.module_is_initialized) call mpp_error(FATAL, &
'fms_io(register_restart_field_r2d_2level): need to call fms_io_init')
call setup_one_field(fileObj, filename, fieldname, (/size(data1,1), size(data1,2), 1, 2/), &
index_field, domain, mandatory, no_domain, .false., &
position, tile_count, data_default, longname, units, read_only=read_only)
fileObj%p2dr8(1, index_field)%p => data1
fileObj%p2dr8(2, index_field)%p => data2
fileObj%var(index_field)%ndim = 2
register_restart_field_r2d8_2level = index_field
return
end function register_restart_field_r2d8_2level
!-------------------------------------------------------------------------------
!
! The routine will register a 3-D double_kind restart file field with two time level
!
!-------------------------------------------------------------------------------
function register_restart_field_r3d8_2level(fileObj, filename, fieldname, data1, data2, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real(8), dimension(:,:,:), intent(in), target :: data1, data2
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer :: index_field
integer :: register_restart_field_r3d8_2level
if(.not.module_is_initialized) call mpp_error(FATAL, &
'fms_io(register_restart_field_r3d_2level): need to call fms_io_init')
call setup_one_field(fileObj, filename, fieldname, (/size(data1,1), size(data1,2), size(data1,3), 2/), &
index_field, domain, mandatory, no_domain, .false., &
position, tile_count, data_default, longname, units, read_only=read_only)
fileObj%p3dr8(1, index_field)%p => data1
fileObj%p3dr8(2, index_field)%p => data2
fileObj%var(index_field)%ndim = 3
register_restart_field_r3d8_2level = index_field
return
end function register_restart_field_r3d8_2level
!-------------------------------------------------------------------------------
!
! The routine will register a scalar integer restart file field with two time level
!
!-------------------------------------------------------------------------------
function register_restart_field_i0d_2level(fileObj, filename, fieldname, data1, data2, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer, intent(in), target :: data1, data2
type(domain2d), optional, intent(in), target :: domain
integer, optional, intent(in) :: data_default
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
logical, optional, intent(in) :: no_domain
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer :: index_field
integer :: register_restart_field_i0d_2level
real :: data_default_r
if(.not.module_is_initialized) call mpp_error(FATAL, &
'fms_io(register_restart_field_i0d_2level): need to call fms_io_init')
if (KIND(data_default)/=KIND(data1)) call mpp_error(FATAL,'fms_io(register_restart_field_i0d_2level): data_default and data1 different KIND()')
if (KIND(data_default)/=KIND(data2)) call mpp_error(FATAL,'fms_io(register_restart_field_i0d_2level): data_default and data2 different KIND()')
data_default_r = TRANSFER(MPP_FILL_INT,data_default_r)
if (present(data_default)) data_default_r = TRANSFER(data_default ,data_default_r)
call setup_one_field(fileObj, filename, fieldname, (/1, 1, 1, 2/), index_field, domain, &
mandatory, no_domain=no_domain, scalar_or_1d=.true., position=position, tile_count=tile_count, &
data_default=data_default_r, longname=longname, units=units, read_only=read_only)
fileObj%p0di(1, index_field)%p => data1
fileObj%p0di(2, index_field)%p => data2
fileObj%var(index_field)%ndim = 0
register_restart_field_i0d_2level = index_field
return
end function register_restart_field_i0d_2level
!-------------------------------------------------------------------------------
!
! The routine will register a 1-D integer restart file field with two time level
!
!-------------------------------------------------------------------------------
function register_restart_field_i1d_2level(fileObj, filename, fieldname, data1, data2, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:), intent(in), target :: data1, data2
type(domain2d), optional, intent(in), target :: domain
integer, optional, intent(in) :: data_default
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
logical, optional, intent(in) :: no_domain
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer :: index_field
integer :: register_restart_field_i1d_2level
real :: data_default_r
if(.not.module_is_initialized) call mpp_error(FATAL, &
'fms_io(register_restart_field_i1d_2level): need to call fms_io_init')
if (KIND(data_default)/=KIND(data1)) call mpp_error(FATAL,'fms_io(register_restart_field_i1d_2level): data_default and data1 different KIND()')
if (KIND(data_default)/=KIND(data2)) call mpp_error(FATAL,'fms_io(register_restart_field_i1d_2level): data_default and data2 different KIND()')
data_default_r = TRANSFER(MPP_FILL_INT,data_default_r)
if (present(data_default)) data_default_r = TRANSFER(data_default ,data_default_r)
call setup_one_field(fileObj, filename, fieldname, (/size(data1,1), 1, 1, 2/), index_field, domain, &
mandatory, no_domain=no_domain, scalar_or_1d=.true., position=position, tile_count=tile_count, &
data_default=data_default_r, longname=longname, units=units, read_only=read_only)
fileObj%p1di(1, index_field)%p => data1
fileObj%p1di(2, index_field)%p => data2
fileObj%var(index_field)%ndim = 1
register_restart_field_i1d_2level = index_field
return
end function register_restart_field_i1d_2level
!-------------------------------------------------------------------------------
!
! The routine will register a 2-D integer restart file field with two time level
!
!-------------------------------------------------------------------------------
function register_restart_field_i2d_2level(fileObj, filename, fieldname, data1, data2, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:,:), intent(in), target :: data1, data2
type(domain2d), optional, intent(in), target :: domain
integer, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer :: index_field
integer :: register_restart_field_i2d_2level
real :: data_default_r
if(.not.module_is_initialized) call mpp_error(FATAL, &
'fms_io(register_restart_field_i2d_2level): need to call fms_io_init')
if (KIND(data_default)/=KIND(data1)) call mpp_error(FATAL,'fms_io(register_restart_field_i2d_2level): data_default and data1 different KIND()')
if (KIND(data_default)/=KIND(data2)) call mpp_error(FATAL,'fms_io(register_restart_field_i2d_2level): data_default and data2 different KIND()')
data_default_r = TRANSFER(MPP_FILL_INT,data_default_r)
if (present(data_default)) data_default_r = TRANSFER(data_default ,data_default_r)
call setup_one_field(fileObj, filename, fieldname, (/size(data1,1), size(data1,2), 1, 2/), &
index_field, domain, mandatory, no_domain, .false., &
position, tile_count, data_default_r, longname, units, read_only=read_only)
fileObj%p2di(1, index_field)%p => data1
fileObj%p2di(2, index_field)%p => data2
fileObj%var(index_field)%ndim = 2
register_restart_field_i2d_2level = index_field
return
end function register_restart_field_i2d_2level
!-------------------------------------------------------------------------------
!
! The routine will register a 3-D integer restart file field with two time level
!
!-------------------------------------------------------------------------------
function register_restart_field_i3d_2level(fileObj, filename, fieldname, data1, data2, domain, mandatory, &
no_domain, position, tile_count, data_default, longname, units, read_only)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:,:,:), intent(in), target :: data1, data2
type(domain2d), optional, intent(in), target :: domain
integer, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer :: index_field
integer :: register_restart_field_i3d_2level
real :: data_default_r
if(.not.module_is_initialized) call mpp_error(FATAL, &
'fms_io(register_restart_field_i3d_2level): need to call fms_io_init')
if (KIND(data_default)/=KIND(data1)) call mpp_error(FATAL,'fms_io(register_restart_field_i3d_2level): data_default and data1 different KIND()')
if (KIND(data_default)/=KIND(data2)) call mpp_error(FATAL,'fms_io(register_restart_field_i3d_2level): data_default and data2 different KIND()')
data_default_r = TRANSFER(MPP_FILL_INT,data_default_r)
if (present(data_default)) data_default_r = TRANSFER(data_default ,data_default_r)
call setup_one_field(fileObj, filename, fieldname, (/size(data1,1), size(data1,2), size(data1,3), 2/), &
index_field, domain, mandatory, no_domain, .false., &
position, tile_count, data_default_r, longname, units, read_only=read_only)
fileObj%p3di(1, index_field)%p => data1
fileObj%p3di(2, index_field)%p => data2
fileObj%var(index_field)%ndim = 3
register_restart_field_i3d_2level = index_field
return
end function register_restart_field_i3d_2level
!-------------------------------------------------------------------------------
!
! The routine will register a 2-D real for a generic region defined
! by the global_size variable.
!
!-------------------------------------------------------------------------------
function register_restart_region_r2d (fileObj, filename, fieldname, data, indices, global_size, &
pelist, is_root_pe, longname, units, position, &
x_halo, y_halo, ishift, jshift, read_only, mandatory)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:), intent(in), target :: data
integer, dimension(:), intent(in) :: indices, global_size, pelist
logical, intent(in) :: is_root_pe
character(len=*), optional, intent(in) :: longname, units
integer, optional, intent(in) :: position, x_halo, y_halo, ishift, jshift
logical, optional, intent(in) :: read_only
logical, optional, intent(in) :: mandatory
integer :: index_field, l_position
integer :: register_restart_region_r2d
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_region_r2d): need to call fms_io_init')
if ((is_root_pe) .and. (.not.ANY(mpp_pe().eq.pelist))) call mpp_error(FATAL, &
'fms_io(register_restart_region_r2d) designated root_pe is not a member of pelist')
l_position = CENTER
if (present(position)) l_position = position
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), 1, 1/), &
index_field, no_domain=.true., position=l_position, longname=longname, units=units, &
read_only=read_only, mandatory=mandatory)
fileObj%p2dr(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 2
fileObj%var(index_field)%is = indices(1)
fileObj%var(index_field)%ie = indices(2)
fileObj%var(index_field)%js = indices(3)
fileObj%var(index_field)%je = indices(4)
fileObj%var(index_field)%gsiz(1) = global_size(1)
fileObj%var(index_field)%gsiz(2) = global_size(2)
fileObj%is_root_pe = is_root_pe
fileObj%var(index_field)%x_halo = 0
fileObj%var(index_field)%y_halo = 0
fileObj%var(index_field)%ishift = 0
fileObj%var(index_field)%jshift = 0
if (present(x_halo)) fileObj%var(index_field)%x_halo = x_halo
if (present(y_halo)) fileObj%var(index_field)%y_halo = y_halo
if (present(ishift)) fileObj%var(index_field)%ishift = ishift
if (present(jshift)) fileObj%var(index_field)%jshift = jshift
if (allocated(fileObj%var(index_field)%pelist)) deallocate(fileObj%var(index_field)%pelist)
if (allocated(fileObj%var(index_field)%pelist)) deallocate(fileObj%var(index_field)%pelist)
allocate(fileObj%var(index_field)%pelist(size(pelist)))
fileObj%var(index_field)%pelist = pelist
register_restart_region_r2d = index_field
return
end function register_restart_region_r2d
!-------------------------------------------------------------------------------
!
! The routine will register a 3-D real for a generic region defined
! by the global_size variable.
!
!-------------------------------------------------------------------------------
function register_restart_region_r3d (fileObj, filename, fieldname, data, indices, global_size, &
pelist, is_root_pe, longname, units, position, &
x_halo, y_halo, ishift, jshift, read_only, mandatory)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:,:), intent(in), target :: data
integer, dimension(:), intent(in) :: indices, global_size, pelist
logical, intent(in) :: is_root_pe
character(len=*), optional, intent(in) :: longname, units
logical, optional, intent(in) :: read_only
integer, optional, intent(in) :: position, x_halo, y_halo, ishift, jshift
logical, optional, intent(in) :: mandatory
integer :: index_field, l_position
integer :: register_restart_region_r3d
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(register_restart_region_r3d): need to call fms_io_init')
if ((is_root_pe) .and. (.not.ANY(mpp_pe().eq.pelist))) call mpp_error(FATAL, &
'fms_io(register_restart_region_r3d) designated root_pe is not a member of pelist')
l_position = CENTER
if (present(position)) l_position = position
call setup_one_field(fileObj, filename, fieldname, (/size(data,1), size(data,2), size(data,3), 1/), &
index_field, no_domain=.true., position=l_position, longname=longname, units=units, &
read_only=read_only, mandatory=mandatory)
fileObj%p3dr(fileObj%var(index_field)%siz(4), index_field)%p => data
fileObj%var(index_field)%ndim = 3
fileObj%var(index_field)%is = indices(1)
fileObj%var(index_field)%ie = indices(2)
fileObj%var(index_field)%js = indices(3)
fileObj%var(index_field)%je = indices(4)
fileObj%var(index_field)%gsiz(1) = global_size(1)
fileObj%var(index_field)%gsiz(2) = global_size(2)
fileObj%var(index_field)%gsiz(3) = global_size(3)
fileObj%is_root_pe = is_root_pe
fileObj%var(index_field)%x_halo = 0
fileObj%var(index_field)%y_halo = 0
fileObj%var(index_field)%ishift = 0
fileObj%var(index_field)%jshift = 0
if (present(x_halo)) fileObj%var(index_field)%x_halo = x_halo
if (present(y_halo)) fileObj%var(index_field)%y_halo = y_halo
if (present(ishift)) fileObj%var(index_field)%ishift = ishift
if (present(jshift)) fileObj%var(index_field)%jshift = jshift
if (allocated(fileObj%var(index_field)%pelist)) deallocate(fileObj%var(index_field)%pelist)
allocate(fileObj%var(index_field)%pelist(size(pelist)))
fileObj%var(index_field)%pelist = pelist
register_restart_region_r3d = index_field
return
end function register_restart_region_r3d
!-------------------------------------------------------------------------------
!
! saves all registered variables to restart files. Those variables are set
! through register_restart_field
!
!-------------------------------------------------------------------------------
subroutine save_restart(fileObj, time_stamp, directory, append, time_level)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in), optional :: directory
character(len=*), intent(in), optional :: time_stamp
! Arguments:
! (in) directory - The directory where the restart file goes.
! (in) time_stamp - character format of the time of this restart file.
logical, intent(in), optional :: append
real, intent(in), optional :: time_level
character(len=256) :: dir
character(len=80) :: restartname ! The restart file name (no dir).
character(len=336) :: restartpath ! The restart file path (dir/file).
! This approach is taken rather than interface overloading in order to preserve
! use of the register_restart_field infrastructure
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(save_restart): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
dir = "RESTART"
if(present(directory)) dir = directory
restartname = fileObj%name
if(time_stamp_restart) then
if (PRESENT(time_stamp)) then
if(len_trim(restartname)+len_trim(time_stamp) > 79) call mpp_error(FATAL, "fms_io(save_restart): " // &
"Length of restart file name + time_stamp is greater than allowed character length of 79")
restartname = trim(time_stamp)//"."//trim(restartname)
endif
end if
if(len_trim(dir) > 0) then
if(len_trim(dir)+len_trim(restartname) > 335) call mpp_error(FATAL, "fms_io(save_restart): " // &
"Length of full restart path + file name is greater than allowed character length of 355")
restartpath = trim(dir)//"/"// trim(restartname)
else
restartpath = trim(restartname)
end if
if(fileObj%is_compressed .AND. ALLOCATED(fileObj%axes)) then
! fileObj%axes must also be allocated if the file contains compressed axes
! But will this always be true in the future?
call save_compressed_restart(fileObj,restartpath,append,time_level)
elseif(fileObj%unlimited_axis .AND. ALLOCATED(fileObj%axes)) then
call save_unlimited_axis_restart(fileObj,restartpath)
else
call save_default_restart(fileObj,restartpath)
endif
if(print_chksum) call write_chksum(fileObj, MPP_OVERWR)
end subroutine save_restart
!---- return true if all fields in fileObj is read only
function all_field_read_only(fileObj)
type(restart_file_type), intent(in) :: fileObj
logical :: all_field_read_only
integer :: j
all_field_read_only = .TRUE.
do j = 1, fileObj%nvar
if( .not. fileObj%var(j)%read_only) then
all_field_read_only = .FALSE.
exit
endif
enddo
return
end function all_field_read_only
!-------------------------------------------------------------------------------
!
! saves all registered variables to restart files. Those variables are set
! through register_restart_field
!
!-------------------------------------------------------------------------------
subroutine save_compressed_restart(fileObj,restartpath,append,time_level)
type(restart_file_type), intent(inout),target :: fileObj
character(len=336) :: restartpath ! The restart file path (dir/file).
! Optional arguments:
! If neither append or time_level is present:
! routine writes both meta data and field data.
! If append is present and append=.true.:
! Only field data is written.
! The field data is appended to a new time level.
! time_level must also be present and it must be >= 0.0
! The value of time_level is written as a new value of the time axis data.
! If time_level is present and time_level < 0.0:
! A new file is opened and only the meta data is written.
! If append is present and append=.false.:
! Behaves the same was as if it were not present. That is, meta data is
! written and whether or not field data is written is determined by time_level.
logical, intent(in), optional :: append
real, intent(in), optional :: time_level
integer :: unit ! The mpp unit of the open file.
type(axistype) :: x_axis, y_axis, z_axis, CC_axis, other_axis
type(axistype) :: t_axis, c_axis, h_axis ! time & sparse compressed vector axes
type(axistype) :: comp_axis
logical :: naxis_z=.false.
type(axistype), dimension(4) :: var_axes
type(var_type), pointer, save :: cur_var=>NULL()
integer :: i, j, k, l, num_var_axes, cpack, idx, mpp_action
real :: tlev
real, allocatable, dimension(:,:) :: r2d
real, allocatable, dimension(:) :: r1d
real :: r0d
integer(8), allocatable, dimension(:) :: check_val
character(len=256) :: checksum_char
logical :: domain_present, write_meta_data, write_field_data
logical :: c_axis_defined, h_axis_defined, CC_axis_defined
type(domain2d), pointer :: domain =>NULL()
type(ax_type), pointer :: axis =>NULL()
!-- no need to proceed if all the variables are read only.
if( all_field_read_only(fileObj) ) return
if (.not.ALLOCATED(fileObj%axes(CIDX)%idx) .and. .not.ALLOCATED(fileObj%axes(HIDX)%idx) ) then
call mpp_error(FATAL, "fms_io(save_compressed_restart): A compressed axis has "// &
"not been defined for file "//trim(fileObj%name))
else if (ALLOCATED(fileObj%axes(CIDX)%idx)) then
domain =>fileObj%axes(CIDX)%domain
else
domain =>fileObj%axes(HIDX)%domain
endif
if(present(append)) then
if(append .and. .not.present(time_level)) then
call mpp_error(FATAL, 'fms_io(save_compressed_restart): time_level must be present when append=.true.'// &
' for file '//trim(fileObj%name))
endif
endif
mpp_action = MPP_OVERWR
write_meta_data = .true.
if(present(append)) then
if(append) then
mpp_action = MPP_APPEND
write_meta_data = .false. ! Assuming meta data is already written when routine is called to append to field data.
if(time_level < 0.0) then
call mpp_error(FATAL, 'fms_io(save_compressed_restart): time_level cannot be negative when append is .true.'// &
' for file '//trim(fileObj%name))
endif
endif
endif
write_field_data = .true.
if(present(time_level)) then
write_field_data = time_level >= 0.0 ! Using negative value of time_level as a flag that there is no valid field data to write.
endif
call mpp_open(unit,trim(restartpath),action=mpp_action,form=form, &
is_root_pe=fileObj%is_root_pe, domain=domain)
if(write_meta_data) then
! User has defined axes and these are assumed to be unique
! Unfortunately it has proven difficult to write a generalized form because
! of the variations possible across all of the axes
! Currently support only 1 user defined axis of each type
! In fact, this config is specifically designed to support the land model
! sparse, compressed tile data
axis => fileobj%axes(XIDX)
if(.not. ASSOCIATED(axis)) call mpp_error(FATAL, "fms_io(save_compressed_restart): "// &
" The X axis has not been defined for "// &
" file "//trim(fileObj%name) )
call mpp_write_meta(unit,x_axis,axis%name,axis%units,axis%longname,data=axis%data,cartesian='X')
axis => fileobj%axes(YIDX)
if(.not. ASSOCIATED(axis)) call mpp_error(FATAL, "fms_io(save_compressed_restart): "// &
" The Y axis has not been defined for "// &
" file "//trim(fileObj%name) )
call mpp_write_meta(unit,y_axis,axis%name,axis%units,axis%longname,data=axis%data,cartesian='Y')
axis => fileobj%axes(ZIDX)
naxis_z = .false.
if(ASSOCIATED(axis%data))then
call mpp_write_meta(unit,z_axis,axis%name,axis%units,axis%longname, &
data=axis%data,cartesian='Z')
naxis_z = .true.
endif
axis => fileobj%axes(CCIDX)
if(ASSOCIATED(axis%data))then
call mpp_write_meta(unit,CC_axis,axis%name,axis%units,axis%longname,data=axis%data,cartesian='CC')
CC_axis_defined = .TRUE.
else
CC_axis_defined = .FALSE.
endif
! The compressed axis
axis => fileObj%axes(CIDX)
if(ALLOCATED(axis%idx)) then
call mpp_def_dim(unit,trim(axis%dimlen_name),axis%dimlen,trim(axis%dimlen_lname), (/(i,i=1,axis%dimlen)/))
call mpp_write_meta(unit,c_axis,axis%name,axis%units,axis%longname, &
data=axis%idx,compressed=axis%compressed,min=axis%imin)
c_axis_defined = .TRUE.
else
c_axis_defined = .FALSE.
endif
axis => fileObj%axes(HIDX)
if (ALLOCATED(axis%idx)) then
call mpp_def_dim(unit,trim(axis%dimlen_name),axis%dimlen,trim(axis%dimlen_lname), (/(i,i=1,axis%dimlen)/))
call mpp_write_meta(unit,h_axis,axis%name,axis%units,axis%longname, &
data=axis%idx,compressed=axis%compressed,min=axis%imin)
h_axis_defined = .TRUE.
else
h_axis_defined = .FALSE.
endif
! write out time axis
axis => fileobj%axes(TIDX)
if(ASSOCIATED(axis%data))then
call mpp_write_meta(unit,t_axis, axis%name, units=axis%units, longname=axis%longname, cartesian='T', calendar=axis%calendar)
else
call mpp_write_meta(unit,t_axis, 'Time','time level','Time',cartesian='T')
endif
! write metadata for fields
do j = 1,fileObj%nvar
cur_var => fileObj%var(j)
if(cur_var%read_only) cycle
if(cur_var%siz(4) > 1 .AND. cur_var%siz(4) .NE. fileObj%max_ntime ) call mpp_error(FATAL, &
"fms_io(save_restart): "//trim(cur_var%name)//" in file "//trim(fileObj%name)// &
" has more than one time level, but number of time level is not equal to max_ntime")
select case (trim(cur_var%compressed_axis))
case ('C')
comp_axis = c_axis
other_axis = z_axis
case ('C_CC')
comp_axis = c_axis
other_axis = CC_axis
case ('H')
comp_axis = h_axis
case default
if (ALLOCATED(fileObj%axes(CIDX)%idx)) then
comp_axis = c_axis
other_axis = z_axis
else
comp_axis = h_axis
endif
end select
if(cur_var%ndim == 0) then
num_var_axes = 1
var_axes(1) = t_axis
elseif(cur_var%ndim == 1) then
num_var_axes = 1
var_axes(1) = comp_axis
if(cur_var%siz(4) == fileObj%max_ntime) then
num_var_axes = 2
var_axes(2) = t_axis
endif
elseif(cur_var%ndim == 2) then
num_var_axes = 2
var_axes(1) = comp_axis
var_axes(2) = other_axis
if(cur_var%siz(4) == fileObj%max_ntime) then
num_var_axes = 3
var_axes(3) = t_axis
endif
elseif(cur_var%ndim == 3) then
num_var_axes = 3
var_axes(1) = comp_axis
var_axes(2) = z_axis
var_axes(3) = CC_axis
if(cur_var%siz(4) == fileObj%max_ntime) then
num_var_axes = 4
var_axes(4) = t_axis
endif
else
call mpp_error(FATAL, "fms_io(save_compressed_restart): "//trim(cur_var%name)//" in file "// &
trim(fileObj%name)//" has more than three dimensions (not including time level)")
endif
cpack = pack_size ! Default size of real
allocate(check_val(max(1,cur_var%siz(4))))
do k = 1, cur_var%siz(4)
if ( Associated(fileObj%p0dr(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p0dr(k,j)%p, (/mpp_pe()/), mask_val=cur_var%default_data)
else if ( Associated(fileObj%p1dr(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p1dr(k,j)%p(:), mask_val=cur_var%default_data)
else if ( Associated(fileObj%p2dr(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p2dr(k,j)%p(:,:), mask_val=cur_var%default_data)
else if ( Associated(fileObj%p3dr(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p3dr(k,j)%p(:,:,:))
else if ( Associated(fileObj%p0di(k,j)%p) ) then
check_val(k) = fileObj%p0di(k,j)%p
cpack = 0 ! Write data as integer*4
else if ( Associated(fileObj%p1di(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p1di(k,j)%p(:), mask_val=cur_var%default_data)
cpack = 0 ! Write data as integer*4
else if ( Associated(fileObj%p2di(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p2di(k,j)%p(:,:), mask_val=cur_var%default_data)
cpack = 0 ! Write data as integer*4
else if ( Associated(fileObj%p3di(k,j)%p) ) then
call mpp_error(FATAL, "fms_io(save_compressed_restart): integer 3D restart fields are not currently supported"// &
trim(cur_var%name)//" of file "//trim(fileObj%name) )
else
call mpp_error(FATAL, "fms_io(save_restart): There is no pointer associated with the data of field "// &
trim(cur_var%name)//" of file "//trim(fileObj%name) )
end if
enddo
! The chksum could not reproduce when running on different processor count. So commenting out now.
! Also the chksum of compressed data is not read.
if(write_field_data) then ! Write checksums only if valid field data exists
call mpp_write_meta(unit,cur_var%field, var_axes(1:num_var_axes), cur_var%name, &
cur_var%units,cur_var%longname,pack=cpack,checksum=check_val,fill=cur_var%default_data)
else
call mpp_write_meta(unit,cur_var%field, var_axes(1:num_var_axes), cur_var%name, &
cur_var%units,cur_var%longname,pack=cpack,fill=cur_var%default_data)
endif
deallocate(check_val)
enddo
! write values for ndim of spatial and compressed axes
call mpp_write(unit,x_axis)
call mpp_write(unit,y_axis)
if (c_axis_defined) call mpp_write(unit,c_axis)
if (h_axis_defined) call mpp_write(unit,h_axis)
if (CC_axis_defined) call mpp_write(unit,CC_axis)
if(naxis_z) call mpp_write(unit,z_axis)
endif ! End of section to write meta data. Write meta data only if not appending.
if(write_field_data) then
! write data of each field
do k = 1, fileObj%max_ntime
if(present(time_level)) then
tlev = time_level
else
tlev = k
endif
do j=1,fileObj%nvar
cur_var => fileObj%var(j)
if(cur_var%read_only) cycle
select case (trim(cur_var%compressed_axis))
case ('C')
idx = CIDX
case ('H')
idx = HIDX
case default
if (ALLOCATED(fileObj%axes(CIDX)%idx)) then
idx = CIDX
else
idx = HIDX
endif
end select
! If some fields only have one time level, we do not need to write the second level, just keep
! the data missing.
if(k <= cur_var%siz(4)) then
if ( Associated(fileObj%p0dr(k,j)%p) ) then
call mpp_write(unit, cur_var%field, fileObj%p0dr(k,j)%p, tlev)
elseif ( Associated(fileObj%p1dr(k,j)%p) ) then
call mpp_write_compressed(unit, cur_var%field, domain, fileObj%p1dr(k,j)%p, &
fileObj%axes(idx)%nelems(:), tstamp=tlev, default_data=cur_var%default_data)
elseif ( Associated(fileObj%p2dr(k,j)%p) ) then
call mpp_write_compressed(unit, cur_var%field, domain, fileObj%p2dr(k,j)%p, &
fileObj%axes(idx)%nelems(:), tstamp=tlev, default_data=cur_var%default_data)
elseif ( Associated(fileObj%p3dr(k,j)%p) ) then
call mpp_write_compressed(unit, cur_var%field, domain, fileObj%p3dr(k,j)%p, &
fileObj%axes(idx)%nelems(:), tstamp=tlev, default_data=cur_var%default_data)
elseif ( Associated(fileObj%p0di(k,j)%p) ) then
r0d = fileObj%p0di(k,j)%p
call mpp_write(unit, cur_var%field, r0d, tlev)
elseif ( Associated(fileObj%p1di(k,j)%p) ) then
allocate(r1d(cur_var%siz(1)) )
r1d = fileObj%p1di(k,j)%p
call mpp_write_compressed(unit, cur_var%field, domain, r1d, &
fileObj%axes(idx)%nelems(:), tstamp=tlev, default_data=cur_var%default_data)
deallocate(r1d)
elseif ( Associated(fileObj%p2di(k,j)%p) ) then
allocate(r2d(cur_var%siz(1), cur_var%siz(2)) )
r2d = fileObj%p2di(k,j)%p
call mpp_write_compressed(unit, cur_var%field, domain, r2d, &
fileObj%axes(idx)%nelems(:), tstamp=tlev, default_data=cur_var%default_data)
deallocate(r2d)
else
call mpp_error(FATAL, "fms_io(save_restart): There is no pointer associated with the data of field "// &
trim(cur_var%name)//" of file "//trim(fileObj%name) )
endif
endif
enddo ! end j loop
enddo ! end k loop
cur_var =>NULL()
endif
call mpp_close(unit)
end subroutine save_compressed_restart
!-------------------------------------------------------------------------------
!
! saves all registered variables to restart files. Those variables are set
! through register_restart_field
!
!-------------------------------------------------------------------------------
subroutine save_unlimited_axis_restart(fileObj,restartpath)
type(restart_file_type), intent(inout),target :: fileObj
character(len=336) :: restartpath ! The restart file path (dir/file).
integer :: unit ! The mpp unit of the open file.
type(axistype) :: u_axis
type(axistype), dimension(4) :: var_axes
type(var_type), pointer, save :: cur_var=>NULL()
integer :: i, j, k, l, num_var_axes, cpack, idx
real, allocatable, dimension(:) :: r1d
integer(8) :: check_val
character(len=256) :: checksum_char
type(domain2d), pointer :: domain =>NULL()
type(ax_type), pointer :: axis =>NULL()
if ( .NOT.fileObj%unlimited_axis ) then
call mpp_error(FATAL, "fms_io(save_unlimited_axis_restart): An unlimited axis has "// &
"not been defined for file "//trim(fileObj%name))
endif
domain =>fileObj%axes(UIDX)%domain
call mpp_open(unit,trim(restartpath),action=MPP_OVERWR,form=form, &
is_root_pe=fileObj%is_root_pe, domain=domain)
! Set unlimited axis
axis => fileobj%axes(UIDX)
call mpp_write_meta(unit,u_axis,axis%name,data=sum(axis%nelems(:)),unlimited=.true.)
call write_meta_global(unit,fileObj) ! Write any additional global metadata
call mpp_write(unit,u_axis)
! write metadata for fields
do j = 1,fileObj%nvar
cur_var => fileObj%var(j)
if(cur_var%siz(4) > 1) call mpp_error(FATAL, &
"fms_io(save_restart): "//trim(cur_var%name)//" in file "//trim(fileObj%name)// &
" has more than one time level. Only single time level is currrently supported")
if(cur_var%ndim == 1) then
num_var_axes = 1
var_axes(1) = u_axis
else
call mpp_error(FATAL, 'fms_io(save_unlimited_axis_restart): Only vectors are currently supported')
endif
cpack = pack_size ! Default size of real
if ( Associated(fileObj%p1dr(1,j)%p) ) then
check_val = mpp_chksum(fileObj%p1dr(1,j)%p(:))
else if ( Associated(fileObj%p1di(1,j)%p) ) then
! Fill values are -HUGE(i4) which don't behave as desired for checksum algorithm
check_val = mpp_chksum(INT(fileObj%p1di(1,j)%p(:),8))
cpack = 0 ! Write data as integer*4
else
call mpp_error(FATAL, "fms_io(save_unlimited_axis_restart): There is no pointer associated with the record data of field "// &
trim(cur_var%name)//" of file "//trim(fileObj%name) )
end if
call mpp_write_meta(unit,cur_var%field, var_axes(1:num_var_axes), cur_var%name, &
cur_var%units,cur_var%longname,pack=cpack,checksum=(/check_val/))
enddo ! end j loop
! write data of each field
do j=1,fileObj%nvar
cur_var => fileObj%var(j)
if ( Associated(fileObj%p1dr(1,j)%p) ) then
call mpp_write_unlimited_axis(unit,cur_var%field,domain,fileObj%p1dr(1,j)%p,fileObj%axes(UIDX)%nelems(:))
elseif ( Associated(fileObj%p1di(1,j)%p) ) then
allocate(r1d(cur_var%siz(1)) )
r1d = fileObj%p1di(1,j)%p
call mpp_write_unlimited_axis(unit,cur_var%field,domain,r1d,fileObj%axes(UIDX)%nelems(:))
deallocate(r1d)
else
call mpp_error(FATAL, "fms_io(save_restart): There is no pointer associated with the data of field "// &
trim(cur_var%name)//" of file "//trim(fileObj%name) )
endif
enddo ! end j loop
call mpp_close(unit)
cur_var =>NULL()
end subroutine save_unlimited_axis_restart
!-------------------------------------------------------------------------------
!
! saves all registered variables to restart files. Those variables are set
! through register_restart_field
!
!-------------------------------------------------------------------------------
subroutine save_default_restart(fileObj,restartpath)
type(restart_file_type), intent(inout) :: fileObj
character(len=336) :: restartpath ! The restart file path (dir/file).
character(len=8) :: suffix ! A suffix (like _2) that is appended to the name of files after the first.
integer :: var_sz, size_in_file ! The size in bytes of each variable and of the variables already in a file.
integer :: unit ! The mpp unit of the open file.
real, dimension(max_axis_size) :: axisdata
integer, dimension(max_axes) :: id_x_axes, siz_x_axes
integer, dimension(max_axes) :: id_y_axes, siz_y_axes
integer, dimension(max_axes) :: id_z_axes, siz_z_axes
integer, dimension(max_axes) :: id_a_axes, siz_a_axes
integer, dimension(max_axes) :: x_axes_indx, y_axes_indx, z_axes_indx, a_axes_indx
type(axistype), dimension(max_axes) :: x_axes, y_axes, z_axes, a_axes
type(axistype) :: t_axes
integer :: num_var_axes
type(axistype), dimension(5) :: var_axes
type(var_type), pointer, save :: cur_var=>NULL()
integer :: num_x_axes, num_y_axes, num_z_axes, num_a_axes
integer :: naxes_x, naxes_y, naxes_z, naxes_a
integer :: i, j, k, l, siz, ind_dom
logical :: domain_present
real :: tlev
real(8) :: tlev_r8
character(len=10) :: axisname
integer :: meta_size
type(domain2d) :: domain
real, allocatable, dimension(:,:,:) :: r3d
real, allocatable, dimension(:,:) :: r2d
real, allocatable, dimension(:) :: r1d
real :: r0d
integer(8), allocatable, dimension(:) :: check_val
character(len=256) :: checksum_char
integer :: isc, iec, jsc, jec
integer :: isg, ieg, jsg, jeg
integer :: ishift, jshift, iadd, jadd, cpack_size
logical :: write_on_this_pe
type(domain2d), pointer :: io_domain =>NULL()
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(save_restart): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
!-- no need to proceed if all the variables are read only.
if( all_field_read_only(fileObj) ) return
do i=1,max_axis_size
axisdata(i) = i
enddo
!--- check if any field in this file present domain.
domain_present = .false.
do j = 1, fileObj%nvar
if (fileObj%var(j)%domain_present) then
domain_present = .true.
ind_dom = j
exit
end if
end do
num_x_axes = unique_axes(fileObj, 1, id_x_axes, siz_x_axes, domain_x)
num_y_axes = unique_axes(fileObj, 2, id_y_axes, siz_y_axes, domain_y)
num_z_axes = unique_axes(fileObj, 3, id_z_axes, siz_z_axes )
num_a_axes = unique_axes(fileObj, 4, id_a_axes, siz_a_axes )
write_on_this_pe = .false.
if(domain_present) then
io_domain => mpp_get_io_domain(array_domain(fileObj%var(ind_dom)%domain_idx))
if(associated(io_domain)) then
if(mpp_domain_is_tile_root_pe(io_domain)) write_on_this_pe = .true.
endif
endif
!--- always write out from root pe
if( fileObj%is_root_pe ) write_on_this_pe = .true.
if( domain_present ) then
call mpp_open(unit,trim(restartpath),action=MPP_OVERWR,form=form,&
is_root_pe=fileObj%is_root_pe, domain=array_domain(fileObj%var(ind_dom)%domain_idx) )
else ! global data
call mpp_open(unit,trim(restartpath),action=MPP_OVERWR,form=form,threading=MPP_SINGLE,&
fileset=MPP_SINGLE, is_root_pe=fileObj%is_root_pe)
end if
naxes_x = 0
x_axes_indx = 0
y_axes_indx = 0
z_axes_indx = 0
a_axes_indx = 0
! write_out x_axes
do j = 1, num_x_axes
! make sure this axis is used by some variable
do l=1,fileObj%nvar
if(fileObj%var(l)%read_only) cycle
if( fileObj%var(l)%id_axes(1) == j ) exit
end do
if( l > fileObj%nvar ) cycle
naxes_x = naxes_x + 1
x_axes_indx(naxes_x) = j
if (naxes_x < 10) then
write(axisname,'(a,i1)') 'xaxis_',naxes_x
else
write(axisname,'(a,i2)') 'xaxis_',naxes_x
endif
if(id_x_axes(j) > 0) then
call mpp_write_meta(unit,x_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_x_axes(j)),domain=domain_x(id_x_axes(j)),cartesian='X')
else
call mpp_write_meta(unit,x_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_x_axes(j)),cartesian='X')
endif
end do
! write out y_axes
naxes_y = 0
do j = 1, num_y_axes
! make sure this axis is used by some variable
do l=1,fileObj%nvar
if(fileObj%var(l)%read_only) cycle
if( fileObj%var(l)%id_axes(2) == j ) exit
end do
if( l > fileObj%nvar ) cycle
naxes_y = naxes_y + 1
y_axes_indx(naxes_y) = j
if (naxes_y < 10) then
write(axisname,'(a,i1)') 'yaxis_',naxes_y
else
write(axisname,'(a,i2)') 'yaxis_',naxes_y
endif
if(id_y_axes(j) > 0) then
call mpp_write_meta(unit,y_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_y_axes(j)),domain=domain_y(id_y_axes(j)),cartesian='Y')
else
call mpp_write_meta(unit,y_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_y_axes(j)),cartesian='Y')
endif
end do
! write out z_axes
naxes_z = 0
do j = 1, num_z_axes
! make sure this axis is used by some variable
do l=1,fileObj%nvar
if(fileObj%var(l)%read_only) cycle
if( fileObj%var(l)%id_axes(3) == j ) exit
end do
if( l > fileObj%nvar ) cycle
naxes_z = naxes_z + 1
z_axes_indx(naxes_z) = j
if (naxes_z < 10) then
write(axisname,'(a,i1)') 'zaxis_',naxes_z
else
write(axisname,'(a,i2)') 'zaxis_',naxes_z
endif
call mpp_write_meta(unit,z_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_z_axes(j)),cartesian='Z')
end do
! write out a_axes
naxes_a = 0
do j = 1, num_a_axes
! make sure this axis is used by some variable
do l=1,fileObj%nvar
if(fileObj%var(l)%read_only) cycle
if( fileObj%var(l)%id_axes(4) == j ) exit
end do
if( l > fileObj%nvar ) cycle
naxes_a = naxes_a + 1
a_axes_indx(naxes_a) = j
if (naxes_a < 10) then
write(axisname,'(a,i1)') 'aaxis_',naxes_a
else
write(axisname,'(a,i2)') 'aaxis_',naxes_a
endif
call mpp_write_meta(unit,a_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_a_axes(j)),cartesian='N')
end do
! write out time axis
call mpp_write_meta(unit,t_axes,&
'Time','time level','Time',cartesian='T')
! write metadata for fields
do j = 1,fileObj%nvar
cur_var => fileObj%var(j)
if(cur_var%read_only) cycle
if(cur_var%siz(4) > 1 .AND. cur_var%siz(4) .NE. fileObj%max_ntime ) call mpp_error(FATAL, &
"fms_io(save_restart): "//trim(cur_var%name)//" in file "//trim(fileObj%name)// &
" has more than one time level, but number of time level is not equal to max_ntime")
if(cur_var%ndim == 0) then
num_var_axes = 1
var_axes(1) = t_axes
else if(cur_var%ndim == 1) then
num_var_axes = 1
var_axes(1) = x_axes(cur_var%id_axes(1))
if(cur_var%siz(4) == fileObj%max_ntime) then
num_var_axes = 2
var_axes(2) = t_axes
end if
else if(cur_var%ndim == 2) then
num_var_axes = 2
var_axes(1) = x_axes(cur_var%id_axes(1))
var_axes(2) = y_axes(cur_var%id_axes(2))
if(cur_var%siz(4) == fileObj%max_ntime) then
num_var_axes = 3
var_axes(3) = t_axes
end if
else if(cur_var%ndim == 3) then
num_var_axes = 3
var_axes(1) = x_axes(cur_var%id_axes(1))
var_axes(2) = y_axes(cur_var%id_axes(2))
var_axes(3) = z_axes(cur_var%id_axes(3))
if(cur_var%siz(4) == fileObj%max_ntime) then
num_var_axes = 4
var_axes(4) = t_axes
end if
else if(cur_var%ndim == 4) then
num_var_axes = 4
var_axes(1) = x_axes(cur_var%id_axes(1))
var_axes(2) = y_axes(cur_var%id_axes(2))
var_axes(3) = z_axes(cur_var%id_axes(3))
var_axes(4) = a_axes(cur_var%id_axes(4))
if(cur_var%siz(4) == fileObj%max_ntime) then
num_var_axes = 5
var_axes(5) = t_axes
end if
end if
if ( cur_var%domain_idx > 0) then
call mpp_get_compute_domain(array_domain(cur_var%domain_idx), isc, iec, jsc, jec)
call mpp_get_global_domain(array_domain(cur_var%domain_idx), isg, ieg, jsg, jeg)
call mpp_get_domain_shift(array_domain(cur_var%domain_idx), ishift, jshift, cur_var%position)
else if (ASSOCIATED(Current_domain)) then
call mpp_get_compute_domain(Current_domain, isc, iec, jsc, jec)
call mpp_get_global_domain(Current_domain, isg, ieg, jsg, jeg)
call mpp_get_domain_shift(Current_domain, ishift, jshift, cur_var%position)
else
iec = cur_var%ie
isc = cur_var%is
ieg = cur_var%ie
jec = cur_var%je
jsc = cur_var%js
jeg = cur_var%je
ishift = 0
jshift = 0
endif
! call return_domain(domain)
iadd = iec-isc ! Size of the i-dimension on this processor (-1 as it is an increment)
jadd = jec-jsc ! Size of the j-dimension on this processor
if(iec == ieg) iadd = iadd + ishift
if(jec == jeg) jadd = jadd + jshift
allocate(check_val(max(1,cur_var%siz(4))))
cpack_size = pack_size
do k = 1, cur_var%siz(4)
if ( Associated(fileObj%p0dr(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p0dr(k,j)%p, (/mpp_pe()/) )
else if ( Associated(fileObj%p1dr(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p1dr(k,j)%p, (/mpp_pe()/) )
else if ( Associated(fileObj%p2dr(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p2dr(k,j)%p(cur_var%is:cur_var%is+iadd, cur_var%js:cur_var%js+jadd) )
else if ( Associated(fileObj%p3dr(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p3dr(k,j)%p(cur_var%is:cur_var%is+iadd, cur_var%js:cur_var%js+jadd, :) )
else if ( Associated(fileObj%p2dr8(k,j)%p) ) then
cpack_size = 1
check_val(k) = mpp_chksum(fileObj%p2dr8(k,j)%p(cur_var%is:cur_var%is+iadd, cur_var%js:cur_var%js+jadd) )
else if ( Associated(fileObj%p3dr8(k,j)%p) ) then
cpack_size = 1
check_val(k) = mpp_chksum(fileObj%p3dr8(k,j)%p(cur_var%is:cur_var%is+iadd, cur_var%js:cur_var%js+jadd, :) )
else if ( Associated(fileObj%p4dr(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p4dr(k,j)%p(cur_var%is:cur_var%is+iadd, cur_var%js:cur_var%js+jadd, :, :) )
else if ( Associated(fileObj%p0di(k,j)%p) ) then
check_val(k) = fileObj%p0di(k,j)%p
else if ( Associated(fileObj%p1di(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p1di(k,j)%p, (/mpp_pe()/) )
else if ( Associated(fileObj%p2di(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p2di(k,j)%p(cur_var%is:cur_var%is+iadd, cur_var%js:cur_var%js+jadd) )
else if ( Associated(fileObj%p3di(k,j)%p) ) then
check_val(k) = mpp_chksum(fileObj%p3di(k,j)%p(cur_var%is:cur_var%is+iadd, cur_var%js:cur_var%js+jadd, :))
else
call mpp_error(FATAL, "fms_io(save_restart): There is no pointer associated with the data of field "// &
trim(cur_var%name)//" of file "//trim(fileObj%name) )
end if
enddo
call mpp_write_meta(unit,cur_var%field, var_axes(1:num_var_axes), cur_var%name, &
cur_var%units,cur_var%longname,pack=cpack_size,checksum=check_val)
deallocate(check_val)
enddo
! write values for ndim of spatial axes
do j = 1, naxes_x
call mpp_write(unit,x_axes(x_axes_indx(j)))
enddo
do j = 1, naxes_y
call mpp_write(unit,y_axes(y_axes_indx(j)))
enddo
do j = 1, naxes_z
call mpp_write(unit,z_axes(z_axes_indx(j)))
enddo
do j = 1, naxes_a
call mpp_write(unit,a_axes(a_axes_indx(j)))
enddo
! write data of each field
do k = 1, fileObj%max_ntime
do j=1,fileObj%nvar
cur_var => fileObj%var(j)
if(cur_var%read_only) cycle
tlev =k
tlev_r8=k
! If some fields only have one time level, we do not need to write the second level, just keep
! the data missing.
if(k <= cur_var%siz(4)) then
if(cur_var%domain_present) then ! one 2-D or 3-D case possible present domain
if( Associated(fileObj%p2dr(k,j)%p) ) then
call mpp_write(unit, cur_var%field, array_domain(cur_var%domain_idx), fileObj%p2dr(k,j)%p, tlev, &
default_data=cur_var%default_data)
else if( Associated(fileObj%p3dr(k,j)%p) ) then
call mpp_write(unit, cur_var%field, array_domain(cur_var%domain_idx), fileObj%p3dr(k,j)%p, tlev, &
default_data=cur_var%default_data)
else if( Associated(fileObj%p2dr8(k,j)%p) ) then
call mpp_write(unit, cur_var%field, array_domain(cur_var%domain_idx), fileObj%p2dr8(k,j)%p, tlev_r8, &
default_data=real(cur_var%default_data,kind=8))
else if( Associated(fileObj%p3dr8(k,j)%p) ) then
call mpp_write(unit, cur_var%field, array_domain(cur_var%domain_idx), fileObj%p3dr8(k,j)%p, tlev_r8, &
default_data=real(cur_var%default_data,kind=8))
else if( Associated(fileObj%p4dr(k,j)%p) ) then
call mpp_write(unit, cur_var%field, array_domain(cur_var%domain_idx), fileObj%p4dr(k,j)%p, tlev, &
default_data=cur_var%default_data)
else if( Associated(fileObj%p2di(k,j)%p) ) then
allocate(r2d(cur_var%siz(1), cur_var%siz(2)) )
r2d = fileObj%p2di(k,j)%p
call mpp_write(unit, cur_var%field, array_domain(cur_var%domain_idx), r2d, tlev, &
default_data=cur_var%default_data)
deallocate(r2d)
else if( Associated(fileObj%p3di(k,j)%p) ) then
allocate(r3d(cur_var%siz(1), cur_var%siz(2), cur_var%siz(3)) )
r3d = fileObj%p3di(k,j)%p
call mpp_write(unit, cur_var%field, array_domain(cur_var%domain_idx), r3d, tlev, &
default_data=cur_var%default_data)
deallocate(r3d)
else
call mpp_error(FATAL, "fms_io(save_restart): domain is present, "// &
"field "//trim(cur_var%name)//" of file "//trim(fileObj%name)// &
", but none of p2dr, p3dr, p2di and p3di is associated")
end if
else if (write_on_this_pe) then
if ( Associated(fileObj%p0dr(k,j)%p) ) then
call mpp_write(unit, cur_var%field, fileObj%p0dr(k,j)%p, tlev)
else if ( Associated(fileObj%p1dr(k,j)%p) ) then
call mpp_write(unit, cur_var%field, fileObj%p1dr(k,j)%p, tlev)
else if ( Associated(fileObj%p2dr(k,j)%p) ) then
call mpp_write(unit, cur_var%field, fileObj%p2dr(k,j)%p, tlev)
else if ( Associated(fileObj%p3dr(k,j)%p) ) then
call mpp_write(unit, cur_var%field, fileObj%p3dr(k,j)%p, tlev)
! else if ( Associated(fileObj%p2dr8(k,j)%p) ) then
! call mpp_write(unit, cur_var%field, fileObj%p2dr8(k,j)%p, tlev_r8)
! else if ( Associated(fileObj%p3dr8(k,j)%p) ) then
! call mpp_write(unit, cur_var%field, fileObj%p3dr8(k,j)%p, tlev_r8)
else if ( Associated(fileObj%p4dr(k,j)%p) ) then
call mpp_write(unit, cur_var%field, fileObj%p4dr(k,j)%p, tlev)
else if ( Associated(fileObj%p0di(k,j)%p) ) then
r0d = fileObj%p0di(k,j)%p
call mpp_write(unit, cur_var%field, r0d, tlev)
else if ( Associated(fileObj%p1di(k,j)%p) ) then
allocate(r1d(cur_var%siz(1)) )
r1d = fileObj%p1di(k,j)%p
call mpp_write(unit, cur_var%field, r1d, tlev)
deallocate(r1d)
else if ( Associated(fileObj%p2di(k,j)%p) ) then
allocate(r2d(cur_var%siz(1), cur_var%siz(2)) )
r2d = fileObj%p2di(k,j)%p
call mpp_write(unit, cur_var%field, r2d, tlev)
deallocate(r2d)
else if ( Associated(fileObj%p3di(k,j)%p) ) then
allocate(r3d(cur_var%siz(1), cur_var%siz(2), cur_var%siz(3)) )
r3d = fileObj%p3di(k,j)%p
call mpp_write(unit, cur_var%field, r3d, tlev)
deallocate(r3d)
else
call mpp_error(FATAL, "fms_io(save_restart): There is no pointer associated with the data of field "// &
trim(cur_var%name)//" of file "//trim(fileObj%name) )
end if
end if
end if
enddo ! end j loop
enddo ! end k loop
call mpp_close(unit)
cur_var =>NULL()
end subroutine save_default_restart
!-------------------------------------------------------------------------------
!
! saves all registered border/halo variables to restart files. Those variables
! are set through register_restart_field (region option)
!
!-------------------------------------------------------------------------------
subroutine save_restart_border (fileObj, time_stamp, directory)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in), optional :: directory
character(len=*), intent(in), optional :: time_stamp
character(len=256) :: dir
character(len=256) :: restartpath ! The restart file path (dir/file).
character(len=80) :: restartname ! The restart file name (no dir).
!rab integer :: start_var, next_var ! The starting variables of the current and next files.
integer :: unit ! The mpp unit of the open file.
real, dimension(max_axis_size) :: axisdata
integer, dimension(max_axes) :: id_x_axes, siz_x_axes
integer, dimension(max_axes) :: id_y_axes, siz_y_axes
integer, dimension(max_axes) :: id_z_axes, siz_z_axes
integer, dimension(max_axes) :: x_axes_indx, y_axes_indx, z_axes_indx
type(axistype), dimension(max_axes) :: x_axes, y_axes, z_axes
type(axistype) :: t_axes
integer :: num_var_axes
type(axistype), dimension(4) :: var_axes
type(var_type), pointer, save :: cur_var=>NULL()
integer :: num_x_axes, num_y_axes, num_z_axes
integer :: naxes_x, naxes_y, naxes_z
integer :: i, j, k, l
integer :: isc, iec, jsc, jec
integer :: is, ie, js, je
integer :: i_add, i1, i2
integer :: j_add, j1, j2
integer :: i_glob, j_glob, k_glob
real :: tlev
character(len=10) :: axisname
real, allocatable, dimension(:,:) :: r2d
real, allocatable, dimension(:,:,:) :: r3d
integer(8), allocatable, dimension(:) :: check_val
!-- no need to proceed if all the variables are read only.
if( all_field_read_only(fileObj) ) return
do i=1,max_axis_size
axisdata(i) = i
enddo
dir = "RESTART"
if(present(directory)) dir = directory
restartname = fileObj%name
if (time_stamp_restart) then
if (PRESENT(time_stamp)) then
restartname = trim(time_stamp)//"."//trim(restartname)
endif
end if
if (len_trim(dir) > 0) then
restartpath = trim(dir)//"/"// trim(restartname)
else
restartpath = trim(restartname)
end if
num_x_axes = unique_axes(fileObj, 1, id_x_axes, siz_x_axes)
num_y_axes = unique_axes(fileObj, 2, id_y_axes, siz_y_axes)
num_z_axes = unique_axes(fileObj, 3, id_z_axes, siz_z_axes)
call mpp_open(unit,trim(restartpath),action=MPP_OVERWR,form=MPP_NETCDF,threading=MPP_SINGLE,&
fileset=MPP_SINGLE, is_root_pe=fileObj%is_root_pe)
! write out axes
naxes_x = 0
x_axes_indx = 0
y_axes_indx = 0
z_axes_indx = 0
! write out x_axes metadata
do j = 1, num_x_axes
! make sure this axis is used by some variable
do l=1, fileObj%nvar
if(fileObj%var(l)%read_only) cycle
if (fileObj%var(l)%id_axes(1) == j) exit
end do
if( l > fileObj%nvar ) cycle
naxes_x = naxes_x + 1
x_axes_indx(naxes_x) = j
if (naxes_x < 10) then
write(axisname,'(a,i1)') 'xaxis_',naxes_x
else
write(axisname,'(a,i2)') 'xaxis_',naxes_x
endif
call mpp_write_meta(unit,x_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_x_axes(j)),cartesian='X')
end do
! write out y_axes metadata
naxes_y = 0
do j = 1, num_y_axes
! make sure this axis is used by some variable
do l=1, fileObj%nvar
if(fileObj%var(l)%read_only) cycle
if (fileObj%var(l)%id_axes(2) == j) exit
end do
if( l > fileObj%nvar ) cycle
naxes_y = naxes_y + 1
y_axes_indx(naxes_y) = j
if (naxes_y < 10) then
write(axisname,'(a,i1)') 'yaxis_',naxes_y
else
write(axisname,'(a,i2)') 'yaxis_',naxes_y
endif
call mpp_write_meta(unit,y_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_y_axes(j)),cartesian='Y')
end do
! write out z_axes metadata
naxes_z = 0
do j = 1, num_z_axes
! make sure this axis is used by some variable
do l=1, fileObj%nvar
if(fileObj%var(l)%read_only) cycle
if (fileObj%var(l)%id_axes(3) == j) exit
end do
if( l > fileObj%nvar ) cycle
naxes_z = naxes_z + 1
z_axes_indx(naxes_z) = j
if (naxes_z < 10) then
write(axisname,'(a,i1)') 'zaxis_',naxes_z
else
write(axisname,'(a,i2)') 'zaxis_',naxes_z
endif
call mpp_write_meta(unit,z_axes(j),axisname,'none',axisname, &
data=axisdata(1:siz_z_axes(j)),cartesian='Z')
end do
! write out time axis
call mpp_write_meta(unit,t_axes,'Time','time level', &
'Time',cartesian='T')
! write metadata for fields
do j = 1, fileObj%nvar
cur_var => fileObj%var(j)
if(cur_var%read_only) cycle
if ((cur_var%siz(4) > 1) .AND. (cur_var%siz(4).NE.fileObj%max_ntime)) call mpp_error(FATAL, &
"fms_io(save_restart_border): "//trim(cur_var%name)//" in file "//trim(fileObj%name)// &
" has more than one time level, but number of time level is not equal to max_ntime")
if (cur_var%ndim == 2) then
num_var_axes = 2
var_axes(1) = x_axes(cur_var%id_axes(1))
var_axes(2) = y_axes(cur_var%id_axes(2))
if(cur_var%siz(4) == fileObj%max_ntime) then
num_var_axes = 3
var_axes(3) = t_axes
end if
else if (cur_var%ndim == 3) then
num_var_axes = 3
var_axes(1) = x_axes(cur_var%id_axes(1))
var_axes(2) = y_axes(cur_var%id_axes(2))
var_axes(3) = z_axes(cur_var%id_axes(3))
if(cur_var%siz(4) == fileObj%max_ntime) then
num_var_axes = 4
var_axes(4) = t_axes
end if
else
call mpp_error(FATAL, "fms_io(save_restart_border): "//trim(cur_var%name)//" in file "// &
trim(fileObj%name)//" has more than three dimension (not including time level)")
end if
! cycle the loop for pes not a member of the current pelist
if (.not.ANY(mpp_pe().eq.cur_var%pelist(:))) cycle
! IN ORDER TO GET CHECKSUM INFO, PERFORM THE GATHER AS IF YOU WILL BE DOING THE WRITE
! BUT INSTEAD CHECKSUM THE RESULTING TEMPORARY ARRAY
allocate(check_val(max(1,cur_var%siz(4))))
do k = 1, cur_var%siz(4)
! cycle the loop for pes not a member of the current pelist
if (.not.ANY(mpp_pe().eq.cur_var%pelist(:))) cycle
isc = cur_var%is
iec = cur_var%ie
jsc = cur_var%js
jec = cur_var%je
! set up indices for local array segment pointer (pointer is 1-based)
i1 = 1 + cur_var%x_halo
i2 = i1 + (iec-isc)
j1 = 1 + cur_var%y_halo
j2 = j1 + (jec-jsc)
! set up index shifts for global array r*d (1-based, but potentially needs offsets: i_add, j_add)
i_add = cur_var%ishift
j_add = cur_var%jshift
! If some fields only have one time level, we do not need to write the second level, just keep
! the data missing.
if(k <= cur_var%siz(4)) then
if ( Associated(fileObj%p2dr(k,j)%p) ) then
i_glob = cur_var%gsiz(1)
j_glob = cur_var%gsiz(2)
if (fileObj%is_root_pe) allocate(r2d(i_glob, j_glob))
call mpp_gather(isc+i_add, iec+i_add, jsc+j_add, jec+j_add, cur_var%pelist, &
fileObj%p2dr(k,j)%p(i1:i2,j1:j2), &
r2d, fileObj%is_root_pe)
check_val(k) = mpp_chksum(r2d, (/mpp_pe()/))
if (allocated(r2d)) deallocate(r2d)
else if ( Associated(fileObj%p3dr(k,j)%p) ) then
i_glob = cur_var%gsiz(1)
j_glob = cur_var%gsiz(2)
k_glob = cur_var%gsiz(3)
if (fileObj%is_root_pe) allocate(r3d(i_glob, j_glob, k_glob))
call mpp_gather(isc+i_add, iec+i_add, jsc+j_add, jec+j_add, k_glob, cur_var%pelist, &
fileObj%p3dr(k,j)%p(i1:i2,j1:j2,:), r3d, fileObj%is_root_pe)
check_val(k) = mpp_chksum(r3d, (/mpp_pe()/))
if (allocated(r3d)) deallocate(r3d)
else
call mpp_error(FATAL, "fms_io(save_restart_border): no pointer associated with data of field "// &
trim(cur_var%name)//" in file "//trim(fileObj%name) )
end if
end if
enddo ! end k loop
call mpp_write_meta(unit,cur_var%field, var_axes(1:num_var_axes), cur_var%name, &
cur_var%units,cur_var%longname,pack=pack_size,checksum=check_val)
if (allocated(check_val)) deallocate(check_val)
enddo
! write values for ndim of spatial axes
do j = 1, naxes_x
call mpp_write(unit,x_axes(x_axes_indx(j)))
enddo
do j = 1, naxes_y
call mpp_write(unit,y_axes(y_axes_indx(j)))
enddo
do j = 1, naxes_z
call mpp_write(unit,z_axes(z_axes_indx(j)))
enddo
! write data of each field
do k = 1, fileObj%max_ntime
tlev=k
do j=1, fileObj%nvar
cur_var => fileObj%var(j)
if(cur_var%read_only) cycle
! cycle the loop for pes not a member of the current pelist
if (.not.ANY(mpp_pe().eq.cur_var%pelist(:))) cycle
isc = cur_var%is
iec = cur_var%ie
jsc = cur_var%js
jec = cur_var%je
! set up indices for local array segment pointer (pointer is 1-based)
i1 = 1 + cur_var%x_halo
i2 = i1 + (iec-isc)
j1 = 1 + cur_var%y_halo
j2 = j1 + (jec-jsc)
! set up index shifts for global array r*d (1-based, but potentially needs offsets: i_add, j_add)
i_add = cur_var%ishift
j_add = cur_var%jshift
! If some fields only have one time level, we do not need to write the second level, just keep
! the data missing.
if(k <= cur_var%siz(4)) then
if (Associated(fileObj%p2dr(k,j)%p)) then
i_glob = cur_var%gsiz(1)
j_glob = cur_var%gsiz(2)
if (fileObj%is_root_pe) allocate(r2d(i_glob, j_glob))
call mpp_gather(isc+i_add, iec+i_add, jsc+j_add, jec+j_add, cur_var%pelist, &
fileObj%p2dr(k,j)%p(i1:i2,j1:j2), r2d, fileObj%is_root_pe)
call mpp_write(unit, cur_var%field, r2d, tlev)
if (allocated(r2d)) deallocate(r2d)
else if (Associated(fileObj%p3dr(k,j)%p)) then
i_glob = cur_var%gsiz(1)
j_glob = cur_var%gsiz(2)
k_glob = cur_var%gsiz(3)
if (fileObj%is_root_pe) allocate(r3d(i_glob, j_glob, k_glob))
call mpp_gather(isc+i_add, iec+i_add, jsc+j_add, jec+j_add, k_glob, cur_var%pelist, &
fileObj%p3dr(k,j)%p(i1:i2,j1:j2,:), r3d, fileObj%is_root_pe)
call mpp_write(unit, cur_var%field, r3d, tlev)
if (allocated(r3d)) deallocate(r3d)
else
call mpp_error(FATAL, "fms_io(save_restart_border): no pointer associated with data of field "// &
trim(cur_var%name)//" in file "//trim(fileObj%name) )
end if
end if
enddo ! end j loop
enddo ! end k loop
call mpp_close(unit)
cur_var =>NULL()
if(print_chksum) call write_chksum(fileObj, MPP_OVERWR)
return
end subroutine save_restart_border
!-------------------------------------------------------------------------------
!
! restores all registered border/halo variables to restart files. Those
! variables are set through register_restart_field (region option)
!
!-------------------------------------------------------------------------------
subroutine restore_state_border(fileObj, directory, nonfatal_missing_files)
type(restart_file_type), intent(inout) :: fileObj !< The restart_file_type object that has
!! information about the restarts
character(len=*), optional, intent(in) :: directory !< The directory in which to seek restart files
logical, optional, intent(in) :: nonfatal_missing_files !< If true, the inability to find
!! the expected restart file is not necessarily fatal
! Arguments:
! (in) directory - The directory where the restart or save
! files should be found. The default is 'INPUT'
character(len=128) :: dir
character(len=256) :: restartpath ! The restart file path (dir/file).
character(len=200) :: filepath ! The path (dir/file) to the file being opened.
character(len=80) :: varname ! A variable's name.
character(len=256) :: mesg ! Message to be constructed for checksum error.
type(var_type), pointer, save :: cur_var=>NULL()
integer :: ndim, nvar, natt, ntime, tlev, siz
type(fieldtype), allocatable :: fields(:)
logical :: fexist
integer :: j, n, l, k, unit
real, allocatable, dimension(:,:,:) :: r3d
real, allocatable, dimension(:,:) :: r2d
integer :: isc, iec, jsc, jec
logical :: check_exist
integer :: i1, i2, j1, j2
integer :: ishift, jshift, i_add, j_add
integer :: i_glob, j_glob, k_glob
integer(8), dimension(3) :: checksum_file
integer(8) :: checksum_data
logical :: is_there_a_checksum
logical :: fatal_missing_files
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(restore_state_border): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
dir = 'INPUT'
if(present(directory)) dir = directory
fatal_missing_files = .true.
if (present(nonfatal_missing_files)) fatal_missing_files = .not.nonfatal_missing_files
if(len_trim(dir) > 0) then
restartpath = trim(dir)//"/"// trim(fileObj%name)
else
restartpath = trim(fileObj%name)
end if
!--- first open the restart files
!--- NOTE: For distributed restart files, we are assuming there is only one file that might exist.
inquire (file=trim(restartpath), exist=fexist)
if (.not.fexist) then ; if (fatal_missing_files) then
call mpp_error(FATAL, "fms_io(restore_state_border): unable to find any restart files "// &
"specified by "//trim(restartpath))
elseif (mpp_pe() == mpp_root_pe()) then
call mpp_error(WARNING, "fms_io(restore_state_border): unable to find any restart files "// &
"specified by "//trim(restartpath))
endif ; endif
if (fexist) then
call mpp_open(unit,trim(restartpath),action=MPP_RDONLY,form=MPP_NETCDF,threading=MPP_SINGLE,&
fileset=MPP_SINGLE, is_root_pe=fileObj%is_root_pe)
! Read each variable from the first file in which it is found.
call mpp_get_info(unit, ndim, nvar, natt, ntime)
allocate(fields(nvar))
call mpp_get_fields(unit,fields(1:nvar))
do j=1,fileObj%nvar
cur_var => fileObj%var(j)
! cycle the loop for pes not a member of the current pelist
if (.not.ANY(mpp_pe().eq.cur_var%pelist(:))) cycle
isc = cur_var%is
iec = cur_var%ie
jsc = cur_var%js
jec = cur_var%je
! set up indices for local array segment pointer (pointer is 1-based)
i1 = 1 + cur_var%x_halo
i2 = i1 + (iec-isc)
j1 = 1 + cur_var%y_halo
j2 = j1 + (jec-jsc)
! set up index shifts for global array r*d (1-based, but potentially needs offsets: i_add, j_add)
i_add = cur_var%ishift
j_add = cur_var%jshift
do l=1, nvar
call mpp_get_atts(fields(l),name=varname)
if (lowercase(trim(varname)) == lowercase(trim(cur_var%name))) then
cur_var%initialized = .true.
check_exist = mpp_attribute_exist(fields(l),"checksum")
checksum_file = 0
is_there_a_checksum = .false.
if ( check_exist ) then
call mpp_get_atts(fields(l),checksum=checksum_file)
is_there_a_checksum = .true.
endif
if (.NOT. checksum_required) is_there_a_checksum = .false. ! Do not need to do data checksumming.
do k = 1, cur_var%siz(4)
tlev = k
! read the field and scatter it to the rest of the pelist
if (Associated(fileObj%p2dr(k,j)%p)) then
i_glob = cur_var%gsiz(1)
j_glob = cur_var%gsiz(2)
if (fileObj%is_root_pe) allocate(r2d(i_glob, j_glob))
call mpp_read(unit, fields(l), r2d, tlev)
call mpp_scatter(isc+i_add, iec+i_add, jsc+j_add, jec+j_add, cur_var%pelist, &
fileObj%p2dr(k,j)%p(i1:i2,j1:j2), r2d, fileObj%is_root_pe)
if ((fileObj%is_root_pe) .and. (is_there_a_checksum)) checksum_data = mpp_chksum(r2d, (/mpp_pe()/) )
if (allocated(r2d)) deallocate(r2d)
else if (Associated(fileObj%p3dr(k,j)%p)) then
i_glob = cur_var%gsiz(1)
j_glob = cur_var%gsiz(2)
k_glob = cur_var%gsiz(3)
if (fileObj%is_root_pe) allocate(r3d(i_glob, j_glob, k_glob))
call mpp_read(unit, fields(l), r3d, tlev)
call mpp_scatter(isc+i_add, iec+i_add, jsc+j_add, jec+j_add, k_glob, cur_var%pelist, &
fileObj%p3dr(k,j)%p(i1:i2,j1:j2,:), r3d, fileObj%is_root_pe)
if ((fileObj%is_root_pe) .and. (is_there_a_checksum)) checksum_data = mpp_chksum(r3d, (/mpp_pe()/) )
if (allocated(r3d)) deallocate(r3d)
else
call mpp_error(FATAL, "fms_io(retore_state_border): no pointer associated with data of field "// &
trim(cur_var%name)//" in file "//trim(fileObj%name) )
end if
if ((fileObj%is_root_pe) .and. (is_there_a_checksum) .and. (checksum_file(k)/=checksum_data)) then
write (mesg,'(a,Z16,a,Z16,a)') "Checksum of input field "// uppercase(trim(varname))//" ", checksum_data,&
" does not match value ", checksum_file(k), " stored in "//uppercase(trim(fileObj%name)//"." )
call mpp_error(FATAL, "fms_io(restore_state_border): "//trim(mesg) )
endif
end do
exit ! Start search for next restart variable.
endif
enddo
enddo
deallocate(fields)
call close_file(unit)
endif ! fexist is true
cur_var =>NULL()
! check whether all fields have been found
do j = 1, fileObj%nvar
if (.not.ANY(mpp_pe().eq.fileObj%var(j)%pelist(:))) cycle
if (.NOT. fileObj%var(j)%initialized) then
if (fileObj%var(j)%mandatory) then
call mpp_error(FATAL, "fms_io(restore_state_border): unable to find mandatory variable "// &
trim(fileObj%var(j)%name)//" in restart file "//trim(fileObj%name) )
end if
end if
end do
if(print_chksum) call write_chksum(fileObj, MPP_RDONLY )
return
end subroutine restore_state_border
!-------------------------------------------------------------------------------
! This subroutine will calculate chksum and print out chksum information.
!
subroutine write_chksum(fileObj, action)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: action
integer(8) :: data_chksum
integer :: j, k, outunit
integer :: isc, iec, jsc, jec
integer :: isg, ieg, jsg, jeg
integer :: ishift, jshift, iadd, jadd
type(var_type), pointer, save :: cur_var=>NULL()
character(len=32) :: routine_name
if(action == MPP_OVERWR) then
routine_name = "save_restart"
else if(action == MPP_RDONLY) then
routine_name = "restore_state"
else
call mpp_error(FATAL, "fms_io_mod(write_chksum): action should be MPP_OVERWR or MPP_RDONLY")
endif
do j=1,fileObj%nvar
cur_var => fileObj%var(j)
if ( cur_var%domain_idx > 0) then
call mpp_get_compute_domain(array_domain(cur_var%domain_idx), isc, iec, jsc, jec)
call mpp_get_global_domain(array_domain(cur_var%domain_idx), isg, ieg, jsg, jeg)
call mpp_get_domain_shift(array_domain(cur_var%domain_idx), ishift, jshift, cur_var%position)
else if (ASSOCIATED(Current_domain)) then
call mpp_get_compute_domain(Current_domain, isc, iec, jsc, jec)
call mpp_get_global_domain(Current_domain, isg, ieg, jsg, jeg)
call mpp_get_domain_shift(Current_domain, ishift, jshift, cur_var%position)
else
iec = cur_var%ie
isc = cur_var%is
ieg = cur_var%ie
jec = cur_var%je
jsc = cur_var%js
jeg = cur_var%je
ishift = 0
jshift = 0
endif
iadd = iec-isc ! Size of the i-dimension on this processor (-1 as it is an increment)
jadd = jec-jsc ! Size of the j-dimension on this processor
if(iec == ieg) iadd = iadd + ishift
if(jec == jeg) jadd = jadd + jshift
if(action == MPP_OVERWR .OR. (action == MPP_RDONLY .AND. cur_var%initialized) ) then
do k = 1, cur_var%siz(4)
if ( Associated(fileObj%p0dr(k,j)%p) ) then
data_chksum = mpp_chksum(fileObj%p0dr(k,j)%p, (/mpp_pe()/) )
else if ( Associated(fileObj%p1dr(k,j)%p) ) then
data_chksum = mpp_chksum(fileObj%p1dr(k,j)%p, (/mpp_pe()/) )
else if ( Associated(fileObj%p2dr(k,j)%p) ) then
data_chksum = mpp_chksum(fileObj%p2dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
else if ( Associated(fileObj%p3dr(k,j)%p) ) then
data_chksum = mpp_chksum(fileObj%p3dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :) )
else if ( Associated(fileObj%p4dr(k,j)%p) ) then
data_chksum = mpp_chksum(fileObj%p4dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :, :) )
else if ( Associated(fileObj%p0di(k,j)%p) ) then
data_chksum = fileObj%p0di(k,j)%p
else if ( Associated(fileObj%p1di(k,j)%p) ) then
data_chksum = mpp_chksum(fileObj%p1di(k,j)%p, (/mpp_pe()/) )
else if ( Associated(fileObj%p2di(k,j)%p) ) then
data_chksum = mpp_chksum(fileObj%p2di(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
else if ( Associated(fileObj%p3di(k,j)%p) ) then
data_chksum = mpp_chksum(fileObj%p3di(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :))
else
call mpp_error(FATAL, "fms_io(write_chksum): There is no pointer associated with the data of field "// &
trim(cur_var%name)//" of file "//trim(fileObj%name) )
end if
outunit = stdout()
write(outunit,'(a, I1, a, Z16)')'fms_io('//trim(routine_name)//'): At time level = ', k, ', chksum for "'// &
trim(cur_var%name)// '" of "'// trim(fileObj%name)// '" = ', data_chksum
enddo
endif
enddo
cur_var =>NULL()
end subroutine write_chksum
!-------------------------------------------------------------------------------
!
! This subroutine reads the model state from previously
! generated files. All restart variables are read from the first
! file in the input filename list in which they are found.
subroutine restore_state_all(fileObj, directory, nonfatal_missing_files)
type(restart_file_type), intent(inout) :: fileObj !< The restart_file_type object that has
!! information about the restarts
character(len=*), optional, intent(in) :: directory !< The directory in which to seek restart files
logical, optional, intent(in) :: nonfatal_missing_files !< If true, the inability to find
!! the expected restart file is not necessarily fatal
! Arguments:
! (in) directory - The directory where the restart or save
! files should be found. The default is 'INPUT'
character(len=128) :: dir
character(len=256) :: restartpath ! The restart file path (dir/file).
character(len=200) :: filepath ! The path (dir/file) to the file being opened.
character(len=8) :: suffix ! A suffix (like "_2") that is added to any
! additional restart files.
character(len=80) :: varname ! A variable's name.
character(len=256) :: filename
character(len=256) :: mesg ! Message to be constructed for checksum error.
integer :: num_restart ! The number of restart files that have already
! been opened.
integer :: nfile ! The number of files (restart files and others
! explicitly in filename) that are open.
integer :: unit(max_split_file) ! The mpp unit of all open files.
type(var_type), pointer, save :: cur_var=>NULL()
integer :: ndim, nvar, natt, ntime, tlev, siz
type(fieldtype), allocatable :: fields(:)
logical :: fexist, domain_present
integer :: j, n, l, k, missing_fields, domain_idx
integer :: tile_id(1)
real, allocatable, dimension(:,:,:) :: r3d
real, allocatable, dimension(:,:) :: r2d
real, allocatable, dimension(:) :: r1d
real :: r0d
type(domain2d), pointer, save :: io_domain=>NULL()
integer :: isc, iec, jsc, jec
logical :: check_exist
integer :: isg, ieg, jsg, jeg
integer :: ishift, jshift, iadd, jadd
integer(8), dimension(3) :: checksum_file
integer(8) :: checksum_data
logical :: is_there_a_checksum
logical :: fatal_missing_files
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(restore_state_all): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
dir = 'INPUT'
if(present(directory)) dir = directory
fatal_missing_files = .true.
if (present(nonfatal_missing_files)) fatal_missing_files = .not.nonfatal_missing_files
num_restart = 0
nfile = 0
if(len_trim(dir) > 0) then
restartpath = trim(dir)//"/"// trim(fileObj%name)
else
restartpath = trim(fileObj%name)
end if
domain_present = .false.
do j = 1, fileObj%nvar
if (fileObj%var(j)%domain_present) then
domain_present = .true.
domain_idx = fileObj%var(j)%domain_idx
exit
end if
end do
!--- first open all the restart files
!--- NOTE: For distributed restart file, we are assuming there is only one file exist.
fexist = .FALSE.
if(domain_present) then
io_domain => mpp_get_io_domain(array_domain(domain_idx))
if(associated(io_domain)) then
tile_id = mpp_get_tile_id(io_domain)
write(filename, '(a,i4.4)' ) trim(restartpath)//'.', tile_id(1)
inquire (file=trim(filename), exist = fexist)
if( .NOT. fexist ) then
write(filename, '(a,i6.6)' ) trim(restartpath)//'.', tile_id(1)
inquire (file=trim(filename), exist = fexist)
endif
endif
io_domain => NULL()
endif
if(fexist) then
nfile = 1
!--- domain_present is true
call mpp_open(unit(nfile), trim(restartpath), form=form,action=MPP_RDONLY, &
threading=MPP_MULTI, domain=array_domain(domain_idx) )
else
do while(.true.)
if (num_restart < 10) then
write(suffix,'("_",I1)') num_restart
else
write(suffix,'("_",I2)') num_restart
endif
if (num_restart > 0) then
siz = len_trim(restartpath)
if(restartpath(siz-2:siz) == ".nc") then
filepath = restartpath(1:siz-3)//trim(suffix)
else
filepath = trim(restartpath) // trim(suffix)
end if
else
filepath = trim(restartpath)
end if
inquire (file=trim(filepath), exist=fexist)
if(.not. fexist) inquire(file=trim(filepath)//".nc", exist=fexist)
if(fexist) then
nfile = nfile + 1
if(nfile > max_split_file) call mpp_error(FATAL, &
"fms_io(restore_state_all): nfile is larger than max_split_file, increase max_split_file")
call mpp_open(unit(nfile), trim(filepath), form=form,action=MPP_RDONLY,threading=MPP_MULTI, &
fileset=MPP_SINGLE)
else
exit
end if
num_restart = num_restart + 1
end do
end if
if (nfile == 0) then ; if (fatal_missing_files) then
call mpp_error(FATAL, "fms_io(restore_state_all): unable to find any restart files "// &
"specified by "//trim(restartpath))
elseif (mpp_pe() == mpp_root_pe()) then
call mpp_error(WARNING, "fms_io(restore_state_all): unable to find any restart files "// &
"specified by "//trim(restartpath))
endif ; endif
! Read each variable from the first file in which it is found.
do n=1,nfile
call mpp_get_info(unit(n), ndim, nvar, natt, ntime)
allocate(fields(nvar))
call mpp_get_fields(unit(n),fields(1:nvar))
missing_fields = 0
do j=1,fileObj%nvar
cur_var => fileObj%var(j)
domain_present = cur_var%domain_present
domain_idx = cur_var%domain_idx
if ( cur_var%domain_idx > 0) then
call mpp_get_compute_domain(array_domain(cur_var%domain_idx), isc, iec, jsc, jec)
call mpp_get_global_domain(array_domain(cur_var%domain_idx), isg, ieg, jsg, jeg)
call mpp_get_domain_shift(array_domain(cur_var%domain_idx), ishift, jshift, cur_var%position)
else if (ASSOCIATED(Current_domain)) then
call mpp_get_compute_domain(Current_domain, isc, iec, jsc, jec)
call mpp_get_global_domain(Current_domain, isg, ieg, jsg, jeg)
call mpp_get_domain_shift(Current_domain, ishift, jshift, cur_var%position)
else
iec = cur_var%ie
isc = cur_var%is
ieg = cur_var%ie
jec = cur_var%je
jsc = cur_var%js
jeg = cur_var%je
ishift = 0
jshift = 0
endif
iadd = iec-isc ! Size of the i-dimension on this processor (-1 as it is an increment)
jadd = jec-jsc ! Size of the j-dimension on this processor
if(iec == ieg) iadd = iadd + ishift
if(jec == jeg) jadd = jadd + jshift
isc = cur_var%is
iec = cur_var%ie
jsc = cur_var%js
jec = cur_var%je
do l=1, nvar
call mpp_get_atts(fields(l),name=varname)
if (lowercase(trim(varname)) == lowercase(trim(cur_var%name))) then
cur_var%initialized = .true.
check_exist = mpp_attribute_exist(fields(l),"checksum")
checksum_file = 0
is_there_a_checksum = .false.
if ( check_exist ) then
call mpp_get_atts(fields(l),checksum=checksum_file)
is_there_a_checksum = .true.
endif
if (.NOT. checksum_required ) is_there_a_checksum = .false. ! Do not need to do data checksumming.
do k = 1, cur_var%siz(4)
tlev = k
if(domain_present) then
if( Associated(fileObj%p0dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p0dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p0dr(k,j)%p, (/mpp_pe()/) )
else if( Associated(fileObj%p1dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p1dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p1dr(k,j)%p, (/mpp_pe()/) )
else if( Associated(fileObj%p2dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), array_domain(domain_idx), fileObj%p2dr(k,j)%p, tlev)
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p2dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
else if( Associated(fileObj%p3dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), array_domain(domain_idx), fileObj%p3dr(k,j)%p, tlev)
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p3dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :) )
else if( Associated(fileObj%p2dr8(k,j)%p) ) then
call mpp_read(unit(n), fields(l), array_domain(domain_idx), fileObj%p2dr8(k,j)%p, tlev)
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p2dr8(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
else if( Associated(fileObj%p3dr8(k,j)%p) ) then
call mpp_read(unit(n), fields(l), array_domain(domain_idx), fileObj%p3dr8(k,j)%p, tlev)
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p3dr8(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :) )
else if( Associated(fileObj%p4dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), array_domain(domain_idx), fileObj%p4dr(k,j)%p, tlev)
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p4dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd,:,:))
else if( Associated(fileObj%p0di(k,j)%p) ) then
call mpp_read(unit(n), fields(l), r0d, tlev)
fileObj%p0di(k,j)%p = r0d
if ( is_there_a_checksum ) checksum_data = fileObj%p0di(k,j)%p
else if( Associated(fileObj%p1di(k,j)%p) ) then
allocate(r1d(cur_var%siz(1)))
call mpp_read(unit(n), fields(l), r1d, tlev)
fileObj%p1di(k,j)%p = r1d
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p1di(k,j)%p, (/mpp_pe()/) )
deallocate(r1d)
else if( Associated(fileObj%p2di(k,j)%p) ) then
allocate(r2d(cur_var%siz(1), cur_var%siz(2)) )
r2d = 0
call mpp_read(unit(n), fields(l), array_domain(domain_idx), r2d, tlev)
fileObj%p2di(k,j)%p(isc:iec,jsc:jec) = r2d(isc:iec,jsc:jec)
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p2di(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
deallocate(r2d)
else if( Associated(fileObj%p3di(k,j)%p) ) then
allocate(r3d(cur_var%siz(1), cur_var%siz(2), cur_var%siz(3)) )
r3d = 0
call mpp_read(unit(n), fields(l), array_domain(domain_idx), r3d, tlev)
fileObj%p3di(k,j)%p(isc:iec,jsc:jec,:) = r3d(isc:iec,jsc:jec,:)
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p3di(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :))
deallocate(r3d)
else
call mpp_error(FATAL, "fms_io(restore_state_all): domain is present for the field "//trim(varname)// &
" of file "//trim(fileObj%name)//", but none of p2dr, p3dr, p2di and p3di is associated")
end if
else
if( Associated(fileObj%p0dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p0dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p0dr(k,j)%p, (/mpp_pe()/) )
else if( Associated(fileObj%p1dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p1dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p1dr(k,j)%p, (/mpp_pe()/) )
else if( Associated(fileObj%p2dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p2dr(k,j)%p, tlev)
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p2dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
else if( Associated(fileObj%p3dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p3dr(k,j)%p, tlev)
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p3dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :) )
else if( Associated(fileObj%p4dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p4dr(k,j)%p, tlev)
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p4dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd,:,:))
else if( Associated(fileObj%p0di(k,j)%p) ) then
call mpp_read(unit(n), fields(l), r0d, tlev)
fileObj%p0di(k,j)%p = r0d
if ( is_there_a_checksum ) checksum_data = fileObj%p0di(k,j)%p
else if( Associated(fileObj%p1di(k,j)%p) ) then
allocate(r1d(cur_var%siz(1)) )
call mpp_read(unit(n), fields(l), r1d, tlev)
fileObj%p1di(k,j)%p = r1d
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p1di(k,j)%p, (/mpp_pe()/) )
deallocate(r1d)
else if( Associated(fileObj%p2di(k,j)%p) ) then
allocate(r2d(cur_var%siz(1), cur_var%siz(2)) )
r2d = 0
call mpp_read(unit(n), fields(l), r2d, tlev)
fileObj%p2di(k,j)%p = r2d
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p2di(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
deallocate(r2d)
else if( Associated(fileObj%p3di(k,j)%p) ) then
allocate(r3d(cur_var%siz(1), cur_var%siz(2), cur_var%siz(3)) )
r3d = 0
call mpp_read(unit(n), fields(l), r3d, tlev)
fileObj%p3di(k,j)%p = r3d
if ( is_there_a_checksum ) &
checksum_data = mpp_chksum(fileObj%p3di(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :))
deallocate(r3d)
else
call mpp_error(FATAL, "fms_io(restore_state_all): There is no pointer "//&
"associated with the data of field "// trim(varname)//" of file "//trim(fileObj%name) )
end if
end if
if ( ( is_there_a_checksum ) .and. (checksum_file(k) /= checksum_data) ) then
write (mesg,'(a,Z16,a,Z16,a)') "Checksum of input field "// uppercase(trim(varname))//" ", checksum_data,&
" does not match value ", checksum_file(k), " stored in "//uppercase(trim(fileObj%name)//"." )
call mpp_error(FATAL, "fms_io(restore_state_all): "//trim(mesg) )
endif
end do
exit ! Start search for next restart variable.
endif
enddo
if (l>nvar) missing_fields = missing_fields+1
enddo
deallocate(fields)
if (missing_fields == 0) exit
enddo
do n=1,nfile
call close_file(unit(n))
enddo
! check whether all fields have been found
do j = 1, fileObj%nvar
if( .NOT. fileObj%var(j)%initialized ) then
if( fileObj%var(j)%mandatory ) then
call mpp_error(FATAL, "fms_io(restore_state_all): unable to find mandatory variable "// &
trim(fileObj%var(j)%name)//" in restart file "//trim(fileObj%name) )
end if
end if
end do
cur_var =>NULL()
if(print_chksum) call write_chksum(fileObj, MPP_RDONLY )
end subroutine restore_state_all
!-------------------------------------------------------------------------------
!
! This subroutine reads the model state from previously
! generated files. All restart variables are read from the first
! file in the input filename list in which they are found.
subroutine restore_state_one_field(fileObj, id_field, directory, nonfatal_missing_files)
type(restart_file_type), intent(inout) :: fileObj !< The restart_file_type object that has
!! information about the restarts
integer, intent(in) :: id_field !< The field id of a variable that was
!! returned by a previous call to register_restart_field
character(len=*), optional, intent(in) :: directory !< The directory in which to seek restart files
logical, optional, intent(in) :: nonfatal_missing_files !< If true, the inability to find
!! the expected restart file is not necessarily fatal
! Arguments:
! (in) directory - The directory where the restart or save
! files should be found. The default is 'INPUT'
character(len=128) :: dir
character(len=256) :: restartpath ! The restart file path (dir/file).
character(len=200) :: filepath ! The path (dir/file) to the file being opened.
character(len=8) :: suffix ! A suffix (like "_2") that is added to any
! additional restart files.
character(len=80) :: varname ! A variable's name.
character(len=256) :: filename
character(len=256) :: mesg ! Message to be constructed for checksum error.
integer :: num_restart ! The number of restart files that have already
! been opened.
integer :: nfile ! The number of files (restart files and others
! explicitly in filename) that are open.
integer :: unit(max_split_file) ! The mpp unit of all open files.
type(var_type), pointer, save :: cur_var=>NULL()
integer :: ndim, nvar, natt, ntime, tlev, siz
integer :: tile_id(1)
type(fieldtype), allocatable :: fields(:)
logical :: fexist, domain_present
integer :: j, n, l, k, missing_fields, domain_idx
real, allocatable, dimension(:,:,:) :: r3d
real, allocatable, dimension(:,:) :: r2d
real, allocatable, dimension(:) :: r1d
real :: r0d
type(domain2d), pointer, save :: io_domain=>NULL()
integer :: isc, iec, jsc, jec
logical :: check_exist
integer :: isg, ieg, jsg, jeg
integer :: ishift, jshift, iadd, jadd
integer(8), dimension(3) :: checksum_file ! There should be no more than 3 timelevels in a restart file.
integer(8) :: checksum_data
logical :: is_there_a_checksum
logical :: fatal_missing_files
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(restore_state_one_field): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
dir = 'INPUT'
if(present(directory)) dir = directory
fatal_missing_files = .true.
if (present(nonfatal_missing_files)) fatal_missing_files = .not.nonfatal_missing_files
cur_var => fileObj%var(id_field)
domain_present = cur_var%domain_present
domain_idx = cur_var%domain_idx
if ( cur_var%domain_idx > 0) then
call mpp_get_compute_domain(array_domain(cur_var%domain_idx), isc, iec, jsc, jec)
call mpp_get_global_domain(array_domain(cur_var%domain_idx), isg, ieg, jsg, jeg)
call mpp_get_domain_shift(array_domain(cur_var%domain_idx), ishift, jshift, cur_var%position)
else if (ASSOCIATED(Current_domain)) then
call mpp_get_compute_domain(Current_domain, isc, iec, jsc, jec)
call mpp_get_global_domain(Current_domain, isg, ieg, jsg, jeg)
call mpp_get_domain_shift(Current_domain, ishift, jshift, cur_var%position)
else
iec = cur_var%ie
isc = cur_var%is
ieg = cur_var%ie
jec = cur_var%je
jsc = cur_var%js
jeg = cur_var%je
ishift = 0
jshift = 0
endif
iadd = iec-isc ! Size of the i-dimension on this processor (-1 as it is an increment)
jadd = jec-jsc ! Size of the j-dimension on this processor
if(iec == ieg) iadd = iadd + ishift
if(jec == jeg) jadd = jadd + jshift
num_restart = 0
nfile = 0
if(len_trim(dir) > 0) then
restartpath = trim(dir)//"/"// trim(fileObj%name)
else
restartpath = trim(fileObj%name)
end if
!--- first open all the restart files
!--- NOTE: For distributed restart file, we are assuming there is only one file exist.
fexist = .FALSE.
if(domain_present) then
io_domain => mpp_get_io_domain(array_domain(domain_idx))
if(associated(io_domain)) then
tile_id = mpp_get_tile_id(io_domain)
write(filename, '(a,i4.4)' ) trim(restartpath)//'.', tile_id(1)
inquire (file=trim(filename), exist = fexist)
if( .NOT. fexist ) then
write(filename, '(a,i6.6)' ) trim(restartpath)//'.', tile_id(1)
inquire (file=trim(filename), exist = fexist)
endif
endif
io_domain=>NULL()
endif
if(fexist) then
nfile = 1
!--- domain_present is true here.
call mpp_open(unit(nfile), trim(restartpath), form=form,action=MPP_RDONLY, &
threading=MPP_MULTI, domain=array_domain(domain_idx) )
else
do while(.true.)
if (num_restart < 10) then
write(suffix,'("_",I1)') num_restart
else
write(suffix,'("_",I2)') num_restart
endif
if (num_restart > 0) then
siz = len_trim(restartpath)
if(restartpath(siz-2:siz) == ".nc") then
filepath = restartpath(1:siz-3)//trim(suffix)
else
filepath = trim(restartpath) // trim(suffix)
end if
else
filepath = trim(restartpath)
end if
inquire (file=trim(filepath), exist=fexist)
if(.not. fexist) inquire(file=trim(filepath)//".nc", exist=fexist)
if(fexist) then
nfile = nfile + 1
if(nfile > max_split_file) call mpp_error(FATAL, &
"fms_io(restore_state_one_field): nfile is larger than max_split_file, increase max_split_file")
call mpp_open(unit(nfile), trim(filepath), form=form,action=MPP_RDONLY,threading=MPP_MULTI, &
fileset=MPP_SINGLE)
else
exit
end if
num_restart = num_restart + 1
end do
end if
if (nfile == 0) then ; if (fatal_missing_files) then
call mpp_error(FATAL, "fms_io(restore_state_all): unable to find any restart files "// &
"specified by "//trim(restartpath))
elseif (mpp_pe() == mpp_root_pe()) then
call mpp_error(WARNING, "fms_io(restore_state_all): unable to find any restart files "// &
"specified by "//trim(restartpath))
endif ; endif
! Read each variable from the first file in which it is found.
do n=1,nfile
call mpp_get_info(unit(n), ndim, nvar, natt, ntime)
allocate(fields(nvar))
call mpp_get_fields(unit(n),fields(1:nvar))
missing_fields = 0
j = id_field
do l=1, nvar
call mpp_get_atts(fields(l),name=varname)
if (lowercase(trim(varname)) == lowercase(trim(cur_var%name))) then
cur_var%initialized = .true.
check_exist = mpp_attribute_exist(fields(l),"checksum")
checksum_file = 0
is_there_a_checksum = .false.
if ( check_exist ) then
call mpp_get_atts(fields(l),checksum=checksum_file)
is_there_a_checksum = .true.
endif
if (.NOT. checksum_required ) is_there_a_checksum = .false. ! Do not need to do data checksumming.
isc = cur_var%is
iec = cur_var%ie
jsc = cur_var%js
jec = cur_var%je
do k = 1, cur_var%siz(4)
tlev = k
if(domain_present) then
if( Associated(fileObj%p0dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p0dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p0dr(k,j)%p, (/mpp_pe()/) )
else if( Associated(fileObj%p1dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p1dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p1dr(k,j)%p, (/mpp_pe()/) )
else if( Associated(fileObj%p2dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), array_domain(domain_idx), fileObj%p2dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data =&
& mpp_chksum(fileObj%p2dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
else if( Associated(fileObj%p3dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), array_domain(domain_idx), fileObj%p3dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data =&
& mpp_chksum(fileObj%p3dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :) )
else if( Associated(fileObj%p4dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), array_domain(domain_idx), fileObj%p4dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data =&
& mpp_chksum(fileObj%p4dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :,:) )
else if( Associated(fileObj%p0di(k,j)%p) ) then
call mpp_read(unit(n), fields(l), r0d, tlev)
fileObj%p0di(k,j)%p = r0d
if ( is_there_a_checksum ) checksum_data = fileObj%p0di(k,j)%p
else if( Associated(fileObj%p1di(k,j)%p) ) then
allocate(r1d(cur_var%siz(1)))
call mpp_read(unit(n), fields(l), r1d, tlev)
fileObj%p1di(k,j)%p = r1d
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p1di(k,j)%p, (/mpp_pe()/) )
deallocate(r1d)
else if( Associated(fileObj%p2di(k,j)%p) ) then
allocate(r2d(cur_var%siz(1), cur_var%siz(2)) )
r2d = 0
call mpp_read(unit(n), fields(l), array_domain(domain_idx), r2d, tlev)
fileObj%p2di(k,j)%p(isc:iec,jsc:jec) = r2d(isc:iec,jsc:jec)
if ( is_there_a_checksum ) checksum_data =&
& mpp_chksum(fileObj%p2di(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
deallocate(r2d)
else if( Associated(fileObj%p3di(k,j)%p) ) then
allocate(r3d(cur_var%siz(1), cur_var%siz(2), cur_var%siz(3)) )
r3d = 0
call mpp_read(unit(n), fields(l), array_domain(domain_idx), r3d, tlev)
fileObj%p3di(k,j)%p(isc:iec,jsc:jec,:) = r3d(isc:iec,jsc:jec,:)
if ( is_there_a_checksum ) checksum_data =&
& mpp_chksum(fileObj%p3di(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :))
deallocate(r3d)
else
call mpp_error(FATAL, "fms_io(restore_state_one_field): domain is present for the field "//trim(varname)// &
" of file "//trim(fileObj%name)//", but none of p2dr, p3dr, p2di and p3di is associated")
end if
else
if( Associated(fileObj%p0dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p0dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p0dr(k,j)%p, (/mpp_pe()/) )
else if( Associated(fileObj%p1dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p1dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data = mpp_chksum(fileObj%p1dr(k,j)%p, (/mpp_pe()/) )
else if( Associated(fileObj%p2dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p2dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data =&
& mpp_chksum(fileObj%p2dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
else if( Associated(fileObj%p3dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p3dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data =&
& mpp_chksum(fileObj%p3dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :) )
else if( Associated(fileObj%p4dr(k,j)%p) ) then
call mpp_read(unit(n), fields(l), fileObj%p4dr(k,j)%p, tlev)
if ( is_there_a_checksum ) checksum_data =&
& mpp_chksum(fileObj%p4dr(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :, :) )
else if( Associated(fileObj%p0di(k,j)%p) ) then
call mpp_read(unit(n), fields(l), r0d, tlev)
fileObj%p0di(k,j)%p = r0d
if ( is_there_a_checksum ) checksum_data = fileObj%p0di(k,j)%p
else if( Associated(fileObj%p1di(k,j)%p) ) then
allocate(r1d(cur_var%siz(1)) )
call mpp_read(unit(n), fields(l), r1d, tlev)
fileObj%p1di(k,j)%p = r1d
if ( is_there_a_checksum ) checksum_data = fileObj%p0di(k,j)%p
deallocate(r1d)
else if( Associated(fileObj%p2di(k,j)%p) ) then
allocate(r2d(cur_var%siz(1), cur_var%siz(2)) )
r2d = 0
call mpp_read(unit(n), fields(l), r2d, tlev)
fileObj%p2di(k,j)%p = r2d
if ( is_there_a_checksum ) checksum_data =&
& mpp_chksum(fileObj%p2di(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd) )
deallocate(r2d)
else if( Associated(fileObj%p3di(k,j)%p) ) then
allocate(r3d(cur_var%siz(1), cur_var%siz(2), cur_var%siz(3)) )
r3d = 0
call mpp_read(unit(n), fields(l), r3d, tlev)
fileObj%p3di(k,j)%p = r3d
if ( is_there_a_checksum ) checksum_data =&
& mpp_chksum(fileObj%p3di(k,j)%p(cur_var%is:cur_var%is+iadd,cur_var%js:cur_var%js+jadd, :))
deallocate(r3d)
else
call mpp_error(FATAL, "fms_io(restore_state_one_field): There is no pointer "// &
"associated with the data of field "//trim(varname)//" of file "//trim(fileObj%name) )
end if
end if
if ( (is_there_a_checksum ) .and. (checksum_file(k) /= checksum_data) ) then
write (mesg,'(a,Z16,a,Z16,a)') "Checksum of input field "// uppercase(trim(varname)), checksum_data,&
" does not match value ", checksum_file(k), "stored in "//uppercase(trim(fileObj%name)//"." )
call mpp_error(FATAL, "fms_io(restore_state_one_field): "//trim(mesg) )
endif
end do
exit ! Start search for next restart variable.
endif
enddo
if (l>nvar) missing_fields = missing_fields+1
deallocate(fields)
if (missing_fields == 0) exit
enddo
do n=1,nfile
call close_file(unit(n))
enddo
! check whether the field have been found
if( .NOT. fileObj%var(id_field)%initialized ) then
if( fileObj%var(id_field)%mandatory ) then
call mpp_error(FATAL, "fms_io(restore_state_one_field): unable to find mandatory variable "// &
trim(fileObj%var(id_field)%name)//" in restart file "//trim(fileObj%name) )
end if
end if
cur_var =>NULL()
end subroutine restore_state_one_field
!-------------------------------------------------------------------------------
!
! This routine will setup one entry to be written out
!
!-------------------------------------------------------------------------------
subroutine setup_one_field(fileObj, filename, fieldname, field_siz, index_field, domain, mandatory, &
no_domain, scalar_or_1d, position, tile_count, data_default, longname, units, &
compressed_axis, read_only, owns_data)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:), intent(in) :: field_siz
integer, intent(out) :: index_field
type(domain2d), optional, intent(in), target :: domain
real, optional, intent(in) :: data_default
logical, optional, intent(in) :: no_domain
logical, optional, intent(in) :: scalar_or_1d
integer, optional, intent(in) :: position, tile_count
logical, optional, intent(in) :: mandatory
character(len=*), optional, intent(in) :: longname, units, compressed_axis
logical, optional, intent(in) :: owns_data !data will be deallocated on dellocation of restart
logical, optional, intent(in) :: read_only !The variable will not be written to restart file.
!--- local variables
integer :: i, domain_idx
integer :: ishift, jshift
integer :: gxsize, gysize
integer :: cxsize, cysize
integer :: dxsize, dysize
real :: default_data
logical :: is_no_domain = .false.
logical :: is_scalar_or_1d = .false.
character(len=256) :: fname, filename2, append_string
type(domain2d), pointer, save :: d_ptr =>NULL()
type(var_type), pointer, save :: cur_var =>NULL()
integer :: length, n_field_siz
if(ANY(field_siz < 0)) then
call mpp_error(FATAL, "fms_io(setup_one_field): each entry of field_size should be a non-negative integer")
end if
if(PRESENT(data_default))then
default_data=data_default
else
default_data = MPP_FILL_DOUBLE
endif
if(present(tile_count) .AND. .not. present(domain)) call mpp_error(FATAL, &
'fms_io(setup_one_field): when tile_count is present, domain must be present')
is_scalar_or_1d = .false.
if(PRESENT(scalar_or_1d)) is_scalar_or_1d = scalar_or_1d
is_no_domain = .false.
if (PRESENT(no_domain)) THEN
is_no_domain = no_domain
end if
if(is_no_domain) then
if(PRESENT(domain)) &
call mpp_error(FATAL, 'fms_io(setup_one_field): no_domain cannot be .true. when optional argument domain is present.')
else if(PRESENT(domain))then
d_ptr => domain
else if (ASSOCIATED(Current_domain)) then
d_ptr => Current_domain
endif
!--- remove .nc from file name
length = len_trim(filename)
if(filename(length-2:length) == '.nc') then
filename2 = filename(1:length-3)
else
filename2 = filename(1:length)
end if
!Append a string to the file name
append_string=''
!If the filename_appendix is set override the passed argument.
if(len_trim(filename_appendix) > 0) append_string = filename_appendix
if(len_trim(append_string) > 0) filename2 = trim(filename2)//'.'//trim(append_string)
!JWD: This is likely a temporary fix. Since fms_io needs to know tile_count,
!JWD: I just don't see how the physics can remain "tile neutral"
!z1l: one solution is add one more public interface called set_tile_count
call get_mosaic_tile_file(filename2, fname, is_no_domain, domain, tile_count)
if(Associated(fileObj%var) ) then
! make sure the consistency of file name
if(trim(fileObj%name) .NE. trim(fname)) call mpp_error(FATAL, 'fms_io(setup_one_field): filename = '// &
trim(fname)//' is not consistent with the filename of the restart object = '//trim(fileObj%name) )
else
allocate(fileObj%var(max_fields) )
allocate(fileObj%p0dr(MAX_TIME_LEVEL_REGISTER, max_fields))
allocate(fileObj%p1dr(MAX_TIME_LEVEL_REGISTER, max_fields))
allocate(fileObj%p2dr(MAX_TIME_LEVEL_REGISTER, max_fields))
allocate(fileObj%p3dr(MAX_TIME_LEVEL_REGISTER, max_fields))
allocate(fileObj%p2dr8(MAX_TIME_LEVEL_REGISTER, max_fields))
allocate(fileObj%p3dr8(MAX_TIME_LEVEL_REGISTER, max_fields))
allocate(fileObj%p4dr(MAX_TIME_LEVEL_REGISTER, max_fields))
allocate(fileObj%p0di(MAX_TIME_LEVEL_REGISTER, max_fields))
allocate(fileObj%p1di(MAX_TIME_LEVEL_REGISTER, max_fields))
allocate(fileObj%p2di(MAX_TIME_LEVEL_REGISTER, max_fields))
allocate(fileObj%p3di(MAX_TIME_LEVEL_REGISTER, max_fields))
!--- make sure fname is not used in other restart_file_type object.
do i = 1, num_registered_files
if(trim(fname) == trim(registered_file(i)) ) then
call mpp_error(NOTE, &
'fms_io(setup_one_field): '//trim(fname)//' is already registered with other restart_file_type data')
exit
endif
end do
num_registered_files = num_registered_files + 1
if( num_registered_files > max_files_w ) call mpp_error(WARNING, &
'fms_io(setup_one_field): num_registered_files > max_files_w, increase fms_io_nml max_files_w')
registered_file(num_registered_files) = trim(fname)
fileObj%register_id = num_registered_files
fileObj%name = trim(fname)
fileObj%tile_count=1
if(present(tile_count)) fileObj%tile_count = tile_count
if(ASSOCIATED(d_ptr))then
fileObj%is_root_pe = mpp_domain_is_tile_root_pe(d_ptr)
else
fileObj%is_root_pe = mpp_pe() == mpp_root_pe()
endif
fileObj%max_ntime = field_siz(4)
fileObj%nvar = 0
!-- allocate memory
do i = 1, max_fields
fileObj%var(i)%name = 'none'
fileObj%var(i)%domain_present = .false.
fileObj%var(i)%domain_idx = -1
fileObj%var(i)%is_dimvar = .false.
fileObj%var(i)%position = CENTER
fileObj%var(i)%siz(:) = 0
fileObj%var(i)%gsiz(:) = 0
fileObj%var(i)%id_axes(:) = -1
fileObj%var(i)%longname = '';
fileObj%var(i)%units = 'none';
fileObj%var(i)%mandatory = .true.
fileObj%var(i)%initialized = .false.
fileObj%var(i)%compressed_axis = ''
fileObj%var(i)%read_only = .false.
fileObj%var(i)%owns_data = .false.
end do
endif
! check if the field is new or not and get position and dimension of the field
index_field = -1
do i = 1, fileObj%nvar
if(trim(fileObj%var(i)%name) == trim(fieldname)) then
index_field = i
exit
end if
end do
if(index_field > 0) then
cur_var => fileObj%var(index_field)
if(cur_var%siz(1) .NE. field_siz(1) .OR. cur_var%siz(2) .NE. field_siz(2) .OR. cur_var%siz(3) .NE. field_siz(3) ) &
call mpp_error(FATAL, 'fms_io(setup_one_field): field size mismatch for field '// &
trim(fieldname)//' of file '//trim(filename) )
cur_var%siz(4) = cur_var%siz(4) + field_siz(4)
if(fileObj%max_ntime < cur_var%siz(4) ) fileObj%max_ntime = cur_var%siz(4)
! the time level should be no larger than MAX_TIME_LEVEL_REGISTER ( = 2)
if( cur_var%siz(4) > MAX_TIME_LEVEL_REGISTER ) call mpp_error(FATAL, 'fms_io(setup_one_field): ' // &
'the time level of field '//trim(cur_var%name)//' in file '//trim(fileObj%name)// &
' is greater than MAX_TIME_LEVEL_REGISTER(=2), increase MAX_TIME_LEVEL_REGISTER or check your code')
else
fileObj%nvar = fileObj%nvar +1
if(fileObj%nvar>max_fields) then
write(error_msg,'(I3,"/",I3)') fileObj%nvar, max_fields
call mpp_error(FATAL,'fms_io(setup_one_field): max_fields exceeded, needs increasing, nvar/max_fields=' &
//trim(error_msg))
endif
index_field = fileObj%nvar
cur_var => fileObj%var(index_field)
n_field_siz = size(field_siz(:))
cur_var%siz(1:n_field_siz) = field_siz(1:n_field_siz)
cur_var%gsiz(3) = field_siz(3)
if(n_field_siz == 5) cur_var%gsiz(4) = field_siz(5)
cur_var%name = fieldname
cur_var%default_data = default_data
if(present(mandatory)) cur_var%mandatory = mandatory
if(present(read_only)) cur_var%read_only = read_only
if(present(owns_data)) cur_var%owns_data = owns_data
if(present(longname)) then
cur_var%longname = longname
else
cur_var%longname = fieldname
end if
if(present(units)) cur_var%units = units
if(present(position)) cur_var%position = position
if(present(compressed_axis)) cur_var%compressed_axis = compressed_axis
cur_var%is = 1; cur_var%ie = cur_var%siz(1)
cur_var%js = 1; cur_var%je = cur_var%siz(2)
if(ASSOCIATED(d_ptr) .AND. .NOT. is_scalar_or_1d ) then
cur_var%domain_present = .true.
domain_idx = lookup_domain(d_ptr)
if(domain_idx == -1) then
num_domains = num_domains + 1
if(num_domains > max_domains) call mpp_error(FATAL,'fms_io(setup_one_field), 1: max_domains exceeded,' &
//' needs increasing')
domain_idx = num_domains
array_domain(domain_idx) = d_ptr
call mpp_get_domain_components(array_domain(domain_idx), domain_x(domain_idx), domain_y(domain_idx), &
tile_count=tile_count)
endif
cur_var%domain_idx = domain_idx
call mpp_get_domain_shift ( array_domain(domain_idx), ishift, jshift, position)
call mpp_get_global_domain(array_domain(domain_idx), xsize=gxsize,ysize=gysize,tile_count=tile_count)
call mpp_get_compute_domain(array_domain(domain_idx), xsize = cxsize, ysize = cysize, tile_count=tile_count)
call mpp_get_data_domain (array_domain(domain_idx), xsize = dxsize, ysize = dysize, tile_count=tile_count)
if (ishift .NE. 0) then
cxsize = cxsize+ishift; dxsize = dxsize+ishift; gxsize = gxsize + ishift
end if
if (jshift .NE. 0) then
cysize = cysize+jshift; dysize = dysize+jshift; gysize = gysize + jshift
endif
if( (cur_var%siz(1) .NE. cxsize .AND. cur_var%siz(1) .NE. dxsize ) .OR. &
(cur_var%siz(2) .NE. cysize .AND. cur_var%siz(2) .NE. dysize ) ) then
call mpp_error(FATAL, 'fms_io(setup_one_field): data should be on either compute domain '//&
'or data domain when domain is present for field '//trim(fieldname)//' of file '//trim(filename) )
end if
cur_var%is = 1 + (cur_var%siz(1) - cxsize)/2
cur_var%ie = cur_var%is + cxsize - 1;
cur_var%js = 1 + (cur_var%siz(2) - cysize)/2
cur_var%je = cur_var%js + cysize - 1;
cur_var%gsiz(1) = gxsize
cur_var%gsiz(2) = gysize
else
cur_var%domain_present=.false.
cur_var%gsiz(1:2) = field_siz(1:2)
endif
end if
d_ptr =>NULL()
cur_var =>NULL()
end subroutine setup_one_field
!.....................................................................
subroutine write_data_4d_new(filename, fieldname, data, domain, &
no_domain, position,tile_count, data_default)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:,:,:), intent(in) :: data
real, dimension(size(data,1),size(data,2),size(data,3)*size(data,4)) :: data_3d
real, intent(in), optional :: data_default
type(domain2d), intent(in), optional :: domain
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: position, tile_count
integer :: i, k, l
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(write_data_4d_new):need to call fms_io_init first')
i = 0
do l = 1, size(data,4) ; do k = 1, size(data,3)
i = i + 1
data_3d(:,:,i) = data(:,:,k,l)
enddo ; enddo
call write_data_3d_new(filename, fieldname, data_3d, domain, &
no_domain, .false., position, tile_count, data_default)
end subroutine write_data_4d_new
!.....................................................................
subroutine write_data_2d_new(filename, fieldname, data, domain, &
no_domain, position,tile_count, data_default)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:), intent(in) :: data
real, dimension(size(data,1),size(data,2),1) :: data_3d
real, intent(in), optional :: data_default
type(domain2d), intent(in), optional :: domain
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: position, tile_count
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(write_data_2d_new):need to call fms_io_init first')
data_3d(:,:,1) = data(:,:)
call write_data_3d_new(filename, fieldname, data_3d, domain, &
no_domain, .false., position, tile_count, data_default)
end subroutine write_data_2d_new
! ........................................................
subroutine write_data_1d_new(filename, fieldname, data,domain, &
no_domain, tile_count, data_default)
type(domain2d), intent(in), optional :: domain
character(len=*), intent(in) :: filename, fieldname
real, dimension(:), intent(in) :: data
real, dimension(size(data(:)),1,1) :: data_3d
real, intent(in), optional :: data_default
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: tile_count
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(write_data_1d_new): module not initialized')
data_3d(:,1,1) = data(:)
call write_data_3d_new(filename, fieldname, data_3d,domain, &
no_domain=no_domain, scalar_or_1d=.true., tile_count=tile_count, data_default=data_default)
end subroutine write_data_1d_new
! ..........................................................
subroutine write_data_scalar_new(filename, fieldname, data, domain, &
no_domain, tile_count, data_default)
type(domain2d), intent(in), optional :: domain
character(len=*), intent(in) :: filename, fieldname
real, intent(in) :: data
real, dimension(1,1,1) :: data_3d
real, intent(in), optional :: data_default
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: tile_count
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(write_data_scalar_new): module not initialized: '//fieldname)
data_3d(1,1,1) = data
call write_data_3d_new(filename, fieldname, data_3d,domain, &
no_domain=no_domain, scalar_or_1d=.true., tile_count=tile_count, data_default=data_default)
end subroutine write_data_scalar_new
! ..........................................................
function lookup_field_r(nfile,fieldname)
! Given fieldname, this function returns the field position in the model's fields list
integer, intent(in) :: nfile
character(len=*), intent(in) :: fieldname
integer :: lookup_field_r
integer :: j
lookup_field_r=-1
do j = 1, files_read(nfile)%nvar
if (trim(files_read(nfile)%var(j)%name) == trim(fieldname)) then
lookup_field_r = j
exit
endif
enddo
return
end function lookup_field_r
!..........................................................
function lookup_domain(domain)
! given domain, this function returns the position of domain in array_domain or -1 if not found
type(domain2d), intent(in) :: domain
integer :: i, lookup_domain
lookup_domain = -1
do i =1, num_domains
if(domain .EQ. array_domain(i)) then
lookup_domain = i
exit
endif
enddo
end function lookup_domain
!.........................................................
function lookup_axis(axis_sizes,siz,domains,dom)
! Given axis size (global), this function returns the axis id
integer, intent(in) :: axis_sizes(:), siz
type(domain1d), optional :: domains(:)
type(domain1d), optional :: dom
integer :: lookup_axis
integer :: j
lookup_axis=-1
do j=1,size(axis_sizes(:))
if (siz == axis_sizes(j)) then
if (PRESENT(domains)) then
if (dom .EQ. domains(j)) then
lookup_axis = j
exit
endif
else
lookup_axis = j
exit
endif
endif
enddo
if (lookup_axis == -1) call mpp_error(FATAL,'fms_io(lookup_axis): could not find axis in set of axes')
end function lookup_axis
!.....................................................................
!
!
! Given filename and fieldname, this subroutine returns the size of field
!
!
! call field_size(filename, fieldname, siz)
!
!
! File name
!
!
! Field name
!
!
! siz must be a dimension(4) array to retrieve the size of the field
!
!
! if this flag is present, field_size will not abort if
! called for a non-existent field.
! Instead it will return T or F depending on
! whether or not the field was found.
!
subroutine field_size(filename, fieldname, siz, field_found, domain, no_domain )
character(len=*), intent(in) :: filename, fieldname
integer, intent(inout) :: siz(:)
logical, intent(out), optional :: field_found
type(domain2d), intent(in), optional, target :: domain
logical, intent(in), optional :: no_domain
integer :: nfile, unit
logical :: found, found_file
character(len=256) :: actual_file
logical :: read_dist, io_domain_exist, is_no_domain
if (size(siz(:)) < 4) call mpp_error(FATAL,'fms_io(field_size): size array must be >=4 to receive field size of ' &
//trim(fieldname)//' in file '// trim(filename))
is_no_domain = .false.
if(present(no_domain)) is_no_domain = no_domain
!--- first need to get the filename, when is_no_domain is true, only check file without tile
!--- if is_no_domain is false, first check no_domain=.false., then check no_domain = .true.
found_file = get_file_name(filename, actual_file, read_dist, io_domain_exist, no_domain=is_no_domain, &
domain=domain)
!--- when is_no_domain is true and file is not found, send out error message.
if(is_no_domain .AND. .NOT. found_file) call mpp_error(FATAL, &
'fms_io_mod(field_size): file '//trim(filename)//' and corresponding distributed file are not found')
found = .false.
if(found_file) then
call get_file_unit(actual_file, unit, nfile, read_dist, io_domain_exist, domain=domain)
call get_size(unit,fieldname,siz,found)
endif
if(.not.found .AND. .not. is_no_domain) then
found_file = get_file_name(filename, actual_file, read_dist, io_domain_exist, no_domain=.true.)
if(found_file) then
call get_file_unit(actual_file, unit, nfile, read_dist, io_domain_exist, domain=domain)
call get_size(unit,fieldname,siz,found)
endif
endif
! If field_found is present we assume that it is being checked on exit.
! If not present and the field was not found, exit with a FATAL error.
if( PRESENT(field_found) )then
field_found = found
else if (.not. found )then
call mpp_error(FATAL, 'fms_io(field_size): field '//trim(fieldname)//' NOT found in file '//trim(actual_file))
end if
return
end subroutine field_size
!
subroutine file_unit(filename, found_file, unit, domain, no_domain)
character(len=*), intent(in) :: filename
logical, intent(out) :: found_file
integer, intent(out) :: unit
type(domain2d), intent(in), optional, target :: domain
logical, intent(in), optional :: no_domain
integer :: nfile
character(len=256) :: actual_file
logical :: read_dist, io_domain_exist, is_no_domain
is_no_domain = .false.
if(present(no_domain)) is_no_domain = no_domain
!--- first need to get the filename, when is_no_domain is true, only check file without tile
!--- if is_no_domain is false, first check no_domain=.false., then check no_domain = .true.
found_file = get_file_name(filename, actual_file, read_dist, io_domain_exist, no_domain=is_no_domain, &
domain=domain)
!--- when is_no_domain is true and file is not found, send out error message.
if(is_no_domain .AND. .NOT. found_file) call mpp_error(FATAL, &
'fms_io_mod(field_size): file '//trim(filename)//' and corresponding distributed file are not found')
if(found_file) then
call get_file_unit(actual_file, unit, nfile, read_dist, io_domain_exist, domain=domain)
else if(.not. is_no_domain) then
found_file = get_file_name(filename, actual_file, read_dist, io_domain_exist, no_domain=.true.)
if(found_file) then
call get_file_unit(actual_file, unit, nfile, read_dist, io_domain_exist, domain=domain)
endif
endif
return
end subroutine file_unit
!.....................................................................
!
!
! Given filename and dimension name, this function returns the size of field
!
!
! dimsize = dimension_size(filename, dimensionname)
!
!
! File name
!
!
! Field name
!
function dimension_size(filename, dimname, domain, no_domain )
character(len=*), intent(in) :: filename, dimname
type(domain2d), intent(in), optional, target :: domain
logical, intent(in), optional :: no_domain
integer :: dimension_size
integer :: nfile, unit
logical :: found, found_file
character(len=256) :: actual_file
logical :: read_dist, io_domain_exist, is_no_domain
is_no_domain = .false.
if(present(no_domain)) is_no_domain = no_domain
!--- first need to get the filename, when is_no_domain is true, only check file without tile
!--- if is_no_domain is false, first check no_domain=.false., then check no_domain = .true.
found_file = get_file_name(filename, actual_file, read_dist, io_domain_exist, no_domain=is_no_domain, &
domain=domain)
!--- when is_no_domain is true and file is not found, send out error message.
if(is_no_domain .AND. .NOT. found_file) call mpp_error(FATAL, &
'fms_io_mod(dimesion_size): file '//trim(filename)//' and corresponding distributed file are not found')
found = .false.
if(found_file) then
call get_file_unit(actual_file, unit, nfile, read_dist, io_domain_exist, domain=domain)
dimension_size = mpp_get_dimension_length(unit, dimname, found)
endif
if(.not.found .AND. .not. is_no_domain) then
found_file = get_file_name(filename, actual_file, read_dist, io_domain_exist, no_domain=.true.)
if(found_file) then
call get_file_unit(actual_file, unit, nfile, read_dist, io_domain_exist, domain=domain)
dimension_size = mpp_get_dimension_length(unit, dimname, found)
endif
endif
if(.not. found) call mpp_error(FATAL, &
'fms_io_mod(dimesion_size): failed at inquiring size of dimesion '//trim(dimname)//' from file '//trim(filename))
return
end function dimension_size
!
!.....................................................................
!
!
! Given filename and fieldname, this subroutine returns the size of field
! This is the io subset interface to field_size
!
!
! call field_size(filename, fieldname, siz)
!
!
! File name
!
!
! Field name
!
!
! siz must be a dimension(4) array to retrieve the size of the field
!
!
! if this flag is present, field_size will not abort if
! called for a non-existent field.
! Instead it will return T or F depending on
! whether or not the field was found.
!
subroutine get_field_size(filename, fieldname, siz, field_found, domain, no_domain)
character(len=*), intent(in) :: filename, fieldname
integer, intent(inout) :: siz(:)
logical, intent(out), optional :: field_found
type(domain2d), intent(in), optional, target :: domain
logical, intent(in), optional :: no_domain
integer :: npes, p, unit
integer, allocatable :: pelist(:)
logical :: found, found_file
type(domain2d), pointer :: domain_in =>NULL()
type(domain2d), pointer :: io_domain =>NULL()
if(PRESENT(domain)) then
domain_in =>domain
elseif(ASSOCIATED(current_domain)) then
domain_in =>current_domain
else
call mpp_error(FATAL,'fms_io(get_field_size): The domain must be defined')
endif
io_domain =>mpp_get_io_domain(domain)
if(.not. ASSOCIATED(io_domain)) call mpp_error(FATAL,'fms_io(get_field_size): The io domain must be defined')
npes = mpp_get_domain_npes(io_domain)
allocate(pelist(npes))
call mpp_get_pelist(io_domain,pelist)
call file_unit(filename, found_file, unit, domain, no_domain)
if(mpp_pe() == pelist(1)) then
found=.false.
if(found_file) call get_size(unit,fieldname,siz,found)
if(.not. found) siz(:) = -1
endif
!--- z1l replace mpp_broadcast with mpp_send/mpp_recv to avoid hang in calling MPI_COMM_CREATE
!--- because size(pelist) might be different for different rank.
!--- prepost receive
if( mpp_pe() == pelist(1) ) then
do p = 2, npes
call mpp_send(siz(1), plen=size(siz(:)), to_pe=pelist(p), tag=COMM_TAG_1)
enddo
call mpp_sync_self()
else
call mpp_recv(siz(1), glen=size(siz(:)), from_pe=pelist(1), block=.false., tag=COMM_TAG_1)
call mpp_sync_self(check=EVENT_RECV)
endif
found = .true.
if(siz(1) == -1) found=.false.
! If field_found is present we assume that it is being checked on exit.
! If not present and the field was not found, exit with a FATAL error.
if( PRESENT(field_found) )then
field_found = found
else if (.not. found )then
call mpp_error(FATAL, 'fms_io(field_size): field '//trim(fieldname)//' NOT found in file '//trim(filename))
endif
end subroutine get_field_size
!
subroutine get_size(unit, fieldname, siz, found)
integer, intent(in) :: unit
character(len=*), intent(in) :: fieldname
integer, intent(inout) :: siz(:)
logical, intent(out) :: found
character(len=128) :: name
character(len=1) :: cart
integer :: i, ndim, nvar, natt, ntime, siz_in(4), j, len
type(fieldtype) :: fields(max_fields)
type(axistype) :: axes(max_fields)
found = .false.
call mpp_get_info(unit,ndim,nvar,natt,ntime)
if (nvar > max_fields) then
write(error_msg,'(I3,"/",I3)') nvar,max_fields
call mpp_error(FATAL,'fms_io(field_size): max_fields too small, needs increasing, nvar/max_fields=' &
//trim(error_msg))!//' in file '//trim(filename))
endif
call mpp_get_fields(unit,fields(1:nvar))
do i=1, nvar
call mpp_get_atts(fields(i),name=name)
if (lowercase(trim(name)) == lowercase(trim(fieldname))) then
call mpp_get_atts(fields(i),ndim=ndim)
call mpp_get_atts(fields(i),axes=axes(1:ndim))
call mpp_get_atts(fields(i),siz=siz_in)
siz = siz_in
siz(4) = ntime
if(ndim == 1) then
call mpp_get_atts(axes(1), len=siz(1))
end if
do j = 1, ndim
call mpp_get_atts(axes(j),len=len)
call get_axis_cart(axes(j),cart)
select case (cart)
case ('X')
siz(1) = len
case('Y')
siz(2) = len
case('Z')
siz(3) = len
case('T')
siz(4) = len
end select
enddo
found = .true.
exit
endif
enddo
if(.not. found) then
call mpp_get_axes(unit,axes(1:ndim))
do i=1, ndim
call mpp_get_atts(axes(i),name=name, len= siz_in(1))
if (lowercase(trim(name)) == lowercase(trim(fieldname))) then
siz(1)= siz_in(1)
found = .true.
exit
endif
enddo
endif
end subroutine get_size
!
!
! This routine performs reading "fieldname" stored in "filename". The data values of fieldname
! will be stored in "data" at the end of this routine. For fieldname with multiple timelevel
! just repeat the routine with explicit timelevel in each call.
!
!
! call read_data(filename,fieldname,data,domain,timelevel)
!
!
! File name
!
!
! Field name
!
!
! domain of fieldname
!
!
! time level of fieldname
!
!
! array containing data of fieldname
!
!=====================================================================================
subroutine read_data_i3d_new(filename,fieldname,data,domain,timelevel, &
no_domain,position, tile_count)
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:,:,:), intent(inout) :: data ! 3 dimensional data
type(domain2d), intent(in), optional :: domain
integer, intent(in), optional :: timelevel
logical, intent(in), optional :: no_domain
integer, intent(in) , optional :: position, tile_count
real, dimension(size(data,1),size(data,2),size(data,3)) :: r_data
r_data = 0
call read_data_3d_new(filename,fieldname,r_data,domain,timelevel, &
no_domain, .false., position, tile_count)
data = CEILING(r_data)
end subroutine read_data_i3d_new
subroutine read_data_i2d_new(filename,fieldname,data,domain,timelevel, &
no_domain,position, tile_count)
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:,:), intent(inout) :: data ! 2 dimensional data
type(domain2d), intent(in), optional :: domain
integer, intent(in), optional :: timelevel
logical, intent(in), optional :: no_domain
integer, intent(in) , optional :: position, tile_count
real, dimension(size(data,1),size(data,2)) :: r_data
r_data = 0
call read_data_2d_new(filename,fieldname,r_data,domain,timelevel, &
no_domain, position, tile_count)
data = CEILING(r_data)
end subroutine read_data_i2d_new
!.....................................................................
subroutine read_data_i1d_new(filename,fieldname,data,domain,timelevel, &
no_domain, tile_count)
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:), intent(inout) :: data ! 1 dimensional data
type(domain2d), intent(in), optional :: domain
integer, intent(in) , optional :: timelevel
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: tile_count
real, dimension(size(data,1)) :: r_data
call read_data_1d_new(filename,fieldname,r_data,domain,timelevel, &
no_domain, tile_count)
data = CEILING(r_data)
end subroutine read_data_i1d_new
!.....................................................................
subroutine read_data_iscalar_new(filename,fieldname,data,domain,timelevel, &
no_domain, tile_count)
character(len=*), intent(in) :: filename, fieldname
integer, intent(inout) :: data
type(domain2d), intent(in), optional :: domain
integer, intent(in) , optional :: timelevel
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: tile_count
real :: r_data
call read_data_scalar_new(filename,fieldname,r_data,domain,timelevel, &
no_domain, tile_count)
data = CEILING(r_data)
end subroutine read_data_iscalar_new
!=====================================================================================
subroutine read_data_3d_new(filename,fieldname,data,domain,timelevel, &
no_domain, scalar_or_1d, position, tile_count)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:,:), intent(inout) :: data ! 3 dimensional data
type(domain2d), target, optional, intent(in) :: domain
integer, optional, intent(in) :: timelevel
logical, optional, intent(in) :: no_domain
logical, optional, intent(in) :: scalar_or_1d
integer, optional, intent(in) :: position, tile_count
character(len=256) :: fname
integer :: unit, siz_in(4)
integer :: file_index ! index of the opened file in array files
integer :: tlev=1
integer :: index_field ! position of the fieldname in the list of variables
integer :: cxsize, cysize
integer :: dxsize, dysize
integer :: gxsize, gysize
integer :: ishift, jshift
logical :: is_scalar_or_1d = .false.
logical :: is_no_domain = .false.
logical :: read_dist, io_domain_exist, found_file
type(domain2d), pointer, save :: d_ptr =>NULL()
type(domain2d), pointer, save :: io_domain =>NULL()
! read disttributed files is used when reading restart files that are NOT mppnccombined. In this
! case PE 0 will read file_res.nc.0000, PE 1 will read file_res.nc.0001 and so forth.
!
! Initialize files to default values
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_data_3d_new): module not initialized')
is_no_domain = .false.
if (PRESENT(no_domain)) THEN
if(PRESENT(domain) .AND. no_domain) &
call mpp_error(FATAL, 'fms_io(read_data_3d_new): no_domain cannot be .true. when optional argument domain is present.')
is_no_domain = no_domain
endif
if(PRESENT(domain))then
d_ptr => domain
elseif (ASSOCIATED(Current_domain) .AND. .NOT. is_no_domain ) then
d_ptr => Current_domain
endif
is_scalar_or_1d = .false.
if(present(scalar_or_1d)) is_scalar_or_1d = scalar_or_1d
if(.not. PRESENT(domain) .and. .not. ASSOCIATED(Current_domain) ) is_no_domain = .true.
found_file = get_file_name(filename, fname, read_dist, io_domain_exist, is_no_domain, domain, tile_count)
if(.not.found_file) call mpp_error(FATAL, 'fms_io_mod(read_data_3d_new): file ' //trim(filename)// &
'(with the consideration of tile number) and corresponding distributed file are not found')
call get_file_unit(fname, unit, file_index, read_dist, io_domain_exist, domain=domain)
siz_in(3) = size(data,3)
if(is_no_domain .or. .NOT. associated(d_ptr) .or. is_scalar_or_1d) then
gxsize = size(data,1)
gysize = size(data,2)
else if(read_dist) then
if(io_domain_exist) then
io_domain=>mpp_get_io_domain(d_ptr)
call mpp_get_global_domain(io_domain, xsize = gxsize, ysize = gysize, tile_count=tile_count, position=position)
io_domain=>NULL()
else
call mpp_get_compute_domain(d_ptr, xsize = gxsize, ysize = gysize, tile_count=tile_count, position=position)
endif
else
call mpp_get_compute_domain(d_ptr, xsize = cxsize, ysize = cysize, tile_count=tile_count, position=position)
call mpp_get_data_domain (d_ptr, xsize = dxsize, ysize = dysize, tile_count=tile_count, position=position)
call mpp_get_global_domain (d_ptr, xsize = gxsize, ysize = gysize, tile_count=tile_count, position=position)
call mpp_get_domain_shift (d_ptr, ishift, jshift, position)
if( (size(data,1) .NE. cxsize .AND. size(data,1) .NE. dxsize) .OR. &
(size(data,2) .NE. cysize .AND. size(data,2) .NE. dysize) )then
call mpp_error(FATAL,'fms_io(read_data_3d_new): data should be on either compute domain '//&
'or data domain when domain is present. '//&
'shape(data)=',shape(data),' cxsize,cysize,dxsize,dysize=',(/cxsize,cysize,dxsize,dysize/))
end if
endif
if (PRESENT(timelevel)) then
tlev = timelevel
else
tlev = 1
endif
call get_field_id(unit, file_index, fieldname, index_field, is_no_domain, .false. )
siz_in(1:4) = files_read(file_index)%var(index_field)%siz(1:4)
if(files_read(file_index)%var(index_field)%is_dimvar ) then
if (.not. read_dist) then
if (siz_in(1) /= gxsize) &
call mpp_error(FATAL,'fms_io(read_data_3d_new), field '//trim(fieldname)// &
' in file '//trim(filename)//' field size mismatch 2')
endif
else
if (siz_in(1) /= gxsize .or. siz_in(2) /= gysize .or. siz_in(3) /= size(data,3)) then
PRINT *, gxsize, gysize, size(data, 3), siz_in(1), siz_in(2), siz_in(3)
call mpp_error(FATAL,'fms_io(read_data_3d_new), field '//trim(fieldname)// &
' in file '//trim(filename)//': field size mismatch 1')
endif
end if
if ( tlev < 1 .or. files_read(file_index)%max_ntime < tlev) then
write(error_msg,'(I5,"/",I5)') tlev, files_read(file_index)%max_ntime
call mpp_error(FATAL,'fms_io(read_data_3d_new): time level out of range, time level/max_time_level=' &
//trim(error_msg)//' in field/file: '//trim(fieldname)//'/'//trim(filename))
endif
if(is_no_domain .OR. is_scalar_or_1d) then
if (files_read(file_index)%var(index_field)%is_dimvar) then
call mpp_get_axis_data(files_read(file_index)%var(index_field)%axis,data(:,1,1))
else
call mpp_read(unit,files_read(file_index)%var(index_field)%field,data(:,:,:),tlev)
endif
else
call mpp_read(unit,files_read(file_index)%var(index_field)%field,d_ptr,data,tlev,tile_count)
endif
d_ptr =>NULL()
return
end subroutine read_data_3d_new
!=====================================================================================
subroutine read_compressed_i1d(filename,fieldname,data,domain,timelevel,start,nread,threading)
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:), intent(inout) :: data ! 1 dimensional data
type(domain2d), intent(in), optional :: domain
integer, intent(in) , optional :: timelevel
integer, intent(in) , optional :: start(:), nread(:)
integer, intent(in) , optional :: threading
real, dimension(size(data)) :: r_data
r_data = 0.0
call read_compressed_1d(filename,fieldname,r_data,domain,timelevel,start,nread,threading)
data = CEILING(r_data)
end subroutine read_compressed_i1d
!.....................................................................
subroutine read_compressed_i2d(filename,fieldname,data,domain,timelevel,start,nread,threading)
character(len=*), intent(in) :: filename, fieldname
integer, dimension(:,:), intent(inout) :: data ! 2 dimensional data
type(domain2d), intent(in), optional :: domain
integer, intent(in), optional :: timelevel
integer, intent(in) , optional :: start(:), nread(:)
integer, intent(in) , optional :: threading
real, dimension(size(data,1),size(data,2)) :: r_data
r_data = 0.0
call read_compressed_2d(filename,fieldname,r_data,domain,timelevel,start,nread,threading)
data = CEILING(r_data)
end subroutine read_compressed_i2d
!.....................................................................
subroutine read_compressed_1d(filename,fieldname,data,domain,timelevel,start,nread,threading)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:), intent(inout) :: data !1 dimensional data
real, dimension(size(data,1),1) :: data_2d
type(domain2d), intent(in), optional :: domain
integer, intent(in) , optional :: timelevel
integer, intent(in) , optional :: start(:), nread(:)
integer, intent(in) , optional :: threading
pointer( p, data_2d )
p = LOC(data)
call read_compressed_2d(filename,fieldname,data_2d,domain,timelevel,start,nread,threading)
end subroutine read_compressed_1d
!.....................................................................
subroutine read_compressed_2d(filename,fieldname,data,domain,timelevel,start,nread,threading)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:), intent(inout) :: data !2 dimensional data
type(domain2d), target, optional, intent(in) :: domain
integer, intent(in) , optional :: timelevel
integer, intent(in) , optional :: start(:), nread(:)
integer, intent(in) , optional :: threading
character(len=256) :: fname
integer :: unit, siz_in(4)
integer :: file_index ! index of the opened file in array files
integer :: index_field ! position of the fieldname in the list of variables
logical :: read_dist, io_domain_exist, found_file
type(domain2d), pointer, save :: d_ptr =>NULL()
type(domain2d), pointer, save :: io_domain =>NULL()
! Initialize files to default values
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_compressed_2d): module not initialized')
if(PRESENT(domain))then
d_ptr => domain
elseif (ASSOCIATED(Current_domain)) then
d_ptr => Current_domain
else
call mpp_error(FATAL,'fms_io(read_compressed_2d): Domain must be an argument or set by set_domain()')
endif
found_file = get_file_name(filename, fname, read_dist, io_domain_exist, domain=d_ptr)
if(.not. found_file) then
found_file = get_file_name(filename, fname, read_dist, io_domain_exist, no_domain=.true. )
endif
if(.not.found_file) call mpp_error(FATAL, 'fms_io_mod(read_compressed_2d): file ' //trim(filename)// &
'(with the consideration of tile number) and corresponding distributed file are not found')
call get_file_unit(fname, unit, file_index, read_dist, io_domain_exist, domain=d_ptr)
call get_field_id(unit, file_index, fieldname, index_field, .false., .false. )
if (files_read(file_index)%var(index_field)%is_dimvar) then
call mpp_get_axis_data(files_read(file_index)%var(index_field)%axis,data(:,1))
else
call mpp_read_compressed(unit,files_read(file_index)%var(index_field)%field,d_ptr,data,timelevel,start,nread,threading)
endif
d_ptr =>NULL()
end subroutine read_compressed_2d
!.....................................................................
subroutine read_compressed_3d(filename,fieldname,data,domain,timelevel)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:,:), intent(inout) :: data !3 dimensional data
type(domain2d), target, optional, intent(in) :: domain
integer, intent(in) , optional :: timelevel
character(len=256) :: fname
integer :: unit
integer :: file_index ! index of the opened file in array files
integer :: index_field ! position of the fieldname in the list of variables
logical :: read_dist, io_domain_exist, found_file
type(domain2d), pointer, save :: d_ptr =>NULL()
type(domain2d), pointer, save :: io_domain =>NULL()
! Initialize files to default values
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_compressed_3d): module not initialized')
if(PRESENT(domain))then
d_ptr => domain
elseif (ASSOCIATED(Current_domain)) then
d_ptr => Current_domain
else
call mpp_error(FATAL,'fms_io(read_compressed_3d): Domain must be an argument or set by set_domain()')
endif
found_file = get_file_name(filename, fname, read_dist, io_domain_exist, domain=d_ptr)
if(.not. found_file) then
found_file = get_file_name(filename, fname, read_dist, io_domain_exist, no_domain=.true. )
endif
if(.not.found_file) call mpp_error(FATAL, 'fms_io_mod(read_compressed_3d): file ' //trim(filename)// &
'(with the consideration of tile number) and corresponding distributed file are not found')
call get_file_unit(fname, unit, file_index, read_dist, io_domain_exist, domain=d_ptr)
call get_field_id(unit, file_index, fieldname, index_field, .false., .false. )
if (files_read(file_index)%var(index_field)%is_dimvar) then
call mpp_get_axis_data(files_read(file_index)%var(index_field)%axis,data(:,1,1))
else
call mpp_read_compressed(unit,files_read(file_index)%var(index_field)%field,d_ptr,data,timelevel)
endif
d_ptr =>NULL()
end subroutine read_compressed_3d
!.....................................................................
subroutine read_distributed_a1D(unit,fmt,iostat,data)
integer, intent(in) :: unit
character(*), intent(in) :: fmt
integer, intent(out) :: iostat
character(len=*), dimension(:), intent(inout) :: data
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_distributed_a1D): module not initialized')
call mpp_read_distributed_ascii(unit,fmt,dr_set_size,data,iostat)
end subroutine read_distributed_a1D
!.....................................................................
subroutine read_distributed_i1D(unit,fmt,iostat,data)
integer, intent(in) :: unit
character(*), intent(in) :: fmt
integer, intent(out) :: iostat
integer, dimension(:), intent(inout) :: data
integer, allocatable :: pelist(:)
integer :: i,lsize
logical :: is_ioroot=.false.
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_distributed_i1D): module not initialized')
call mpp_read_distributed_ascii(unit,fmt,dr_set_size,data,iostat)
end subroutine read_distributed_i1D
!.....................................................................
subroutine read_distributed_iscalar(unit,fmt,iostat,data)
integer, intent(in) :: unit
character(*), intent(in) :: fmt
integer, intent(out) :: iostat
integer, intent(inout) :: data
integer :: idata(1)
pointer(ptr,idata)
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_distributed_iscalar): module not initialized')
ptr = LOC(data)
call read_distributed(unit,fmt,iostat,idata)
end subroutine read_distributed_iscalar
!.....................................................................
subroutine read_distributed_r3D(unit,fmt,iostat,data)
integer, intent(in) :: unit
character(*), intent(in) :: fmt
integer, intent(out) :: iostat
real, dimension(:,:,:), intent(inout) :: data
real :: data1D(size(data))
pointer(ptr,data1D)
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_distributed_r5D): module not initialized')
ptr = LOC(data)
call read_distributed(unit,fmt,iostat,data1D)
end subroutine read_distributed_r3D
!.....................................................................
subroutine read_distributed_r5D(unit,fmt,iostat,data)
integer, intent(in) :: unit
character(*), intent(in) :: fmt
integer, intent(out) :: iostat
real, dimension(:,:,:,:,:), intent(inout) :: data
real :: data1D(size(data))
pointer(ptr,data1D)
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_distributed_r5D): module not initialized')
ptr = LOC(data)
call read_distributed(unit,fmt,iostat,data1D)
end subroutine read_distributed_r5D
!.....................................................................
subroutine read_distributed_r1D(unit,fmt,iostat,data)
integer, intent(in) :: unit
character(*), intent(in) :: fmt
integer, intent(out) :: iostat
real, dimension(:), intent(inout) :: data
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_distributed_r1D): module not initialized')
call mpp_read_distributed_ascii(unit,fmt,dr_set_size,data,iostat)
end subroutine read_distributed_r1D
!=====================================================================================
subroutine read_data_2d_region(filename,fieldname,data,start,nread,domain, &
no_domain, tile_count)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:), intent(inout) :: data ! 3 dimensional data
integer, dimension(:), intent(in) :: start, nread
type(domain2d), target, optional, intent(in) :: domain
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: tile_count
character(len=256) :: fname
integer :: unit, siz_in(4)
integer :: file_index ! index of the opened file in array files
integer :: index_field ! position of the fieldname in the list of variables
logical :: is_no_domain = .false.
logical :: read_dist, io_domain_exist, found_file
type(domain2d), pointer, save :: d_ptr =>NULL()
! Initialize files to default values
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_data_2d_region): module not initialized')
is_no_domain = .false.
if (PRESENT(no_domain)) is_no_domain = no_domain
if(PRESENT(domain))then
d_ptr => domain
elseif (ASSOCIATED(Current_domain) .AND. .NOT. is_no_domain ) then
d_ptr => Current_domain
endif
if(.not. PRESENT(domain) .and. .not. ASSOCIATED(Current_domain) ) is_no_domain = .true.
found_file = get_file_name(filename, fname, read_dist, io_domain_exist, is_no_domain, domain, tile_count)
if(.not.found_file) call mpp_error(FATAL, 'fms_io_mod(read_data_2d_region): file ' //trim(filename)// &
'(with the consideration of tile number) and corresponding distributed file are not found')
call get_file_unit(fname, unit, file_index, read_dist, io_domain_exist, domain=domain)
call get_field_id(unit, file_index, fieldname, index_field, is_no_domain, .false. )
siz_in(1:4) = files_read(file_index)%var(index_field)%siz(1:4)
if(files_read(file_index)%var(index_field)%is_dimvar) then
call mpp_error(FATAL, 'fms_io_mod(read_data_2d_region): the field should not be a dimension variable')
endif
call mpp_read(unit,files_read(file_index)%var(index_field)%field,data,start, nread)
d_ptr =>NULL()
return
end subroutine read_data_2d_region
subroutine read_data_3d_region(filename,fieldname,data,start,nread,domain, &
no_domain, tile_count)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:,:), intent(inout) :: data ! 3 dimensional data
integer, dimension(:), intent(in) :: start, nread
type(domain2d), target, optional, intent(in) :: domain
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: tile_count
character(len=256) :: fname
integer :: unit, siz_in(4)
integer :: file_index ! index of the opened file in array files
integer :: index_field ! position of the fieldname in the list of variables
logical :: is_no_domain = .false.
logical :: read_dist, io_domain_exist, found_file
type(domain2d), pointer, save :: d_ptr =>NULL()
! Initialize files to default values
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_data_3d_region): module not initialized')
is_no_domain = .false.
if (PRESENT(no_domain)) is_no_domain = no_domain
if(PRESENT(domain))then
d_ptr => domain
elseif (ASSOCIATED(Current_domain) .AND. .NOT. is_no_domain ) then
d_ptr => Current_domain
endif
if(.not. PRESENT(domain) .and. .not. ASSOCIATED(Current_domain) ) is_no_domain = .true.
found_file = get_file_name(filename, fname, read_dist, io_domain_exist, is_no_domain, domain, tile_count)
if(.not.found_file) call mpp_error(FATAL, 'fms_io_mod(read_data_2d_region): file ' //trim(filename)// &
'(with the consideration of tile number) and corresponding distributed file are not found')
call get_file_unit(fname, unit, file_index, read_dist, io_domain_exist, domain=domain)
call get_field_id(unit, file_index, fieldname, index_field, is_no_domain, .false. )
siz_in(1:4) = files_read(file_index)%var(index_field)%siz(1:4)
if(files_read(file_index)%var(index_field)%is_dimvar) then
call mpp_error(FATAL, 'fms_io_mod(read_data_3d_region): the field should not be a dimension variable')
endif
call mpp_read(unit,files_read(file_index)%var(index_field)%field,data,start, nread)
d_ptr =>NULL()
return
end subroutine read_data_3d_region
!=====================================================================================
subroutine read_data_2d_region_r8(filename,fieldname,data,start,nread,domain, &
no_domain, tile_count)
character(len=*), intent(in) :: filename, fieldname
real(kind=8), dimension(:,:), intent(inout) :: data ! 3 dimensional data
integer, dimension(:), intent(in) :: start, nread
type(domain2d), target, optional, intent(in) :: domain
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: tile_count
character(len=256) :: fname
integer :: unit, siz_in(4)
integer :: file_index ! index of the opened file in array files
integer :: index_field ! position of the fieldname in the list of variables
logical :: is_no_domain = .false.
logical :: read_dist, io_domain_exist, found_file
type(domain2d), pointer, save :: d_ptr =>NULL()
! Initialize files to default values
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_data_2d_region_r8): module not initialized')
is_no_domain = .false.
if (PRESENT(no_domain)) is_no_domain = no_domain
if(PRESENT(domain))then
d_ptr => domain
elseif (ASSOCIATED(Current_domain) .AND. .NOT. is_no_domain ) then
d_ptr => Current_domain
endif
if(.not. PRESENT(domain) .and. .not. ASSOCIATED(Current_domain) ) is_no_domain = .true.
found_file = get_file_name(filename, fname, read_dist, io_domain_exist, is_no_domain, domain, tile_count)
if(.not.found_file) call mpp_error(FATAL, 'fms_io_mod(read_data_2d_region): file ' //trim(filename)// &
'(with the consideration of tile number) and corresponding distributed file are not found')
call get_file_unit(fname, unit, file_index, read_dist, io_domain_exist, domain=domain)
call get_field_id(unit, file_index, fieldname, index_field, is_no_domain, .false. )
siz_in(1:4) = files_read(file_index)%var(index_field)%siz(1:4)
if(files_read(file_index)%var(index_field)%is_dimvar) then
call mpp_error(FATAL, 'fms_io_mod(read_data_2d_region_r8): the field should not be a dimension variable')
endif
call mpp_read(unit,files_read(file_index)%var(index_field)%field,data,start, nread)
d_ptr =>NULL()
return
end subroutine read_data_2d_region_r8
subroutine read_data_3d_region_r8(filename,fieldname,data,start,nread,domain, &
no_domain, tile_count)
character(len=*), intent(in) :: filename, fieldname
real(kind=8), dimension(:,:,:), intent(inout) :: data ! 3 dimensional data
integer, dimension(:), intent(in) :: start, nread
type(domain2d), target, optional, intent(in) :: domain
logical, optional, intent(in) :: no_domain
integer, optional, intent(in) :: tile_count
character(len=256) :: fname
integer :: unit, siz_in(4)
integer :: file_index ! index of the opened file in array files
integer :: index_field ! position of the fieldname in the list of variables
logical :: is_no_domain = .false.
logical :: read_dist, io_domain_exist, found_file
type(domain2d), pointer, save :: d_ptr =>NULL()
! Initialize files to default values
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_data_3d_region_r8): module not initialized')
is_no_domain = .false.
if (PRESENT(no_domain)) is_no_domain = no_domain
if(PRESENT(domain))then
d_ptr => domain
elseif (ASSOCIATED(Current_domain) .AND. .NOT. is_no_domain ) then
d_ptr => Current_domain
endif
if(.not. PRESENT(domain) .and. .not. ASSOCIATED(Current_domain) ) is_no_domain = .true.
found_file = get_file_name(filename, fname, read_dist, io_domain_exist, is_no_domain, domain, tile_count)
if(.not.found_file) call mpp_error(FATAL, 'fms_io_mod(read_data_2d_region): file ' //trim(filename)// &
'(with the consideration of tile number) and corresponding distributed file are not found')
call get_file_unit(fname, unit, file_index, read_dist, io_domain_exist, domain=domain)
call get_field_id(unit, file_index, fieldname, index_field, is_no_domain, .false. )
siz_in(1:4) = files_read(file_index)%var(index_field)%siz(1:4)
if(files_read(file_index)%var(index_field)%is_dimvar) then
call mpp_error(FATAL, 'fms_io_mod(read_data_3d_region_r8): the field should not be a dimension variable')
endif
call mpp_read(unit,files_read(file_index)%var(index_field)%field,data,start, nread)
d_ptr =>NULL()
return
end subroutine read_data_3d_region_r8
!=====================================================================================
!--- we assume any text data are at most 2-dimensional and level is for first dimension
subroutine read_data_text(filename,fieldname,data,level)
character(len=*), intent(in) :: filename, fieldname
character(len=*), intent(out) :: data
integer, intent(in) , optional :: level
logical :: file_opened, found_file, read_dist, io_domain_exist
integer :: lev, unit, index_field
integer :: file_index
character(len=256) :: fname
! Initialize files to default values
if(.not.module_is_initialized) call mpp_error(FATAL,'fms_io(read_data_text): module not initialized')
file_opened=.false.
if (PRESENT(level)) then
lev = level
else
lev = 1
endif
found_file = get_file_name(filename, fname, read_dist, io_domain_exist, no_domain=.true. )
if(.not.found_file) call mpp_error(FATAL, 'fms_io_mod(read_data_text): file ' //trim(filename)// &
'(with the consideration of tile number) and corresponding distributed file are not found')
call get_file_unit(fname, unit, file_index, read_dist, io_domain_exist )
! Get info of this file and field
call get_field_id(unit, file_index, fieldname, index_field, .true., .true. )
if ( lev < 1 .or. lev > files_read(file_index)%var(index_field)%siz(1) ) then
write(error_msg,'(I5,"/",I5)') lev, files_read(file_index)%var(index_field)%siz(1)
call mpp_error(FATAL,'fms_io(read_data_text): text level out of range, level/max_level=' &
//trim(error_msg)//' in field/file: '//trim(fieldname)//'/'//trim(filename))
endif
call mpp_read(unit,files_read(file_index)%var(index_field)%field,data, level=level)
return
end subroutine read_data_text
!..............................................................
!
subroutine read_data_4d_new(filename,fieldname,data,domain,timelevel,&
no_domain,position,tile_count)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:,:,:), intent(inout) :: data !2 dimensional data
real, dimension(size(data,1),size(data,2),size(data,3)*size(data,4)) :: data_3d
type(domain2d), intent(in), optional :: domain
integer, intent(in) , optional :: timelevel
logical, intent(in), optional :: no_domain
integer, intent(in) , optional :: position, tile_count
integer :: i, k, l
integer :: isc,iec,jsc,jec,isd,ied,jsd,jed
integer :: isg,ieg,jsg,jeg
integer :: xsize_c,ysize_c,xsize_d,ysize_d
integer :: xsize_g,ysize_g, ishift, jshift
!#ifdef
! pointer( p, data_3d )
! p = LOC(data)
!#endif
call read_data_3d_new(filename,fieldname,data_3d,domain,timelevel,&
no_domain,.false., position,tile_count)
if(PRESENT(domain)) then
call mpp_get_global_domain( domain,isg,ieg,jsg,jeg,xsize=xsize_g,ysize=ysize_g, tile_count=tile_count, position=position)
call mpp_get_compute_domain( domain,isc,iec,jsc,jec,xsize=xsize_c,ysize=ysize_c, tile_count=tile_count, position=position)
call mpp_get_data_domain( domain,isd,ied,jsd,jed,xsize=xsize_d,ysize=ysize_d, tile_count=tile_count, position=position)
call mpp_get_domain_shift (domain, ishift, jshift, position)
if(((size(data,1)==xsize_c) .and. (size(data,2)==ysize_c))) then !on_comp_domain
i = 0
do l = 1, size(data,4) ; do k = 1, size(data,3)
i = i + 1
data(:,:,k,l) = data_3d(:,:,i)
enddo ; enddo
else if((size(data,1)==xsize_d) .and. (size(data,2)==ysize_d)) then !on_data_domain
i = 0
do l = 1, size(data,4) ; do k = 1, size(data,3)
i = i + 1
data(isc-isd+1:iec-isd+1,jsc-jsd+1:jec-jsd+1,k,l) = data_3d(isc-isd+1:iec-isd+1,jsc-jsd+1:jec-jsd+1,i)
enddo ; enddo
else if((size(data,1)==xsize_g) .and. (size(data,2)==ysize_g)) then !on_global_domain
i = 0
do l = 1, size(data,4) ; do k = 1, size(data,3)
i = i + 1
data(:,:,k,l) = data_3d(:,:,i)
enddo ; enddo
else
call mpp_error(FATAL,'error in read_data_4d_new, field '//trim(fieldname)// &
' in file '//trim(filename)//' data must be in compute or data domain')
endif
else
i = 0
do l = 1, size(data,4) ; do k = 1, size(data,3)
i = i + 1
data(:,:,k,l) = data_3d(:,:,i)
enddo ; enddo
endif
end subroutine read_data_4d_new
subroutine read_data_2d_UG(filename,fieldname,data,SG_domain,UG_domain,timelevel)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:), intent(inout) :: data !2 dimensional data
type(domain2d), intent(in) :: SG_domain
type(domainUG), intent(in) :: UG_domain
integer, intent(in) , optional :: timelevel
real, dimension(:,:), allocatable :: data_2d
integer :: is, ie, js, je
call mpp_get_compute_domain(SG_domain, is, ie, js, je)
allocate(data_2d(is:ie,js:je))
call read_data_2d_new(filename,fieldname,data_2d, SG_domain, timelevel)
call mpp_pass_SG_to_UG(UG_domain, data_2d, data)
deallocate(data_2d)
end subroutine read_data_2d_UG
subroutine read_data_2d_new(filename,fieldname,data,domain,timelevel,&
no_domain,position,tile_count)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:,:), intent(inout) :: data !2 dimensional data
real, dimension(size(data,1),size(data,2),1) :: data_3d
type(domain2d), intent(in), optional :: domain
integer, intent(in) , optional :: timelevel
logical, intent(in), optional :: no_domain
integer, intent(in) , optional :: position, tile_count
integer :: isc,iec,jsc,jec,isd,ied,jsd,jed
integer :: isg,ieg,jsg,jeg
integer :: xsize_c,ysize_c,xsize_d,ysize_d
integer :: xsize_g,ysize_g, ishift, jshift
!#ifdef
! pointer( p, data_3d )
! p = LOC(data)
!#endif
call read_data_3d_new(filename,fieldname,data_3d,domain,timelevel,&
no_domain,.false., position,tile_count)
if(PRESENT(domain)) then
call mpp_get_global_domain( domain,isg,ieg,jsg,jeg,xsize=xsize_g,ysize=ysize_g, tile_count=tile_count, position=position)
call mpp_get_compute_domain( domain,isc,iec,jsc,jec,xsize=xsize_c,ysize=ysize_c, tile_count=tile_count, position=position)
call mpp_get_data_domain( domain,isd,ied,jsd,jed,xsize=xsize_d,ysize=ysize_d, tile_count=tile_count, position=position)
call mpp_get_domain_shift (domain, ishift, jshift, position)
if(((size(data,1)==xsize_c) .and. (size(data,2)==ysize_c))) then !on_comp_domain
data(:,:) = data_3d(:,:,1)
else if((size(data,1)==xsize_d) .and. (size(data,2)==ysize_d)) then !on_data_domain
data(isc-isd+1:iec-isd+1,jsc-jsd+1:jec-jsd+1) = data_3d(isc-isd+1:iec-isd+1,jsc-jsd+1:jec-jsd+1,1)
else if((size(data,1)==xsize_g) .and. (size(data,2)==ysize_g)) then !on_global_domain
data(:,:) = data_3d(:,:,1)
else
call mpp_error(FATAL,'error in read_data_2d_new, field '//trim(fieldname)// &
' in file '//trim(filename)//' data must be in compute or data domain')
endif
else
data(:,:) = data_3d(:,:,1)
endif
end subroutine read_data_2d_new
!.....................................................................
subroutine read_data_1d_new(filename,fieldname,data,domain,timelevel,&
no_domain, tile_count)
character(len=*), intent(in) :: filename, fieldname
real, dimension(:), intent(inout) :: data !1 dimensional data
real, dimension(size(data,1),1,1) :: data_3d
type(domain2d), intent(in), optional :: domain
integer, intent(in) , optional :: timelevel
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: tile_count
pointer( p, data_3d )
p = LOC(data)
call read_data_3d_new(filename,fieldname,data_3d,domain,timelevel,&
no_domain=no_domain, scalar_or_1d=.true., tile_count=tile_count)
end subroutine read_data_1d_new
!.....................................................................
subroutine read_data_scalar_new(filename,fieldname,data,domain,timelevel,&
no_domain, tile_count)
! this subroutine is for reading a single number
character(len=*), intent(in) :: filename, fieldname
real, intent(inout) :: data !zero dimension data
real, dimension(1,1,1) :: data_3d
type(domain2d), intent(in), optional :: domain
integer, intent(in) , optional :: timelevel
logical, intent(in), optional :: no_domain
integer, intent(in), optional :: tile_count
if(present(no_domain)) then
if(.NOT. no_domain) call mpp_error(FATAL, 'fms_io(read_data_scalar_new): no_domain should be true for field ' &
//trim(fieldname)//' of file '//trim(filename) )
end if
call read_data_3d_new(filename,fieldname,data_3d,domain,timelevel,&
no_domain=no_domain, scalar_or_1d=.true., tile_count=tile_count)
data = data_3d(1,1,1)
end subroutine read_data_scalar_new
!.....................................................................
function unique_axes(file, index, id_axes, siz_axes, dom)
type(restart_file_type), intent(inout) :: file
integer, intent(in) :: index
integer, dimension(:), intent(out) :: id_axes
integer, dimension(:), intent(out) :: siz_axes
type(domain1d), dimension(:), intent(in), optional :: dom
integer :: unique_axes
type(var_type), pointer, save :: cur_var => NULL()
integer :: i,j
logical :: found
unique_axes=0
if(index <0 .OR. index > 4) call mpp_error(FATAL,"unique_axes(fms_io_mod): index should be 1, 2, 3 or 4")
do i = 1, file%nvar
cur_var => file%var(i)
if(cur_var%read_only) cycle
if(cur_var%ndim < index) cycle
found = .false.
do j = 1, unique_axes
if(siz_axes(j) == cur_var%gsiz(index) ) then
if(PRESENT(dom)) then
if(cur_var%domain_idx == id_axes(j) ) then
found = .true.
exit
else if(cur_var%domain_idx >0 .AND. id_axes(j) >0) then
if(dom(cur_var%domain_idx) .EQ. dom(id_axes(j)) ) then
found = .true.
exit
end if
end if
else
found = .true.
exit
end if
end if
end do
if(found) then
cur_var%id_axes(index) = j
else
unique_axes = unique_axes+1
if(unique_axes > max_axes) then
write(error_msg,'(I3,"/",I3)') unique_axes, max_axes
if(index == 1 ) then
call mpp_error(FATAL,'# x axes exceeded max_axes in fms_io,num_axes/max_axes= '//trim(error_msg))
else if(index == 2 ) then
call mpp_error(FATAL,'# y axes exceeded max_axes in fms_io,num_axes/max_axes= '//trim(error_msg))
else
call mpp_error(FATAL,'# z axes exceeded max_axes in fms_io,num_axes/max_axes= '//trim(error_msg))
end if
endif
id_axes(unique_axes) = cur_var%domain_idx
siz_axes(unique_axes) = cur_var%gsiz(index)
if(siz_axes(unique_axes) > max_axis_size) then
call mpp_error(FATAL, 'fms_io_mod(unique_axes): size_axes is greater than max_axis_size, '//&
'increase fms_io_nml variable max_axis_size to at least ', siz_axes(unique_axes))
endif
cur_var%id_axes(index) = unique_axes
end if
end do
cur_var => NULL()
return
end function unique_axes
!#######################################################################
!#######################################################################
! --------- routines for reading distributed data ---------
! before calling these routines the domain decompostion must be set
! by calling "set_domain" with the appropriate domain2d data type
!
! reading can be done either by all PEs (default) or by only the root PE
! this is controlled by namelist variable "read_all_pe".
! By default, array data is expected to be declared in data domain and no_halo
!is NOT needed, however IF data is decalared in COMPUTE domain then optional NO_HALO should be .true.
!#######################################################################
subroutine read_data_2d ( unit, data, end)
integer, intent(in) :: unit
real, intent(out), dimension(isd:,jsd:) :: data
logical, intent(out), optional :: end
real, dimension(isg:ieg,jsg:jeg) :: gdata
integer :: len
logical :: no_halo
include "read_data_2d.inc"
end subroutine read_data_2d
!#######################################################################
subroutine read_ldata_2d ( unit, data, end)
integer, intent(in) :: unit
logical, intent(out), dimension(isd:,jsd:) :: data
logical, intent(out), optional :: end
logical, dimension(isg:ieg,jsg:jeg) :: gdata
integer :: len
logical :: no_halo
include "read_data_2d.inc"
end subroutine read_ldata_2d
!#######################################################################
subroutine read_idata_2d ( unit, data, end)
integer, intent(in) :: unit
integer, intent(out), dimension(isd:,jsd:) :: data
logical, intent(out), optional :: end
integer, dimension(isg:ieg,jsg:jeg) :: gdata
integer :: len
logical :: no_halo
include "read_data_2d.inc"
end subroutine read_idata_2d
!#######################################################################
# 6217
!#######################################################################
subroutine read_data_3d ( unit, data, end)
integer, intent(in) :: unit
real, intent(out), dimension(isd:,jsd:,:) :: data
logical, intent(out), optional :: end
real, dimension(isg:ieg,jsg:jeg,size(data,3)) :: gdata
integer :: len
logical :: no_halo
include "read_data_3d.inc"
end subroutine read_data_3d
!#######################################################################
# 6247
!#######################################################################
subroutine read_data_4d ( unit, data, end)
integer, intent(in) :: unit
real, intent(out), dimension(isd:,jsd:,:,:) :: data
logical, intent(out), optional :: end
real, dimension(isg:ieg,jsg:jeg,size(data,3),size(data,4)) :: gdata
integer :: len
logical :: no_halo
! WARNING: memory usage with this routine could be costly
include "read_data_4d.inc"
end subroutine read_data_4d
!#######################################################################
# 6279
!#######################################################################
! -------- routines for writing distributed data --------
! before calling these routines the domain decompostion must be set
! by calling "set_domain" with the appropriate domain2d data type
!#######################################################################
subroutine write_data_2d ( unit, data )
integer, intent(in) :: unit
real, intent(in), dimension(isd:,jsd:) :: data
real, dimension(isg:ieg,jsg:jeg) :: gdata
include "write_data.inc"
end subroutine write_data_2d
!#######################################################################
subroutine write_ldata_2d ( unit, data )
integer, intent(in) :: unit
logical, intent(in), dimension(isd:,jsd:) :: data
logical, dimension(isg:ieg,jsg:jeg) :: gdata
include "write_data.inc"
end subroutine write_ldata_2d
!#######################################################################
subroutine write_idata_2d ( unit, data )
integer, intent(in) :: unit
integer, intent(in), dimension(isd:,jsd:) :: data
integer, dimension(isg:ieg,jsg:jeg) :: gdata
include "write_data.inc"
end subroutine write_idata_2d
!#######################################################################
# 6326
!#######################################################################
subroutine write_data_3d ( unit, data )
integer, intent(in) :: unit
real, intent(in), dimension(isd:,jsd:,:) :: data
real, dimension(isg:ieg,jsg:jeg,size(data,3)) :: gdata
include "write_data.inc"
end subroutine write_data_3d
!#######################################################################
# 6350
!#######################################################################
subroutine write_data_4d ( unit, data )
integer, intent(in) :: unit
real, intent(in), dimension(isd:,jsd:,:,:) :: data
real, dimension(isg:ieg,jsg:jeg,size(data,3),size(data,4)) :: gdata
integer :: n
if (.not.associated(Current_domain)) &
call mpp_error(FATAL,'fms_io(write_data_4d): need to call set_domain ')
! get the global data and write only on root pe
! do this one field at a time to save memory
do n = 1, size(data,4)
call mpp_global_field ( Current_domain, data(:,:,:,n), gdata(:,:,:,n) )
enddo
if ( mpp_pe() == mpp_root_pe() ) write (unit) gdata
end subroutine write_data_4d
!#######################################################################
# 6390
!#######################################################################
! private routines (read_eof,do_read)
! this routine is called when an EOF is found while
! reading a distributed data file using read_data
subroutine read_eof (end_found)
logical, intent(out), optional :: end_found
if (present(end_found))then
end_found = .true.
else
call mpp_error(FATAL,'fms_io(read_eof): unexpected EOF')
endif
end subroutine read_eof
!#######################################################################
! determines if current pe should read data
! checks namelist variable read_all_pe
function do_read ( )
logical :: do_read
do_read = mpp_pe() == mpp_root_pe() .or. read_all_pe
end function do_read
!!#######################################################################
subroutine reset_field_name(fileObj, id_field, name)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
character(len=*), intent(in) :: name
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_name): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_name): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
fileObj%var(id_field)%name = trim(name)
end subroutine reset_field_name
!#######################################################################
subroutine reset_field_pointer_r0d(fileObj, id_field, data)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
real, intent(in), target :: data
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_r0d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r0d): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 1) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r0d): one-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not one level" )
fileObj%p0dr(1, id_field)%p => data
end subroutine reset_field_pointer_r0d
!#######################################################################
subroutine reset_field_pointer_r1d(fileObj, id_field, data)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
real, dimension(:), intent(in), target :: data
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_r1d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r1d): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 1) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r1d): one-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not one level" )
fileObj%p1dr(1, id_field)%p => data
end subroutine reset_field_pointer_r1d
!#######################################################################
subroutine reset_field_pointer_r2d(fileObj, id_field, data)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
real, dimension(:,:), intent(in), target :: data
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_r2d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r2d): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 1) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r2d): one-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not one level" )
fileObj%p2dr(1, id_field)%p => data
end subroutine reset_field_pointer_r2d
!#######################################################################
subroutine reset_field_pointer_r3d(fileObj, id_field, data)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
real, dimension(:,:,:), intent(in), target :: data
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_r3d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r3d): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 1) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r3d): one-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not one level" )
fileObj%p3dr(1, id_field)%p => data
end subroutine reset_field_pointer_r3d
!#######################################################################
subroutine reset_field_pointer_r4d(fileObj, id_field, data)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
real, dimension(:,:,:,:), intent(in), target :: data
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_r4d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r4d): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 1) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r4d): one-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not one level" )
fileObj%p4dr(1, id_field)%p => data
end subroutine reset_field_pointer_r4d
!#######################################################################
subroutine reset_field_pointer_i0d(fileObj, id_field, data)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
integer, intent(in), target :: data
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_i0d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i0d): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 1) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i0d): one-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not one level" )
fileObj%p0di(1, id_field)%p => data
end subroutine reset_field_pointer_i0d
!#######################################################################
subroutine reset_field_pointer_i1d(fileObj, id_field, data)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
integer, dimension(:), intent(in), target :: data
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_i1d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i1d): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 1) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i1d): one-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not one level" )
fileObj%p1di(1, id_field)%p => data
end subroutine reset_field_pointer_i1d
!#######################################################################
subroutine reset_field_pointer_i2d(fileObj, id_field, data)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
integer, dimension(:,:), intent(in), target :: data
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_i2d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i2d): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 1) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i2d): one-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not one level" )
fileObj%p2di(1, id_field)%p => data
end subroutine reset_field_pointer_i2d
!#######################################################################
subroutine reset_field_pointer_i3d(fileObj, id_field, data)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
integer, dimension(:,:,:), intent(in), target :: data
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_i3d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i3d): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 1) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i3d): one-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not one level" )
fileObj%p3di(1, id_field)%p => data
end subroutine reset_field_pointer_i3d
!#######################################################################
subroutine reset_field_pointer_r0d_2level(fileObj, id_field, data1, data2)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
real, intent(in), target :: data1, data2
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_r0d_2level): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r0d_2level): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 2) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r0d_2level): two-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not two level" )
fileObj%p0dr(1, id_field)%p => data1
fileObj%p0dr(2, id_field)%p => data2
end subroutine reset_field_pointer_r0d_2level
!#######################################################################
subroutine reset_field_pointer_r1d_2level(fileObj, id_field, data1, data2)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
real, dimension(:), intent(in), target :: data1, data2
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_r1d_2level): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r1d_2level): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 2) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r1d_2level): two-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not two level" )
fileObj%p1dr(1, id_field)%p => data1
fileObj%p1dr(2, id_field)%p => data2
end subroutine reset_field_pointer_r1d_2level
!#######################################################################
subroutine reset_field_pointer_r2d_2level(fileObj, id_field, data1, data2)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
real, dimension(:,:), intent(in), target :: data1, data2
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_r2d_2level): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r2d_2level): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 2) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r2d_2level): two-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not two level" )
fileObj%p2dr(1, id_field)%p => data1
fileObj%p2dr(2, id_field)%p => data2
end subroutine reset_field_pointer_r2d_2level
!#######################################################################
subroutine reset_field_pointer_r3d_2level(fileObj, id_field, data1, data2)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
real, dimension(:,:,:), intent(in), target :: data1, data2
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_r3d_2level): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r3d_2level): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 2) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_r3d_2level): two-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not two level" )
fileObj%p3dr(1, id_field)%p => data1
fileObj%p3dr(2, id_field)%p => data2
end subroutine reset_field_pointer_r3d_2level
!#######################################################################
subroutine reset_field_pointer_i0d_2level(fileObj, id_field, data1, data2)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
integer, intent(in), target :: data1, data2
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_i0d_2level): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i0d_2level): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 2) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i0d_2level): two-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not two level" )
fileObj%p0di(1, id_field)%p => data1
fileObj%p0di(2, id_field)%p => data2
end subroutine reset_field_pointer_i0d_2level
!#######################################################################
subroutine reset_field_pointer_i1d_2level(fileObj, id_field, data1, data2)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
integer, dimension(:), intent(in), target :: data1, data2
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_i1d_2level): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i1d_2level): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 2) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i1d_2level): two-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not two level" )
fileObj%p1di(1, id_field)%p => data1
fileObj%p1di(2, id_field)%p => data2
end subroutine reset_field_pointer_i1d_2level
!#######################################################################
subroutine reset_field_pointer_i2d_2level(fileObj, id_field, data1, data2)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
integer, dimension(:,:), intent(in), target :: data1, data2
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_i2d_2level): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i2d_2level): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 2) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i2d_2level): two-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not two level" )
fileObj%p2di(1, id_field)%p => data1
fileObj%p2di(2, id_field)%p => data2
end subroutine reset_field_pointer_i2d_2level
!#######################################################################
subroutine reset_field_pointer_i3d_2level(fileObj, id_field, data1, data2)
type(restart_file_type), intent(inout) :: fileObj
integer, intent(in) :: id_field
integer, dimension(:,:,:), intent(in), target :: data1, data2
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(reset_field_pointer_i3d_2level): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id_field < 0 .OR. id_field > fileObj%nvar) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i3d_2level): id_field should be positive integer and "// &
"no larger than number of fields in the file "//trim(fileObj%name) )
if(fileObj%var(id_field)%siz(4) .NE. 2) call mpp_error(FATAL, &
"fms_io(reset_field_pointer_i3d_2level): two-level reset_field_pointer is called, but "//&
"field "//trim(fileObj%var(id_field)%name)//" of file "//trim(fileObj%name)//" is not two level" )
fileObj%p3di(1, id_field)%p => data1
fileObj%p3di(2, id_field)%p => data2
end subroutine reset_field_pointer_i3d_2level
!#########################################################################
! This function returns .true. if the field referred to by id has
! initialized from a restart file, and .false. otherwise.
!
! Arguments: id - A integer that is the index of the field in fileObj.
! (in) fileObj - The control structure returned by a previous call to
! register_restart_field
function query_initialized_id(fileObj, id)
type(restart_file_type), intent(in) :: fileObj
integer, intent(in) :: id
logical :: query_initialized_id
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(query_initialized_id): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
if(id < 1 .OR. id > fileObj%nvar) call mpp_error(FATAL, "fms_io(query_initialized_id): " // &
"argument id must be between 1 and nvar in the restart_file_type object")
query_initialized_id = fileObj%var(id)%initialized
return
end function query_initialized_id
!#########################################################################
! This function returns .true. if the field referred to by name has
! initialized from a restart file, and .false. otherwise.
!
! Arguments: name - A pointer to the field that is being queried.
! (in) fileObj - The control structure returned by a previous call to
! register_restart_field
function query_initialized_name(fileObj, name)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: name
logical :: query_initialized_name
integer :: m
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(query_initialized_name): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
query_initialized_name = .false.
do m=1,fileObj%nvar
if (trim(name) == fileObj%var(m)%name) then
if (fileObj%var(m)%initialized) query_initialized_name = .true.
exit
endif
enddo
! Assume that you are going to initialize it now, so set flag to initialized if
! queried again.
if ((m>fileObj%nvar) .and. (mpp_pe() == mpp_root_pe())) then
call mpp_error(NOTE,"fms_io(query_initialized_name): Unknown restart variable "//name// &
" queried for initialization.")
end if
end function query_initialized_name
!#########################################################################
! This function returns 1 if the field pointed to by f_ptr has
! initialized from a restart file, and 0 otherwise. If f_ptr is
! NULL, it tests whether the entire restart file has been success-
! fully read.
!
! Arguments: f_ptr - A pointer to the field that is being queried.
! (in) name - The name of the field that is being queried.
! (in) CS - The control structure returned by a previous call to
! restart_init.
function query_initialized_r2d(fileObj, f_ptr, name)
type(restart_file_type), intent(inout) :: fileObj
real, dimension(:,:), target, intent(in) :: f_ptr
character(len=*), intent(in) :: name
logical :: query_initialized_r2d
integer :: m
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(query_initialized_r2d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
query_initialized_r2d = .false.
do m=1, fileObj%nvar
if (ASSOCIATED(fileObj%p2dr(1,m)%p,f_ptr)) then
if (fileObj%var(m)%initialized) query_initialized_r2d = .true.
exit
endif
enddo
! Assume that you are going to initialize it now, so set flag to initialized if
! queried again.
if (m>fileObj%nvar) then
if (mpp_pe() == mpp_root_pe() ) call mpp_error(NOTE, "fms_io(query_initialized_r2d): Unable to find "// &
trim(name)//" queried by pointer, "//"probably because of the suspect comparison of pointers by ASSOCIATED.")
query_initialized_r2d = query_initialized_name(fileObj, name)
if (mpp_pe() == mpp_root_pe() .AND. query_initialized_r2d) call mpp_error(NOTE, &
"fms_io(query_initialized_r2d): "//trim(name)// " initialization confirmed by name.")
endif
return
end function query_initialized_r2d
!#########################################################################
! This function returns 1 if the field pointed to by f_ptr has
! initialized from a restart file, and 0 otherwise. If f_ptr is
! NULL, it tests whether the entire restart file has been success-
! fully read.
!
! Arguments: f_ptr - A pointer to the field that is being queried.
! (in) name - The name of the field that is being queried.
! (in) CS - The control structure returned by a previous call to
! restart_init.
function query_initialized_r3d(fileObj, f_ptr, name)
type(restart_file_type), intent(inout) :: fileObj
real, dimension(:,:,:), target, intent(in) :: f_ptr
character(len=*), intent(in) :: name
logical :: query_initialized_r3d
integer :: m
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(query_initialized_r3d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
query_initialized_r3d = .false.
do m=1, fileObj%nvar
if (ASSOCIATED(fileObj%p3dr(1,m)%p,f_ptr)) then
if (fileObj%var(m)%initialized) query_initialized_r3d = .true.
exit
endif
enddo
! Assume that you are going to initialize it now, so set flag to initialized if
! queried again.
if (m>fileObj%nvar) then
if (mpp_pe() == mpp_root_pe() ) call mpp_error(NOTE, "fms_io(query_initialized_r3d): Unable to find "// &
trim(name)//" queried by pointer, "//"probably because of the suspect comparison of pointers by ASSOCIATED.")
query_initialized_r3d = query_initialized_name(fileObj, name)
if (mpp_pe() == mpp_root_pe() .AND. query_initialized_r3d) call mpp_error(NOTE, &
"fms_io(query_initialized_r3d): "//trim(name)// " initialization confirmed by name.")
endif
return
end function query_initialized_r3d
!#########################################################################
! This function returns 1 if the field pointed to by f_ptr has
! initialized from a restart file, and 0 otherwise. If f_ptr is
! NULL, it tests whether the entire restart file has been success-
! fully read.
!
! Arguments: f_ptr - A pointer to the field that is being queried.
! (in) name - The name of the field that is being queried.
! (in) CS - The control structure returned by a previous call to
! restart_init.
function query_initialized_r4d(fileObj, f_ptr, name)
type(restart_file_type), intent(inout) :: fileObj
real, dimension(:,:,:,:), target, intent(in) :: f_ptr
character(len=*), intent(in) :: name
logical :: query_initialized_r4d
integer :: m
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(query_initialized_r4d): " // &
"restart_file_type data must be initialized by calling register_restart_field before using it")
query_initialized_r4d = .false.
do m=1, fileObj%nvar
if (ASSOCIATED(fileObj%p4dr(1,m)%p,f_ptr)) then
if (fileObj%var(m)%initialized) query_initialized_r4d = .true.
exit
endif
enddo
! Assume that you are going to initialize it now, so set flag to initialized if
! queried again.
if (m>fileObj%nvar) then
if (mpp_pe() == mpp_root_pe() ) call mpp_error(NOTE, "fms_io(query_initialized_r4d): Unable to find "// &
trim(name)//" queried by pointer, "//"probably because of the suspect comparison of pointers by ASSOCIATED.")
query_initialized_r4d = query_initialized_name(fileObj, name)
if (mpp_pe() == mpp_root_pe() .AND. query_initialized_r4d) call mpp_error(NOTE, &
"fms_io(query_initialized_r4d): "//trim(name)// " initialization confirmed by name.")
endif
return
end function query_initialized_r4d
!#########################################################################
! This function sets that a variable has been initialized for future queries.
!
! Arguments: name - A pointer to the field whose initialization status is being set.
! (in) fileObj - The control structure returned by a previous call to
! register_restart_field
subroutine set_initialized_id(fileObj, id, is_set)
type(restart_file_type), intent(inout) :: fileObj
integer , intent(in) :: id
logical, optional, intent(in) :: is_set
logical :: set_val
integer :: m
set_val = .true.
if (present(is_set)) set_val = is_set
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(set_initialized_id): " // &
"restart_file_type data must be initialized by calling set_restart_field before using it")
if(id < 1 .OR. id > fileObj%nvar) call mpp_error(FATAL, "fms_io(set_initialized_id): " // &
"argument id must be between 1 and nvar in the restart_file_type object")
fileObj%var(id)%initialized = set_val
end subroutine set_initialized_id
!#########################################################################
! This function sets that a variable has been initialized for future queries.
!
! Arguments: name - A pointer to the field whose initialization status is being set.
! (in) fileObj - The control structure returned by a previous call to
! register_restart_field
subroutine set_initialized_name(fileObj, name, is_set)
type(restart_file_type), intent(inout) :: fileObj
character(len=*), intent(in) :: name
logical, optional, intent(in) :: is_set
logical :: set_val
integer :: m
set_val = .true.
if (present(is_set)) set_val = is_set
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(set_initialized_name): " // &
"restart_file_type data must be initialized by calling set_restart_field before using it")
do m=1,fileObj%nvar
if (trim(name) == fileObj%var(m)%name) then
fileObj%var(m)%initialized = set_val
exit
endif
enddo
if (m>fileObj%nvar) then
call mpp_error(NOTE,"fms_io(set_initialized_name): Unknown restart variable "//name// &
" attempted to set initialization.")
end if
end subroutine set_initialized_name
!#########################################################################
! This function sets that a variable has been initialized for future queries.
!
! Arguments: name - A pointer to the field whose initialization status is being set.
! (in) fileObj - The control structure returned by a previous call to
! register_restart_field
subroutine set_initialized_r2d(fileObj, f_ptr, name, is_set)
type(restart_file_type), intent(inout) :: fileObj
real, dimension(:,:), target, intent(in) :: f_ptr
character(len=*), intent(in) :: name
logical, optional, intent(in) :: is_set
logical :: set_val
integer :: m
set_val = .true.
if (present(is_set)) set_val = is_set
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(set_initialized_r2d): " // &
"restart_file_type data must be initialized by calling set_restart_field before using it")
do m=1, fileObj%nvar
if (ASSOCIATED(fileObj%p2dr(1,m)%p,f_ptr)) then
fileObj%var(m)%initialized = set_val
return
endif
enddo
if (m>fileObj%nvar .AND. mpp_pe() == mpp_root_pe() ) then
call mpp_error(NOTE,"fms_io(set_initialized_r2d): Unable to find "// &
trim(name)//" queried by pointer, "//"probably because of the suspect comparison of pointers by ASSOCIATED"// &
" when attempting to set initialization.")
end if
do m=1,fileObj%nvar
if (trim(name) == fileObj%var(m)%name) then
fileObj%var(m)%initialized = set_val
return
endif
enddo
if (m>fileObj%nvar .AND. mpp_pe() == mpp_root_pe() ) then
call mpp_error(NOTE,"fms_io(set_initialized_r2d): Unknown restart variable "//name// &
" attempted to set initialization.")
end if
end subroutine set_initialized_r2d
!#########################################################################
! This function sets that a variable has been initialized for future queries.
!
! Arguments: name - A pointer to the field whose initialization status is being set.
! (in) fileObj - The control structure returned by a previous call to
! register_restart_field
subroutine set_initialized_r3d(fileObj, f_ptr, name, is_set)
type(restart_file_type), intent(inout) :: fileObj
real, dimension(:,:,:), target, intent(in) :: f_ptr
character(len=*), intent(in) :: name
logical, optional, intent(in) :: is_set
logical :: set_val
integer :: m
set_val = .true.
if (present(is_set)) set_val = is_set
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(set_initialized_r3d): " // &
"restart_file_type data must be initialized by calling set_restart_field before using it")
do m=1, fileObj%nvar
if (ASSOCIATED(fileObj%p3dr(1,m)%p,f_ptr)) then
fileObj%var(m)%initialized = set_val
return
endif
enddo
if (m>fileObj%nvar .AND. mpp_pe() == mpp_root_pe() ) then
call mpp_error(NOTE,"fms_io(set_initialized_r3d): Unable to find "// &
trim(name)//" queried by pointer, "//"probably because of the suspect comparison of pointers by ASSOCIATED"//&
" when attempting to set initialization.")
end if
do m=1,fileObj%nvar
if (trim(name) == fileObj%var(m)%name) then
fileObj%var(m)%initialized = set_val
return
endif
enddo
if (m>fileObj%nvar .AND. mpp_pe() == mpp_root_pe() ) then
call mpp_error(NOTE,"fms_io(set_initialized_r3d): Unknown restart variable "//name// &
" attempted to set initialization.")
end if
end subroutine set_initialized_r3d
!#########################################################################
! This function sets that a variable has been initialized for future queries.
!
! Arguments: name - A pointer to the field whose initialization status is being set.
! (in) fileObj - The control structure returned by a previous call to
! register_restart_field
subroutine set_initialized_r4d(fileObj, f_ptr, name, is_set)
type(restart_file_type), intent(inout) :: fileObj
real, dimension(:,:,:,:), target, intent(in) :: f_ptr
character(len=*), intent(in) :: name
logical, optional, intent(in) :: is_set
logical :: set_val
integer :: m
set_val = .true.
if (present(is_set)) set_val = is_set
if (.not.associated(fileObj%var)) call mpp_error(FATAL, "fms_io(set_initialized_r4d): " // &
"restart_file_type data must be initialized by calling set_restart_field before using it")
do m=1, fileObj%nvar
if (ASSOCIATED(fileObj%p4dr(1,m)%p,f_ptr)) then
fileObj%var(m)%initialized = set_val
return
endif
enddo
if (m>fileObj%nvar .AND. mpp_pe() == mpp_root_pe() ) then
call mpp_error(NOTE,"fms_io(set_initialized_r4d): Unable to find "// &
trim(name)//" queried by pointer, "//"probably because of the suspect comparison of pointers by ASSOCIATED"//&
" when attempting to set initialization.")
end if
do m=1,fileObj%nvar
if (trim(name) == fileObj%var(m)%name) then
fileObj%var(m)%initialized = set_val
return
endif
enddo
if (m>fileObj%nvar .AND. mpp_pe() == mpp_root_pe() ) then
call mpp_error(NOTE,"fms_io(set_initialized_r4d): Unknown restart variable "//name// &
" attempted to set initialization.")
end if
end subroutine set_initialized_r4d
!#######################################################################
!#######################################################################
!
! routines for opening specific types of files:
!
! form action
! open_namelist_file MPP_ASCII MPP_RDONLY
! open restart_file MPP_NATIVE
! open_ieee32_file MPP_IEEE32
!
! all have: access=MPP_SEQUENTIAL, threading=MPP_SINGLE, nohdrs=.true.
! use the close_file interface to close these files
!
! if other types of files need to be opened the mpp_open and
! mpp_close routines in the mpp_io_mod should be used
!
!#######################################################################
!
!
! Opens single namelist file for reading only by all PEs
! the default file opened is called "input.nml".
!
!
! name of the file to be opened
!
!
! unit number returned by this function
!
function open_namelist_file (file) result (unit)
character(len=*), intent(in), optional :: file
integer :: unit
! local variables necessary for nesting code and alternate input.nmls
character(len=32) :: pelist_name
character(len=128) :: filename
if(show_open_namelist_file_warning) call mpp_error(WARNING, "fms_io_mod: open_namelist_file should not be called when INTERNAL_FILE_NML is defined")
if (.not.module_is_initialized) call fms_io_init ( )
if (present(file)) then
call mpp_open ( unit, file, form=MPP_ASCII, action=MPP_RDONLY, &
access=MPP_SEQUENTIAL, threading=MPP_SINGLE )
else
! the following code is necessary for using alternate namelist files (nests, stretched grids, etc)
pelist_name = mpp_get_current_pelist_name()
if ( file_exist('input_'//trim(pelist_name)//'.nml', no_domain=.true.) ) then
filename='input_'//trim(pelist_name)//'.nml'
else
filename='input.nml'
endif
call mpp_open ( unit, trim(filename), form=MPP_ASCII, action=MPP_RDONLY, &
access=MPP_SEQUENTIAL, threading=MPP_SINGLE )
endif
end function open_namelist_file
!
!
!
! Opens single restart file for reading by all PEs or
! writing by root PE only
! the file has native format and no mpp header records.
!
!
! name of the file to be opened
!
!
! action to be performed: can be 'read' or 'write'
!
!
! unit number returned by this function
!
function open_restart_file (file, action) result (unit)
character(len=*), intent(in) :: file, action
integer :: unit
integer :: mpp_action
if (.not.module_is_initialized) call fms_io_init ( )
! --- action (read,write) ---
select case (lowercase(trim(action)))
case ('read')
mpp_action = MPP_RDONLY
case ('write')
mpp_action = MPP_OVERWR
case default
call mpp_error(FATAL,'fms_io(open_restart_file): action should be either read or write in file'//trim(file))
end select
call mpp_open ( unit, file, form=MPP_NATIVE, action=mpp_action, &
access=MPP_SEQUENTIAL, threading=MPP_SINGLE, nohdrs=.true. )
end function open_restart_file
!
!
!
! Opens single direct access file for reading by all PEs or
! writing by root PE only
! the file has native format and no mpp header records.
!
function open_direct_file (file, action, recl) result (unit)
character(len=*), intent(in) :: file, action
integer, intent(in) :: recl
integer :: unit
integer :: mpp_action
if (.not.module_is_initialized) call fms_io_init ( )
! --- action (read,write) ---
select case (lowercase(trim(action)))
case ('read')
mpp_action = MPP_RDONLY
case ('write')
mpp_action = MPP_OVERWR
case default
call mpp_error(FATAL,'invalid option for argument action')
end select
call mpp_open ( unit, file, form=MPP_NATIVE, action=mpp_action, &
access=MPP_DIRECT, threading=MPP_SINGLE, nohdrs=.true., recl=recl )
end function open_direct_file
!
!
!
! Opens single 32-bit ieee file for reading by all PEs or
! writing by root PE only (writing is not recommended)
! the file has no mpp header records.
!
!
! name of the file to be opened
!
!
! action to be performed: can be 'read' or 'write'
!
!
! unit number returned by this function
!
function open_ieee32_file (file, action) result (unit)
character(len=*), intent(in) :: file, action
integer :: unit
integer :: mpp_action
if (.not.module_is_initialized) call fms_io_init ( )
! --- action (read,write) ---
select case (lowercase(trim(action)))
case ('read')
mpp_action = MPP_RDONLY
case ('write')
mpp_action = MPP_OVERWR
case default
call mpp_error (FATAL,'fms_io(open_ieee32_file): action should be either read or write in file'//trim(file))
end select
if (iospec_ieee32(1:1) == ' ') then
call mpp_open ( unit, file, form=MPP_IEEE32, action=mpp_action, &
access=MPP_SEQUENTIAL, threading=MPP_SINGLE, &
nohdrs=.true. )
else
call mpp_open ( unit, file, form=MPP_IEEE32, action=mpp_action, &
access=MPP_SEQUENTIAL, threading=MPP_SINGLE, &
nohdrs=.true., iospec=iospec_ieee32 )
endif
end function open_ieee32_file
!
!#######################################################################
!
!
! Closes files that are opened by: open_namelist_file, open restart_file,
! and open_ieee32_file. Users should use mpp_close for other cases.
!
!
! unit number of the file to be closed
!
!
! action to be performed: can be 'delete'
!
subroutine close_file (unit, status, dist)
integer, intent(in) :: unit
character(len=*), intent(in), optional :: status
logical, intent(in), optional :: dist
if (.not.module_is_initialized) call fms_io_init ( )
if(PRESENT(dist))then
! If distributed, return if not I/O root
if(dist)then
if(.not. mpp_is_dist_ioroot(dr_set_size)) return
endif
endif
if (unit == stdlog()) return
if (present(status)) then
if (lowercase(trim(status)) == 'delete') then
call mpp_close (unit, action=MPP_DELETE)
else
call mpp_error(FATAL,'fms_io(close_file): status should be DELETE')
endif
else
call mpp_close (unit)
endif
end subroutine close_file
!
!#######################################################################
!
!
! set_domain is called to save the domain2d data type prior to
! calling the distributed data I/O routines, read_data and write_data.
!
!
! domain to be passed to routines in fms_io_mod, Current_domain will point to
! this Domain2
!
subroutine set_domain (Domain2)
type(domain2D), intent(in), target :: Domain2
if (.NOT.module_is_initialized) call fms_io_init ( )
! --- set_domain must be called before a read_data or write_data ---
if (associated(Current_domain)) nullify (Current_domain)
Current_domain => Domain2
! --- module indexing to shorten read/write routines ---
call mpp_get_compute_domain (Current_domain,is ,ie ,js ,je )
call mpp_get_data_domain (Current_domain,isd,ied,jsd,jed)
call mpp_get_global_domain (Current_domain,isg,ieg,jsg,jeg)
end subroutine set_domain
!#######################################################################
!
!
subroutine nullify_domain ()
!
! Use to nulify domain that has been assigned by set_domain.
!
if (.NOT.module_is_initialized) call fms_io_init ( )
! --- set_domain must be called before a read_data or write_data ---
if (associated(Current_domain)) nullify (Current_domain)
is=0;ie=0;js=0;je=0
isd=0;ied=0;jsd=0;jed=0
isg=0;ieg=0;jsg=0;jeg=0
end subroutine nullify_domain
!
!
!
! This routine is the reverse of set_domain above. This routine is called when
! users want to retrieve the domain2d that is used in fms_io_mod
!
!
! domain returned from fms_io_mod.
!
subroutine return_domain(domain2)
type(domain2D), intent(inout) :: domain2
if (associated(Current_domain)) then
domain2 = Current_domain
else
domain2 = NULL_DOMAIN2D
endif
end subroutine return_domain
!
!#######################################################################
! this will be a private routine with the next release
! users should get the domain decomposition from the domain2d data type
!#######################################################################
!
!
! This will be a private routine with the next release.
! Users should get the domain decomposition from the domain2d data type.
!
!
! array containing beginning and ending indices of global and compute domain in x direction
!
!
! array containing beginning and ending indices of global and compute domain in y direction
!
subroutine get_domain_decomp ( x, y )
integer, intent(out), dimension(4) :: x, y
if (mpp_pe() == mpp_root_pe()) call mpp_error(NOTE, &
'subroutine get_domain_decomp will be removed with the next release')
x = (/ isg, ieg, is, ie /)
y = (/ jsg, jeg, js, je /)
end subroutine get_domain_decomp
!
subroutine get_axis_cart(axis, cart)
type(axistype), intent(in) :: axis
character(len=1), intent(out) :: cart
character(len=1) :: axis_cart
character(len=16), dimension(2) :: lon_names, lat_names
character(len=16), dimension(3) :: z_names
character(len=16), dimension(2) :: t_names
character(len=16), dimension(2) :: lon_units, lat_units
character(len=8) , dimension(4) :: z_units
character(len=3) , dimension(4) :: t_units
character(len=32) :: name
integer :: i
lon_names = (/'lon','x '/)
lat_names = (/'lat','y '/)
z_names = (/'depth ','height','z '/)
t_names = (/'time','t '/)
lon_units = (/'degrees_e ', 'degrees_east'/)
lat_units = (/'degrees_n ', 'degrees_north'/)
z_units = (/'cm ','m ','pa ','hpa'/)
t_units = (/'sec', 'min','hou','day'/)
call mpp_get_atts(axis,cartesian=axis_cart)
cart = 'N'
if (axis_cart == 'x' ) cart = 'X'
if (axis_cart == 'y' ) cart = 'Y'
if (axis_cart == 'z' ) cart = 'Z'
if (axis_cart == 't' ) cart = 'T'
if (cart /= 'X' .and. cart /= 'Y' .and. cart /= 'Z' .and. cart /= 'T') then
call mpp_get_atts(axis,name=name)
name = lowercase(name)
do i=1,size(lon_names(:))
if (lowercase(name(1:3)) == trim(lon_names(i))) cart = 'X'
enddo
do i=1,size(lat_names(:))
if (name(1:3) == trim(lat_names(i))) cart = 'Y'
enddo
do i=1,size(z_names(:))
if (name == trim(z_names(i))) cart = 'Z'
enddo
do i=1,size(t_names(:))
if (name(1:3) == t_names(i)) cart = 'T'
enddo
end if
if (cart /= 'X' .and. cart /= 'Y' .and. cart /= 'Z' .and. cart /= 'T') then
call mpp_get_atts(axis,units=name)
name = lowercase(name)
do i=1,size(lon_units(:))
if (trim(name) == trim(lon_units(i))) cart = 'X'
enddo
do i=1,size(lat_units(:))
if (trim(name) == trim(lat_units(i))) cart = 'Y'
enddo
do i=1,size(z_units(:))
if (trim(name) == trim(z_units(i))) cart = 'Z'
enddo
do i=1,size(t_units(:))
if (name(1:3) == trim(t_units(i))) cart = 'T'
enddo
end if
return
end subroutine get_axis_cart
! The following function is here as a last resort.
! This is copied from what was utilities_mod in order that redundant code
! could be deleted.
function open_file(file, form, action, access, threading, recl, dist) result(unit)
character(len=*), intent(in) :: file
character(len=*), intent(in), optional :: form, action, access, threading
integer , intent(in), optional :: recl
logical , intent(in), optional :: dist ! Distributed open?
integer :: unit
character(len=32) :: form_local, action_local, access_local, thread_local
character(len=32) :: action_ieee32
logical :: open, no_headers, do_ieee32
integer :: mpp_format, mpp_action, mpp_access, mpp_thread
!-----------------------------------------------------------------------
if ( .not. module_is_initialized ) call fms_io_init ( )
if (present(action)) then ! must be present
action_local = action
else
call mpp_error (FATAL, 'open_file in fms_mod : argument action not present')
endif
unit = 0 ! Initialize return value. Note that mpp_open will call mpi_abort on error
if(PRESENT(dist))then
if(lowercase(trim(action_local)) /= 'read') &
call mpp_error(FATAL,'open_file in fms_mod: distributed'//lowercase(trim(action_local))// &
' not currently supported')
! If distributed, return if not I/O root
if(dist) then
if(.not. mpp_is_dist_ioroot(dr_set_size)) return
endif
endif
! ---- return stdlog if this is the logfile ----
if (trim(file) == 'logfile.out') then
unit = stdlog()
return
endif
! ---- is this file open and connected to a unit ?? ----
inquire (file=trim(file), opened=open, number=unit)
! cannot open a file that is already open
! except for the log file
if ( open .and. unit >= 0 ) then
call mpp_error (FATAL, 'open_file in fms_mod : '// &
'file '//trim(file)//' is already open')
endif
! --- defaults ---
form_local = 'formatted'; if (present(form)) form_local = form
access_local = 'sequential'; if (present(access)) access_local = access
thread_local = 'single'; if (present(threading)) thread_local = threading
no_headers = .true.
do_ieee32 = .false.
! --- file format ---
select case (lowercase(trim(form_local)))
case ('formatted')
mpp_format = MPP_ASCII
case ('ascii')
mpp_format = MPP_ASCII
case ('unformatted')
mpp_format = MPP_NATIVE
case ('native')
mpp_format = MPP_NATIVE
case ('ieee32')
do_ieee32 = .true.
case ('netcdf')
mpp_format = MPP_NETCDF
case default
call mpp_error (FATAL, 'open_file in fms_mod : '// &
'invalid option for argument form')
end select
! --- action (read,write,append) ---
select case (lowercase(trim(action_local)))
case ('read')
mpp_action = MPP_RDONLY
case ('write')
mpp_action = MPP_OVERWR
case ('append')
mpp_action = MPP_APPEND
case default
call mpp_error (FATAL, 'open_file in fms_mod : '// &
'invalid option for argument action')
end select
! --- file access (sequential,direct) ---
select case (lowercase(trim(access_local)))
case ('sequential')
mpp_access = MPP_SEQUENTIAL
case ('direct')
mpp_access = MPP_DIRECT
case default
call mpp_error (FATAL, 'open_file in fms_mod : '// &
'invalid option for argument access')
end select
! --- threading (single,multi) ---
select case (lowercase(trim(thread_local)))
case ('single')
mpp_thread = MPP_SINGLE
case ('multi')
mpp_thread = MPP_MULTI
case default
call mpp_error (FATAL, 'open_file in fms_mod : '// &
'invalid option for argument thread')
if (trim(file) /= '_read_error.nml') no_headers = .false.
end select
! ---------------- open file -----------------------
if ( .not.do_ieee32 ) then
call mpp_open ( unit, file, form=mpp_format, action=mpp_action, &
access=mpp_access, threading=mpp_thread, &
fileset=MPP_SINGLE,nohdrs=no_headers, recl=recl )
else
! special open for ieee32 file
! fms_mod has iospec value
! pass local action flag to open changing append to write
action_ieee32 = action_local
if (lowercase(trim(action_ieee32)) == 'append') action_ieee32 = 'write'
unit = open_ieee32_file ( file, action_ieee32 )
endif
!-----------------------------------------------------------------------
end function open_file
!#######################################################################
function string_from_integer(n)
integer, intent(in) :: n
character(len=16) :: string_from_integer
if(n<0) then
call mpp_error(FATAL, 'fms_io_mod: n should be non-negative integer, contact developer')
else if( n<10 ) then
write(string_from_integer,'(i1)') n
else if( n<100 ) then
write(string_from_integer,'(i2)') n
else if( n<1000 ) then
write(string_from_integer,'(i3)') n
else if( n<10000 ) then
write(string_from_integer,'(i4)') n
else if( n<100000 ) then
write(string_from_integer,'(i5)') n
else if( n<1000000 ) then
write(string_from_integer,'(i6)') n
else if( n<10000000 ) then
write(string_from_integer,'(i7)') n
else if( n<100000000 ) then
write(string_from_integer,'(i8)') n
else
call mpp_error(FATAL, 'fms_io_mod: n is too big, contact developer')
end if
return
end function string_from_integer
!#######################################################################
function string_from_real(a)
real, intent(in) :: a
character(len=32) :: string_from_real
write(string_from_real,*) a
return
end function string_from_real
!#######################################################################
subroutine get_tile_string(str_out, str_in, tile, str2_in)
character(len=*), intent(inout) :: str_out
character(len=*), intent(in) :: str_in
integer, intent(in) :: tile
character(len=*), intent(in), optional :: str2_in
if(tile > 0 .AND. tile < 9) then
write(str_out,'(a,i1)') trim(str_in), tile
else if(tile >= 10 .AND. tile < 99) then
write(str_out,'(a,i2)') trim(str_in), tile
else
call mpp_error(FATAL, "FMS_IO: get_tile_string: tile must be a positive number less than 100")
end if
if(present(str2_in)) str_out=trim(str_out)//trim(str2_in)
end subroutine get_tile_string
!#####################################################################
subroutine get_mosaic_tile_file_sg(file_in, file_out, is_no_domain, domain, tile_count)
character(len=*), intent(in) :: file_in
character(len=*), intent(out) :: file_out
logical, intent(in) :: is_no_domain
type(domain2D), intent(in), optional, target :: domain
integer, intent(in), optional :: tile_count
character(len=256) :: basefile, tilename
integer :: lens, ntiles, ntileMe, tile, my_tile_id
integer, dimension(:), allocatable :: tile_id
type(domain2d), pointer, save :: d_ptr =>NULL()
logical :: domain_exist
if(index(file_in, '.nc', back=.true.)==0) then
basefile = trim(file_in)
else
lens = len_trim(file_in)
if(file_in(lens-2:lens) .NE. '.nc') call mpp_error(FATAL, &
'fms_io_mod: .nc should be at the end of file '//trim(file_in))
basefile = file_in(1:lens-3)
end if
!--- get the tile name
ntiles = 1
my_tile_id = 1
domain_exist = .false.
if(PRESENT(domain))then
domain_exist = .true.
ntiles = mpp_get_ntile_count(domain)
d_ptr => domain
elseif (ASSOCIATED(Current_domain) .AND. .NOT. is_no_domain ) then
domain_exist = .true.
ntiles = mpp_get_ntile_count(Current_domain)
d_ptr => Current_domain
endif
if(domain_exist) then
ntileMe = mpp_get_current_ntile(d_ptr)
allocate(tile_id(ntileMe))
tile_id = mpp_get_tile_id(d_ptr)
tile = 1
if(present(tile_count)) tile = tile_count
my_tile_id = tile_id(tile)
endif
if(ntiles > 1 .or. my_tile_id > 1 )then
tilename = 'tile'//string(my_tile_id)
if(index(basefile,'.'//trim(tilename),back=.true.) == 0)then
basefile = trim(basefile)//'.'//trim(tilename);
end if
end if
if(allocated(tile_id)) deallocate(tile_id)
file_out = trim(basefile)//'.nc'
d_ptr =>NULL()
end subroutine get_mosaic_tile_file_sg
subroutine get_mosaic_tile_file_ug(file_in, file_out, domain)
character(len=*), intent(in) :: file_in
character(len=*), intent(out) :: file_out
type(domainUG), intent(in), optional :: domain
character(len=256) :: basefile, tilename
integer :: lens, ntiles, my_tile_id
if(index(file_in, '.nc', back=.true.)==0) then
basefile = trim(file_in)
else
lens = len_trim(file_in)
if(file_in(lens-2:lens) .NE. '.nc') call mpp_error(FATAL, &
'fms_io_mod: .nc should be at the end of file '//trim(file_in))
basefile = file_in(1:lens-3)
end if
!--- get the tile name
ntiles = 1
my_tile_id = 1
if(PRESENT(domain))then
ntiles = mpp_get_UG_domain_ntiles(domain)
my_tile_id = mpp_get_UG_domain_tile_id(domain)
endif
if(ntiles > 1 .or. my_tile_id > 1 )then
tilename = 'tile'//string(my_tile_id)
if(index(basefile,'.'//trim(tilename),back=.true.) == 0)then
basefile = trim(basefile)//'.'//trim(tilename);
end if
end if
file_out = trim(basefile)//'.nc'
end subroutine get_mosaic_tile_file_ug
!#############################################################################
subroutine get_mosaic_tile_grid(grid_file, mosaic_file, domain, tile_count)
character(len=*), intent(out) :: grid_file
character(len=*), intent(in) :: mosaic_file
type(domain2D), intent(in) :: domain
integer, intent(in), optional :: tile_count
integer :: tile, ntileMe
integer, dimension(:), allocatable :: tile_id
tile = 1
if(present(tile_count)) tile = tile_count
ntileMe = mpp_get_current_ntile(domain)
allocate(tile_id(ntileMe))
tile_id = mpp_get_tile_id(domain)
call read_data(mosaic_file, "gridfiles", grid_file, level=tile_id(tile) )
grid_file = 'INPUT/'//trim(grid_file)
deallocate(tile_id)
end subroutine get_mosaic_tile_grid
subroutine get_var_att_value_text(file, varname, attname, attvalue)
character(len=*), intent(in) :: file
character(len=*), intent(in) :: varname
character(len=*), intent(in) :: attname
character(len=*), intent(inout) :: attvalue
integer :: unit
call mpp_open(unit,trim(file),MPP_RDONLY,MPP_NETCDF,threading=MPP_MULTI,fileset=MPP_SINGLE)
call mpp_get_att_value(unit, varname, attname, attvalue)
call mpp_close(unit)
return
end subroutine get_var_att_value_text
!#############################################################################
! return false if the attribute is not found in the file.
function get_global_att_value_text(file, att, attvalue)
character(len=*), intent(in) :: file
character(len=*), intent(in) :: att
character(len=*), intent(inout) :: attvalue
logical :: get_global_att_value_text
integer :: unit, ndim, nvar, natt, ntime, i
type(atttype), allocatable :: global_atts(:)
get_global_att_value_text = .false.
call mpp_open(unit,trim(file),MPP_RDONLY,MPP_NETCDF,threading=MPP_MULTI,fileset=MPP_SINGLE)
call mpp_get_info(unit, ndim, nvar, natt, ntime)
allocate(global_atts(natt))
call mpp_get_atts(unit,global_atts)
do i=1,natt
if( trim(mpp_get_att_name(global_atts(i))) == trim(att) ) then
attvalue = trim(mpp_get_att_char(global_atts(i)))
get_global_att_value_text = .true.
exit
end if
end do
deallocate(global_atts)
return
end function get_global_att_value_text
!#############################################################################
! return false if the attribute is not found in the file.
function get_global_att_value_real(file, att, attvalue)
character(len=*), intent(in) :: file
character(len=*), intent(in) :: att
real, intent(inout) :: attvalue
logical :: get_global_att_value_real
integer :: unit, ndim, nvar, natt, ntime, i
type(atttype), allocatable :: global_atts(:)
get_global_att_value_real = .false.
call mpp_open(unit,trim(file),MPP_RDONLY,MPP_NETCDF,threading=MPP_MULTI,fileset=MPP_SINGLE)
call mpp_get_info(unit, ndim, nvar, natt, ntime)
allocate(global_atts(natt))
call mpp_get_atts(unit,global_atts)
do i=1,natt
if( trim(mpp_get_att_name(global_atts(i))) == trim(att) ) then
attvalue = mpp_get_att_real_scalar(global_atts(i))
get_global_att_value_real = .true.
exit
end if
end do
deallocate(global_atts)
return
end function get_global_att_value_real
!#############################################################################
! This routine will get the actual file name, as well as if read_dist is true or false.
! return true if such file exist and return false if not.
function get_file_name(orig_file, actual_file, read_dist, io_domain_exist, no_domain, domain, &
tile_count)
character(len=*), intent(in) :: orig_file
character(len=*), intent(out) :: actual_file
logical, intent(out) :: read_dist
logical, intent(out) :: io_domain_exist
logical, optional, intent(in) :: no_domain
type(domain2D), target, optional, intent(in) :: domain
integer, optional, intent(in) :: tile_count
logical :: get_file_name
type(domain2d), pointer, save :: d_ptr, io_domain
logical :: fexist, is_no_domain
integer :: tile_id(1)
character(len=256) :: fname
character(len=512) :: actual_file_tmp
is_no_domain=.false.
if(PRESENT(no_domain)) is_no_domain = no_domain
fexist = .false.
read_dist = .false.
get_file_name = .false.
io_domain_exist = .false.
!--- The file maybe not netcdf file, we just check the original file.
if(index(orig_file, '.nc', back=.true.) == 0) then
inquire (file=trim(orig_file), exist=fexist)
if(fexist) then
actual_file = orig_file
get_file_name = .true.
return
endif
endif
if(present(domain)) then
d_ptr => domain
elseif (ASSOCIATED(Current_domain) .AND. .NOT. is_no_domain ) then
d_ptr => Current_domain
endif
!JWD: This is likely a temporary fix. Since fms_io needs to know tile_count,
!JWD: I just don't see how the physics can remain "tile neutral"
call get_mosaic_tile_file(orig_file, actual_file, is_no_domain, domain, tile_count)
!--- check if the file is group redistribution.
if(ASSOCIATED(d_ptr)) then
io_domain => mpp_get_io_domain(d_ptr)
if(associated(io_domain)) then
tile_id = mpp_get_tile_id(io_domain)
write(fname, '(a,i4.4)' ) trim(actual_file)//'.', tile_id(1)
inquire (file=trim(fname), exist=fexist)
if(.not. fexist) then
write(fname, '(a,i6.6)' ) trim(actual_file)//'.', tile_id(1)
inquire (file=trim(fname), exist=fexist)
endif
if(fexist) io_domain_exist = .true.
endif
io_domain=>NULL()
endif
if(fexist) then
read_dist = .true.
d_ptr => NULL()
get_file_name = .true.
return
endif
inquire (file=trim(actual_file), exist=fexist)
if(fexist) then
d_ptr => NULL()
get_file_name = .true.
return
endif
!Perhaps the file has an ensemble instance appendix
if(len_trim(filename_appendix) > 0) then
call get_instance_filename(orig_file, actual_file)
if(index(orig_file, '.nc', back=.true.) == 0) then
inquire (file=trim(actual_file), exist=fexist)
if(fexist) then
d_ptr => NULL()
get_file_name = .true.
return
endif
endif
! Set actual_file to tmp for passing to get_mosaic_tile_file
actual_file_tmp = actual_file
call get_mosaic_tile_file(actual_file_tmp, actual_file, is_no_domain, domain, tile_count)
!--- check if the file is group redistribution.
if(ASSOCIATED(d_ptr)) then
io_domain => mpp_get_io_domain(d_ptr)
if(associated(io_domain)) then
tile_id = mpp_get_tile_id(io_domain)
if(mpp_npes()>10000) then
write(fname, '(a,i6.6)' ) trim(actual_file)//'.', tile_id(1)
else
write(fname, '(a,i4.4)' ) trim(actual_file)//'.', tile_id(1)
endif
inquire (file=trim(fname), exist=fexist)
if(fexist) io_domain_exist = .true.
endif
io_domain=>NULL()
endif
if(fexist) then
read_dist = .true.
d_ptr => NULL()
get_file_name = .true.
return
endif
inquire (file=trim(actual_file), exist=fexist)
if(fexist) then
d_ptr => NULL()
get_file_name = .true.
return
endif
endif
end function get_file_name
!#############################################################################
subroutine get_file_unit(filename, unit, index_file, read_dist, io_domain_exist, domain )
character(len=*), intent(in) :: filename
integer, intent(out) :: unit, index_file
logical, intent(in) :: read_dist, io_domain_exist
type(domain2d), optional, intent(in) :: domain
logical :: file_opened
integer :: i
! Need to check if filename has been opened or not
file_opened=.false.
do i=1,num_files_r
if (files_read(i)%name == trim(filename)) then
index_file = i
unit = files_read(index_file)%unit
return
endif
enddo
! need to open the file now
! Increase num_files_r and set file_type
if(num_files_r == max_files_r) & ! need to have bigger max_files_r
call mpp_error(FATAL,'fms_io(get_file_unit): max_files_r exceeded, increase it via fms_io_nml')
num_files_r=num_files_r + 1
if(read_dist) then
if(io_domain_exist) then
if(present(domain)) then
call mpp_open(unit,filename,form=form,action=MPP_RDONLY,threading=MPP_MULTI, &
fileset=MPP_MULTI, domain=domain)
else if(ASSOCIATED(current_domain) ) then
call mpp_open(unit,filename,form=form,action=MPP_RDONLY,threading=MPP_MULTI, &
fileset=MPP_MULTI, domain=current_domain)
else
call mpp_error(FATAL,'fms_io(get_file_unit): when io_domain_exsit = .true., '// &
'either domain is present or current_domain is associated')
endif
else
call mpp_open(unit,trim(filename),form=form,action=MPP_RDONLY,threading=MPP_MULTI, &
fileset=MPP_MULTI)
endif
else
call mpp_open(unit,trim(filename),form=form,action=MPP_RDONLY,threading=MPP_MULTI, &
fileset=MPP_SINGLE)
end if
files_read(num_files_r)%name = trim(filename)
allocate(files_read(num_files_r)%var (max_fields) )
files_read(num_files_r)%nvar = 0
index_file = num_files_r
files_read(index_file)%unit = unit
end subroutine get_file_unit
!#############################################################################
subroutine get_field_id(unit, index_file, fieldname, index_field, is_no_domain, is_not_dim)
integer, intent(in) :: unit
integer, intent(in) :: index_file
character(len=*), intent(in) :: fieldname
integer, intent(out) :: index_field
logical, intent(in) :: is_no_domain
logical, intent(in) :: is_not_dim
character(len=128) :: name
type(axistype), dimension(max_axes) :: axes
type(fieldtype), dimension(max_fields) :: fields
integer :: i, j, ndim, nvar, natt, var_dim
integer :: siz_in(4)
index_field = -1
do j = 1, files_read(index_file)%nvar
if (trim(files_read(index_file)%var(j)%name) == trim(fieldname)) then
index_field = j
return
endif
enddo
!--- fieldname is not read, so need to get fieldname from file
files_read(index_file)%nvar = files_read(index_file)%nvar + 1
if(files_read(index_file)%nvar > max_fields) then
write(error_msg,'(I3,"/",I3)') files_read(index_file)%nvar, max_fields
call mpp_error(FATAL,'fms_io(get_field_id): max_fields exceeded, needs increasing, nvar/max_fields=' &
//trim(error_msg))
endif
call mpp_get_info(unit, ndim, nvar, natt, files_read(index_file)%max_ntime)
if(files_read(index_file)%max_ntime < 1) files_read(index_file)%max_ntime = 1
if(nvar > max_fields) then
write(error_msg,'(I3,"/",I3)') files_read(index_file)%nvar,max_fields
call mpp_error(FATAL,'fms_io(get_field_id): max_fields too small needs increasing,nvar/max_fields=' &
//trim(error_msg)//'in file'//trim(files_read(index_file)%name))
endif
call mpp_get_fields(unit, fields(1:nvar))
siz_in = 1
index_field = files_read(index_file)%nvar
files_read(index_file)%var(index_field)%is_dimvar = .false.
do i=1, nvar
call mpp_get_atts(fields(i),name=name,ndim=var_dim,siz=siz_in)
if(var_dim .GT. 4) call mpp_error(FATAL, 'fms_io(get_field_id): number of dimension of field '// &
trim(name)//' in file '//trim(files_read(index_file)%name)//' should not be greater than 4')
if (lowercase(trim(name)) == lowercase(trim(fieldname))) then ! found the variable
if(var_dim .lt.3) then
do j=var_dim+1,3
siz_in(j)=1
enddo
endif
files_read(index_file)%var(index_field)%name = fieldname
files_read(index_file)%var(index_field)%field = fields(i)
files_read(index_file)%var(index_field)%siz(1:4) = siz_in(1:4)
files_read(index_file)%var(index_field)%gsiz(1:3) = siz_in(1:3)
return
endif
enddo
!--- the fieldname may be a dimension variable.
if( .not. is_not_dim) then
if (ndim > max_axes) then
write(error_msg,'(I3,"/",I3)') ndim, max_axes
call mpp_error(FATAL,'fms_io(get_field_id): max_axes exceeded, needs increasing, ndim/max_fields=' &
//trim(error_msg)//' in file '//trim(files_read(index_file)%name))
endif
call mpp_get_axes(unit, axes(1:ndim))
do i=1,ndim
call mpp_get_atts(axes(i), name=name, len = siz_in(1))
if (lowercase(trim(name)) == lowercase(trim(fieldname))) then
! if(.not. is_no_domain) call mpp_error(FATAL, &
! 'fms_io(get_field_id): the field is a dimension variable, no_domain should be true.')
files_read(index_file)%var(index_field)%is_dimvar = .true.
files_read(index_file)%var(index_field)%name = fieldname
files_read(index_file)%var(index_field)%axis = axes(i)
files_read(index_file)%var(index_field)%siz(1:4) = siz_in(1:4)
files_read(index_file)%var(index_field)%gsiz(1:3) = siz_in(1:3)
return
endif
enddo
end if
!--- the field is not in the file when reaching here.
call mpp_error(FATAL, 'fms_io(get_field_id): field '//trim(fieldname)// &
' NOT found in file '//trim(files_read(index_file)%name))
end subroutine get_field_id
!#######################################################################
! check the existence of the given file name
! if the file_name string has zero length or the
! first character is blank return a false result
!
!
! Checks the existence of a given file name.
!
!
! Checks the existence of the given file name.
! If the file_name string has zero length or the
! first character is blank return a false result.
!
!
! file_exist ( file_name )
!
!
! A file name (or path name) that is checked for existence.
!
!
! This function returns a logical result. If file_name exists the result
! is true, otherwise false is returned.
! If the length of character string "file_name" is zero or the first
! character is blank, then the returned value will be false.
! When reading a file, this function is often used in conjunction with
! routine open_file.
!
!
! Before calling write_data you must first call set_domain with domain2d data
! type associated with the distributed data you are writing.
!
function file_exist (file_name, domain, no_domain)
character(len=*), intent(in) :: file_name
type(domain2d), intent(in), optional :: domain
logical, intent(iN), optional :: no_domain
logical :: file_exist, is_no_domain
character(len=256) :: fname
logical :: read_dist, io_domain_exist
is_no_domain = .false.
if(present(no_domain)) is_no_domain = no_domain
!--- to deal with mosaic file, in this case, the file is assumed to be in netcdf format
file_exist = get_file_name(file_name, fname, read_dist, io_domain_exist, no_domain=is_no_domain, domain=domain)
if(is_no_domain) return
if(.not.file_exist) file_exist=get_file_name(file_name, fname, read_dist, io_domain_exist, no_domain=.true.)
return
end function file_exist
!
!#######################################################################
!
!
! check if a given field name exists in a given file name.
!
!
! check if a given field name exists in a given file name.
! If the field_name string has zero length or the
! first character is blank return a false result.
! if the file file_name don't exist, return a false result.
!
!
! field_exist ( file_name, field_name )
!
!
! A file name (or path name) that is checked for existence.
!
!
! A field name that is checked for existence.
!
!
! This function returns a logical result. If field exists in the
! file file_name, the result is true, otherwise false is returned.
! If the length of character string "field_name" is zero or the first
! character is blank, then the returned value will be false.
! if the file file_name don't exist, return a false result.
!
function field_exist (file_name, field_name, domain, no_domain)
character(len=*), intent(in) :: file_name
character(len=*), intent(in) :: field_name
type(domain2d), intent(in), optional, target :: domain
logical, intent(in), optional :: no_domain
logical :: field_exist, is_no_domain
integer :: unit, ndim, nvar, natt, ntime, i, nfile
character(len=64) :: name
type(fieldtype), allocatable :: fields(:)
logical :: file_exist, read_dist, io_domain_exist
character(len=256) :: fname
field_exist = .false.
if (len_trim(field_name) == 0) return
if (field_name(1:1) == ' ') return
is_no_domain = .false.
if(present(no_domain)) is_no_domain = no_domain
file_exist=get_file_name(file_name, fname, read_dist, io_domain_exist, no_domain=is_no_domain, domain=domain)
if(file_exist) then
call get_file_unit(fname, unit, nfile, read_dist, io_domain_exist, domain=domain)
call mpp_get_info(unit, ndim, nvar, natt, ntime)
allocate(fields(nvar))
call mpp_get_fields(unit,fields)
do i=1, nvar
call mpp_get_atts(fields(i),name=name)
if(lowercase(trim(name)) == lowercase(trim(field_name))) field_exist = .true.
enddo
deallocate(fields)
endif
if(field_exist .or. is_no_domain) return
file_exist = get_file_name(file_name, fname, read_dist, io_domain_exist, no_domain=.true.)
if(file_exist) then
call get_file_unit(fname, unit, nfile, read_dist, io_domain_exist)
call mpp_get_info(unit, ndim, nvar, natt, ntime)
allocate(fields(nvar))
call mpp_get_fields(unit,fields)
do i=1, nvar
call mpp_get_atts(fields(i),name=name)
if(lowercase(trim(name)) == lowercase(trim(field_name))) field_exist = .true.
enddo
deallocate(fields)
endif
return
end function field_exist
!
subroutine get_filename_appendix(string_out)
character(len=*) , intent(out) :: string_out
string_out = trim(filename_appendix)
end subroutine get_filename_appendix
subroutine nullify_filename_appendix()
filename_appendix = ''
end subroutine nullify_filename_appendix
subroutine set_filename_appendix(string_in)
character(len=*) , intent(in) :: string_in
integer :: index_num
! Check if string has already been added
index_num = index(filename_appendix, string_in)
if ( index_num .le. 0 ) then
filename_appendix = trim(filename_appendix)//trim(string_in)
end if
end subroutine set_filename_appendix
subroutine get_instance_filename(name_in,name_out)
character(len=*) , intent(in) :: name_in
character(len=*), intent(inout) :: name_out
integer :: length
length = len_trim(name_in)
name_out = name_in(1:length)
if(len_trim(filename_appendix) > 0) then
if(name_in(length-2:length) == '.nc') then
name_out = name_in(1:length-3)//'.'//trim(filename_appendix)//'.nc'
else
name_out = name_in(1:length) //'.'//trim(filename_appendix)
end if
end if
end subroutine get_instance_filename
!#######################################################################
subroutine parse_mask_table_2d(mask_table, maskmap, modelname)
character(len=*), intent(in) :: mask_table
logical, intent(out) :: maskmap(:,:)
character(len=*), intent(in) :: modelname
integer :: nmask, layout(2)
integer, allocatable :: mask_list(:,:)
integer :: unit, mystat, n, stdoutunit
character(len=128) :: record
maskmap = .true.
nmask = 0
stdoutunit = stdout()
if( mpp_pe() == mpp_root_pe() ) then
call mpp_open(unit, mask_table, action=MPP_RDONLY)
read(unit, FMT=*, IOSTAT=mystat) nmask
if( mystat /= 0 ) call mpp_error(FATAL, &
"fms_io(parse_mask_table_2d): Error reading nmask from file " //trim(mask_table))
write(stdoutunit,*)"parse_mask_table: Number of domain regions masked in ", trim(modelname), " = ", nmask
if( nmask > 0 ) then
!--- read layout from mask_table and confirm it matches the shape of maskmap
read(unit, FMT=*, IOSTAT=mystat) layout
if( mystat /= 0 ) call mpp_error(FATAL, &
"fms_io(parse_mask_talbe_2d): Error reading layout from file " //trim(mask_table))
if( (layout(1) .NE. size(maskmap,1)) .OR. (layout(2) .NE. size(maskmap,2)) )then
write(stdoutunit,*)"layout=", layout, ", size(maskmap) = ", size(maskmap,1), size(maskmap,2)
call mpp_error(FATAL, "fms_io(parse_mask_table_2d): layout in file "//trim(mask_table)// &
"does not match size of maskmap for "//trim(modelname))
endif
!--- make sure mpp_npes() == layout(1)*layout(2) - nmask
if( mpp_npes() .NE. layout(1)*layout(2) - nmask ) call mpp_error(FATAL, &
"fms_io(parse_mask_table_2d): mpp_npes() .NE. layout(1)*layout(2) - nmask for "//trim(modelname))
endif
endif
call mpp_broadcast(nmask, mpp_root_pe())
if(nmask==0) then
if( mpp_pe() == mpp_root_pe() ) call mpp_close(unit)
return
endif
allocate(mask_list(nmask,2))
if( mpp_pe() == mpp_root_pe() ) then
n = 0
do while( .true. )
read(unit,'(a)',end=999) record
if (record(1:1) == '#') cycle
if (record(1:10) == ' ') cycle
n = n + 1
if( n > nmask ) then
call mpp_error(FATAL, "fms_io(parse_mask_table_2d): number of mask_list entry "// &
"is greater than nmask in file "//trim(mask_table) )
endif
read(record,*,err=888) mask_list(n,1), mask_list(n,2)
enddo
888 call mpp_error(FATAL, "fms_io(parse_mask_table_2d): Error in reading mask_list from file "//trim(mask_table))
999 continue
!--- make sure the number of entry for mask_list is nmask
if( n .NE. nmask) call mpp_error(FATAL, &
"fms_io(parse_mask_table_2d): number of mask_list entry does not match nmask in file "//trim(mask_table))
call mpp_close(unit)
endif
call mpp_broadcast(mask_list, 2*nmask, mpp_root_pe())
do n = 1, nmask
if(debug_mask_list) then
write(stdoutunit,*) "==>NOTE from parse_mask_table_2d: ", trim(modelname), " mask_list = ", mask_list(n,1), mask_list(n,2)
endif
maskmap(mask_list(n,1),mask_list(n,2)) = .false.
enddo
deallocate(mask_list)
end subroutine parse_mask_table_2d
!#######################################################################
subroutine parse_mask_table_3d(mask_table, maskmap, modelname)
character(len=*), intent(in) :: mask_table
logical, intent(out) :: maskmap(:,:,:)
character(len=*), intent(in) :: modelname
integer :: nmask, layout(2)
integer, allocatable :: mask_list(:,:)
integer :: unit, mystat, n, stdoutunit, ntiles
character(len=128) :: record
maskmap = .true.
nmask = 0
stdoutunit = stdout()
if( mpp_pe() == mpp_root_pe() ) then
call mpp_open(unit, mask_table, action=MPP_RDONLY)
read(unit, FMT=*, IOSTAT=mystat) nmask
if( mystat /= 0 ) call mpp_error(FATAL, &
"fms_io(parse_mask_table_3d): Error reading nmask from file " //trim(mask_table))
write(stdoutunit,*)"parse_mask_table: Number of domain regions masked in ", trim(modelname), " = ", nmask
if( nmask > 0 ) then
!--- read layout from mask_table and confirm it matches the shape of maskmap
read(unit, FMT=*, IOSTAT=mystat) layout(1), layout(2), ntiles
if( mystat /= 0 ) call mpp_error(FATAL, &
"fms_io(parse_mask_talbe_3d): Error reading layout from file " //trim(mask_table))
if( (layout(1) .NE. size(maskmap,1)) .OR. (layout(2) .NE. size(maskmap,2)) )then
write(stdoutunit,*)"layout=", layout, ", size(maskmap) = ", size(maskmap,1), size(maskmap,2)
call mpp_error(FATAL, "fms_io(parse_mask_table_3d): layout in file "//trim(mask_table)// &
"does not match size of maskmap for "//trim(modelname))
endif
if( ntiles .NE. size(maskmap,3) ) then
write(stdoutunit,*)"ntiles=", ntiles, ", size(maskmap,3) = ", size(maskmap,3)
call mpp_error(FATAL, "fms_io(parse_mask_table_3d): ntiles in file "//trim(mask_table)// &
"does not match size of maskmap for "//trim(modelname))
endif
!--- make sure mpp_npes() == layout(1)*layout(2) - nmask
if( mpp_npes() .NE. layout(1)*layout(2)*ntiles - nmask ) then
print*, "layout=", layout, nmask, mpp_npes()
call mpp_error(FATAL, &
"fms_io(parse_mask_table_3d): mpp_npes() .NE. layout(1)*layout(2) - nmask for "//trim(modelname))
endif
endif
endif
call mpp_broadcast(nmask, mpp_root_pe())
if(nmask==0) then
if( mpp_pe() == mpp_root_pe() ) call mpp_close(unit)
return
endif
allocate(mask_list(nmask,3))
if( mpp_pe() == mpp_root_pe() ) then
n = 0
do while( .true. )
read(unit,'(a)',end=999) record
if (record(1:1) == '#') cycle
if (record(1:10) == ' ') cycle
n = n + 1
if( n > nmask ) then
call mpp_error(FATAL, "fms_io(parse_mask_table_3d): number of mask_list entry "// &
"is greater than nmask in file "//trim(mask_table) )
endif
read(record,*,err=888) mask_list(n,1), mask_list(n,2), mask_list(n,3)
enddo
888 call mpp_error(FATAL, "fms_io(parse_mask_table_3d): Error in reading mask_list from file "//trim(mask_table))
999 continue
!--- make sure the number of entry for mask_list is nmask
if( n .NE. nmask) call mpp_error(FATAL, &
"fms_io(parse_mask_table_3d): number of mask_list entry does not match nmask in file "//trim(mask_table))
call mpp_close(unit)
endif
call mpp_broadcast(mask_list, 3*nmask, mpp_root_pe())
do n = 1, nmask
if(debug_mask_list) then
write(stdoutunit,*) "==>NOTE from parse_mask_table_3d: ", trim(modelname), " mask_list = ", &
mask_list(n,1), mask_list(n,2), mask_list(n,3)
endif
maskmap(mask_list(n,1),mask_list(n,2),mask_list(n,3)) = .false.
enddo
deallocate(mask_list)
end subroutine parse_mask_table_3d
function get_great_circle_algorithm()
logical :: get_great_circle_algorithm
if(.NOT. module_is_initialized) call mpp_error(FATAL, &
"fms_io(use_great_circle_algorithm): fms_io_init is not called yet")
get_great_circle_algorithm = great_circle_algorithm
end function get_great_circle_algorithm
!#######################################################################
!
!
! Prints to the log file (or a specified unit) the (cvs) version id string and
! (cvs) tag name.
!
!
! Prints to the log file (stdlog) or a specified unit the (cvs) version id string
! and (cvs) tag name.
!
!
! call write_version_number ( version [, tag, unit] )
!
!
! string that contains routine name and version number.
!
!
! The tag/name string, this is usually the Name string
! returned by CVS when checking out the code.
!
!
! The Fortran unit number of an open formatted file. If this unit number
! is not supplied the log file unit number is used (stdlog).
!
! prints module version number to the log file of specified unit number
subroutine write_version_number (version, tag, unit)
! in: version = string that contains routine name and version number
!
! optional in:
! tag = cvs tag name that code was checked out with
! unit = alternate unit number to direct output
! (default: unit=stdlog)
character(len=*), intent(in) :: version
character(len=*), intent(in), optional :: tag
integer, intent(in), optional :: unit
integer :: logunit
if (.not.module_is_initialized) call fms_io_init ( )
logunit = stdlog()
if (present(unit)) then
logunit = unit
else
! only allow stdlog messages on root pe
if ( mpp_pe() /= mpp_root_pe() ) return
endif
if (present(tag)) then
write (logunit,'(/,80("="),/(a))') trim(version), trim(tag)
else
write (logunit,'(/,80("="),/(a))') trim(version)
endif
end subroutine write_version_number
!
!----------
!ug support
# 1 "../fms/fms_io_unstructured_register_restart_axis.inc" 1
!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
!----------
!ug support
!------------------------------------------------------------------------------
!>Store a real axis (x,y,z,...) in a restart object assoicated with an
!!unstructured mpp domain.
subroutine fms_io_unstructured_register_restart_axis_r1D(fileObj, &
filename, &
fieldname, &
fdata, &
cartesian, &
domain, &
units, &
longname, &
sense, &
fmin, &
calendar)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! null()
io_domain => mpp_get_UG_io_domain(domain)
io_domain_npes = mpp_get_UG_domain_npes(io_domain)
allocate(pelist(io_domain_npes))
call mpp_get_UG_domain_pelist(io_domain, &
pelist)
allocate(fdata_sizes(io_domain_npes))
fdata_sizes = 0
call mpp_gather((/size(fdata)/), &
fdata_sizes, &
pelist)
if (mpp_pe() .eq. pelist(1)) then
if (maxval(fdata_sizes) .ne. size(fdata) .or. &
minval(fdata_sizes) .ne. size(fdata)) then
call mpp_error(FATAL, &
"fms_io_unstructured_register_restart_axis_r1D:" &
//" the "//trim(cartesian)//" axis must be the" &
//" the same size for all ranks in the" &
//" unstructured I/O domain pelist.")
endif
endif
io_domain => null()
deallocate(pelist)
deallocate(fdata_sizes)
!Set the name of the axis.
fileObj%axes(axis_index)%name = trim(fieldname)
!Point to the inputted unstructured domain for the axis.
fileObj%axes(axis_index)%domain_ug => domain
!Point to the inputted axis data.
fileObj%axes(axis_index)%data => fdata
!Store the inputted cartesian string. (Why?)
fileObj%axes(axis_index)%cartesian = trim(cartesian)
!Set the dimension length for the axis to -1 to signify that this is
!not a "compressed" axis.
fileObj%axes(axis_index)%dimlen = -1
!Store the units for the axis.
if (present(units)) then
fileObj%axes(axis_index)%units = trim(units)
else
fileObj%axes(axis_index)%units = ""
endif
!Store the longname for the axis.
if (present(longname)) then
fileObj%axes(axis_index)%longname = trim(longname)
else
fileObj%axes(axis_index)%longname = ""
endif
!Store the "sense" for the axis. Inputs must be for the z-dimension.
if (present(sense)) then
if (axis_index .ne. ZIDX) then
call mpp_error(FATAL, &
"fms_io_unstructured_register_restart_axis_r1D:" &
//" sense may only be defined for the z-axis.")
endif
if (abs(sense) .ne. 1) then
call mpp_error(FATAL, &
"fms_io_unstructured_register_restart_axis_r1D:" &
//" sense may only have the values +/- 1")
endif
fileObj%axes(axis_index)%sense = sense
else
fileObj%axes(axis_index)%sense = 0
endif
!Store the minimum value allowed for the axis.
if (present(fmin)) then
fileObj%axes(axis_index)%min = fmin
else
fileObj%axes(axis_index)%min = 0
endif
!Store the calendar for the axis. This is only done for the time dimension.
if (axis_index .eq. TIDX) then
fileObj%axes(axis_index)%calendar = trim(calendar)
endif
return
end subroutine fms_io_unstructured_register_restart_axis_r1D
!------------------------------------------------------------------------------
!>Store an integer "compressed" axis in a restart object assoicated with an
!!unstructured mpp domain.
subroutine fms_io_unstructured_register_restart_axis_i1D(fileObj, &
filename, &
fieldname, &
fdata, &
compressed, &
compressed_axis, &
dimlen, &
domain, &
dimlen_name, &
dimlen_lname, &
units, &
longname, &
imin)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! domain
!Initialize the number of data elements each rank in an unstructured I/O
!domain is responsible for.
io_domain => null()
io_domain => mpp_get_UG_io_domain(domain)
io_domain_npes = mpp_get_UG_domain_npes(io_domain)
allocate(fileObj%axes(axis_index)%nelems(io_domain_npes))
fileObj%axes(axis_index)%nelems = 0
fileObj%axes(axis_index)%nelems_for_current_rank = size(fdata)
!Gather the sizes of the inputted data arrays for each rank onto the root
!rank of the I/O domain pelist.
allocate(pelist(io_domain_npes))
call mpp_get_UG_domain_pelist(io_domain, &
pelist)
call mpp_gather((/size(fdata)/), &
fileObj%axes(axis_index)%nelems, &
pelist)
!Gather the inputted data from each rank onto the root rank of the I/O
!domain pelist.
if (mpp_pe() .eq. pelist(1)) then
allocate(fileObj%axes(axis_index)%idx(sum(fileObj%axes(axis_index)%nelems)))
else
!This array for a non-root rank on the I/O domain pelist should never
!be used, but is allocated to signify that this axis is defined for
!this restart object.
allocate(fileObj%axes(axis_index)%idx(1))
fileObj%axes(axis_index)%idx = 0
endif
call mpp_gather(fdata, &
size(fdata), &
fileObj%axes(axis_index)%idx, &
fileObj%axes(axis_index)%nelems, &
pelist)
!Nullify local pointers and deallocate local allocatables.
io_domain => null()
deallocate(pelist)
!Set the "compressed" string for the axis.
fileObj%axes(axis_index)%compressed = trim(compressed)
!Set the dimension length for the axis.
fileObj%axes(axis_index)%dimlen = dimlen
!Set the dimlen_name (???) for the axis.
if (present(dimlen_name)) then
fileObj%axes(axis_index)%dimlen_name = trim(dimlen_name)
else
fileObj%axes(axis_index)%dimlen_name = ""
endif
!Set the dimlen_lname (???) for the axis.
if (present(dimlen_lname)) then
fileObj%axes(axis_index)%dimlen_lname = trim(dimlen_lname)
else
fileObj%axes(axis_index)%dimlen_lname = ""
endif
!Set the units for the axis.
if (present(units)) then
fileObj%axes(axis_index)%units = trim(units)
else
fileObj%axes(axis_index)%units = ""
endif
!Set the longname for the axis.
if (present(longname)) then
fileObj%axes(axis_index)%longname = trim(longname)
else
fileObj%axes(axis_index)%longname = ""
endif
!Set the minimum value for the axis.
if (present(imin)) then
fileObj%axes(axis_index)%imin = imin
else
fileObj%axes(axis_index)%imin = 0
endif
return
end subroutine fms_io_unstructured_register_restart_axis_i1D
!------------------------------------------------------------------------------
!>Store an unlimited axis in a restart object assoicated with an unstructured
!!mpp domain.
subroutine fms_io_unstructured_register_restart_axis_u(fileObj, &
filename, &
fieldname, &
nelems, &
domain, &
units, &
longname)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! domain
!Initialize the number of data elements each rank in an unstructured I/O
!domain is responsible for.
io_domain => null()
io_domain => mpp_get_UG_io_domain(domain)
io_domain_npes = mpp_get_UG_domain_npes(io_domain)
allocate(fileObj%axes(axis_index)%nelems(io_domain_npes))
fileObj%axes(axis_index)%nelems = 0
!Gather the inputted number of elements each rank is responsible for onto
!the root rank of the I/O domain pelist.
allocate(pelist(io_domain_npes))
call mpp_get_UG_domain_pelist(io_domain, &
pelist)
call mpp_gather((/nelems/), &
fileObj%axes(axis_index)%nelems, &
pelist)
!Nullify local pointers and deallocate local allocatables.
io_domain => null()
deallocate(pelist)
!Set the units for the axis.
if (present(units)) then
fileObj%axes(axis_index)%units = trim(units)
else
fileObj%axes(axis_index)%units = ""
endif
!Set the longname for the axis.
if (present(longname)) then
fileObj%axes(axis_index)%longname = trim(longname)
else
fileObj%axes(axis_index)%longname = ""
endif
return
end subroutine fms_io_unstructured_register_restart_axis_u
!------------------------------------------------------------------------------
!----------
# 8657 "../fms/fms_io.F90" 2
# 1 "../fms/fms_io_unstructured_setup_one_field.inc" 1
!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
!----------
!ug support
!>Add a field to a restart object (restart_file_type). Return the index of the
!!inputted field in the fileObj%var array.
subroutine fms_io_unstructured_setup_one_field(fileObj, &
filename, &
fieldname, &
field_dimension_order, &
field_dimension_sizes, &
index_field, &
domain, &
mandatory, &
data_default, &
longname, &
units, &
read_only, &
owns_data)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! 0) then
!If the field already exists in the fileObj%var array, then update its
!time level.
cur_var => null()
cur_var => fileObj%var(index_field)
!Make sure tha the inputted array describing the ordering of the
!dimensions for the field matches the dimension ordering for the
!found field.
do i = 1,size(field_dimension_order)
if (field_dimension_order(i) .ne. cur_var%field_dimension_order(i)) then
call mpp_error(FATAL, &
"fms_io_unstructured_setup_one_field:" &
//" field dimension ordering mismatch for " &
//trim(fieldname)//" of file "//trim(filename))
endif
enddo
!Make sure that the array of field dimension sizes matches the
!dimension sizes of the found field for all dimensions except the
!time level.
if (cur_var%field_dimension_sizes(XIDX) .ne. field_dimension_sizes(XIDX) .or. &
cur_var%field_dimension_sizes(YIDX) .ne. field_dimension_sizes(YIDX) .or. &
cur_var%field_dimension_sizes(CIDX) .ne. field_dimension_sizes(CIDX) .or. &
cur_var%field_dimension_sizes(ZIDX) .ne. field_dimension_sizes(ZIDX) .or. &
cur_var%field_dimension_sizes(HIDX) .ne. field_dimension_sizes(HIDX) .or. &
cur_var%field_dimension_sizes(UIDX) .ne. field_dimension_sizes(UIDX) .or. &
cur_var%field_dimension_sizes(CCIDX) .ne. field_dimension_sizes(CCIDX)) then
call mpp_error(FATAL, &
"fms_io_unstructured_setup_one_field:" &
//" field dimension size mismatch for field " &
//trim(fieldname)//" of file "//trim(filename))
endif
!Update the time level.
cur_var%siz(4) = cur_var%siz(4) + field_dimension_sizes(TIDX)
if (fileObj%max_ntime .lt. cur_var%siz(4)) then
fileObj%max_ntime = cur_var%siz(4)
endif
if (cur_var%siz(4) .gt. MAX_TIME_LEVEL_REGISTER) then
call mpp_error(FATAL, &
"fms_io_unstructured_setup_one_field:" &
//" the time level of field "//trim(cur_var%name) &
//" in file "//trim(fileObj%name)//" is greater" &
//" than MAX_TIME_LEVEL_REGISTER(=2), increase" &
//" MAX_TIME_LEVEL_REGISTER or check your code.")
endif
else
!If this is a new field, then add it the restart object.
fileObj%nvar = fileObj%nvar + 1
if (fileObj%nvar .gt. max_fields) then
write(error_msg,'(I3,"/",I3)') fileObj%nvar,max_fields
call mpp_error(FATAL, &
"fms_io_unstructured_setup_one_field:" &
//" max_fields exceeded, needs increasing," &
//" nvar/max_fields = "//trim(error_msg))
endif
index_field = fileObj%nvar
cur_var => null()
cur_var => fileObj%var(index_field)
!Point to the inputted unstructured domain.
cur_var%domain_ug => domain
!Copy in the dimension sizes of the data domain (siz, used for
!writes), and of the global domain (gsiz, used for reads).
cur_var%field_dimension_sizes = field_dimension_sizes
do i = 1,size(field_dimension_order)
cur_var%field_dimension_order(i) = field_dimension_order(i)
enddo
cur_var%siz(4) = field_dimension_sizes(TIDX)
!Copy in the rest of the data.
cur_var%name = fieldname
cur_var%default_data = real(default_data)
if (present(mandatory)) then
cur_var%mandatory = mandatory
endif
if (present(read_only)) then
cur_var%read_only = read_only
endif
if (present(owns_data)) then
cur_var%owns_data = owns_data
endif
if (present(longname)) then
cur_var%longname = longname
else
cur_var%longname = fieldname
endif
if (present(units)) then
cur_var%units = units
endif
endif
!Nullify local pointer.
cur_var => null()
return
end subroutine fms_io_unstructured_setup_one_field
!----------
# 8658 "../fms/fms_io.F90" 2
# 1 "../fms/fms_io_unstructured_register_restart_field.inc" 1
!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
!----------
!ug support
!------------------------------------------------------------------------------
!>Add a real scalar field to a restart object (restart_file_type). Return
!!the index of the inputted field in the fileObj%var array.
function fms_io_unstructured_register_restart_field_r_0d(fileObj, &
filename, &
fieldname, &
fdata_0d, &
domain, &
mandatory, &
data_default, &
longname, &
units, &
read_only, &
restart_owns_data) &
result(restart_index)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! null()
io_domain => mpp_get_UG_io_domain(domain)
io_domain_npes = mpp_get_UG_domain_npes(io_domain)
allocate(pelist(io_domain_npes))
call mpp_get_UG_domain_pelist(io_domain, &
pelist)
allocate(fdata_per_rank(io_domain_npes))
fdata_per_rank = 0.0
call mpp_gather((/fdata_0d/), &
fdata_per_rank, &
pelist)
if (mpp_pe() .eq. pelist(1)) then
if (maxval(fdata_per_rank) .ne. fdata_0d .or. &
minval(fdata_per_rank) .ne. fdata_0d) then
call mpp_error(FATAL, &
"fms_io_unstructured_register_restart_field_r_0d:" &
//" the scalar field data is not consistent across" &
//" all ranks in the I/O domain pelist.")
endif
endif
io_domain => null()
deallocate(pelist)
deallocate(fdata_per_rank)
!Set the dimension sizes for the field. These correspond to:
!field_dimension_sizes(XIDX) = x-dimension size
!field_dimension_sizes(YIDX) = y-dimension size
!field_dimension_sizes(CIDX) = c-dimension size
!field_dimension_sizes(ZIDX) = z-dimension size
!field_dimension_sizes(HIDX) = h-dimension size
!field_dimension_sizes(TIDX) = t-dimension size
!field_dimension_sizes(UIDX) = u-dimension size
!field_dimension_sizes(CCIDX) = cc-dimension size
field_dimension_sizes = 1
!Set the ordering of the dimensions for the field.
field_dimension_order(1) = TIDX
!Add a field to a restart object (restart_file_type). Get the index of the
!inputted field in the fileObj%var array.
call fms_io_unstructured_setup_one_field(fileObj, &
filename, &
fieldname, &
field_dimension_order, &
field_dimension_sizes, &
index_field, &
domain, &
mandatory=mandatory, &
data_default=data_default, &
longname=longname, &
units=units, &
read_only=read_only, &
owns_data=restart_owns_data)
!Point to the inputted data and return the "index_field" for the field.
fileObj%p0dr(fileObj%var(index_field)%siz(4),index_field)%p => fdata_0d
fileObj%var(index_field)%ndim = 0
restart_index = index_field
return
end function fms_io_unstructured_register_restart_field_r_0d
!------------------------------------------------------------------------------
!>Add a real 1D field to a restart object (restart_file_type), where the
!!field is assumed to be along the unstructured axis. Return
!!the index of the inputted field in the fileObj%var array.
function fms_io_unstructured_register_restart_field_r_1d(fileObj, &
filename, &
fieldname, &
fdata_1d, &
fdata_1d_axes, &
domain, &
mandatory, &
data_default, &
longname, &
units, &
read_only, &
restart_owns_data) &
result(restart_index)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! fdata_1d
fileObj%var(index_field)%ndim = 1
restart_index = index_field
return
end function fms_io_unstructured_register_restart_field_r_1d
!------------------------------------------------------------------------------
!>Add a real 2D field to a restart object (restart_file_type), where the
!!field's 1st axis assumed to be along the unstructured axis and the field's
!!2nd axis is assumed to be along the z-axis. Return the index of the
!!inputted field in the fileObj%var array.
function fms_io_unstructured_register_restart_field_r_2d(fileObj, &
filename, &
fieldname, &
fdata_2d, &
fdata_2d_axes, &
domain, &
mandatory, &
data_default, &
longname, &
units, &
read_only, &
restart_owns_data) &
result(restart_index)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! fdata_2d
fileObj%var(index_field)%ndim = 2
restart_index = index_field
return
end function fms_io_unstructured_register_restart_field_r_2d
!------------------------------------------------------------------------------
!>Add a real 3D field to a restart object (restart_file_type), where the
!!field's 1st axis assumed to be along the unstructured axis, the fields's
!!second axis is assumed to be along the z-axis, and the field's third axis
!!is assumed to be along the cc-axis (???). Return the index of the
!!inputted field in the fileObj%var array.
function fms_io_unstructured_register_restart_field_r_3d(fileObj, &
filename, &
fieldname, &
fdata_3d, &
fdata_3d_axes, &
domain, &
mandatory, &
data_default, &
longname, &
units, &
read_only, &
restart_owns_data) &
result(restart_index)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! fdata_3d
fileObj%var(index_field)%ndim = 3
restart_index = index_field
return
end function fms_io_unstructured_register_restart_field_r_3d
!------------------------------------------------------------------------------
!>Add a double_kind 2D field to a restart object (restart_file_type), where the
!!field's 1st axis assumed to be along the unstructured axis and the field's
!!2nd axis is assumed to be along the z-axis. Return the index of the
!!inputted field in the fileObj%var array.
function fms_io_unstructured_register_restart_field_r8_2d(fileObj, &
filename, &
fieldname, &
fdata_2d, &
fdata_2d_axes, &
domain, &
mandatory, &
data_default, &
longname, &
units, &
read_only, &
restart_owns_data) &
result(restart_index)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! fdata_2d
fileObj%var(index_field)%ndim = 2
restart_index = index_field
return
end function fms_io_unstructured_register_restart_field_r8_2d
!------------------------------------------------------------------------------
!>Add a double_kind 3D field to a restart object (restart_file_type), where the
!!field's 1st axis assumed to be along the unstructured axis, the fields's
!!second axis is assumed to be along the z-axis, and the field's third axis
!!is assumed to be along the cc-axis (???). Return the index of the
!!inputted field in the fileObj%var array.
function fms_io_unstructured_register_restart_field_r8_3d(fileObj, &
filename, &
fieldname, &
fdata_3d, &
fdata_3d_axes, &
domain, &
mandatory, &
data_default, &
longname, &
units, &
read_only, &
restart_owns_data) &
result(restart_index)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! fdata_3d
fileObj%var(index_field)%ndim = 3
restart_index = index_field
return
end function fms_io_unstructured_register_restart_field_r8_3d
!------------------------------------------------------------------------------
!>Add an integer scalar field to a restart object (restart_file_type). Return
!!the index of the inputted field in the fileObj%var array.
function fms_io_unstructured_register_restart_field_i_0d(fileObj, &
filename, &
fieldname, &
fdata_0d, &
domain, &
mandatory, &
data_default, &
longname, &
units, &
read_only, &
restart_owns_data) &
result(restart_index)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! null()
io_domain => mpp_get_UG_io_domain(domain)
io_domain_npes = mpp_get_UG_domain_npes(io_domain)
allocate(pelist(io_domain_npes))
call mpp_get_UG_domain_pelist(io_domain, &
pelist)
allocate(fdata_per_rank(io_domain_npes))
fdata_per_rank = 0.0
call mpp_gather((/fdata_0d/), &
fdata_per_rank, &
pelist)
if (mpp_pe() .eq. pelist(1)) then
if (maxval(fdata_per_rank) .ne. fdata_0d .or. &
minval(fdata_per_rank) .ne. fdata_0d) then
call mpp_error(FATAL, &
"fms_io_unstructured_register_restart_field_i_0d:" &
//" the scalar field data is not consistent across" &
//" all ranks in the I/O domain pelist.")
endif
endif
io_domain => null()
deallocate(pelist)
deallocate(fdata_per_rank)
!Set the dimension sizes for the field. These correspond to:
!field_dimension_sizes(XIDX) = x-dimension size
!field_dimension_sizes(YIDX) = y-dimension size
!field_dimension_sizes(CIDX) = c-dimension size
!field_dimension_sizes(ZIDX) = z-dimension size
!field_dimension_sizes(HIDX) = h-dimension size
!field_dimension_sizes(TIDX) = t-dimension size
!field_dimension_sizes(UIDX) = u-dimension size
!field_dimension_sizes(CCIDX) = cc-dimension size
field_dimension_sizes = 1
!Set the ordering of the dimensions for the field.
field_dimension_order(1) = TIDX
!Add a field to a restart object (restart_file_type). Get the index of the
!inputted field in the fileObj%var array.
call fms_io_unstructured_setup_one_field(fileObj, &
filename, &
fieldname, &
field_dimension_order, &
field_dimension_sizes, &
index_field, &
domain, &
mandatory=mandatory, &
data_default=data_default, &
longname=longname, &
units=units, &
read_only=read_only, &
owns_data=restart_owns_data)
!Point to the inputted data and return the "index_field" for the field.
fileObj%p0di(fileObj%var(index_field)%siz(4),index_field)%p => fdata_0d
fileObj%var(index_field)%ndim = 0
restart_index = index_field
return
end function fms_io_unstructured_register_restart_field_i_0d
!------------------------------------------------------------------------------
!>Add an integer 1D field to a restart object (restart_file_type), where the
!!field is assumed to be along the unstructured axis. Return
!!the index of the inputted field in the fileObj%var array.
function fms_io_unstructured_register_restart_field_i_1d(fileObj, &
filename, &
fieldname, &
fdata_1d, &
fdata_1d_axes, &
domain, &
mandatory, &
data_default, &
longname, &
units, &
read_only, &
restart_owns_data) &
result(restart_index)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! fdata_1d
fileObj%var(index_field)%ndim = 1
restart_index = index_field
return
end function fms_io_unstructured_register_restart_field_i_1d
!------------------------------------------------------------------------------
!>Add an integer 2D field to a restart object (restart_file_type), where the
!!field's 1st axis assumed to be along the unstructured axis and the field's
!!2nd axis is assumed to be along the z-axis. Return the index of the
!!inputted field in the fileObj%var array.
function fms_io_unstructured_register_restart_field_i_2d(fileObj, &
filename, &
fieldname, &
fdata_2d, &
fdata_2d_axes, &
domain, &
mandatory, &
data_default, &
longname, &
units, &
read_only, &
restart_owns_data) &
result(restart_index)
!Inputs/Outputs
type(restart_file_type),intent(inout) :: fileObj ! fdata_2d
fileObj%var(index_field)%ndim = 2
restart_index = index_field
return
end function fms_io_unstructured_register_restart_field_i_2d
!------------------------------------------------------------------------------
!----------
# 8659 "../fms/fms_io.F90" 2
# 1 "../fms/fms_io_unstructured_save_restart.inc" 1
!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
!----------
!ug support
!>Write out metadata and data for axes and fields to a restart file
!!associated with an unstructured mpp domain.
subroutine fms_io_unstructured_save_restart(fileObj, &
time_stamp, &
directory, &
append, &
time_level)
!Inputs/Outputs
type(restart_file_type),intent(inout),target :: fileObj != 0.0
! The value of time_level is written as a new value of the time axis data.
!If time_level is present and time_level < 0.0:
! A new file is opened and only the meta data is written.
!If append is present and append=.false.:
! Behaves the same was as if it were not present. That is, meta data is
! written and whether or not field data is written is determined by time_level.
!Local variables
type(domainUG),pointer :: domain ! null()
do j = 1,size(fileObj%axes)
if (j .eq. CIDX .or. j .eq. HIDX .or. j .eq. UIDX) then
if (allocated(fileObj%axes(j)%idx)) then
if (.not. associated(fileObj%axes(j)%domain_ug)) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" the axis "//trim(fileObj%axes(j)%name) &
//" in the file "//trim(fileObj%name) &
//" was not registered with an unstructured" &
//" mpp domain.")
endif
if (associated(domain)) then
if (.not. (domain .EQ. fileObj%axes(j)%domain_ug)) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" two axes registered to same" &
//" restart file are associated with" &
//" different unstructured mpp domains.")
endif
else
domain => fileObj%axes(j)%domain_ug
endif
endif
else
if (associated(fileObj%axes(j)%data)) then
if (.not. associated(fileObj%axes(j)%domain_ug)) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" the axis "//trim(fileObj%axes(j)%name) &
//" in the file "//trim(fileObj%name) &
//" was not registered with an unstructured" &
//" mpp domain.")
endif
if (associated(domain)) then
if (.not. (domain .EQ. fileObj%axes(j)%domain_ug)) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" two axes registered to same" &
//" restart file are associated with" &
//" different unstructured mpp domains.")
endif
else
domain => fileObj%axes(j)%domain_ug
endif
endif
endif
enddo
!Make sure that all registered fields are associated with the same
!unstructured domain that all axes were registered with.
do j = 1,fileObj%nvar
if (.not. associated(fileObj%var(j)%domain_ug)) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" the field "//trim(fileObj%var(j)%name) &
//" in the file "//trim(fileObj%name) &
//" was not registered with an unstructured" &
//" mpp domain.")
endif
if (.not. (domain .EQ. fileObj%var(j)%domain_ug)) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" the unstructured domain associated with" &
//" field "//trim(fileObj%var(j)%name) &
//" in the file "//trim(fileObj%name) &
//" does not match the unstructured domain" &
//" associated with the registered axes.")
endif
enddo
!If necessary, make sure a valid set of optional arguments was provided.
if (present(append)) then
if (append .and. .not. present(time_level)) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_compressed_restart:" &
//" a time_level must be present when" &
//" append=.true. for file "//trim(fileObj%name))
endif
endif
!Determine whether or not metadata will be written to the restart file. If
!no optional arguments are specified, metadata will be written to the file,
!with any old data overwritten. If the optional append flag is true, then
!it is assumed that the metadata already exists in the file, and thus
!metadata will not be written to the file.
mpp_action = MPP_OVERWR
write_meta_data = .true.
if (present(append)) then
if (append) then
mpp_action = MPP_APPEND
write_meta_data = .false.
if (time_level .lt. 0.0) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" the inputted time_level cannot be" &
//" negative when append is .true." &
//" for file "//trim(fileObj%name))
endif
endif
endif
!Determine whether or not field data will be written to the restart file.
!Field data will be written to the restart file unless a negative
!time_level value is passed in.
write_field_data = .true.
if (present(time_level)) then
if (time_level .lt. 0) then
write_field_data = .false.
endif
endif
!Set the directory where the restart file lives. This defaults to
!"./RESTART".
dir = "RESTART"
if (present(directory)) then
dir = trim(directory)
endif
!Set the name of the restart file excluding its path.
!time_stamp_restart is a module variable.
restartname = trim(fileObj%name)
if (time_stamp_restart) then
if (present(time_stamp)) then
if (len_trim(restartname) + len_trim(time_stamp) .gt. 79) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" length of restart file name including" &
//" time stamp is greater than allowed" &
//" restart file name length.")
endif
restartname = trim(time_stamp)//"."//trim(restartname)
endif
endif
!Set the name of the restart file including the path to it.
if (len_trim(dir) .gt. 0) then
restartpath = trim(dir)//"/"//trim(restartname)
else
restartpath = trim(restartname)
endif
!Open the restart file.
call mpp_open(funit, &
trim(restartpath), &
action=mpp_action, &
form=form, &
is_root_pe=fileObj%is_root_pe, &
domain_ug=domain)
!Write out the metadata for the axes and fields.
axis => null()
cur_var => null()
if (write_meta_data) then
!If it is registered, then write out the metadata for the x-axis
!to the restart file.
if (associated(fileObj%axes(XIDX)%data)) then
axis => fileObj%axes(XIDX)
call mpp_write_meta(funit, &
x_axis, &
axis%name, &
axis%units, &
axis%longname, &
data=axis%data, &
cartesian="X")
axis => null()
x_axis_defined = .true.
else
x_axis_defined = .false.
endif
!If it is registered, then write out the metadata for the y-axis
!to the restart file.
if (associated(fileObj%axes(YIDX)%data)) then
axis => fileObj%axes(YIDX)
call mpp_write_meta(funit, &
y_axis, &
axis%name, &
axis%units, &
axis%longname, &
data=axis%data, &
cartesian="Y")
axis => null()
y_axis_defined = .true.
else
y_axis_defined = .false.
endif
!If it is registered, then write out the metadata for the z-axis
!to the restart file.
if (associated(fileObj%axes(ZIDX)%data)) then
axis => fileObj%axes(ZIDX)
call mpp_write_meta(funit, &
z_axis, &
axis%name, &
axis%units, &
axis%longname, &
data=axis%data, &
cartesian="Z")
axis => null()
z_axis_defined = .true.
else
z_axis_defined = .false.
endif
!If it is registered, then write out the metadata for the cc-axis (???)
!to the restart file.
if (associated(fileObj%axes(CCIDX)%data)) then
axis => fileObj%axes(CCIDX)
call mpp_write_meta(funit, &
cc_axis, &
axis%name, &
axis%units, &
axis%longname, &
data=axis%data, &
cartesian="CC")
axis => null()
cc_axis_defined = .true.
else
cc_axis_defined = .false.
endif
!If it is registered, then write out the metadata for the compressed
!c-axis to the restart file.
if (allocated(fileObj%axes(CIDX)%idx)) then
axis => fileObj%axes(CIDX)
call mpp_def_dim(funit, &
trim(axis%dimlen_name), &
axis%dimlen, &
trim(axis%dimlen_lname), &
(/(i,i=1,axis%dimlen)/))
call mpp_write_meta(funit, &
c_axis, &
axis%name, &
axis%units, &
axis%longname, &
data=axis%idx, &
compressed=axis%compressed, &
min=axis%imin)
axis => null()
c_axis_defined = .true.
else
c_axis_defined = .false.
endif
!If it is registered, then write out the metadata for the compressed
!h-axis to the restart file.
if (allocated(fileObj%axes(HIDX)%idx)) then
axis => fileObj%axes(HIDX)
call mpp_def_dim(funit, &
trim(axis%dimlen_name), &
axis%dimlen, &
trim(axis%dimlen_lname), &
(/(i,i=1,axis%dimlen)/))
call mpp_write_meta(funit, &
h_axis, &
axis%name, &
axis%units, &
axis%longname, &
data=axis%idx, &
compressed=axis%compressed, &
min=axis%imin)
axis => null()
h_axis_defined = .true.
else
h_axis_defined = .false.
endif
!Write out the time axis to the restart file.
if (associated(fileObj%axes(TIDX)%data)) then
axis => fileObj%axes(TIDX)
call mpp_write_meta(funit, &
t_axis, &
axis%name, &
units=axis%units, &
longname=axis%longname, &
cartesian="T", &
calendar=axis%calendar)
axis => null()
else
call mpp_write_meta(funit, &
t_axis, &
"Time", &
"time level", &
"Time", &
cartesian="T")
endif
!Loop through the fields and write out the metadata.
do j = 1,fileObj%nvar
!Point to the current field.
cur_var => fileObj%var(j)
!Cycle to the next field if the current field is read only.
if (cur_var%read_only) then
cur_var => null()
cycle
endif
!Make sure the field has a valid number of time levels.
if (cur_var%siz(4) .gt. 1 .and. cur_var%siz(4) .ne. &
fileObj%max_ntime) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart: " &
//trim(cur_var%name)//" in file " &
//trim(fileObj%name)//" has more than one" &
//" time level, but the number of time levels" &
//" is not equal to max_ntime.")
endif
!Determine the dimensions for the field. For a scalar field foo,
!it is assumed that foo = foo(t). For non-scalar fields, time
!maybe added as the last dimension.
if (cur_var%ndim .eq. 0) then
num_var_axes = 1
var_axes(1) = t_axis
else
num_var_axes = cur_var%ndim
do k = 1,cur_var%ndim
select case (cur_var%field_dimension_order(k))
case (XIDX)
var_axes(k) = x_axis
case (YIDX)
var_axes(k) = y_axis
case (ZIDX)
var_axes(k) = z_axis
case (CCIDX)
var_axes(k) = cc_axis
case (CIDX)
var_axes(k) = c_axis
case (HIDX)
var_axes(k) = h_axis
case default
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" unsupported dimension type for" &
//" field "//trim(cur_var%name) &
//" in file "//trim(fileObj%name))
end select
enddo
if (cur_var%siz(4) .eq. fileObj%max_ntime) then
num_var_axes = num_var_axes + 1
var_axes(num_var_axes) = t_axis
endif
endif
!Get the "pack size" for default real types, where
!pack_size = (Number of bits in a real(8))/(Number of bits in a real).
cpack = pack_size
!For each time level, calculate a check-sum of the field data.
!Fields with integer(4) data are handled differently then real
!fields. To signify an integer(4) field, set cpack = 0.
allocate(check_val(max(1,cur_var%siz(4))))
do k = 1,cur_var%siz(4)
if (associated(fileObj%p0dr(k,j)%p)) then
check_val(k) = mpp_chksum(fileObj%p0dr(k,j)%p, &
(/mpp_pe()/), &
mask_val=cur_var%default_data)
elseif (associated(fileObj%p1dr(k,j)%p)) then
check_val(k) = mpp_chksum(fileObj%p1dr(k,j)%p, &
mask_val=cur_var%default_data)
elseif (associated(fileObj%p2dr(k,j)%p)) then
check_val(k) = mpp_chksum(fileObj%p2dr(k,j)%p, &
mask_val=cur_var%default_data)
elseif (associated(fileObj%p3dr(k,j)%p)) then
check_val(k) = mpp_chksum(fileObj%p3dr(k,j)%p, &
mask_val=cur_var%default_data)
elseif (associated(fileObj%p0di(k,j)%p)) then
check_val(k) = int(fileObj%p0di(k,j)%p,kind=8)
cpack = 0
elseif (associated(fileObj%p1di(k,j)%p)) then
check_val(k) = mpp_chksum(fileObj%p1di(k,j)%p, &
mask_val=cur_var%default_data)
cpack = 0
elseif (associated(fileObj%p2di(k,j)%p)) then
check_val(k) = mpp_chksum(fileObj%p2di(k,j)%p, &
mask_val=cur_var%default_data)
cpack = 0
elseif (associated(fileObj%p3di(k,j)%p)) then
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" 3D integer restart fields are not" &
//" currently supported. (" &
//trim(cur_var%name)//" of file " &
//trim(fileObj%name)//")")
else
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" there is no pointer associated with " &
//" the data of field " &
//trim(cur_var%name)//" of file " &
//trim(fileObj%name))
endif
enddo
!Write out the metadata from a field. Check-sums are only written
!if field data is written to the restart file.
if (write_field_data) then ! Write checksums only if valid field data exists
call mpp_write_meta(funit, &
cur_var%field, &
var_axes(1:num_var_axes), &
cur_var%name, &
cur_var%units, &
cur_var%longname, &
pack=cpack, &
checksum=check_val, &
fill=cur_var%default_data)
else
call mpp_write_meta(funit, &
cur_var%field, &
var_axes(1:num_var_axes), &
cur_var%name, &
cur_var%units, &
cur_var%longname, &
pack=cpack, &
fill=cur_var%default_data)
endif
deallocate(check_val)
cur_var => null()
enddo
!Write the axis data to the restart file.
if (x_axis_defined) then
call mpp_write(funit, &
x_axis)
endif
if (y_axis_defined) then
call mpp_write(funit, &
y_axis)
endif
if (c_axis_defined) then
call mpp_write(funit, &
c_axis)
endif
if (h_axis_defined) then
call mpp_write(funit, &
h_axis)
endif
if (cc_axis_defined) then
call mpp_write(funit, &
cc_axis)
endif
if (z_axis_defined) then
call mpp_write(funit, &
z_axis)
endif
endif
!Write out field data to the restart file.
if (write_field_data) then
!Loop through all time levels.
do k = 1,fileObj%max_ntime
!Get the time value for the time level.
if (present(time_level)) then
tlev = time_level
else
tlev = real(k)
endif
!Loop through the fields.
do j = 1,fileObj%nvar
!Point to the current field.
cur_var => fileObj%var(j)
!Cycle to the next field if the current field is read only.
if (cur_var%read_only) then
cur_var => null()
cycle
endif
!Write out the field data to the file.
if (k .le. cur_var%siz(4)) then
if (associated(fileObj%p0dr(k,j)%p)) then
call mpp_write(funit, &
cur_var%field, &
fileObj%p0dr(k,j)%p, &
tlev)
elseif (associated(fileObj%p1dr(k,j)%p)) then
call mpp_io_unstructured_write(funit, &
cur_var%field, &
domain, &
fileObj%p1dr(k,j)%p, &
fileObj%axes(cur_var%field_dimension_order(1))%nelems, &
tstamp=tlev, &
default_data=cur_var%default_data)
elseif (associated(fileObj%p2dr(k,j)%p)) then
call mpp_io_unstructured_write(funit, &
cur_var%field, &
domain, &
fileObj%p2dr(k,j)%p, &
fileObj%axes(cur_var%field_dimension_order(1))%nelems, &
tstamp=tlev, &
default_data=cur_var%default_data)
elseif (associated(fileObj%p3dr(k,j)%p)) then
call mpp_io_unstructured_write(funit, &
cur_var%field, &
domain, &
fileObj%p3dr(k,j)%p, &
fileObj%axes(cur_var%field_dimension_order(1))%nelems, &
tstamp=tlev, &
default_data=cur_var%default_data)
elseif (associated(fileObj%p0di(k,j)%p)) then
r0d = real(fileObj%p0di(k,j)%p)
call mpp_write(funit, &
cur_var%field, &
r0d, &
tlev)
elseif (associated(fileObj%p1di(k,j)%p)) then
allocate(r1d(size(fileObj%p1di(k,j)%p,1)))
r1d = real(fileObj%p1di(k,j)%p)
call mpp_io_unstructured_write(funit, &
cur_var%field, &
domain, &
r1d, &
fileObj%axes(cur_var%field_dimension_order(1))%nelems, &
tstamp=tlev, &
default_data=cur_var%default_data)
deallocate(r1d)
elseif (associated(fileObj%p2di(k,j)%p)) then
allocate(r2d(size(fileObj%p2di(k,j)%p,1),size(fileObj%p2di(k,j)%p,2)))
r2d = real(fileObj%p2di(k,j)%p)
call mpp_io_unstructured_write(funit, &
cur_var%field, &
domain, &
r2d, &
fileObj%axes(cur_var%field_dimension_order(1))%nelems, &
tstamp=tlev, &
default_data=cur_var%default_data)
deallocate(r2d)
else
call mpp_error(FATAL, &
"fms_io_unstructured_save_restart:" &
//" there is no pointer associated" &
//" with the data of field " &
//trim(cur_var%name)//" of file " &
//trim(fileObj%name))
endif
endif
cur_var => null()
enddo
enddo
endif
!Close the restart file.
call mpp_close(funit)
!Nullify local pointers.
domain => null()
axis => null()
cur_var => null()
return
end subroutine fms_io_unstructured_save_restart
!----------
# 8660 "../fms/fms_io.F90" 2
# 1 "../fms/fms_io_unstructured_read.inc" 1
!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
!----------
!ug support
!------------------------------------------------------------------------------
!>Read in a scalar field from a file associated with an unstructured mpp
!!domain.
subroutine fms_io_unstructured_read_r_scalar(filename, &
fieldname, &
fdata, &
domain, &
timelevel, &
start, &
nread, &
threading)
!Inputs/Outputs
character(len=*),intent(in) :: filename !Read in a one dimensional "compressed" field from a file associated with
!!an unstructured mpp domain.
subroutine fms_io_unstructured_read_r_1D(filename, &
fieldname, &
fdata, &
domain, &
timelevel, &
start, &
nread, &
threading)
!Inputs/Outputs
character(len=*),intent(in) :: filename !Read in a two dimensional "compressed" field from a file associated with
!!an unstructured mpp domain.
subroutine fms_io_unstructured_read_r_2D(filename, &
fieldname, &
fdata, &
domain, &
timelevel, &
start, &
nread, &
threading)
!Inputs/Outputs
character(len=*),intent(in) :: filename !Read in a three dimensional "compressed" field from a file associated with
!!an unstructured mpp domain.
subroutine fms_io_unstructured_read_r_3D(filename, &
fieldname, &
fdata, &
domain, &
timelevel, &
start, &
nread, &
threading)
!Inputs/Outputs
character(len=*),intent(in) :: filename !Read in a scalar field from a file associated with an unstructured mpp
!!domain.
subroutine fms_io_unstructured_read_i_scalar(filename, &
fieldname, &
fdata, &
domain, &
timelevel, &
start, &
nread, &
threading)
!Inputs/Outputs
character(len=*),intent(in) :: filename !Read in a one dimensional "compressed" field from a file associated with
!!an unstructured mpp domain.
subroutine fms_io_unstructured_read_i_1D(filename, &
fieldname, &
fdata, &
domain, &
timelevel, &
start, &
nread, &
threading)
!Inputs/Outputs
character(len=*),intent(in) :: filename !Read in a two dimensional "compressed" field from a file associated with
!!an unstructured mpp domain.
subroutine fms_io_unstructured_read_i_2D(filename, &
fieldname, &
fdata, &
domain, &
timelevel, &
start, &
nread, &
threading)
!Inputs/Outputs
character(len=*),intent(in) :: filename !.
!***********************************************************************
!----------
!ug support
!>For an inputted file name, check if it or any of its variants exist.
!!For a file named "foo", variants checked (in order) include:
!!
!! foo
!! foo.nc
!! foo..nc
!! foo.nc.
!! foo..nc.
!! foo.
!! foo..nc
!! foo...nc
!! foo..nc.
!! foo...nc.
!!
!!If a match is found, the value true is returned for the "does_file_exist"
!!flag. In addition, the actual file name is returned and the "read_dist"
!!flag, which tells whether or not the filename contains the
!!IO_domain_tile_id_string appended.
!!
!!Should this be a subroutine instead of a funtion for clarity since it
!!returns more than one value?
function fms_io_unstructured_get_file_name(orig_file, &
actual_file, &
read_dist, &
domain) &
result(does_file_exist)
!Inputs/Outputs
character(len=*),intent(in) :: orig_file ! null()
io_domain => mpp_get_UG_io_domain(domain)
!Get the tile id for the I/O domain.
io_tile_id = mpp_get_UG_domain_tile_id(io_domain)
io_domain => null()
!Check if the file has the I/O domain's tile id appended to the end of its
!name. For a file named foo.nc, this would become foo.nc.yyyy, where
!"yyyy" would in reality be the I/O domain's tile id. If the file exists,
!then set the read_dist and does_file_exist flags to true and return.
write(fname,'(a,i4.4)') trim(actual_file)//'.',io_tile_id
inquire(file=trim(fname),exist=fexist)
if (.not. fexist) then
write(fname,'(a,i6.6)') trim(actual_file)//'.',io_tile_id
inquire(file=trim(fname),exist=fexist)
endif
if (fexist) then
read_dist = .true.
does_file_exist = .true.
return
endif
!Check if the file is part of an ensemble.
!filename_appendix is a module variable.
if (len_trim(filename_appendix) .gt. 0) then
call get_instance_filename(orig_file, &
actual_file)
if (index(orig_file,'.nc',back=.true.) .eq. 0) then
inquire(file=trim(actual_file),exist=fexist)
if (fexist) then
does_file_exist = .true.
return
endif
endif
!Make a local copy of "actual_file", and the use the local copy to
!add the domain ".tilexxxx" string to "actual_file".
actual_file_tmp = actual_file
call get_mosaic_tile_file_ug(actual_file_tmp, &
actual_file, &
domain)
inquire(file=trim(actual_file),exist=fexist)
if (fexist) then
does_file_exist = .true.
return
endif
!Point to the I/O domain for the unstructured grid. This function call
!will throw a fatal error if the I/O domain does not exist.
io_domain => mpp_get_UG_io_domain(domain)
!Get the tile id for the I/O domain.
io_tile_id = mpp_get_UG_domain_tile_id(io_domain)
io_domain => null()
!Check if the file has the I/O domain's tile id appended to the end of
!its name. If it does then set the read_dist and does_file_exist flags
!to true and return.
write(fname,'(a,i4.4)') trim(actual_file)//'.',io_tile_id
inquire(file=trim(fname),exist=fexist)
if (.not. fexist) then
write(fname,'(a,i6.6)') trim(actual_file)//'.',io_tile_id
inquire(file=trim(fname),exist=fexist)
endif
if (fexist) then
read_dist = .true.
does_file_exist = .true.
return
endif
endif
return
end function fms_io_unstructured_get_file_name
!------------------------------------------------------------------------------
# 8662 "../fms/fms_io.F90" 2
# 1 "../fms/fms_io_unstructured_get_file_unit.inc" 1
!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
!----------
!ug support
!>Return the file unit and index in the "files_read" module array for the
!!inputted file. If the file does not currently exist in the "files_read"
!!array (i.e., it is not currenly open), then open it.
subroutine fms_io_unstructured_get_file_unit(filename, &
funit, &
index_file, &
read_dist, &
domain)
!Inputs/Outputs
character(len=*),intent(in) :: filename !.
!***********************************************************************
!----------
!ug support
!>Find the file unit for an inputted file, searching for its variants. If the
!!file is not found, then throw a fatal error.
subroutine fms_io_unstructured_file_unit(filename, &
funit, &
domain)
!Inputs/Outputs
character(len=*),intent(in) :: filename !.
!***********************************************************************
!----------
!ug support
!>Get the size of the dimensions of a field from a file associated with an
!!unstructured mpp domain.
subroutine fms_io_unstructured_get_field_size(filename, &
fieldname, &
field_dimension_sizes, &
domain, &
field_found)
!Inputs/Outputs
character(len=*),intent(in) :: filename ! null()
io_domain => mpp_get_UG_io_domain(domain)
!Get the pelist associated with the I/O domain.
io_domain_npes = mpp_get_UG_domain_npes(io_domain)
allocate(pelist(io_domain_npes))
call mpp_get_UG_domain_pelist(io_domain, &
pelist)
io_domain => null()
!Get the file unit for the inputted file.
call fms_io_unstructured_file_unit(filename, &
funit, &
domain)
!Have the root rank of the I/O domain pelist get the size of the dimensions
!of the inputted fields from the inputted file.
if (mpp_pe() .eq. pelist(1)) then
!Get the number of fields and axes contained in the inputted file.
call mpp_get_info(funit, &
num_axes, &
num_fields, &
num_atts, &
num_time_levels)
!Make sure that the number of fields in the file does not exceed the
!maximum number allowed per file.
!max_fields is a module variable.
if (num_fields .gt. max_fields) then
call mpp_error(FATAL, &
"fms_io_unstructured_get_field_size:" &
//" the number of fields in the file " &
//trim(filename)//" exceeds the maximum number" &
//" of fields allowed per file (max_fields)")
endif
!Read in all fields contained in the inputted file.
call mpp_get_fields(funit, &
file_fields(1:num_fields))
!Check if the inputted field matches one the fields contained in
!the inputted file. If it matches, get the size of the field
!dimensions.
found = .false.
field_dimension_sizes = -1
do i = 1,num_fields
call mpp_get_atts(file_fields(i), &
name=file_field_name)
if (lowercase(trim(file_field_name)) .eq. &
lowercase(trim(fieldname))) then
call mpp_get_atts(file_fields(i), &
ndim=file_field_ndim)
call mpp_get_atts(file_fields(i), &
axes=file_field_axes(1:file_field_ndim))
do j = 1,file_field_ndim
call mpp_get_atts(file_field_axes(j), &
len=field_dimension_sizes(j))
enddo
found = .true.
exit
endif
enddo
!If the inputted field does not match any of the fields contained
!in the inputted file, then check if it matches any of the axes
!contained in the file.
if (.not. found) then
call mpp_get_axes(funit, &
file_field_axes(1:num_axes))
do i = 1,num_axes
call mpp_get_atts(file_field_axes(i), &
name=file_axis_name, &
len=file_axis_size)
if (lowercase(trim(file_axis_name)) .eq. &
lowercase(trim(fieldname))) then
field_dimension_sizes(1) = file_axis_size
found = .true.
exit
endif
enddo
endif
endif
!Broadcast the flag telling if the inputted field was found in the inputted
!file and the field dimension sizes array to all non-root ranks on the
!I/O domain pelist.
if (mpp_pe() .eq. pelist(1)) then
do i = 2,io_domain_npes
call mpp_send(found, &
pelist(i), &
tag=COMM_TAG_1)
call mpp_send(field_dimension_sizes, &
size(field_dimension_sizes), &
pelist(i), &
tag=COMM_TAG_2)
enddo
call mpp_sync_self()
else
call mpp_recv(found, &
pelist(1), &
block = .false., &
tag=COMM_TAG_1)
call mpp_recv(field_dimension_sizes, &
size(field_dimension_sizes), &
pelist(1), &
block = .false., &
tag=COMM_TAG_2)
call mpp_sync_self(check=EVENT_RECV)
endif
!If the field_found flag is present, then return the value of the found
!flag. It is assumed that this value will be checked by the calling
!routine. If the field_found flag is not present and the field was not
!found in the file, then throw a fatal error.
if (present(field_found)) then
field_found = found
elseif (.not. found) then
call mpp_error(FATAL, &
"fms_io_unstructured_get_field_size:" &
//" the inputted field "//trim(fieldname) &
//" was not found in the file "//trim(filename))
endif
!Deallocate local allocatables.
deallocate(pelist)
return
end subroutine fms_io_unstructured_get_field_size
# 8665 "../fms/fms_io.F90" 2
# 1 "../fms/fms_io_unstructured_field_exist.inc" 1
!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
!----------
!ug support
!>Return a flag indicating whether the inputted field exists in the inputted
!!file, where the file is associated with an unstructured mpp domain.
function fms_io_unstructured_field_exist(file_name, &
field_name, &
domain) &
result(does_field_exist)
!Inputs/Outputs
character(len=*),intent(in) :: file_name !