@@ -319,11 +319,127 @@ function is_dimension_registered(fileobj, dimension_name) &
319319
320320end 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.
442579subroutine 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