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