!***********************************************************************
!* 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
#include
!
!
!
! 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(INT_KIND),parameter,public :: XIDX = 1
integer(INT_KIND),parameter,public :: YIDX = 2
integer(INT_KIND),parameter,public :: CIDX = 3
integer(INT_KIND),parameter,public :: ZIDX = 4
integer(INT_KIND),parameter,public :: HIDX = 5
integer(INT_KIND),parameter,public :: TIDX = 6
integer(INT_KIND),parameter,public :: UIDX = 7
integer(INT_KIND),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(DOUBLE_KIND), dimension(:,:), pointer :: p => NULL()
end type Ptr2Dr8
type Ptr3Dr8
real(DOUBLE_KIND), 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
#ifdef OVERLOAD_C8
module procedure read_cdata_2d,read_cdata_3d,read_cdata_4d
#endif
module procedure read_data_text
module procedure read_data_2d_region
module procedure read_data_3d_region
#ifdef OVERLOAD_R8
module procedure read_data_2d_region_r8
module procedure read_data_3d_region_r8
#endif
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
#ifdef OVERLOAD_C8
module procedure write_cdata_2d,write_cdata_3d,write_cdata_4d
#endif
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
#ifdef OVERLOAD_R8
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
#endif
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.
#include
!----------
!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
#ifdef OVERLOAD_R8
module procedure fms_io_unstructured_register_restart_field_r8_2d
module procedure fms_io_unstructured_register_restart_field_r8_3d
#endif
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(DOUBLE_KIND) :: 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()
#ifdef INTERNAL_FILE_NML
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
#else
call mpp_open(unit, 'input.nml',form=MPP_ASCII,action=MPP_RDONLY)
read(unit,fms_io_nml,iostat=io_status)
if (io_status > 0) then
call mpp_error(FATAL,'=>fms_io_init: Error reading input.nml')
endif
call mpp_close (unit)
#endif
! 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
#ifdef OVERLOAD_R8
!-------------------------------------------------------------------------------
!
! 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(DOUBLE_KIND), dimension(:,:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
real(DOUBLE_KIND), 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(FLOAT_KIND) :: 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, FLOAT_KIND)
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(DOUBLE_KIND), dimension(:,:,:), intent(in), target :: data
type(domain2d), optional, intent(in), target :: domain
real(DOUBLE_KIND), 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(FLOAT_KIND) :: 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, FLOAT_KIND)
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
#endif
!-------------------------------------------------------------------------------
!
! 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
#ifdef OVERLOAD_R8
!-------------------------------------------------------------------------------
!
! 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(DOUBLE_KIND), 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(DOUBLE_KIND), 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
#endif
!-------------------------------------------------------------------------------
!
! 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(LONG_KIND), 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(LONG_KIND) :: 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(DOUBLE_KIND) :: 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(LONG_KIND), 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=DOUBLE_KIND))
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=DOUBLE_KIND))
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(LONG_KIND), 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(LONG_KIND), dimension(3) :: checksum_file
integer(LONG_KIND) :: 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(LONG_KIND) :: 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(LONG_KIND), dimension(3) :: checksum_file
integer(LONG_KIND) :: 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(LONG_KIND), dimension(3) :: checksum_file ! There should be no more than 3 timelevels in a restart file.
integer(LONG_KIND) :: 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
#ifdef use_CRI_pointers
pointer( p, data_2d )
p = LOC(data)
#endif
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
#ifdef OVERLOAD_R8
!=====================================================================================
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
#endif
!=====================================================================================
!--- 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 use_CRI_pointers
! 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 use_CRI_pointers
! 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
#ifdef use_CRI_pointers
pointer( p, data_3d )
p = LOC(data)
#endif
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
!#######################################################################
#ifdef OVERLOAD_C8
subroutine read_cdata_2d ( unit, data, end)
integer, intent(in) :: unit
complex, intent(out), dimension(isd:,jsd:) :: data
logical, intent(out), optional :: end
complex, dimension(isg:ieg,jsg:jeg) :: gdata
integer :: len
logical :: no_halo
include "read_data_2d.inc"
end subroutine read_cdata_2d
#endif
!#######################################################################
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
!#######################################################################
#ifdef OVERLOAD_C8
subroutine read_cdata_3d ( unit, data, end)
integer, intent(in) :: unit
complex, intent(out), dimension(isd:,jsd:,:) :: data
logical, intent(out), optional :: end
complex, dimension(isg:ieg,jsg:jeg,size(data,3)) :: gdata
integer :: len
logical :: no_halo
include "read_data_3d.inc"
end subroutine read_cdata_3d
#endif
!#######################################################################
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
!#######################################################################
#ifdef OVERLOAD_C8
subroutine read_cdata_4d ( unit, data, end)
integer, intent(in) :: unit
complex, intent(out), dimension(isd:,jsd:,:,:) :: data
logical, intent(out), optional :: end
complex, 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_cdata_4d
#endif
!#######################################################################
! -------- 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
!#######################################################################
#ifdef OVERLOAD_C8
subroutine write_cdata_2d ( unit, data )
integer, intent(in) :: unit
complex, intent(in), dimension(isd:,jsd:) :: data
complex, dimension(isg:ieg,jsg:jeg) :: gdata
include "write_data.inc"
end subroutine write_cdata_2d
#endif
!#######################################################################
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
!#######################################################################
#ifdef OVERLOAD_C8
subroutine write_cdata_3d ( unit, data )
integer, intent(in) :: unit
complex, intent(in), dimension(isd:,jsd:,:) :: data
complex, dimension(isg:ieg,jsg:jeg,size(data,3)) :: gdata
include "write_data.inc"
end subroutine write_cdata_3d
#endif
!#######################################################################
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
!#######################################################################
#ifdef OVERLOAD_C8
subroutine write_cdata_4d ( unit, data )
integer, intent(in) :: unit
complex, intent(in), dimension(isd:,jsd:,:,:) :: data
complex, 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_cdata_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_cdata_4d
#endif
!#######################################################################
! 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
#ifdef INTERNAL_FILE_NML
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")
#endif
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
!----------
end module fms_io_mod