Changed h1_summary/h1_summary2 to write a timestamp that is centered on the time bounds.
h1_summary and h1_summary2 previously wrote out a timestamp representing the
last time entry in the last file to be summarized.  Now, to follow convention,
the timestamp is recomputed as the mean of the first and last time_bounds,
where the first time_bounds value is the first value from the first time
entry in the first file and the last time_bounds is the second value from
the last time entry in the last file.
     9  * Loop through one month of hourly history tapes from the Community Land Model
 
    10  * and generate monthly statistics (means and standard deviations) of fields
 
    17 #define HOURS_PER_DAY	24
 
    18 #define MIN_PER_HOUR	60
 
    19 #define SEC_PER_MIN	60
 
    20 #define SEC_PER_HOUR	(MIN_PER_HOUR * SEC_PER_MIN)
 
    22 static char *metadata_vars[] = {
 
    28 	"hour", /* new metadata variable to be added to output files */
 
    58 	struct text_att *next;
 
    59 	struct text_att *prev;
 
    84 static char *time_name = "time";
 
    85 static char *time_bounds_name = "time_bounds";
 
    86 static char *mcsec_name = "mcsec";
 
    87 static char *history_name = "history";
 
    88 static char *nsamples_name = "nsamples";
 
    89 static char *hour_name = "hour", *hour_long_name = "hour of day",
 
    91 static char *cell_method_name = "cell_method";
 
    93 static int nmvars = sizeof(metadata_vars)/sizeof(*metadata_vars);
 
    94 static int input_ncid, input_ndims, input_nvars, input_ngatts, input_unlimdimid;
 
    95 static struct text_att *input_gatt_head = NULL;
 
    96 static struct dim *input_dim_head = NULL, **input_dim_idx;
 
    97 static struct var *input_var_head = NULL;
 
    98 /* translation stuff */
 
    99 static int *idid2mdid, *idid2sdid; /* dimension IDs */
 
   101 static int mean_ncid, mean_ndims, mean_nvars, mean_ngatts, mean_unlimdimid;
 
   102 static int mean_hour_dimid; /* special notes */
 
   103 static struct dim *mean_dim_head = NULL;
 
   104 static struct var *mean_var_head = NULL;
 
   105 static int stddev_ncid, stddev_ndims, stddev_nvars, stddev_ngatts, stddev_unlimdimid;
 
   106 static int stddev_hour_dimid; /* special notes */
 
   107 static struct dim *stddev_dim_head = NULL;
 
   108 static struct var *stddev_var_head = NULL;
 
   110 char is_metadata(char *name)
 
   114 	for (i = 0; i < nmvars && strcmp(name, metadata_vars[i]); i++);
 
   123 struct dim *dimlist_find_by_name(struct dim *head, char *name)
 
   126 	struct dim *p = NULL;
 
   130 		for (i = 0 ; i == 0 || node != head; i++) {
 
   131 			if (!strcmp(node->name, name))
 
   142 struct var *varlist_find_by_name(struct var *head, char *name)
 
   149 		for (i = 0 ; (i == 0 || node != head) && strcmp(node->name, name); i++)
 
   151 		if (i && node == head)
 
   160 void gattlist_add_node(struct text_att **headp, char *name, char *value)
 
   162 	struct text_att *head, *node;
 
   166 	if (!(node = (struct text_att *)malloc(sizeof(struct text_att)))) {
 
   167 		perror("gattlist_add_node");
 
   171 	if (!(node->name = strdup(name))) {
 
   172 		perror("gattlist_add_node");
 
   175 	if (!(node->value = strdup(value))) {
 
   176 		perror("gattlist_add_node");
 
   188 		node->prev = head->prev;
 
   189 		/* set this after setting node->prev from here */
 
   191 		/* set this after having set node->prev */
 
   192 		node->prev->next = node;
 
   198 struct dim *dimlist_add_node(struct dim **headp, int dimid, char *name, size_t len)
 
   200 	struct dim *head, *node;
 
   204 	if (!(node = (struct dim *)malloc(sizeof(struct dim)))) {
 
   205 		perror("dimlist_add_node");
 
   210 	if (!(node->name = strdup(name))) {
 
   211 		perror("dimlist_add_node");
 
   224 		node->prev = head->prev;
 
   225 		/* set this after setting node->prev from here */
 
   227 		/* set this after having set node->prev */
 
   228 		node->prev->next = node;
 
   234 void varlist_add_node(struct var **headp, int ncvarid, char *name,
 
   235 	nc_type nctype, int ndims, int *dimids, int natts, char FillFlag,
 
   239 	struct var *head, *node;
 
   243 	if (!(node = (struct var *)malloc(sizeof(struct var)))) {
 
   244 		perror("varlist_add_node");
 
   248 	node->ncvarid = ncvarid;
 
   249 	if (!(node->name = strdup(name))) {
 
   250 		perror("varlist_add_node");
 
   253 	node->nctype = nctype;
 
   255 	if (!(node->dimids = (int *)malloc(ndims * sizeof(int)))) {
 
   256 		perror("varlist_add_node");
 
   259 	for (i = 0; i < ndims; i++) node->dimids[i] = dimids[i];
 
   261 	node->metadata = is_metadata(name);
 
   262 	node->FillFlag = FillFlag;
 
   263 	node->FillValue = FillValue;
 
   273 		node->prev = head->prev;
 
   274 		/* set this after setting node->prev from here */
 
   276 		/* set this after having set node->prev */
 
   277 		node->prev->next = node;
 
   283 void varlist_print(struct var *headp)
 
   288 	printf("Beginning of Variable List\n");
 
   292 		for (j = 0; j == 0 || node != headp; j++) {
 
   293 			printf("Variable %d (ptr=%ld):\n", j, (long)node);
 
   294 			printf("\tncvarid = %d\n", node->ncvarid);
 
   295 			printf("\tname = %s\n",  node->name);
 
   296 			printf("\tnctype = %d\n", node->nctype);
 
   297 			printf("\tndims = %d\n", node->ndims);
 
   298 			printf("\tdimids =");
 
   299 			for (i = 0; i < node->ndims; i++)
 
   300 				printf(" %d", node->dimids[i]);
 
   302 			printf("\tnatts = %d\n", node->natts);
 
   303 			printf("\tmetadata = %d\n", (int)node->metadata);
 
   304 			printf("\tnext = %ld\n", (long)node->next);
 
   305 			printf("\tprev = %ld\n", (long)node->prev);
 
   310 		printf("\tList undefined\n");
 
   313 	printf("End of Variable List\n");
 
   318 void wrap_nc(int status)
 
   320 	if (status != NC_NOERR) {
 
   321 		fprintf(stderr, "netCDF error: %s\n", nc_strerror(status));
 
   328 void get_dimensions(int ncid, int ndims, struct dim **dim_headp, struct dim ***in_dim_idxp)
 
   331 	char name[NC_MAX_NAME+1];
 
   333 	struct dim **in_dim_idx;
 
   335 	if (!(*in_dim_idxp = (struct dim **)malloc(ndims * sizeof(struct dim *)))) {
 
   336 		perror("*in_dim_idxp");
 
   339 	in_dim_idx = *in_dim_idxp;
 
   341 	for (i = 0; i < ndims; i++) {
 
   342 		wrap_nc(nc_inq_dim(ncid, i, name, &len));
 
   343 		in_dim_idx[i] = dimlist_add_node(dim_headp, i, name, len);
 
   348 void get_gatts(int ncid, int ngatts, struct text_att **gatt_headp)
 
   351 	char name[NC_MAX_NAME+1], *value;
 
   355 	for (i = 0; i < ngatts; i++) {
 
   356 		wrap_nc(nc_inq_attname(ncid, NC_GLOBAL, i, name));
 
   357 		wrap_nc(nc_inq_att(ncid, NC_GLOBAL, name, &xtype, &len));
 
   358 		if (xtype != NC_CHAR) {
 
   359 			fprintf(stderr, "Global attribute %s is not of type NC_CHAR\n", name);
 
   362 		if (!(value = (char *)malloc((len+1)*sizeof(char)))) {
 
   363 			perror("get_gatts: value");
 
   366 		wrap_nc(nc_get_att_text(ncid, NC_GLOBAL, name, value));
 
   367 		value[(len+1-1)] = '\0';
 
   368 		gattlist_add_node(gatt_headp, name, value);
 
   369 		free(value); /* because gattlist_add_node() duplicates it */
 
   375 void get_vars(int ncid, int nvars, struct var **var_headp)
 
   377 	int i, ndims, dimids[NC_MAX_VAR_DIMS], natts;
 
   380 	char name[NC_MAX_NAME+1], *fill_att_name = "_FillValue", FillFlag;
 
   381 	nc_type xtype, atype;
 
   383 	for (i = 0; i < nvars; i++) {
 
   384 		wrap_nc(nc_inq_var(ncid, i, name, &xtype, &ndims, dimids,
 
   386 		/* Check for _FillValue */
 
   389 		if (nc_inq_att(ncid, i, fill_att_name, &atype, &len)
 
   391 			if (atype == NC_FLOAT && len) {
 
   392 				wrap_nc(nc_get_att_float(ncid, i,
 
   393 					fill_att_name, &FillValue));
 
   397 		varlist_add_node(var_headp, i, name, xtype, ndims, dimids,
 
   398 			natts, FillFlag, FillValue);
 
   404 int put_dimensions(struct dim *in_dim_head, int in_ndims, int in_unlimdimid,
 
   405 	size_t nsamples, int **in2outp, int out_ncid,
 
   406 	struct dim **out_dim_headp, int *out_unlimdimidp, int *out_hour_dimidp)
 
   409 	 * Define dimensions on new files and build translation tables between
 
   412 	int j, dimid, ndims, *in2out;
 
   418 	if (!(*in2outp = (int *)malloc((in_ndims+1)*sizeof(int)))) {
 
   419 		perror("put_dimensions: in2outp");
 
   426 		for (j = 0; j == 0 || dnode != in_dim_head; j++) {
 
   427 			if (dnode->dimid == in_unlimdimid)
 
   431 			wrap_nc(nc_def_dim(out_ncid, dnode->name, len, &dimid));
 
   432 			dimlist_add_node(out_dim_headp, dimid, dnode->name, len);
 
   434 			in2out[dnode->dimid] = dimid;
 
   435 			if (dnode->dimid == in_unlimdimid)
 
   436 				*out_unlimdimidp = dimid;
 
   438 			 * Just after the "time" dimension, add the new
 
   439 			 * "nsamples" and "hour" dimensions.
 
   441 			if (!strcmp(dnode->name, time_name)) {
 
   442 				wrap_nc(nc_def_dim(out_ncid, nsamples_name, nsamples, &dimid));
 
   443 				dimlist_add_node(out_dim_headp, dimid, nsamples_name, nsamples);
 
   446 				wrap_nc(nc_def_dim(out_ncid, hour_name, HOURS_PER_DAY, &dimid));
 
   447 				dimlist_add_node(out_dim_headp, dimid, hour_name, HOURS_PER_DAY);
 
   449 				/* Track hour dimid for out files */
 
   450 				*out_hour_dimidp = dimid;
 
   457 		fprintf(stderr, "WARNING: No dimensions defined!\n");
 
   463 int put_gatts(struct text_att *in_gatt_head, int out_ncid, char *out_history)
 
   466 	 * Write out global attributes matching those of the input file.
 
   467 	 * Change history attribute to the string passed in as an argument.
 
   469 	int j, hflag = 0, ngatts;
 
   471 	struct text_att *anode;
 
   476 		anode = in_gatt_head;
 
   477 		for (j = 0; j == 0 || anode != in_gatt_head; j++) {
 
   478 			if (!strcmp(anode->name, history_name)) {
 
   483 				value = anode->value;
 
   484 			wrap_nc(nc_put_att_text(out_ncid, NC_GLOBAL, anode->name, strlen(value), value));
 
   488 		/* If no history attribute on input, add one to the output */
 
   490 			wrap_nc(nc_put_att_text(out_ncid, NC_GLOBAL, history_name, strlen(out_history), out_history));
 
   495 		fprintf(stderr, "WARNING: No global attributes defined!\n");
 
   501 int translate_dimids(struct dim **in_dim_idx, char metadata, int in_ndims, int *in_dimids, int *in2out, int *out_dimids, int hour_dimid)
 
   504 	 * Translate between input dimension IDs and those for the output file.
 
   505 	 * For normal time-based variables, add a new dimension for hour of
 
   506 	 * day.  For metadata variables, do not add new dimensions, even if
 
   507 	 * it is time-based.  Return (possibly new) number of dimensions.
 
   511 	for (i = ndims = 0; i < in_ndims; i++, ndims++) {
 
   512 		out_dimids[ndims] = in2out[in_dimids[i]];
 
   513 		if (!strcmp((in_dim_idx[in_dimids[i]])->name, time_name)
 
   516 			out_dimids[ndims] = hour_dimid;
 
   523 int copy_att(char metadata, int stat_type, int in_natts, int in_ncid,
 
   524 	int in_varid, int out_ncid, int out_varid)
 
   527 	 * Copy the attributes of the input variable to those of the output
 
   528 	 * variable. If the variable is not a metadata variable, ensure that
 
   529 	 * the cell_method attribute is set either to "time: mean" or
 
   530 	 * "time: standard_deviation", depending on the output file type.
 
   534 	char name[NC_MAX_NAME+1], cmflag = 0;
 
   535 	char *cell_methods[] = { "time: mean", "time: standard_deviation" };
 
   537 	for (i = natts = 0; i < in_natts; i++, natts++) {
 
   538 		wrap_nc(nc_inq_attname(in_ncid, in_varid, i, name));
 
   539 		if (!strcmp(name, cell_method_name) && !metadata) {
 
   540 			wrap_nc(nc_put_att_text(out_ncid, out_varid, cell_method_name, strlen(cell_methods[stat_type]), cell_methods[stat_type]));
 
   544 			wrap_nc(nc_copy_att(in_ncid, in_varid, name, out_ncid, out_varid));
 
   547 	 * If no cell_method attribute was specified for a non-metadata
 
   548 	 * variable on the input file, add the appropriate cell_method anyway
 
   549 	 * on the output file.
 
   551 	if (!cmflag && !metadata) {
 
   552 		wrap_nc(nc_put_att_text(out_ncid, out_varid, cell_method_name, strlen(cell_methods[stat_type]), cell_methods[stat_type]));
 
   559 int define_vars(int in_ncid, struct dim **in_dim_idx, struct var *in_var_head,
 
   560 	int *in2out, int out_ncid, int hour_dimid, struct var **out_var_headp,
 
   564 	 * Define variables on output file and copy attributes from input file.
 
   565 	 * Return number of variables defined.
 
   567 	int j, varid, nvars, ndims, dimids[NC_MAX_VAR_DIMS], natts;
 
   575 		 * March through input variables creating (mostly) the same
 
   576 		 * variables on the output file.
 
   578 		for (j = 0; j == 0 || vnode != in_var_head; j++) {
 
   579 			ndims = translate_dimids(in_dim_idx, vnode->metadata, vnode->ndims, vnode->dimids, in2out, dimids, hour_dimid);
 
   580 			wrap_nc(nc_def_var(out_ncid, vnode->name, vnode->nctype, ndims, dimids, &varid));
 
   581 			natts = copy_att(vnode->metadata, stat_type, vnode->natts, in_ncid, vnode->ncvarid, out_ncid, varid);
 
   582 			varlist_add_node(out_var_headp, varid, vnode->name, vnode->nctype, ndims, dimids, natts, vnode->FillFlag, vnode->FillValue);
 
   585 			 * Just after the "time" variable, add the new "hour"
 
   586 			 * variable for hour of day.
 
   588 			if (!strcmp(vnode->name, time_name)) {
 
   590 				dimids[0] = hour_dimid;
 
   591 				wrap_nc(nc_def_var(out_ncid, hour_name, NC_FLOAT, ndims, dimids, &varid));
 
   592 				wrap_nc(nc_put_att_text(out_ncid, varid, "long_name", strlen(hour_long_name), hour_long_name));
 
   593 				wrap_nc(nc_put_att_text(out_ncid, varid, "units", strlen(hour_units), hour_units));
 
   594 				varlist_add_node(out_var_headp, varid, hour_name, NC_FLOAT, ndims, dimids, 2, 0, 0.0);
 
   602 		fprintf(stderr, "WARNING: No variables defined!\n");
 
   608 void *alloc_var(nc_type nctype, size_t len)
 
   614 			if (!(val = (float *)malloc((len) * sizeof(float)))) {
 
   615 				perror("alloc_var: val");
 
   620 			if (!(val = (int *)malloc((len) * sizeof(int)))) {
 
   621 				perror("alloc_var: val");
 
   626 			if (!(val = (double *)malloc((len) * sizeof(double)))) {
 
   627 				perror("alloc_var: val");
 
   632 			if (!(val = (char *)malloc(((len)+1) * sizeof(char)))) {
 
   633 				perror("alloc_var: val");
 
   638 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
 
   645 void *read_var(int ncid, int varid, nc_type nctype, int ndims, int *dimids, struct dim **dim_idx)
 
   648 	size_t len = (size_t)1;
 
   651 	/* Figure out the total size */
 
   652 	for (i = 0; i < ndims; i++) len *= (dim_idx[dimids[i]])->len;
 
   654 	val = alloc_var(nctype, len);
 
   658 			wrap_nc(nc_get_var_float(ncid, varid, val));
 
   661 			wrap_nc(nc_get_var_int(ncid, varid, val));
 
   664 			wrap_nc(nc_get_var_double(ncid, varid, val));
 
   667 			wrap_nc(nc_get_var_text(ncid, varid, val));
 
   670 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
 
   677 void *read_timeslice(int ncid, int varid, nc_type nctype, int ndims, int *dimids, struct dim **dim_idx, size_t tslice)
 
   680 	size_t len = (size_t)1, start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
 
   683 	/* Make sure time is really the first dimension */
 
   684 	if (strcmp((dim_idx[dimids[0]])->name, time_name)) {
 
   685 		fprintf(stderr, "read_timeslice: %s is not first dimension of variable!\n", time_name);
 
   689 	 * Figure out the total size for the slice (start at second dimension)
 
   690 	 * and build start/count vectors.
 
   694 	for (i = 1; i < ndims; i++) {
 
   696 		count[i] = (dim_idx[dimids[i]])->len;
 
   697 		len *= (dim_idx[dimids[i]])->len;
 
   700 	val = alloc_var(nctype, len);
 
   704 			wrap_nc(nc_get_vara_float(ncid, varid, start, count, val));
 
   707 			wrap_nc(nc_get_vara_int(ncid, varid, start, count, val));
 
   710 			wrap_nc(nc_get_vara_double(ncid, varid, start, count, val));
 
   713 			wrap_nc(nc_get_vara_text(ncid, varid, start, count, val));
 
   716 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
 
   723 void write_var(int ncid, int varid, nc_type nctype, void *val)
 
   727 			wrap_nc(nc_put_var_float(ncid, varid, val));
 
   730 			wrap_nc(nc_put_var_int(ncid, varid, val));
 
   733 			wrap_nc(nc_put_var_double(ncid, varid, val));
 
   736 			wrap_nc(nc_put_var_text(ncid, varid, val));
 
   739 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
 
   746 void write_timeslice(int ncid, int varid, nc_type nctype, int ndims, int *dimids, struct dim **dim_idx, void *val, size_t tslice)
 
   749 	size_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
 
   751 	/* Make sure time is really the first dimension */
 
   752 	if (strcmp((dim_idx[dimids[0]])->name, time_name)) {
 
   753 		fprintf(stderr, "write_timeslice: %s is not first dimension of variable!\n", time_name);
 
   757 	/* Build start/count vectors */
 
   760 	for (i = 1; i < ndims; i++) {
 
   762 		count[i] = (dim_idx[dimids[i]])->len;
 
   767 			wrap_nc(nc_put_vara_float(ncid, varid, start, count, val));
 
   770 			wrap_nc(nc_put_vara_int(ncid, varid, start, count, val));
 
   773 			wrap_nc(nc_put_vara_double(ncid, varid, start, count, val));
 
   776 			wrap_nc(nc_put_vara_text(ncid, varid, start, count, val));
 
   779 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
 
   786 void copy_metadata(int in_ncid, struct var *in_var_head,
 
   787 	struct dim **in_dim_idx, int out_ncid, struct var *out_var_head,
 
   791 	float hr[HOURS_PER_DAY], mean_time;
 
   792 	struct var *in_vnode, *out_vnode;
 
   795 	for (i = 0; i < HOURS_PER_DAY; i++) hr[i] = (float)i;
 
   798 		in_vnode = in_var_head;
 
   800 		 * March through input variables to stuff metadata values into
 
   801 		 * the new output files. NOTE: Time-based metadata variables
 
   802 		 * should contain only the last (time-slice) value (from all
 
   805 		for (j = 0; j == 0 || in_vnode != in_var_head; j++) {
 
   806 			if (in_vnode->metadata) {
 
   807 				out_vnode = varlist_find_by_name(out_var_head, in_vnode->name);
 
   808 				if (strcmp((input_dim_idx[in_vnode->dimids[0]])->name, time_name)) {
 
   809 					/* time is not the first dimension */
 
   811 					printf("Copying metadata variable %s\n",
 
   814 					val = read_var(in_ncid, in_vnode->ncvarid, in_vnode->nctype, in_vnode->ndims, in_vnode->dimids, in_dim_idx);
 
   815 					write_var(out_ncid, out_vnode->ncvarid, out_vnode->nctype, val);
 
   818 					/* time is the first dimension */
 
   820 					printf("Copying last value of \
 
   821 time-based metadata variable %s\n", in_vnode->name);
 
   823 					val = read_timeslice(in_ncid, in_vnode->ncvarid, in_vnode->nctype, in_vnode->ndims, in_vnode->dimids, in_dim_idx, ((input_dim_idx[in_vnode->dimids[0]])->len - 1));
 
   824 					if (!strcmp(in_vnode->name, time_bounds_name))
 
   825 						write_timeslice(out_ncid, out_vnode->ncvarid, out_vnode->nctype, in_vnode->ndims, in_vnode->dimids, in_dim_idx, tbounds, 0);
 
   826 					else if (!strcmp(in_vnode->name, time_name)) {
 
   827 						/* force the timestamp to be the
 
   828 						 * mean of the time bounds */
 
   829 						mean_time = (tbounds[0] + tbounds[1]) / 2.0;
 
   830 						write_timeslice(out_ncid, out_vnode->ncvarid, out_vnode->nctype, in_vnode->ndims, in_vnode->dimids, in_dim_idx, &mean_time, 0);
 
   833 						write_timeslice(out_ncid, out_vnode->ncvarid, out_vnode->nctype, in_vnode->ndims, in_vnode->dimids, in_dim_idx, val, 0);
 
   837 				 * Just after the "time" variable, write out
 
   838 				 * the "hour" variable values.
 
   840 				if (!strcmp(in_vnode->name, time_name)) {
 
   841 					out_vnode = varlist_find_by_name(out_var_head, hour_name);
 
   842 					write_var(out_ncid, out_vnode->ncvarid,
 
   843 						out_vnode->nctype, hr);
 
   848 				printf("Skipping variable %s\n",
 
   852 			in_vnode = in_vnode->next;
 
   859 void get_time_bounds(char *first_fname, char *last_fname, double *tbounds)
 
   861 	int time_dimid, time_bounds_varid;
 
   862 	size_t time_len, time_bounds_index[2];
 
   865 	/* Open first input file */
 
   866 	wrap_nc(nc_open(first_fname, NC_NOWRITE, &input_ncid));
 
   867 	/* Get dimension ID of the time dimension */
 
   868 	wrap_nc(nc_inq_dimid(input_ncid, time_name, &time_dimid));
 
   869 	/* Get length of time dimension */
 
   870 	wrap_nc(nc_inq_dimlen(input_ncid, time_dimid, &time_len));
 
   871 	/* Get variable ID of time_bounds variable */
 
   872 	wrap_nc(nc_inq_varid(input_ncid, time_bounds_name, &time_bounds_varid));
 
   873 	/* Set index for reading the first value of time_bounds from the
 
   874 	 * first timeslice of the first file */
 
   875 	time_bounds_index[0] = time_bounds_index[1] = 0;
 
   877 	wrap_nc(nc_get_var1_double(input_ncid, time_bounds_varid,
 
   878 		time_bounds_index, &bnd1));
 
   879 	/* If the first and last file are not the same, close the first one and
 
   880 	 * open the second one */
 
   881 	if (strcmp(first_fname, last_fname)) {
 
   882 		/* Close the first input file */
 
   883 		wrap_nc(nc_close(input_ncid));
 
   884 		/* Open the last input file */
 
   885 		wrap_nc(nc_open(last_fname, NC_NOWRITE, &input_ncid));
 
   886 		/* Get dimension ID of the time dimension */
 
   887 		wrap_nc(nc_inq_dimid(input_ncid, time_name, &time_dimid));
 
   888 		/* Get length of time dimension */
 
   889 		wrap_nc(nc_inq_dimlen(input_ncid, time_dimid, &time_len));
 
   890 		/* Get variable ID of time_bounds variable */
 
   891 		wrap_nc(nc_inq_varid(input_ncid, time_bounds_name,
 
   892 			&time_bounds_varid));
 
   894 	/* Set index for reading the second value of time_bounds from the
 
   895 	 * last timeslice of the last file */
 
   896 	time_bounds_index[0] = time_len - 1; time_bounds_index[1] = 1;
 
   898 	wrap_nc(nc_get_var1_double(input_ncid, time_bounds_varid,
 
   899 		time_bounds_index, &bnd2));
 
   901 	/* Close the last input file */
 
   902 	wrap_nc(nc_close(input_ncid));
 
   910 void open_inout(char *first_fname, char *last_fname, char *mean_fname, char *stddev_fname, char *flist, size_t nsamples)
 
   912 	char *mean_history_gatt, *stddev_history_gatt,
 
   913 		*mean_prefix="Statistical means from history files:",
 
   914 		*stddev_prefix="Statistical standard deviations from history files:";
 
   918 	 * Construct strings for history global attributes for the two output
 
   921 	if (!(mean_history_gatt = (char *)malloc((strlen(mean_prefix) + strlen(flist)+1)*sizeof(char)))) {
 
   922 		perror("open_inout:mean_history_gatt");
 
   925 	if (!(stddev_history_gatt = (char *)malloc((strlen(stddev_prefix) + strlen(flist)+1)*sizeof(char)))) {
 
   926 		perror("open_inout:stddev_history_gatt");
 
   929 	sprintf(mean_history_gatt, "%s%s", mean_prefix, flist);
 
   930 	sprintf(stddev_history_gatt, "%s%s", stddev_prefix, flist);
 
   932 	/* The two time_bounds values must be handled explicitly by obtaining
 
   933 	 * the first one from the first file and the last one from the last
 
   934 	 * file.  These are then passed to copy_metadata() below. */
 
   935 	get_time_bounds(first_fname, last_fname, tbounds);
 
   937 	fprintf(stderr, "Got back tbounds=%lf,%lf\n", tbounds[0], tbounds[1]);
 
   940 	/* Open last input file */
 
   941 	wrap_nc(nc_open(last_fname, NC_NOWRITE, &input_ncid));
 
   942 	/* Inquire about number of dimensions, variables, global attributes
 
   943 	 * and the ID of the unlimited dimension
 
   945 	wrap_nc(nc_inq(input_ncid, &input_ndims, &input_nvars, &input_ngatts,
 
   947 	/* Grab dimension IDs and lengths from input file */
 
   948 	get_dimensions(input_ncid, input_ndims, &input_dim_head, &input_dim_idx);
 
   949 	/* Grab desired global attributes from input file */
 
   950 	get_gatts(input_ncid, input_ngatts, &input_gatt_head);
 
   951 	/* Grab variable IDs and attributes from input file */
 
   952 	get_vars(input_ncid, input_nvars, &input_var_head);
 
   954 	varlist_print(input_var_head);
 
   956 	/* Create netCDF files for monthly means and standard deviations */
 
   957 	/* Create new netCDF files */
 
   958 	wrap_nc(nc_create(mean_fname, NC_NOCLOBBER, &mean_ncid));
 
   959 	wrap_nc(nc_create(stddev_fname, NC_NOCLOBBER, &stddev_ncid));
 
   960 	/* Define dimensions */
 
   961 	mean_ndims = put_dimensions(input_dim_head, input_ndims,
 
   962 		input_unlimdimid, nsamples, &idid2mdid, mean_ncid,
 
   963 		&mean_dim_head, &mean_unlimdimid, &mean_hour_dimid);
 
   964 	stddev_ndims = put_dimensions(input_dim_head, input_ndims,
 
   965 		input_unlimdimid, nsamples, &idid2sdid, stddev_ncid,
 
   966 		&stddev_dim_head, &stddev_unlimdimid, &stddev_hour_dimid);
 
   967 	/* Define variables and their attributes */
 
   968 	mean_nvars = define_vars(input_ncid, input_dim_idx, input_var_head,
 
   969 		idid2mdid, mean_ncid, mean_hour_dimid, &mean_var_head,
 
   971 	stddev_nvars = define_vars(input_ncid, input_dim_idx, input_var_head,
 
   972 		idid2sdid, stddev_ncid, stddev_hour_dimid, &stddev_var_head,
 
   974 	/* Store global attributes */
 
   975 	mean_ngatts = put_gatts(input_gatt_head, mean_ncid, mean_history_gatt);
 
   976 	stddev_ngatts = put_gatts(input_gatt_head, stddev_ncid,
 
   977 		stddev_history_gatt);
 
   978 	/* End define mode */
 
   979 	wrap_nc(nc_enddef(mean_ncid));
 
   980 	wrap_nc(nc_enddef(stddev_ncid));
 
   981 	/* Write out metdata variables that do not depend on "time" */
 
   982 	copy_metadata(input_ncid, input_var_head, input_dim_idx, mean_ncid,
 
   983 		mean_var_head, tbounds);
 
   984 	copy_metadata(input_ncid, input_var_head, input_dim_idx, stddev_ncid,
 
   985 		stddev_var_head, tbounds);
 
   987 	wrap_nc(nc_close(input_ncid));
 
   992 size_t count_samples(int nifnames, char **input_fnames)
 
   995 	char name[NC_MAX_NAME+1];
 
   998 	for (i = 0; i < nifnames; i++) {
 
   999 		/* Open input file */
 
  1000 		wrap_nc(nc_open(input_fnames[i], NC_NOWRITE, &input_ncid));
 
  1002 		 * Inquire about number of dimensions, variables, global
 
  1003 		 * attributes and the ID of the unlimited dimension
 
  1005 		wrap_nc(nc_inq(input_ncid, &input_ndims, &input_nvars,
 
  1006 			&input_ngatts, &input_unlimdimid));
 
  1007 		wrap_nc(nc_inq_dim(input_ncid, input_unlimdimid, name, &len));
 
  1008 		if (strcmp(name, time_name)) {
 
  1009 			fprintf(stderr, "%s is not the unlimited dimension for file %s!\n", time_name, input_fnames[i]);
 
  1013 		printf("%ld samples in %s\n", (long)len, input_fnames[i]);
 
  1015 		wrap_nc(nc_close(input_ncid));
 
  1021 void alloc_means_stddevs(size_t d1, size_t d2, double ***meansp, double ***stddevsp, size_t ***cell_samplesp)
 
  1024 	 * Allocate space for arrays of means and standard deviations by
 
  1028 	size_t **cell_samples;
 
  1029 	double **means, **stddevs;
 
  1031 	if (!(*meansp = (double **)malloc(HOURS_PER_DAY * sizeof(double *)))) {
 
  1032 		perror("alloc_means_stddevs:*meansp");
 
  1035 	if (!(*stddevsp = (double **)malloc(HOURS_PER_DAY * sizeof(double *)))) {
 
  1036 		perror("alloc_means_stddevs:*stddevsp");
 
  1039 	if (!(*cell_samplesp = (size_t **)malloc(HOURS_PER_DAY * sizeof(size_t *)))) {
 
  1040 		perror("alloc_means_stddevs:*cell_samplesp");
 
  1045 	stddevs = *stddevsp;
 
  1046 	cell_samples = *cell_samplesp;
 
  1048 	for (i = 0; i < HOURS_PER_DAY; i++) {
 
  1049 		if (!(means[i] = (double *)malloc(d1 * d2 * sizeof(double)))) {
 
  1050 			perror("alloc_means_stddevs:means[i]");
 
  1053 		if (!(stddevs[i] = (double *)malloc(d1 * d2 * sizeof(double)))) {
 
  1054 			perror("alloc_means_stddevs:stddevs[i]");
 
  1057 		if (!(cell_samples[i] = (size_t *)malloc(d1 * d2 * sizeof(size_t)))) {
 
  1058 			perror("alloc_means_stddevs:cell_samples[i]");
 
  1066 void init_means_stddevs(size_t d1, size_t d2, double **means, double **stddevs, size_t **cell_samples, float FillValue)
 
  1070 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
 
  1071 		for (i = 0; i < d1; i++) {
 
  1072 #pragma _CRI concurrent
 
  1073 			for (j = 0; j < d2; j++) {
 
  1075 				means[hr][pos] = FillValue;
 
  1076 				stddevs[hr][pos] = FillValue;
 
  1077 				cell_samples[hr][pos] = 0;
 
  1085 void reset_cell_samples(size_t d1, size_t d2, size_t **cell_samples)
 
  1089 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
 
  1090 		for (i = 0; i < d1; i++) {
 
  1091 #pragma _CRI concurrent
 
  1092 			for (j = 0; j < d2; j++) {
 
  1094 				cell_samples[hr][pos] = 0;
 
  1102 void free_means_stddevs(double **means, double **stddevs, size_t **cell_samples)
 
  1105 	 * Free space from arrays of means and standard deviations by
 
  1110 	for (i = 0; i < HOURS_PER_DAY; i++) {
 
  1113 		free(cell_samples[i]);
 
  1123 void add_to_means_float(float *val, int sec, size_t d1, size_t d2,
 
  1124 	char FillFlag, float FillValue, double **means, size_t **cell_samples)
 
  1128 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
 
  1130 	for (i = 0; i < d1; i++) {
 
  1131 #pragma _CRI concurrent
 
  1132 		for (j = 0; j < d2; j++) {
 
  1134 			if (!FillFlag || (FillFlag && val[pos] != FillValue)) {
 
  1135 				if (cell_samples[hr][pos] == 0)
 
  1136 					means[hr][pos] = (double)val[pos];
 
  1138 					means[hr][pos] += (double)val[pos];
 
  1139 				++cell_samples[hr][pos];
 
  1147 void add_to_means_double(double *val, int sec, size_t d1, size_t d2,
 
  1148 	char FillFlag, float FillValue, double **means, size_t **cell_samples)
 
  1152 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
 
  1154 	for (i = 0; i < d1; i++) {
 
  1155 #pragma _CRI concurrent
 
  1156 		for (j = 0; j < d2; j++) {
 
  1158 			if (!FillFlag || (FillFlag && val[pos] != FillValue)) {
 
  1159 				if (cell_samples[hr][pos] == 0)
 
  1160 					means[hr][pos] = val[pos];
 
  1162 					means[hr][pos] += val[pos];
 
  1163 				++cell_samples[hr][pos];
 
  1172 void divide_means(size_t d1, size_t d2, double **means, size_t **cell_samples)
 
  1176 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
 
  1177 		for (i = 0; i < d1; i++) {
 
  1178 #pragma _CRI concurrent
 
  1179 			for (j = 0; j < d2; j++) {
 
  1181 				if (cell_samples[hr][pos] != 0) {
 
  1182 					means[hr][pos] /= (double)cell_samples[hr][pos];
 
  1191 void add_to_stddevs_float(float *val, int sec, size_t d1, size_t d2,
 
  1192 	char FillFlag, float FillValue, double **means, double **stddevs,
 
  1193 	size_t **cell_samples)
 
  1198 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
 
  1200 	for (i = 0; i < d1; i++) {
 
  1201 #pragma _CRI concurrent
 
  1202 		for (j = 0; j < d2; j++) {
 
  1204 			if (!FillFlag || (FillFlag && val[pos] != FillValue
 
  1205 				&& means[hr][pos] != FillValue)) {
 
  1206 				delta = means[hr][pos] - (double)val[pos];
 
  1207 				if (cell_samples[hr][pos] == 0)
 
  1208 					stddevs[hr][pos] = delta * delta;
 
  1210 					stddevs[hr][pos] += delta * delta;
 
  1211 				++cell_samples[hr][pos];
 
  1219 void add_to_stddevs_double(double *val, int sec, size_t d1, size_t d2,
 
  1220 	char FillFlag, float FillValue, double **means, double **stddevs,
 
  1221 	size_t **cell_samples)
 
  1226 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
 
  1228 	for (i = 0; i < d1; i++) {
 
  1229 #pragma _CRI concurrent
 
  1230 		for (j = 0; j < d2; j++) {
 
  1232 			if (!FillFlag || (FillFlag && val[pos] != FillValue
 
  1233 				&& means[hr][pos] != FillValue)) {
 
  1234 				delta = means[hr][pos] - val[pos];
 
  1235 				if (cell_samples[hr][pos] == 0)
 
  1236 					stddevs[hr][pos] = delta * delta;
 
  1238 					stddevs[hr][pos] += delta * delta;
 
  1239 				++cell_samples[hr][pos];
 
  1248 void divide_sqrt_stddevs(size_t d1, size_t d2, double **stddevs, size_t **cell_samples)
 
  1252 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
 
  1253 		for (i = 0; i < d1; i++) {
 
  1254 #pragma _CRI concurrent
 
  1255 			for (j = 0; j < d2; j++) {
 
  1257 				if (cell_samples[hr][pos] != 0) {
 
  1258 					stddevs[hr][pos] /= (double)cell_samples[hr][pos];
 
  1259 					stddevs[hr][pos] = sqrt(stddevs[hr][pos]);
 
  1269 void read_and_compute(int nifnames, char **input_fnames, size_t d1, size_t d2, char *var_name, nc_type nctype, int level, int ndims, char FillFlag, float FillValue, int stat_type, double **means, double **stddevs, size_t **cell_samples)
 
  1273 	size_t len, start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
 
  1274 	char name[NC_MAX_NAME+1];
 
  1277 	/* Allocate space for timeslice */
 
  1278 	val = alloc_var(nctype, (d1 * d2));
 
  1280 	for (i = 0; i < nifnames; i++) {
 
  1282 		printf("\tOpening %s", input_fnames[i]);
 
  1283 		if (ndims > 3) printf(" to retrieve level %d", level);
 
  1284 		if (stat_type == MEAN_TYPE)
 
  1285 			printf(" for computing means\n");
 
  1287 			printf(" for computing stddevs\n");
 
  1289 		/* Open input file */
 
  1290 		wrap_nc(nc_open(input_fnames[i], NC_NOWRITE, &input_ncid));
 
  1292 		 * Inquire about number of dimensions, variables, global
 
  1293 		 * attributes and the ID of the unlimited dimension
 
  1295 		wrap_nc(nc_inq(input_ncid, &input_ndims, &input_nvars,
 
  1296 			&input_ngatts, &input_unlimdimid));
 
  1297 		wrap_nc(nc_inq_dim(input_ncid, input_unlimdimid, name, &len));
 
  1298 		if (strcmp(name, time_name)) {
 
  1299 			fprintf(stderr, "%s is not the unlimited dimension for file %s!\n", time_name, input_fnames[i]);
 
  1302 		/* Get seconds of day variable */
 
  1303 		wrap_nc(nc_inq_varid(input_ncid, mcsec_name, &varid));
 
  1304 		if (!(mcsec = (int *)malloc(len * sizeof(int)))) {
 
  1305 			perror("read_and_compute:mcsec");
 
  1308 		wrap_nc(nc_get_var_int(input_ncid, varid, mcsec));
 
  1309 		/* Get variable ID for requested variable */
 
  1310 		wrap_nc(nc_inq_varid(input_ncid, var_name, &varid));
 
  1311 		/* Retrieve time slice of desired variable */
 
  1312 		for (ts = 0; ts < len; ts++) {
 
  1315 			start[(ndims-2)] = 0;
 
  1316 			count[(ndims-2)] = d1;
 
  1317 			start[(ndims-1)] = 0;
 
  1318 			count[(ndims-1)] = d2;
 
  1325 					wrap_nc(nc_get_vara_float(input_ncid, varid, start, count, val));
 
  1326 					if (stat_type == MEAN_TYPE)
 
  1327 						add_to_means_float(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, cell_samples);
 
  1329 						add_to_stddevs_float(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, stddevs, cell_samples);
 
  1332 					wrap_nc(nc_get_vara_double(input_ncid, varid, start, count, val));
 
  1333 					if (stat_type == MEAN_TYPE)
 
  1334 						add_to_means_double(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, cell_samples);
 
  1336 						add_to_stddevs_double(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, stddevs, cell_samples);
 
  1339 					fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
 
  1344 		/* Free mcsec vector */
 
  1346 		/* Close input file */
 
  1347 		wrap_nc(nc_close(input_ncid));
 
  1349 	/* Divide sums by number of samples */
 
  1350 	if (stat_type == MEAN_TYPE)
 
  1351 		divide_means(d1, d2, means, cell_samples);
 
  1353 		divide_sqrt_stddevs(d1, d2, stddevs, cell_samples);
 
  1355 	/* Free working variable array */
 
  1361 float *double_to_float(size_t len, double *dvar)
 
  1366 	if (!(fvar = (float *)malloc(len * sizeof(float)))) {
 
  1367 		perror("double_to_float:fvar");
 
  1371 	for (i = 0; i < len; i++)
 
  1372 		fvar[i] = (float)dvar[i];
 
  1377 void write_var_hours(int ncid, int varid, nc_type nctype, size_t d1, size_t d2,
 
  1378 	int level, int ndims, double **var_hours)
 
  1380 	/* Output dimensions should be one larger than input dimensions */
 
  1382 	size_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
 
  1385 	if (nctype == NC_FLOAT) {
 
  1386 		if (!(fvar = (float *)malloc(d1 * d2 * sizeof(float)))) {
 
  1387 			perror("write_var_hours:fvar");
 
  1392 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
 
  1394 		count[0] = 1; /* time */
 
  1396 		count[1] = 1; /* hour */
 
  1397 		start[(ndims-2)] = 0;
 
  1398 		count[(ndims-2)] = d1;
 
  1399 		start[(ndims-1)] = 0;
 
  1400 		count[(ndims-1)] = d2;
 
  1407 				for (i = 0; i < (d1 * d2); i++)
 
  1408 					fvar[i] = (float)var_hours[hr][i];
 
  1409 				wrap_nc(nc_put_vara_float(ncid, varid, start, count, fvar));
 
  1412 				wrap_nc(nc_put_vara_double(ncid, varid, start, count, var_hours[hr]));
 
  1415 				fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
 
  1420 	if (nctype == NC_FLOAT)
 
  1426 void compute_stats(int nifnames, char **input_fnames, size_t nsamples)
 
  1429 	size_t d1, d2, **cell_samples;
 
  1430 	double **means, **stddevs;
 
  1431 	struct var *in_vnode, *out_vnode;
 
  1433 	if (input_var_head) {
 
  1434 		in_vnode = input_var_head;
 
  1435 		/* March through non-metadata variables to compute stats */
 
  1436 		for (j = 0; j == 0 || in_vnode != input_var_head; j++) {
 
  1437 			if (!in_vnode->metadata) {
 
  1438 				/* Make sure time is really the first dimension */
 
  1439 				if (strcmp((input_dim_idx[in_vnode->dimids[0]])->name, time_name)) {
 
  1440 					fprintf(stderr, "compute_stats: %s is not first dimension of variable %s!\n", time_name, in_vnode->name);
 
  1443 				/* Figure out input dimensions */
 
  1444 				if (in_vnode->ndims < 3 || in_vnode->ndims > 4) {
 
  1445 					fprintf(stderr, "compute_stats: %s has %d dimensions!\n", in_vnode->name, in_vnode->ndims);
 
  1448 				/* Assume 2D output; find dimensions */
 
  1449 				d1 = (input_dim_idx[in_vnode->dimids[(in_vnode->ndims-2)]])->len;
 
  1450 				d2 = (input_dim_idx[in_vnode->dimids[(in_vnode->ndims-1)]])->len;
 
  1451 				if (in_vnode->ndims == 3)
 
  1454 					nlevels = (input_dim_idx[in_vnode->dimids[1]])->len;
 
  1455 				/* Allocate working space for means and stddevs */
 
  1456 				alloc_means_stddevs(d1, d2, &means, &stddevs, &cell_samples);
 
  1457 				init_means_stddevs(d1, d2, means, stddevs, cell_samples, in_vnode->FillValue);
 
  1458 				printf("Computing statistics for %s\n",
 
  1460 				/* Compute means and stddevs, then write them */
 
  1461 				for (k = 0; k < nlevels; k++) {
 
  1462 					/* Read and compute means */
 
  1463 					read_and_compute(nifnames, input_fnames, d1, d2, in_vnode->name, in_vnode->nctype, k, in_vnode->ndims, in_vnode->FillFlag, in_vnode->FillValue, MEAN_TYPE, means, stddevs, cell_samples);
 
  1464 					/* Find corresponding output variable on the mean output file */
 
  1465 					out_vnode = varlist_find_by_name(mean_var_head, in_vnode->name);
 
  1466 					/* Write out the means for this variable */
 
  1467 					write_var_hours(mean_ncid, out_vnode->ncvarid, out_vnode->nctype, d1, d2, k, out_vnode->ndims, means);
 
  1468 					/* Zero out cell_samples so they can be used as a flag again for computing stddevs */
 
  1469 					reset_cell_samples(d1, d2, cell_samples);
 
  1470 					/* Read and compute stddevs using means */
 
  1471 					read_and_compute(nifnames, input_fnames, d1, d2, in_vnode->name, in_vnode->nctype, k, in_vnode->ndims, in_vnode->FillFlag, in_vnode->FillValue, STDDEV_TYPE, means, stddevs, cell_samples);
 
  1472 					/* Find corresponding output variable on the stddev output file */
 
  1473 					out_vnode = varlist_find_by_name(stddev_var_head, in_vnode->name);
 
  1474 					/* Write out stddevs for this variable */
 
  1475 					write_var_hours(stddev_ncid, out_vnode->ncvarid, out_vnode->nctype, d1, d2, k, out_vnode->ndims, stddevs);
 
  1478 				/* Free working space for means and stddevs */
 
  1479 				free_means_stddevs(means, stddevs, cell_samples);
 
  1481 			in_vnode = in_vnode->next;
 
  1487 void usage(char *program)
 
  1489 	fprintf(stderr, "Usage: %s -m mean.nc -s stddev.nc hist_file1.nc [ hist_file2.nc ... ]\n", program);
 
  1490 	fprintf(stderr, "WARNING: It is assumed that the list of input files is specified in time order!\n");
 
  1494 int main(int argc, char **argv)
 
  1496 	int i, ic, nifnames;
 
  1497 	size_t len, pos, nsamples;
 
  1498 	char *mfname, *sfname, **ifnames, *flist;
 
  1501 	mfname = sfname = flist = NULL;
 
  1505 	printf("Number of metadata variables (nmvars) = %d\n", nmvars);
 
  1509 	/* check command-line flags and switches */
 
  1510 	while ((ic = getopt(argc, argv, "m:s:")) != -1) {
 
  1513 				if (!(mfname = strdup(optarg))) {
 
  1519 				if (!(sfname = strdup(optarg))) {
 
  1528 		fprintf(stderr, "Output file name for writing means required.\n");
 
  1532 		fprintf(stderr, "Output file name for writing standard deviations required.\n");
 
  1536 	if (optind < argc) {
 
  1537 		if (!(ifnames = (char **)malloc((argc-optind+1)*sizeof(char *)))) {
 
  1541 		for (i = optind; i < argc; i++) {
 
  1542 			if (!(ifnames[nifnames++] = strdup(argv[i]))) {
 
  1543 				perror("ifnames[nifnames++]");
 
  1549 		fprintf(stderr, "At least one input file name is required.\n");
 
  1555 	 * Build list of input files to be included in the global history
 
  1556 	 * attribute on the two outputs files.
 
  1558 	for (i = len = 0; i < nifnames; i++)
 
  1559 		len += strlen(ifnames[i]);
 
  1560 	len += nifnames + 1; /* space in front of every name + null terminator */
 
  1561 	if (!(flist = (char *)malloc(len * sizeof(char)))) {
 
  1565 	for (i = pos = 0; i < nifnames; pos += (strlen(ifnames[i])+1), i++)
 
  1566 		sprintf(&flist[pos], " %s", ifnames[i]);
 
  1568 	printf("flist=%s\n", flist);
 
  1572 	/* Open every file to count up the number of time samples. */
 
  1573 	nsamples = count_samples(nifnames, ifnames);
 
  1575 	printf("Number of samples across all files = %ld\n", (long)nsamples);
 
  1580 	 * Open the *last* input file and the two output files (for mean and
 
  1581 	 * standard deviation).  Define dimensions, variables, and attributes
 
  1582 	 * for the two output files based on the *last* input files.  The
 
  1583 	 * last file is used because some metadata variables will contain
 
  1584 	 * only values from the last time slice from the period over which
 
  1585 	 * the statistics are computed.
 
  1587 	open_inout(ifnames[0], ifnames[(nifnames-1)], mfname, sfname, flist,
 
  1590 	compute_stats(nifnames, ifnames, nsamples);
 
  1592 	/* Close the two output files */
 
  1593 	wrap_nc(nc_close(mean_ncid));
 
  1594 	wrap_nc(nc_close(stddev_ncid));