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

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