[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: 20050202: How to write to the unlimited-dimension variable



>From: Jiye Zeng <address@hidden>
>Organization: Center For Global Environmental Research, Japan
>Keywords:  200502021439.j12Edlsi019750 netCDF C example

Jiya,

> I tried nc_put_vara_xxx(...) function, but I still missed something. When the 
> time 
> dimension and variable are created like
> 
> nc_def_dim(ncid, "time", NC_UNLIMITED, &varid);
> nc_def_var(ncid, "time", nctype, 1, time_dimid, &varid);
> 
> The dimemsion of time is zero. I am not able to write to time(time) variable 
> or P(time, 
> lat, lon). For example
> 
> static int P_start[] = {0, 0, 0};
> static int P_edges[] = {1, nlat, nlon};
> status = nc_put_vara_xxx(ncid, P_id, P_start, P_edges, P);
> 
> does not work.

That should work fine.  Are you calling nc_close(ncid) before exiting
the program, to make sure the buffers get flushed and the in-memory
metadata, such as number of records, is synchronized to disk?  If your
program calls nc_close() after the above call and you look at the
resulting file with something like "ncdump -h", it should show that
one record was written, because the time dimension will appear like:

        time = UNLIMITED ; // (1 currently)

> My question is how to extend the time?
> 
> Could you give me a rather complete example? (Without any attribute IO is OK).

Sure, and I'll also tell you how to generate your own example with the
"ncgen" program.  Just create a small text CDL file, say
"example.cdl", for some data you want to write, for example here is
one, with a few attributes:

//----------------------- example.cdl --------------------
netcdf example {  // A small example netCDF file

dimensions:
        lat = 2,   lon = 3,  time = unlimited;

variables:
        float   P(time, lat, lon);
                P:long_name = "pressure";
                P:units = "hectopascals";

        float   lat(lat);
                lat:long_name = "latitude";
                lat:units = "degrees_north";

        float   lon(lon);
                lon:long_name = "longitude";
                lon:units = "degrees_east";

        float   time(time);
                time:units = "hours since 1992-1-1";
data:
        P = 1000, 1001, 1002, 1003, 1004, 1005,
            1010, 1011, 1012, 1013, 1014, 1015; 
}
//----------------------- end of example.cdl --------------

Then to generate a C program that will create the corresponding netCDF
file, invoke ncgen with the "-c" option to tell it that you want to
generate C output:

 ncgen -c example.cdl > example.c

Now the C program, which I've attached, can be compiled and linked
with the netCDF library, and when run, it should produce the netCDF
file corresponding to the CDL above.  The part that writes the first
two records looks like this:

    static size_t P_start[RANK_P];
    static size_t P_count[RANK_P];
    static float P[] = {1000, 1001, 1002, 1003, 1004, 1005, 1010, 1011, 1012, 
1013, 1014, 1015};
    time_len = 2;                       /* number of records of P data */
    P_start[0] = 0;
    P_start[1] = 0;
    P_start[2] = 0;
    P_count[0] = time_len;
    P_count[1] = lat_len;
    P_count[2] = lon_len;
    stat = nc_put_vara_float(ncid, P_id, P_start, P_count, P);

and if you only wanted to store 1 record, all that would change is
setting P_count[0] to 1 instead of 2.  By the way, you don't have to
write the records in order; fill values are provided for unwritten
data.

> By the way, it seems that global attributes must be created right after a 
> file creation. Is 
> that true?

No, you can later enter define mode and define new attributes, but this
may cause the whole file to be rewritten if there was not enough space
in the "file header" to store the new attributes.  It's possible to
allocate extra space in the file header when you create the file to
prevent the expense of later copying it when you add new attributes,
using the nc__enddef() function:

http://www.unidata.ucar.edu/packages/netcdf/docs/netcdf-c/nc_005f_005fenddef.ht
ml#nc_005f_005fenddef

But that's advanced usage for large files.  For small files, you
probably don't need to worry about it.

--Russ

#include <stdio.h>
#include <stdlib.h>
#include <netcdf.h>

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 example.nc */

   int  stat;                   /* return status */
   int  ncid;                   /* netCDF id */

   /* dimension ids */
   int lat_dim;
   int lon_dim;
   int time_dim;

   /* dimension lengths */
   size_t lat_len = 2;
   size_t lon_len = 3;
   size_t time_len = NC_UNLIMITED;

   /* variable ids */
   int P_id;
   int lat_id;
   int lon_id;
   int time_id;

   /* rank (number of dimensions) for each variable */
#  define RANK_P 3
#  define RANK_lat 1
#  define RANK_lon 1
#  define RANK_time 1

   /* variable shapes */
   int P_dims[RANK_P];
   int lat_dims[RANK_lat];
   int lon_dims[RANK_lon];
   int time_dims[RANK_time];

   /* enter define mode */
   stat = nc_create("example.nc", NC_CLOBBER, &ncid);
   check_err(stat,__LINE__,__FILE__);

   /* define dimensions */
   stat = nc_def_dim(ncid, "lat", lat_len, &lat_dim);
   check_err(stat,__LINE__,__FILE__);
   stat = nc_def_dim(ncid, "lon", lon_len, &lon_dim);
   check_err(stat,__LINE__,__FILE__);
   stat = nc_def_dim(ncid, "time", time_len, &time_dim);
   check_err(stat,__LINE__,__FILE__);

   /* define variables */

   P_dims[0] = time_dim;
   P_dims[1] = lat_dim;
   P_dims[2] = lon_dim;
   stat = nc_def_var(ncid, "P", NC_FLOAT, RANK_P, P_dims, &P_id);
   check_err(stat,__LINE__,__FILE__);

   lat_dims[0] = lat_dim;
   stat = nc_def_var(ncid, "lat", NC_FLOAT, RANK_lat, lat_dims, &lat_id);
   check_err(stat,__LINE__,__FILE__);

   lon_dims[0] = lon_dim;
   stat = nc_def_var(ncid, "lon", NC_FLOAT, RANK_lon, lon_dims, &lon_id);
   check_err(stat,__LINE__,__FILE__);

   time_dims[0] = time_dim;
   stat = nc_def_var(ncid, "time", NC_FLOAT, RANK_time, time_dims, &time_id);
   check_err(stat,__LINE__,__FILE__);

   /* assign attributes */
   stat = nc_put_att_text(ncid, P_id, "long_name", 8, "pressure");
   check_err(stat,__LINE__,__FILE__);
   stat = nc_put_att_text(ncid, P_id, "units", 12, "hectopascals");
   check_err(stat,__LINE__,__FILE__);
   stat = nc_put_att_text(ncid, lat_id, "long_name", 8, "latitude");
   check_err(stat,__LINE__,__FILE__);
   stat = nc_put_att_text(ncid, lat_id, "units", 13, "degrees_north");
   check_err(stat,__LINE__,__FILE__);
   stat = nc_put_att_text(ncid, lon_id, "long_name", 9, "longitude");
   check_err(stat,__LINE__,__FILE__);
   stat = nc_put_att_text(ncid, lon_id, "units", 12, "degrees_east");
   check_err(stat,__LINE__,__FILE__);
   stat = nc_put_att_text(ncid, time_id, "units", 20, "hours since 1992-1-1");
   check_err(stat,__LINE__,__FILE__);

   /* leave define mode */
   stat = nc_enddef (ncid);
   check_err(stat,__LINE__,__FILE__);

   {                    /* store P */
    static size_t P_start[RANK_P];
    static size_t P_count[RANK_P];
    static float P[] = {1000, 1001, 1002, 1003, 1004, 1005, 1010, 1011, 1012, 
1013, 1014, 1015};
    time_len = 2;                       /* number of records of P data */
    P_start[0] = 0;
    P_start[1] = 0;
    P_start[2] = 0;
    P_count[0] = time_len;
    P_count[1] = lat_len;
    P_count[2] = lon_len;
    stat = nc_put_vara_float(ncid, P_id, P_start, P_count, P);
    check_err(stat,__LINE__,__FILE__);
   }
   stat = nc_close(ncid);
   check_err(stat,__LINE__,__FILE__);
   return 0;
}