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