source: hdldemo/trunk/ncq3.py @ 558

Subversion URL: http://proj.badc.rl.ac.uk/svn/exarch/hdldemo/trunk/ncq3.py@558
Revision 558, 10.3 KB checked in by mjuckes, 5 years ago (diff)

Adding hdldemo

Line 
1# Based on ncquick.py, by - M. Neish 2008/09/21
2# ncquick.py was released as part of CCMval3.0
3# - Read-only access
4#####################################################
5# Extended
6# June 2014, Martin Juckes:
7# Added routines to read all attributes.
8# Refactored to creat a digest of file metadata: the digest is returned as a named-tuple, containing lists
9# of named-tuples. This contains al the information in a clearly defined structure, but is not very convenient to
10# browse because of lots of cross-referencing by object ids.
11# The "Browser" class provides one method for extracting variable information.
12#####################################################
13#
14# Examples:
15#
16## import ncq3.py
17
18## file = ncq3.open( 'test.nc' )
19## file.getDigest()
20## ncq3.close(file)
21## b = ncq3.Broswer( file.digest )
22## print b.varsum( 'time' )
23
24##
25## dependency on ctypes: may not work on windows or mac because of different naming conventions
26## for C library.
27##
28
29from ctypes import *
30import collections, string
31scriptVersion=0.5
32
33libnetcdf = CDLL("libnetcdf.so")
34
35# Some codes from the netcdf library
36NC_GLOBAL = -1
37
38NC_BYTE = 1
39NC_CHAR = 2
40NC_SHORT = 3
41NC_INT = 4
42NC_LONG = NC_INT
43NC_FLOAT = 5
44NC_DOUBLE = 6
45NC_FORMAT_CLASSIC = 1
46NC_FORMAT_64BIT   = 2
47NC_FORMAT_NETCDF4 = 3
48NC_FORMAT_NETCDF4_CLASSIC  = 4
49## values copied from /usr/include/netcdf.h
50class DummyClass(object):
51  def __init__(self):
52    pass
53ncmappings = DummyClass()
54ncmappings.fmt= { NC_FORMAT_CLASSIC:'Classic', NC_FORMAT_64BIT:'64Bit',
55                  NC_FORMAT_NETCDF4:'NetCDF4', NC_FORMAT_NETCDF4_CLASSIC:'NetCDF4 Classic' }
56## dictionary of c-types to deal with different data types
57ncmappings.tdict = { NC_BYTE:c_byte,
58      NC_SHORT:c_short,
59      NC_INT:c_int,
60      NC_LONG:c_long,
61      NC_FLOAT:c_float,
62      NC_DOUBLE:c_double }
63
64ncmappings.tdict2 = { NC_BYTE:"byte",
65      NC_SHORT:"short",
66      NC_INT:"int",
67      NC_LONG:"long",
68      NC_FLOAT:"float",
69      NC_DOUBLE:"double" }
70
71NC_MAX_NAME = 256
72
73class NCError (Exception):
74  def __init__ (self, value): self.value = value
75  def __str__ (self): return self.value
76
77## Added Group Id (gid).
78Attribute = collections.namedtuple('Attribute', ['name','gid','vid', 'id', 'type', 'len', 'value'])
79VarInfo = collections.namedtuple('VarInfo', ['name', 'gid','id', 'type', 'natts', 'ndims', 'dimids'])
80Variable = collections.namedtuple('Variable', ['name', 'gid','id', 'type', 'natts', 'ndims', 'dimids','data'])
81Dimension = collections.namedtuple('Dimension', ['name', 'gid','id', 'len', 'isunlimited'])
82Fileinfo = collections.namedtuple('Fileinfo', ['filename','ndims','nvar','ngatts','ngrp','ncformat'] )
83Sysinfo = collections.namedtuple('Sysinfo',['scriptVersion','libnetcdfVersion'])
84FileDigest = collections.namedtuple('FileDigest',['sysinfo','fileinfo','dimensions','variables','attributes'])
85
86class Browser(object):
87  def __init__(self, digest):
88    self.digest = digest
89    self.varlist = []
90    self.dimlist = []
91    for v in digest.variables:
92      self.varlist.append(v.name)
93    for d in digest.dimensions:
94      self.dimlist.append(d.name)
95
96  def varsum(self,name):
97    if name not in self.varlist:
98      print ( '%s not in %s' % (name,str(self.varlist)) )
99      return None
100    v = self.digest.variables[self.varlist.index(name)]
101    assert v.name == name, 'Error in internal logic: name mismatch in varsum'
102    dlist = map( lambda x: self.digest.dimensions[x], v.dimids )
103    dstr = map( lambda x: '%s[%s]' % (x.name, x.len), dlist )
104    return '%s[%s]' % (name,string.join( dstr, ',') )
105   
106
107#FileDigest = collections.namedtuple('FileDigest',['sysinfo','fileinfo','dimensions','variables','attributes'])
108class Browse(object):
109  def __init__(self,fileobj):
110    self.nc = fileobj
111    for k in range(len(self.nc.variables)):
112      assert self.nc.variables[k].id == k, 'Internal error: index does not match variable id'
113
114    self._ll = []
115    self._vdict = {}
116    self._ddict = {}
117    for v in self.nc.variables:
118      self._ll.append( [] )
119    self._gal = []
120    tl = None
121    ti = -2
122    for a in self.nc.attributes:
123      if a.vid != -1:
124        assert a.id == len(self._ll[a.vid]), 'Unexpected attribute id: %s -- %s' % (str(a),str(self._ll[a.vid]) )
125        self._ll[a.vid].append( a )
126      else:
127        self._gal.append( a )
128    for v in self.nc.variables:
129      self._vdict[v.name] = (v,map(lambda x: x.name,self._ll[v.id]) )
130    for d in self.nc.dimensions:
131      self._ddict[d.name] = d
132         
133class File(object):
134  def __init__ (self, name, id) :
135    self.name = name
136    self.id = id
137    self.vars = None
138    self.dimensions = {}
139    self.dimensions_ex = {}
140## use "restype" to change default handling of function return values (default is as c_int)
141    libnetcdf.nc_inq_libvers.restype = c_char_p
142    self.nclibversion = libnetcdf.nc_inq_libvers()
143    iformat = c_int()
144    err = libnetcdf.nc_inq_format( id, byref(iformat) )
145    if err != 0: raise NCError("can't read file format")
146    self.fileformat = iformat.value
147
148## extension by Martin Juckes, June 2014 ##
149  def nc_int(self) :
150    ndims = c_int()
151    nvars = c_int()
152    ngatts = c_int()
153    unlimdimid = c_int()
154
155    err = libnetcdf.nc_inq(self.id, byref(ndims), byref(nvars), byref(ngatts), byref(unlimdimid));
156    if err != 0: raise NCError("can't read ndims etc in file '"+self.name+"'")
157    self.ndims = ndims.value
158    self.nvars = nvars.value
159    self.ngatts = ngatts.value
160    self.unlimdimid = unlimdimid.value
161##Fileinfo = collections.namedtuple('Fileinfo', ['filename','ndims','nvar','ngatts','ngrp','ncformat'] )
162    self.fileinfo = Fileinfo( self.name, ndims.value, nvars.value, ngatts.value, 0, self.fileformat )
163    return self.fileinfo
164
165  def getDigest(self,props=True, full=True):
166    self.nc_int()
167    self.dims()
168    self.varnames(props=props, full=full)
169    self.allatts()
170
171  def dims(self):
172     self.dimensions = []
173     for i in range(self.ndims):
174        len = c_int()
175        libnetcdf.nc_inq_dimlen(self.id, i, byref(len))
176        name = create_string_buffer(NC_MAX_NAME+1)
177        libnetcdf.nc_inq_dimname(self.id, i, name)
178        self.dimensions.append( Dimension( name.value, 0, i, len.value, i == self.unlimdimid ) )
179     return self.dimensions
180
181  def varnames(self,props=False,full=False,maxrank=2):
182     l = []
183     self.vars = []
184     for i in range(self.nvars):
185        name = create_string_buffer(NC_MAX_NAME+1)
186        libnetcdf.nc_inq_varname(self.id, i, name)
187        l.append( name.value )
188        if props:
189          type = c_int()
190          libnetcdf.nc_inq_vartype(self.id, i, byref(type))
191          natts = c_int()
192          libnetcdf.nc_inq_varnatts(self.id, i, byref(natts));
193          ndims = c_int()
194          libnetcdf.nc_inq_varndims(self.id, i, byref(ndims));
195          dimids = (c_int*ndims.value)()
196          libnetcdf.nc_inq_vardimid (self.id, i, dimids);
197          if full:
198            tt = ncmappings.tdict.get( type.value, None )
199            if tt == None: raise NCError("unknown data type")
200            len = 1
201            for d in list(dimids):
202              len *= self.dimensions[d].len
203            if ndims.value <= maxrank:
204              data = (c_double*len)()
205              err = libnetcdf.nc_get_var_double(self.id, i, data)
206              self.vars.append( Variable( name.value, 0, i, ncmappings.tdict2[type.value], natts.value, ndims.value, list(dimids), data[:] ) )
207            else:
208              self.vars.append( Variable( name.value, 0, i, ncmappings.tdict2[type.value], natts.value, ndims.value, list(dimids), None ) )
209          else:
210            self.vars.append( VarInfo( name.value, 0, i, type.value, natts.value, ndims.value, list(dimids) ) )
211     return self.vars
212
213  def allatts(self):
214     self.alla = []
215     for aid in range(self.ngatts):
216       self.alla.append(self.atts ( aid, vid=NC_GLOBAL) )
217     for v in self.vars:
218       for aid in range(v.natts):
219         self.alla.append(self.atts ( aid, vid=v.id)) 
220
221  def info(self):
222    self.sysinfo = Sysinfo( scriptVersion, self.nclibversion )
223    self.digest = FileDigest( self.sysinfo, self.fileinfo, self.dimensions, self.vars, self.alla )
224##Sysinfo = collections.namedtuple('Sysinfo',['scriptVersion','libnetcdfVersion'])
225#FileDigest = collections.namedtuple('FileDigest',['sysinfo','fileinfo','dimensions','variables','attributes'])
226
227  def attnames(self,varid=NC_GLOBAL):
228     l = []
229     for i in range(self.ngatts):
230        name = create_string_buffer(NC_MAX_NAME+1)
231        libnetcdf.nc_inq_attname(self.id, varid, i, name)
232        l.append( name.value )
233     if varid == NC_GLOBAL:
234       self.gatts = tuple(l)
235     else:
236       if self.vars == None:
237         self.varnames()
238       self.vadict[self.vars[varid]] = tuple(l)
239     return tuple(l)
240
241  def atts (self, aid, vid=NC_GLOBAL) :
242    cname = create_string_buffer(NC_MAX_NAME+1)
243    libnetcdf.nc_inq_attname(self.id, vid, aid, cname)
244    name=cname.value
245    len = c_int()
246    type = c_int()
247    err = libnetcdf.nc_inq_att (self.id, vid, c_char_p(name), 
248                                byref(type), byref(len))
249    if err != 0: raise NCError("can't find attribute '"+name+"' in var '"+str(vid)+"'")
250    type = type.value
251    len = len.value
252
253    if type == NC_CHAR:
254        data = create_string_buffer(len)
255        err = libnetcdf.nc_get_att_text (self.id, vid,
256                                     c_char_p(name), data)
257        if err != 0: raise NCError("can't read char attribute")
258        return Attribute( name, 0, vid, aid, type, len, data.value )
259
260    else:
261      tt = ncmappings.tdict.get( type, None )
262      if tt == None: raise NCError("unknown data type")
263      t2 = ncmappings.tdict2.get( type, None )
264      if len > 1:
265        data = (tt*len)()
266        err = libnetcdf.nc_get_att(self.id, vid, c_char_p(name), data)
267        if err != 0: raise NCError( 'Error reading attribute value %s, type:%s' % (name,type) )
268        return Attribute( name, 0, vid, aid, t2, len, data )
269      else:
270        data = tt()
271        err = libnetcdf.nc_get_att(self.id, vid, c_char_p(name), byref(data))
272        if err != 0: raise NCError( 'Error reading attribute value %s, type:%s' % (name,type) )
273        dv = data.value
274        return Attribute( name, 0, vid, aid, t2, len, dv )
275
276## end of extension ##
277
278def open (filename) :
279  fileid = c_int()
280  err = libnetcdf.nc_open (c_char_p(filename), 0, byref(fileid))
281  if err != 0: raise NCError("can't open file '"+filename+"'")
282  return File (filename, fileid)
283
284def close (file) :
285  file.info()
286  err = libnetcdf.nc_close (file.id)
287  if err != 0: raise NCError("can't close file '"+file.name+"'")
288
289
290
Note: See TracBrowser for help on using the repository browser.