h1_summary2.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 void alloc_all_samples(size_t d1, size_t d2, size_t nsamples, nc_type nctype, void ***valsp, int **mcsecp)
  1263 {
  1264 	int i;
  1265 	void **vals;
  1266 
  1267 	/* Allocate space for all timeslices */
  1268 	if (!(*valsp = (void **)malloc(nsamples * sizeof(void *)))) {
  1269 		perror("alloc_all_samples:*valsp");
  1270 		exit(2);
  1271 	}
  1272 	vals = *valsp;
  1273 	for (i = 0; i < nsamples; i++) {
  1274 		vals[i] = alloc_var(nctype, (d1 * d2));
  1275 	}
  1276 	if (!(*mcsecp = (int *)malloc(nsamples * sizeof(int)))) {
  1277 		perror("alloc_all_samples:*mcsecp");
  1278 		exit(2);
  1279 	}
  1280 
  1281 	return;
  1282 }
  1283 
  1284 void init_all_samples(size_t d1, size_t d2, size_t nsamples, nc_type nctype, void **vals, int *mcsec)
  1285 {
  1286 	int i, j;
  1287         float **fvals = NULL;
  1288 	double **dvals = NULL;
  1289 
  1290 	switch(nctype) {
  1291 		case NC_FLOAT:
  1292 			fvals = (float **)vals;
  1293 			break;
  1294 		case NC_DOUBLE:
  1295 			dvals = (double **)vals;
  1296 			break;
  1297 		default:
  1298 			fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
  1299 			exit(3);
  1300 	}
  1301 
  1302 	for (i = 0; i < nsamples; i++) {
  1303 		for (j = 0; j < (d1 * d2); j++) {
  1304 			switch(nctype) {
  1305 				case NC_FLOAT:
  1306 					fvals[i][j] = (float)0.0;
  1307 					break;
  1308 				case NC_DOUBLE:
  1309 					dvals[i][j] = (double)0.0;
  1310 					break;
  1311 				default:
  1312 					fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
  1313 					exit(3);
  1314 			}
  1315 		}
  1316 		mcsec[i] = 0;
  1317 	}
  1318 
  1319 	return;
  1320 }
  1321 
  1322 void free_all_samples(size_t nsamples, void **vals, int *mcsec)
  1323 {
  1324 	int i;
  1325 
  1326 	for (i = 0; i < nsamples; i++)
  1327 		if (vals[i]) free(vals[i]);
  1328 
  1329 	free(vals);
  1330 	free(mcsec);
  1331 
  1332 	return;
  1333 }
  1334 
  1335 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)
  1336 {
  1337 	int i, ts;
  1338 	int varid;
  1339 	size_t len, nslice = 0, start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
  1340 	char name[NC_MAX_NAME+1];
  1341 
  1342 	for (i = 0; i < nifnames; i++) {
  1343 #ifdef DEBUG
  1344 		printf("\tOpening %s", input_fnames[i]);
  1345 		if (ndims > 3) printf(" to retrieve level %d\n", level);
  1346 #endif /* DEBUG */
  1347 		/* Open input file */
  1348 		wrap_nc(nc_open(input_fnames[i], NC_NOWRITE, &input_ncid));
  1349 		/*
  1350 		 * Inquire about number of dimensions, variables, global
  1351 		 * attributes and the ID of the unlimited dimension
  1352 		 */
  1353 		wrap_nc(nc_inq(input_ncid, &input_ndims, &input_nvars,
  1354 			&input_ngatts, &input_unlimdimid));
  1355 		wrap_nc(nc_inq_dim(input_ncid, input_unlimdimid, name, &len));
  1356 		if (strcmp(name, time_name)) {
  1357 			fprintf(stderr, "%s is not the unlimited dimension for file %s!\n", time_name, input_fnames[i]);
  1358 			exit(4);
  1359 		}
  1360 		/* Make sure we don't run off the end of allocated space */
  1361 		if ((nslice+len) > nsamples) {
  1362 			fprintf(stderr, "Number of time slices exceeds previous count of %ld!\n", (long)nsamples);
  1363 			exit(4);
  1364 		}
  1365 		/* Get seconds of day variable */
  1366 		wrap_nc(nc_inq_varid(input_ncid, mcsec_name, &varid));
  1367 		wrap_nc(nc_get_var_int(input_ncid, varid, &mcsec[nslice]));
  1368 		/* Get variable ID for requested variable */
  1369 		wrap_nc(nc_inq_varid(input_ncid, var_name, &varid));
  1370 		/* Retrieve time slice of desired variable */
  1371 		for (ts = 0; ts < len; ts++) {
  1372 			start[0] = ts;
  1373 			count[0] = 1;
  1374 			start[(ndims-2)] = 0;
  1375 			count[(ndims-2)] = d1;
  1376 			start[(ndims-1)] = 0;
  1377 			count[(ndims-1)] = d2;
  1378 			if (ndims == 4) {
  1379 				start[1] = level;
  1380 				count[1] = 1;
  1381 			}
  1382 			switch(nctype) {
  1383 				case NC_FLOAT:
  1384 					wrap_nc(nc_get_vara_float(input_ncid, varid, start, count, vals[nslice]));
  1385 					break;
  1386 				case NC_DOUBLE:
  1387 					wrap_nc(nc_get_vara_double(input_ncid, varid, start, count, vals[nslice]));
  1388 					break;
  1389 				default:
  1390 					fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
  1391 					exit(3);
  1392 			}
  1393 			nslice++;
  1394 		}
  1395 
  1396 		/* Close input file */
  1397 		wrap_nc(nc_close(input_ncid));
  1398 	}
  1399 
  1400 	return;
  1401 }
  1402 
  1403 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)
  1404 {
  1405 	int i;
  1406 
  1407 	for (i = 0; i < nsamples; i++) {
  1408 		switch(nctype) {
  1409 			case NC_FLOAT:
  1410 				if (stat_type == MEAN_TYPE)
  1411 					add_to_means_float(vals[i], mcsec[i], d1, d2, FillFlag, FillValue, means, cell_samples);
  1412 				else
  1413 					add_to_stddevs_float(vals[i], mcsec[i], d1, d2, FillFlag, FillValue, means, stddevs, cell_samples);
  1414 				break;
  1415 			case NC_DOUBLE:
  1416 				if (stat_type == MEAN_TYPE)
  1417 					add_to_means_double(vals[i], mcsec[i], d1, d2, FillFlag, FillValue, means, cell_samples);
  1418 				else
  1419 					add_to_stddevs_double(vals[i], mcsec[i], d1, d2, FillFlag, FillValue, means, stddevs, cell_samples);
  1420 				break;
  1421 			default:
  1422 				fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
  1423 				exit(3);
  1424 		}
  1425 	}
  1426 
  1427 	/* Divide sums by number of samples */
  1428 	if (stat_type == MEAN_TYPE)
  1429 		divide_means(d1, d2, means, cell_samples);
  1430 	else
  1431 		divide_sqrt_stddevs(d1, d2, stddevs, cell_samples);
  1432 
  1433 	return;
  1434 }
  1435 
  1436 float *double_to_float(size_t len, double *dvar)
  1437 {
  1438 	int i;
  1439 	float *fvar;
  1440 
  1441 	if (!(fvar = (float *)malloc(len * sizeof(float)))) {
  1442 		perror("double_to_float:fvar");
  1443 		exit(2);
  1444 	}
  1445 
  1446 	for (i = 0; i < len; i++)
  1447 		fvar[i] = (float)dvar[i];
  1448 
  1449 	return fvar;
  1450 }
  1451 
  1452 void write_var_hours(int ncid, int varid, nc_type nctype, size_t d1, size_t d2,
  1453 	int level, int ndims, double **var_hours)
  1454 {
  1455 	/* Output dimensions should be one larger than input dimensions */
  1456 	int i, hr;
  1457 	size_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
  1458 	float *fvar = NULL;
  1459 
  1460 	if (nctype == NC_FLOAT) {
  1461 		if (!(fvar = (float *)malloc(d1 * d2 * sizeof(float)))) {
  1462 			perror("write_var_hours:fvar");
  1463 			exit(2);
  1464 		}
  1465 	}
  1466 
  1467 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1468 		start[0] = 0;
  1469 		count[0] = 1; /* time */
  1470 		start[1] = hr;
  1471 		count[1] = 1; /* hour */
  1472 		start[(ndims-2)] = 0;
  1473 		count[(ndims-2)] = d1;
  1474 		start[(ndims-1)] = 0;
  1475 		count[(ndims-1)] = d2;
  1476 		if (ndims == 5) {
  1477 			start[2] = level;
  1478 			count[2] = 1;
  1479 		}
  1480 		switch (nctype) {
  1481 			case NC_FLOAT:
  1482 				for (i = 0; i < (d1 * d2); i++)
  1483 					fvar[i] = (float)var_hours[hr][i];
  1484 				wrap_nc(nc_put_vara_float(ncid, varid, start, count, fvar));
  1485 				break;
  1486 			case NC_DOUBLE:
  1487 				wrap_nc(nc_put_vara_double(ncid, varid, start, count, var_hours[hr]));
  1488 				break;
  1489 			default:
  1490 				fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
  1491 				exit(3);
  1492 		}
  1493 	}
  1494 
  1495 	if (nctype == NC_FLOAT)
  1496 		free(fvar);
  1497 
  1498 	return;
  1499 }
  1500 
  1501 void compute_stats(int nifnames, char **input_fnames, size_t nsamples)
  1502 {
  1503 	int j, k, nlevels, *mcsec;
  1504 	size_t d1, d2, **cell_samples;
  1505 	double **means, **stddevs;
  1506 	struct var *in_vnode, *out_vnode;
  1507 	void **vals;
  1508 
  1509 	if (input_var_head) {
  1510 		in_vnode = input_var_head;
  1511 		/* March through non-metadata variables to compute stats */
  1512 		for (j = 0; j == 0 || in_vnode != input_var_head; j++) {
  1513 			if (!in_vnode->metadata) {
  1514 				/* Make sure time is really the first dimension */
  1515 				if (strcmp((input_dim_idx[in_vnode->dimids[0]])->name, time_name)) {
  1516 					fprintf(stderr, "compute_stats: %s is not first dimension of variable %s!\n", time_name, in_vnode->name);
  1517 					exit(3);
  1518 				}
  1519 				/* Figure out input dimensions */
  1520 				if (in_vnode->ndims < 3 || in_vnode->ndims > 4) {
  1521 					fprintf(stderr, "compute_stats: %s has %d dimensions!\n", in_vnode->name, in_vnode->ndims);
  1522 					exit(3);
  1523 				}
  1524 				/* Assume 2D output; find dimensions */
  1525 				d1 = (input_dim_idx[in_vnode->dimids[(in_vnode->ndims-2)]])->len;
  1526 				d2 = (input_dim_idx[in_vnode->dimids[(in_vnode->ndims-1)]])->len;
  1527 				if (in_vnode->ndims == 3)
  1528 					nlevels = 1;
  1529 				else
  1530 					nlevels = (input_dim_idx[in_vnode->dimids[1]])->len;
  1531 				/* Allocate working space for means and stddevs */
  1532 				alloc_means_stddevs(d1, d2, &means, &stddevs, &cell_samples);
  1533 				init_means_stddevs(d1, d2, means, stddevs, cell_samples, in_vnode->FillValue);
  1534 				/* Allocate and initize space for entire field across all files */
  1535 				alloc_all_samples(d1, d2, nsamples, in_vnode->nctype, &vals, &mcsec);
  1536 				init_all_samples(d1, d2, nsamples, in_vnode->nctype, vals, mcsec);
  1537 				printf("Computing statistics for %s\n",
  1538 					in_vnode->name);
  1539 				/* Compute means and stddevs, then write them */
  1540 				for (k = 0; k < nlevels; k++) {
  1541 					/* Read the entire fields from all files */
  1542 					read_all_samples(nifnames, input_fnames, d1, d2, nsamples, in_vnode->name, in_vnode->nctype, k, in_vnode->ndims, vals, mcsec);
  1543 					/* Compute means */
  1544 					compute(d1, d2, nsamples, in_vnode->nctype, vals, mcsec, in_vnode->FillFlag, in_vnode->FillValue, MEAN_TYPE, means, stddevs, cell_samples);
  1545 					/* Find corresponding output variable on the mean output file */
  1546 					out_vnode = varlist_find_by_name(mean_var_head, in_vnode->name);
  1547 					/* Write out the means for this variable */
  1548 					write_var_hours(mean_ncid, out_vnode->ncvarid, out_vnode->nctype, d1, d2, k, out_vnode->ndims, means);
  1549 					/* Zero out cell_samples so they can be used as a flag again for computing stddevs */
  1550 					reset_cell_samples(d1, d2, cell_samples);
  1551 					/* Compute stddevs using means */
  1552 					compute(d1, d2, nsamples, in_vnode->nctype, vals, mcsec, in_vnode->FillFlag, in_vnode->FillValue, STDDEV_TYPE, means, stddevs, cell_samples);
  1553 					/* Find corresponding output variable on the stddev output file */
  1554 					out_vnode = varlist_find_by_name(stddev_var_head, in_vnode->name);
  1555 					/* Write out stddevs for this variable */
  1556 					write_var_hours(stddev_ncid, out_vnode->ncvarid, out_vnode->nctype, d1, d2, k, out_vnode->ndims, stddevs);
  1557 				}
  1558 
  1559 				/* Free all samples */
  1560 				free_all_samples(nsamples, vals, mcsec);
  1561 				/* Free working space for means and stddevs */
  1562 				free_means_stddevs(means, stddevs, cell_samples);
  1563 			}
  1564 			in_vnode = in_vnode->next;
  1565 		}
  1566 	}
  1567 	return;
  1568 }
  1569 
  1570 void usage(char *program)
  1571 {
  1572 	fprintf(stderr, "Usage: %s -m mean.nc -s stddev.nc hist_file1.nc [ hist_file2.nc ... ]\n", program);
  1573 	fprintf(stderr, "WARNING: It is assumed that the list of input files is specified in time order!\n");
  1574 	exit(4);
  1575 }
  1576 
  1577 int main(int argc, char **argv)
  1578 {
  1579 	int i, ic, nifnames;
  1580 	size_t len, pos, nsamples;
  1581 	char *mfname, *sfname, **ifnames, *flist;
  1582 
  1583 	ifnames = NULL;
  1584 	mfname = sfname = flist = NULL;
  1585 	nifnames = 0;
  1586 
  1587 #ifdef DEBUG
  1588 	printf("Number of metadata variables (nmvars) = %d\n", nmvars);
  1589 	fflush(stdout);
  1590 #endif /* DEBUG */
  1591 
  1592 	/* check command-line flags and switches */
  1593 	while ((ic = getopt(argc, argv, "m:s:")) != -1) {
  1594 		switch(ic) {
  1595 			case 'm':
  1596 				if (!(mfname = strdup(optarg))) {
  1597 					perror("mfname");
  1598 					exit(2);
  1599 				}
  1600 				break;
  1601 			case 's':
  1602 				if (!(sfname = strdup(optarg))) {
  1603 					perror("sfname");
  1604 					exit(2);
  1605 				}
  1606 				break;
  1607 		}
  1608 	}
  1609 
  1610 	if (!mfname) {
  1611 		fprintf(stderr, "Output file name for writing means required.\n");
  1612 		usage(argv[0]);
  1613 	}
  1614 	if (!sfname) {
  1615 		fprintf(stderr, "Output file name for writing standard deviations required.\n");
  1616 		usage(argv[0]);
  1617 	}
  1618 
  1619 	if (optind < argc) {
  1620 		if (!(ifnames = (char **)malloc((argc-optind+1)*sizeof(char *)))) {
  1621 			perror("ifnames");
  1622 			exit(2);
  1623 		}
  1624 		for (i = optind; i < argc; i++) {
  1625 			if (!(ifnames[nifnames++] = strdup(argv[i]))) {
  1626 				perror("ifnames[nifnames++]");
  1627 				exit(2);
  1628 			}
  1629 		}
  1630 	}
  1631 	else {
  1632 		fprintf(stderr, "At least one input file name is required.\n");
  1633 		usage(argv[0]);
  1634 	}
  1635 
  1636 
  1637 	/*
  1638 	 * Build list of input files to be included in the global history
  1639 	 * attribute on the two outputs files.
  1640 	 */
  1641 	for (i = len = 0; i < nifnames; i++)
  1642 		len += strlen(ifnames[i]);
  1643 	len += nifnames + 1; /* space in front of every name + null terminator */
  1644 	if (!(flist = (char *)malloc(len * sizeof(char)))) {
  1645 		perror("flist");
  1646 		exit(2);
  1647 	}
  1648 	for (i = pos = 0; i < nifnames; pos += (strlen(ifnames[i])+1), i++)
  1649 		sprintf(&flist[pos], " %s", ifnames[i]);
  1650 #ifdef DEBUG
  1651 	printf("flist=%s\n", flist);
  1652 	fflush(stdout);
  1653 #endif /* DEBUG */
  1654 
  1655 	/* Open every file to count up the number of time samples. */
  1656 	nsamples = count_samples(nifnames, ifnames);
  1657 #ifdef DEBUG
  1658 	printf("Number of samples across all files = %ld\n", (long)nsamples);
  1659 	fflush(stdout);
  1660 #endif /* DEBUG */
  1661 
  1662 	/*
  1663 	 * Open the *last* input file and the two output files (for mean and
  1664 	 * standard deviation).  Define dimensions, variables, and attributes
  1665 	 * for the two output files based on the *last* input files.  The
  1666 	 * last file is used because some metadata variables will contain
  1667 	 * only values from the last time slice from the period over which
  1668 	 * the statistics are computed.
  1669 	 */
  1670 	open_inout(ifnames[0], ifnames[(nifnames-1)], mfname, sfname, flist,
  1671 		nsamples);
  1672 
  1673 	compute_stats(nifnames, ifnames, nsamples);
  1674 
  1675 	/* Close the two output files */
  1676 	wrap_nc(nc_close(mean_ncid));
  1677 	wrap_nc(nc_close(stddev_ncid));
  1678 
  1679 	return 0;
  1680 }