/*! \defgroup h5multiblock_c_api H5MultiBlock C API This package includes C code for writing and reading 3D datasets intended for distributed memory applications. The underlying file format is H5Part, a simple data storage schema and API derived from HDF5. In particular, we use the H5Block subset of H5Part for managing 3D field data. To achieve large contiguous reads and writes, the datasets use HDF5's chunking mechanism to reorder the layout on disk such that local subfields (or "blocks") of the 3D field are stored contiguously (as opposed to storing an entire column contiguously, as in the Fortran column-major layout for an array). We refer to this chunked layout as "multiblock" and the column-major layout as "uniblock." Multiblocked datasets exhibit the following constraints: * All blocks must be the same size (a requirement of the HDF5 chunking mechanism). * The block dimensions must divide the field dimensions, to prevent fringe data that will lower performance by interfering with contiguous I/O operations. * For load-balancing purposes, the total number of blocks that the field is decomposed into must be a multiple of the number of nodes in the distributed system. (For ease of implementation, we require that the number of blocks equal the number of nodes.) */ /*! \ingroup h5multiblock_c_api \defgroup h5multiblock_model Setting up the Data Model */ /*! \ingroup h5multiblock_c_api \defgroup h5multiblock_data Reading and Writing Datasets */ /*! \ingroup h5multiblock_c_api \defgroup h5multiblock_attrib Reading and Writing Attributes */ /*! \internal \defgroup h5multiblock_static H5MultiBlock Static */ /*! \internal \defgroup h5multiblock_private H5MultiBlock Private */ #include #include #include #include #include "H5Part.h" #include "H5PartErrors.h" #include "H5PartPrivate.h" #include "H5Block.h" #include "H5BlockTypes.h" #include "H5BlockErrors.h" #include "H5BlockPrivate.h" #include "H5MultiBlock.h" #include "H5MultiBlockTypes.h" #include "H5MultiBlockErrors.h" #include "H5MultiBlockPrivate.h" #ifdef PARALLEL_IO #define HALO_EXCHANGE_METHOD _halo_exchange_vectors /******************************************************************************* * Static functions *******************************************************************************/ /*! \ingroup h5multiblock_static \internal Check whether \c f points to a valid file handle. \return H5PART_SUCCESS or error code */ static h5part_int64_t _H5MultiBlock_file_is_valid ( const H5PartFile *f /*!< IN: file handle */ ) { if ( f == NULL ) return H5PART_ERR_BADFD; if ( f->file == 0 ) return H5PART_ERR_BADFD; if ( f->block == NULL ) return H5PART_ERR_BADFD; if ( f->multiblock == NULL ) return H5PART_ERR_BADFD; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_static \internal Gets the block decomposition and offsets. \return H5PART_SUCCESS or error code */ static h5part_int64_t _get_decomp_and_offsets ( const H5PartFile *f /*!< IN: file handle */ ) { h5part_int64_t i, j, k; h5part_int64_t nblocks; struct H5MultiBlockStruct *mb = f->multiblock; mb->decomp[0] = mb->field_dims[0] / mb->block_dims[0]; mb->decomp[1] = mb->field_dims[1] / mb->block_dims[1]; mb->decomp[2] = mb->field_dims[2] / mb->block_dims[2]; if (f->myproc == 0) { _H5Part_print_info ("Block decomposition: (%ld,%ld,%ld)", mb->decomp[2], mb->decomp[1], mb->decomp[0] ); } _H5Part_print_debug ("PROC[%d]: Block decomposition: (%ld,%ld,%ld)", f->myproc, mb->decomp[2], mb->decomp[1], mb->decomp[0] ); int rank = (f->myproc + mb->proc_shift) % f->nprocs; k = rank % mb->decomp[2]; j = (rank / mb->decomp[2]) % mb->decomp[1]; i = rank / (mb->decomp[2] * mb->decomp[1]); /* keep track of blocks that border the edges of the field */ if (i == 0) mb->field_edges |= H5MB_EDGE_Z0; if (i == mb->decomp[0] - 1) mb->field_edges |= H5MB_EDGE_Z1; if (j == 0) mb->field_edges |= H5MB_EDGE_Y0; if (j == mb->decomp[1] - 1) mb->field_edges |= H5MB_EDGE_Y1; if (k == 0) mb->field_edges |= H5MB_EDGE_X0; if (k == mb->decomp[2] - 1) mb->field_edges |= H5MB_EDGE_X1; mb->offsets[0] = i * mb->block_dims[0]; mb->offsets[1] = j * mb->block_dims[1]; mb->offsets[2] = k * mb->block_dims[2]; _H5Part_print_debug ("PROC[%d]: Block offsets: (%ld,%ld,%ld)", f->myproc, mb->offsets[2], mb->offsets[1], mb->offsets[0] ); nblocks = mb->decomp[0] * mb->decomp[1] * mb->decomp[2]; if (f->myproc == 0) { _H5Part_print_info ("Number of blocks: %ld", nblocks); } if (nblocks != f->nprocs) { return HANDLE_H5PART_BLOCK_DECOMP_ERR; } return H5PART_SUCCESS; } /*! \ingroup h5multiblock_static \internal Calculates an integer \c divisor of \c m that is >= the \c n root of \c m. Used for finding block decompositions for an arbitrary number of processors. \return \c divisor */ static int _nth_root_int_divisor (const int m, const int n) { int i, root; double p; p = 1.0 / (double)n; root = (int) ceil ( pow ( (double)m, p ) ); for (i=root; i<=m; i++) { if (m % i == 0) return i; } return i; } /*! \ingroup h5multiblock_static \internal Allocates a block using the dimensions read from the file and the halo radii specified by the user. \return H5PART_SUCCESS or error code */ static h5part_int64_t _alloc_block ( const H5PartFile *f, /*!< IN: file handle */ char **data, /*!< IN/OUT: buffer to alloc */ hid_t type /*!< IN: HDF5 datatype of buffer */ ) { size_t typesize; h5part_int64_t nelems; struct H5MultiBlockStruct *mb = f->multiblock; /* size of datatype */ typesize = H5Tget_size ( type ); /* number of elements */ nelems = mb->halo_dims[0] * mb->halo_dims[1] * mb->halo_dims[2]; *data = (char*) malloc ( nelems * typesize ); if ( ! *data ) return HANDLE_H5PART_NOMEM_ERR; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_static \internal Takes a contiguous buffer of data and rearranges it in place to add padding with \c radius layers. Assumes that \c buffer is already large enough to perform this operation. \return H5PART_SUCCESS or error code */ static h5part_int64_t _pad_block ( const H5PartFile *f, /*!< IN: file handle */ char *data, /*!< IN/OUT: buffer to pad */ hid_t type /*!< IN: HDF5 datatype of buffer */ ) { size_t typesize; h5part_int64_t j, k; h5part_int64_t iDst, iSrc; h5part_int64_t xSize, xySize; h5part_int64_t hxSize, hxySize; h5part_int64_t hxInset; struct H5MultiBlockStruct *mb = f->multiblock; /* size of datatype */ typesize = H5Tget_size ( type ); /* size of row in original block */ xSize = mb->block_dims[2] * typesize; /* size of slab in original block */ xySize = xSize * mb->block_dims[1]; /* size of row/slab with halo regions */ hxSize = mb->halo_dims[2] * typesize; hxySize = hxSize * mb->halo_dims[1]; /* inset of row in halo region */ hxInset = mb->halo_radii[2] * typesize; for (k=(mb->block_dims[0]-1);k>=0;k--) { for (j=(mb->block_dims[1]-1);j>=0;j--) { iSrc = k*xySize + j*xSize; iDst = (k + mb->halo_radii[0]) * hxySize + (j + mb->halo_radii[1]) * hxSize + hxInset; memcpy ( data + iDst, data + iSrc, xSize ); } } return H5PART_SUCCESS; } /*! \ingroup h5multiblock_static \internal Zeroes the padding of a block. This routine uses the 0 value for the specific datatype (int, float, etc.) so that it is safe even with floating point implementations that do not use all bytes zero to represent 0.F. \return H5PART_SUCCESS or error code */ static h5part_int64_t _zero_block ( const H5PartFile *f, /*!< IN: file handle */ char *data, /*!< IN/OUT: buffer to pad */ hid_t type /*!< IN: HDF5 datatype of buffer */ ) { h5part_int64_t i, j, k; h5part_int64_t offset; size_t typesize; char zeroval[8]; h5part_int64_t xySize, xSize; struct H5MultiBlockStruct *mb = f->multiblock; typesize = H5Tget_size ( type ); if (type == H5T_NATIVE_DOUBLE) { typesize = sizeof (h5part_float64_t); h5part_float64_t zero = 0.0; memcpy ( zeroval, &zero, typesize ); } else if (type == H5T_NATIVE_FLOAT) { typesize = sizeof (h5part_float32_t); h5part_float32_t zero = 0.F; memcpy ( zeroval, &zero, typesize ); } else if (type == H5T_NATIVE_INT64) { typesize = sizeof (h5part_int64_t); h5part_int64_t zero = 0; memcpy ( zeroval, &zero, typesize ); } else if (type == H5T_NATIVE_INT32) { typesize = sizeof (h5part_int32_t); h5part_int32_t zero = 0; memcpy ( zeroval, &zero, typesize ); } xSize = mb->halo_dims[2] * typesize; xySize = xSize * mb->halo_dims[1]; #define ZERO_SLAB(I0,J0,K0,I1,J1,K1,X,Y,Z) \ for (i=I0; ihalo_radii[0], mb->halo_dims[1], mb->halo_dims[2], xySize, xSize, typesize ); ZERO_SLAB ( 0, 0, 0, mb->halo_radii[1], mb->halo_dims[0], mb->halo_dims[2], xSize, xySize, typesize ); ZERO_SLAB ( 0, 0, 0, mb->halo_radii[2], mb->halo_dims[0], mb->halo_dims[1], typesize, xySize, xSize ); ZERO_SLAB ( mb->halo_dims[0] - mb->halo_radii[0], 0, 0, mb->halo_dims[0], mb->halo_dims[1], mb->halo_dims[2], xySize, xSize, typesize ); ZERO_SLAB ( mb->halo_dims[1] - mb->halo_radii[1], 0, 0, mb->halo_dims[1], mb->halo_dims[0], mb->halo_dims[2], xSize, xySize, typesize ); ZERO_SLAB ( mb->halo_dims[2] - mb->halo_radii[2], 0, 0, mb->halo_dims[2], mb->halo_dims[0], mb->halo_dims[1], typesize, xySize, xSize ); return H5PART_SUCCESS; } static h5part_int64_t _halo_exchange_vector ( const H5PartFile *f, /*!< IN: file handle */ char *data, /*!< IN/OUT: local buffer */ MPI_Datatype *halo_vector, int send_offset, int recv_offset, char send_only_edge, char recv_only_edge, int proc_spacing ) { int ret; MPI_Status status; struct H5MultiBlockStruct *mb = f->multiblock; if ( mb->field_edges & recv_only_edge ) { ret = MPI_Recv ( data + recv_offset, 1, *halo_vector, f->myproc - proc_spacing, f->myproc, // use destination proc as tag f->comm, &status ); } else if ( mb->field_edges & send_only_edge ) { ret = MPI_Send ( data + send_offset, 1, *halo_vector, f->myproc + proc_spacing, f->myproc + proc_spacing, f->comm ); } else { ret = MPI_Sendrecv ( data + send_offset, 1, *halo_vector, f->myproc + proc_spacing, f->myproc + proc_spacing, data + recv_offset, 1, *halo_vector, f->myproc - proc_spacing, f->myproc, f->comm, &status ); } if ( ret != MPI_SUCCESS ) return HANDLE_MPI_SENDRECV_ERR; return H5PART_SUCCESS; } static h5part_int64_t _halo_exchange_buffer ( const H5PartFile *f, /*!< IN: file handle */ char *send_buffer, char *recv_buffer, int bufsize, char send_only_edge, char recv_only_edge, int proc_spacing ) { int ret; MPI_Status status; struct H5MultiBlockStruct *mb = f->multiblock; if ( mb->field_edges & recv_only_edge ) { ret = MPI_Recv ( recv_buffer, bufsize, MPI_BYTE, f->myproc - proc_spacing, f->myproc, // use destination proc as tag f->comm, &status ); } else if ( mb->field_edges & send_only_edge ) { ret = MPI_Send ( send_buffer, bufsize, MPI_BYTE, f->myproc + proc_spacing, f->myproc + proc_spacing, f->comm ); } else { ret = MPI_Sendrecv ( send_buffer, bufsize, MPI_BYTE, f->myproc + proc_spacing, f->myproc + proc_spacing, recv_buffer, bufsize, MPI_BYTE, f->myproc - proc_spacing, f->myproc, f->comm, &status ); } if ( ret != MPI_SUCCESS ) return HANDLE_MPI_SENDRECV_ERR; return H5PART_SUCCESS; } static void _halo_buffer_pack ( const char *data, /*!< IN: local block */ char *buffer, /*!< OUT: packed buffer */ h5part_int64_t offset, h5part_int64_t count, h5part_int64_t blocklen, h5part_int64_t stride ) { h5part_int64_t i; h5part_int64_t dst_offset, src_offset; dst_offset = 0; src_offset = offset; for (i=0; imultiblock; if ( f->myproc == 0 ) _H5Part_print_info ( "Using halo exchange method _halo_exchange_buffers"); bufsize = count * blocklen; ibufsize = (int)bufsize; if ( (h5part_int64_t)ibufsize != bufsize ) return HANDLE_MPI_INT64_ERR; send_buffer = (char*) malloc ( bufsize ); if ( send_buffer == NULL ) return HANDLE_H5PART_NOMEM_ERR; recv_buffer = (char*) malloc ( bufsize ); if ( recv_buffer == NULL ) return HANDLE_H5PART_NOMEM_ERR; /* forward */ _halo_buffer_pack ( data, send_buffer, stride - 2*blocklen, count, blocklen, stride ); herr = _halo_exchange_buffer ( f, send_buffer, recv_buffer, ibufsize, edges[0], edges[1], proc_spacing); if ( herr != H5PART_SUCCESS ) return herr; if ( ! (mb->field_edges & edges[0]) ) { _halo_buffer_unpack ( data, recv_buffer, 0, count, blocklen, stride ); } /* backward */ _halo_buffer_pack ( data, send_buffer, blocklen, count, blocklen, stride ); herr = _halo_exchange_buffer ( f, send_buffer, recv_buffer, ibufsize, edges[1], edges[0], -proc_spacing); if ( herr != H5PART_SUCCESS ) return herr; if ( ! (mb->field_edges & edges[1]) ) { _halo_buffer_unpack ( data, recv_buffer, stride - blocklen, count, blocklen, stride ); } free ( send_buffer ); free ( recv_buffer ); return H5PART_SUCCESS; } static h5part_int64_t _halo_exchange_vectors ( const H5PartFile *f, char *data, h5part_int64_t count, h5part_int64_t blocklen, h5part_int64_t stride, int proc_spacing, const char *edges ) { int ret; int icount, iblocklen, istride; h5part_int64_t herr; MPI_Datatype halo_vector; if ( f->myproc == 0 ) _H5Part_print_info ( "Using halo exchange method _halo_exchange_vectors"); icount = (int)count; if ( (h5part_int64_t)icount != count ) return HANDLE_MPI_INT64_ERR; iblocklen = (int)blocklen; if ( (h5part_int64_t)iblocklen != blocklen ) return HANDLE_MPI_INT64_ERR; istride = (int)stride; if ( (h5part_int64_t)istride != stride ) return HANDLE_MPI_INT64_ERR; ret = MPI_Type_vector ( icount, iblocklen, istride, MPI_BYTE, &halo_vector); if (ret != MPI_SUCCESS) return HANDLE_MPI_TYPE_ERR; ret = MPI_Type_commit ( &halo_vector ); if (ret != MPI_SUCCESS) return HANDLE_MPI_TYPE_ERR; /* forward */ herr = _halo_exchange_vector ( f, data, &halo_vector, stride - 2*blocklen, 0, edges[0], edges[1], proc_spacing ); if (herr != H5PART_SUCCESS) return herr; /* backward */ herr = _halo_exchange_vector ( f, data, &halo_vector, blocklen, stride - blocklen, edges[1], edges[0], -proc_spacing ); if (herr != H5PART_SUCCESS) return herr; ret = MPI_Type_free ( &halo_vector ); if (ret != MPI_SUCCESS) return HANDLE_MPI_TYPE_ERR; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_static \internal Exchanges halo regions among neighboring blocks using MPI. \return H5PART_SUCCESS or error code */ static h5part_int64_t _halo_exchange ( const H5PartFile *f, /*!< IN: file handle */ char *data, /*!< IN/OUT: local buffer */ hid_t type /*!< IN: HDF5 datatype of buffer */ ) { size_t typesize; char edges[2]; int proc_spacing; h5part_int64_t count, blocklen, stride; h5part_int64_t herr; struct H5MultiBlockStruct *mb = f->multiblock; typesize = H5Tget_size (type); /* xy-slab * the best case: the entire x-dimension by y-dimension by z halo * radius slab can be transferred contiguosly */ if ( mb->decomp[0] > 1 ) { count = 1; blocklen = mb->halo_dims[2] * mb->halo_dims[1] * mb->halo_radii[0] * typesize; stride = mb->halo_dims[2] * mb->halo_dims[1] * mb->halo_dims[0] * typesize; /* jump by an xy-slab to get the next z block */ proc_spacing = mb->decomp[2] * mb->decomp[1]; edges[0] = H5MB_EDGE_Z0; edges[1] = H5MB_EDGE_Z1; herr = HALO_EXCHANGE_METHOD ( f, data, count, blocklen, stride, proc_spacing, edges); if ( herr != H5PART_SUCCESS ) return herr; } /* xz-slab * the second best case: a rectangle of x-dimension by y halo radius * can be transferred contiguously */ if ( mb->decomp[1] > 1 ) { count = mb->halo_dims[0]; blocklen = mb->halo_dims[2] * mb->halo_radii[1] * typesize; stride = mb->halo_dims[2] * mb->halo_dims[1] * typesize; /* jump by an x-row to get the next y block */ proc_spacing = mb->decomp[2]; edges[0] = H5MB_EDGE_Y0; edges[1] = H5MB_EDGE_Y1; herr = HALO_EXCHANGE_METHOD ( f, data, count, blocklen, stride, proc_spacing, edges); if ( herr != H5PART_SUCCESS ) return herr; } /* yz-slab * the worst case: only small rows with length = x halo radius */ if ( mb->decomp[2] > 1 ) { count = mb->halo_dims[1] * mb->halo_dims[0]; blocklen = mb->halo_radii[2] * typesize; stride = mb->halo_dims[2] * typesize; /* blocks are contiguous in the x direction */ proc_spacing = 1; edges[0] = H5MB_EDGE_X0; edges[1] = H5MB_EDGE_X1; herr = HALO_EXCHANGE_METHOD ( f, data, count, blocklen, stride, proc_spacing, edges); if ( herr != H5PART_SUCCESS ) return herr; } return H5PART_SUCCESS; } /******************************************************************************* * Private functions *******************************************************************************/ /*! \ingroup h5multiblock_private \internal Initialize H5MultiBlock internal structure. \return H5PART_SUCCESS or error code */ h5part_int64_t _H5MultiBlock_init ( H5PartFile *f /*!< IN: file handle */ ) { struct H5MultiBlockStruct *mb; BLOCK_INIT( f ); if (f->multiblock != NULL) return H5PART_SUCCESS; f->multiblock = (struct H5MultiBlockStruct*) malloc( sizeof (*f->multiblock) ); if ( f->multiblock == NULL ) { return HANDLE_H5PART_NOMEM_ERR; } mb = f->multiblock; mb->halo_radii[0] = 0; mb->halo_radii[1] = 0; mb->halo_radii[2] = 0; mb->field_edges = 0; mb->halo = 0; mb->read = 0; mb->have_decomp = 0; mb->proc_shift = 0; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_private \internal Free H5MultiBlock internal struct; \return H5PART_SUCCESS or error code */ h5part_int64_t _H5MultiBlock_close ( H5PartFile *f /*!< IN: file handle */ ) { free ( f->multiblock ); f->multiblock = NULL; f->close_multiblock = NULL; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_private \internal Read a multiblock field \c name into the buffer starting at \c data using the current time-step and the block decomposition and dimensions defined in the file. \return H5PART_SUCCESS or error code */ h5part_int64_t _H5MultiBlock_read_data ( H5PartFile *f, /*!< IN: file handle */ const char *field_name, /*!< IN: Name of field */ char **data, /*!< OUT: ptr to read buffer ptr*/ hid_t type /*!< IN: HDF5 datatype of buffer */ ) { MULTIBLOCK_INIT( f ); hid_t dataset_id; hid_t dataspace_id; int rank; h5part_int64_t herr; struct H5BlockStruct *b = f->block; struct H5MultiBlockStruct *mb = f->multiblock; char * const fname = _H5Part_get_funcname(); herr = _H5Block_open_field_group ( f, field_name ); if ( herr < 0 ) return herr; dataset_id = H5Dopen ( b->field_group_id, "0" #ifndef H5_USE_16_API , H5P_DEFAULT #endif ); if ( dataset_id < 0 ) return HANDLE_H5D_OPEN_ERR ( "0" ); /* read block dimensions from field attribute */ herr = _H5Part_read_attrib ( b->field_group_id, H5MULTIBLOCK_ATTR_NAME, mb->block_dims ); if ( herr < 0 ) return herr; if ( f->myproc == 0 ) { _H5Part_print_info ("Block dimensions: (%ld,%ld,%ld)", mb->block_dims[2], mb->block_dims[1], mb->block_dims[0] ); } _H5Part_print_debug ("PROC[%d]: Block dimensions: (%ld,%ld,%ld)", f->myproc, mb->block_dims[2], mb->block_dims[1], mb->block_dims[0] ); mb->halo_dims[0] = mb->block_dims[0] + 2*mb->halo_radii[0]; mb->halo_dims[1] = mb->block_dims[1] + 2*mb->halo_radii[1]; mb->halo_dims[2] = mb->block_dims[2] + 2*mb->halo_radii[2]; dataspace_id = H5Dget_space ( dataset_id ); if ( dataspace_id < 0 ) return HANDLE_H5D_GET_SPACE_ERR; rank = H5Sget_simple_extent_dims ( dataspace_id, (hsize_t*)mb->field_dims, NULL ); if ( rank < 0 ) return HANDLE_H5S_GET_SIMPLE_EXTENT_DIMS_ERR; if ( rank != 3 ) return HANDLE_H5PART_DATASET_RANK_ERR ( rank, 3 ); #ifdef H5MB_TIMING _H5Part_print_info ( "timing[%d]: alloc: %.7f", f->myproc, MPI_Wtime() ); #endif herr = _alloc_block ( f, data, type ); if ( herr < 0 ) return herr; #ifdef H5MB_TIMING _H5Part_print_info ( "timing[%d]: read: %.7f", f->myproc, MPI_Wtime() ); #endif herr = _get_decomp_and_offsets ( f ); if ( herr < 0 ) return herr; mb->have_decomp = 1; /* shortcut: use the H5Block layout; indices have to be inverted, though, since * the API exposes Fortran ordering and all internal data uses C ordering */ herr = H5BlockDefine3DFieldLayout ( f, mb->offsets[2], mb->offsets[2] + mb->block_dims[2] - 1, mb->offsets[1], mb->offsets[1] + mb->block_dims[1] - 1, mb->offsets[0], mb->offsets[0] + mb->block_dims[0] - 1); if ( herr < 0 ) return herr; _H5Part_set_funcname ( fname ); herr = _H5Block_select_hyperslab_for_reading ( f, dataset_id ); if ( herr < 0 ) return herr; herr = _H5Part_start_throttle( f ); if (herr < 0) return herr; herr = H5Dread ( dataset_id, type, f->block->memshape, f->block->diskshape, f->xfer_prop, *data ); if ( herr < 0 ) return HANDLE_H5D_READ_ERR ( field_name, f->timestep ); herr = _H5Part_end_throttle( f ); if (herr < 0) return herr; herr = H5Dclose ( dataset_id ); if ( herr < 0 ) return HANDLE_H5D_CLOSE_ERR; herr = _H5Block_close_field_group ( f ); if ( herr < 0 ) return herr; if ( mb->halo ) { #ifdef H5MB_TIMING _H5Part_print_info ( "timing[%d]: pad: %.7f", f->myproc, MPI_Wtime() ); #endif _pad_block ( f, *data, type ); #ifdef H5MB_TIMING _H5Part_print_info ( "timing[%d]: zero: %.7f", f->myproc, MPI_Wtime() ); #endif _zero_block (f, *data, type ); #ifdef H5MB_TIMING _H5Part_print_info ( "timing[%d]: halo: %.7f", f->myproc, MPI_Wtime() ); #endif herr = _halo_exchange ( f, *data, type ); if ( herr != H5PART_SUCCESS ) return herr; #ifdef H5MB_TIMING _H5Part_print_info ( "timing[%d]: end: %.7f", f->myproc, MPI_Wtime() ); #endif } mb->read = 1; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_private \internal Write a multiblock field \c name from the buffer starting at \c data to the current time-step using the defined block decomposition and dimensions. \return H5PART_SUCCESS or error code */ h5part_int64_t _H5MultiBlock_write_data ( H5PartFile *f, /*!< IN: file handle */ const char *name, /*!< IN: name of dataset to write */ const void* data, /*!< IN: data buffer */ hid_t type /*!< IN: HDF5 datatype */ ) { MULTIBLOCK_INIT( f ); CHECK_WRITABLE_MODE( f ); CHECK_TIMEGROUP( f ); CHECK_DECOMP ( f ); hid_t dataset; h5part_int64_t herr; struct H5BlockStruct *b = f->block; struct H5MultiBlockStruct *mb = f->multiblock; char * const fname = _H5Part_get_funcname(); /* shortcut: use the H5Block layout; indices have to be inverted, though, since * the API exposes Fortran ordering and all internal data uses C ordering */ herr = H5BlockDefine3DFieldLayout ( f, mb->offsets[2], mb->offsets[2] + mb->block_dims[2] - 1, mb->offsets[1], mb->offsets[1] + mb->block_dims[1] - 1, mb->offsets[0], mb->offsets[0] + mb->block_dims[0] - 1); if ( herr < 0 ) return herr; herr = H5BlockDefine3DChunkDims( f, mb->block_dims[2], mb->block_dims[1], mb->block_dims[0]); if ( herr < 0 ) return herr; _H5Part_set_funcname ( fname ); herr = _H5Block_create_field_group ( f, name ); if ( herr < 0 ) return herr; dataset = H5Dcreate ( b->field_group_id, "0", type, b->shape, #ifndef H5_USE_16_API H5P_DEFAULT, b->create_prop, H5P_DEFAULT #else b->create_prop #endif ); if ( dataset < 0 ) return HANDLE_H5D_CREATE_ERR ( name, f->timestep ); herr = _H5Part_start_throttle( f ); if (herr < 0) return herr; herr = H5Dwrite ( dataset, type, b->memshape, b->diskshape, f->xfer_prop, data ); if ( herr < 0 ) return HANDLE_H5D_WRITE_ERR ( name, f->timestep ); herr = _H5Part_end_throttle( f ); if (herr < 0) return herr; herr = H5Dclose ( dataset ); if ( herr < 0 ) return HANDLE_H5D_CLOSE_ERR; /* write out the block dimensions to a special field attribute */ herr = _H5Part_write_attrib ( f->block->field_group_id, H5MULTIBLOCK_ATTR_NAME, H5T_NATIVE_INT64, mb->block_dims, 3 ); if ( herr < 0 ) return herr; herr = _H5Block_close_field_group ( f ); if ( herr < 0 ) return herr; return H5PART_SUCCESS; } /******************************************************************************* * Public API *******************************************************************************/ /*! \ingroup h5multiblock_model Define the radius for halo exchanges between the blocks. Blocks on the edges of the field will be padded with zero values out to the radius. \return \c H5PART_SUCCESS on success */ h5part_int64_t H5MultiBlock3dDefineRadius ( H5PartFile *f, /* IN: file handle */ const h5part_int64_t r /* IN: radius for i, j and k directions*/ ) { SET_FNAME ( "H5MultiBlock3dDefineRadius" ); MULTIBLOCK_INIT( f ); struct H5MultiBlockStruct *mb = f->multiblock; mb->halo_radii[0] = mb->halo_radii[1] = mb->halo_radii[2] = r; mb->halo = 1; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_model Define the radii for halo exchanges between the blocks. Blocks on the edges of the field will be padded with zero values out to the radius. Different radii can be set for each direction. \return \c H5PART_SUCCESS on success */ h5part_int64_t H5MultiBlock3dDefineRadii ( H5PartFile *f, /* IN: file handle */ const h5part_int64_t ri, /* IN: radius for i direction */ const h5part_int64_t rj, /* IN: radius for j direction */ const h5part_int64_t rk /* IN: radius for k direction */ ) { SET_FNAME ( "H5MultiBlock3dDefineRadii" ); MULTIBLOCK_INIT( f ); struct H5MultiBlockStruct *mb = f->multiblock; mb->halo_radii[0] = rk; mb->halo_radii[1] = rj; mb->halo_radii[2] = ri; mb->halo = 1; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_model Define the field and block dimensions for writing. \return \c H5PART_SUCCESS on success \c H5PART_ERR_INVAL if block dims do not divide field dims */ h5part_int64_t H5MultiBlock3dDefineDims ( H5PartFile *f, /* IN: file handle */ const h5part_int64_t *field_dims, /* IN: field dimensions */ const h5part_int64_t *block_dims /* IN: block dimensions */ ) { SET_FNAME ( "H5MultiBlock3dDefineDims" ); MULTIBLOCK_INIT ( f ); struct H5MultiBlockStruct *mb = f->multiblock; mb->field_dims[0] = field_dims[2]; mb->field_dims[1] = field_dims[1]; mb->field_dims[2] = field_dims[0]; mb->block_dims[0] = block_dims[2]; mb->block_dims[1] = block_dims[1]; mb->block_dims[2] = block_dims[0]; h5part_int64_t herr = _get_decomp_and_offsets ( f ); if ( herr < 0 ) return H5PART_ERR_INVAL; mb->have_decomp = 1; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_model Returns the field dimensions of the last field that was read. \return \c H5PART_SUCCESS on success
\c H5PART_ERR_INVAL if no field has been read yet */ h5part_int64_t H5MultiBlock3dGetFieldDims( H5PartFile *f, h5part_int64_t *dims ) { SET_FNAME ( "H5MultiBlock3dGetFieldDims" ); MULTIBLOCK_INIT ( f ); struct H5MultiBlockStruct *mb = f->multiblock; if ( ! mb->read ) return H5PART_ERR_INVAL; dims[0] = mb->field_dims[2]; dims[1] = mb->field_dims[1]; dims[2] = mb->field_dims[0]; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_model Returns the block dimensions of the last field that was read. \return \c H5PART_SUCCESS on success
\c H5PART_ERR_INVAL if no field has been read yet */ h5part_int64_t H5MultiBlock3dGetBlockDims( H5PartFile *f, /* IN: file handle */ const char *field_name, /* IN: field name */ h5part_int64_t *dims /* OUT: block dimensions */ ) { SET_FNAME ( "H5MultiBlock3dGetBlockDims" ); MULTIBLOCK_INIT ( f ); struct H5MultiBlockStruct *mb = f->multiblock; if ( ! mb->read ) return H5PART_ERR_INVAL; dims[0] = mb->block_dims[2]; dims[1] = mb->block_dims[1]; dims[2] = mb->block_dims[0]; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_model Return the offsets for the block belonging to processor \c proc. \return \c H5PART_SUCCESS on success
\c H5PART_ERR_INVAL if proc is invalid */ h5part_int64_t H5MultiBlock3dGetOffsetsOfProc ( H5PartFile *f, /* IN: file handle */ const h5part_int64_t proc, /* IN: processor number */ h5part_int64_t *offsets /* OUT: 3d array of offsets */ ) { SET_FNAME ( "H5MultiBlock3dGetOffsetsOfProc" ); MULTIBLOCK_INIT ( f ); if ( ( proc < 0 ) || ( proc >= f->nprocs ) ) return H5PART_ERR_INVAL; struct H5MultiBlockStruct *mb = f->multiblock; offsets[0] = mb->offsets[2]; offsets[1] = mb->offsets[1]; offsets[2] = mb->offsets[0]; return H5PART_SUCCESS; } /*! \ingroup h5multiblock_model Finds a 3D block decomposition for an arbitrary number of processors \c nprocs. \return \c H5PART_SUCCESS on success
\c H5PART_ERR_INVAL if the decomp doesn't have \c nprocs blocks */ h5part_int64_t H5MultiBlock3dCalculateDecomp ( const int nprocs, /*!< IN: number of processors/blocks */ h5part_int64_t *decomp /*!< OUT: 3D block decomposition */ ) { decomp[0] = _nth_root_int_divisor (nprocs, 3); decomp[1] = _nth_root_int_divisor (nprocs / decomp[0], 2); decomp[2] = nprocs / decomp[0] / decomp[1]; if (decomp[0] * decomp[1] * decomp[2] != nprocs) { return H5PART_ERR_INVAL; } return H5PART_SUCCESS; } /*! \ingroup h5multiblock_data Frees a \c block that was allocated during a read. \return \c H5PART_SUCCESS */ h5part_int64_t H5MultiBlockFree ( void *block /*!< IN: block that was allocated during a read */ ) { free (block); return H5PART_SUCCESS; } /*! \ingroup h5multiblock_model Shifts the assignment of procs to blocks within the field. This is useful mainly for I/O benchmarking, in order to defeat the write cache when reading in data that was just written out. \return \c H5PART_SUCCESS */ h5part_int64_t H5MultiBlockShiftProcs ( H5PartFile *f, /* IN: file handle */ const int shift /* IN: shift amount (non-negative) */ ) { SET_FNAME ( "H5MultiBlockShiftProcs" ); MULTIBLOCK_INIT ( f ); if ( shift < 0 ) return H5PART_ERR_INVAL; struct H5MultiBlockStruct *mb = f->multiblock; mb->proc_shift = shift; return H5PART_SUCCESS; } #endif // PARALLEL_IO