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

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

h1_summary_cb will be used for the remaining C-LAMP experiments, starting
with Experiment 2.2.
     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <string.h>
     5 #include <math.h>
     6 #include "netcdf.h"
     7 
     8 /*
     9  * Loop through one month of hourly history tapes from the Community Land Model
    10  * and generate monthly statistics (means and standard deviations) of fields
    11  * by hour of day.
    12  */
    13 
    14 #define	MEAN_TYPE	0
    15 #define STDDEV_TYPE	1
    16 
    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], mean_time;
   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 if (!strcmp(in_vnode->name, time_name)) {
   827 						/* force the timestamp to be the
   828 						 * mean of the time bounds */
   829 						mean_time = (tbounds[0] + tbounds[1]) / 2.0;
   830 						write_timeslice(out_ncid, out_vnode->ncvarid, out_vnode->nctype, in_vnode->ndims, in_vnode->dimids, in_dim_idx, &mean_time, 0);
   831 					}
   832 					else
   833 						write_timeslice(out_ncid, out_vnode->ncvarid, out_vnode->nctype, in_vnode->ndims, in_vnode->dimids, in_dim_idx, val, 0);
   834 				}
   835 				free(val);
   836 				/*
   837 				 * Just after the "time" variable, write out
   838 				 * the "hour" variable values.
   839 				 */
   840 				if (!strcmp(in_vnode->name, time_name)) {
   841 					out_vnode = varlist_find_by_name(out_var_head, hour_name);
   842 					write_var(out_ncid, out_vnode->ncvarid,
   843 						out_vnode->nctype, hr);
   844 				}
   845 			}
   846 			else {
   847 #ifdef DEBUG
   848 				printf("Skipping variable %s\n",
   849 					in_vnode->name);
   850 #endif /* DEBUG */
   851 			}
   852 			in_vnode = in_vnode->next;
   853 		}
   854 	}
   855 
   856 	return;
   857 }
   858 
   859 void get_time_bounds(char *first_fname, char *last_fname, double *tbounds)
   860 {
   861 	int time_dimid, time_bounds_varid;
   862 	size_t time_len, time_bounds_index[2];
   863 	double bnd1, bnd2;
   864 
   865 	/* Open first input file */
   866 	wrap_nc(nc_open(first_fname, NC_NOWRITE, &input_ncid));
   867 	/* Get dimension ID of the time dimension */
   868 	wrap_nc(nc_inq_dimid(input_ncid, time_name, &time_dimid));
   869 	/* Get length of time dimension */
   870 	wrap_nc(nc_inq_dimlen(input_ncid, time_dimid, &time_len));
   871 	/* Get variable ID of time_bounds variable */
   872 	wrap_nc(nc_inq_varid(input_ncid, time_bounds_name, &time_bounds_varid));
   873 	/* Set index for reading the first value of time_bounds from the
   874 	 * first timeslice of the first file */
   875 	time_bounds_index[0] = time_bounds_index[1] = 0;
   876 	/* Read the value */
   877 	wrap_nc(nc_get_var1_double(input_ncid, time_bounds_varid,
   878 		time_bounds_index, &bnd1));
   879 	/* If the first and last file are not the same, close the first one and
   880 	 * open the second one */
   881 	if (strcmp(first_fname, last_fname)) {
   882 		/* Close the first input file */
   883 		wrap_nc(nc_close(input_ncid));
   884 		/* Open the last input file */
   885 		wrap_nc(nc_open(last_fname, NC_NOWRITE, &input_ncid));
   886 		/* Get dimension ID of the time dimension */
   887 		wrap_nc(nc_inq_dimid(input_ncid, time_name, &time_dimid));
   888 		/* Get length of time dimension */
   889 		wrap_nc(nc_inq_dimlen(input_ncid, time_dimid, &time_len));
   890 		/* Get variable ID of time_bounds variable */
   891 		wrap_nc(nc_inq_varid(input_ncid, time_bounds_name,
   892 			&time_bounds_varid));
   893 	}
   894 	/* Set index for reading the second value of time_bounds from the
   895 	 * last timeslice of the last file */
   896 	time_bounds_index[0] = time_len - 1; time_bounds_index[1] = 1;
   897 	/* Read the value */
   898 	wrap_nc(nc_get_var1_double(input_ncid, time_bounds_varid,
   899 		time_bounds_index, &bnd2));
   900 
   901 	/* Close the last input file */
   902 	wrap_nc(nc_close(input_ncid));
   903 
   904 	tbounds[0] = bnd1;
   905 	tbounds[1] = bnd2;
   906 
   907 	return;
   908 }
   909 
   910 void open_inout(char *first_fname, char *last_fname, char *mean_fname, char *stddev_fname, char *flist, size_t nsamples)
   911 {
   912 	char *mean_history_gatt, *stddev_history_gatt,
   913 		*mean_prefix="Statistical means from history files:",
   914 		*stddev_prefix="Statistical standard deviations from history files:";
   915 	double tbounds[2];
   916 
   917 	/*
   918 	 * Construct strings for history global attributes for the two output
   919 	 * files.
   920 	 */
   921 	if (!(mean_history_gatt = (char *)malloc((strlen(mean_prefix) + strlen(flist)+1)*sizeof(char)))) {
   922 		perror("open_inout:mean_history_gatt");
   923 		exit(2);
   924 	}
   925 	if (!(stddev_history_gatt = (char *)malloc((strlen(stddev_prefix) + strlen(flist)+1)*sizeof(char)))) {
   926 		perror("open_inout:stddev_history_gatt");
   927 		exit(2);
   928 	}
   929 	sprintf(mean_history_gatt, "%s%s", mean_prefix, flist);
   930 	sprintf(stddev_history_gatt, "%s%s", stddev_prefix, flist);
   931 
   932 	/* The two time_bounds values must be handled explicitly by obtaining
   933 	 * the first one from the first file and the last one from the last
   934 	 * file.  These are then passed to copy_metadata() below. */
   935 	get_time_bounds(first_fname, last_fname, tbounds);
   936 #ifdef DEBUG
   937 	fprintf(stderr, "Got back tbounds=%lf,%lf\n", tbounds[0], tbounds[1]);
   938 #endif /* DEBUG */
   939 
   940 	/* Open last input file */
   941 	wrap_nc(nc_open(last_fname, NC_NOWRITE, &input_ncid));
   942 	/* Inquire about number of dimensions, variables, global attributes
   943 	 * and the ID of the unlimited dimension
   944 	 */
   945 	wrap_nc(nc_inq(input_ncid, &input_ndims, &input_nvars, &input_ngatts,
   946 		&input_unlimdimid));
   947 	/* Grab dimension IDs and lengths from input file */
   948 	get_dimensions(input_ncid, input_ndims, &input_dim_head, &input_dim_idx);
   949 	/* Grab desired global attributes from input file */
   950 	get_gatts(input_ncid, input_ngatts, &input_gatt_head);
   951 	/* Grab variable IDs and attributes from input file */
   952 	get_vars(input_ncid, input_nvars, &input_var_head);
   953 #ifdef DEBUG
   954 	varlist_print(input_var_head);
   955 #endif /* DEBUG */
   956 	/* Create netCDF files for monthly means and standard deviations */
   957 	/* Create new netCDF files */
   958 	wrap_nc(nc_create(mean_fname, NC_NOCLOBBER, &mean_ncid));
   959 	wrap_nc(nc_create(stddev_fname, NC_NOCLOBBER, &stddev_ncid));
   960 	/* Define dimensions */
   961 	mean_ndims = put_dimensions(input_dim_head, input_ndims,
   962 		input_unlimdimid, nsamples, &idid2mdid, mean_ncid,
   963 		&mean_dim_head, &mean_unlimdimid, &mean_hour_dimid);
   964 	stddev_ndims = put_dimensions(input_dim_head, input_ndims,
   965 		input_unlimdimid, nsamples, &idid2sdid, stddev_ncid,
   966 		&stddev_dim_head, &stddev_unlimdimid, &stddev_hour_dimid);
   967 	/* Define variables and their attributes */
   968 	mean_nvars = define_vars(input_ncid, input_dim_idx, input_var_head,
   969 		idid2mdid, mean_ncid, mean_hour_dimid, &mean_var_head,
   970 		MEAN_TYPE);
   971 	stddev_nvars = define_vars(input_ncid, input_dim_idx, input_var_head,
   972 		idid2sdid, stddev_ncid, stddev_hour_dimid, &stddev_var_head,
   973 		STDDEV_TYPE);
   974 	/* Store global attributes */
   975 	mean_ngatts = put_gatts(input_gatt_head, mean_ncid, mean_history_gatt);
   976 	stddev_ngatts = put_gatts(input_gatt_head, stddev_ncid,
   977 		stddev_history_gatt);
   978 	/* End define mode */
   979 	wrap_nc(nc_enddef(mean_ncid));
   980 	wrap_nc(nc_enddef(stddev_ncid));
   981 	/* Write out metdata variables that do not depend on "time" */
   982 	copy_metadata(input_ncid, input_var_head, input_dim_idx, mean_ncid,
   983 		mean_var_head, tbounds);
   984 	copy_metadata(input_ncid, input_var_head, input_dim_idx, stddev_ncid,
   985 		stddev_var_head, tbounds);
   986 
   987 	wrap_nc(nc_close(input_ncid));
   988 
   989 	return;
   990 }
   991 
   992 size_t count_samples(int nifnames, char **input_fnames)
   993 {
   994 	int i;
   995 	char name[NC_MAX_NAME+1];
   996 	size_t len, cnt = 0;
   997 
   998 	for (i = 0; i < nifnames; i++) {
   999 		/* Open input file */
  1000 		wrap_nc(nc_open(input_fnames[i], NC_NOWRITE, &input_ncid));
  1001 		/*
  1002 		 * Inquire about number of dimensions, variables, global
  1003 		 * attributes and the ID of the unlimited dimension
  1004 		 */
  1005 		wrap_nc(nc_inq(input_ncid, &input_ndims, &input_nvars,
  1006 			&input_ngatts, &input_unlimdimid));
  1007 		wrap_nc(nc_inq_dim(input_ncid, input_unlimdimid, name, &len));
  1008 		if (strcmp(name, time_name)) {
  1009 			fprintf(stderr, "%s is not the unlimited dimension for file %s!\n", time_name, input_fnames[i]);
  1010 			exit(4);
  1011 		}
  1012 #ifdef DEBUG
  1013 		printf("%ld samples in %s\n", (long)len, input_fnames[i]);
  1014 #endif /* DEBUG */
  1015 		wrap_nc(nc_close(input_ncid));
  1016 		cnt += len;
  1017 	}
  1018 	return cnt;
  1019 }
  1020 
  1021 void alloc_means_stddevs(size_t d1, size_t d2, double ***meansp, double ***stddevsp, size_t ***cell_samplesp)
  1022 {
  1023 	/*
  1024 	 * Allocate space for arrays of means and standard deviations by
  1025 	 * hour of day.
  1026 	 */
  1027 	int i;
  1028 	size_t **cell_samples;
  1029 	double **means, **stddevs;
  1030 
  1031 	if (!(*meansp = (double **)malloc(HOURS_PER_DAY * sizeof(double *)))) {
  1032 		perror("alloc_means_stddevs:*meansp");
  1033 		exit(2);
  1034 	}
  1035 	if (!(*stddevsp = (double **)malloc(HOURS_PER_DAY * sizeof(double *)))) {
  1036 		perror("alloc_means_stddevs:*stddevsp");
  1037 		exit(2);
  1038 	}
  1039 	if (!(*cell_samplesp = (size_t **)malloc(HOURS_PER_DAY * sizeof(size_t *)))) {
  1040 		perror("alloc_means_stddevs:*cell_samplesp");
  1041 		exit(2);
  1042 	}
  1043 
  1044 	means = *meansp;
  1045 	stddevs = *stddevsp;
  1046 	cell_samples = *cell_samplesp;
  1047 
  1048 	for (i = 0; i < HOURS_PER_DAY; i++) {
  1049 		if (!(means[i] = (double *)malloc(d1 * d2 * sizeof(double)))) {
  1050 			perror("alloc_means_stddevs:means[i]");
  1051 			exit(2);
  1052 		}
  1053 		if (!(stddevs[i] = (double *)malloc(d1 * d2 * sizeof(double)))) {
  1054 			perror("alloc_means_stddevs:stddevs[i]");
  1055 			exit(2);
  1056 		}
  1057 		if (!(cell_samples[i] = (size_t *)malloc(d1 * d2 * sizeof(size_t)))) {
  1058 			perror("alloc_means_stddevs:cell_samples[i]");
  1059 			exit(2);
  1060 		}
  1061 	}
  1062 
  1063 	return;
  1064 }
  1065 
  1066 void init_means_stddevs(size_t d1, size_t d2, double **means, double **stddevs, size_t **cell_samples, float FillValue)
  1067 {
  1068 	int hr, i, j, pos;
  1069 
  1070 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1071 		for (i = 0; i < d1; i++) {
  1072 #pragma _CRI concurrent
  1073 			for (j = 0; j < d2; j++) {
  1074 				pos = i*d2+j;
  1075 				means[hr][pos] = FillValue;
  1076 				stddevs[hr][pos] = FillValue;
  1077 				cell_samples[hr][pos] = 0;
  1078 			}
  1079 		}
  1080 	}
  1081 
  1082 	return;
  1083 }
  1084 
  1085 void reset_cell_samples(size_t d1, size_t d2, size_t **cell_samples)
  1086 {
  1087 	int i, j, hr, pos;
  1088 
  1089 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1090 		for (i = 0; i < d1; i++) {
  1091 #pragma _CRI concurrent
  1092 			for (j = 0; j < d2; j++) {
  1093 				pos = i*d2+j;
  1094 				cell_samples[hr][pos] = 0;
  1095 			}
  1096 		}
  1097 	}
  1098 
  1099 	return;
  1100 }
  1101 
  1102 void free_means_stddevs(double **means, double **stddevs, size_t **cell_samples)
  1103 {
  1104 	/*
  1105 	 * Free space from arrays of means and standard deviations by
  1106 	 * hour of day.
  1107 	 */
  1108 	int i;
  1109 
  1110 	for (i = 0; i < HOURS_PER_DAY; i++) {
  1111 		free(means[i]);
  1112 		free(stddevs[i]);
  1113 		free(cell_samples[i]);
  1114 	}
  1115 
  1116 	free(means);
  1117 	free(stddevs);
  1118 	free(cell_samples);
  1119 
  1120 	return;
  1121 }
  1122 
  1123 void add_to_means_float(float *val, int sec, size_t d1, size_t d2,
  1124 	char FillFlag, float FillValue, double **means, size_t **cell_samples)
  1125 {
  1126 	int i, j, hr, pos;
  1127 
  1128 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
  1129 
  1130 	for (i = 0; i < d1; i++) {
  1131 #pragma _CRI concurrent
  1132 		for (j = 0; j < d2; j++) {
  1133 			pos = i*d2+j;
  1134 			if (!FillFlag || (FillFlag && val[pos] != FillValue)) {
  1135 				if (cell_samples[hr][pos] == 0)
  1136 					means[hr][pos] = (double)val[pos];
  1137 				else
  1138 					means[hr][pos] += (double)val[pos];
  1139 				++cell_samples[hr][pos];
  1140 			}
  1141 		}
  1142 	}
  1143 
  1144 	return;
  1145 }
  1146 
  1147 void add_to_means_double(double *val, int sec, size_t d1, size_t d2,
  1148 	char FillFlag, float FillValue, double **means, size_t **cell_samples)
  1149 {
  1150 	int i, j, hr, pos;
  1151 
  1152 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
  1153 
  1154 	for (i = 0; i < d1; i++) {
  1155 #pragma _CRI concurrent
  1156 		for (j = 0; j < d2; j++) {
  1157 			pos = i*d2+j;
  1158 			if (!FillFlag || (FillFlag && val[pos] != FillValue)) {
  1159 				if (cell_samples[hr][pos] == 0)
  1160 					means[hr][pos] = val[pos];
  1161 				else
  1162 					means[hr][pos] += val[pos];
  1163 				++cell_samples[hr][pos];
  1164 			}
  1165 		}
  1166 	}
  1167 
  1168 	return;
  1169 }
  1170 
  1171 
  1172 void divide_means(size_t d1, size_t d2, double **means, size_t **cell_samples)
  1173 {
  1174 	int i, j, hr, pos;
  1175 
  1176 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1177 		for (i = 0; i < d1; i++) {
  1178 #pragma _CRI concurrent
  1179 			for (j = 0; j < d2; j++) {
  1180 				pos = i*d2+j;
  1181 				if (cell_samples[hr][pos] != 0) {
  1182 					means[hr][pos] /= (double)cell_samples[hr][pos];
  1183 				}
  1184 			}
  1185 		}
  1186 	}
  1187 
  1188 	return;
  1189 }
  1190 
  1191 void add_to_stddevs_float(float *val, int sec, size_t d1, size_t d2,
  1192 	char FillFlag, float FillValue, double **means, double **stddevs,
  1193 	size_t **cell_samples)
  1194 {
  1195 	int i, j, hr, pos;
  1196 	double delta;
  1197 
  1198 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
  1199 
  1200 	for (i = 0; i < d1; i++) {
  1201 #pragma _CRI concurrent
  1202 		for (j = 0; j < d2; j++) {
  1203 			pos = i*d2+j;
  1204 			if (!FillFlag || (FillFlag && val[pos] != FillValue
  1205 				&& means[hr][pos] != FillValue)) {
  1206 				delta = means[hr][pos] - (double)val[pos];
  1207 				if (cell_samples[hr][pos] == 0)
  1208 					stddevs[hr][pos] = delta * delta;
  1209 				else
  1210 					stddevs[hr][pos] += delta * delta;
  1211 				++cell_samples[hr][pos];
  1212 			}
  1213 		}
  1214 	}
  1215 
  1216 	return;
  1217 }
  1218 
  1219 void add_to_stddevs_double(double *val, int sec, size_t d1, size_t d2,
  1220 	char FillFlag, float FillValue, double **means, double **stddevs,
  1221 	size_t **cell_samples)
  1222 {
  1223 	int i, j, hr, pos;
  1224 	double delta;
  1225 
  1226 	hr = (int)floor((double)sec/(double)SEC_PER_HOUR);
  1227 
  1228 	for (i = 0; i < d1; i++) {
  1229 #pragma _CRI concurrent
  1230 		for (j = 0; j < d2; j++) {
  1231 			pos = i*d2+j;
  1232 			if (!FillFlag || (FillFlag && val[pos] != FillValue
  1233 				&& means[hr][pos] != FillValue)) {
  1234 				delta = means[hr][pos] - val[pos];
  1235 				if (cell_samples[hr][pos] == 0)
  1236 					stddevs[hr][pos] = delta * delta;
  1237 				else
  1238 					stddevs[hr][pos] += delta * delta;
  1239 				++cell_samples[hr][pos];
  1240 			}
  1241 		}
  1242 	}
  1243 
  1244 	return;
  1245 }
  1246 
  1247 
  1248 void divide_sqrt_stddevs(size_t d1, size_t d2, double **stddevs, size_t **cell_samples)
  1249 {
  1250 	int i, j, hr, pos;
  1251 
  1252 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1253 		for (i = 0; i < d1; i++) {
  1254 #pragma _CRI concurrent
  1255 			for (j = 0; j < d2; j++) {
  1256 				pos = i*d2+j;
  1257 				if (cell_samples[hr][pos] != 0) {
  1258 					stddevs[hr][pos] /= (double)cell_samples[hr][pos];
  1259 					stddevs[hr][pos] = sqrt(stddevs[hr][pos]);
  1260 				}
  1261 			}
  1262 		}
  1263 	}
  1264 
  1265 	return;
  1266 }
  1267 
  1268 
  1269 void read_and_compute(int nifnames, char **input_fnames, size_t d1, size_t d2, char *var_name, nc_type nctype, int level, int ndims, char FillFlag, float FillValue, int stat_type, double **means, double **stddevs, size_t **cell_samples)
  1270 {
  1271 	int i, ts;
  1272 	int varid, *mcsec;
  1273 	size_t len, start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
  1274 	char name[NC_MAX_NAME+1];
  1275 	void *val;
  1276 
  1277 	/* Allocate space for timeslice */
  1278 	val = alloc_var(nctype, (d1 * d2));
  1279 
  1280 	for (i = 0; i < nifnames; i++) {
  1281 #ifdef DEBUG
  1282 		printf("\tOpening %s", input_fnames[i]);
  1283 		if (ndims > 3) printf(" to retrieve level %d", level);
  1284 		if (stat_type == MEAN_TYPE)
  1285 			printf(" for computing means\n");
  1286 		else
  1287 			printf(" for computing stddevs\n");
  1288 #endif /* DEBUG */
  1289 		/* Open input file */
  1290 		wrap_nc(nc_open(input_fnames[i], NC_NOWRITE, &input_ncid));
  1291 		/*
  1292 		 * Inquire about number of dimensions, variables, global
  1293 		 * attributes and the ID of the unlimited dimension
  1294 		 */
  1295 		wrap_nc(nc_inq(input_ncid, &input_ndims, &input_nvars,
  1296 			&input_ngatts, &input_unlimdimid));
  1297 		wrap_nc(nc_inq_dim(input_ncid, input_unlimdimid, name, &len));
  1298 		if (strcmp(name, time_name)) {
  1299 			fprintf(stderr, "%s is not the unlimited dimension for file %s!\n", time_name, input_fnames[i]);
  1300 			exit(4);
  1301 		}
  1302 		/* Get seconds of day variable */
  1303 		wrap_nc(nc_inq_varid(input_ncid, mcsec_name, &varid));
  1304 		if (!(mcsec = (int *)malloc(len * sizeof(int)))) {
  1305 			perror("read_and_compute:mcsec");
  1306 			exit(2);
  1307 		}
  1308 		wrap_nc(nc_get_var_int(input_ncid, varid, mcsec));
  1309 		/* Get variable ID for requested variable */
  1310 		wrap_nc(nc_inq_varid(input_ncid, var_name, &varid));
  1311 		/* Retrieve time slice of desired variable */
  1312 		for (ts = 0; ts < len; ts++) {
  1313 			start[0] = ts;
  1314 			count[0] = 1;
  1315 			start[(ndims-2)] = 0;
  1316 			count[(ndims-2)] = d1;
  1317 			start[(ndims-1)] = 0;
  1318 			count[(ndims-1)] = d2;
  1319 			if (ndims == 4) {
  1320 				start[1] = level;
  1321 				count[1] = 1;
  1322 			}
  1323 			switch(nctype) {
  1324 				case NC_FLOAT:
  1325 					wrap_nc(nc_get_vara_float(input_ncid, varid, start, count, val));
  1326 					if (stat_type == MEAN_TYPE)
  1327 						add_to_means_float(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, cell_samples);
  1328 					else
  1329 						add_to_stddevs_float(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, stddevs, cell_samples);
  1330 					break;
  1331 				case NC_DOUBLE:
  1332 					wrap_nc(nc_get_vara_double(input_ncid, varid, start, count, val));
  1333 					if (stat_type == MEAN_TYPE)
  1334 						add_to_means_double(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, cell_samples);
  1335 					else
  1336 						add_to_stddevs_double(val, mcsec[ts], d1, d2, FillFlag, FillValue, means, stddevs, cell_samples);
  1337 					break;
  1338 				default:
  1339 					fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
  1340 					exit(3);
  1341 			}
  1342 		}
  1343 
  1344 		/* Free mcsec vector */
  1345 		free(mcsec);
  1346 		/* Close input file */
  1347 		wrap_nc(nc_close(input_ncid));
  1348 	}
  1349 	/* Divide sums by number of samples */
  1350 	if (stat_type == MEAN_TYPE)
  1351 		divide_means(d1, d2, means, cell_samples);
  1352 	else
  1353 		divide_sqrt_stddevs(d1, d2, stddevs, cell_samples);
  1354 
  1355 	/* Free working variable array */
  1356 	free(val);
  1357 
  1358 	return;
  1359 }
  1360 
  1361 float *double_to_float(size_t len, double *dvar)
  1362 {
  1363 	int i;
  1364 	float *fvar;
  1365 
  1366 	if (!(fvar = (float *)malloc(len * sizeof(float)))) {
  1367 		perror("double_to_float:fvar");
  1368 		exit(2);
  1369 	}
  1370 
  1371 	for (i = 0; i < len; i++)
  1372 		fvar[i] = (float)dvar[i];
  1373 
  1374 	return fvar;
  1375 }
  1376 
  1377 void write_var_hours(int ncid, int varid, nc_type nctype, size_t d1, size_t d2,
  1378 	int level, int ndims, double **var_hours)
  1379 {
  1380 	/* Output dimensions should be one larger than input dimensions */
  1381 	int i, hr;
  1382 	size_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
  1383 	float *fvar = NULL;
  1384 
  1385 	if (nctype == NC_FLOAT) {
  1386 		if (!(fvar = (float *)malloc(d1 * d2 * sizeof(float)))) {
  1387 			perror("write_var_hours:fvar");
  1388 			exit(2);
  1389 		}
  1390 	}
  1391 
  1392 	for (hr = 0; hr < HOURS_PER_DAY; hr++) {
  1393 		start[0] = 0;
  1394 		count[0] = 1; /* time */
  1395 		start[1] = hr;
  1396 		count[1] = 1; /* hour */
  1397 		start[(ndims-2)] = 0;
  1398 		count[(ndims-2)] = d1;
  1399 		start[(ndims-1)] = 0;
  1400 		count[(ndims-1)] = d2;
  1401 		if (ndims == 5) {
  1402 			start[2] = level;
  1403 			count[2] = 1;
  1404 		}
  1405 		switch (nctype) {
  1406 			case NC_FLOAT:
  1407 				for (i = 0; i < (d1 * d2); i++)
  1408 					fvar[i] = (float)var_hours[hr][i];
  1409 				wrap_nc(nc_put_vara_float(ncid, varid, start, count, fvar));
  1410 				break;
  1411 			case NC_DOUBLE:
  1412 				wrap_nc(nc_put_vara_double(ncid, varid, start, count, var_hours[hr]));
  1413 				break;
  1414 			default:
  1415 				fprintf(stderr, "netCDF external data type %d not supported\n", nctype);
  1416 				exit(3);
  1417 		}
  1418 	}
  1419 
  1420 	if (nctype == NC_FLOAT)
  1421 		free(fvar);
  1422 
  1423 	return;
  1424 }
  1425 
  1426 void compute_stats(int nifnames, char **input_fnames, size_t nsamples)
  1427 {
  1428 	int j, k, nlevels;
  1429 	size_t d1, d2, **cell_samples;
  1430 	double **means, **stddevs;
  1431 	struct var *in_vnode, *out_vnode;
  1432 
  1433 	if (input_var_head) {
  1434 		in_vnode = input_var_head;
  1435 		/* March through non-metadata variables to compute stats */
  1436 		for (j = 0; j == 0 || in_vnode != input_var_head; j++) {
  1437 			if (!in_vnode->metadata) {
  1438 				/* Make sure time is really the first dimension */
  1439 				if (strcmp((input_dim_idx[in_vnode->dimids[0]])->name, time_name)) {
  1440 					fprintf(stderr, "compute_stats: %s is not first dimension of variable %s!\n", time_name, in_vnode->name);
  1441 					exit(3);
  1442 				}
  1443 				/* Figure out input dimensions */
  1444 				if (in_vnode->ndims < 3 || in_vnode->ndims > 4) {
  1445 					fprintf(stderr, "compute_stats: %s has %d dimensions!\n", in_vnode->name, in_vnode->ndims);
  1446 					exit(3);
  1447 				}
  1448 				/* Assume 2D output; find dimensions */
  1449 				d1 = (input_dim_idx[in_vnode->dimids[(in_vnode->ndims-2)]])->len;
  1450 				d2 = (input_dim_idx[in_vnode->dimids[(in_vnode->ndims-1)]])->len;
  1451 				if (in_vnode->ndims == 3)
  1452 					nlevels = 1;
  1453 				else
  1454 					nlevels = (input_dim_idx[in_vnode->dimids[1]])->len;
  1455 				/* Allocate working space for means and stddevs */
  1456 				alloc_means_stddevs(d1, d2, &means, &stddevs, &cell_samples);
  1457 				init_means_stddevs(d1, d2, means, stddevs, cell_samples, in_vnode->FillValue);
  1458 				printf("Computing statistics for %s\n",
  1459 					in_vnode->name);
  1460 				/* Compute means and stddevs, then write them */
  1461 				for (k = 0; k < nlevels; k++) {
  1462 					/* Read and compute means */
  1463 					read_and_compute(nifnames, input_fnames, d1, d2, in_vnode->name, in_vnode->nctype, k, in_vnode->ndims, in_vnode->FillFlag, in_vnode->FillValue, MEAN_TYPE, means, stddevs, cell_samples);
  1464 					/* Find corresponding output variable on the mean output file */
  1465 					out_vnode = varlist_find_by_name(mean_var_head, in_vnode->name);
  1466 					/* Write out the means for this variable */
  1467 					write_var_hours(mean_ncid, out_vnode->ncvarid, out_vnode->nctype, d1, d2, k, out_vnode->ndims, means);
  1468 					/* Zero out cell_samples so they can be used as a flag again for computing stddevs */
  1469 					reset_cell_samples(d1, d2, cell_samples);
  1470 					/* Read and compute stddevs using means */
  1471 					read_and_compute(nifnames, input_fnames, d1, d2, in_vnode->name, in_vnode->nctype, k, in_vnode->ndims, in_vnode->FillFlag, in_vnode->FillValue, STDDEV_TYPE, means, stddevs, cell_samples);
  1472 					/* Find corresponding output variable on the stddev output file */
  1473 					out_vnode = varlist_find_by_name(stddev_var_head, in_vnode->name);
  1474 					/* Write out stddevs for this variable */
  1475 					write_var_hours(stddev_ncid, out_vnode->ncvarid, out_vnode->nctype, d1, d2, k, out_vnode->ndims, stddevs);
  1476 				}
  1477 
  1478 				/* Free working space for means and stddevs */
  1479 				free_means_stddevs(means, stddevs, cell_samples);
  1480 			}
  1481 			in_vnode = in_vnode->next;
  1482 		}
  1483 	}
  1484 	return;
  1485 }
  1486 
  1487 void usage(char *program)
  1488 {
  1489 	fprintf(stderr, "Usage: %s -m mean.nc -s stddev.nc hist_file1.nc [ hist_file2.nc ... ]\n", program);
  1490 	fprintf(stderr, "WARNING: It is assumed that the list of input files is specified in time order!\n");
  1491 	exit(4);
  1492 }
  1493 
  1494 int main(int argc, char **argv)
  1495 {
  1496 	int i, ic, nifnames;
  1497 	size_t len, pos, nsamples;
  1498 	char *mfname, *sfname, **ifnames, *flist;
  1499 
  1500 	ifnames = NULL;
  1501 	mfname = sfname = flist = NULL;
  1502 	nifnames = 0;
  1503 
  1504 #ifdef DEBUG
  1505 	printf("Number of metadata variables (nmvars) = %d\n", nmvars);
  1506 	fflush(stdout);
  1507 #endif /* DEBUG */
  1508 
  1509 	/* check command-line flags and switches */
  1510 	while ((ic = getopt(argc, argv, "m:s:")) != -1) {
  1511 		switch(ic) {
  1512 			case 'm':
  1513 				if (!(mfname = strdup(optarg))) {
  1514 					perror("mfname");
  1515 					exit(2);
  1516 				}
  1517 				break;
  1518 			case 's':
  1519 				if (!(sfname = strdup(optarg))) {
  1520 					perror("sfname");
  1521 					exit(2);
  1522 				}
  1523 				break;
  1524 		}
  1525 	}
  1526 
  1527 	if (!mfname) {
  1528 		fprintf(stderr, "Output file name for writing means required.\n");
  1529 		usage(argv[0]);
  1530 	}
  1531 	if (!sfname) {
  1532 		fprintf(stderr, "Output file name for writing standard deviations required.\n");
  1533 		usage(argv[0]);
  1534 	}
  1535 
  1536 	if (optind < argc) {
  1537 		if (!(ifnames = (char **)malloc((argc-optind+1)*sizeof(char *)))) {
  1538 			perror("ifnames");
  1539 			exit(2);
  1540 		}
  1541 		for (i = optind; i < argc; i++) {
  1542 			if (!(ifnames[nifnames++] = strdup(argv[i]))) {
  1543 				perror("ifnames[nifnames++]");
  1544 				exit(2);
  1545 			}
  1546 		}
  1547 	}
  1548 	else {
  1549 		fprintf(stderr, "At least one input file name is required.\n");
  1550 		usage(argv[0]);
  1551 	}
  1552 
  1553 
  1554 	/*
  1555 	 * Build list of input files to be included in the global history
  1556 	 * attribute on the two outputs files.
  1557 	 */
  1558 	for (i = len = 0; i < nifnames; i++)
  1559 		len += strlen(ifnames[i]);
  1560 	len += nifnames + 1; /* space in front of every name + null terminator */
  1561 	if (!(flist = (char *)malloc(len * sizeof(char)))) {
  1562 		perror("flist");
  1563 		exit(2);
  1564 	}
  1565 	for (i = pos = 0; i < nifnames; pos += (strlen(ifnames[i])+1), i++)
  1566 		sprintf(&flist[pos], " %s", ifnames[i]);
  1567 #ifdef DEBUG
  1568 	printf("flist=%s\n", flist);
  1569 	fflush(stdout);
  1570 #endif /* DEBUG */
  1571 
  1572 	/* Open every file to count up the number of time samples. */
  1573 	nsamples = count_samples(nifnames, ifnames);
  1574 #ifdef DEBUG
  1575 	printf("Number of samples across all files = %ld\n", (long)nsamples);
  1576 	fflush(stdout);
  1577 #endif /* DEBUG */
  1578 
  1579 	/*
  1580 	 * Open the *last* input file and the two output files (for mean and
  1581 	 * standard deviation).  Define dimensions, variables, and attributes
  1582 	 * for the two output files based on the *last* input files.  The
  1583 	 * last file is used because some metadata variables will contain
  1584 	 * only values from the last time slice from the period over which
  1585 	 * the statistics are computed.
  1586 	 */
  1587 	open_inout(ifnames[0], ifnames[(nifnames-1)], mfname, sfname, flist,
  1588 		nsamples);
  1589 
  1590 	compute_stats(nifnames, ifnames, nsamples);
  1591 
  1592 	/* Close the two output files */
  1593 	wrap_nc(nc_close(mean_ncid));
  1594 	wrap_nc(nc_close(stddev_ncid));
  1595 
  1596 	return 0;
  1597 }