h1_summary.c
author Forrest Hoffman <forrest@climatemodeling.org>
Mon, 01 Oct 2007 15:12:14 -0400
changeset 1 2ce4ee911439
parent 0 3c02cce30be8
child 3 d3122367777b
permissions -rw-r--r--
Fixed h1_summary and h1_summary2 to correctly construct time_bounds values.

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