Skip to content

Commit d8a19fd

Browse files
authored
fms2_io: Add collective writes (#1733)
1 parent 41141e9 commit d8a19fd

6 files changed

Lines changed: 1033 additions & 11 deletions

File tree

fms2_io/fms_netcdf_domain_io.F90

Lines changed: 152 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -319,11 +319,127 @@ function is_dimension_registered(fileobj, dimension_name) &
319319

320320
end function is_dimension_registered
321321

322+
!> @brief Open a NetCDF-4 file in parallel write mode
323+
!! @return True on success
324+
function open_collective_netcdf_file(fileobj, path, mode, domain, is_restart, dont_add_res_to_filename) &
325+
result(success)
326+
327+
type(FmsNetcdfDomainFile_t),intent(inout) :: fileobj !< File object.
328+
character(len=*), intent(in) :: path !< File path.
329+
character(len=*), intent(in) :: mode !< File mode. Allowed values
330+
!! are "read", "append", "write", or
331+
!! "overwrite".
332+
type(domain2d), intent(in) :: domain !< Two-dimensional domain.
333+
logical, intent(in), optional :: is_restart !< Flag telling if this file
334+
!! is a restart file. Defaults
335+
!! to false.
336+
logical, intent(in), optional :: dont_add_res_to_filename !< Flag indicating not to add
337+
!! ".res" to the filename
338+
339+
integer :: nc_format_param
340+
integer :: tile_id(1)
341+
integer :: err
342+
character(len=FMS_PATH_LEN) :: combined_filepath
343+
character(len=FMS_PATH_LEN) :: full_path
344+
logical :: is_res
345+
logical :: dont_add_res
346+
integer :: success
347+
integer :: domain_size
348+
349+
success = .true.
350+
call string_copy(fileobj%non_mangled_path, path)
351+
352+
!TODO Lots of duplicate code between this and netcdf_file_open
353+
354+
!! Determine the name of your file !!
355+
356+
!! If the number of tiles is greater than 1 or if the current tile is greater
357+
!than 1 add .tileX. to the filename
358+
tile_id = mpp_get_tile_id(domain)
359+
if (mpp_get_ntile_count(domain) .gt. 1 .or. tile_id(1) > 1) then
360+
call domain_tile_filepath_mangle(combined_filepath, path, tile_id(1))
361+
else
362+
call string_copy(combined_filepath, path)
363+
endif
364+
365+
!< Only add ".res" to the file path if is_restart is set to true
366+
!! and dont_add_res_to_filename is set to false.
367+
is_res = .false.
368+
if (present(is_restart)) then
369+
is_res = is_restart
370+
endif
371+
fileobj%is_restart = is_res
372+
373+
dont_add_res = .false.
374+
if (present(dont_add_res_to_filename)) then
375+
dont_add_res = dont_add_res_to_filename
376+
endif
377+
378+
if (is_res .and. .not. dont_add_res) then
379+
call restart_filepath_mangle(full_path, trim(combined_filepath))
380+
else
381+
call string_copy(full_path, trim(combined_filepath))
382+
endif
383+
384+
call string_copy(fileobj%path, trim(full_path))
385+
386+
nc_format_param = ior(nf90_netcdf4, NF90_MPIIO)
387+
fileobj%is_netcdf4 = .true.
388+
389+
fileobj%domain = domain
390+
call mpp_get_global_domain(fileobj%domain, xsize=domain_size)
391+
392+
if (string_compare(mode, "read", .true.) .or. string_compare(mode, "append", .true.)) &
393+
call error("The use_netcdf_mpi = .true. option for reads is currently not supported")
394+
395+
if (string_compare(mode, "write", .true.)) then
396+
err = nf90_create(trim(fileobj%path), ior(nf90_noclobber, nc_format_param), fileobj%ncid, &
397+
comm = mpp_get_domain_tile_commid(fileobj%domain), info = MPP_INFO_NULL)
398+
elseif (string_compare(mode,"overwrite",.true.)) then
399+
err = nf90_create(trim(fileobj%path), ior(nf90_clobber, nc_format_param), fileobj%ncid, &
400+
comm = mpp_get_domain_tile_commid(fileobj%domain), info = MPP_INFO_NULL)
401+
endif
402+
call check_netcdf_code(err, "open_collective_netcdf_file:"//trim(fileobj%path))
403+
404+
allocate(fileobj%xdims(max_num_domain_decomposed_dims))
405+
fileobj%nx = 0
406+
allocate(fileobj%ydims(max_num_domain_decomposed_dims))
407+
fileobj%ny = 0
408+
409+
! Every rank is the root PE of its own pelist. This forces all ranks to hit any NetCDF calls,
410+
! which are usually inside `if (fileobj%is_root)` blocks.
411+
allocate(fileobj%pelist(1))
412+
fileobj%pelist(1) = mpp_pe()
413+
fileobj%io_root = mpp_pe()
414+
fileobj%is_root = .true.
415+
416+
fileobj%use_collective = .false. !TODO
417+
fileobj%is_diskless = .false.
418+
419+
if (fileobj%is_restart) then
420+
allocate(fileobj%restart_vars(max_num_restart_vars))
421+
fileobj%num_restart_vars = 0
422+
endif
423+
424+
fileobj%is_readonly = string_compare(mode, "read", .true.)
425+
fileobj%mode_is_append = string_compare(mode, "append", .true.)
426+
allocate(fileobj%compressed_dims(max_num_compressed_dims))
427+
fileobj%num_compressed_dims = 0
428+
! Set the is_open flag to true for this file object.
429+
if (.not.allocated(fileobj%is_open)) allocate(fileobj%is_open)
430+
fileobj%is_open = .true.
431+
432+
fileobj%bc_dimensions%xlen = 0
433+
fileobj%bc_dimensions%ylen = 0
434+
fileobj%bc_dimensions%zlen = 0
435+
fileobj%bc_dimensions%cur_dim_len = 0
436+
437+
end function open_collective_netcdf_file
322438

323439
!> @brief Open a domain netcdf file.
324440
!! @return Flag telling if the open completed successfully.
325-
function open_domain_file(fileobj, path, mode, domain, nc_format, is_restart, dont_add_res_to_filename) &
326-
result(success)
441+
function open_domain_file(fileobj, path, mode, domain, nc_format, is_restart, dont_add_res_to_filename, &
442+
use_netcdf_mpi) result(success)
327443

328444
type(FmsNetcdfDomainFile_t),intent(inout) :: fileobj !< File object.
329445
character(len=*), intent(in) :: path !< File path.
@@ -342,6 +458,9 @@ function open_domain_file(fileobj, path, mode, domain, nc_format, is_restart, do
342458
!! to false.
343459
logical, intent(in), optional :: dont_add_res_to_filename !< Flag indicating not to add
344460
!! ".res" to the filename
461+
logical, intent(in), optional :: use_netcdf_mpi !< Flag telling if this file should be using netcdf4 collective
462+
!! reads and writes. Defaults to false.
463+
!! nc_format is automatically set to netcdf4
345464
logical :: success
346465

347466
integer, dimension(2) :: io_layout
@@ -354,6 +473,25 @@ function open_domain_file(fileobj, path, mode, domain, nc_format, is_restart, do
354473
logical :: success2
355474
type(FmsNetcdfDomainFile_t) :: fileobj2
356475

476+
io_domain => mpp_get_io_domain(domain)
477+
478+
fileobj%use_netcdf_mpi = .false.
479+
if (present(use_netcdf_mpi)) fileobj%use_netcdf_mpi = use_netcdf_mpi
480+
481+
if (fileobj%use_netcdf_mpi) then
482+
#ifdef NO_NC_PARALLEL4
483+
call mpp_error(FATAL, "NetCDF was not built with HDF5 parallel I/O features, so parallel writes are not supported. &
484+
&Please turn parallel writes off for the file: " // trim(path))
485+
#endif
486+
487+
if (associated(io_domain)) then
488+
call mpp_error(NOTE, "NetCDF MPI is enabled: ignoring I/O domain. Only one output file will be produced.")
489+
endif
490+
491+
success = open_collective_netcdf_file(fileobj, path, mode, domain, is_restart, dont_add_res_to_filename)
492+
return
493+
endif
494+
357495
!Get the path of a "combined" file.
358496
io_layout = mpp_get_io_domain_layout(domain)
359497
tile_id = mpp_get_tile_id(domain)
@@ -367,7 +505,6 @@ function open_domain_file(fileobj, path, mode, domain, nc_format, is_restart, do
367505
endif
368506

369507
!Get the path of a "distributed" file.
370-
io_domain => mpp_get_io_domain(domain)
371508
if (.not. associated(io_domain)) then
372509
call error("The domain associated with the file:"//trim(path)//" does not have an io_domain.")
373510
endif
@@ -441,7 +578,7 @@ end subroutine close_domain_file
441578
!> @brief Add a dimension to a file associated with a two-dimensional domain.
442579
subroutine register_domain_decomposed_dimension(fileobj, dim_name, xory, domain_position)
443580

444-
type(FmsNetcdfDomainFile_t), intent(inout) :: fileobj !< File object.
581+
type(FmsNetcdfDomainFile_t), target, intent(inout) :: fileobj !< File object.
445582
character(len=*), intent(in) :: dim_name !< Dimension name.
446583
character(len=*), intent(in) :: xory !< Flag telling if the dimension
447584
!! is associated with the "x" or "y"
@@ -458,7 +595,15 @@ subroutine register_domain_decomposed_dimension(fileobj, dim_name, xory, domain_
458595
if (mpp_domain_is_symmetry(fileobj%domain) .and. present(domain_position)) then
459596
dpos = domain_position
460597
endif
461-
io_domain => mpp_get_io_domain(fileobj%domain)
598+
599+
! If using NetCDF MPI, the IO domain is ignored, so use the domain to determine the correct size of each
600+
! domain-decomposed dimension.
601+
if (fileobj%use_netcdf_mpi) then
602+
io_domain => fileobj%domain
603+
else
604+
io_domain => mpp_get_io_domain(fileobj%domain)
605+
endif
606+
462607
if (string_compare(xory, x, .true.)) then
463608
if (dpos .ne. center .and. dpos .ne. east) then
464609
call error("Only domain_position=center or domain_position=EAST is supported for x dimensions."// &
@@ -509,9 +654,10 @@ subroutine add_domain_attribute(fileobj, variable_name)
509654
integer, dimension(2) :: io_layout !< Io_layout in the fileobj's domain
510655

511656
!< Don't add the "domain_decomposition" variable attribute if the io_layout is
512-
!! 1,1, to avoid frecheck "failures"
657+
!! 1,1, or if using mpi netcdf for writes, to avoid frecheck "failures"
513658
io_layout = mpp_get_io_domain_layout(fileobj%domain)
514659
if (io_layout(1) .eq. 1 .and. io_layout(2) .eq. 1) return
660+
if (fileobj%use_netcdf_mpi) return
515661

516662
io_domain => mpp_get_io_domain(fileobj%domain)
517663
dpos = get_domain_decomposed_index(variable_name, fileobj%xdims, fileobj%nx)

0 commit comments

Comments
 (0)