Files
src_old/src/H5MultiBlock.c
T

1231 lines
29 KiB
C

/*!
\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.)
*/
/*!
\internal
\defgroup h5multiblock_static H5MultiBlock Static
*/
/*!
\internal
\defgroup h5multiblock_private H5MultiBlock Private
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <hdf5.h>
#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_buffers
/*******************************************************************************
* 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
_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] );
k = f->myproc % mb->decomp[2];
j = (f->myproc / mb->decomp[2]) % mb->decomp[1];
i = f->myproc / (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; i<I1; i++) { \
for (j=J0; j<J1; j++) { \
for (k=K0; k<K1; k++) { \
offset = i*X + j*Y + k*Z; \
memcpy ( data + offset, zeroval, typesize ); \
} \
} \
}
ZERO_SLAB ( 0, 0, 0,
mb->halo_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; i<count; i++)
{
memcpy ( buffer + dst_offset,
data + src_offset,
blocklen );
dst_offset += blocklen;
src_offset += stride;
}
}
static void
_halo_buffer_unpack (
char *data, /*!< OUT: local block */
const char *buffer, /*!< IN: 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 = offset;
src_offset = 0;
for (i=0; i<count; i++)
{
memcpy ( data + dst_offset,
buffer + src_offset,
blocklen );
dst_offset += stride;
src_offset += blocklen;
}
}
static h5part_int64_t
_halo_exchange_buffers (
const H5PartFile *f, /*!< IN: file handle */
char *data, /*!< IN/OUT: local buffer */
h5part_int64_t count,
h5part_int64_t blocklen,
h5part_int64_t stride,
int proc_spacing,
const char *edges
) {
char *send_buffer;
char *recv_buffer;
int ibufsize;
h5part_int64_t bufsize;
h5part_int64_t herr;
struct H5MultiBlockStruct *mb = f->multiblock;
bufsize = count * blocklen;
ibufsize = (int)bufsize;
if ( (h5part_int64_t)ibufsize != bufsize ) return HANDLE_MPI_INT64_ERR;
send_buffer = malloc ( bufsize );
if ( send_buffer == NULL ) return HANDLE_H5PART_NOMEM_ERR;
recv_buffer = 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;
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 */
count = 1;
blocklen = mb->halo_dims[2] * mb->halo_dims[1] * mb->halo_radii[0];
blocklen *= typesize;
stride = mb->halo_dims[2] * mb->halo_dims[1] * mb->halo_dims[0];
stride *= 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 */
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 */
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;
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;
#if H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 8
dataset_id = H5Dopen2 ( b->field_group_id, "0", H5P_DEFAULT );
#else
dataset_id = H5Dopen ( b->field_group_id, "0" );
#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 );
herr = _alloc_block ( f, data, type );
if ( herr < 0 ) return herr;
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 = 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 = 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 ) {
_pad_block ( f, *data, type );
_zero_block (f, *data, type );
herr = _halo_exchange ( f, *data, type );
if ( herr != H5PART_SUCCESS ) return herr;
}
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;
#if H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 8
dataset = H5Dcreate2 (
b->field_group_id,
"0",
type,
b->shape,
H5P_DEFAULT,
b->create_prop,
H5P_DEFAULT );
#else
dataset = H5Dcreate (
b->field_group_id,
"0",
type,
b->shape,
b->create_prop );
#endif
if ( dataset < 0 ) return HANDLE_H5D_CREATE_ERR ( name, f->timestep );
herr = H5Dwrite (
dataset,
type,
b->memshape,
b->diskshape,
f->xfer_prop,
data );
if ( herr < 0 ) return HANDLE_H5D_WRITE_ERR ( name, f->timestep );
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_c_api
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_c_api
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_c_api
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_c_api
Returns the field dimensions of the last field that was read.
\return \c H5PART_SUCCESS on success<br>
\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_c_api
Returns the block dimensions of the last field that was read.
\return \c H5PART_SUCCESS on success<br>
\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_c_api
Return the offsets for the block belonging to processor \c proc.
\return \c H5PART_SUCCESS on success<br>
\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_c_api
Finds a 3D block decomposition for an arbitrary number of processors
\c nprocs.
\return \c H5PART_SUCCESS on success<br>
\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_c_api
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;
}
#endif