aboutsummaryrefslogtreecommitdiff
path: root/src/util/hdf5_recombiner.c
diff options
context:
space:
mode:
authortradke <tradke@4825ed28-b72c-4eae-9704-e50c059e567d>2001-02-24 20:42:07 +0000
committertradke <tradke@4825ed28-b72c-4eae-9704-e50c059e567d>2001-02-24 20:42:07 +0000
commit1e0f92af6a27dc604a94ef17344479a47dd37b32 (patch)
tree46360f8d0dd3887c653da61cba79cdad60a3d6fb /src/util/hdf5_recombiner.c
parent7198b7259ee6e7fd9aeba8a4f064579de8844241 (diff)
Source code for the HDF5 recombiner.
git-svn-id: http://svn.cactuscode.org/arrangements/CactusPUGHIO/IOHDF5/trunk@40 4825ed28-b72c-4eae-9704-e50c059e567d
Diffstat (limited to 'src/util/hdf5_recombiner.c')
-rw-r--r--src/util/hdf5_recombiner.c878
1 files changed, 878 insertions, 0 deletions
diff --git a/src/util/hdf5_recombiner.c b/src/util/hdf5_recombiner.c
new file mode 100644
index 0000000..157d7bc
--- /dev/null
+++ b/src/util/hdf5_recombiner.c
@@ -0,0 +1,878 @@
+ /*@@
+ @file hdf5_recombiner.c
+ @date Sat 24 Feb 2001
+ @author Thomas Radke
+ @desc
+ This utility program recombines chunked Cactus output file(s)
+ in HDF5 file format into a single unchunked HDF5 file.
+ @enddesc
+ @version $Id$
+ @@*/
+
+#include "cctk.h"
+
+#include <hdf5.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* sysconf(3) */
+#endif
+#include "BetaThorns/IOHDF5Util/src/ioHDF5UtilGH.h"
+
+/* the rcs ID and its dummy function to use it */
+static char *rcsid = "$Header$";
+CCTK_FILEVERSION(BetaThorns_IOHDF5_hdf5_recombiner_c)
+
+
+/*****************************************************************************/
+/* macro definitions */
+/*****************************************************************************/
+/* uncomment the following to get some debugging output */
+/* #define IOHDF5_DEBUG 1 */
+
+/* macro to do an HDF5 call, check its return code, and print a warning
+ in case of an error */
+#define CHECK_ERROR(hdf5_call) \
+ do \
+ { \
+ int _error_code = hdf5_call; \
+ \
+ \
+ if (_error_code < 0) \
+ { \
+ fprintf (stderr, "WARNING: line %d: HDF5 call '%s' returned " \
+ "error code %d\n", \
+ __LINE__, #hdf5_call, _error_code); \
+ nerrors++; \
+ } \
+ } while (0)
+
+
+/*****************************************************************************/
+/* global variables */
+/*****************************************************************************/
+/* NOTE: although it isn't good programming practice
+ we make these variables global for convenience
+ since they are accessed from recursively or
+ indirectly called routines which only get passed
+ a single user-supplied argument */
+static char *pathname = NULL; /* pathname of the current object */
+static hid_t *infiles = NULL; /* list of input file handles */
+static hid_t outfile = -1 ; /* output file handle */
+static int max_filehandles = 0; /* maximum number of open files */
+static char **infilenames = NULL; /* list of input filenames */
+static int nprocs = 0; /* total number of processors */
+static int ioproc_every = 0; /* I/O was done on every N'th processor */
+static int ninfiles = 0; /* number of input files */
+static unsigned int nerrors = 0; /* global error counter */
+
+/*****************************************************************************/
+/* local function prototypes */
+/*****************************************************************************/
+static herr_t CopyObject (hid_t copy_from,
+ const char *objectname,
+ void *arg);
+static herr_t CopyAttribute (hid_t src,
+ const char *attr_name,
+ void *arg);
+static int RecombineGroupData (const char *groupname);
+
+
+ /*@@
+ @routine main
+ @date Sat 24 Feb 2001
+ @author Thomas Radke
+ @desc
+ Main routine of the HDF5 recombiner
+ @enddesc
+
+ @calls CopyObject
+
+ @var argc
+ @vdesc number of command line arguments
+ @vtype int
+ @vio in
+ @endvar
+ @var argv
+ @vdesc command line arguments
+ @vtype char *[]
+ @vio in
+ @endvar
+
+ @returntype int
+ @returndesc
+ 0 for success, negative return values indicate an error
+ @endreturndesc
+@@*/
+int main (int argc, char *argv[])
+{
+ int infile;
+ int unchunked;
+ struct stat fileinfo;
+ char *tmp, *template;
+ hid_t group, attr, dataspace;
+
+
+ /* say hello to the user */
+ printf ("\n");
+ printf (" -----------------------------\n");
+ printf (" Cactus 4 HDF5 File Recombiner\n");
+ printf (" -----------------------------\n");
+ printf ("\n");
+
+ /* give some help if called with incorrect number of parameters */
+ if (argc != 3)
+ {
+ fprintf (stderr, "Usage: %s <chunked_infile0> <unchunked_outfile>\n",
+ argv[0]);
+ fprintf (stderr, " eg, %s alp.file_0.h5 alp.h5\n\n", argv[0]);
+ return (0);
+ }
+
+ /* determine maximum number of files opened simultaneously */
+ max_filehandles = sysconf (_SC_OPEN_MAX);
+ if (max_filehandles < 0)
+ {
+ fprintf (stderr, "WARNING: Cannot determine filehandle limit ! "
+ "Assuming no limit...\n");
+ }
+ /* subtract stdin, stdout, stderr, and output filehandle */
+ max_filehandles -= 4;
+
+ /* open (first) input file */
+ if (stat (argv[1], &fileinfo))
+ {
+ fprintf (stderr, "ERROR: Cannot open input file '%s': %s\n\n",
+ argv[1], strerror (errno));
+ return (-1);
+ }
+ if (! H5Fis_hdf5 (argv[1]))
+ {
+ fprintf (stderr, "ERROR: Input file '%s' is not an HDF5 file !\n\n",
+ argv[1]);
+ return (-1);
+ }
+ infiles = (hid_t *) malloc (1 * sizeof (hid_t));
+ infiles[0] = H5Fopen (argv[1], H5F_ACC_RDONLY, H5P_DEFAULT);
+ if (infiles[0] < 0)
+ {
+ fprintf (stderr, "ERROR: Cannot open input file '%s' as an HDF5 file !\n\n",
+ argv[1]);
+ return (-1);
+ }
+
+ /* read the file set info from the GLOBAL_ATTRIBUTES_GROUP group */
+ CHECK_ERROR (group = H5Gopen (infiles[0], GLOBAL_ATTRIBUTES_GROUP));
+ if (group < 0)
+ {
+ fprintf (stderr, "ERROR: Cannot open attribute group '"
+ GLOBAL_ATTRIBUTES_GROUP "' in input file '%s' ! "
+ "Is this really a Cactus 4 HDF5 data file ?\n\n",
+ argv[1]);
+ return (-1);
+ }
+ tmp = NULL;
+ if (tmp == NULL)
+ {
+ attr = H5Aopen_name (group, "nprocs");
+ if (attr < 0)
+ {
+ tmp = "Cannot find attribute 'nprocs'";
+ }
+ else
+ {
+ if (H5Aread (attr, H5T_NATIVE_INT, &nprocs) < 0)
+ {
+ tmp = "Cannot read attribute 'nprocs'";
+ }
+ CHECK_ERROR (H5Aclose (attr));
+ }
+ }
+ if (tmp == NULL)
+ {
+ attr = H5Aopen_name (group, "ioproc_every");
+ if (attr < 0)
+ {
+ tmp = "Cannot find attribute 'ioproc_every'";
+ }
+ else
+ {
+ if (H5Aread (attr, H5T_NATIVE_INT, &ioproc_every) < 0)
+ {
+ tmp = "Cannot read attribute 'ioproc_every'";
+ }
+ CHECK_ERROR (H5Aclose (attr));
+ }
+ }
+ if (tmp == NULL)
+ {
+ attr = H5Aopen_name (group, "unchunked");
+ if (attr < 0)
+ {
+ tmp = "Cannot find attribute 'unchunked'";
+ }
+ else
+ {
+ if (H5Aread (attr, H5T_NATIVE_INT, &unchunked) < 0)
+ {
+ tmp = "Cannot read attribute 'unchunked'";
+ }
+ CHECK_ERROR (H5Aclose (attr));
+ }
+ }
+ CHECK_ERROR (H5Gclose (group));
+ if (tmp)
+ {
+ fprintf (stderr, "ERROR: %s in attribute group '"
+ GLOBAL_ATTRIBUTES_GROUP "' of input file '%s' ! "
+ "Is this really a Cactus 4 HDF5 data file ?\n\n",
+ tmp, argv[1]);
+ return (-1);
+ }
+
+ /* check if the input file already contains only unchunked data */
+ if (unchunked)
+ {
+ fprintf (stderr, "WARNING: Nothing to do ! Input file '%s' already "
+ "contains only unchunked data.\n\n", argv[1]);
+ return (0);
+ }
+
+ /* create output file */
+ outfile = H5Fcreate (argv[2], H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+ if (outfile < 0)
+ {
+ fprintf (stderr, "ERROR: Cannot create output file '%s' !\n\n", argv[2]);
+ return (-1);
+ }
+
+ /* get the number of chunked input files to process */
+ ninfiles = nprocs / ioproc_every;
+ if (nprocs % ioproc_every)
+ {
+ ninfiles++;
+ }
+ if (max_filehandles < 0)
+ {
+ max_filehandles = ninfiles;
+ }
+
+ printf ("Recombining HDF5 data from %d processors, "
+ "output was written to %d file(s)\n\n", nprocs, ninfiles);
+
+ /* allocate arrays for input file descriptors and filenames */
+ infiles = (hid_t *) realloc (infiles, ninfiles * sizeof (hid_t));
+ infilenames = (char **) malloc (ninfiles * sizeof (char *));
+
+ /* now get all chunked input filenames and check that they can be opened */
+ if (ninfiles == 1)
+ {
+ /* not much to be done here */
+ infilenames[0] = strdup (argv[1]);
+ }
+ else
+ {
+ /* close the first file (it might not be the one written by processor 0) */
+ CHECK_ERROR (H5Fclose (infiles[0]));
+
+ /* get the basename of input file(s) */
+ tmp = strstr (argv[1], ".file_");
+ if (tmp == NULL)
+ {
+ fprintf (stderr, "ERROR: Cannot parse input file name '%s' ! "
+ "Is this really a chunked Cactus HDF5 input file ?\n\n",
+ argv[1]);
+ return (-1);
+ }
+
+ /* build the input filename template */
+ template = strdup (argv[1]);
+ template[tmp - argv[1] + 6] = 0;
+ strcat (template, "%d.h5");
+
+ /* now loop through all the files */
+ for (infile = 0; infile < ninfiles; infile++)
+ {
+ /* build the input filename */
+ infilenames[infile] = (char *) malloc (strlen (template) + 10);
+ sprintf (infilenames[infile], template, infile);
+ infiles[infile] = H5Fopen (infilenames[infile], H5F_ACC_RDONLY,
+ H5P_DEFAULT);
+ if (infiles[infile] < 0)
+ {
+ fprintf (stderr, "ERROR: Cannot open chunked HDF5 input file '%s' !\n",
+ infilenames[infile]);
+ return (-1);
+ }
+
+ /* close file if filehandle limit would be exceeded */
+ if (infile > max_filehandles)
+ {
+ CHECK_ERROR (H5Fclose (infiles[infile]));
+ }
+ }
+
+ free (template);
+ }
+
+ /* do the recombination by iterating over all objects
+ in the (first) input file written by processor 0 */
+ pathname = "";
+ CHECK_ERROR (H5Giterate (infiles[0], "/", NULL, CopyObject, &outfile));
+
+ /* now reset the file info attributes in the GLOBAL_ATTRIBUTES_GROUP group
+ to indicate unchunked file data */
+ CHECK_ERROR (group = H5Gopen (outfile, GLOBAL_ATTRIBUTES_GROUP));
+ CHECK_ERROR (H5Adelete (group, "unchunked"));
+ CHECK_ERROR (H5Adelete (group, "nprocs"));
+ CHECK_ERROR (H5Adelete (group, "ioproc_every"));
+
+ unchunked = 1;
+ nprocs = ioproc_every = 1;
+ CHECK_ERROR (dataspace = H5Screate (H5S_SCALAR));
+ CHECK_ERROR (attr = H5Acreate (group, "unchunked", H5T_NATIVE_INT,
+ dataspace, H5P_DEFAULT));
+ CHECK_ERROR (H5Awrite (attr, H5T_NATIVE_INT, &unchunked));
+ CHECK_ERROR (H5Aclose (attr));
+ CHECK_ERROR (attr = H5Acreate (group, "nprocs", H5T_NATIVE_INT,
+ dataspace, H5P_DEFAULT));
+ CHECK_ERROR (H5Awrite (attr, H5T_NATIVE_INT, &nprocs));
+ CHECK_ERROR (H5Aclose (attr));
+ CHECK_ERROR (attr = H5Acreate (group, "ioproc_every", H5T_NATIVE_INT,
+ dataspace, H5P_DEFAULT));
+ CHECK_ERROR (H5Awrite (attr, H5T_NATIVE_INT, &ioproc_every));
+ CHECK_ERROR (H5Aclose (attr));
+ CHECK_ERROR (H5Sclose (dataspace));
+ CHECK_ERROR (H5Gclose (group));
+
+ /* finally, close all open files */
+ for (infile = 0; infile < ninfiles; infile++)
+ {
+ if (infile <= max_filehandles)
+ {
+ CHECK_ERROR (H5Fclose (infiles[infile]));
+ }
+ free (infilenames[infile]);
+ }
+ free (infiles);
+ CHECK_ERROR (H5Fclose (outfile));
+
+ /* report status */
+ if (nerrors == 0)
+ {
+ printf ("\n\n *** All Cactus data successfully recombined. ***\n\n");
+ }
+ else
+ {
+ fprintf (stderr, "\n\n *** WARNING: %d errors occured during "
+ "recombination. ***\n\n", nerrors);
+ }
+
+ return (0);
+}
+
+
+/*****************************************************************************/
+/* local routines */
+/*****************************************************************************/
+ /*@@
+ @routine CopyObject
+ @date Sat 24 Feb 2001
+ @author Thomas Radke
+ @desc
+ Iterator recursively called by H5Giterate() for every object
+ in the input file
+ It copies the current object or invokes the recombiner on it.
+ @enddesc
+
+ @calls CopyAttribute
+ RecombineGroupData
+
+ @var from
+ @vdesc identifier for the group the current object belongs to
+ @vtype hid_t
+ @vio in
+ @endvar
+ @var objectname
+ @vdesc name of the current object
+ @vtype const char *
+ @vio in
+ @endvar
+ @var _to
+ @vdesc user-supplied argument indicating the output object identifier
+ @vtype hid_t
+ @vio in
+ @endvar
+
+ @returntype int
+ @returndesc
+ 0 - continue the iteration for following group objects
+ 1 - short-curcuit, no further iteration of this group
+ @endreturndesc
+@@*/
+static herr_t CopyObject (hid_t from,
+ const char *objectname,
+ void *_to)
+{
+ hid_t to, datatype, dataspace;
+ H5G_stat_t objectinfo;
+ char *current_pathname;
+ int retval;
+ size_t objectsize;
+ void *value;
+
+
+ /* build the full pathname for the current to object to process */
+ current_pathname = pathname;
+ pathname = (char *) malloc (strlen (current_pathname) +
+ strlen (objectname) + 2);
+ sprintf (pathname, "%s/%s", current_pathname, objectname);
+
+ /* get the output object identifier */
+ retval = 0;
+ to = *(hid_t *) _to;
+
+ /* check the type of the current object */
+ CHECK_ERROR (H5Gget_objinfo (from, objectname, 0, &objectinfo));
+ if (objectinfo.type == H5G_GROUP)
+ {
+ /* try to recombine data within this group */
+ if (RecombineGroupData (pathname) <= 0)
+ {
+ /* this group didn't contain chunked data
+ so just copy it as is */
+ printf (" copying group '%s'\n", pathname);
+
+ CHECK_ERROR (from = H5Gopen (from, objectname));
+ CHECK_ERROR (to = H5Gcreate (to, objectname, 0));
+ /* iterate over all objects in the (first) input file */
+ CHECK_ERROR (H5Giterate (from, ".", NULL, CopyObject, &to));
+ CHECK_ERROR (H5Aiterate (from, NULL, CopyAttribute, &to));
+ CHECK_ERROR (H5Gclose (to));
+ CHECK_ERROR (H5Gclose (from));
+ }
+ }
+ else if (objectinfo.type == H5G_DATASET)
+ {
+ /* current object is an unchunked dataset - copy it as is */
+ printf (" copying dataset '%s'\n", pathname);
+
+ CHECK_ERROR (from = H5Dopen (from, objectname));
+ CHECK_ERROR (datatype = H5Dget_type (from));
+ CHECK_ERROR (dataspace = H5Dget_space (from));
+ CHECK_ERROR (to = H5Dcreate (to, objectname, datatype, dataspace,
+ H5P_DEFAULT));
+ objectsize = H5Tget_size (datatype);
+ if (H5Sis_simple (dataspace) > 0)
+ {
+ objectsize *= H5Sget_simple_extent_npoints (dataspace);
+ }
+ if (objectsize > 0)
+ {
+ value = malloc (objectsize);
+ CHECK_ERROR (H5Dread (from, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ value));
+ CHECK_ERROR (H5Dwrite (to, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ value));
+ free (value);
+ }
+ CHECK_ERROR (H5Aiterate (from, NULL, CopyAttribute, &to));
+ CHECK_ERROR (H5Dclose (to));
+ CHECK_ERROR (H5Dclose (from));
+ CHECK_ERROR (H5Sclose (dataspace));
+ CHECK_ERROR (H5Tclose (datatype));
+ }
+ else
+ {
+ fprintf (stderr, "WARNING: Found object '%s' which is not neither a "
+ "group nor a dataset ! Object will not be copied.\n",
+ pathname);
+ nerrors++;
+ }
+
+ /* reset the pathname */
+ free (pathname);
+ pathname = current_pathname;
+
+ return (retval);
+}
+
+
+ /*@@
+ @routine CopyAttribute
+ @date Sat 24 Feb 2001
+ @author Thomas Radke
+ @desc
+ Iterator recursively called by H5Aiterate() for every attribute
+ of an object (dataset or group)
+ @enddesc
+
+ @var from
+ @vdesc identifier for the group or dataset to read the attribute from
+ @vtype hid_t
+ @vio in
+ @endvar
+ @var attrname
+ @vdesc name of the current attribute
+ @vtype const char *
+ @vio in
+ @endvar
+ @var _to
+ @vdesc user-supplied argument indicating the group or dataset
+ to copy the attribute to
+ @vtype hid_t
+ @vio in
+ @endvar
+
+ @returntype int
+ @returndesc
+ 0 - continue the iteration for following attributes
+ @endreturndesc
+@@*/
+static herr_t CopyAttribute (hid_t from,
+ const char *attrname,
+ void *_to)
+{
+ hid_t attr, datatype, dataspace, to;
+ size_t attrsize;
+ void *value;
+
+
+ /* get the target group/dataset identifier */
+ to = *(hid_t *) _to;
+
+ /* open the attribute given by its name, get type, dataspace, and value
+ and just copy it */
+ CHECK_ERROR (attr = H5Aopen_name (from, attrname));
+ CHECK_ERROR (datatype = H5Aget_type (attr));
+ CHECK_ERROR (dataspace = H5Aget_space (attr));
+ attrsize = H5Tget_size (datatype);
+ if (H5Sis_simple (dataspace) > 0)
+ {
+ attrsize *= H5Sget_simple_extent_npoints (dataspace);
+ }
+ if (attrsize > 0)
+ {
+ value = malloc (attrsize);
+ CHECK_ERROR (H5Aread (attr, datatype, value));
+ CHECK_ERROR (H5Aclose (attr));
+ CHECK_ERROR (attr = H5Acreate (to, attrname, datatype, dataspace,
+ H5P_DEFAULT));
+ CHECK_ERROR (H5Awrite (attr, datatype, value));
+ free (value);
+ }
+ CHECK_ERROR (H5Aclose (attr));
+ CHECK_ERROR (H5Sclose (dataspace));
+ CHECK_ERROR (H5Tclose (datatype));
+
+ return (0);
+}
+
+
+ /*@@
+ @routine RecombineGroupData
+ @date Sat 24 Feb 2001
+ @author Thomas Radke
+ @desc
+ Checks whether the passed group indicates a group with chunked
+ datasets. If so it will recombine these from all chunked input
+ files.
+ @enddesc
+
+ @calls CopyAttribute
+
+ @var groupname
+ @vdesc name of the current group to process
+ @vtype const char *
+ @vio in
+ @endvar
+
+ @returntype int
+ @returndesc
+ 0 - group does not contain chunked data
+ 1 - chunked group data successfully recombined
+ @endreturndesc
+@@*/
+static int RecombineGroupData (const char *groupname)
+{
+ int infile, chunk, nchunks;
+ hid_t group, attr, datatype, dataspace;
+ hid_t chunked_datatype, chunked_dataspace, chunked_dataset;
+ hid_t unchunked_datatype, unchunked_dataspace, unchunked_dataset;
+ hsize_t tmp1, ndims, chunk_ndims, *global_size, *chunk_dims;
+ hssize_t tmp2, *chunk_origin;
+ size_t chunksize;
+ void *chunkdata;
+ char *chunkname;
+ H5T_class_t class;
+
+
+ /* open the group given by its name */
+ CHECK_ERROR (group = H5Gopen (infiles[0], groupname));
+
+ /* a group with chunked data must have a 'global_size' attribute */
+ CHECK_ERROR (H5Eset_auto (NULL, NULL));
+ attr = H5Aopen_name (group, "global_size");
+ CHECK_ERROR (H5Eset_auto ((H5E_auto_t) H5Eprint, stderr));
+ if (attr < 0)
+ {
+ CHECK_ERROR (H5Gclose (group));
+ return (0);
+ }
+
+ /* read the global size of the unchunked dataset */
+ datatype = H5Aget_type (attr);
+ dataspace = H5Aget_space (attr);
+ ndims = H5Sget_simple_extent_npoints (dataspace);
+ if (H5Tget_class (datatype) != H5T_INTEGER)
+ {
+ fprintf (stderr, "WARNING: 'global_size' attribute of group '%s' is not "
+ "of type integer !\n", groupname);
+ ndims = 0;
+ }
+ if (ndims > 0)
+ {
+ global_size = (hsize_t *) calloc (ndims, sizeof (hsize_t));
+ CHECK_ERROR (H5Aread (attr, H5T_NATIVE_HSIZE, global_size));
+ }
+ else
+ {
+ global_size = NULL;
+ }
+ CHECK_ERROR (H5Sclose (dataspace));
+ CHECK_ERROR (H5Tclose (datatype));
+ CHECK_ERROR (H5Aclose (attr));
+
+ /* return if the 'global_size' attribute couldn't be read */
+ if (ndims <= 0)
+ {
+ CHECK_ERROR (H5Gclose (group));
+ return (0);
+ }
+
+ /* allocate string buffer holding the possible dataset chunk names */
+ chunkname = (char *) malloc (strlen (groupname) + sizeof ("chunk") + 20);
+
+ /* now try to open the first chunk of a dataset as '<groupname>/chunk0' */
+ sprintf (chunkname, "%s/chunk0", pathname);
+ CHECK_ERROR (H5Eset_auto (NULL, NULL));
+ chunked_dataset = H5Dopen (infiles[0], chunkname);
+ CHECK_ERROR (H5Eset_auto ((H5E_auto_t) H5Eprint, stderr));
+ if (chunked_dataset < 0)
+ {
+ free (chunkname);
+ free (global_size);
+ CHECK_ERROR (H5Gclose (group));
+ return (0);
+ }
+
+ /*** Okay, now we can be quite sure that this is a group containing
+ chunks of a dataset named 'groupname'.
+ So let's go on and create the unchunked dataset in the output file.
+ ***/
+
+ /* the unchunked dataset gets the same datatype as the first chunk */
+ CHECK_ERROR (unchunked_datatype = H5Dget_type (chunked_dataset));
+
+ /* its dimensions are taken from the 'global_size' attribute
+ with the least changing dimension being the first element */
+ for (chunk_ndims = 0; chunk_ndims < ndims/2; chunk_ndims++)
+ {
+ tmp1 = global_size[chunk_ndims];
+ global_size[chunk_ndims] = global_size[ndims - chunk_ndims - 1];
+ global_size[ndims - chunk_ndims - 1] = tmp1;
+ }
+ CHECK_ERROR (unchunked_dataspace = H5Screate_simple (ndims, global_size,
+ NULL));
+
+ /* create the unchunked dataset */
+ CHECK_ERROR (unchunked_dataset = H5Dcreate (outfile, pathname,
+ unchunked_datatype,
+ unchunked_dataspace,
+ H5P_DEFAULT));
+
+ /* copy all group attributes to the new dataset */
+ CHECK_ERROR (H5Aiterate (group, NULL, CopyAttribute, &unchunked_dataset));
+
+ /* don't need these anymore */
+ free (global_size);
+ CHECK_ERROR (H5Dclose (chunked_dataset));
+ CHECK_ERROR (H5Gclose (group));
+
+ /* allocate buffers to read a 'chunk_origin' attribute and the chunk dims */
+ chunk_origin = (hssize_t *) calloc (ndims, sizeof (hssize_t));
+ chunk_dims = (hsize_t *) calloc (ndims, sizeof (hsize_t));
+
+ /* now read all the chunks from all input files and write them into the
+ unchunked output dataset as a hyperslab */
+ printf (" recombining dataset '%s'\n", pathname);
+ for (infile = 0; infile < ninfiles; infile++)
+ {
+ /* get the number of chunks in this file */
+#if 0
+ nchunks = nprocs / ninfiles;
+ if (infile == ninfiles - 1)
+ {
+ nchunks += nprocs % ninfiles;
+ }
+#else
+ nchunks = ioproc_every;
+ if (infile == ninfiles - 1 && nprocs % ioproc_every)
+ {
+ nchunks = nprocs % ninfiles;
+ }
+#endif
+
+ /* re-open the file if it was closed before */
+ if (infile > max_filehandles)
+ {
+#ifdef DEBUG
+ printf ("reopening input file '%s'\n", infilenames[infile]);
+#endif
+ CHECK_ERROR (infiles[infile] = H5Fopen (infilenames[infile],
+ H5F_ACC_RDONLY, H5P_DEFAULT));
+ }
+
+ /* loop over all chunks of this input file */
+ for (chunk = 0; chunk < nchunks; chunk++)
+ {
+ /* build the object name of this chunk */
+ sprintf (chunkname, "%s/chunk%d", pathname, chunk);
+
+ /* open the dataset chunk */
+ CHECK_ERROR (chunked_dataset = H5Dopen (infiles[infile], chunkname));
+ if (chunked_dataset < 0)
+ {
+ continue;
+ }
+
+ /* read the 'chunk_origin' attribute of this chunk */
+ attr = H5Aopen_name (chunked_dataset, "chunk_origin");
+ datatype = H5Aget_type (attr);
+ dataspace = H5Aget_space (attr);
+ class = H5Tget_class (datatype);
+ chunk_ndims = H5Sget_simple_extent_npoints (dataspace);
+ if (chunk_ndims == ndims)
+ {
+ CHECK_ERROR (H5Aread (attr, H5T_NATIVE_HSSIZE, chunk_origin));
+ }
+ CHECK_ERROR (H5Sclose (dataspace));
+ CHECK_ERROR (H5Tclose (datatype));
+ CHECK_ERROR (H5Aclose (attr));
+
+ /* check consistency */
+ if (class != H5T_INTEGER)
+ {
+ fprintf (stderr, "WARNING: 'chunk_origin' attribute of dataset '%s' "
+ "is not of type integer !\n", chunkname);
+ CHECK_ERROR (H5Dclose (chunked_dataset));
+ nerrors++;
+ continue;
+ }
+ if (chunk_ndims != ndims)
+ {
+ fprintf (stderr, "WARNING: dimensions of 'chunk_origin' attribute of "
+ "dataset '%s' don't match with dataset dimensions !\n",
+ chunkname);
+ CHECK_ERROR (H5Dclose (chunked_dataset));
+ nerrors++;
+ continue;
+ }
+
+ /* check the datatype of this chunk to be consistent with the others
+ NOTE: for data files from a distributed heterogenous run
+ the data types might differ in their native representation
+ so we cannot use H5Tequal() here */
+ chunked_datatype = H5Dget_type (chunked_dataset);
+ class = H5Tget_class (chunked_datatype);
+ CHECK_ERROR (H5Tclose (chunked_datatype));
+ if (class != H5Tget_class (unchunked_datatype))
+ {
+ fprintf (stderr, "WARNING: datatype clas of chunk '%s' differs from "
+ "first chunk's datatype ! Chunk will be omitted.\n",
+ chunkname);
+ CHECK_ERROR (H5Dclose (chunked_dataset));
+ nerrors++;
+ continue;
+ }
+
+ /* read the chunk data */
+ CHECK_ERROR (chunked_dataspace = H5Dget_space (chunked_dataset));
+ chunk_ndims = H5Sget_simple_extent_ndims (chunked_dataspace);
+ if (chunk_ndims != ndims)
+ {
+ fprintf (stderr, "WARNING: dimensions of 'chunk_origin' attribute of "
+ "dataset '%s' don't match with dataset dimensions !\n",
+ chunkname);
+ CHECK_ERROR (H5Sclose (chunked_dataspace));
+ CHECK_ERROR (H5Dclose (chunked_dataset));
+ nerrors++;
+ continue;
+ }
+ CHECK_ERROR (H5Sget_simple_extent_dims (chunked_dataspace, chunk_dims,
+ NULL));
+
+ /* HDF5 needs the least changing dimension first */
+ for (chunk_ndims = 0; chunk_ndims < ndims/2; chunk_ndims++)
+ {
+ tmp2 = chunk_origin[chunk_ndims];
+ chunk_origin[chunk_ndims] = chunk_origin[ndims - chunk_ndims - 1];
+ chunk_origin[ndims - chunk_ndims - 1] = tmp2;
+ }
+ CHECK_ERROR (H5Sselect_hyperslab (unchunked_dataspace, H5S_SELECT_SET,
+ chunk_origin, NULL, chunk_dims, NULL));
+ chunksize = H5Tget_size (unchunked_datatype);
+ if (H5Sis_simple (chunked_dataspace) > 0)
+ {
+ chunksize *= H5Sget_simple_extent_npoints (chunked_dataspace);
+ }
+ if (chunksize > 0)
+ {
+ /* give some info output */
+ printf (" - file %d chunk %d\n", infile, chunk + ioproc_every*infile);
+ printf (" chunk dimensions: [%d", (int) chunk_dims[ndims - 1]);
+ for (chunk_ndims = 1; chunk_ndims < ndims; chunk_ndims++)
+ printf (", %d", (int) chunk_dims[ndims - chunk_ndims - 1]);
+ printf ("] chunk origin: [%d", (int) chunk_origin[ndims - 1]);
+ for (chunk_ndims = 1; chunk_ndims < ndims; chunk_ndims++)
+ printf (", %d", (int) chunk_origin[ndims - chunk_ndims - 1]);
+ printf ("]\n");
+
+ /* read the chunk and write it to the unchunked dataset */
+ chunkdata = malloc (chunksize);
+ CHECK_ERROR (H5Dread (chunked_dataset, unchunked_datatype, H5S_ALL,
+ H5S_ALL, H5P_DEFAULT, chunkdata));
+ CHECK_ERROR (H5Dwrite (unchunked_dataset, unchunked_datatype,
+ chunked_dataspace, unchunked_dataspace,
+ H5P_DEFAULT, chunkdata));
+ free (chunkdata);
+ }
+ CHECK_ERROR (H5Dclose (chunked_dataset));
+ CHECK_ERROR (H5Sclose (chunked_dataspace));
+
+ } /* end of loop over all chunks in this input file */
+
+ /* close input file if filehandle limit would be exceeded */
+ if (infile > max_filehandles)
+ {
+#ifdef DEBUG
+ printf ("temporarily closing input file '%s'\n", infilenames[infile]);
+#endif
+ CHECK_ERROR (H5Fclose (infiles[infile]));
+ }
+ } /* end of loop over all input files */
+
+ /* close objects and free allocated resources */
+ CHECK_ERROR (H5Dclose (unchunked_dataset));
+ CHECK_ERROR (H5Tclose (unchunked_datatype));
+ CHECK_ERROR (H5Sclose (unchunked_dataspace));
+ free (chunk_dims);
+ free (chunk_origin);
+ free (chunkname);
+
+ /* indicate no further processing of this group in H5Giterate() */
+ return (1);
+}