FMS  2025.04
Flexible Modeling System
fms_netcdf_domain_io.F90
1 !***********************************************************************
2 !* Apache License 2.0
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* Licensed under the Apache License, Version 2.0 (the "License");
7 !* you may not use this file except in compliance with the License.
8 !* You may obtain a copy of the License at
9 !*
10 !* http://www.apache.org/licenses/LICENSE-2.0
11 !*
12 !* FMS is distributed in the hope that it will be useful, but WITHOUT
13 !* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied;
14 !* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 !* PARTICULAR PURPOSE. See the License for the specific language
16 !* governing permissions and limitations under the License.
17 !***********************************************************************
18 !> @defgroup fms_netcdf_domain_io_mod fms_netcdf_domain_io_mod
19 !> @ingroup fms2_io
20 !> @brief Domain-specific I/O wrappers.
21 
22 !> @addtogroup fms_netcdf_domain_io_mod
23 !> @{
24 module fms_netcdf_domain_io_mod
25 use netcdf
26 use mpp_mod
27 use mpp_domains_mod
28 use fms_io_utils_mod
29 use netcdf_io_mod
30 use platform_mod
31 implicit none
32 private
33 
34 
35 !Module constants.
36 integer, parameter :: no_domain_decomposed_dimension = 0
37 integer, parameter, public :: max_num_domain_decomposed_dims = 10
38 integer, parameter :: variable_not_found = 0
39 integer, parameter :: default_domain_position = center
40 character(len=16), parameter :: domain_pos_att = "domain_position"
41 character(len=16), parameter :: domain_axis_att_name = "domain_axis"
42 character(len=16), parameter :: x = "x"
43 character(len=16), parameter :: y = "y"
44 
45 !> @}
46 
47 !> @brief Domain variable.
48 !> @ingroup fms_netcdf_domain_io_mod
49 type, private :: domaindimension_t
50  character(len=nf90_max_name) :: varname !< Variable name.
51  integer :: pos !< Domain position.
52 endtype domaindimension_t
53 
54 
55 !> @brief netcdf domain file type.
56 !> @ingroup fms_netcdf_domain_io_mod
57 type, extends(fmsnetcdffile_t), public :: fmsnetcdfdomainfile_t
58  type(domain2d) :: domain !< Two-dimensional domain.
59  type(domaindimension_t), dimension(:), allocatable :: xdims !< Dimensions associated
60  !! with the "x" axis
61  !! of a 2d domain.
62  integer :: nx !< Number of "x" dimensions.
63  type(domaindimension_t), dimension(:), allocatable :: ydims !< Dimensions associated
64  !! with the "y" axis
65  !! of a 2d domain.
66  integer :: ny !< Number of "y" dimensions.
67  character(len=FMS_PATH_LEN) :: non_mangled_path !< Non-domain-mangled file path.
68  logical :: adjust_indices !< Flag telling if indices need to be adjusted
69  !! for domain-decomposed read.
71 
72 
73 public :: open_domain_file
74 public :: close_domain_file
83 public :: domain_read_0d
84 public :: domain_read_1d
85 public :: domain_read_2d
86 public :: domain_read_3d
87 public :: domain_read_4d
88 public :: domain_read_5d
89 public :: domain_write_0d
90 public :: domain_write_1d
91 public :: domain_write_2d
92 public :: domain_write_3d
93 public :: domain_write_4d
94 public :: domain_write_5d
95 public :: save_domain_restart
96 public :: restore_domain_state
100 public :: get_mosaic_tile_grid
101 
102 !> @ingroup fms_netcdf_domain_io_mod
104  module procedure compute_global_checksum_2d
105  module procedure compute_global_checksum_3d
106  module procedure compute_global_checksum_4d
107 end interface compute_global_checksum
108 
109 !> @addtogroup fms_netcdf_domain_io_mod
110 !> @{
111 
112 contains
113 
114 
115 !> @brief Get the index of a domain decomposed dimension.
116 !! @return Index of domain decomposed dimension.
117 function get_domain_decomposed_index(name_, array, size_) &
118  result(index_)
119 
120  character(len=*), intent(in) :: name_ !< Name.
121  type(domaindimension_t), dimension(:), intent(in) :: array !< Array to search through.
122  integer, intent(in) :: size_ !< Number of spots to look in.
123  integer :: index_
124 
125  integer :: i
126 
127  index_ = variable_not_found
128  do i = 1, size_
129  if (string_compare(array(i)%varname, name_)) then
130  index_ = i
131  return
132  endif
133  enddo
134 end function get_domain_decomposed_index
135 
136 
137 !> @brief Add a domain decomposed dimension to an array.
138 subroutine append_domain_decomposed_dimension(name_, position_, array, size_)
139 
140  character(len=*), intent(in) :: name_ !< Variable name.
141  integer, intent(in) :: position_ !< Domain position.
142  type(domaindimension_t), dimension(:), intent(inout) :: array !< Array to search through.
143  integer, intent(inout) :: size_ !< Number of spots to look in.
144 
145  integer :: i
146 
147  do i = 1, size_
148  if (string_compare(array(i)%varname, name_)) then
149  call error("variable "//trim(name_)//" already registered.")
150  endif
151  enddo
152  size_ = size_ + 1
153  if (size_ .gt. size(array)) then
154  call error("number of domain decomposed variables exceeds limit.")
155  endif
156  call string_copy(array(size_)%varname, name_)
157  array(size_)%pos = position_
159 
160 
161 !> @brief Given a domain decomposed dimension, get its domain position.
162 !! @return Position of the domain decomposed variable.
163 function get_domain_position(name_, array, size_) &
164  result(dpos)
165 
166  character(len=*), intent(in) :: name_ !< Variable name.
167  type(domaindimension_t), dimension(:), intent(in) :: array !< Array to search through.
168  integer, intent(in) :: size_
169  integer :: dpos
170 
171  dpos = get_domain_decomposed_index(name_, array, size_)
172  if (dpos .ne. variable_not_found) then
173  dpos = array(dpos)%pos
174  endif
175 end function get_domain_position
176 
177 
178 !> @brief Given a variable, get the index of the "x" or "y" domain decomposed
179 !! dimension.
180 !! @return Index of the domain decomposed dimension or else
181 !! no_domain_decomposed_dimension.
182 function get_domain_decomposed_dimension_index(fileobj, variable_name, &
183  xory, broadcast) &
184  result(index_)
185 
186  type(fmsnetcdfdomainfile_t), intent(in), target :: fileobj !< File object.
187  character(len=*), intent(in) :: variable_name !< Variable name.
188  character(len=*), intent(in) :: xory !< String telling which dimension to
189  !! look for. Valid values are "x"
190  !! or "y".
191  logical, intent(in), optional :: broadcast !< Flag controlling whether or
192  !! not the index will be
193  !! broadcast to non "I/O root"
194  !! ranks. The broadcast will
195  !! be done by default.
196  integer :: index_
197 
198  integer :: ndims
199  character(len=nf90_max_name), dimension(:), allocatable :: dim_names
200  type(domaindimension_t), dimension(:), pointer :: p
201  integer :: n
202  integer :: i
203 
204  index_ = no_domain_decomposed_dimension
205  if (fileobj%is_root) then
206  ndims = get_variable_num_dimensions(fileobj, variable_name, broadcast=.false.)
207  allocate(dim_names(ndims))
208  dim_names(:) = ""
209  call get_variable_dimension_names(fileobj, variable_name, dim_names, broadcast=.false.)
210  if (string_compare(xory, x, .true.)) then
211  p => fileobj%xdims
212  n = fileobj%nx
213  elseif (string_compare(xory, y, .true.)) then
214  p => fileobj%ydims
215  n = fileobj%ny
216  else
217  call error("unrecognized xory flag value.")
218  endif
219  do i = 1, size(dim_names)
220  if (get_domain_decomposed_index(dim_names(i), p, n) .ne. variable_not_found) then
221  index_ = i
222  exit
223  endif
224  enddo
225  deallocate(dim_names)
226  endif
227  if (present(broadcast)) then
228  if (.not. broadcast) then
229  return
230  endif
231  endif
232  call mpp_broadcast(index_, fileobj%io_root, pelist=fileobj%pelist)
234 
235 
236 !> @brief Determine if a variable is "domain decomposed."
237 !! @return Flag telling if the variable is "domain decomposed."
238 function is_variable_domain_decomposed(fileobj, variable_name, broadcast, &
239  xindex, yindex, xpos, ypos) &
240  result(is_decomposed)
241 
242  type(fmsnetcdfdomainfile_t), intent(in) :: fileobj !< File object.
243  character(len=*), intent(in) :: variable_name !< Variable name.
244  logical, intent(in), optional :: broadcast !< Flag controlling whether or
245  !! not the index will be
246  !! broadcast to non "I/O root"
247  !! ranks. The broadcast will
248  !! be done by default.
249  integer, intent(out), optional :: xindex !< The index of the domain
250  !! x dimension.
251  integer, intent(out), optional :: yindex !< The index of the domain
252  !! y dimension.
253  integer, intent(out), optional :: xpos !< Domain position of the x dimension.
254  integer, intent(out), optional :: ypos !< Domain position of the y dimension.
255  logical :: is_decomposed
256 
257  integer, dimension(2) :: indices
258  integer :: ndims
259  character(len=nf90_max_name), dimension(:), allocatable :: dim_names
260 
261  indices(1) = get_domain_decomposed_dimension_index(fileobj, variable_name, x, broadcast)
262  if (present(xindex)) then
263  xindex = indices(1)
264  endif
265  indices(2) = get_domain_decomposed_dimension_index(fileobj, variable_name, y, broadcast)
266  if (present(yindex)) then
267  yindex = indices(2)
268  endif
269  is_decomposed = (indices(1) .ne. no_domain_decomposed_dimension) .and. &
270  (indices(2) .ne. no_domain_decomposed_dimension)
271  if (is_decomposed) then
272  if (.not. present(xpos) .and. .not. present(ypos)) then
273  return
274  endif
275  ndims = get_variable_num_dimensions(fileobj, variable_name, broadcast)
276  allocate(dim_names(ndims))
277  dim_names(:) = ""
278  call get_variable_dimension_names(fileobj, variable_name, dim_names, broadcast)
279  if (present(xpos)) then
280  xpos = get_domain_position(dim_names(indices(1)), fileobj%xdims, fileobj%nx)
281  endif
282  if (present(ypos)) then
283  ypos = get_domain_position(dim_names(indices(2)), fileobj%ydims, fileobj%ny)
284  endif
285  deallocate(dim_names)
286  else
287  if (present(xpos)) then
288  xpos = -1
289  endif
290  if (present(ypos)) then
291  ypos = -1
292  endif
293  endif
295 
296 
297 !> @brief Determine whether a domain-decomposed dimension has been registered to the file object
298 !! @return Flag telling if the dimension is registered to the file object
299 function is_dimension_registered(fileobj, dimension_name) &
300  result(is_registered)
301 
302  type(fmsnetcdfdomainfile_t), intent(in) :: fileobj !< File object.
303  character(len=*), intent(in) :: dimension_name !< Dimension name.
304 
305  ! local
306  logical :: is_registered
307  integer :: dpos
308 
309  dpos = 0
310  is_registered = .false.
311  dpos = get_domain_decomposed_index(dimension_name, fileobj%xdims, fileobj%nx)
312  if (dpos .ne. variable_not_found) then
313  is_registered = .true.
314  else
315  dpos = get_domain_decomposed_index(dimension_name, fileobj%ydims, fileobj%ny)
316  if (dpos .ne. variable_not_found) is_registered = .true.
317  endif
318 
319 end function is_dimension_registered
320 
321 !> @brief Open a NetCDF-4 file in parallel write mode
322 !! @return True on success
323 function open_collective_netcdf_file(fileobj, path, mode, domain, is_restart, dont_add_res_to_filename) &
324  result(success)
325 
326  type(fmsnetcdfdomainfile_t),intent(inout) :: fileobj !< File object.
327  character(len=*), intent(in) :: path !< File path.
328  character(len=*), intent(in) :: mode !< File mode. Allowed values
329  !! are "read", "append", "write", or
330  !! "overwrite".
331  type(domain2d), intent(in) :: domain !< Two-dimensional domain.
332  logical, intent(in), optional :: is_restart !< Flag telling if this file
333  !! is a restart file. Defaults
334  !! to false.
335  logical, intent(in), optional :: dont_add_res_to_filename !< Flag indicating not to add
336  !! ".res" to the filename
337 
338  integer :: nc_format_param
339  integer :: tile_id(1)
340  integer :: err
341  character(len=FMS_PATH_LEN) :: combined_filepath
342  character(len=FMS_PATH_LEN) :: full_path
343  logical :: is_res
344  logical :: dont_add_res
345  integer :: success
346  integer :: domain_size
347 
348  success = .true.
349  call string_copy(fileobj%non_mangled_path, path)
350 
351  !TODO Lots of duplicate code between this and netcdf_file_open
352 
353  !! Determine the name of your file !!
354 
355  !! If the number of tiles is greater than 1 or if the current tile is greater
356  !than 1 add .tileX. to the filename
357  tile_id = mpp_get_tile_id(domain)
358  if (mpp_get_ntile_count(domain) .gt. 1 .or. tile_id(1) > 1) then
359  call domain_tile_filepath_mangle(combined_filepath, path, tile_id(1))
360  else
361  call string_copy(combined_filepath, path)
362  endif
363 
364  !< Only add ".res" to the file path if is_restart is set to true
365  !! and dont_add_res_to_filename is set to false.
366  is_res = .false.
367  if (present(is_restart)) then
368  is_res = is_restart
369  endif
370  fileobj%is_restart = is_res
371 
372  dont_add_res = .false.
373  if (present(dont_add_res_to_filename)) then
374  dont_add_res = dont_add_res_to_filename
375  endif
376 
377  if (is_res .and. .not. dont_add_res) then
378  call restart_filepath_mangle(full_path, trim(combined_filepath))
379  else
380  call string_copy(full_path, trim(combined_filepath))
381  endif
382 
383  call string_copy(fileobj%path, trim(full_path))
384 
385  nc_format_param = ior(nf90_netcdf4, nf90_mpiio)
386  fileobj%is_netcdf4 = .true.
387 
388  fileobj%domain = domain
389  call mpp_get_global_domain(fileobj%domain, xsize=domain_size)
390 
391  if (string_compare(mode, "read", .true.) .or. string_compare(mode, "append", .true.)) &
392  call error("The use_netcdf_mpi = .true. option for reads is currently not supported")
393 
394  if (string_compare(mode, "write", .true.)) then
395  err = nf90_create(trim(fileobj%path), ior(nf90_noclobber, nc_format_param), fileobj%ncid, &
396  comm = mpp_get_domain_tile_commid(fileobj%domain), info = mpp_info_null)
397  elseif (string_compare(mode,"overwrite",.true.)) then
398  err = nf90_create(trim(fileobj%path), ior(nf90_clobber, nc_format_param), fileobj%ncid, &
399  comm = mpp_get_domain_tile_commid(fileobj%domain), info = mpp_info_null)
400  endif
401  call check_netcdf_code(err, "open_collective_netcdf_file:"//trim(fileobj%path))
402 
403  allocate(fileobj%xdims(max_num_domain_decomposed_dims))
404  fileobj%nx = 0
405  allocate(fileobj%ydims(max_num_domain_decomposed_dims))
406  fileobj%ny = 0
407 
408  ! Every rank is the root PE of its own pelist. This forces all ranks to hit any NetCDF calls,
409  ! which are usually inside `if (fileobj%is_root)` blocks.
410  allocate(fileobj%pelist(1))
411  fileobj%pelist(1) = mpp_pe()
412  fileobj%io_root = mpp_pe()
413  fileobj%is_root = .true.
414 
415  fileobj%use_collective = .false. !TODO
416  fileobj%is_diskless = .false.
417 
418  if (fileobj%is_restart) then
419  allocate(fileobj%restart_vars(max_num_restart_vars))
420  fileobj%num_restart_vars = 0
421  endif
422 
423  fileobj%is_readonly = string_compare(mode, "read", .true.)
424  fileobj%mode_is_append = string_compare(mode, "append", .true.)
425  allocate(fileobj%compressed_dims(max_num_compressed_dims))
426  fileobj%num_compressed_dims = 0
427  ! Set the is_open flag to true for this file object.
428  if (.not.allocated(fileobj%is_open)) allocate(fileobj%is_open)
429  fileobj%is_open = .true.
430 
431  fileobj%bc_dimensions%xlen = 0
432  fileobj%bc_dimensions%ylen = 0
433  fileobj%bc_dimensions%zlen = 0
434  fileobj%bc_dimensions%cur_dim_len = 0
435 
436 end function open_collective_netcdf_file
437 
438 !> @brief Open a domain netcdf file.
439 !! @return Flag telling if the open completed successfully.
440 function open_domain_file(fileobj, path, mode, domain, nc_format, is_restart, dont_add_res_to_filename, &
441  use_netcdf_mpi) result(success)
442 
443  type(fmsnetcdfdomainfile_t),intent(inout) :: fileobj !< File object.
444  character(len=*), intent(in) :: path !< File path.
445  character(len=*), intent(in) :: mode !< File mode. Allowed values
446  !! are "read", "append", "write", or
447  !! "overwrite".
448  type(domain2d), intent(in) :: domain !< Two-dimensional domain.
449  character(len=*), intent(in), optional :: nc_format !< Netcdf format that
450  !! new files are written
451  !! as. Allowed values
452  !! are: "64bit", "classic",
453  !! or "netcdf4". Defaults to
454  !! "64bit".
455  logical, intent(in), optional :: is_restart !< Flag telling if this file
456  !! is a restart file. Defaults
457  !! to false.
458  logical, intent(in), optional :: dont_add_res_to_filename !< Flag indicating not to add
459  !! ".res" to the filename
460  logical, intent(in), optional :: use_netcdf_mpi !< Flag telling if this file should be using netcdf4 collective
461  !! reads and writes. Defaults to false.
462  !! nc_format is automatically set to netcdf4
463  logical :: success
464 
465  integer, dimension(2) :: io_layout
466  integer, dimension(1) :: tile_id
467  character(len=FMS_PATH_LEN) :: combined_filepath
468  type(domain2d), pointer :: io_domain
469  character(len=FMS_PATH_LEN) :: distributed_filepath
470  integer :: pelist_size
471  integer, dimension(:), allocatable :: pelist
472  logical :: success2
473  type(fmsnetcdfdomainfile_t) :: fileobj2
474 
475  io_domain => mpp_get_io_domain(domain)
476 
477  fileobj%use_netcdf_mpi = .false.
478  if (present(use_netcdf_mpi)) fileobj%use_netcdf_mpi = use_netcdf_mpi
479 
480  if (fileobj%use_netcdf_mpi) then
481 #ifdef NO_NC_PARALLEL4
482  call mpp_error(fatal, "NetCDF was not built with HDF5 parallel I/O features, so parallel writes are not supported. &
483  &Please turn parallel writes off for the file: " // trim(path))
484 #endif
485 
486  if (associated(io_domain)) then
487  call mpp_error(note, "NetCDF MPI is enabled: ignoring I/O domain. Only one output file will be produced.")
488  endif
489 
490  success = open_collective_netcdf_file(fileobj, path, mode, domain, is_restart, dont_add_res_to_filename)
491  return
492  endif
493 
494  !Get the path of a "combined" file.
495  io_layout = mpp_get_io_domain_layout(domain)
496  tile_id = mpp_get_tile_id(domain)
497 
498  !< If the number of tiles is greater than 1 or if the current tile is greater
499  !than 1 add .tileX. to the filename
500  if (mpp_get_ntile_count(domain) .gt. 1 .or. tile_id(1) > 1) then
501  call domain_tile_filepath_mangle(combined_filepath, path, tile_id(1))
502  else
503  call string_copy(combined_filepath, path)
504  endif
505 
506  !Get the path of a "distributed" file.
507  if (.not. associated(io_domain)) then
508  call error("The domain associated with the file:"//trim(path)//" does not have an io_domain.")
509  endif
510  if (io_layout(1)*io_layout(2) .gt. 1) then
511  tile_id = mpp_get_tile_id(io_domain)
512  call io_domain_tile_filepath_mangle(distributed_filepath, combined_filepath, tile_id(1))
513  else
514  call string_copy(distributed_filepath, combined_filepath)
515  endif
516 
517  !Make sure the input domain has an I/O domain and get its pelist.
518  pelist_size = mpp_get_domain_npes(io_domain)
519  allocate(pelist(pelist_size))
520  call mpp_get_pelist(io_domain, pelist)
521  fileobj%adjust_indices = .true. !Set the default to true
522 
523  !Open the distibuted files.
524  success = netcdf_file_open(fileobj, distributed_filepath, mode, nc_format, pelist, &
525  is_restart, dont_add_res_to_filename)
526  if (string_compare(mode, "read", .true.) .or. string_compare(mode, "append", .true.)) then
527  if (success) then
528  if (.not. string_compare(distributed_filepath, combined_filepath)) then
529  success2 = netcdf_file_open(fileobj2, combined_filepath, mode, nc_format, pelist, &
530  is_restart, dont_add_res_to_filename)
531  if (success2) then
532  call error("The domain decomposed file:"//trim(path)// &
533  & " contains both combined (*.nc) and distributed files (*.nc.XXXX).")
534  endif
535  endif
536  else
537  success = netcdf_file_open(fileobj, combined_filepath, mode, nc_format, pelist, &
538  is_restart, dont_add_res_to_filename)
539  !If the file is combined and the layout is not (1,1) set the adjust_indices flag to false
540  if (success .and. (io_layout(1)*io_layout(2) .gt. 1)) fileobj%adjust_indices = .false.
541  endif
542  endif
543  if (.not. success) then
544  deallocate(pelist)
545  return
546  endif
547 
548  !Store/initialize necessary properties.
549  call string_copy(fileobj%non_mangled_path, path)
550  fileobj%domain = domain
551  allocate(fileobj%xdims(max_num_domain_decomposed_dims))
552  fileobj%nx = 0
553  allocate(fileobj%ydims(max_num_domain_decomposed_dims))
554  fileobj%ny = 0
555  call string_copy(fileobj%non_mangled_path, path)
556 
557  if (string_compare(mode, "write", .true.) .or. string_compare(mode, "overwrite", .true.)) then
558  !Add global attribute needed by mppnccombine.
559  call register_global_attribute(fileobj, "NumFilesInSet", io_layout(1)*io_layout(2))
560  endif
561 end function open_domain_file
562 
563 
564 !> @brief Close a domain netcdf file.
565 subroutine close_domain_file(fileobj)
566 
567  type(fmsnetcdfdomainfile_t), intent(inout) :: fileobj !< File object.
568 
569  call netcdf_file_close(fileobj)
570  deallocate(fileobj%xdims)
571  fileobj%nx = 0
572  deallocate(fileobj%ydims)
573  fileobj%ny = 0
574 end subroutine close_domain_file
575 
576 
577 !> @brief Add a dimension to a file associated with a two-dimensional domain.
578 subroutine register_domain_decomposed_dimension(fileobj, dim_name, xory, domain_position)
579 
580  type(fmsnetcdfdomainfile_t), target, intent(inout) :: fileobj !< File object.
581  character(len=*), intent(in) :: dim_name !< Dimension name.
582  character(len=*), intent(in) :: xory !< Flag telling if the dimension
583  !! is associated with the "x" or "y"
584  !! axis of the 2d domain. Allowed
585  !! values are "x" or "y".
586  integer, intent(in), optional :: domain_position !< Domain position.
587 
588  integer :: dpos
589  type(domain2d), pointer :: io_domain
590  integer :: domain_size
591  integer :: dim_size
592 
593  dpos = default_domain_position
594  if (mpp_domain_is_symmetry(fileobj%domain) .and. present(domain_position)) then
595  dpos = domain_position
596  endif
597 
598  ! If using NetCDF MPI, the IO domain is ignored, so use the domain to determine the correct size of each
599  ! domain-decomposed dimension.
600  if (fileobj%use_netcdf_mpi) then
601  io_domain => fileobj%domain
602  else
603  io_domain => mpp_get_io_domain(fileobj%domain)
604  endif
605 
606  if (string_compare(xory, x, .true.)) then
607  if (dpos .ne. center .and. dpos .ne. east) then
608  call error("Only domain_position=center or domain_position=EAST is supported for x dimensions."// &
609  & " Fix your register_axis call for file:"&
610  &//trim(fileobj%path)//" and dimension:"//trim(dim_name))
611  endif
612  call mpp_get_global_domain(io_domain, xsize=domain_size, position=dpos)
613  call append_domain_decomposed_dimension(dim_name, dpos, fileobj%xdims, fileobj%nx)
614  elseif (string_compare(xory, y, .true.)) then
615  if (dpos .ne. center .and. dpos .ne. north) then
616  call error("Only domain_position=center or domain_position=NORTH is supported for y dimensions."// &
617  & " Fix your register_axis call for file:"&
618  &//trim(fileobj%path)//" and dimension:"//trim(dim_name))
619  endif
620  call mpp_get_global_domain(io_domain, ysize=domain_size, position=dpos)
621  call append_domain_decomposed_dimension(dim_name, dpos, fileobj%ydims, fileobj%ny)
622  else
623  call error("The register_axis call for file:"//trim(fileobj%path)//" and dimension:"//trim(dim_name)// &
624  & " has an unrecognized xory flag value:"&
625  &//trim(xory)//" only 'x' and 'y' are allowed.")
626  endif
627  if (fileobj%is_readonly .or. (fileobj%mode_is_append .and. dimension_exists(fileobj, dim_name))) then
628  call get_dimension_size(fileobj, dim_name, dim_size, broadcast=.true.)
629  if (dim_size .lt. domain_size) then
630  call error("dimension "//trim(dim_name)//" in the file "//trim(fileobj%path)//" is smaller than the size of" &
631  //" the associated domain "//trim(xory)//" axis.")
632  endif
633  else
634  call netcdf_add_dimension(fileobj, dim_name, domain_size, is_compressed=.false.)
635  endif
637 
638 
639 !> @brief Add a "domain_decomposed" attribute to the axis variables because it is
640 !! required by mppnccombine.
641 !! @internal
642 subroutine add_domain_attribute(fileobj, variable_name)
643 
644  type(fmsnetcdfdomainfile_t), intent(inout) :: fileobj !< File object.
645  character(len=*), intent(in) :: variable_name !< Variable name.
646 
647  type(domain2d), pointer :: io_domain
648  integer :: dpos
649  integer :: sg
650  integer :: eg
651  integer :: s
652  integer :: e
653  integer, dimension(2) :: io_layout !< Io_layout in the fileobj's domain
654 
655  !< Don't add the "domain_decomposition" variable attribute if the io_layout is
656  !! 1,1, or if using mpi netcdf for writes, to avoid frecheck "failures"
657  io_layout = mpp_get_io_domain_layout(fileobj%domain)
658  if (io_layout(1) .eq. 1 .and. io_layout(2) .eq. 1) return
659  if (fileobj%use_netcdf_mpi) return
660 
661  io_domain => mpp_get_io_domain(fileobj%domain)
662  dpos = get_domain_decomposed_index(variable_name, fileobj%xdims, fileobj%nx)
663  if (dpos .ne. variable_not_found) then
664  dpos = fileobj%xdims(dpos)%pos
665  call mpp_get_global_domain(fileobj%domain, xbegin=sg, xend=eg, position=dpos)
666  call mpp_get_global_domain(io_domain, xbegin=s, xend=e, position=dpos)
667  call register_variable_attribute(fileobj, variable_name, "domain_decomposition", &
668  (/sg, eg, s, e/))
669  else
670  dpos = get_domain_decomposed_index(variable_name, fileobj%ydims, fileobj%ny)
671  if (dpos .ne. variable_not_found) then
672  dpos = fileobj%ydims(dpos)%pos
673  call mpp_get_global_domain(fileobj%domain, ybegin=sg, yend=eg, position=dpos)
674  call mpp_get_global_domain(io_domain, ybegin=s, yend=e, position=dpos)
675  call register_variable_attribute(fileobj, variable_name, "domain_decomposition", &
676  (/sg, eg, s, e/))
677  endif
678  endif
679 end subroutine add_domain_attribute
680 
681 
682 !> @brief Add a domain decomposed variable.
683 subroutine register_domain_variable(fileobj, variable_name, variable_type, dimensions, chunksizes)
684 
685  type(fmsnetcdfdomainfile_t), intent(inout) :: fileobj !< File object.
686  character(len=*), intent(in) :: variable_name !< Variable name.
687  character(len=*), intent(in) :: variable_type !< Variable type. Allowed
688  !! values are: "int", "int64",
689  !! "float", or "double".
690  character(len=*), dimension(:), intent(in), optional :: dimensions !< Dimension names.
691  integer, intent(in), optional :: chunksizes(:) !< netcdf chunksize to use for this variable (netcdf4 only)
692 
693  if (.not. fileobj%is_readonly) then
694  call netcdf_add_variable(fileobj, variable_name, variable_type, dimensions, chunksizes)
695  if (present(dimensions)) then
696  if (size(dimensions) .eq. 1) then
697  call add_domain_attribute(fileobj, variable_name)
698  endif
699  endif
700  endif
701 end subroutine register_domain_variable
702 
703 
704 !> @brief Loop through registered restart variables and write them to
705 !! a netcdf file.
706 subroutine save_domain_restart(fileobj, unlim_dim_level)
707 
708  type(fmsnetcdfdomainfile_t), intent(in) :: fileobj !< File object.
709  integer, intent(in), optional :: unlim_dim_level !< Unlimited dimension level.
710 
711  integer :: i
712  character(len=32) :: chksum
713  logical :: is_decomposed
714 
715  if (.not. fileobj%is_restart) then
716  call error("file "//trim(fileobj%path)// &
717  & " is not a restart file. You must set is_restart=.true. in your open_file call.")
718  endif
719 
720 ! Calculate the variable's checksum and write it to the netcdf file
721  do i = 1, fileobj%num_restart_vars
722  if (associated(fileobj%restart_vars(i)%data2d)) then
723  chksum = compute_global_checksum(fileobj, fileobj%restart_vars(i)%varname, &
724  fileobj%restart_vars(i)%data2d, is_decomposed)
725  if (is_decomposed) then
726  call register_variable_attribute(fileobj, fileobj%restart_vars(i)%varname, &
727  "checksum", chksum(1:len(chksum)), str_len=len(chksum))
728  endif
729  elseif (associated(fileobj%restart_vars(i)%data3d)) then
730  chksum = compute_global_checksum(fileobj, fileobj%restart_vars(i)%varname, &
731  fileobj%restart_vars(i)%data3d, is_decomposed)
732  if (is_decomposed) then
733  call register_variable_attribute(fileobj, fileobj%restart_vars(i)%varname, &
734  "checksum", chksum(1:len(chksum)), str_len=len(chksum))
735  endif
736  elseif (associated(fileobj%restart_vars(i)%data4d)) then
737  chksum = compute_global_checksum(fileobj, fileobj%restart_vars(i)%varname, &
738  fileobj%restart_vars(i)%data4d, is_decomposed)
739  if (is_decomposed) then
740  call register_variable_attribute(fileobj, fileobj%restart_vars(i)%varname, &
741  "checksum", chksum(1:len(chksum)), str_len=len(chksum))
742  endif
743  endif
744  enddo
745 
746 ! Write the variable's data to the netcdf file
747  do i = 1, fileobj%num_restart_vars
748  if (associated(fileobj%restart_vars(i)%data0d)) then
749  call domain_write_0d(fileobj, fileobj%restart_vars(i)%varname, &
750  fileobj%restart_vars(i)%data0d, unlim_dim_level=unlim_dim_level)
751  elseif (associated(fileobj%restart_vars(i)%data1d)) then
752  call domain_write_1d(fileobj, fileobj%restart_vars(i)%varname, &
753  fileobj%restart_vars(i)%data1d, unlim_dim_level=unlim_dim_level)
754  elseif (associated(fileobj%restart_vars(i)%data2d)) then
755  call domain_write_2d(fileobj, fileobj%restart_vars(i)%varname, &
756  fileobj%restart_vars(i)%data2d, unlim_dim_level=unlim_dim_level)
757  elseif (associated(fileobj%restart_vars(i)%data3d)) then
758  call domain_write_3d(fileobj, fileobj%restart_vars(i)%varname, &
759  fileobj%restart_vars(i)%data3d, unlim_dim_level=unlim_dim_level)
760  elseif (associated(fileobj%restart_vars(i)%data4d)) then
761  call domain_write_4d(fileobj, fileobj%restart_vars(i)%varname, &
762  fileobj%restart_vars(i)%data4d, unlim_dim_level=unlim_dim_level)
763  else
764  call error("This routine only accepts data that is scalar, 1d 2d 3d or 4d."//&
765  " The data sent in has an unsupported dimensionality")
766  endif
767  enddo
768 
769 end subroutine save_domain_restart
770 
771 
772 !> @brief Loop through registered restart variables and read them from
773 !! a netcdf file.
774 subroutine restore_domain_state(fileobj, unlim_dim_level, ignore_checksum)
775 
776  type(fmsnetcdfdomainfile_t), intent(inout) :: fileobj !< File object.
777  integer, intent(in), optional :: unlim_dim_level !< Unlimited dimension level.
778  logical, intent(in), optional :: ignore_checksum !< Checksum data integrity flag.
779 
780  integer :: i
781  character(len=32) :: chksum_in_file
782  character(len=32) :: chksum
783  logical :: chksum_ignore = .false. !< local variable for data integrity checks
784  !! default: .FALSE. - checks enabled
785  logical :: is_decomposed
786 
787  if (PRESENT(ignore_checksum)) chksum_ignore = ignore_checksum
788 
789  if (.not. fileobj%is_restart) then
790  call error("file "//trim(fileobj%path)// &
791  & " is not a restart file. You must set is_restart=.true. in your open_file call.")
792  endif
793  do i = 1, fileobj%num_restart_vars
794  if (associated(fileobj%restart_vars(i)%data0d)) then
795  call domain_read_0d(fileobj, fileobj%restart_vars(i)%varname, &
796  fileobj%restart_vars(i)%data0d, unlim_dim_level=unlim_dim_level)
797  elseif (associated(fileobj%restart_vars(i)%data1d)) then
798  call domain_read_1d(fileobj, fileobj%restart_vars(i)%varname, &
799  fileobj%restart_vars(i)%data1d, unlim_dim_level=unlim_dim_level)
800  elseif (associated(fileobj%restart_vars(i)%data2d)) then
801  call domain_read_2d(fileobj, fileobj%restart_vars(i)%varname, &
802  fileobj%restart_vars(i)%data2d, unlim_dim_level=unlim_dim_level)
803  if (.not.chksum_ignore) then
804  chksum = compute_global_checksum(fileobj, fileobj%restart_vars(i)%varname, &
805  fileobj%restart_vars(i)%data2d, is_decomposed)
806  if (variable_att_exists(fileobj, fileobj%restart_vars(i)%varname, "checksum") .and. &
807  is_decomposed) then
808  call get_variable_attribute(fileobj, fileobj%restart_vars(i)%varname, &
809  "checksum", chksum_in_file)
810  if (.not. string_compare(trim(adjustl(chksum_in_file)), trim(adjustl(chksum)))) then
811  call error("The checksum in the file:"//trim(fileobj%path)//" and variable:"// &
812  & trim(fileobj%restart_vars(i)%varname)// &
813  &" does not match the checksum calculated from the data. file:"//trim(adjustl(chksum_in_file))//&
814  &" from data:"//trim(adjustl(chksum)))
815  endif
816  endif
817  endif
818  elseif (associated(fileobj%restart_vars(i)%data3d)) then
819  call domain_read_3d(fileobj, fileobj%restart_vars(i)%varname, &
820  fileobj%restart_vars(i)%data3d, unlim_dim_level=unlim_dim_level)
821  if (.not.chksum_ignore) then
822  chksum = compute_global_checksum(fileobj, fileobj%restart_vars(i)%varname, &
823  fileobj%restart_vars(i)%data3d, is_decomposed)
824  if (variable_att_exists(fileobj, fileobj%restart_vars(i)%varname, "checksum") .and. &
825  is_decomposed) then
826  call get_variable_attribute(fileobj, fileobj%restart_vars(i)%varname, &
827  "checksum", chksum_in_file(1:len(chksum_in_file)))
828  if (.not. string_compare(trim(adjustl(chksum_in_file)), trim(adjustl(chksum)))) then
829  call error("The checksum in the file:"//trim(fileobj%path)//" and variable:"// &
830  & trim(fileobj%restart_vars(i)%varname)//&
831  &" does not match the checksum calculated from the data. file:"//trim(adjustl(chksum_in_file))//&
832  &" from data:"//trim(adjustl(chksum)))
833  endif
834  endif
835  endif
836  elseif (associated(fileobj%restart_vars(i)%data4d)) then
837  call domain_read_4d(fileobj, fileobj%restart_vars(i)%varname, &
838  fileobj%restart_vars(i)%data4d, unlim_dim_level=unlim_dim_level)
839  if (.not.chksum_ignore) then
840  chksum = compute_global_checksum(fileobj, fileobj%restart_vars(i)%varname, &
841  fileobj%restart_vars(i)%data4d, is_decomposed)
842  if (variable_att_exists(fileobj, fileobj%restart_vars(i)%varname, "checksum") .and. &
843  is_decomposed) then
844  call get_variable_attribute(fileobj, fileobj%restart_vars(i)%varname, &
845  "checksum", chksum_in_file)
846  if (.not. string_compare(trim(adjustl(chksum_in_file)), trim(adjustl(chksum)))) then
847  call error("The checksum in the file:"//trim(fileobj%path)//" and variable:"// &
848  & trim(fileobj%restart_vars(i)%varname)//&
849  &" does not match the checksum calculated from the data. file:"//trim(adjustl(chksum_in_file))//&
850  &" from data:"//trim(adjustl(chksum)))
851  endif
852  endif
853  endif
854  else
855  call error("There is no data associated with the variable: "//trim(fileobj%restart_vars(i)%varname)//&
856  &" and the file: "//trim(fileobj%path)//". Check your register_restart_variable call")
857  endif
858  enddo
859 end subroutine restore_domain_state
860 
861 
862 !> @brief Return an array of compute domain indices.
863 subroutine get_compute_domain_dimension_indices(fileobj, dimname, indices)
864 
865  type(fmsnetcdfdomainfile_t), intent(in) :: fileobj !< File object.
866  character(len=*), intent(in) :: dimname !< Name of dimension variable.
867  integer, dimension(:), allocatable, intent(inout) :: indices !< Compute domain indices.
868 
869  type(domain2d), pointer :: io_domain
870  integer :: dpos
871  integer :: s
872  integer :: e
873  integer :: i
874 
875  io_domain => mpp_get_io_domain(fileobj%domain)
876  dpos = get_domain_decomposed_index(dimname, fileobj%xdims, fileobj%nx)
877  if (dpos .ne. variable_not_found) then
878  dpos = fileobj%xdims(dpos)%pos
879  call mpp_get_compute_domain(io_domain, xbegin=s, xend=e, position=dpos)
880  else
881  dpos = get_domain_decomposed_index(dimname, fileobj%ydims, fileobj%ny)
882  if (dpos .ne. variable_not_found) then
883  dpos = fileobj%ydims(dpos)%pos
884  call mpp_get_compute_domain(io_domain, ybegin=s, yend=e, position=dpos)
885  else
886  call error("get_compute_domain_dimension_indices: the input dimension:"//trim(dimname)// &
887  & " is not domain decomposed.")
888  endif
889  endif
890  if (allocated(indices)) then
891  deallocate(indices)
892  endif
893  allocate(indices(e-s+1))
894  do i = s, e
895  indices(i-s+1) = i
896  enddo
898 
899 
900 !> @brief Utility routine that retrieves domain indices.
901 !! @internal
902 subroutine domain_offsets(data_xsize, data_ysize, domain, xpos, ypos, &
903  isd, isc, xc_size, jsd, jsc, yc_size, &
904  buffer_includes_halos, extra_x_point, &
905  extra_y_point, msg)
906 
907  integer, intent(in) :: data_xsize !< Size of buffer's domain "x" dimension.
908  integer, intent(in) :: data_ysize !< Size of buffer's domain "y" dimension.
909  type(domain2d), intent(in) :: domain !< Parent domain.
910  integer, intent(in) :: xpos !< Variable's domain x dimension position.
911  integer, intent(in) :: ypos !< Variable's domain y dimension position.
912  integer, intent(out) :: isd !< Starting index for x dimension of data domain.
913  integer, intent(out) :: isc !< Starting index for x dimension of compute domain.
914  integer, intent(out) :: xc_size !< Size of x dimension of compute domain.
915  integer, intent(out) :: jsd !< Starting index for y dimension of data domain.
916  integer, intent(out) :: jsc !< Starting index for y dimension of compute domain.
917  integer, intent(out) :: yc_size !< Size of y dimension of compute domain.
918  logical, intent(out) :: buffer_includes_halos !< Flag telling if input buffer includes space for halos.
919  logical, intent(out), optional :: extra_x_point !< Flag indicating if data_array has an extra point in x
920  logical, intent(out), optional :: extra_y_point !< Flag indicating if data_array has an extra point in y
921  character(len=*), intent(in), optional :: msg !< Message appended to fatal error
922 
923  integer :: xd_size
924  integer :: yd_size
925  integer :: iec
926  integer :: xmax
927  integer :: jec
928  integer :: ymax
929  type(domain2d), pointer :: io_domain !< I/O domain variable is decomposed over.
930 
931  io_domain => mpp_get_io_domain(domain)
932 
933  call mpp_get_global_domain(domain, xend=xmax, position=xpos)
934  call mpp_get_global_domain(domain, yend=ymax, position=ypos)
935  call mpp_get_data_domain(io_domain, xbegin=isd, xsize=xd_size, position=xpos)
936  call mpp_get_data_domain(io_domain, ybegin=jsd, ysize=yd_size, position=ypos)
937 
938  call mpp_get_compute_domain(io_domain, xbegin=isc, xend=iec, xsize=xc_size, &
939  position=xpos)
940  ! If the xpos is east and the ending x index is NOT equal to max allowed, set extra_x_point to true
941  if (present(extra_x_point)) then
942  if ((xpos .eq. east) .and. (iec .ne. xmax)) then
943  extra_x_point = .true.
944  else
945  extra_x_point = .false.
946  endif
947  endif
948 
949  call mpp_get_compute_domain(io_domain, ybegin=jsc, yend=jec, ysize=yc_size, &
950  position=ypos)
951  ! If the ypost is north and the ending y index is NOT equal to max allowed, set extra_y_point to true
952  if (present(extra_y_point)) then
953  if ((ypos .eq. north) .and. (jec .ne. ymax)) then
954  extra_y_point = .true.
955  else
956  extra_y_point = .false.
957  endif
958  endif
959 
960  buffer_includes_halos = (data_xsize .eq. xd_size) .and. (data_ysize .eq. yd_size)
961  if (.not. buffer_includes_halos .and. data_xsize .ne. xc_size .and. data_ysize &
962  .ne. yc_size) then
963  print *, "buffer_includes_halos:", buffer_includes_halos, " data_xsize:", &
964  data_xsize, " xc_size:", xc_size, " data_ysize:", data_ysize, " yc_size:", &
965  yc_size
966  call error(trim(msg)//" The data is not on the compute domain or the data domain")
967  endif
968 end subroutine domain_offsets
969 
970 
971 !> @brief Get starting/ending global indices of the I/O domain for a domain decomposed
972 !! file.
973 subroutine get_global_io_domain_indices(fileobj, dimname, is, ie, indices)
974 
975  type(fmsnetcdfdomainfile_t), intent(in) :: fileobj !< File object.
976  character(len=*), intent(in) :: dimname !< Name of dimension variable.
977  integer, intent(out) :: is !< Starting index of I/O global domain.
978  integer, intent(out) :: ie !< Ending index of I/O global domain.
979  integer, dimension(:), allocatable, intent(out), optional :: indices !< Global domain indices
980 
981  type(domain2d), pointer :: io_domain
982  integer :: dpos
983  integer :: i
984 
985  io_domain => mpp_get_io_domain(fileobj%domain)
986  dpos = get_domain_decomposed_index(dimname, fileobj%xdims, fileobj%nx)
987  if (dpos .ne. variable_not_found) then
988  dpos = fileobj%xdims(dpos)%pos
989  call mpp_get_global_domain(io_domain, xbegin=is, xend=ie, position=dpos)
990  else
991  dpos = get_domain_decomposed_index(dimname, fileobj%ydims, fileobj%ny)
992  if (dpos .ne. variable_not_found) then
993  dpos = fileobj%ydims(dpos)%pos
994  call mpp_get_global_domain(io_domain, ybegin=is, yend=ie, position=dpos)
995  else
996  call error("get_global_io_domain_indices: the dimension "//trim(dimname)//" in the file: "//trim(fileobj%path)//&
997  &" is not domain decomposed. Check your register_axis call")
998  endif
999  endif
1000 
1001 ! Allocate indices to the difference between the ending and starting indices and
1002 ! fill indices with the data
1003  if (present(indices)) then
1004  if(allocated(indices)) then
1005  call error("get_global_io_domain_indices: the variable indices should not be allocated.")
1006  endif
1007  allocate(indices(ie-is+1))
1008  do i = is, ie
1009  indices(i-is+1) = i
1010  enddo
1011  endif
1012 
1013 
1014 end subroutine get_global_io_domain_indices
1015 
1016 !> @brief Read a mosaic_file and get the grid filename for the current tile or
1017 !! for the tile specified
1018 subroutine get_mosaic_tile_grid(grid_file,mosaic_file, domain, tile_count)
1019  character(len=*), intent(out) :: grid_file !< Filename of the grid file for the
1020  !! current domain tile or for tile
1021  !! specified in tile_count
1022  character(len=*), intent(in) :: mosaic_file !< Filename that will be read
1023  type(domain2d), intent(in) :: domain !< Input domain
1024  integer, intent(in), optional :: tile_count !< Optional argument indicating
1025  !! the tile you want grid file name for
1026  !! this is for when a pe is in more than
1027  !! tile.
1028  integer :: tile !< Current domian tile or tile_count
1029  integer :: ntileme !< Total number of tiles in the domain
1030  integer, dimension(:), allocatable :: tile_id !< List of tiles in the domain
1031  type(fmsnetcdffile_t) :: fileobj !< Fms2io file object
1032 
1033  tile = 1
1034  if(present(tile_count)) tile = tile_count
1035  ntileme = mpp_get_current_ntile(domain)
1036  allocate(tile_id(ntileme))
1037  tile_id = mpp_get_tile_id(domain)
1038 
1039  if (netcdf_file_open(fileobj, mosaic_file, "read")) then
1040  call netcdf_read_data(fileobj, "gridfiles", grid_file, corner=tile_id(tile))
1041  grid_file = 'INPUT/'//trim(grid_file)
1042  call netcdf_file_close(fileobj)
1043  endif
1044 
1045 end subroutine get_mosaic_tile_grid
1046 
1047 include "register_domain_restart_variable.inc"
1048 include "domain_read.inc"
1049 include "domain_write.inc"
1050 #include "compute_global_checksum.inc"
1051 
1052 
1053 end module fms_netcdf_domain_io_mod
1054 !> @}
1055 ! close documentation grouping
subroutine domain_read_0d(fileobj, variable_name, vdata, unlim_dim_level, corner)
I/O domain root reads in a domain decomposed variable at a specific unlimited dimension level and sca...
Definition: domain_read.inc:30
subroutine domain_write_3d(fileobj, variable_name, vdata, unlim_dim_level, corner, edge_lengths)
Gather "compute" domain data on the I/O root rank and then have the I/O root write out the data that ...
subroutine register_domain_restart_variable_3d(fileobj, variable_name, vdata, dimensions, is_optional, chunksizes)
Add a domain decomposed variable.
subroutine domain_write_4d(fileobj, variable_name, vdata, unlim_dim_level, corner, edge_lengths)
Gather "compute" domain data on the I/O root rank and then have the I/O root write out the data that ...
subroutine register_domain_restart_variable_1d(fileobj, variable_name, vdata, dimensions, is_optional, chunksizes)
Add a domain decomposed variable.
subroutine domain_write_1d(fileobj, variable_name, vdata, unlim_dim_level, corner, edge_lengths)
Gather "compute" domain data on the I/O root rank and then have the I/O root write out the data that ...
subroutine domain_write_5d(fileobj, variable_name, vdata, unlim_dim_level, corner, edge_lengths)
Gather "compute" domain data on the I/O root rank and then have the I/O root write out the data that ...
subroutine register_domain_restart_variable_5d(fileobj, variable_name, vdata, dimensions, is_optional, chunksizes)
Add a domain decomposed variable.
subroutine domain_write_0d(fileobj, variable_name, vdata, unlim_dim_level, corner)
Gather "compute" domain data on the I/O root rank and then have the I/O root write out the data that ...
subroutine domain_write_2d(fileobj, variable_name, vdata, unlim_dim_level, corner, edge_lengths)
Gather "compute" domain data on the I/O root rank and then have the I/O root write out the data that ...
subroutine domain_read_3d(fileobj, variable_name, vdata, unlim_dim_level, corner, edge_lengths)
I/O domain root reads in a domain decomposed variable at a specific unlimited dimension level and sca...
subroutine register_domain_restart_variable_4d(fileobj, variable_name, vdata, dimensions, is_optional, chunksizes)
Add a domain decomposed variable.
subroutine domain_read_1d(fileobj, variable_name, vdata, unlim_dim_level, corner, edge_lengths)
I/O domain root reads in a domain decomposed variable at a specific unlimited dimension level and sca...
Definition: domain_read.inc:57
subroutine domain_read_2d(fileobj, variable_name, vdata, unlim_dim_level, corner, edge_lengths)
I/O domain root reads in a domain decomposed variable at a specific unlimited dimension level and sca...
Definition: domain_read.inc:88
subroutine domain_read_4d(fileobj, variable_name, vdata, unlim_dim_level, corner, edge_lengths)
I/O domain root reads in a domain decomposed variable at a specific unlimited dimension level and sca...
subroutine domain_read_5d(fileobj, variable_name, vdata, unlim_dim_level, corner, edge_lengths)
I/O domain root reads in a domain decomposed variable at a specific unlimited dimension level and sca...
subroutine register_domain_restart_variable_0d(fileobj, variable_name, vdata, dimensions, is_optional, chunksizes)
Add a domain decomposed variable.
subroutine register_domain_restart_variable_2d(fileobj, variable_name, vdata, dimensions, is_optional, chunksizes)
Add a domain decomposed variable.
subroutine, public restart_filepath_mangle(dest, source)
Add ".res" to an input file path.
subroutine, public io_domain_tile_filepath_mangle(dest, source, io_domain_tile_id)
Add the I/O domain tile id to an input filepath.
logical function, public string_compare(string1, string2, ignore_case)
Compare strings.
subroutine, public domain_tile_filepath_mangle(dest, source, domain_tile_id)
Add the domain tile id to an input filepath.
subroutine add_domain_attribute(fileobj, variable_name)
Add a "domain_decomposed" attribute to the axis variables because it is required by mppnccombine.
integer function get_domain_decomposed_dimension_index(fileobj, variable_name, xory, broadcast)
Given a variable, get the index of the "x" or "y" domain decomposed dimension.
integer function get_domain_decomposed_index(name_, array, size_)
Get the index of a domain decomposed dimension.
subroutine, public save_domain_restart(fileobj, unlim_dim_level)
Loop through registered restart variables and write them to a netcdf file.
logical function is_variable_domain_decomposed(fileobj, variable_name, broadcast, xindex, yindex, xpos, ypos)
Determine if a variable is "domain decomposed.".
logical function, public is_dimension_registered(fileobj, dimension_name)
Determine whether a domain-decomposed dimension has been registered to the file object.
subroutine domain_offsets(data_xsize, data_ysize, domain, xpos, ypos, isd, isc, xc_size, jsd, jsc, yc_size, buffer_includes_halos, extra_x_point, extra_y_point, msg)
Utility routine that retrieves domain indices.
subroutine, public get_global_io_domain_indices(fileobj, dimname, is, ie, indices)
Get starting/ending global indices of the I/O domain for a domain decomposed file.
subroutine, public register_domain_variable(fileobj, variable_name, variable_type, dimensions, chunksizes)
Add a domain decomposed variable.
logical function, public open_domain_file(fileobj, path, mode, domain, nc_format, is_restart, dont_add_res_to_filename, use_netcdf_mpi)
Open a domain netcdf file.
subroutine, public get_mosaic_tile_grid(grid_file, mosaic_file, domain, tile_count)
Read a mosaic_file and get the grid filename for the current tile or for the tile specified.
subroutine, public register_domain_decomposed_dimension(fileobj, dim_name, xory, domain_position)
Add a dimension to a file associated with a two-dimensional domain.
subroutine, public close_domain_file(fileobj)
Close a domain netcdf file.
character(len=32) function compute_global_checksum_3d(fileobj, variable_name, variable_data, is_decomposed)
@briefs Calculates a variable's checksum across all ranks in the current pelist.
subroutine, public restore_domain_state(fileobj, unlim_dim_level, ignore_checksum)
Loop through registered restart variables and read them from a netcdf file.
integer function open_collective_netcdf_file(fileobj, path, mode, domain, is_restart, dont_add_res_to_filename)
Open a NetCDF-4 file in parallel write mode.
character(len=32) function compute_global_checksum_4d(fileobj, variable_name, variable_data, is_decomposed)
@briefs Calculates a variable's checksum across all ranks in the current pelist.
subroutine append_domain_decomposed_dimension(name_, position_, array, size_)
Add a domain decomposed dimension to an array.
character(len=32) function compute_global_checksum_2d(fileobj, variable_name, variable_data, is_decomposed)
@briefs Calculates a variable's checksum across all ranks in the current pelist.
subroutine, public get_compute_domain_dimension_indices(fileobj, dimname, indices)
Return an array of compute domain indices.
integer function get_domain_position(name_, array, size_)
Given a domain decomposed dimension, get its domain position.
integer function mpp_get_domain_npes(domain)
Set user stack size.
integer function mpp_get_domain_tile_commid(domain)
Set user stack size.
integer function, dimension(size(domain%tile_id(:))) mpp_get_tile_id(domain)
Returns the tile_id on current pe.
logical function mpp_domain_is_symmetry(domain)
Set user stack size.
integer function mpp_get_current_ntile(domain)
Returns number of tile on current pe.
integer function mpp_get_ntile_count(domain)
Returns number of tiles in mosaic.
integer function, dimension(2) mpp_get_io_domain_layout(domain)
Set user stack size.
type(domain2d) function, pointer mpp_get_io_domain(domain)
Set user stack size.
These routines retrieve the axis specifications associated with the compute domains....
These routines retrieve the axis specifications associated with the data domains. The domain is a der...
These routines retrieve the axis specifications associated with the global domains....
Retrieve list of PEs associated with a domain decomposition. The 1D version of this call returns an a...
The domain2D type contains all the necessary information to define the global, compute and data domai...
integer, parameter, public mpp_info_null
MPP_INFO_NULL acts as an analagous mpp-macro for MPI_INFO_NULL to share with fms2_io NetCDF4 mpi-io....
Definition: mpp.F90:1349
integer function mpp_pe()
Returns processor ID.
Definition: mpp_util.inc:406
Perform parallel broadcasts.
Definition: mpp.F90:1104
Error handler.
Definition: mpp.F90:381
subroutine, public netcdf_file_close(fileobj)
Close a netcdf file.
Definition: netcdf_io.F90:742
logical function, public netcdf_file_open(fileobj, path, mode, nc_format, pelist, is_restart, dont_add_res_to_filename)
Open a netcdf file.
Definition: netcdf_io.F90:547
subroutine, public netcdf_add_dimension(fileobj, dimension_name, dimension_length, is_compressed)
Add a dimension to a file.
Definition: netcdf_io.F90:875
integer function, public get_variable_num_dimensions(fileobj, variable_name, broadcast)
Get the number of dimensions a variable depends on.
Definition: netcdf_io.F90:1598
subroutine, public get_dimension_size(fileobj, dimension_name, dim_size, broadcast)
Get the length of a dimension.
Definition: netcdf_io.F90:1448
logical function, public dimension_exists(fileobj, dimension_name, broadcast)
Determine if a dimension exists.
Definition: netcdf_io.F90:1342
logical function, public variable_att_exists(fileobj, variable_name, attribute_name, broadcast)
Determine if a variable's attribute exists.
Definition: netcdf_io.F90:1222
subroutine, public get_variable_dimension_names(fileobj, variable_name, dim_names, broadcast)
Get the name of a variable's dimensions.
Definition: netcdf_io.F90:1632
subroutine, public check_netcdf_code(err, msg)
Check for errors returned by netcdf.
Definition: netcdf_io.F90:383
subroutine, public netcdf_add_variable(fileobj, variable_name, variable_type, dimensions, chunksizes)
Add a variable to a file.
Definition: netcdf_io.F90:956