sandbox/acastillo/output_fields/tests_spectra/test_spectra_hdf5.c
The HDF5 spectrum file
Three blocks written to one file, then read back in C – which keeps the checker on the standard library and gives it the stored values rather than a transcription. As in test_spectra_ascii.c the fields are scaled by z and the mode moves between blocks, so the stored array is checked by its contents.
Each output is its own group, so the groups are enumerated rather than assumed: the test reads whatever the writer left, in the order HDF5 lists it by name, and checks that order is time order – which is what the zero padding in the group name buys.
The plane count changes between blocks (NZS), as it does
for a caller sampling a zone that grows. A fixed nz here
would not exercise it, and did not: the shape was wrong on disk for two
layouts running.
Only the vector file’s shapes are read.
#include "utils.h"
#include "acastillo/output_fields/spectra/spectra.h"
#define ML 5
#define NT 3
// planes per block: a different count each time, including one that shrinks
static const int NZS[NT] = {4, 2, 7};
#ifdef HAVE_HDF5
// Peak bin, its energy, and the total, for each plane of one spectrum.
static void summarise (FILE * fp, const char * name, const double * E,
const double * z, int it, int nz, int nk)
{
for (int iz = 0; iz < nz; iz++) {
const double * s = E + (size_t) iz*nk;
double sum = 0., peak = -1.;
int bpeak = -1;
for (int b = 0; b < nk; b++) {
sum += s[b];
if (s[b] > peak)
peak = s[b], bpeak = b;
}
fprintf (fp, "peak %s %d %d %d %.17g %.17g %.17g\n",
name, it, iz, bpeak, peak, sum, z[iz]);
}
}
// Every group of one file, in the order HDF5 lists them by name.
static void dump_file (FILE * fp, const char * filename,
const char ** fields, int nfields, bool values)
{
hid_t file = H5Fopen (filename, H5F_ACC_RDONLY, H5P_DEFAULT);
if (file < 0) {
fprintf (fp, "missing %s\n", filename);
return;
}
if (values) {
int format = -1;
H5LTget_attribute_int (file, ".", "format", &format);
fprintf (fp, "format %d\n", format);
}
// H5Gget_num_objs() rather than H5Gget_info(), whose H5G_info_t qcc
// cannot parse. Root holds nothing but the per-output groups.
hsize_t ngroups = 0;
H5Gget_num_objs (file, &ngroups);
for (hsize_t i = 0; i < ngroups; i++) {
char gname[64];
H5Lget_name_by_idx (file, ".", H5_INDEX_NAME, H5_ITER_INC, i,
gname, sizeof(gname), H5P_DEFAULT);
hid_t g = H5Gopen2 (file, gname, H5P_DEFAULT);
int it = i;
hsize_t dz[2] = {0, 0}, de[2] = {0, 0};
H5LTget_dataset_info (g, "z", dz, NULL, NULL);
double * zv = malloc (dz[0]*sizeof(double));
H5LTread_dataset_double (g, "z", zv);
if (values) {
double tv, hminv, hmaxv;
int nzv, nkv, mv;
H5LTget_attribute_double (g, ".", "t", &tv);
H5LTget_attribute_double (g, ".", "hmin", &hminv);
H5LTget_attribute_double (g, ".", "hmax", &hmaxv);
H5LTget_attribute_int (g, ".", "nz", &nzv);
H5LTget_attribute_int (g, ".", "nk", &nkv);
H5LTget_attribute_int (g, ".", "m", &mv);
fprintf (fp, "block %d %s %d %d %d %.17g %.17g %.17g\n",
it, gname, nzv, nkv, mv, tv, hminv, hmaxv);
fprintf (fp, "shape %d z %d\n", it, (int) dz[0]);
// the bin axis is stored per group, so check each one
hsize_t dk[2] = {0, 0};
H5LTget_dataset_info (g, "kphys", dk, NULL, NULL);
double * kv = malloc (dk[0]*sizeof(double));
H5LTread_dataset_double (g, "kphys", kv);
double dkmax = 0.;
for (int b = 0; b < (int) dk[0]; b++)
dkmax = max (dkmax, fabs (kv[b] - 2.*pi*b/L0));
fprintf (fp, "kphys %d %d %.17g\n", it, (int) dk[0], dkmax);
free (kv);
}
for (int k = 0; k < nfields; k++) {
H5LTget_dataset_info (g, fields[k], de, NULL, NULL);
fprintf (fp, "shape %d %s %d %d\n", it, fields[k],
(int) de[0], (int) de[1]);
if (values) {
double * Ev = malloc ((size_t) de[0]*de[1]*sizeof(double));
H5LTread_dataset_double (g, fields[k], Ev);
summarise (fp, fields[k], Ev, zv, it, (int) de[0], (int) de[1]);
free (Ev);
}
}
free (zv);
H5Gclose (g);
}
H5Fclose (file);
}
static void readback (const char * filename, const char * vecfile)
{
FILE * fp = fopen ("spectra_hdf5.asc", "w");
const char * scalars[2] = {"a", "b"};
const char * comps[3] = {"v.x", "v.y", "v.z"};
dump_file (fp, filename, scalars, 2, true);
// the vector file goes through the same writer; check that it is there
dump_file (fp, vecfile, comps, 3, false);
fclose (fp);
}
#endif // HAVE_HDF5
int main()
{
L0 = 2.*pi;
X0 = Y0 = Z0 = -L0/2.;
int m = 1 << ML;
init_grid (m);
scalar a[], b[];
vector v[];
// three times, with a widening zone so the heights move between blocks,
// and a different plane count each time
for (int step = 0; step < NT; step++) {
t = 0.1*step;
double Lz = 0.5 + 0.25*step;
foreach() {
a[] = (1. + z)*cos ((5. + step)*x); // bin 5 + step
b[] = 3. + step + z; // bin 0
v.x[] = a[], v.y[] = b[], v.z[] = cos (3.*y);
}
spectrum_scalar_stack ({a, b}, "spec.h5", -Lz, Lz, NZS[step], m,
X0, X0 + L0, Y0, Y0 + L0, "a", SPECTRA_HDF5);
spectrum_vector_stack (v, "spec_u.h5", -Lz, Lz, NZS[step], m,
X0, X0 + L0, Y0, Y0 + L0, "a", SPECTRA_HDF5);
}
#ifdef HAVE_HDF5
if (pid() == 0) {
readback ("spec.h5", "spec_u.h5");
system ("python3 ../test_spectra.py hdf5 spectra_hdf5.asc 1>&2");
}
#else
if (pid() == 0)
fprintf (stderr, "HDF5 not available\n");
#endif
}