mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-12-21 08:39:46 +08:00
292 lines
10 KiB
C
292 lines
10 KiB
C
/*! \file
|
|
|
|
Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
|
2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,
|
|
2015, 2016, 2017, 2018
|
|
University Corporation for Atmospheric Research/Unidata.
|
|
|
|
See \ref copyright file for more info.
|
|
|
|
*/
|
|
|
|
/* This example program is part of Unidata's netCDF library for
|
|
scientific data access.
|
|
|
|
This program demonstrates various ways to create netCDF dimensions
|
|
and variables.
|
|
|
|
We will create a dataset with 4 variables. We'll store a 3D surface
|
|
temperature (lat x lon x timestep), 4D pressure (lat x lon x height x
|
|
timestep), a 2D initial temperature (lat x lon) and a 3D initial
|
|
pressure (lat x lon x height). All variables will be stored as
|
|
single precision floating point.
|
|
|
|
All variables are intended to share dimensions. For example, the
|
|
latitude axis is the same for all of them.
|
|
|
|
We'll also include the coordinate axis data for three of the four
|
|
dimensions, that is, labels for the lat, lon, and height axes.
|
|
|
|
Finally, we'll use some attributes to store some metadata about the
|
|
variables, the units. Also we'll use a file-level, or global,
|
|
attribute to record some information about the dataset as a whole.
|
|
|
|
Ed Hartnett, 6/3/4
|
|
$Id: met4D.c,v 1.1 2004/07/26 14:04:42 ed Exp $
|
|
*/
|
|
|
|
#include <netcdf.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/* This macro handles errors by outputting a message to stdout and
|
|
then exiting. */
|
|
#define NC_EXAMPLE_ERROR 2 /* This is the exit code for failure. */
|
|
#define BAIL(e) do { \
|
|
printf("Bailing out in file %s, line %d, error:%s.\n", \
|
|
__FILE__, __LINE__, nc_strerror(e)); \
|
|
return NC_EXAMPLE_ERROR; \
|
|
} while (0)
|
|
|
|
#define FILENAME "test.nc"
|
|
#define NUMDIMS 4
|
|
#define NUMVARS 4
|
|
|
|
/* We'll need to specify the dimensionality of our netCDF
|
|
variables. These will allow us to do so with less confusion. */
|
|
#define NUMDIMS_1D 1
|
|
#define NUMDIMS_2D 2
|
|
#define NUMDIMS_3D 3
|
|
#define NUMDIMS_4D 4
|
|
|
|
/* The following will be the variable names in the netCDF file. */
|
|
#define TEMP_VARNAME "sfc_temp"
|
|
#define PRES_VARNAME "pressure"
|
|
#define INIT_TEMP_VARNAME "initial_temp"
|
|
#define INIT_PRES_VARNAME "initial_pressure"
|
|
|
|
/* These will be names and values of attributes. */
|
|
#define UNITS "units"
|
|
#define CELSIUS "celsius"
|
|
#define MILIBARS "milibars"
|
|
#define HISTORY "history"
|
|
#define HISTSTR "These data were produced by a thousand typing monkeys"
|
|
|
|
/* These will be the names of the dimensions, and also the names of
|
|
the variables that will hold the coordinate axis labels. */
|
|
#define TIME_NAME "timestep"
|
|
#define LAT_NAME "latitude"
|
|
#define LON_NAME "longitude"
|
|
#define HEIGHT_NAME "height"
|
|
|
|
/* These are the lengths of each dimension. */
|
|
#define LAT_LEN 7
|
|
#define LON_LEN 10
|
|
#define HEIGHT_LEN 5
|
|
#define TIME_LEN 3
|
|
|
|
/* These will be used to create some phoney data. */
|
|
#define PHONEY_LON_START (-120.)
|
|
#define PHONEY_LON_MULT (.5)
|
|
#define PHONEY_LAT_START (40.)
|
|
#define PHONEY_LAT_MULT (2.5)
|
|
#define PHONEY_HEIGHT_INC 100
|
|
#define PHONEY_TEMP_START (10.5)
|
|
#define PHONEY_TEMP_DIV 20
|
|
#define PHONEY_PRES_START (1013.0)
|
|
#define PHONEY_PRES_DIV 1
|
|
|
|
int
|
|
main()
|
|
{
|
|
/* NetCDF IDs for the file, dimensions, and variables. We define
|
|
three extra varids for the coordinate axis data. */
|
|
int ncid, time_dimid, lat_dimid, lon_dimid, height_dimid;
|
|
int temp_varid, pres_varid, itemp_varid, ipres_varid;
|
|
int lat_varid, lon_varid, height_varid;
|
|
|
|
/* We'll reuse this array every time we define a variable. */
|
|
int dimids[NUMDIMS];
|
|
|
|
/* Coordinate axis data (ex. what specific longitude values do we
|
|
have data for?) */
|
|
float lat[LAT_LEN], lon[LON_LEN];
|
|
int height[HEIGHT_LEN];
|
|
|
|
/* Phoney variable data. In the real world, the temp and pres
|
|
variables might be the output of a model or assimilation system,
|
|
and the itemp and ipres initial values of the temp and pres
|
|
variables. For temp and pres we will reserve enough memory to
|
|
store one timestep, and keep using the same timestep's worth of
|
|
data over and over again. */
|
|
float temp[LAT_LEN][LON_LEN];
|
|
float itemp[LAT_LEN][LON_LEN];
|
|
float pres[LAT_LEN][LON_LEN][HEIGHT_LEN];
|
|
float ipres[LAT_LEN][LON_LEN][HEIGHT_LEN];
|
|
|
|
/* We need these to specify the array subsections we'll be writing
|
|
in the temp and pres variables. */
|
|
size_t start[NUMDIMS], count[NUMDIMS];
|
|
|
|
float *fp, *ifp;
|
|
int rec, i, res;
|
|
|
|
/* Create a bunch of phoney data so we have something to write in
|
|
the example file. */
|
|
for (i=0; i<LON_LEN; i++)
|
|
lon[i] = PHONEY_LON_START + (i * PHONEY_LON_MULT);
|
|
for (i=0; i<LAT_LEN; i++)
|
|
lat[i] = PHONEY_LAT_START + (i * PHONEY_LAT_MULT);
|
|
for (i=0; i<HEIGHT_LEN; i++)
|
|
height[i] = i * PHONEY_HEIGHT_INC;
|
|
for (fp=(float *)temp, ifp=(float *)itemp, i=0;
|
|
i<LAT_LEN*LON_LEN; i++)
|
|
*fp++ = (*ifp++ = PHONEY_TEMP_START) + i/PHONEY_TEMP_DIV;
|
|
for (fp=(float *)pres, ifp=(float *)ipres, i=0;
|
|
i<LAT_LEN*LON_LEN*HEIGHT_LEN; i++)
|
|
*fp++ = (*ifp++ = PHONEY_PRES_START) - i/PHONEY_PRES_DIV;
|
|
|
|
/* Create the netCDF file. */
|
|
if ((res = nc_create(FILENAME, NC_CLOBBER, &ncid)))
|
|
BAIL(res);
|
|
|
|
/* Store a history string as a file-level attribute. This could
|
|
store the name of the person or lab producing the data, or any
|
|
information that applies to the dataset as a whole. */
|
|
if ((res = nc_put_att_text(ncid, NC_GLOBAL, HISTORY,
|
|
strlen(HISTSTR), HISTSTR)))
|
|
BAIL(res);
|
|
|
|
/* Define dimensions. The functions will return a dimension ID to
|
|
the appropriate dim ID variable for later reference. Note that
|
|
we define the time dimension with a length of NC_UNLIMITED. This
|
|
means we can arbitrarily add timesteps to variables with a time
|
|
dimension. */
|
|
if ((res = nc_def_dim(ncid, TIME_NAME, NC_UNLIMITED, &time_dimid)))
|
|
BAIL(res);
|
|
if ((res = nc_def_dim(ncid, LAT_NAME, LAT_LEN, &lat_dimid)))
|
|
BAIL(res);
|
|
if ((res = nc_def_dim(ncid, LON_NAME, LON_LEN, &lon_dimid)))
|
|
BAIL(res);
|
|
if ((res = nc_def_dim(ncid, HEIGHT_NAME, HEIGHT_LEN,
|
|
&height_dimid)))
|
|
BAIL(res);
|
|
|
|
/* Define the variables. Each variable's dimension ids are set in
|
|
the dimids array before the call to nc_def_var. The first two
|
|
variables share three dimensions, time, lat and lon. */
|
|
dimids[0] = time_dimid;
|
|
dimids[1] = lat_dimid;
|
|
dimids[2] = lon_dimid;
|
|
if ((res = nc_def_var(ncid, TEMP_VARNAME, NC_FLOAT, NUMDIMS_3D,
|
|
dimids, &temp_varid)))
|
|
BAIL(res);
|
|
|
|
/* For pressure we also add a 4th dimension: height. */
|
|
dimids[3] = height_dimid;
|
|
if ((res = nc_def_var(ncid, PRES_VARNAME, NC_FLOAT, NUMDIMS_4D,
|
|
dimids, &pres_varid)))
|
|
BAIL(res);
|
|
|
|
/* The initial temp and pressure are not unlimited dimensions, and
|
|
they don't have a time dimension. */
|
|
dimids[0] = lat_dimid;
|
|
dimids[1] = lon_dimid;
|
|
if ((res = nc_def_var(ncid, INIT_TEMP_VARNAME, NC_FLOAT,
|
|
NUMDIMS_2D, dimids, &itemp_varid)))
|
|
BAIL(res);
|
|
dimids[2] = height_dimid;
|
|
if ((res = nc_def_var(ncid, INIT_PRES_VARNAME, NC_FLOAT,
|
|
NUMDIMS_3D, dimids, &ipres_varid)))
|
|
BAIL(res);
|
|
|
|
/* We'll store the units as attributes for the temperature and
|
|
pressure variables. */
|
|
if ((res = nc_put_att_text(ncid, pres_varid, UNITS,
|
|
strlen(MILIBARS), MILIBARS)))
|
|
BAIL(res);
|
|
if ((res = nc_put_att_text(ncid, temp_varid, UNITS,
|
|
strlen(CELSIUS), CELSIUS)))
|
|
BAIL(res);
|
|
|
|
|
|
/* In order to store the coordinate axis data we need to define a
|
|
netCDF variable for each axis we want to store data for. In our
|
|
case, we'll store latitudes, longitudes, and heights. We'll
|
|
leave the time dimension as unitless timesteps, with no
|
|
coordinate data specifying what exact times these data apply
|
|
for. In each case, by netCDF convention, the variable name will
|
|
be the same as the dimension, it will be one-dimensional, with
|
|
the length of the dimension. As it's dimension, it will be
|
|
assigned the dimension that it represents. */
|
|
if ((res = nc_def_var(ncid, LAT_NAME, NC_FLOAT, NUMDIMS_1D,
|
|
&lat_dimid, &lat_varid)))
|
|
BAIL(res);
|
|
if ((res = nc_def_var(ncid, LON_NAME, NC_FLOAT, NUMDIMS_1D,
|
|
&lon_dimid, &lon_varid)))
|
|
BAIL(res);
|
|
if ((res = nc_def_var(ncid, HEIGHT_NAME, NC_INT, NUMDIMS_1D,
|
|
&height_dimid, &height_varid)))
|
|
BAIL(res);
|
|
|
|
/* We're finished defining metadata. We have to call nc_enddef
|
|
before writing any data. */
|
|
if ((res = nc_enddef(ncid)))
|
|
BAIL(res);
|
|
|
|
/* Write out our coordinate axis data for lat, lon, and height. */
|
|
if ((res = nc_put_var_float(ncid, lat_varid, lat)))
|
|
BAIL(res);
|
|
if ((res = nc_put_var_float(ncid, lon_varid, lon)))
|
|
BAIL(res);
|
|
if ((res = nc_put_var_int(ncid, height_varid, height)))
|
|
BAIL(res);
|
|
|
|
/* Write out the two fixed-size variables. Since these were not
|
|
defined with an unlimited dimension, they cannot be changed in
|
|
size in the file. We'll write each of them out all at once. */
|
|
if ((res = nc_put_var_float(ncid, itemp_varid, (float *)itemp)))
|
|
BAIL(res);
|
|
if ((res = nc_put_var_float(ncid, ipres_varid, (float *)ipres)))
|
|
BAIL(res);
|
|
|
|
/* Let's simulate the action of some data-producing system by
|
|
writing some records of temp and pres data, one record at a
|
|
time. In the real world, we wouldn't be writing the same data
|
|
over and over again for every timestep, and we need not know in
|
|
advance how many records there will be. */
|
|
|
|
/* The start and count array specify the array we want to
|
|
write. Specify the information in the same order in which you
|
|
defined the dimensions. */
|
|
start[1] = 0;
|
|
start[2] = 0;
|
|
start[3] = 0;
|
|
count[0] = 1;
|
|
count[1] = LAT_LEN;
|
|
count[2] = LON_LEN;
|
|
count[3] = HEIGHT_LEN;
|
|
|
|
for (rec=0; rec<TIME_LEN; rec++)
|
|
{
|
|
/* To write just one record of data, we'll use the
|
|
nc_put_vara_<type> function, which can write any array subset
|
|
of the variable. */
|
|
start[0] = rec; /* We want to write *this* record. */
|
|
if ((res = nc_put_vara_float(ncid, temp_varid, start, count,
|
|
(float *)temp)))
|
|
BAIL(res);
|
|
if ((res = nc_put_vara_float(ncid, pres_varid, start, count,
|
|
(float *)pres)))
|
|
BAIL(res);
|
|
/* We're done writing one record. */
|
|
}
|
|
|
|
/* Close the file. We're done, so we can go out and see the new
|
|
Harry Potter movie! */
|
|
if ((res = nc_close(ncid)))
|
|
BAIL(res);
|
|
|
|
return 0;
|
|
}
|