ncdump and ncgen. See below.
go tutorialat the Ferret command prompt once you have Ferret working. Look at the SLP data you just downloaded
ncdump -h [filename] | more
netcdf magicsquare {
dimensions:
x = 3,
y = 3;
variables:
float sqr1(x,y);
int sqr2(x,y);
data:
sqr1 = 8.0,1.0,6.0,3.0,5.0,7.0,4.0,9.0,2.0;
sqr2 = 8,1,6,3,5,7,4,9,2;
}
warning: If you mess around writing cdl by hand (which you will if you use the method I describe here), be sure to put the dimensions in brackets for each variable. This is not
always caught as a syntax error and causes segmentation faults. float xdim; xdim:units = "furlong";which tells postprocessors that x is dimensioned in furlongs.
:Conventions="COARDS"which represents a claim that the whole file attempts to follow COARDS standards. Both of these attribute definitions come in the data section of the CDL.
ncdump [filename]Or, to just get the header information (typically more useful)
ncdump -h [filename]You will probably want to redirect the output to a file, which you will probably want to give the extension ".cdl". For instance:
ncdump -h ncep-mslp.nc > ncep-mslp.cdl
ncgen -o magsqr.nc magsqr.cdl ncdump magsqr.nc
ncgen can also generate your source code for you! Writing the CDL is all you need to do. For example:
ncgen -c magsqr.cdl > magsqr.creturns this source file in magsqr.c . This is C source that would write exactly the file you started with!
ncdump on a NetCDF file of a similar format to the one you want to make. nc_put_var_THINGwhere THING is a recognized data type. See the example below, or the automatically generated code here for example, or see the C programmer's manual section 7.6 for more details. (Fancier write methods are shown in sections 7.5 - 7.9 .)
When you automatically generated your code, it essentially produced a netCDF handle (here, ncid):
int stat = nc_create("c5.nc", NC_CLOBBER, &ncid);
and a NetCDF tag for each variable (here, drom_id):
drom_dims[0] = x_dim; drom_dims[1] = y_dim; stat = nc_def_var(ncid, "drom", NC_FLOAT, RANK_drom, drom_dims, &drom_id); check_err(stat,__LINE__,__FILE__);With these two things defined, you can now write your array:
nc_put_var_float(ncid,drom_id,&(drom[0][0])); check_err(stat,__LINE__,__FILE__);The above writes the float array where it goes. When you have written all your fields as above you This is the only code you have to write by hand!
Note, There's comparable routines for
nc_put_var_typewhere "type" is one ofbyte short int float double. If you prefer you can userealas a synonym forfloat.Note, in case you didn't know (I didn't) __LINE__ and __FILE__ are system constants provided by the preprocessor. This facilitates reporting of runtime errors.
ncdump -hncgen -c to get the endless setup code automatically.
I'm a fan of code generation in general. I don't think humans should write tedious code at all. So I'm using the ncgen tool here.
However, let me emphasize that the case below is simple enough that you can follow it as a prototype. You don't need to use ncgen if you prefer writing C directly. That said, I can't resist asserting that this is a perfect example of a case where using a code generator is a big win.
Here I build a couple of arrays. (Ignore the variable names, they are for my own peculiar amusement.)
float drom[4][4];
float bact[4][4];
main()
{
int i,j;
printf("hello\n");
for (i=0;i<4;++i)
for (j=0;j<4;++j)
{
drom[i][j] = 10. * i + j;
bact[i][j] = 10. * j + i;
}
}
I call it c5.cdl
netcdf camelsrc{
dimensions:
x = 4,
y = 4;
variables:
real drom(x,y);
real bact(x,y);
}
ncgen -c c5.cdl > c5src.c
/* Include standard and NetCDF libraries */
#include <stdio.h> #include <stdlib.h> #include <netcdf.h>/* Error handling routine */
void
check_err(const int stat, const int line, const char *file) {
if (stat != NC_NOERR) {
(void) fprintf(stderr, "line %d of %s: %s\n", line, file, nc_strerror(stat));
exit(1);
}
}
int
main() { /* create camelsrc.nc */
/* Set up all sorts of tags and handles. Notice there is no actual data in this program! This just creates the CDL header. */
int ncid; /* netCDF id */ /* dimension ids */ int x_dim; int y_dim; /* dimension lengths */ size_t x_len = 4; size_t y_len = 4; /* variable ids */ int drom_id; int bact_id; /* rank (number of dimensions) for each variable */ # define RANK_drom 2 # define RANK_bact 2 /* variable shapes */ int drom_dims[RANK_drom]; int bact_dims[RANK_bact];/* create the NetCDF handle, akin to opening the file */
/* enter define mode */
int stat = nc_create("camelsrc.nc", NC_CLOBBER, &ncid);
check_err(stat,__LINE__,__FILE__);
/* declare the variables */
/* define dimensions */ stat = nc_def_dim(ncid, "x", x_len, &x_dim); check_err(stat,__LINE__,__FILE__); stat = nc_def_dim(ncid, "y", y_len, &y_dim); check_err(stat,__LINE__,__FILE__); /* define variables */ drom_dims[0] = x_dim; drom_dims[1] = y_dim; stat = nc_def_var(ncid, "drom", NC_FLOAT, RANK_drom, drom_dims, &drom_id); check_err(stat,__LINE__,__FILE__); bact_dims[0] = x_dim; bact_dims[1] = y_dim; stat = nc_def_var(ncid, "bact", NC_FLOAT, RANK_bact, bact_dims, &bact_id); check_err(stat,__LINE__,__FILE__); /* leave define mode */ stat = nc_enddef (ncid); check_err(stat,__LINE__,__FILE__);/* now close the file and exit */
stat = nc_close(ncid); check_err(stat,__LINE__,__FILE__); return 0; }
nc_put_var_float(ncid,drom_id,&(drom[0][0])); check_err(stat,__LINE__,__FILE__); nc_put_var_float(ncid,bact_id,&(bact[0][0])); check_err(stat,__LINE__,__FILE__);
The above is the only new code I wrote by hand (not counting the original model)!
KEY:
- Original file (black)
- Autogenerated code (red)
- New code to write variables and close file (blue)
#include <stdio.h>
#include <stdlib.h>
#include <netcdf.h>
float drom[4][4];
float bact[4][4];
void
check_err(const int stat, const int line, const char *file) {
if (stat != NC_NOERR) {
(void) fprintf(stderr, "line %d of %s: %s\n", line, file, nc_strerror(stat));
exit(1);
}
}
main()
{
int i,j;
for (i=0;i<4;++i)
for (j=0;j<4;++j)
{
drom[i][j] = 10.*i + j;
bact[i][j] = 10.*j + i;
}
int ncid; /* netCDF id */
/* dimension ids */
int x_dim;
int y_dim;
/* dimension lengths */
size_t x_len = 4;
size_t y_len = 4;
/* variable ids */
int drom_id;
int bact_id;
/* rank (number of dimensions) for each variable */
# define RANK_drom 2
# define RANK_bact 2
/* variable shapes */
int drom_dims[RANK_drom];
int bact_dims[RANK_bact];
/* enter define mode */
int stat = nc_create("camelsrc.nc", NC_CLOBBER, &ncid);
check_err(stat,__LINE__,__FILE__);
/* define dimensions */
stat = nc_def_dim(ncid, "x", x_len, &x_dim);
check_err(stat,__LINE__,__FILE__);
stat = nc_def_dim(ncid, "y", y_len, &y_dim);
check_err(stat,__LINE__,__FILE__);
/* define variables */
drom_dims[0] = x_dim;
drom_dims[1] = y_dim;
stat = nc_def_var(ncid, "drom", NC_FLOAT, RANK_drom, drom_dims, &drom_id);
check_err(stat,__LINE__,__FILE__);
bact_dims[0] = x_dim;
bact_dims[1] = y_dim;
stat = nc_def_var(ncid, "bact", NC_FLOAT, RANK_bact, bact_dims, &bact_id);
check_err(stat,__LINE__,__FILE__);
/* leave define mode */
stat = nc_enddef(ncid);
check_err(stat,__LINE__,__FILE__);
/* now write them */
nc_put_var_float(ncid,drom_id,&(drom[0][0]));
check_err(stat,__LINE__,__FILE__);
nc_put_var_float(ncid,bact_id,&(bact[0][0]));
check_err(stat,__LINE__,__FILE__);
/* close the file */
stat = nc_close(ncid);
check_err(stat,__LINE__,__FILE__);
}