h1_summary2.c
author Forrest Hoffman <forrest@climatemodeling.org>
Wed, 26 Sep 2007 17:16:40 -0400
changeset 0 3c02cce30be8
child 1 2ce4ee911439
permissions -rw-r--r--
Initial commit of post-processing codes for C-LAMP experiments

add_total_fields - modifies model output netCDF files to add fields computed
from fields within the files.

h1_summary - 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.

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