1 /* Copyright (c) 1999-2017 Massachusetts Institute of Technology
2  *
3  * [ IMPORTANT: Note that the following is DIFFERENT from the license
4  *   for the rest of the h5utils package. ]
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21 
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include <unistd.h>
27 
28 #include "config.h"
29 #include "arrayh5.h"
30 #include "h5utils.h"
31 
32 /* Vis5d header files: */
33 #if defined(HAVE_VIS5Dp_V5D_H)
34 #  include <vis5d+/v5d.h>
35 #elif defined(HAVE_VIS5D_V5D_H)
36 #  include <vis5d/v5d.h>
37 #else
38 #  include <v5d.h>
39 #endif
40 
41 #define CHECK(cond, msg) { if (!(cond)) { fprintf(stderr, "h5tov5d error: %s\n", msg); exit(EXIT_FAILURE); } }
42 
usage(FILE * f)43 void usage(FILE *f)
44 {
45      fprintf(f, "Usage: h5tov5d [options] [<filenames>]\n"
46 	     "Options:\n"
47 	     "         -h : this help message\n"
48              "         -V : print version number and copyright\n"
49 	     "         -v : verbose output\n"
50 	     "         -T : transposed output dimensions\n"
51 	     "    -x <ix> : take x=<ix> slice of data\n"
52 	     "    -y <iy> : take y=<iy> slice of data\n"
53 	     "    -z <iz> : take z=<iz> slice of data\n"
54 	     "    -t <it> : take t=<it> slice of data's last dimension\n"
55 	     "         -0 : use dataset center as origin for -x/-y/-z\n"
56 	     "  -o <file> : output datasets from all input files to <file>\n"
57 "   -1,-2,-4 : number of bytes per data point to use in output (default: 1)\n"
58 	     "              (fewer bytes is faster, but has less resolution)\n"
59 	     "  -d <name> : use dataset <name> in the input files (default: first dataset)\n"
60 	     "              -- you can also specify a dataset via <filename>:<name>\n"
61 	  );
62 }
63 
64 /* The following routine was adapted from convert/foo2_to_v5d.c from
65    Vis5D 4.2, which is Copyright (C) 1990-1997 Bill Hibbard, Johan
66    Kellum, Brian Paul, Dave Santek, and Andre Battaiola, and is
67    distributed under the GNU General Public License. */
output_v5d(char * v5d_fname,char * data_label,int nslicedim,const int * slicedim,const int * islice,const int * center_slice,int store_bytes,int transpose,char ** h5_fnames,int num_h5,int join)68 void output_v5d(char *v5d_fname, char *data_label,
69 		int nslicedim, const int *slicedim,
70 		const int *islice, const int *center_slice,
71 		int store_bytes, int transpose,
72 		char **h5_fnames, int num_h5, int join)
73 {
74      char *data_name;
75      char *fname;
76      arrayh5 a;
77      int it, iv, firstdim, ifile;
78      float *g = 0;
79 
80      /** Parameters to v5dCreate: */
81      int NumTimes;                      /* number of time steps */
82      int NumVars;                       /* number of variables */
83      int Nr, Nc, Nl[MAXVARS];           /* size of 3-D grids */
84      char VarName[MAXVARS][10];         /* names of variables */
85      int TimeStamp[MAXTIMES];           /* real times for each time step */
86      int DateStamp[MAXTIMES];           /* real dates for each time step */
87      int CompressMode;                  /* number of bytes per grid */
88      int Projection;                    /* a projection number */
89      float ProjArgs[100];               /* the projection parameters */
90      int Vertical;                      /* a vertical coord system number */
91      float VertArgs[MAXLEVELS];         /* the vertical coord sys parameters */
92 
93      if (num_h5 <= 0)
94 	  return;
95 
96      for (ifile = 0; ifile < num_h5; ++ifile) {
97 	  int err;
98 	  fname = split_fname(h5_fnames[ifile], &data_name);
99 	  if (!data_name[0]) data_name = data_label;
100 	  err = arrayh5_read(&a, fname, data_name, NULL,
101 			     nslicedim, slicedim, islice, center_slice);
102 	  free(fname);
103 	  CHECK(!err, arrayh5_read_strerror[err]);
104 	  CHECK(a.rank >= 1, "data must have at least one dimension");
105 	  CHECK(a.rank <= 5, "data cannot have more than 5 dimensions");
106 
107 
108 	  /* if the data is 4 dimensional, express that by using
109 	     different times and/or variables */
110 	  NumTimes = a.rank < 4 ? 1 : a.dims[a.rank - 1];
111 	  CHECK(NumTimes <= MAXTIMES, "too many time steps");
112 	  firstdim = a.rank <= 4 ? 0 : a.rank - 4;
113 
114 	  /* If the data is 5 dimensional, express that by using different
115 	     variables.  Alternatively, if we are joining, the different
116 	     variables are the different files; in that case, the data
117 	     cannot be 5d. */
118 	  NumVars = a.rank < 5 ? 1 : a.dims[0];
119 	  if (join) {
120 	       CHECK(NumVars == 1, "cannot join 5d datasets");
121 	       NumVars = num_h5;
122 	  }
123 	  CHECK(NumVars <= MAXVARS, "too many vars");
124 
125 	  if (!transpose) {
126 	       /* we will need to transpose the data, since HDF5 gives
127 		  us the data in row-major order, while Vis5D expects
128 		  it in column-major order (we could avoid physically
129 		  transposing the data by passing Vis5d transposed
130 		  dimensions, but that seems ugly). */
131 	       Nr = firstdim >= a.rank ? 1 : a.dims[firstdim];
132 	       Nc = firstdim+1 >= a.rank ? 1 : a.dims[firstdim+1];
133 	       Nl[0] = firstdim+2 >= a.rank ? 1 : a.dims[firstdim+2];
134 	  }
135 	  else {
136 	       Nr = firstdim+2 >= a.rank ? 1 : a.dims[firstdim+2];
137 	       Nc = firstdim+1 >= a.rank ? 1 : a.dims[firstdim+1];
138 	       Nl[0] = firstdim >= a.rank ? 1 : a.dims[firstdim];
139 	  }
140 
141 	  if (!v5d_fname) {
142 	       fname = split_fname(h5_fnames[ifile], &data_name);
143 	       v5d_fname = replace_suffix(fname, ".h5", ".v5d");
144 	       free(fname);
145 	  }
146 
147 	  if (join && ifile == 0) {
148 	       arrayh5_destroy(a); /* destroy while we check other datasets */
149 
150 	       /* loop to assign VarName[] and Nl[] arrays: */
151 	       for (iv = 0; iv < NumVars; ++iv) {
152 		    char *name;
153 		    int numTimes, nr, nc, fdim;
154 
155 		    fname = split_fname(h5_fnames[iv], &data_name);
156 		    name =  replace_suffix(fname, ".h5",
157 					   data_name[0] ? data_name - 1 : "");
158 		    if (!data_name[0]) data_name = data_label;
159 
160 		    for (it = 0; it < 9 && name[it]; ++it)
161 			 VarName[iv][it] = name[it];
162 		    VarName[iv][it] = 0;
163 		    free(name);
164 
165 		    /* we don't really have to read the whole array;
166 		       if we called HDF5 routines directly, we could just
167 		       get the dimensions...oh well */
168 		    err = arrayh5_read(&a, fname, data_name, NULL,
169 				       nslicedim, slicedim,
170 				       islice, center_slice);
171 		    CHECK(!err, arrayh5_read_strerror[err]);
172 		    free(fname);
173 
174 		    numTimes = a.rank < 4 ? 1 : a.dims[a.rank - 1];
175 		    fdim = a.rank <= 4 ? 0 : a.rank - 4;
176 		    if (!transpose) {
177 			 nr = fdim >= a.rank ? 1 : a.dims[fdim];
178 			 nc = fdim+1 >= a.rank ? 1 : a.dims[fdim+1];
179 			 Nl[iv] = fdim+2 >= a.rank ? 1 : a.dims[fdim+2];
180 		    }
181 		    else {
182 			 nr = fdim+2 >= a.rank ? 1 : a.dims[fdim+2];
183 			 nc = fdim+1 >= a.rank ? 1 : a.dims[fdim+1];
184 			 Nl[iv] = fdim >= a.rank ? 1 : a.dims[fdim];
185 		    }
186 		    CHECK(numTimes == NumTimes && nr == Nr && nc == Nc,
187 			  "datasets to be joined must have same dimensions");
188 
189 		    arrayh5_destroy(a);
190 	       }
191 
192 	       /* read first array back in */
193 	       fname = split_fname(h5_fnames[ifile], &data_name);
194 	       if (!data_name[0]) data_name = data_label;
195 	       err = arrayh5_read(&a, fname, data_name, NULL,
196 				  nslicedim, slicedim, islice, center_slice);
197 	       CHECK(!err, arrayh5_read_strerror[err]);
198 	       free(fname);
199 	  }
200 	  else if (!join) {
201 	       if (data_label) {
202 		    for (it = 0; it < 9 && data_label[it]; ++it)
203 			 VarName[0][it] = data_label[it];
204 		    VarName[0][it] = 0;
205 	       }
206 	       else {  /* use file name, minus ".v5d" suffix, for var name */
207 		    int suff = strlen(v5d_fname) - 4;
208 		    if (suff < 0 || strcmp(v5d_fname + suff, ".v5d"))
209 			 suff += 4;  /* no ".v5d"; suff = end of string */
210 
211 		    for (it = 0; it < 9 && it < suff; ++it)
212 			 VarName[0][it] = v5d_fname[it];
213 		    VarName[0][it] = 0;
214 	       }
215 
216 	       for (iv = 1; iv < NumVars; ++iv) {
217 		    Nl[iv] = Nl[0];  /* all variables have the same dims */
218 		    if (iv <= 999999999) /* paranoia: ensure sprintf is safe */
219 			 sprintf(VarName[iv], "%d", iv);
220 		    else
221 			 strcpy(VarName[iv], "Infinity");
222 	       }
223 	  }
224 
225 	  for (it = 0; it < NumTimes; ++it) {
226 	       TimeStamp[it] = it;
227 	       DateStamp[it] = 0; /* don't bother to make up a real date */
228 	  }
229 
230 	  CompressMode = store_bytes;
231 	  CHECK(CompressMode == 1 || CompressMode == 2 || CompressMode == 4,
232 		"can only store v5d data as 1, 2, or 4 bytes!");
233 
234 	  Projection = 0;  /* linear, rectangular, generic units */
235 	  ProjArgs[0] = ProjArgs[1] = 0; /* origin of row/col coord system */
236 	  ProjArgs[2] = ProjArgs[3] = 1; /* coord increment between rows/cols*/
237 
238 	  Vertical = 0;  /* equally spaced levels in generic units */
239 	  VertArgs[0] = 0.0;  /* position of bottom level */
240 	  VertArgs[1] = 1.0;  /* spacing between levels */
241 
242 	  /* use v5dCreate call to create the v5d file and write the header */
243 	  if (!join || ifile == 0) {
244 	       CHECK(v5dCreate(v5d_fname, NumTimes, NumVars, Nr, Nc, Nl,
245 			       VarName, TimeStamp, DateStamp, CompressMode,
246 			       Projection, ProjArgs, Vertical, VertArgs),
247 		     "couldn't create v5d file");
248 	  }
249 
250 	  /* may call v5dSetLowLev() or v5dSetUnits() here; see Vis5d README */
251 
252 	  /* allocate array for copying transpose of data: */
253 	  g = (float *) malloc(sizeof(float) * Nr * Nc * Nl[0]);
254 	  CHECK(g, "out of memory!");
255 
256 	  for (iv = join ? ifile : 0; iv < (join ? ifile + 1 : NumVars); ++iv)
257 	       for (it = 0; it < NumTimes; ++it) {
258 		    double *d = a.data + it +
259 			 (join ? 0 : iv) * NumTimes * Nr * Nc * Nl[0];
260 
261 		    if (!transpose) {
262 			 int ir, ic, il;
263 			 for (ir = 0; ir < Nr; ++ir)
264 			      for (ic = 0; ic < Nc; ++ic)
265 				   for (il = 0; il < Nl[0]; ++il)
266 					g[ir + Nr * (ic + Nc * il)] =
267 					     d[(il + Nl[0] * (ic + Nc * ir))
268 					       * NumTimes];
269 		    }
270 		    else {
271 			 int id;
272 			 for (id = 0; id < Nr * Nc * Nl[0]; ++id)
273 			      g[id] = d[id * NumTimes];
274 		    }
275 		    CHECK(v5dWrite(it + 1, iv + 1, g),
276 			  "error writing v5d output");
277 	       }
278 
279 	  free(g);
280 
281 	  if (!join || ifile == num_h5 - 1)
282 	       v5dClose();
283 
284 	  arrayh5_destroy(a);
285 	  if (v5d_fname)
286 	       free(v5d_fname);
287 	  v5d_fname = NULL;
288      }
289 }
290 
main(int argc,char ** argv)291 int main(int argc, char **argv)
292 {
293      char *v5d_fname = NULL, *data_name = NULL;
294      extern char *optarg;
295      extern int optind;
296      int c;
297      int verbose = 0, transpose = 0;
298      int slicedim[4] = {NO_SLICE_DIM,NO_SLICE_DIM,NO_SLICE_DIM,NO_SLICE_DIM};
299      int islice[4], center_slice[4] = {0,0,0,0};
300      int store_bytes = 1;
301 
302      while ((c = getopt(argc, argv, "ho:d:vTV124x:y:z:t:0")) != -1)
303 	  switch (c) {
304 	      case 'h':
305 		   usage(stdout);
306 		   return EXIT_SUCCESS;
307 	      case 'V':
308 		   printf("h5tov5d " PACKAGE_VERSION " by Steven G. Johnson\n"
309        "Copyright (c) 1999-2017 Massachusetts Institute of Technology\n"
310        "\n"
311        "This program is free software; you can redistribute it and/or modify\n"
312        "it under the terms of the GNU General Public License as published by\n"
313        "the Free Software Foundation; either version 2 of the License, or\n"
314        "(at your option) any later version.\n"
315        "\n"
316        "This program is distributed in the hope that it will be useful,\n"
317        "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
318        "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
319        "GNU General Public License for more details.\n"
320 			);
321 		   return EXIT_SUCCESS;
322 	      case 'v':
323 		   verbose = 1;
324 		   break;
325 	      case 'T':
326 		   transpose = 1;
327 		   break;
328 	      case 'x':
329 		   islice[0] = atoi(optarg);
330 		   slicedim[0] = 0;
331 		   break;
332 	      case 'y':
333 		   islice[1] = atoi(optarg);
334 		   slicedim[1] = 1;
335 		   break;
336 	      case 'z':
337 		   islice[2] = atoi(optarg);
338 		   slicedim[2] = 2;
339 		   break;
340 	      case 't':
341 		   islice[3] = atoi(optarg);
342 		   slicedim[3] = LAST_SLICE_DIM;
343 		   break;
344               case '0':
345                    center_slice[0] = center_slice[1] = center_slice[2] = 1;
346                    break;
347 	      case '1':
348 		   store_bytes = 1;
349 		   break;
350 	      case '2':
351 		   store_bytes = 2;
352 		   break;
353 	      case '4':
354 		   store_bytes = 4;
355 		   break;
356 	      case 'o':
357 		   v5d_fname = my_strdup(optarg);
358 		   break;
359 	      case 'd':
360 		   data_name = my_strdup(optarg);
361 		   break;
362 	      default:
363 		   fprintf(stderr, "Invalid argument -%c\n", c);
364 		   usage(stderr);
365 		   return EXIT_FAILURE;
366 	  }
367      if (optind == argc) {  /* no parameters left */
368 	  usage(stderr);
369 	  return EXIT_FAILURE;
370      }
371 
372      output_v5d(v5d_fname, data_name,
373 		4, slicedim, islice, center_slice,
374 		store_bytes, transpose,
375 		argv + optind, argc - optind, v5d_fname != NULL);
376 
377      if (data_name)
378 	  free(data_name);
379 
380      return EXIT_SUCCESS;
381 }
382