!> @file piovdc.F90 !> @author Yannick Polius !> @version 1.0 !> @date 03/22/2012 !> @brief The piovdc library for writing Vapor Data Collection (VDC) 2 data files !> !>
!> @details The piovdc library is used to write VDC2 data files in a !> parallel manner using PIO. After the prerequisite library functions are !> used, a call to pio_writedarray is made, writing the passed !> data to an on disk VDC2 collection.
!> PRE-REQUISITES:
!> VDF meta-file must be generated, using either rawtovdf or vdfcreate !> if advanced features (wavelet type, compression ratios, or boundary type) are needed !> VDF file requires VDC version to be 2, and requires the Waveletname, !> WaveletBoundaryMode, CompressionRations, and NumTransforms to be set.
!> POST-EFFECTS:
!> After a successful write, VDC2 data will be in a directory located in !> the same directory as the vdf file, using the vdf name, appended with _data !> (ex. ghost.vdf generates VDC2 data in the dir ghost_data in the vdf dir) !> If no compression is enabled, a single, uncompressed file will be !> generated using PIO instead of a VDC module piovdc use pio_kinds, only : i4, r4, pio_offset implicit none integer (i4) :: vdc_dims(3), vdc_bsize(3), vdc_ts integer (kind=PIO_OFFSET) :: vdc_iostart(3), vdc_iocount(3) contains !> @brief subroutine checks start/count for out of bounds, adjusts if the start/count is too high, zeroes start if it is invalid !> POST-EFFECTS: !>
all start/counts are now legal, non-IO tasks have zeroed start counts !> @param[in] global_dims int(3) global grid dimensions !> @param[in] rank int rank of current MPI task !> @param[inout] start int(3) current MPI task global start !> @param[inout] count int(3) current MPI task global count subroutine adjust_bounds(global_dims, start, count, rank) real (r4), dimension(:), intent(in) :: global_dims integer(i4), intent(in) :: rank integer (kind=PIO_OFFSET), dimension(:), intent(inout) :: start, count !first check to ensure the start is legal if (start(1) .GT. global_dims(1) .OR. start(2) .GT. global_dims(2) .OR. start(3) .GT. global_dims(3)) then !outside of global bounds! !negate everything, they're useless #ifdef DEBUG print *, ' rank: ' , rank, ' start: ' , start, ' count: ' , count , ' negated' #endif start = (/ 0, 0, 0/) count = (/ 0, 0, 0/) else !start is legit but count might not be, check & adjust to the boundaries if(count(1) + start(1) - 1 .GT. global_dims(1)) then count(1) = global_dims(1) - start(1) + 1 endif if(count(2) + start(2) - 1 .GT. global_dims(2)) then count(2) = global_dims(2) - start(2) + 1 endif if(count(3) + start(3) - 1 .GT. global_dims(3)) then count(3) = global_dims(3) - start(3) + 1 endif end if end subroutine !> @brief subroutine that, given a global grid, VDC blocksize, and max # of nioprocs, will !> automatically create an VDC optimized IO decomposition that uses the most possible IO tasks ! !> POST-EFFECTS: !>
Each MPI Task is now either and IO task or a computational task. IO tasks have nonzero start/counts !> @param[in] rank int rank of the current MPI task !> @param[inout] nioprocs int represents the max possible # of IO procs, algorithm will try to get as close as possible to this # and return it in nioprocs !> @param[in] blockdims int(3) global grid dimensions represented as VDC blocks !> @param[out] start int(3) iostart for the current MPI task !> @param[out] count int(3) iocount for the current MPI task !> @param[in] bsize int(3) VDC block size subroutine auto_get_start_count(rank, nioprocs, block_dims, start, count, bsize) use pio_kinds integer (kind=PIO_OFFSET), intent(out):: start(3), count(3) integer(i4), dimension(:), intent(in) :: bsize integer (i4), intent(in) :: rank real (r4), dimension(:), intent(in) :: block_dims integer (i4), intent(inout) :: nioprocs !locals real (r4) :: proc_count integer (i4) :: lpp, spp0, spp1, counter, slab_counter, calc_procs, nslabs, nlinesPslab logical :: found found = .FALSE. nlinesPslab = CEILING(block_dims(2)) !max # of possible lines per slab PER TASK nslabs = CEILING(block_dims(3)) calc_procs = -1 if (nioprocs .EQ. 1) then nioprocs = 1 start = (/0, 0, 0/) count = block_dims * bsize else do slab_counter=1, nslabs do counter=1, nlinesPslab proc_count = CEILING(nlinesPslab / REAL(counter)) * CEILING(nslabs / REAL(slab_counter)) !test to see if counter # of lines per processor per slab is possible if (nioprocs >= proc_count) then if (proc_count .gt. calc_procs) then calc_procs = proc_count ! return the actual # of io procs used count = (/ INT(block_dims(1) * bsize(1)), counter * bsize(2), slab_counter *bsize(3) /) start = (/ 0, mod(rank, INT(CEILING(nlinesPslab / REAL(counter)))) * counter * bsize(2), INT(rank / CEILING(nlinesPslab / REAL(counter))) * slab_counter * bsize(3)/) + 1 call adjust_bounds(block_dims * bsize, start, count, rank) if(proc_count .eq. nioprocs) then !using max #of procs, suitable solution found (for now) found = .TRUE. exit end if if (found) then exit end if end if end if #ifdef DEBUG #endif end do if (found) then exit end if end do end if nioprocs = calc_procs end subroutine !> @brief subroutine that prepares the global grid to be split by the auto_start_count routine ! !> POST-EFFECTS: !>
A valid IO decomposition is created that can be used with PIO !> @param[in] rank int rank of the current !> @param[in] data_dims int(3) size of the global grid !> @param[in] vdc_bsize int(3) VDC block size !> @param[out] iostart int(3) IO start for the current MPI task !> @param[out] iocount int(3) IO count for the current MPI task !> @param[inout] ioprocs int max # of IO procs, gets returned as the actual # used subroutine init_vdc2(rank, data_dims, vdc_bsize, iostart, iocount, ioprocs) integer (kind=PIO_OFFSET), intent(out) :: iostart(3), iocount(3) integer (i4), intent(in) :: rank integer(i4), dimension(:), intent(in) :: data_dims, vdc_bsize integer (i4), intent(inout):: ioprocs !locals real(r4) :: vdc_blocks(3) integer (i4) :: ierr #ifdef DEBUG if(rank .eq. 0) then print *, 'Calling get start count...block_dims: ', data_dims/real(vdc_bsize), ' bsize: ' , vdc_bsize, ' ioprocs: ', ioprocs, ' dims: ', data_dims, ' rank: ',rank endif #endif vdc_blocks = data_dims/real(vdc_bsize) call auto_get_start_count (rank, ioprocs, vdc_blocks, iostart, iocount, vdc_bsize) #ifdef DEBUG print *, 'Retrieved VDF start count', iostart, '-', iocount, 'rank: ' , rank #endif endsubroutine end module piovdc