Canoe
Comprehensive Atmosphere N' Ocean Engine
mppnccombine.cpp
Go to the documentation of this file.
1 // Modified by Cheng Li
2 // change sprintf -> snprintf
3 /*
4  mppnccombine - joins together netCDF data files representing a decomposed
5  domain into a unified netCDF file. It was originally
6  designed to be used as a postprocessor for the parallel I/O
7  programming interface "mpp_io_mod"
8  (http://www.gfdl.noaa.gov/~vb/mpp_io.html) by V. Balaji.
9 
10  V2.1.7: Added option to initialize output variables with a missing_value
11  from the variables of the first input file as suggested by
12  Martin Schmidt (martin.schmidt@io-warnemuende.de) and
13  Franz Tauber (franz.tauber@io-warnemuende.de).
14  V2.1.6: Bug fixes for greater than 4GB record sizes. Does not contain
15  V2.1.5 modifications which were a special case.
16  V2.1.5: Supports running in an MPI environment. Reads arguments from a
17  configuration file instead of from the command line; this is needed
18  to work around a bug in Cray's aprun.
19  V2.1.4: Fixed a bug with file cleanup and the debugging messages.
20  V2.1.3: Fixed a run-time segmentation fault with some compilers; changed
21  ncinfile allocation in process_file function.
22  V2.1.2: Fixed a bug with input files that have decomposed dimensions
23  defined after the variables that use them.
24  V2.1.1: Added option (-64) for creating output files with 64-bit offset
25  format requiring NetCDF 3.6.x.
26  V2.1: Added an option (-h) to pad the output file's header with empty space.
27  Added an option (-e #) to specify an ending number to a range of input
28  filename extensions. It no longer aborts on missing input files, but
29  gives error messages at the end of all the processing.
30  V2.0: Substantial rewrite; memory buffering increases speed several times.
31  V1.2: Added support for specifying the start number in filename extensions.
32  V1.1.1: Added a fix for dimensions that are not also variables.
33  V1.1: Changed loop order for increased I/O efficiency; records are now the
34  innermost loop then the variables loop.
35  V1.0: Original release.
36 
37  Written by Hans Vahlenkamp (Hans.Vahlenkamp@noaa.gov)
38  Geophysical Fluid Dynamics Laboratory / NOAA
39  Princeton Forrestal Campus
40  Last Updated: 05/15/08
41 */
42 
43 // C/C++
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <sys/stat.h>
48 #include <sys/types.h>
49 #include <unistd.h>
50 
51 // canoe
52 #include <configure.hpp>
53 
54 // Only proceed if NETCDF output enabled
55 #ifdef NETCDFOUTPUT
56 
57 extern "C" {
58 #include <netcdf.h>
59 }
60 
61 /* Information structure for a file */
62 struct fileinfo {
63  int ncfid; /* ID of the input netCDF file */
64  int ndims; /* Number of dimensions */
65  int nvars; /* Number of variables */
66  int ngatts; /* Number of global attributes */
67  int recdim; /* ID of the record dimensions */
68  char varname[MAX_NC_VARS][MAX_NC_NAME]; /* Names of the variables */
69  nc_type datatype[MAX_NC_VARS]; /* Data types of the variables */
70  int varndims[MAX_NC_VARS]; /* Number of dimensions for each variable */
71  int vardim[MAX_NC_VARS][MAX_NC_DIMS]; /* Dimensions for each variable */
72  int natts[MAX_NC_VARS]; /* Number of attributes for each variable */
73  unsigned char vardecomp[MAX_NC_VARS]; /* Is the variable decomposed */
74  char dimname[MAX_NC_DIMS][MAX_NC_NAME]; /* Names of the dimensions */
75  long dimsize[MAX_NC_DIMS]; /* Sizes of the dimensions (decomposed) */
76  long dimfullsize[MAX_NC_DIMS]; /* Full sizes of the dimensions */
77  long dimstart[MAX_NC_DIMS]; /* Start positions within full dimensions */
78  long dimend[MAX_NC_DIMS]; /* End positions within full dimensions */
79  unsigned char varmiss[MAX_NC_VARS]; /* Does variable have missing_value */
80  unsigned char varmissval[MAX_NC_VARS][8]; /* missing_value per variable */
81 };
82 
83 /* Auxiliary function prototypes */
84 void usage();
85 int process_file(char *, unsigned char, struct fileinfo *, char *, int *, int *,
86  int, int, void *[], int, unsigned char, unsigned char);
87 int process_vars(struct fileinfo *, struct fileinfo *, unsigned char, int *,
88  int, int, int, void *[], unsigned char, unsigned char);
89 int flush_decomp(struct fileinfo *, int, int, void *[], unsigned char);
90 void print_debug(struct fileinfo *, unsigned char);
91 char *nc_type_to_str(nc_type);
92 
93 static unsigned char _first_; /* First time reading variables? */
94 int mppnccombine(int argc, char *argv[]) {
95  _first_ = 1;
96  unsigned char verbose = 0; /* Print some progress information? */
97  unsigned char appendnc = 0; /* Append to an existing netCDF file? */
98  unsigned char removein = 0; /* Remove the ".####" decomposed input files? */
99  int nstart = 0; /* PE number of the first input netCDF file */
100  int nend = (-1); /* PE number of the last input netCDF file */
101  int headerpad = 16384; /* Additional padding at the end of the header */
102  int format = NC_NOCLOBBER; /* Format of new netCDF output file */
103  unsigned char missing = 0; /* Initialize output variables with */
104  /* "missing_value" instead of 0 value? */
105  int outputarg = (-1); /* Argument # of the output netCDF file */
106  int inputarg = (-1); /* Argument # of first input netCDF file */
107  struct stat statbuf; /* Dummy structure for file-testing "stat" call */
108  struct fileinfo *ncoutfile; /* Information for the output file */
109  char outfilename[2048], *strptr; /* Name of the output netCDF file */
110  int outlen; /* Length of the output filename */
111  char infilename[2048]; /* Name of an input file */
112  unsigned char infileerror = 0; /* Errors reading an input file */
113  unsigned char infileerrors = 0; /* Errors reading any input files */
114  int nfiles = (-1); /* Number of files in the decomposed domain */
115  int a, f, r; /* Loop variables */
116  int nrecs = 1; /* Number of records in each decomposed file */
117  void *varbuf[NC_MAX_VARS]; /* Buffers for decomposed variables */
118 
119  /* Check the command-line arguments */
120  if (argc < 2) {
121  usage();
122  return (1);
123  }
124  for (a = 1; a < argc; a++) {
125  if (!strcmp(argv[a], "-v"))
126  verbose = 1;
127  else if (!strcmp(argv[a], "-vv"))
128  verbose = 2; /* Hidden debug mode */
129  else if (!strcmp(argv[a], "-a"))
130  appendnc = 1;
131  else if (!strcmp(argv[a], "-r"))
132  removein = 1;
133  else if (!strcmp(argv[a], "-n")) {
134  a++;
135  if (a < argc)
136  nstart = atoi(argv[a]);
137  else {
138  usage();
139  return (1);
140  }
141  } else if (!strcmp(argv[a], "-e")) {
142  a++;
143  if (a < argc)
144  nend = atoi(argv[a]);
145  else {
146  usage();
147  return (1);
148  }
149  } else if (!strcmp(argv[a], "-h")) {
150  a++;
151  if (a < argc)
152  headerpad = atoi(argv[a]);
153  else {
154  usage();
155  return (1);
156  }
157  } else if (!strcmp(argv[a], "-64"))
158  format = (NC_NOCLOBBER | NC_64BIT_OFFSET);
159  else if (!strcmp(argv[a], "-n4"))
160  format = (NC_NOCLOBBER | NC_NETCDF4 | NC_CLASSIC_MODEL);
161  else if (!strcmp(argv[a], "-m"))
162  missing = 1;
163  else {
164  outputarg = a;
165  break;
166  }
167  }
168  if (outputarg == (-1)) {
169  usage();
170  return (1);
171  }
172  if (argc - 1 > outputarg) inputarg = outputarg + 1;
173  snprintf(outfilename, sizeof(outfilename), "%s", argv[outputarg]);
174  outlen = strlen(outfilename);
175  if (outlen > 4) {
176  strptr = outfilename + outlen - 5;
177  if (!strcmp(strptr, ".0000")) outfilename[outlen - 5] = '\0';
178  }
179 
180  /* Disable fatal returns from netCDF library functions */
181  ncopts = 0;
182 
183  /* Create a new netCDF output file */
184  if ((ncoutfile = (struct fileinfo *)malloc(sizeof(struct fileinfo))) ==
185  NULL) {
186  fprintf(stderr, "Error: cannot allocate enough memory!\n");
187  return (1);
188  }
189  if (!appendnc) {
190  if (stat(outfilename, &statbuf) == 0) {
191  fprintf(stderr, "Error: output file seems to exist already!\n");
192  free(ncoutfile);
193  return (1);
194  }
195  if ((ncoutfile->ncfid = nccreate(outfilename, NC_NETCDF4)) == (-1)) {
196  fprintf(stderr, "Error: cannot create the output netCDF file!\n");
197  free(ncoutfile);
198  return (1);
199  }
200  ncsetfill(ncoutfile->ncfid, NC_NOFILL);
201  }
202  /* Open an existing netCDF file for appending */
203  else {
204  if ((ncoutfile->ncfid = ncopen(outfilename, NC_WRITE)) == (-1)) {
205  fprintf(stderr,
206  "Error: cannot open the output netCDF file for appending!\n");
207  free(ncoutfile);
208  return (1);
209  }
210  }
211 
212  for (f = 0; f < NC_MAX_VARS; f++) varbuf[f] = NULL;
213 
214  /* No input files are specified on the command-line */
215  if (inputarg == (-1)) {
216  if (nend > -1)
217  for (r = 0; r < nrecs; r++) {
218  if (verbose) printf("record = %d\n", r);
219  f = 0;
220  for (a = nstart; a <= nend; a++) {
221  snprintf(infilename, sizeof(infilename), "%s.%04d", outfilename, a);
222  if (verbose) {
223  if (a == nstart && r == 0)
224  printf(" n files to go... ");
225  else
226  printf(" %d files to go... ", nend - nstart + 1 - f);
227  printf("processing \"%s\"\n", infilename);
228  }
229  if (stat(infilename, &statbuf) != 0) continue;
230  infileerror = process_file(infilename, appendnc, ncoutfile,
231  outfilename, &nfiles, &nrecs, r, f, varbuf,
232  headerpad, verbose, missing);
233  if (infileerror) infileerrors = 1;
234  appendnc = 1;
235  f++;
236  if (f == nfiles || a == nend) {
237  if (verbose > 1)
238  printf(" Write variables from previous %d files\n", f);
239  flush_decomp(ncoutfile, nfiles, r, varbuf, verbose);
240  break;
241  }
242  }
243  }
244  else {
245  nend = nstart + 1;
246  for (r = 0; r < nrecs; r++) {
247  if (verbose) printf("record = %d\n", r);
248  f = 0;
249  for (a = nstart; a < nend; a++) {
250  snprintf(infilename, sizeof(infilename), "%s.%04d", outfilename, a);
251  if (verbose) {
252  if (a == nstart && r == 0)
253  printf(" n files to go... ");
254  else
255  printf(" %d files to go... ", nend - a);
256  printf("processing \"%s\"\n", infilename);
257  }
258  infileerror = process_file(infilename, appendnc, ncoutfile,
259  outfilename, &nfiles, &nrecs, r, f, varbuf,
260  headerpad, verbose, missing);
261  if (infileerror) infileerrors = 1;
262  if (a == nstart && nfiles > 0) nend = nstart + nfiles;
263  appendnc = 1;
264  f++;
265  if (f == nfiles || a == (nend - 1)) {
266  if (verbose > 1)
267  printf(" Write variables from previous %d files\n", f);
268  flush_decomp(ncoutfile, nfiles, r, varbuf, verbose);
269  f = 0;
270  continue;
271  }
272  }
273  }
274  }
275  }
276  /* Loop over all the specified input files */
277  else
278  for (r = 0; r < nrecs; r++) {
279  if (verbose) printf("record = %d\n", r);
280  f = 0;
281  for (a = inputarg; a < argc; a++) {
282  if (verbose) {
283  if ((argc - a) == 1)
284  printf(" 1 file to go... ");
285  else
286  printf(" %d files to go... ", argc - a);
287  printf("processing \"%s\"\n", argv[a]);
288  }
289  infileerror =
290  process_file(argv[a], appendnc, ncoutfile, outfilename, &nfiles,
291  &nrecs, r, f, varbuf, headerpad, verbose, missing);
292  if (infileerror) infileerrors = 1;
293  appendnc = 1;
294  f++;
295  if (f == nfiles || a == (argc - 1)) {
296  if (verbose > 1)
297  printf(" Write variables from previous %d files\n", f);
298  flush_decomp(ncoutfile, nfiles, r, varbuf, verbose);
299  f = 0;
300  continue;
301  }
302  }
303  }
304 
305  /* Clean up... return 1 on error, otherwise 0 */
306  for (f = 0; f < NC_MAX_VARS; f++) {
307  if (varbuf[f] != NULL) free(varbuf[f]);
308  }
309  ncclose(ncoutfile->ncfid);
310  free(ncoutfile);
311  if (!infileerrors) {
312  if (removein) {
313  /* No input files are specified on the command-line */
314  if (inputarg == (-1)) {
315  f = 0;
316  for (a = nstart; a <= nend; a++) {
317  if (++f > nfiles) break;
318  snprintf(infilename, sizeof(infilename), "%s.%04d", outfilename, a);
319  if (verbose) printf("Removing \"%s\"\n", infilename);
320  unlink(infilename);
321  }
322  }
323  /* Loop over all the specified input files */
324  else
325  for (a = inputarg; a < argc; a++) {
326  if (verbose) printf("Removing \"%s\"\n", argv[a]);
327  unlink(argv[a]);
328  }
329  }
330  } else
331  fprintf(stderr, "Warning: output file may be incomplete!\n");
332  return (infileerrors);
333 }
334 
335 /* Print the usage message for mppnccombine */
336 void usage() {
337  printf("mppnccombine 2.1.7 - (written by Hans.Vahlenkamp@noaa.gov)\n\n");
338  printf(
339  "Usage: mppnccombine [-v] [-a] [-r] [-n #] [-e #] [-h #] [-64] [-m]\n");
340  printf(" output.nc [input ...]\n\n");
341  printf(" -v Print some progress information.\n");
342  printf(
343  " -a Append to an existing netCDF file (not heavily tested...).\n");
344  printf(
345  " -r Remove the \".####\" decomposed files after a successful "
346  "run.\n");
347  printf(
348  " -n # Input filename extensions start with number #### instead of "
349  "0000.\n");
350  printf(
351  " -e # Ending number #### of a specified range of input filename "
352  "extensions.\n");
353  printf(
354  " Files within the range do not have to be consecutively "
355  "numbered.\n");
356  printf(
357  " -h # Add a specified number of bytes of padding at the end of the "
358  "header.\n");
359  printf(" -64 Create netCDF output files with the 64-bit offset format.\n");
360  printf(
361  " -m Initialize output variables with a \"missing_value\" from the "
362  "variables\n");
363  printf(" of the first input file instead of the default 0 value.\n\n");
364  printf(
365  "mppnccombine joins together an arbitrary number of netCDF input files, "
366  "each\n");
367  printf(
368  "containing parts of a decomposed domain, into a unified netCDF output "
369  "file.\n");
370  printf(
371  "An output file must be specified and it is assumed to be the first "
372  "filename\n");
373  printf(
374  "argument. If the output file already exists, then it will not be "
375  "modified\n");
376  printf(
377  "unless the option is chosen to append to it. If no input files are "
378  "specified\n");
379  printf(
380  "then their names will be based on the name of the output file plus the "
381  "default\n");
382  printf(
383  "numeric extension \".0000\", which will increment by 1. There is an "
384  "option for\n");
385  printf(
386  "starting the filename extensions with an arbitrary number instead of 0. "
387  " There\n");
388  printf(
389  "is an option for specifying an end to the range of filename extension "
390  "numbers;\n");
391  printf(
392  "files within the range do not have to be consecutively numbered. If "
393  "input\n");
394  printf("files are specified then names will be used verbatim.\n\n");
395  printf(
396  "A value of 0 is returned if execution completed successfully; a value "
397  "of 1\n");
398  printf("otherwise.\n");
399 }
400 
401 /* Open an input file and get some information about it, define the */
402 /* structure of the output file if necessary, prepare to copy all the */
403 /* variables at the current record to memory */
404 int process_file(char *ncname, unsigned char appendnc,
405  struct fileinfo *ncoutfile, char *outncname, int *nfiles,
406  int *nrecs, int r, int f, void *varbuf[], int headerpad,
407  unsigned char verbose, unsigned char missing) {
408  struct fileinfo *ncinfile; /* Information about an input netCDF file */
409  int nfiles2; /* Number of files in the decomposed domain */
410  int d, v, n; /* Loop variables */
411  int dimid; /* ID of a dimension */
412  int decomp[4]; /* "domain_decomposition = #0, #1, #2, #3" attribute */
413  /* #0 starting position of original dimension */
414  /* #1 ending position of original dimension */
415  /* #2 starting position of decomposed dimension */
416  /* #3 ending position of decomposed dimension */
417  char attname[MAX_NC_NAME]; /* Name of a global or variable attribute */
418  unsigned char ncinfileerror = 0; /* Were there any file errors? */
419 
420  /* Information for netCDF input file */
421  if ((ncinfile = (struct fileinfo *)malloc(sizeof(struct fileinfo))) == NULL) {
422  fprintf(stderr, "Error: cannot allocate enough memory!\n");
423  return (1);
424  }
425 
426  /* Open an input netCDF file */
427  if ((ncinfile->ncfid = ncopen(ncname, NC_NOWRITE)) == (-1)) {
428  fprintf(stderr, "Error: cannot open input file \"%s\"\n", ncname);
429  free(ncinfile);
430  return (1);
431  }
432 
433  /* Determine the number of files in the decomposed domain */
434  if (ncattget(ncinfile->ncfid, NC_GLOBAL, "NumFilesInSet", (void *)&nfiles2) ==
435  (-1)) {
436  if (*nfiles == 1) {
437  fprintf(stderr,
438  "Error: missing the \"NumFilesInSet\" global attribute!\n");
439  return (1);
440  } else if (*nfiles == (-1)) {
441  fprintf(stderr,
442  "Warning: missing the \"NumFilesInSet\" global attribute.\n");
443  }
444  }
445  *nfiles = nfiles2;
446 
447  /* Get some general information about the input netCDF file */
448  if (ncinquire(ncinfile->ncfid, &(ncinfile->ndims), &(ncinfile->nvars),
449  &(ncinfile->ngatts), &(ncinfile->recdim)) == (-1)) {
450  fprintf(stderr, "Error: cannot read the file's metadata!\n");
451  ncclose(ncinfile->ncfid);
452  free(ncinfile);
453  return (1);
454  }
455 
456  /* Get some information about the dimensions */
457  for (d = 0; d < ncinfile->ndims; d++) {
458  if ((ncdiminq(ncinfile->ncfid, d, ncinfile->dimname[d],
459  &(ncinfile->dimsize[d]))) == (-1)) {
460  fprintf(stderr, "Error: cannot read dimension #%d's metadata!\n", d);
461  ncclose(ncinfile->ncfid);
462  free(ncinfile);
463  return (1);
464  }
465  ncinfile->dimfullsize[d] = ncinfile->dimsize[d];
466  ncinfile->dimstart[d] = 1;
467  ncinfile->dimend[d] = (-1);
468  }
469 
470  /* Save some information for the output file */
471  if (r == 0) {
472  ncoutfile->nvars = ncinfile->nvars;
473  ncoutfile->recdim = ncinfile->recdim;
474  }
475 
476  /* Get some information about the variables */
477  for (v = 0; v < ncinfile->nvars; v++) {
478  if ((ncvarinq(ncinfile->ncfid, v, ncinfile->varname[v],
479  &(ncinfile->datatype[v]), &(ncinfile->varndims[v]),
480  ncinfile->vardim[v], &(ncinfile->natts[v]))) == (-1)) {
481  fprintf(stderr, "Error: cannot read variable #%d's metadata!\n", v);
482  ncclose(ncinfile->ncfid);
483  free(ncinfile);
484  return (1);
485  }
486 
487  /* If the variable is also a dimension then get decomposition info */
488  if ((dimid = ncdimid(ncinfile->ncfid, ncinfile->varname[v])) != (-1)) {
489  if (ncattget(ncinfile->ncfid, v, "domain_decomposition",
490  (void *)decomp) != (-1)) {
491  ncinfile->dimfullsize[dimid] = decomp[1] - decomp[0] + 1;
492  ncinfile->dimstart[dimid] = decomp[2] - (decomp[0] - 1);
493  ncinfile->dimend[dimid] = decomp[3] - (decomp[0] - 1);
494  } else {
495  ncinfile->dimfullsize[dimid] = ncinfile->dimsize[dimid];
496  ncinfile->dimstart[dimid] = 1;
497  ncinfile->dimend[dimid] = (-1);
498  }
499  }
500  }
501 
502  /* Get some additional information about the variables */
503  for (v = 0; v < ncinfile->nvars; v++) {
504  /* Does the variable have a decomposed dimension? */
505  ncinfile->vardecomp[v] = 0;
506  for (d = 0; d < ncinfile->varndims[v]; d++) {
507  if (ncinfile->dimend[ncinfile->vardim[v][d]] != (-1)) {
508  ncinfile->vardecomp[v] = 1;
509  break;
510  }
511  }
512 
513  /* Save some information for the output file */
514  if (r == 0) {
515  ncoutfile->varndims[v] = ncinfile->varndims[v];
516  for (d = 0; d < ncinfile->ndims; d++)
517  ncoutfile->dimfullsize[d] = ncinfile->dimfullsize[d];
518  for (d = 0; d < ncinfile->varndims[v]; d++)
519  ncoutfile->vardim[v][d] = ncinfile->vardim[v][d];
520  ncoutfile->vardecomp[v] = ncinfile->vardecomp[v];
521  strcpy(ncoutfile->varname[v], ncinfile->varname[v]);
522  ncoutfile->varmiss[v] = 0;
523  }
524  }
525 
526  /* If the output netCDF file was just created then define its structure */
527  if (!appendnc) {
528  if (verbose) printf(" Creating output \"%s\"\n", outncname);
529 
530  /* Define the dimensions */
531  for (d = 0; d < ncinfile->ndims; d++) {
532  if (d == ncinfile->recdim)
533  ncdimdef(ncoutfile->ncfid, ncinfile->dimname[d], NC_UNLIMITED);
534  else
535  ncdimdef(ncoutfile->ncfid, ncinfile->dimname[d],
536  ncinfile->dimfullsize[d]);
537  }
538 
539  /* Define the variables and copy their attributes */
540  for (v = 0; v < ncinfile->nvars; v++) {
541  ncvardef(ncoutfile->ncfid, ncinfile->varname[v], ncinfile->datatype[v],
542  ncinfile->varndims[v], ncinfile->vardim[v]);
543  for (n = 0; n < ncinfile->natts[v]; n++) {
544  ncattname(ncinfile->ncfid, v, n, attname);
545  if (missing) {
546  if (!strcmp(attname, "missing_value")) {
547  ncoutfile->varmiss[v] = 1;
548  ncattget(ncinfile->ncfid, v, "missing_value",
549  (void *)(ncoutfile->varmissval[v]));
550  }
551  }
552  if (!strcmp(attname, "domain_decomposition"))
553  continue;
554  else {
555  if (ncattcopy(ncinfile->ncfid, v, attname, ncoutfile->ncfid, v) ==
556  (-1)) {
557  fprintf(stderr,
558  "Error: cannot copy variable \"%s\"'s attributes!\n",
559  ncinfile->varname[v]);
560  free(ncinfile);
561  return (1);
562  }
563  }
564  }
565  }
566 
567  /* Copy the global attributes */
568  for (n = 0; n < ncinfile->ngatts; n++) {
569  ncattname(ncinfile->ncfid, NC_GLOBAL, n, attname);
570  if (!strcmp(attname, "NumFilesInSet"))
571  continue;
572  else if (!strcmp(attname, "filename"))
573  ncattput(ncoutfile->ncfid, NC_GLOBAL, attname, NC_CHAR,
574  strlen(outncname), (void *)outncname);
575  else {
576  if (ncattcopy(ncinfile->ncfid, NC_GLOBAL, attname, ncoutfile->ncfid,
577  NC_GLOBAL) == (-1)) {
578  fprintf(stderr, "Error: cannot copy the file's global attributes!\n");
579  return (1);
580  }
581  }
582  }
583 
584  /* Definitions done */
585  nc__enddef(ncoutfile->ncfid, headerpad, 4, 0, 4);
586  }
587 
588  /* Copy all data values of the dimensions and variables to memory */
589  ncinfileerror = process_vars(ncinfile, ncoutfile, appendnc, nrecs, r, *nfiles,
590  f, varbuf, verbose, missing);
591 
592  /* Done */
593  ncclose(ncinfile->ncfid);
594  free(ncinfile);
595  return (ncinfileerror);
596 }
597 
598 /* Copy all data values in an input file at the current record to memory */
599 int process_vars(struct fileinfo *ncinfile, struct fileinfo *ncoutfile,
600  unsigned char appendnc, int *nrecs, int r, int nfiles, int f,
601  void *varbuf[], unsigned char verbose, unsigned char missing) {
602  int v, d, i, j, k, l, b, s; /* Loop variables */
603  int dimid; /* ID of a dimension */
604  void *values; /* Current data values */
605  long instart[MAX_NC_DIMS], outstart[MAX_NC_DIMS]; /* Data array sizes */
606  long count[MAX_NC_DIMS]; /* " */
607  long long recsize; /* Decomposed size of one record of a variable */
608  long long recfullsize; /* Non-decomposed size of one record of a variable */
609  int varrecdim; /* Variable's record dimension */
610  int imax, jmax, kmax, lmax;
611  int imaxfull, jmaxfull, kmaxfull, lmaxfull;
612  int imaxjmaxfull, imaxjmaxkmaxfull;
613  int offset, ioffset, joffset, koffset, loffset;
614  long long varbufsize;
615 
616  /* Check the number of records */
617  if (*nrecs == 1)
618  *nrecs = ncinfile->dimsize[ncinfile->recdim];
619  else if (ncinfile->dimsize[ncinfile->recdim] != *nrecs) {
620  fprintf(stderr,
621  "Error: different number of records than the first input file!\n");
622  return (1);
623  }
624 
625  /* Loop over all the variables */
626  for (v = 0; v < ncinfile->nvars; v++) {
627  if (verbose > 1) printf(" variable = %s\n", ncinfile->varname[v]);
628 
629  /* Get read/write dimension sizes for the variable */
630  recsize = 1;
631  recfullsize = 1;
632  varrecdim = (-1);
633  outstart[0] = 0;
634  outstart[1] = 0;
635  outstart[2] = 0;
636  outstart[3] = 0;
637  for (d = 0; d < ncinfile->varndims[v]; d++) {
638  if (ncinfile->vardim[v][d] == ncinfile->recdim) {
639  count[d] = 1;
640  varrecdim = d;
641  } else {
642  count[d] = ncinfile->dimsize[ncinfile->vardim[v][d]];
643  recsize *= count[d];
644  instart[d] = 0;
645  outstart[d] = ncinfile->dimstart[ncinfile->vardim[v][d]] - 1;
646  recfullsize *= ncinfile->dimfullsize[ncinfile->vardim[v][d]];
647  }
648  if (verbose > 1)
649  printf(" dim %d: instart=%ld outstart=%ld count=%ld\n", d,
650  instart[d], outstart[d], count[d]);
651  }
652 
653  /* Prevent unnecessary reads/writes */
654  if (r > 0) {
655  /* Prevent unnecessary reads/writes of the dimensions */
656  if ((dimid = ncdimid(ncinfile->ncfid, ncinfile->varname[v])) != (-1)) {
657  if (ncinfile->recdim == dimid) {
658  if (f != 0) continue;
659  } else
660  continue;
661  }
662  /* Prevent unnecessary reads/writes of the variables */
663  else {
664  /* Prevent unnecessary reads/writes of non-decomposed variables
665  if (ncinfile->vardecomp[v]!=1 && appendnc) continue; */
666 
667  /* Non-record variables */
668  if (varrecdim == (-1)) continue;
669 
670  /* Non-decomposed record variables */
671  if (ncinfile->vardecomp[v] != 1 && f > 0) continue;
672  }
673  } else {
674  if (ncinfile->vardecomp[v] != 1 && appendnc) continue;
675  }
676 
677  /* Allocate a buffer for the variable's record */
678  if ((values = malloc(nctypelen(ncinfile->datatype[v]) * recsize)) == NULL) {
679  fprintf(stderr,
680  "Error: cannot allocate %lld bytes for decomposed variable "
681  "\"%s\"'s values!\n",
682  nctypelen(ncinfile->datatype[v]) * recsize, ncinfile->varname[v]);
683  return (1);
684  }
685 
686  /* Read the variable */
687  if (varrecdim != (-1)) instart[varrecdim] = outstart[varrecdim] = r;
688  if (ncvarget(ncinfile->ncfid, v, instart, count, values) == (-1)) {
689  fprintf(stderr, "Error: cannot read variable \"%s\"'s values!\n",
690  ncinfile->varname[v]);
691  return (1);
692  }
693 
694  /* Write the buffered variable immediately if it's not decomposed */
695  if (ncinfile->vardecomp[v] != 1) {
696  if (verbose > 1)
697  printf(" writing %lld bytes to file\n",
698  nctypelen(ncinfile->datatype[v]) * recsize);
699  if (ncvarput(ncoutfile->ncfid, v, outstart, count, values) == (-1)) {
700  fprintf(stderr, "Error: cannot write variable \"%s\"'s values!\n",
701  ncinfile->varname[v]);
702  return (1);
703  }
704  }
705  /* Save the buffer */
706  else {
707  /* Allocate a buffer for the variable's non-decomposed record size */
708  if (_first_) {
709  varbufsize = nctypelen(ncinfile->datatype[v]) * recfullsize;
710  if (verbose > 1)
711  printf(" allocating %lld bytes for full domain\n", varbufsize);
712  if ((varbuf[v] = calloc(varbufsize, 1)) == NULL) {
713  fprintf(stderr,
714  "Error: cannot allocate %lld bytes for entire variable "
715  "\"%s\"'s values!\n",
716  varbufsize, ncinfile->varname[v]);
717  return (1);
718  }
719  if (missing && ncoutfile->varmiss[v]) switch (ncinfile->datatype[v]) {
720  case NC_BYTE:
721  case NC_CHAR:
722  for (s = 0; s < recfullsize; s++)
723  *((unsigned char *)(varbuf[v]) + s) =
724  *((unsigned char *)(ncoutfile->varmissval[v]));
725  break;
726  case NC_SHORT:
727  for (s = 0; s < recfullsize; s++)
728  *((short *)(varbuf[v]) + s) =
729  *((short *)(ncoutfile->varmissval[v]));
730  break;
731  case NC_INT:
732  for (s = 0; s < recfullsize; s++)
733  *((int *)(varbuf[v]) + s) =
734  *((int *)(ncoutfile->varmissval[v]));
735  break;
736  case NC_FLOAT:
737  for (s = 0; s < recfullsize; s++)
738  *((float *)(varbuf[v]) + s) =
739  *((float *)(ncoutfile->varmissval[v]));
740  break;
741  case NC_DOUBLE:
742  for (s = 0; s < recfullsize; s++)
743  *((double *)(varbuf[v]) + s) =
744  *((double *)(ncoutfile->varmissval[v]));
745  break;
746  }
747  }
748  if (varbuf[v] == NULL) {
749  fprintf(stderr, "Internal memory usage error!\n");
750  return (1);
751  }
752  if (verbose > 1)
753  printf(" writing %lld bytes to memory\n",
754  nctypelen(ncinfile->datatype[v]) * recsize);
755 
756  imax = ncinfile->dimsize[ncinfile->vardim[v][ncinfile->varndims[v] - 1]];
757  if (ncinfile->varndims[v] > 1) {
758  dimid = ncinfile->vardim[v][ncinfile->varndims[v] - 2];
759  if (dimid == ncinfile->recdim)
760  jmax = 1;
761  else
762  jmax = ncinfile->dimsize[dimid];
763  } else
764  jmax = 1;
765  if (ncinfile->varndims[v] > 2) {
766  dimid = ncinfile->vardim[v][ncinfile->varndims[v] - 3];
767  if (dimid == ncinfile->recdim)
768  kmax = 1;
769  else
770  kmax = ncinfile->dimsize[dimid];
771  } else
772  kmax = 1;
773  if (ncinfile->varndims[v] > 3) {
774  dimid = ncinfile->vardim[v][ncinfile->varndims[v] - 4];
775  if (dimid == ncinfile->recdim)
776  lmax = 1;
777  else
778  lmax = ncinfile->dimsize[dimid];
779  } else
780  lmax = 1;
781  if (verbose > 1)
782  printf(" imax=%d jmax=%d kmax=%d lmax=%d\n", imax, jmax, kmax,
783  lmax);
784 
785  imaxfull =
786  ncinfile->dimfullsize[ncinfile->vardim[v][ncinfile->varndims[v] - 1]];
787  if (ncinfile->varndims[v] > 1)
788  jmaxfull =
789  ncinfile
790  ->dimfullsize[ncinfile->vardim[v][ncinfile->varndims[v] - 2]];
791  else
792  jmaxfull = 1;
793  if (ncinfile->varndims[v] > 2)
794  kmaxfull =
795  ncinfile
796  ->dimfullsize[ncinfile->vardim[v][ncinfile->varndims[v] - 3]];
797  else
798  kmaxfull = 1;
799  if (ncinfile->varndims[v] > 3) {
800  if (ncinfile->vardim[v][ncinfile->varndims[v] - 4] != ncinfile->recdim)
801  lmaxfull =
802  ncinfile
803  ->dimfullsize[ncinfile->vardim[v][ncinfile->varndims[v] - 4]];
804  else
805  lmaxfull = 1;
806  } else
807  lmaxfull = 1;
808  if (verbose > 1)
809  printf(" imaxfull=%d jmaxfull=%d kmaxfull=%d lmaxfull=%d\n",
810  imaxfull, jmaxfull, kmaxfull, lmaxfull);
811  imaxjmaxfull = imaxfull * jmaxfull;
812  imaxjmaxkmaxfull = imaxfull * jmaxfull * kmaxfull;
813 
814  ioffset = outstart[ncinfile->varndims[v] - 0 - 1];
815  if (ncinfile->varndims[v] > 1)
816  joffset = outstart[ncinfile->varndims[v] - 1 - 1];
817  else
818  joffset = 0;
819  if (ncinfile->varndims[v] > 2)
820  koffset = outstart[ncinfile->varndims[v] - 2 - 1];
821  else
822  koffset = 0;
823  if (ncinfile->varndims[v] > 3)
824  loffset = outstart[ncinfile->varndims[v] - 3 - 1];
825  else
826  loffset = 0;
827  if (varrecdim != (-1)) {
828  switch (ncinfile->varndims[v]) {
829  case 1:
830  ioffset = 0;
831  break;
832  case 2:
833  joffset = 0;
834  break;
835  case 3:
836  koffset = 0;
837  break;
838  case 4:
839  loffset = 0;
840  break;
841  }
842  }
843  if (verbose > 1)
844  printf(" ioffset=%d joffset=%d koffset=%d loffset=%d\n",
845  ioffset, joffset, koffset, loffset);
846  switch (ncinfile->datatype[v]) {
847  case NC_BYTE:
848  case NC_CHAR:
849  if (verbose > 1) printf(" start copying byte/char\n");
850  b = 0;
851  for (l = 0; l < lmax; l++)
852  for (k = 0; k < kmax; k++)
853  for (j = 0; j < jmax; j++)
854  for (i = 0; i < imax; i++) {
855  offset = (i + ioffset) + (j + joffset) * imaxfull +
856  (k + koffset) * imaxjmaxfull +
857  (l + loffset) * imaxjmaxkmaxfull;
858  *((unsigned char *)(varbuf[v]) + offset) =
859  *((unsigned char *)values + (b++));
860  }
861  if (verbose > 1) printf(" end copying byte/char\n");
862  break;
863  case NC_SHORT:
864  if (verbose > 1) printf(" start copying short\n");
865  b = 0;
866  for (l = 0; l < lmax; l++)
867  for (k = 0; k < kmax; k++)
868  for (j = 0; j < jmax; j++)
869  for (i = 0; i < imax; i++) {
870  offset = (i + ioffset) + (j + joffset) * imaxfull +
871  (k + koffset) * imaxjmaxfull +
872  (l + loffset) * imaxjmaxkmaxfull;
873  *((short *)(varbuf[v]) + offset) = *((short *)values + (b++));
874  }
875  if (verbose > 1) printf(" end copying short\n");
876  break;
877  case NC_INT:
878  if (verbose > 1) printf(" start copying int\n");
879  b = 0;
880  for (l = 0; l < lmax; l++)
881  for (k = 0; k < kmax; k++)
882  for (j = 0; j < jmax; j++)
883  for (i = 0; i < imax; i++) {
884  offset = (i + ioffset) + (j + joffset) * imaxfull +
885  (k + koffset) * imaxjmaxfull +
886  (l + loffset) * imaxjmaxkmaxfull;
887  *((int *)(varbuf[v]) + offset) = *((int *)values + (b++));
888  }
889  if (verbose > 1) printf(" end copying int\n");
890  break;
891  case NC_FLOAT:
892  if (verbose > 1) printf(" start copying float\n");
893  b = 0;
894  for (l = 0; l < lmax; l++)
895  for (k = 0; k < kmax; k++)
896  for (j = 0; j < jmax; j++)
897  for (i = 0; i < imax; i++) {
898  offset = (i + ioffset) + (j + joffset) * imaxfull +
899  (k + koffset) * imaxjmaxfull +
900  (l + loffset) * imaxjmaxkmaxfull;
901  *((float *)(varbuf[v]) + offset) = *((float *)values + (b++));
902  }
903  if (verbose > 1) printf(" end copying float\n");
904  break;
905  case NC_DOUBLE:
906  if (verbose > 1) printf(" start copying double\n");
907  b = 0;
908  for (l = 0; l < lmax; l++)
909  for (k = 0; k < kmax; k++)
910  for (j = 0; j < jmax; j++)
911  for (i = 0; i < imax; i++) {
912  offset = (i + ioffset) + (j + joffset) * imaxfull +
913  (k + koffset) * imaxjmaxfull +
914  (l + loffset) * imaxjmaxkmaxfull;
915  *((double *)(varbuf[v]) + offset) =
916  *((double *)values + (b++));
917  }
918  if (verbose > 1) printf(" end copying double\n");
919  break;
920  }
921  }
922 
923  /* Deallocate the decomposed variable's buffer */
924  free(values);
925  }
926  _first_ = 0;
927  return (0);
928 }
929 
930 /* Write all the buffered decomposed variables to the output file */
931 int flush_decomp(struct fileinfo *ncoutfile, int nfiles, int r, void *varbuf[],
932  unsigned char verbose) {
933  int v, d; /* Loop variable */
934  long outstart[MAX_NC_DIMS]; /* Data array sizes */
935  long count[MAX_NC_DIMS]; /* " */
936  int varrecdim; /* Position of a variable's record dimension */
937 
938  if (verbose > 1) {
939  printf(" nvars=%d\n", ncoutfile->nvars);
940  }
941 
942  /* Write out all the decomposed variables */
943  for (v = 0; v < ncoutfile->nvars; v++) {
944  if (ncoutfile->vardecomp[v] == 0) continue;
945  if (verbose > 1) printf(" v=%d (%s)\n", v, ncoutfile->varname[v]);
946  varrecdim = (-1);
947  for (d = 0; d < ncoutfile->varndims[v]; d++) {
948  outstart[d] = 0;
949  if (ncoutfile->vardim[v][d] == ncoutfile->recdim) {
950  count[d] = 1;
951  varrecdim = d;
952  } else {
953  count[d] = ncoutfile->dimfullsize[ncoutfile->vardim[v][d]];
954  }
955  if (verbose > 1)
956  printf(" d=%d: outstart=%ld count=%ld\n", d, outstart[d],
957  count[d]);
958  }
959  if (varrecdim != (-1)) outstart[varrecdim] = r;
960  if (varrecdim == (-1) && r > 0) continue;
961  if (verbose > 1) printf(" writing to disk\n");
962  if (ncvarput(ncoutfile->ncfid, v, outstart, count, varbuf[v]) == (-1)) {
963  fprintf(stderr, "Error: cannot write variable \"%d\"'s values!\n", v);
964  return (1);
965  }
966  }
967  return (0);
968 }
969 
970 #endif // NETCDFOUTPUT
971 
972 /*
973  U.S. Department of Commerce (DOC) Software License for "mppnccombine"
974  written at NOAA's Geophysical Fluid Dynamics Laboratory, Princeton
975  Forrestal Campus
976 
977  1. Scope of License
978 
979  Subject to all the terms and conditions of this license, DOC grants USER the
980  royalty-free, nonexclusive, nontransferable, and worldwide rights to
981  reproduce, modify, and distribute "mppnccombine", herein referred to as the
982  PRODUCT.
983 
984  2. Conditions and Limitations of Use
985 
986  Warranties. Neither the U.S. Government, nor any agency or employee
987  thereof, makes any warranties, expressed or implied, with respect to the
988  PRODUCT provided under this license, including but not limited to the
989  implied warranties or merchantability and fitness for any particular
990  purpose.
991 
992  Liability. In no event shall the U.S. Government, nor any agency or
993  employee thereof, be liable for any direct, indirect, or consequential
994  damages flowing from the use of the PRODUCT provided under this license.
995 
996  Non-Assignment. Neither this license nor any rights granted hereunder are
997  transferable or assignable without the explicit prior written consent of
998  DOC.
999 
1000  Names and Logos. USER shall not substitute its name or logo for the name or
1001  logo of DOC, or any of its agencies, in identification of the PRODUCT.
1002 
1003  Export of Technology. USER shall comply with all U.S. laws and regulations
1004  restricting the export of the PRODUCT to other countries.
1005 
1006  Governing Law. This license shall be governed by the laws of United States
1007  as interpreted and applied by the Federal courts in the District of
1008  Columbia.
1009 
1010  3. Term of License
1011 
1012  This license shall remain in effect as long as USER uses the PRODUCT in
1013  accordance with Paragraphs 1 and 2.
1014 */
int mppnccombine(int argc, char *argv[])