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

Re: 20020504: basics of using UNLIMITED records in netcdf



>To: address@hidden
>From: David Ovens <address@hidden>
>Subject: basics of using UNLIMITED records in netcdf
>Organization: UCAR/Unidata
>Keywords: 200205040754.g447s1a29265

Hi David,

Sorry it's taken so long to get to your question.

> I can't believe how much time I've just spent trying to figure out how
> to use and write data in FORTRAN netcdf.  I can do things ok if I
> don't use a RECORD/UNLIMITED dimension, but once I do that, I'm all
> fouled up and ncdump never shows me any data in the file.  Please
> help!  I think there must be some step that increments the record
> dimension that I am totally missing and can't find in the
> documentation.  I can fill RHVALS with data, but I can never write it
> into the file when I use an UNLIMITED dimension.
> 
> Thanks for helping me regain my sanity.
> 
> Here's what I do:
>   f90 -I/usr/local/include test.F -lnetcdf ;\
>   rm foo.nc ; a.out ; ncdump foo.nc
> 
> Here's what I get from ncdump:
>  netcdf foo {
>  dimensions:
>         lat = 5 ;
>         lon = 10 ;
>         time = UNLIMITED ; // (0 currently)
>  variables:
>         double rh(time, lat, lon) ;
>  data:
>  }
> 
> Where's rh?
> 
> Here's the simple code (pretty much verbatum from the documentation):
>       INCLUDE 'netcdf.inc'
> 
>       INTEGER  STATUS, NCID, TIMES
>       PARAMETER (TIMES=3, LATS=5, LONS=10) ! dimension lengths
>       INTEGER  RHID             ! variable ID
>       DOUBLEprecision RHVALS(LONS, LATS, TIMES)
>       INTEGER LATID, RECID, ivarid
>       INTEGER  LATDIM, LONDIM, TIMDIM ! dimension IDs
>       INTEGER  RHDIMS(3)        ! variable shape
>       
>       STATUS = NF_CREATE('foo.nc', NF_NOCLOBBER, NCID)
>       IF (STATUS .NE. NF_NOERR) CALL HANDLE_ERR(STATUS)
> 
>       STATUS = NF_DEF_DIM(NCID, 'lat', 5, LATDIM)
>       IF (STATUS .NE. NF_NOERR) CALL HANDLE_ERR(STATUS)
>       STATUS = NF_DEF_DIM(NCID, 'lon', 10, LONDIM)
>       IF (STATUS .NE. NF_NOERR) CALL HANDLE_ERR(STATUS)
>       STATUS = NF_DEF_DIM(NCID, 'time', NF_UNLIMITED, TIMDIM)
> c      STATUS = NF_DEF_DIM(NCID, 'time', 2, TIMDIM)
>       IF (STATUS .NE. NF_NOERR) CALL HANDLE_ERR(STATUS)
>       
>       RHDIMS(1) = LONDIM
>       RHDIMS(2) = LATDIM
>       RHDIMS(3) = TIMDIM
>       STATUS = NF_DEF_VAR (NCID, 'rh', NF_DOUBLE, 3, RHDIMS, RHID)
>       IF (STATUS .NE. NF_NOERR) CALL HANDLE_ERR(STATUS)
> 
>       status = nf_enddef(ncid)
>       status = nf_close(ncid)
> 
>       STATUS = NF_OPEN ('foo.nc', NF_WRITE, NCID)
>       IF (STATUS .NE. NF_NOERR) CALL HANDLE_ERR(STATUS)
>       
>       STATUS = NF_INQ_VARID (NCID, 'rh', RHID)
>       IF (STATUS .NE. NF_NOERR) CALL HANDLE_ERR(STATUS)
> 
>       DO 30 ILON = 1, LONS
>          DO 20 ILAT = 1, LATS
>             DO 10 ITIME = 1, TIMES
>                RHVALS(ILON, ILAT, ITIME) = 0.5
>  10         CONTINUE
>  20      continue
>  30   continue
>       STATUS = NF_PUT_var_DOUBLE (NCID, RHID, RHVALS)
>       IF (STATUS .NE. NF_NOERR) CALL HANDLE_ERR(STATUS)
>       
>       status = nf_close(ncid)
>       stop
>       end
> 
>       subroutine handle_err(iret)
>       integer iret
>       include 'netcdf.inc'
>       if (iret .ne. NF_NOERR) then
>       print *, nf_strerror(iret)
>       stop
>       endif
>       end

The problem is in the documentation for the nf_put_var_ family of
functions, which should contain the following paragraph (and does in a
new version, which is unfortunately not yet available):

  It is usually not appropriate to use this interface with record
  variables, which are typically written a record at a time instead.  If
  you try to write all the values of a record variable into a netCDF
  file that has no record data yet (hence has 0 records), nothing will
  be written.  Similarly, if you try to write all of a record variable
  but there are more records in the file than you assume, more data may
  be written to the file than you supply, which may result in a
  segmentation violation.

Instead of nf_put_var_double() you need to use nf_put_vara_double() to
write one record at a time.  So your rh variable in the program only
needs to be dimensioned for (lat, lon) rather than (time, lat,lon),
although the latter is OK too as long as you write one time slice at a
time.  You could even write all the time slices at once using
nf_put_vara_double(), with something like

      PARAMETER(RHRANK = 3)
      INTEGER RHSTART(RHRANK)
      INTEGER RHCOUNT(RHRANK)
   ...
      RHSTART(1) = 1
      RHSTART(2) = 1
      RHSTART(3) = 1
      RHCOUNT(1) = LONS
      RHCOUNT(2) = LATS
      RHCOUNT(3) = TIMES
      STATUS = NF_PUT_vara_DOUBLE (NCID, RHID, RHSTART, RHCOUNT, RHVALS)

You can generate a working Fortran example from a CDL file using the
"ncgen" utility with the "-f" flag, so for example, from the CDL file

  netcdf foo {
  dimensions:
          lat = 5 ;
          lon = 10 ;
          time = UNLIMITED ; // (0 currently)
  variables:
          double rh(time, lat, lon) ;
  data:
          rh = 1,1,1,1,1,1,1,1,1,1,
               1,1,1,1,1,1,1,1,1,1,
               1,1,1,1,1,1,1,1,1,1,
               1,1,1,1,1,1,1,1,1,1,
               1,1,1,1,1,1,1,1,1,1,
               2,2,2,2,2,2,2,2,2,2,
               2,2,2,2,2,2,2,2,2,2,
               2,2,2,2,2,2,2,2,2,2,
               2,2,2,2,2,2,2,2,2,2,
               2,2,2,2,2,2,2,2,2,2;
  }

stored in "foo.cdl", you could generate a Fortran program to create
the corresponding netCDF file foo.nc using

 ncgen -f foo.cdl > foo.f

and see that it uses a call to nf_put_var_double().

Here's another support response that explains the issues with the
"whole variable" write for record variables in more detail:

  http://www.unidata.ucar.edu/cgi-bin/mfs/70/4293

Please let us know if this answers your question.

--Russ

_____________________________________________________________________

Russ Rew                                         UCAR Unidata Program
address@hidden                     http://www.unidata.ucar.edu