h1_summary_cb.c
author Forrest Hoffman <forrest@climatemodeling.org>
Wed, 10 Oct 2007 11:59:02 -0400
changeset 4 dd8e6719647b
permissions -rw-r--r--
Added hg_summary_cb, which writes statistical outputs using climatology_bounds

h1_summary_cb - computes means and standard deviations of hourly output
netCDF files, creating two new netCDF files (one for the means and one
for the standard deviations) for each month by hour of day, just like
h1_summary and h1_summary2. However, this version does not create a
new "hour" dimension on every output field. Instead, it follows the
CF-1.0 standard that requires a "climatology_bounds" variable (instead
of the normal "time_bounds" variable) and each hour-of-day mean/standard
deviation is stored as a time slice.

h1_summary_cb will be used for the remaining C-LAMP experiments, starting
with Experiment 2.2.
     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <string.h>
     5 #include <math.h>
     6 #include "netcdf.h"
     7 
     8 /*
     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
    11  * by hour of day.
    12  */
    13 
    14 #define	MEAN_TYPE	0
    15 #define STDDEV_TYPE	1
    16 #define NUM_TYPES	2
    17 
    18 #define HOURS_PER_DAY	24
    19 #define MIN_PER_HOUR	60
    20 #define SEC_PER_MIN	60
    21 #define SEC_PER_HOUR	(MIN_PER_HOUR * SEC_PER_MIN)
    22 
    23 static char *metadata_vars[] = {
    24 	"lon",
    25 	"lat",
    26 	"lonrof",
    27 	"latrof",
    28 	"time",
    29 	"hour", /* new metadata variable to be added to output files */
    30 	"levsoi",
    31 	"levlak",
    32 	"edgen",
    33 	"edgee",
    34 	"edges",
    35 	"edgew",
    36 	"longxy",
    37 	"latixy",
    38 	"area",
    39 	"areaupsc",
    40 	"topo",
    41 	"topodnsc",
    42 	"landfrac",
    43 	"landmask",
    44 	"pftmask",
    45 	"indxupsc",
    46 	"mcdate",
    47 	"mcsec",
    48 	"mdcur",
    49 	"mscur",
    50 	"nstep",
    51 	"time_bounds",
    52 	"date_written",
    53 	"time_written"
    54 };
    55 
    56 struct text_att {
    57 	char *name;
    58 	char *value;
    59 	struct text_att *next;
    60 	struct text_att *prev;
    61 };
    62 
    63 struct dim {
    64 	int dimid;
    65 	char *name;
    66 	size_t len;
    67 	struct dim *next;
    68 	struct dim *prev;
    69 };
    70 
    71 struct var {
    72 	int ncvarid;
    73 	char *name;
    74 	nc_type nctype;
    75 	int ndims;
    76 	int *dimids;
    77 	int natts;
    78 	char metadata;
    79 	char FillFlag;
    80 	float FillValue;
    81 	struct var *next;
    82 	struct var *prev;
    83 };
    84 
    85 static char *time_name = "time";
    86 static char *time_bounds_attname = "bounds";
    87 static char *time_bounds_name = "time_bounds";
    88 static char *climatology_bounds_attname = "climatology";
    89 static char *climatology_bounds_name = "climatology_bounds";
    90 static char *mcdate_name = "mcdate";
    91 static char *mcsec_name = "mcsec";
    92 static char *history_name = "history";
    93 static char *nsamples_name = "nsamples";
    94 static char *cell_method_name = "cell_method";
    95 static char *cell_methods_prefix[] = { "time: mean within days time: mean over days", "time: mean within days times: standard_deviation over days" };
    96 /* input stuff */
    97 static int nmvars = sizeof(metadata_vars)/sizeof(*metadata_vars);
    98 static int input_ncid, input_ndims, input_nvars, input_ngatts, input_unlimdimid;
    99 static struct text_att *input_gatt_head = NULL;
   100 static struct dim *input_dim_head = NULL, **input_dim_idx;
   101 static struct var *input_var_head = NULL;
   102 /* translation stuff */
   103 static int *idid2mdid, *idid2sdid; /* dimension IDs */
   104 /* output stuff */
   105 static int mean_ncid, mean_ndims, mean_nvars, mean_ngatts, mean_unlimdimid;
   106 static struct dim *mean_dim_head = NULL;
   107 static struct var *mean_var_head = NULL;
   108 static int stddev_ncid, stddev_ndims, stddev_nvars, stddev_ngatts, stddev_unlimdimid;
   109 static struct dim *stddev_dim_head = NULL;
   110 static struct var *stddev_var_head = NULL;
   111 /* output values */
   112 static double *times;
   113 static double *tbounds;
   114 static int *mcdate;
   115 static int *mcsec;
   116 
   117 char is_metadata(char *name)
   118 {
   119 	int i;
   120 
   121 	for (i = 0; i < nmvars && strcmp(name, metadata_vars[i]); i++);
   122 
   123 	if (i < nmvars)
   124 		return 1;
   125 	else
   126 		return 0;
   127 }
   128 
   129 #if 0
   130 struct dim *dimlist_find_by_name(struct dim *head, char *name)
   131 {
   132 	int i;
   133 	struct dim *p = NULL;
   134 
   135 	if (head) {
   136 		node = head;
   137 		for (i = 0 ; i == 0 || node != head; i++) {
   138 			if (!strcmp(node->name, name))
   139 				p = node;
   140 			node = node->next;
   141 		}
   142 		return p;
   143 	}
   144 	else
   145 		return NULL;
   146 }
   147 #endif
   148 
   149 struct var *varlist_find_by_name(struct var *head, char *name)
   150 {
   151 	int i;
   152 	struct var *node;
   153 
   154 	if (head) {
   155 		node = head;
   156 		for (i = 0 ; (i == 0 || node != head) && strcmp(node->name, name); i++)
   157 			node = node->next;
   158 		if (i && node == head)
   159 			return NULL;
   160 		else
   161 			return node;
   162 	}
   163 	else
   164 		return NULL;
   165 }
   166 
   167 void gattlist_add_node(struct text_att **headp, char *name, char *value)
   168 {
   169 	struct text_att *head, *node;
   170 
   171 	head = *headp;
   172 
   173 	if (!(node = (struct text_att *)malloc(sizeof(struct text_att)))) {
   174 		perror("gattlist_add_node");
   175 		exit(2);
   176 	}
   177 
   178 	if (!(node->name = strdup(name))) {
   179 		perror("gattlist_add_node");
   180 		exit(2);
   181 	}
   182 	if (!(node->value = strdup(value))) {
   183 		perror("gattlist_add_node");
   184 		exit(2);
   185 	}
   186 
   187 	if (head == NULL) {
   188 		/* first one */
   189 		*headp = node;
   190 		node->next = node;
   191 		node->prev = node;
   192 	}
   193 	else {
   194 		node->next = head;
   195 		node->prev = head->prev;
   196 		/* set this after setting node->prev from here */
   197 		head->prev = node;
   198 		/* set this after having set node->prev */
   199 		node->prev->next = node;
   200 	}
   201 
   202 	return;
   203 }
   204 
   205 struct dim *dimlist_add_node(struct dim **headp, int dimid, char *name, size_t len)
   206 {
   207 	struct dim *head, *node;
   208 
   209 	head = *headp;
   210 
   211 	if (!(node = (struct dim *)malloc(sizeof(struct dim)))) {
   212 		perror("dimlist_add_node");
   213 		exit(2);
   214 	}
   215 
   216 	node->dimid = dimid;
   217 	if (!(node->name = strdup(name))) {
   218 		perror("dimlist_add_node");
   219 		exit(2);
   220 	}
   221 	node->len = len;
   222 	
   223 	if (head == NULL) {
   224 		/* first one */
   225 		*headp = node;
   226 		node->next = node;
   227 		node->prev = node;
   228 	}
   229 	else {
   230 		node->next = head;
   231 		node->prev = head->prev;
   232 		/* set this after setting node->prev from here */
   233 		head->prev = node;
   234 		/* set this after having set node->prev */
   235 		node->prev->next = node;
   236 	}
   237 
   238 	return node;
   239 }
   240 
   241 void varlist_add_node(struct var **headp, int ncvarid, char *name,
   242 	nc_type nctype, int ndims, int *dimids, int natts, char FillFlag,
   243 	float FillValue)
   244 {
   245 	int i;
   246 	struct var *head, *node;
   247 
   248 	head = *headp;
   249 
   250 	if (!(node = (struct var *)malloc(sizeof(struct var)))) {
   251 		perror("varlist_add_node");
   252 		exit(3);
   253 	}
   254 
   255 	node->ncvarid = ncvarid;
   256 	if (!(node->name = strdup(name))) {
   257 		perror("varlist_add_node");
   258 		exit(3);
   259 	}
   260 	node->nctype = nctype;
   261 	node->ndims = ndims;
   262 	if (!(node->dimids = (int *)malloc(ndims * sizeof(int)))) {
   263 		perror("varlist_add_node");
   264 		exit(3);
   265 	}
   266 	for (i = 0; i < ndims; i++) node->dimids[i] = dimids[i];
   267 	node->natts = natts;
   268 	node->metadata = is_metadata(name);
   269 	node->FillFlag = FillFlag;
   270 	node->FillValue = FillValue;
   271 	
   272 	if (head == NULL) {
   273 		/* first one */
   274 		*headp = node;
   275 		node->next = node;
   276 		node->prev = node;
   277 	}
   278 	else {
   279 		node->next = head;
   280 		node->prev = head->prev;
   281 		/* set this after setting node->prev from here */
   282 		head->prev = node;
   283 		/* set this after having set node->prev */
   284 		node->prev->next = node;
   285 	}
   286 
   287 	return;
   288 }
   289 
   290 void varlist_print(struct var *headp)
   291 {
   292 	int i, j;
   293 	struct var *node;
   294 
   295 	printf("Beginning of Variable List\n");
   296 
   297 	if (headp) {
   298 		node = headp;
   299 		for (j = 0; j == 0 || node != headp; j++) {
   300 			printf("Variable %d (ptr=%ld):\n", j, (long)node);
   301 			printf("\tncvarid = %d\n", node->ncvarid);
   302 			printf("\tname = %s\n",  node->name);
   303 			printf("\tnctype = %d\n", node->nctype);
   304 			printf("\tndims = %d\n", node->ndims);
   305 			printf("\tdimids =");
   306 			for (i = 0; i < node->ndims; i++)
   307 				printf(" %d", node->dimids[i]);
   308 			printf("\n");
   309 			printf("\tnatts = %d\n", node->natts);
   310 			printf("\tmetadata = %d\n", (int)node->metadata);
   311 			printf("\tnext = %ld\n", (long)node->next);
   312 			printf("\tprev = %ld\n", (long)node->prev);
   313 			node = node->next;
   314 		}
   315 	}
   316 	else {
   317 		printf("\tList undefined\n");
   318 	}
   319 
   320 	printf("End of Variable List\n");
   321 
   322 	return;
   323 }
   324 
   325 void wrap_nc(int status)
   326 {
   327 	if (status != NC_NOERR) {
   328 		fprintf(stderr, "netCDF error: %s\n", nc_strerror(status));
   329 		exit(1);
   330 	}
   331 
   332 	return;
   333 }
   334 
   335 void get_dimensions(int ncid, int ndims, struct dim **dim_headp, struct dim ***in_dim_idxp)
   336 {
   337 	int i;
   338 	char name[NC_MAX_NAME+1];
   339 	size_t len;
   340 	struct dim **in_dim_idx;
   341 
   342 	if (!(*in_dim_idxp = (struct dim **)malloc(ndims * sizeof(struct dim *)))) {
   343 		perror("*in_dim_idxp");
   344 		exit(3);
   345 	}
   346 	in_dim_idx = *in_dim_idxp;
   347 
   348 	for (i = 0; i < ndims; i++) {
   349 		wrap_nc(nc_inq_dim(ncid, i, name, &len));
   350 		in_dim_idx[i] = dimlist_add_node(dim_headp, i, name, len);
   351 	}
   352 
   353 	return;
   354 }
   355 void get_gatts(int ncid, int ngatts, struct text_att **gatt_headp)
   356 {
   357 	int i;
   358 	char name[NC_MAX_NAME+1], *value;
   359 	nc_type xtype;
   360 	size_t len;
   361 
   362 	for (i = 0; i < ngatts; i++) {
   363 		wrap_nc(nc_inq_attname(ncid, NC_GLOBAL, i, name));
   364 		wrap_nc(nc_inq_att(ncid, NC_GLOBAL, name, &xtype, &len));
   365 		if (xtype != NC_CHAR) {
   366 			fprintf(stderr, "Global attribute %s is not of type NC_CHAR\n", name);
   367 			exit(2);
   368 		}
   369 		if (!(value = (char *)malloc((len+1)*sizeof(char)))) {
   370 			perror("get_gatts: value");
   371 			exit(3);
   372 		}
   373 		wrap_nc(nc_get_att_text(ncid, NC_GLOBAL, name, value));
   374 		value[(len+1-1)] = '\0';
   375 		gattlist_add_node(gatt_headp, name, value);
   376 		free(value); /* because gattlist_add_node() duplicates it */
   377 	}
   378 
   379 	return;
   380 }
   381 
   382 void get_vars(int ncid, int nvars, struct var **var_headp)
   383 {
   384 	int i, ndims, dimids[NC_MAX_VAR_DIMS], natts;
   385 	size_t len;
   386 	float FillValue;
   387 	char name[NC_MAX_NAME+1], *fill_att_name = "_FillValue", FillFlag;
   388 	nc_type xtype, atype;
   389 
   390 	for (i = 0; i < nvars; i++) {
   391 		wrap_nc(nc_inq_var(ncid, i, name, &xtype, &ndims, dimids,
   392 			&natts));
   393 		/* Check for _FillValue */
   394 		FillFlag = 0;
   395 		FillValue = 0.;
   396 		if (nc_inq_att(ncid, i, fill_att_name, &atype, &len)
   397 			== NC_NOERR) {
   398 			if (atype == NC_FLOAT && len) {
   399 				wrap_nc(nc_get_att_float(ncid, i,
   400 					fill_att_name, &FillValue));
   401 				FillFlag = 1;
   402 			}
   403 		}
   404 		varlist_add_node(var_headp, i, name, xtype, ndims, dimids,
   405 			natts, FillFlag, FillValue);
   406 	}
   407 
   408 	return;
   409 }
   410 
   411 int put_dimensions(struct dim *in_dim_head, int in_ndims, int in_unlimdimid,
   412 	size_t nsamples, int **in2outp, int out_ncid,
   413 	struct dim **out_dim_headp, int *out_unlimdimidp)
   414 {
   415 	/*
   416 	 * Define dimensions on new files and build translation tables between
   417 	 * dimension IDs.
   418 	 */
   419 	int j, dimid, ndims, *in2out;
   420 	size_t len;
   421 	struct dim *dnode;
   422 
   423 	ndims = 0;
   424 
   425 	if (!(*in2outp = (int *)malloc((in_ndims+1)*sizeof(int)))) {
   426 		perror("put_dimensions:in2outp");
   427 		exit(3);
   428 	}
   429 	in2out = *in2outp;
   430 
   431 	if (in_dim_head) {
   432 		dnode = in_dim_head;
   433 		for (j = 0; j == 0 || dnode != in_dim_head; j++) {
   434 			if (dnode->dimid == in_unlimdimid)
   435 				len = NC_UNLIMITED;
   436 			else
   437 				len = dnode->len;
   438 			wrap_nc(nc_def_dim(out_ncid, dnode->name, len, &dimid));
   439 			dimlist_add_node(out_dim_headp, dimid, dnode->name, len);
   440 			++ndims;
   441 			in2out[dnode->dimid] = dimid;
   442 			if (dnode->dimid == in_unlimdimid)
   443 				*out_unlimdimidp = dimid;
   444 			/* Just after the "time" dimension, add the new
   445 			 * "nsamples" dimension. */
   446 			if (!strcmp(dnode->name, time_name)) {
   447 				wrap_nc(nc_def_dim(out_ncid, nsamples_name, nsamples, &dimid));
   448 				dimlist_add_node(out_dim_headp, dimid, nsamples_name, nsamples);
   449 				++ndims;
   450 			}
   451 			dnode = dnode->next;
   452 		}
   453 	}
   454 	else {
   455 		fprintf(stderr, "WARNING: No dimensions defined!\n");
   456 	}
   457 
   458 	return ndims;
   459 }
   460 
   461 int put_gatts(struct text_att *in_gatt_head, int out_ncid, char *out_history)
   462 {
   463 	/*
   464 	 * Write out global attributes matching those of the input file.
   465 	 * Change history attribute to the string passed in as an argument.
   466 	 */
   467 	int j, hflag = 0, ngatts;
   468 	char *value;
   469 	struct text_att *anode;
   470 
   471 	ngatts = 0;
   472 
   473 	if (in_gatt_head) {
   474 		anode = in_gatt_head;
   475 		for (j = 0; j == 0 || anode != in_gatt_head; j++) {
   476 			if (!strcmp(anode->name, history_name)) {
   477 				value = out_history;
   478 				++hflag;
   479 			}
   480 			else
   481 				value = anode->value;
   482 			wrap_nc(nc_put_att_text(out_ncid, NC_GLOBAL, anode->name, strlen(value), value));
   483 			++ngatts;
   484 			anode = anode->next;
   485 		}
   486 		/* If no history attribute on input, add one to the output */
   487 		if (!hflag) {
   488 			wrap_nc(nc_put_att_text(out_ncid, NC_GLOBAL, history_name, strlen(out_history), out_history));
   489 			++ngatts;
   490 		}
   491 	}
   492 	else {
   493 		fprintf(stderr, "WARNING: No global attributes defined!\n");
   494 	}
   495 
   496 	return ngatts;
   497 }
   498 
   499 int translate_dimids(int in_ndims, int *in_dimids, int *in2out, int *out_dimids)
   500 {
   501 	/*
   502 	 * Translate between input dimension IDs and those for the output file.
   503 	 */
   504 	int i, ndims;
   505 
   506 	for (i = ndims = 0; i < in_ndims; i++, ndims++)
   507 		out_dimids[ndims] = in2out[in_dimids[i]];
   508 
   509 	return ndims;
   510 }
   511 
   512 int copy_att(char metadata, int stat_type, int in_natts, int in_ncid,
   513 	int in_varid, int out_ncid, int out_varid, char **cell_methods)
   514 {
   515 	/* 
   516 	 * Copy the attributes of the input variable to those of the output
   517 	 * variable. If the variable is not a metadata variable, ensure that
   518 	 * the cell_method attribute is set correctly, depending on the output
   519 	 * file type.
   520 	 */
   521 
   522 	int i, natts;
   523 	char name[NC_MAX_NAME+1], cmflag = 0;
   524 
   525 	for (i = natts = 0; i < in_natts; i++, natts++) {
   526 		wrap_nc(nc_inq_attname(in_ncid, in_varid, i, name));
   527 		if (!strcmp(name, cell_method_name) && !metadata) {
   528 			wrap_nc(nc_put_att_text(out_ncid, out_varid, cell_method_name, strlen(cell_methods[stat_type]), cell_methods[stat_type]));
   529 			cmflag = 1;
   530 		}
   531 		else if (!strcmp(name, time_bounds_attname))
   532 			/* Change time_bounds to climatology_bounds */
   533 			wrap_nc(nc_put_att_text(out_ncid, out_varid, climatology_bounds_attname, strlen(climatology_bounds_name), climatology_bounds_name));
   534 		else
   535 			wrap_nc(nc_copy_att(in_ncid, in_varid, name, out_ncid, out_varid));
   536 	}
   537 	/*
   538 	 * If no cell_method attribute was specified for a non-metadata
   539 	 * variable on the input file, add the appropriate cell_method anyway
   540 	 * on the output file.
   541 	 */
   542 	if (!cmflag && !metadata) {
   543 		wrap_nc(nc_put_att_text(out_ncid, out_varid, cell_method_name, strlen(cell_methods[stat_type]), cell_methods[stat_type]));
   544 		natts++;
   545 	}
   546 
   547 	return natts;
   548 }
   549 
   550 int define_vars(int in_ncid, struct dim **in_dim_idx, struct var *in_var_head,
   551 	int *in2out, int out_ncid, struct var **out_var_headp,
   552 	char **cell_methods, int stat_type)
   553 {
   554 	/*
   555 	 * Define variables on output file and copy attributes from input file.
   556 	 * Return number of variables defined.
   557 	 */
   558 	int j, varid, nvars, ndims, dimids[NC_MAX_VAR_DIMS], natts;
   559 	struct var *vnode;
   560 
   561 	nvars = 0;
   562 
   563 	if (in_var_head) {
   564 		vnode = in_var_head;
   565 		/*
   566 		 * March through input variables creating (mostly) the same
   567 		 * variables on the output file.
   568 		 */
   569 		for (j = 0; j == 0 || vnode != in_var_head; j++) {
   570 			ndims = translate_dimids(vnode->ndims, vnode->dimids, in2out, dimids);
   571 			/* Create all normal data variables, but only the four
   572 			 * time-based metadata variables: time,
   573 			 * climatology_bounds, mcdate, and mcsec. */
   574 			if (!strcmp(vnode->name, time_name)) {
   575 				/* Magically promote "time" variable to
   576 				 * double precision on output file */
   577 				wrap_nc(nc_def_var(out_ncid, vnode->name, NC_DOUBLE, ndims, dimids, &varid));
   578 				natts = copy_att(vnode->metadata, stat_type, vnode->natts, in_ncid, vnode->ncvarid, out_ncid, varid, cell_methods);
   579 				varlist_add_node(out_var_headp, varid, vnode->name, NC_DOUBLE, ndims, dimids, natts, vnode->FillFlag, vnode->FillValue);
   580 				++nvars;
   581 				/* Create "climatology_bounds" right after
   582 				 * time and instead of "time_bounds", with no
   583 				 * attributes */
   584 				wrap_nc(nc_def_var(out_ncid, climatology_bounds_name, NC_DOUBLE, ndims, dimids, &varid));
   585 				natts = 0;
   586 				varlist_add_node(out_var_headp, varid, climatology_bounds_name, NC_DOUBLE, ndims, dimids, natts, vnode->FillFlag, vnode->FillValue);
   587 				++nvars;
   588 			}
   589 			else if (!vnode->metadata || !strcmp(vnode->name, mcdate_name) || !strcmp(vnode->name, mcsec_name) || (vnode->metadata && strcmp((in_dim_idx[vnode->dimids[0]])->name, time_name))) {
   590 				/* If it is not a metadata variable OR it is
   591 				 * mcdate OR it is mcsec OR (it is a metadata
   592 				 * variable that does not have time as its
   593 				 * first dimension), then create it */
   594 				wrap_nc(nc_def_var(out_ncid, vnode->name, vnode->nctype, ndims, dimids, &varid));
   595 				natts = copy_att(vnode->metadata, stat_type, vnode->natts, in_ncid, vnode->ncvarid, out_ncid, varid, cell_methods);
   596 				varlist_add_node(out_var_headp, varid, vnode->name, vnode->nctype, ndims, dimids, natts, vnode->FillFlag, vnode->FillValue);
   597 				++nvars;
   598 			}
   599 			vnode = vnode->next;
   600 		}
   601 	}
   602 	else {
   603 		fprintf(stderr, "WARNING: No variables defined!\n");
   604 	}
   605 
   606 	return nvars;
   607 }
   608 
   609 void *alloc_var(nc_type nctype, size_t len)
   610 {
   611 	void *val;
   612 
   613 	switch (nctype) {
   614 		case NC_FLOAT:
   615 			if (!(val = (float *)malloc((len) * sizeof(float)))) {
   616 				perror("alloc_var: val");
   617 				exit(3);
   618 			}
   619 			break;
   620 		case NC_INT:
   621 			if (!(val = (int *)malloc((len) * sizeof(int)))) {
   622 				perror("alloc_var: val");
   623 				exit(3);
   624 			}
   625 			break;
   626 		case NC_DOUBLE:
   627 			if (!(val = (double *)malloc((len) * sizeof(double)))) {
   628 				perror("alloc_var: val");
   629 				exit(3);
   630 			}
   631 			break;
   632 		case NC_CHAR:
   633 			if (!(val = (char *)malloc(((len)+1) * sizeof(char)))) {
   634 				perror("alloc_var: val");
   635 				exit(3);
   636 			}
   637 			break;
   638 		default:
   639 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
   640 			exit(3);
   641 	}
   642 
   643 	return val;
   644 }
   645 
   646 void *read_var(int ncid, int varid, nc_type nctype, int ndims, int *dimids, struct dim **dim_idx)
   647 {
   648 	int i;
   649 	size_t len = (size_t)1;
   650 	void *val;
   651 
   652 	/* Figure out the total size */
   653 	for (i = 0; i < ndims; i++) len *= (dim_idx[dimids[i]])->len;
   654 
   655 	val = alloc_var(nctype, len);
   656 
   657 	switch (nctype) {
   658 		case NC_FLOAT:
   659 			wrap_nc(nc_get_var_float(ncid, varid, val));
   660 			break;
   661 		case NC_INT:
   662 			wrap_nc(nc_get_var_int(ncid, varid, val));
   663 			break;
   664 		case NC_DOUBLE:
   665 			wrap_nc(nc_get_var_double(ncid, varid, val));
   666 			break;
   667 		case NC_CHAR:
   668 			wrap_nc(nc_get_var_text(ncid, varid, val));
   669 			break;
   670 		default:
   671 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
   672 			exit(3);
   673 	}
   674 
   675 	return val;
   676 }
   677 
   678 void *read_timeslice(int ncid, int varid, nc_type nctype, int ndims, int *dimids, struct dim **dim_idx, size_t tslice)
   679 {
   680 	int i;
   681 	size_t len = (size_t)1, start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
   682 	void *val;
   683 
   684 	/* Make sure time is really the first dimension */
   685 	if (strcmp((dim_idx[dimids[0]])->name, time_name)) {
   686 		fprintf(stderr, "read_timeslice: %s is not first dimension of variable!\n", time_name);
   687 		exit(3);
   688 	} 
   689 	/*
   690 	 * Figure out the total size for the slice (start at second dimension)
   691 	 * and build start/count vectors.
   692 	 */
   693 	start[0] = tslice;
   694 	count[0] = 1;
   695 	for (i = 1; i < ndims; i++) {
   696 		start[i] = 0;
   697 		count[i] = (dim_idx[dimids[i]])->len;
   698 		len *= (dim_idx[dimids[i]])->len;
   699 	}
   700 
   701 	val = alloc_var(nctype, len);
   702 	
   703 	switch (nctype) {
   704 		case NC_FLOAT:
   705 			wrap_nc(nc_get_vara_float(ncid, varid, start, count, val));
   706 			break;
   707 		case NC_INT:
   708 			wrap_nc(nc_get_vara_int(ncid, varid, start, count, val));
   709 			break;
   710 		case NC_DOUBLE:
   711 			wrap_nc(nc_get_vara_double(ncid, varid, start, count, val));
   712 			break;
   713 		case NC_CHAR:
   714 			wrap_nc(nc_get_vara_text(ncid, varid, start, count, val));
   715 			break;
   716 		default:
   717 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
   718 			exit(3);
   719 	}
   720 
   721 	return val;
   722 }
   723 
   724 void write_var(int ncid, int varid, nc_type nctype, void *val)
   725 {
   726 	switch (nctype) {
   727 		case NC_FLOAT:
   728 			wrap_nc(nc_put_var_float(ncid, varid, val));
   729 			break;
   730 		case NC_INT:
   731 			wrap_nc(nc_put_var_int(ncid, varid, val));
   732 			break;
   733 		case NC_DOUBLE:
   734 			wrap_nc(nc_put_var_double(ncid, varid, val));
   735 			break;
   736 		case NC_CHAR:
   737 			wrap_nc(nc_put_var_text(ncid, varid, val));
   738 			break;
   739 		default:
   740 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
   741 			exit(3);
   742 	}
   743 
   744 	return;
   745 }
   746 
   747 void write_timeslice(int ncid, int varid, nc_type nctype, int ndims, int *dimids, struct dim **dim_idx, void *val, size_t tslice)
   748 {
   749 	int i;
   750 	size_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
   751 
   752 	/* Make sure time is really the first dimension */
   753 	if (strcmp((dim_idx[dimids[0]])->name, time_name)) {
   754 		fprintf(stderr, "write_timeslice: %s is not first dimension of variable!\n", time_name);
   755 		exit(3);
   756 	}
   757 	
   758 	/* Build start/count vectors */
   759 	start[0] = tslice;
   760 	count[0] = 1;
   761 	for (i = 1; i < ndims; i++) {
   762 		start[i] = 0;
   763 		count[i] = (dim_idx[dimids[i]])->len;
   764 	}
   765 
   766 	switch (nctype) {
   767 		case NC_FLOAT:
   768 			wrap_nc(nc_put_vara_float(ncid, varid, start, count, val));
   769 			break;
   770 		case NC_INT:
   771 			wrap_nc(nc_put_vara_int(ncid, varid, start, count, val));
   772 			break;
   773 		case NC_DOUBLE:
   774 			wrap_nc(nc_put_vara_double(ncid, varid, start, count, val));
   775 			break;
   776 		case NC_CHAR:
   777 			wrap_nc(nc_put_vara_text(ncid, varid, start, count, val));
   778 			break;
   779 		default:
   780 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
   781 			exit(3);
   782 	}
   783 
   784 	return;
   785 }
   786 
   787 void write_time_var(int ncid, int varid, nc_type nctype, void *time_var)
   788 {
   789 	size_t start[1], count[1];
   790 
   791 	start[0] = 0;
   792 	count[0] = 24;
   793 
   794 	switch (nctype) {
   795 		case NC_FLOAT:
   796 			wrap_nc(nc_put_vara_float(ncid, varid, start, count, time_var));
   797 			break;
   798 		case NC_INT:
   799 			wrap_nc(nc_put_vara_int(ncid, varid, start, count, time_var));
   800 			break;
   801 		case NC_DOUBLE:
   802 			wrap_nc(nc_put_vara_double(ncid, varid, start, count, time_var));
   803 			break;
   804 		case NC_CHAR:
   805 			wrap_nc(nc_put_vara_text(ncid, varid, start, count, time_var));
   806 			break;
   807 		default:
   808 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
   809 			exit(3);
   810 	}
   811 
   812 	return;
   813 }
   814 
   815 void write_tbounds(int ncid, int varid, double *tbounds)
   816 {
   817 	size_t start[2], count[2];
   818 
   819 	start[0] = start[1] = 0;
   820 	count[0] = 24; count[1] = 2;
   821 
   822 	wrap_nc(nc_put_vara_double(ncid, varid, start, count, tbounds));
   823 
   824 	return;
   825 }
   826 
   827 void copy_metadata(int in_ncid, struct var *in_var_head,
   828 	struct dim **in_dim_idx, int out_ncid, struct var *out_var_head,
   829 	double *times, double *tbounds, int *mcdate, int *mcsec)
   830 {
   831 	int i;
   832 	struct var *in_vnode, *out_vnode;
   833 	void *val;
   834 
   835 	if (!in_var_head) return;
   836 
   837 	in_vnode = in_var_head;
   838 	/*
   839 	 * March through input variables to stuff metadata values into
   840 	 * the new output files. NOTE: The only time-based metadata
   841 	 * variables, explicitly handled, are: time, climatology_bounds,
   842 	 * mcdate, and mcsec.  These are passed in.
   843 	 */
   844 	for (i = 0; i == 0 || in_vnode != in_var_head; i++) {
   845 		if (in_vnode->metadata) {
   846 			/* Find the corresponding output variable */
   847 			if (!strcmp(in_vnode->name, time_bounds_name))
   848 				out_vnode = varlist_find_by_name(out_var_head, climatology_bounds_name);
   849 			else
   850 				out_vnode = varlist_find_by_name(out_var_head, in_vnode->name);
   851 			if (out_vnode) {
   852 				/* Read values from input and write to output */
   853 				if (strcmp((in_dim_idx[in_vnode->dimids[0]])->name, time_name)) {
   854 					/* "time" is not the first dimension,
   855 					 * so just copy the variable values */
   856 #ifdef DEBUG
   857 					printf("Copying metadata variable %s\n",
   858 						in_vnode->name);
   859 #endif /* DEBUG */
   860 					val = read_var(in_ncid, in_vnode->ncvarid, in_vnode->nctype, in_vnode->ndims, in_vnode->dimids, in_dim_idx);
   861 					write_var(out_ncid, out_vnode->ncvarid, out_vnode->nctype, val);
   862 					free(val);
   863 				}
   864 				else {
   865 					/* "time" is the first dimension, so
   866 					 * explicitly handle the "time",
   867 					 * "climatology_bounds", "mcdate", and
   868 					 * "mcsec" while dropping all others */
   869 					if (!strcmp(out_vnode->name, time_name)) {
   870 						/* Force time stamps to be
   871 						 * those computed previously,
   872 						 * and remember these are now
   873 						 * double precision. */
   874 #ifdef DEBUG
   875 						printf("Writing metadata variable %s\n", out_vnode->name);
   876 #endif /* DEBUG */
   877 						write_time_var(out_ncid, out_vnode->ncvarid, out_vnode->nctype, times);
   878 					}
   879 					if (!strcmp(out_vnode->name, climatology_bounds_name)) {
   880 						/* time_bounds are now
   881 						 * climatology_bounds and have
   882 						 * pre-computed values */
   883 #ifdef DEBUG
   884 						printf("Writing metadata variable %s\n", out_vnode->name);
   885 #endif /* DEBUG */
   886 						write_tbounds(out_ncid, out_vnode->ncvarid, tbounds);
   887 					}
   888 					else if (!strcmp(out_vnode->name, mcdate_name)) {
   889 						/* mcdate is pre-computed */
   890 #ifdef DEBUG
   891 						printf("Writing metadata variable %s\n", out_vnode->name);
   892 #endif /* DEBUG */
   893 						write_time_var(out_ncid, out_vnode->ncvarid, out_vnode->nctype, mcdate);
   894 					}
   895 					else if (!strcmp(out_vnode->name, mcsec_name)) {
   896 						/* mcsec is pre-computed */
   897 #ifdef DEBUG
   898 						printf("Writing metadata variable %s\n", out_vnode->name);
   899 #endif /* DEBUG */
   900 						write_time_var(out_ncid, out_vnode->ncvarid, out_vnode->nctype, mcsec);
   901 					}
   902 				}
   903 			}
   904 			else {
   905 #ifdef DEBUG
   906 				printf("Dropping metadata variable %s\n", in_vnode->name);
   907 #endif /* DEBUG */
   908 			}
   909 		}
   910 		else {
   911 #ifdef DEBUG
   912 			printf("Skipping non-metadata variable %s\n",
   913 				in_vnode->name);
   914 #endif /* DEBUG */
   915 		}
   916 #ifdef DEBUG
   917 		printf ("Done\n");
   918 #endif /* DEBUG */
   919 		in_vnode = in_vnode->next;
   920 	}
   921 
   922 	return;
   923 }
   924 
   925 void get_time_bounds(char *first_fname, char *last_fname, double *times, double *tbounds, int *mcdate, int *mcsec)
   926 {
   927 	int i, time_dimid, time_bounds_varid, mcdate_varid, mcsec_varid;
   928 	size_t time_len, time_bounds_start[2], time_bounds_count[2],
   929 		mc_start[1], mc_count[1];
   930 	double bnd1[24], bnd2[24], day1, day2;
   931 
   932 	/* Open first input file */
   933 	wrap_nc(nc_open(first_fname, NC_NOWRITE, &input_ncid));
   934 	/* Get dimension ID of the time dimension */
   935 	wrap_nc(nc_inq_dimid(input_ncid, time_name, &time_dimid));
   936 	/* Get length of time dimension */
   937 	wrap_nc(nc_inq_dimlen(input_ncid, time_dimid, &time_len));
   938 	/* Get variable ID of time_bounds variable */
   939 	wrap_nc(nc_inq_varid(input_ncid, time_bounds_name, &time_bounds_varid));
   940 	/* Set start/count for reading the left values of time_bounds from the
   941 	 * first 24 timeslices of the first file */
   942 	time_bounds_start[0] = time_bounds_start[1] = 0;
   943 	time_bounds_count[0] = 24; time_bounds_count[1] = 1;
   944 	/* Read the values */
   945 	wrap_nc(nc_get_vara_double(input_ncid, time_bounds_varid,
   946 		time_bounds_start, time_bounds_count, bnd1));
   947 	/* If the first and last file are not the same, close the first one and
   948 	 * open the second one */
   949 	if (strcmp(first_fname, last_fname)) {
   950 		/* Close the first input file */
   951 		wrap_nc(nc_close(input_ncid));
   952 		/* Open the last input file */
   953 		wrap_nc(nc_open(last_fname, NC_NOWRITE, &input_ncid));
   954 		/* Get dimension ID of the time dimension */
   955 		wrap_nc(nc_inq_dimid(input_ncid, time_name, &time_dimid));
   956 		/* Get length of time dimension */
   957 		wrap_nc(nc_inq_dimlen(input_ncid, time_dimid, &time_len));
   958 		/* Get variable ID of time_bounds variable */
   959 		wrap_nc(nc_inq_varid(input_ncid, time_bounds_name,
   960 			&time_bounds_varid));
   961 	}
   962 	/* Set start/count for reading the right values of time_bounds from the
   963 	 * last 24 timeslices of the last file */
   964 	time_bounds_start[0] = time_len - 24; time_bounds_start[1] = 1;
   965 	time_bounds_count[0] = 24; time_bounds_count[1] = 1;
   966 	/* Read the value */
   967 	wrap_nc(nc_get_vara_double(input_ncid, time_bounds_varid,
   968 		time_bounds_start, time_bounds_count, bnd2));
   969 	/* Read the last 24 mcdate and mcsec values */
   970 	wrap_nc(nc_inq_varid(input_ncid, mcdate_name, &mcdate_varid));
   971 	wrap_nc(nc_inq_varid(input_ncid, mcsec_name, &mcsec_varid));
   972 	mc_start[0] = time_len - 24;
   973 	mc_count[0] = 24;
   974 	wrap_nc(nc_get_vara_int(input_ncid, mcdate_varid, mc_start, mc_count,
   975 		mcdate));
   976 	wrap_nc(nc_get_vara_int(input_ncid, mcsec_varid, mc_start, mc_count,
   977 		mcsec));
   978 
   979 	/* Close the last input file */
   980 	wrap_nc(nc_close(input_ncid));
   981 
   982 	/* Store time bounds and compute new time stamps as midpoints */
   983 	for (i = 0; i < 24; i++) {
   984 		tbounds[(i*2)] = bnd1[i];
   985 		tbounds[(i*2+1)] = bnd2[i];
   986 		/* Pick one (integer) day near the midpoint of the bounds and
   987 		 * add in the fraction from the right bounds, so for January
   988 		 * the first hour-of-day value would be 15.0 (plus some year
   989 		 * offset) */
   990 		day1 = floor(bnd1[i]);
   991 		day2 = floor(bnd2[i]);
   992 		times[i] = floor((day1 + day2) / 2.0) + (bnd2[i]-day2);
   993 	}
   994 
   995 	return;
   996 }
   997 
   998 char **make_cell_methods(size_t nsamples)
   999 {
  1000 	int i;
  1001 	size_t len;
  1002 	char **cell_methods;
  1003 
  1004 	if (!(cell_methods = (char **)malloc((NUM_TYPES * sizeof(char *))))) {
  1005 		perror("make_cell_methods:cell_methods");
  1006 		exit(2);
  1007 	}
  1008 	for (i = 0; i < NUM_TYPES; i++) {
  1009 		len = strlen(cell_methods_prefix[i]) + 48;
  1010 		if (!(cell_methods[i] = (char *)malloc((len * sizeof(char))))) {
  1011 			perror("make_cell_methods:cell_methods[i]");
  1012 			exit(2);
  1013 		}
  1014 		sprintf(cell_methods[i], "%s (comment: %ld samples)",
  1015 			cell_methods_prefix[i], (long)nsamples);
  1016 	}
  1017 	return cell_methods;
  1018 }
  1019 
  1020 void open_inout(char *first_fname, char *last_fname, char *mean_fname, char *stddev_fname, char *flist, size_t nsamples)
  1021 {
  1022 	int i;
  1023 	char *mean_history_gatt, *stddev_history_gatt,
  1024 		*mean_prefix="Statistical means from history files:",
  1025 		*stddev_prefix="Statistical standard deviations from history files:",
  1026 		**cell_methods;
  1027 
  1028 	/*
  1029 	 * Construct strings for history global attributes for the two output
  1030 	 * files.
  1031 	 */
  1032 	if (!(mean_history_gatt = (char *)malloc((strlen(mean_prefix) + strlen(flist)+1)*sizeof(char)))) {
  1033 		perror("open_inout:mean_history_gatt");
  1034 		exit(2);
  1035 	}
  1036 	if (!(stddev_history_gatt = (char *)malloc((strlen(stddev_prefix) + strlen(flist)+1)*sizeof(char)))) {
  1037 		perror("open_inout:stddev_history_gatt");
  1038 		exit(2);
  1039 	}
  1040 	sprintf(mean_history_gatt, "%s%s", mean_prefix, flist);
  1041 	sprintf(stddev_history_gatt, "%s%s", stddev_prefix, flist);
  1042 
  1043 	/* Allocate space for time values for each hour of the day */
  1044 	if (!(times = (double *)malloc((24 * sizeof(double))))) {
  1045 		perror("open_inout:times");
  1046 		exit(2);
  1047 	}
  1048 	/* Allocate space for two time bounds values for each hour of the day */
  1049 	if (!(tbounds = (double *)malloc((24 * 2 * sizeof(double))))) {
  1050 		perror("open_inout:tbounds");
  1051 		exit(2);
  1052 	}
  1053 	/* Allocate space for current date and seconds for each hour of day */
  1054 	if (!(mcdate = (int *)malloc((24 * sizeof(int))))) {
  1055 		perror("open_inout:mcdate");
  1056 		exit(2);
  1057 	}
  1058 	if (!(mcsec = (int *)malloc((24 * sizeof(int))))) {
  1059 		perror("open_inout:mcsec");
  1060 		exit(2);
  1061 	}
  1062 
  1063 
  1064 	/* 
  1065 	 * Explicitly handle "time", "time_bounds" -> "climatology_bounds",
  1066 	 * mcdate, and mcsec metadata variables by constructing their values
  1067 	 * before doing anything else.
  1068 	 */
  1069 	get_time_bounds(first_fname, last_fname, times, tbounds, mcdate, mcsec);
  1070 #ifdef DEBUG
  1071 	for (i = 0; i < 24; i++)
  1072 		fprintf(stderr, "Hour of day=%d, time=%lf, tbounds=%lf,%lf mcdate=%d mcsec=%d\n", i, times[i], tbounds[(i*2)], tbounds[(i*2+1)], mcdate[i], mcsec[i]);
  1073 #endif /* DEBUG */
  1074 
  1075 	/* Open last input file */
  1076 	wrap_nc(nc_open(last_fname, NC_NOWRITE, &input_ncid));
  1077 	/* Inquire about number of dimensions, variables, global attributes
  1078 	 * and the ID of the unlimited dimension
  1079 	 */
  1080 	wrap_nc(nc_inq(input_ncid, &input_ndims, &input_nvars, &input_ngatts,
  1081 		&input_unlimdimid));
  1082 	/* Grab dimension IDs and lengths from input file */
  1083 	get_dimensions(input_ncid, input_ndims, &input_dim_head, &input_dim_idx);
  1084 	/* Grab desired global attributes from input file */
  1085 	get_gatts(input_ncid, input_ngatts, &input_gatt_head);
  1086 	/* Grab variable IDs and attributes from input file */
  1087 	get_vars(input_ncid, input_nvars, &input_var_head);
  1088 #ifdef DEBUG
  1089 	varlist_print(input_var_head);
  1090 #endif /* DEBUG */
  1091 	/* Create netCDF files for monthly means and standard deviations */
  1092 	/* Create new netCDF files */
  1093 	wrap_nc(nc_create(mean_fname, NC_NOCLOBBER, &mean_ncid));
  1094 	wrap_nc(nc_create(stddev_fname, NC_NOCLOBBER, &stddev_ncid));
  1095 	/* Define dimensions */
  1096 	mean_ndims = put_dimensions(input_dim_head, input_ndims,
  1097 		input_unlimdimid, nsamples, &idid2mdid, mean_ncid,
  1098 		&mean_dim_head, &mean_unlimdimid);
  1099 	stddev_ndims = put_dimensions(input_dim_head, input_ndims,
  1100 		input_unlimdimid, nsamples, &idid2sdid, stddev_ncid,
  1101 		&stddev_dim_head, &stddev_unlimdimid);
  1102 	/* Define cell_methods[], including number of samples */
  1103 	cell_methods = make_cell_methods(nsamples);
  1104 	/* Define variables and their attributes */
  1105 	mean_nvars = define_vars(input_ncid, input_dim_idx, input_var_head,
  1106 		idid2mdid, mean_ncid, &mean_var_head, cell_methods,
  1107 		MEAN_TYPE);
  1108 	stddev_nvars = define_vars(input_ncid, input_dim_idx, input_var_head,
  1109 		idid2sdid, stddev_ncid, &stddev_var_head, cell_methods,
  1110 		STDDEV_TYPE);
  1111 #ifdef DEBUG
  1112 	printf("Mean variables:\n");
  1113 	varlist_print(mean_var_head);
  1114 	printf("Stddev variables:\n");
  1115 	varlist_print(stddev_var_head);
  1116 #endif /* DEBUG */
  1117 	/* Store global attributes */
  1118 	mean_ngatts = put_gatts(input_gatt_head, mean_ncid, mean_history_gatt);
  1119 	stddev_ngatts = put_gatts(input_gatt_head, stddev_ncid,
  1120 		stddev_history_gatt);
  1121 	/* End define mode */
  1122 	wrap_nc(nc_enddef(mean_ncid));
  1123 	wrap_nc(nc_enddef(stddev_ncid));
  1124 	/* Write out metdata variables that do not depend on "time" */
  1125 	copy_metadata(input_ncid, input_var_head, input_dim_idx, mean_ncid,
  1126 		mean_var_head, times, tbounds, mcdate, mcsec);
  1127 	copy_metadata(input_ncid, input_var_head, input_dim_idx, stddev_ncid,
  1128 		stddev_var_head, times, tbounds, mcdate, mcsec);
  1129 
  1130 	wrap_nc(nc_close(input_ncid));
  1131 
  1132 	return;
  1133 }
  1134 
  1135 size_t count_samples(int nifnames, char **input_fnames)
  1136 {
  1137 	int i;
  1138 	char name[NC_MAX_NAME+1];
  1139 	size_t len, cnt = 0;
  1140 
  1141 	for (i = 0; i < nifnames; i++) {
  1142 		/* Open input file */
  1143 		wrap_nc(nc_open(input_fnames[i], NC_NOWRITE, &input_ncid));
  1144 		/*
  1145 		 * Inquire about number of dimensions, variables, global
  1146 		 * attributes and the ID of the unlimited dimension
  1147 		 */
  1148 		wrap_nc(nc_inq(input_ncid, &input_ndims, &input_nvars,
  1149 			&input_ngatts, &input_unlimdimid));
  1150 		wrap_nc(nc_inq_dim(input_ncid, input_unlimdimid, name, &len));
  1151 		if (strcmp(name, time_name)) {
  1152 			fprintf(stderr, "%s is not the unlimited dimension for file %s!\n", time_name, input_fnames[i]);
  1153 			exit(4);
  1154 		}
  1155 #ifdef DEBUG
  1156 		printf("%ld samples in %s\n", (long)len, input_fnames[i]);
  1157 #endif /* DEBUG */
  1158 		wrap_nc(nc_close(input_ncid));
  1159 		cnt += len;
  1160 	}
  1161 	return cnt;
  1162 }
  1163 
  1164 void alloc_means_stddevs(size_t d1, size_t d2, double ***meansp, double ***stddevsp, size_t ***cell_samplesp)
  1165 {
  1166 	/*
  1167 	 * Allocate space for arrays of means and standard deviations by
  1168 	 * hour of day.
  1169 	 */
  1170 	int i;
  1171 	size_t **cell_samples;
  1172 	double **means, **stddevs;
  1173 
  1174 	if (!(*meansp = (double **)malloc(HOURS_PER_DAY * sizeof(double *)))) {
  1175 		perror("alloc_means_stddevs:*meansp");
  1176 		exit(2);
  1177 	}
  1178 	if (!(*stddevsp = (double **)malloc(HOURS_PER_DAY * sizeof(double *)))) {
  1179 		perror("alloc_means_stddevs:*stddevsp");
  1180 		exit(2);
  1181 	}
  1182 	if (!(*cell_samplesp = (size_t **)malloc(HOURS_PER_DAY * sizeof(size_t *)))) {
  1183 		perror("alloc_means_stddevs:*cell_samplesp");
  1184 		exit(2);
  1185 	}
  1186 
  1187 	means = *meansp;
  1188 	stddevs = *stddevsp;
  1189 	cell_samples = *cell_samplesp;
  1190 
  1191 	for (i = 0; i < HOURS_PER_DAY; i++) {
  1192 		if (!(means[i] = (double *)malloc(d1 * d2 * sizeof(double)))) {
  1193 			perror("alloc_means_stddevs:means[i]");
  1194 			exit(2);
  1195 		}
  1196 		if (!(stddevs[i] = (double *)malloc(d1 * d2 * sizeof(double)))) {
  1197 			perror("alloc_means_stddevs:stddevs[i]");
  1198 			exit(2);
  1199 		}
  1200 		if (!(cell_samples[i] = (size_t *)malloc(d1 * d2 * sizeof(size_t)))) {
  1201 			perror("alloc_means_stddevs:cell_samples[i]");
  1202 			exit(2);
  1203 		}
  1204 	}
  1205 
  1206 	return;
  1207 }
  1208 
  1209 void init_means_stddevs(size_t d1, size_t d2, double **means, double **stddevs, size_t **cell_samples, float FillValue)
  1210 {
  1211 	int hr, i, j, pos;
  1212 
  1213 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1214 		for (i = 0; i < d1; i++) {
  1215 #pragma _CRI concurrent
  1216 			for (j = 0; j < d2; j++) {
  1217 				pos = i*d2+j;
  1218 				means[hr][pos] = FillValue;
  1219 				stddevs[hr][pos] = FillValue;
  1220 				cell_samples[hr][pos] = 0;
  1221 			}
  1222 		}
  1223 	}
  1224 
  1225 	return;
  1226 }
  1227 
  1228 void reset_cell_samples(size_t d1, size_t d2, size_t **cell_samples)
  1229 {
  1230 	int i, j, hr, pos;
  1231 
  1232 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1233 		for (i = 0; i < d1; i++) {
  1234 #pragma _CRI concurrent
  1235 			for (j = 0; j < d2; j++) {
  1236 				pos = i*d2+j;
  1237 				cell_samples[hr][pos] = 0;
  1238 			}
  1239 		}
  1240 	}
  1241 
  1242 	return;
  1243 }
  1244 
  1245 void free_means_stddevs(double **means, double **stddevs, size_t **cell_samples)
  1246 {
  1247 	/*
  1248 	 * Free space from arrays of means and standard deviations by
  1249 	 * hour of day.
  1250 	 */
  1251 	int i;
  1252 
  1253 	for (i = 0; i < HOURS_PER_DAY; i++) {
  1254 		free(means[i]);
  1255 		free(stddevs[i]);
  1256 		free(cell_samples[i]);
  1257 	}
  1258 
  1259 	free(means);
  1260 	free(stddevs);
  1261 	free(cell_samples);
  1262 
  1263 	return;
  1264 }
  1265 
  1266 void add_to_means_float(float *val, int sec, size_t d1, size_t d2,
  1267 	char FillFlag, float FillValue, double **means, size_t **cell_samples)
  1268 {
  1269 	int i, j, hr, pos;
  1270 
  1271 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
  1272 
  1273 	for (i = 0; i < d1; i++) {
  1274 #pragma _CRI concurrent
  1275 		for (j = 0; j < d2; j++) {
  1276 			pos = i*d2+j;
  1277 			if (!FillFlag || (FillFlag && val[pos] != FillValue)) {
  1278 				if (cell_samples[hr][pos] == 0)
  1279 					means[hr][pos] = (double)val[pos];
  1280 				else
  1281 					means[hr][pos] += (double)val[pos];
  1282 				++cell_samples[hr][pos];
  1283 			}
  1284 		}
  1285 	}
  1286 
  1287 	return;
  1288 }
  1289 
  1290 void add_to_means_double(double *val, int sec, size_t d1, size_t d2,
  1291 	char FillFlag, float FillValue, double **means, size_t **cell_samples)
  1292 {
  1293 	int i, j, hr, pos;
  1294 
  1295 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
  1296 
  1297 	for (i = 0; i < d1; i++) {
  1298 #pragma _CRI concurrent
  1299 		for (j = 0; j < d2; j++) {
  1300 			pos = i*d2+j;
  1301 			if (!FillFlag || (FillFlag && val[pos] != FillValue)) {
  1302 				if (cell_samples[hr][pos] == 0)
  1303 					means[hr][pos] = val[pos];
  1304 				else
  1305 					means[hr][pos] += val[pos];
  1306 				++cell_samples[hr][pos];
  1307 			}
  1308 		}
  1309 	}
  1310 
  1311 	return;
  1312 }
  1313 
  1314 
  1315 void divide_means(size_t d1, size_t d2, double **means, size_t **cell_samples)
  1316 {
  1317 	int i, j, hr, pos;
  1318 
  1319 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1320 		for (i = 0; i < d1; i++) {
  1321 #pragma _CRI concurrent
  1322 			for (j = 0; j < d2; j++) {
  1323 				pos = i*d2+j;
  1324 				if (cell_samples[hr][pos] != 0) {
  1325 					means[hr][pos] /= (double)cell_samples[hr][pos];
  1326 				}
  1327 			}
  1328 		}
  1329 	}
  1330 
  1331 	return;
  1332 }
  1333 
  1334 void add_to_stddevs_float(float *val, int sec, size_t d1, size_t d2,
  1335 	char FillFlag, float FillValue, double **means, double **stddevs,
  1336 	size_t **cell_samples)
  1337 {
  1338 	int i, j, hr, pos;
  1339 	double delta;
  1340 
  1341 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
  1342 
  1343 	for (i = 0; i < d1; i++) {
  1344 #pragma _CRI concurrent
  1345 		for (j = 0; j < d2; j++) {
  1346 			pos = i*d2+j;
  1347 			if (!FillFlag || (FillFlag && val[pos] != FillValue
  1348 				&& means[hr][pos] != FillValue)) {
  1349 				delta = means[hr][pos] - (double)val[pos];
  1350 				if (cell_samples[hr][pos] == 0)
  1351 					stddevs[hr][pos] = delta * delta;
  1352 				else
  1353 					stddevs[hr][pos] += delta * delta;
  1354 				++cell_samples[hr][pos];
  1355 			}
  1356 		}
  1357 	}
  1358 
  1359 	return;
  1360 }
  1361 
  1362 void add_to_stddevs_double(double *val, int sec, size_t d1, size_t d2,
  1363 	char FillFlag, float FillValue, double **means, double **stddevs,
  1364 	size_t **cell_samples)
  1365 {
  1366 	int i, j, hr, pos;
  1367 	double delta;
  1368 
  1369 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
  1370 
  1371 	for (i = 0; i < d1; i++) {
  1372 #pragma _CRI concurrent
  1373 		for (j = 0; j < d2; j++) {
  1374 			pos = i*d2+j;
  1375 			if (!FillFlag || (FillFlag && val[pos] != FillValue
  1376 				&& means[hr][pos] != FillValue)) {
  1377 				delta = means[hr][pos] - val[pos];
  1378 				if (cell_samples[hr][pos] == 0)
  1379 					stddevs[hr][pos] = delta * delta;
  1380 				else
  1381 					stddevs[hr][pos] += delta * delta;
  1382 				++cell_samples[hr][pos];
  1383 			}
  1384 		}
  1385 	}
  1386 
  1387 	return;
  1388 }
  1389 
  1390 
  1391 void divide_sqrt_stddevs(size_t d1, size_t d2, double **stddevs, size_t **cell_samples)
  1392 {
  1393 	int i, j, hr, pos;
  1394 
  1395 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1396 		for (i = 0; i < d1; i++) {
  1397 #pragma _CRI concurrent
  1398 			for (j = 0; j < d2; j++) {
  1399 				pos = i*d2+j;
  1400 				if (cell_samples[hr][pos] != 0) {
  1401 					stddevs[hr][pos] /= (double)cell_samples[hr][pos];
  1402 					stddevs[hr][pos] = sqrt(stddevs[hr][pos]);
  1403 				}
  1404 			}
  1405 		}
  1406 	}
  1407 
  1408 	return;
  1409 }
  1410 
  1411 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)
  1412 {
  1413 	int i, ts;
  1414 	int varid, *mcsec;
  1415 	size_t len, start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
  1416 	char name[NC_MAX_NAME+1];
  1417 	void *val;
  1418 
  1419 	/* Allocate space for timeslice */
  1420 	val = alloc_var(nctype, (d1 * d2));
  1421 
  1422 	for (i = 0; i < nifnames; i++) {
  1423 #ifdef DEBUG
  1424 		printf("\tOpening %s", input_fnames[i]);
  1425 		if (ndims > 3) printf(" to retrieve level %d", level);
  1426 		if (stat_type == MEAN_TYPE)
  1427 			printf(" for computing means\n");
  1428 		else
  1429 			printf(" for computing stddevs\n");
  1430 #endif /* DEBUG */
  1431 		/* Open input file */
  1432 		wrap_nc(nc_open(input_fnames[i], NC_NOWRITE, &input_ncid));
  1433 		/*
  1434 		 * Inquire about number of dimensions, variables, global
  1435 		 * attributes and the ID of the unlimited dimension
  1436 		 */
  1437 		wrap_nc(nc_inq(input_ncid, &input_ndims, &input_nvars,
  1438 			&input_ngatts, &input_unlimdimid));
  1439 		wrap_nc(nc_inq_dim(input_ncid, input_unlimdimid, name, &len));
  1440 		if (strcmp(name, time_name)) {
  1441 			fprintf(stderr, "%s is not the unlimited dimension for file %s!\n", time_name, input_fnames[i]);
  1442 			exit(4);
  1443 		}
  1444 		/* Get seconds of day variable */
  1445 		wrap_nc(nc_inq_varid(input_ncid, mcsec_name, &varid));
  1446 		if (!(mcsec = (int *)malloc(len * sizeof(int)))) {
  1447 			perror("read_and_compute:mcsec");
  1448 			exit(2);
  1449 		}
  1450 		wrap_nc(nc_get_var_int(input_ncid, varid, mcsec));
  1451 		/* Get variable ID for requested variable */
  1452 		wrap_nc(nc_inq_varid(input_ncid, var_name, &varid));
  1453 		/* Retrieve time slice of desired variable */
  1454 		for (ts = 0; ts < len; ts++) {
  1455 			start[0] = ts;
  1456 			count[0] = 1;
  1457 			start[(ndims-2)] = 0;
  1458 			count[(ndims-2)] = d1;
  1459 			start[(ndims-1)] = 0;
  1460 			count[(ndims-1)] = d2;
  1461 			if (ndims == 4) {
  1462 				start[1] = level;
  1463 				count[1] = 1;
  1464 			}
  1465 			switch(nctype) {
  1466 				case NC_FLOAT:
  1467 					wrap_nc(nc_get_vara_float(input_ncid, varid, start, count, val));
  1468 					if (stat_type == MEAN_TYPE)
  1469 						add_to_means_float(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, cell_samples);
  1470 					else
  1471 						add_to_stddevs_float(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, stddevs, cell_samples);
  1472 					break;
  1473 				case NC_DOUBLE:
  1474 					wrap_nc(nc_get_vara_double(input_ncid, varid, start, count, val));
  1475 					if (stat_type == MEAN_TYPE)
  1476 						add_to_means_double(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, cell_samples);
  1477 					else
  1478 						add_to_stddevs_double(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, stddevs, cell_samples);
  1479 					break;
  1480 				default:
  1481 					fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
  1482 					exit(3);
  1483 			}
  1484 		}
  1485 
  1486 		/* Free mcsec vector */
  1487 		free(mcsec);
  1488 		/* Close input file */
  1489 		wrap_nc(nc_close(input_ncid));
  1490 	}
  1491 	/* Divide sums by number of samples */
  1492 	if (stat_type == MEAN_TYPE)
  1493 		divide_means(d1, d2, means, cell_samples);
  1494 	else
  1495 		divide_sqrt_stddevs(d1, d2, stddevs, cell_samples);
  1496 
  1497 	/* Free working variable array */
  1498 	free(val);
  1499 
  1500 	return;
  1501 }
  1502 
  1503 float *double_to_float(size_t len, double *dvar)
  1504 {
  1505 	int i;
  1506 	float *fvar;
  1507 
  1508 	if (!(fvar = (float *)malloc(len * sizeof(float)))) {
  1509 		perror("double_to_float:fvar");
  1510 		exit(2);
  1511 	}
  1512 
  1513 	for (i = 0; i < len; i++)
  1514 		fvar[i] = (float)dvar[i];
  1515 
  1516 	return fvar;
  1517 }
  1518 
  1519 void write_var_hours(int ncid, int varid, nc_type nctype, size_t d1, size_t d2,
  1520 	int level, int ndims, double **var_hours)
  1521 {
  1522 	/* Output dimensions should be one larger than input dimensions */
  1523 	int i, hr;
  1524 	size_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
  1525 	float *fvar = NULL;
  1526 
  1527 	if (nctype == NC_FLOAT) {
  1528 		if (!(fvar = (float *)malloc(d1 * d2 * sizeof(float)))) {
  1529 			perror("write_var_hours:fvar");
  1530 			exit(2);
  1531 		}
  1532 	}
  1533 
  1534 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1535 		start[0] = hr;
  1536 		count[0] = 1; /* time */
  1537 		start[(ndims-2)] = 0;
  1538 		count[(ndims-2)] = d1;
  1539 		start[(ndims-1)] = 0;
  1540 		count[(ndims-1)] = d2;
  1541 		if (ndims == 4) {
  1542 			start[1] = level;
  1543 			count[1] = 1;
  1544 		}
  1545 		switch (nctype) {
  1546 			case NC_FLOAT:
  1547 				for (i = 0; i < (d1 * d2); i++)
  1548 					fvar[i] = (float)var_hours[hr][i];
  1549 				wrap_nc(nc_put_vara_float(ncid, varid, start, count, fvar));
  1550 				break;
  1551 			case NC_DOUBLE:
  1552 				wrap_nc(nc_put_vara_double(ncid, varid, start, count, var_hours[hr]));
  1553 				break;
  1554 			default:
  1555 				fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
  1556 				exit(3);
  1557 		}
  1558 	}
  1559 
  1560 	if (nctype == NC_FLOAT)
  1561 		free(fvar);
  1562 
  1563 	return;
  1564 }
  1565 
  1566 void compute_stats(int nifnames, char **input_fnames)
  1567 {
  1568 	int j, k, nlevels;
  1569 	size_t d1, d2, **cell_samples;
  1570 	double **means, **stddevs;
  1571 	struct var *in_vnode, *out_vnode;
  1572 
  1573 	if (input_var_head) {
  1574 		in_vnode = input_var_head;
  1575 		/* March through non-metadata variables to compute stats */
  1576 		for (j = 0; j == 0 || in_vnode != input_var_head; j++) {
  1577 			if (!in_vnode->metadata) {
  1578 				/* Make sure time is really the first dimension */
  1579 				if (strcmp((input_dim_idx[in_vnode->dimids[0]])->name, time_name)) {
  1580 					fprintf(stderr, "compute_stats: %s is not first dimension of variable %s!\n", time_name, in_vnode->name);
  1581 					exit(3);
  1582 				}
  1583 				/* Figure out input dimensions */
  1584 				if (in_vnode->ndims < 3 || in_vnode->ndims > 4) {
  1585 					fprintf(stderr, "compute_stats: %s has %d dimensions!\n", in_vnode->name, in_vnode->ndims);
  1586 					exit(3);
  1587 				}
  1588 				/* Assume 2D output; find dimensions */
  1589 				d1 = (input_dim_idx[in_vnode->dimids[(in_vnode->ndims-2)]])->len;
  1590 				d2 = (input_dim_idx[in_vnode->dimids[(in_vnode->ndims-1)]])->len;
  1591 				if (in_vnode->ndims == 3)
  1592 					nlevels = 1;
  1593 				else
  1594 					nlevels = (input_dim_idx[in_vnode->dimids[1]])->len;
  1595 				/* Allocate working space for means and stddevs */
  1596 				alloc_means_stddevs(d1, d2, &means, &stddevs, &cell_samples);
  1597 				init_means_stddevs(d1, d2, means, stddevs, cell_samples, in_vnode->FillValue);
  1598 				printf("Computing statistics for %s\n",
  1599 					in_vnode->name);
  1600 				/* Compute means and stddevs, then write them */
  1601 				for (k = 0; k < nlevels; k++) {
  1602 					/* Read and compute means */
  1603 					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);
  1604 					/* Find corresponding output variable on the mean output file */
  1605 					out_vnode = varlist_find_by_name(mean_var_head, in_vnode->name);
  1606 					/* Write out the means for this variable */
  1607 					write_var_hours(mean_ncid, out_vnode->ncvarid, out_vnode->nctype, d1, d2, k, out_vnode->ndims, means);
  1608 					/* Zero out cell_samples so they can be used as a flag again for computing stddevs */
  1609 					reset_cell_samples(d1, d2, cell_samples);
  1610 					/* Read and compute stddevs using means */
  1611 					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);
  1612 					/* Find corresponding output variable on the stddev output file */
  1613 					out_vnode = varlist_find_by_name(stddev_var_head, in_vnode->name);
  1614 					/* Write out stddevs for this variable */
  1615 					write_var_hours(stddev_ncid, out_vnode->ncvarid, out_vnode->nctype, d1, d2, k, out_vnode->ndims, stddevs);
  1616 				}
  1617 
  1618 				/* Free working space for means and stddevs */
  1619 				free_means_stddevs(means, stddevs, cell_samples);
  1620 			}
  1621 			in_vnode = in_vnode->next;
  1622 		}
  1623 	}
  1624 	return;
  1625 }
  1626 
  1627 void usage(char *program)
  1628 {
  1629 	fprintf(stderr, "Usage: %s -m mean.nc -s stddev.nc hist_file1.nc [ hist_file2.nc ... ]\n", program);
  1630 	fprintf(stderr, "WARNING: It is assumed that the list of input files is specified in time order!\n");
  1631 	exit(4);
  1632 }
  1633 
  1634 int main(int argc, char **argv)
  1635 {
  1636 	int i, ic, nifnames;
  1637 	size_t len, pos, nsamples;
  1638 	char *mfname, *sfname, **ifnames, *flist;
  1639 
  1640 	ifnames = NULL;
  1641 	mfname = sfname = flist = NULL;
  1642 	nifnames = 0;
  1643 
  1644 #ifdef DEBUG
  1645 	printf("Number of metadata variables (nmvars) = %d\n", nmvars);
  1646 	fflush(stdout);
  1647 #endif /* DEBUG */
  1648 
  1649 	/* check command-line flags and switches */
  1650 	while ((ic = getopt(argc, argv, "m:s:")) != -1) {
  1651 		switch(ic) {
  1652 			case 'm':
  1653 				if (!(mfname = strdup(optarg))) {
  1654 					perror("mfname");
  1655 					exit(2);
  1656 				}
  1657 				break;
  1658 			case 's':
  1659 				if (!(sfname = strdup(optarg))) {
  1660 					perror("sfname");
  1661 					exit(2);
  1662 				}
  1663 				break;
  1664 		}
  1665 	}
  1666 
  1667 	if (!mfname) {
  1668 		fprintf(stderr, "Output file name for writing means required.\n");
  1669 		usage(argv[0]);
  1670 	}
  1671 	if (!sfname) {
  1672 		fprintf(stderr, "Output file name for writing standard deviations required.\n");
  1673 		usage(argv[0]);
  1674 	}
  1675 
  1676 	if (optind < argc) {
  1677 		if (!(ifnames = (char **)malloc((argc-optind+1)*sizeof(char *)))) {
  1678 			perror("ifnames");
  1679 			exit(2);
  1680 		}
  1681 		for (i = optind; i < argc; i++) {
  1682 			if (!(ifnames[nifnames++] = strdup(argv[i]))) {
  1683 				perror("ifnames[nifnames++]");
  1684 				exit(2);
  1685 			}
  1686 		}
  1687 	}
  1688 	else {
  1689 		fprintf(stderr, "At least one input file name is required.\n");
  1690 		usage(argv[0]);
  1691 	}
  1692 
  1693 
  1694 	/*
  1695 	 * Build list of input files to be included in the global history
  1696 	 * attribute on the two outputs files.
  1697 	 */
  1698 	for (i = len = 0; i < nifnames; i++)
  1699 		len += strlen(ifnames[i]);
  1700 	len += nifnames + 1; /* space in front of every name + null terminator */
  1701 	if (!(flist = (char *)malloc(len * sizeof(char)))) {
  1702 		perror("flist");
  1703 		exit(2);
  1704 	}
  1705 	for (i = pos = 0; i < nifnames; pos += (strlen(ifnames[i])+1), i++)
  1706 		sprintf(&flist[pos], " %s", ifnames[i]);
  1707 #ifdef DEBUG
  1708 	printf("flist=%s\n", flist);
  1709 	fflush(stdout);
  1710 #endif /* DEBUG */
  1711 
  1712 	/* Open every file to count up the number of time samples. */
  1713 	nsamples = count_samples(nifnames, ifnames);
  1714 #ifdef DEBUG
  1715 	printf("Number of samples across all files = %ld\n", (long)nsamples);
  1716 	fflush(stdout);
  1717 #endif /* DEBUG */
  1718 
  1719 	/*
  1720 	 * Open the *last* input file and the two output files (for mean and
  1721 	 * standard deviation).  Define dimensions, variables, and attributes
  1722 	 * for the two output files based on the *last* input files.  The
  1723 	 * last file is used because some metadata variables will contain
  1724 	 * only values from the last time slice from the period over which
  1725 	 * the statistics are computed.
  1726 	 */
  1727 	open_inout(ifnames[0], ifnames[(nifnames-1)], mfname, sfname, flist,
  1728 		nsamples);
  1729 
  1730 	compute_stats(nifnames, ifnames);
  1731 
  1732 	/* Close the two output files */
  1733 	wrap_nc(nc_close(mean_ncid));
  1734 	wrap_nc(nc_close(stddev_ncid));
  1735 
  1736 	return 0;
  1737 }