Package solvcon :: Package io :: Module netcdf
[hide private]
[frames] | no frames]

Source Code for Module solvcon.io.netcdf

  1  # -*- coding: UTF-8 -*- 
  2  # 
  3  # Copyright (C) 2011 Yung-Yu Chen <yyc@solvcon.net>. 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation; either version 2 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License along 
 16  # with this program; if not, write to the Free Software Foundation, Inc., 
 17  # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 18   
 19  """ 
 20  This is a simple wrapper to netCDF C library.  This wrapper is designed to be 
 21  self-sufficient.  That is, this should be kept to be an one-file module. 
 22   
 23  The module is designed for reading rather than writing.  Only limited Pythonic 
 24  API is implemented.  All constants are taken from the netcdf.h file in the 
 25  official distribution (4.1.1). 
 26   
 27  For more information about netCDF, please refer to  
 28  http://www.unidata.ucar.edu/software/netcdf/index.html 
 29  """ 
 30   
 31  _libs = dict() 
32 -def get_lib(path):
33 from ctypes import CDLL 34 from ..dependency import guess_dllname 35 if path in _libs: 36 lib = _libs[path] 37 else: 38 lib = _libs[path] = CDLL(guess_dllname('netcdf')) 39 return lib
40
41 -class NetCDF(object):
42 """ 43 Wrapper for the netCDF library by using ctypes. Mainly designed for 44 reading. Native functions remains to be nc_*. NC_* are class members for 45 constants defined in the header file. 46 """ 47 48 # netcdf external data types 49 NC_NAT = 0 # Not A Type. 50 NC_BYTE = 1 # signed 1 byte integer. 51 NC_CHAR = 2 # ISO/ASCII character. 52 NC_SHORT = 3 # signed 2 byte integer. 53 NC_INT = 4 # signed 4 byte integer. 54 NC_LONG = NC_INT # deprecated, but required for backward compatibility. 55 NC_FLOAT = 5 # single precision floating point number. 56 NC_DOUBLE = 6 # double precision floating point number. 57 NC_UBYTE = 7 # unsigned 1 byte int. 58 NC_USHORT = 8 # unsigned 2-byte int. 59 NC_UINT = 9 # unsigned 4-byte int. 60 NC_INT64 = 10 # signed 8-byte int. 61 NC_UINT64 = 11 # unsigned 8-byte int. 62 NC_STRING = 12 # string. 63 # used internally in support of user-defines types. They are also the class 64 # returned by nc_inq_user_type. 65 NC_VLEN = 13 # used internally for vlen types. 66 NC_OPAQUE = 14 # used internally for opaque types. 67 NC_ENUM = 15 # used internally for enum types. 68 NC_COMPOUND = 16 # used internally for compound types. 69 70 # Default fill values. 71 NC_FILL_BYTE = -127 72 NC_FILL_CHAR = 0 73 NC_FILL_SHORT = -32767 74 NC_FILL_INT = -2147483647L 75 NC_FILL_FLOAT = 9.9692099683868690e+36 # near 15 * 2^119. 76 NC_FILL_DOUBLE = 9.9692099683868690e+36 77 NC_FILL_UBYTE = 255 78 NC_FILL_USHORT = 65535 79 NC_FILL_UINT = 4294967295 80 NC_FILL_INT64 = -9223372036854775806 81 NC_FILL_UINT64 = 18446744073709551614 82 NC_FILL_STRING = '' 83 84 # max and min. 85 NC_MAX_BYTE = 127 86 NC_MIN_BYTE = -NC_MAX_BYTE - 1 87 NC_MAX_CHAR = 255 88 NC_MAX_SHORT = 32767 89 NC_MIN_SHORT = -NC_MAX_SHORT - 1 90 NC_MAX_INT = 2147483647 91 NC_MIN_INT = -NC_MAX_INT - 1 92 NC_MAX_FLOAT = 3.402823466e+38 93 NC_MIN_FLOAT = -NC_MAX_FLOAT 94 NC_MAX_DOUBLE = 1.7976931348623157e+308 95 NC_MIN_DOUBLE = -NC_MAX_DOUBLE 96 NC_MAX_UBYTE = NC_MAX_CHAR 97 NC_MAX_USHORT = 65535 98 NC_MAX_UINT = 4294967295 99 NC_MAX_INT64 = 9223372036854775807 100 NC_MIN_INT64 = -9223372036854775807 - 1 101 NC_MAX_UINT64 = 18446744073709551615 102 X_INT64_MAX = 9223372036854775807 103 X_INT64_MIN = -X_INT64_MAX - 1 104 X_UINT64_MAX = 18446744073709551615 105 106 # for fill. 107 _FillValue = '_FillValue' 108 NC_FILL = 0 109 NC_NOFILL = 0x100 110 111 # mode for nc_open 112 NC_NOWRITE = 0 # default read-only. 113 NC_WRITE = 0x0001 # read and write. 114 # mode for nc_create 115 NC_CLOBBER = 0 116 NC_NOCLOBBER = 0x0004 # don't descroy existing file. 117 NC_64BIT_OFFSET = 0x0200 # use 64-bit (large) file offsets. 118 NC_NETCDF4 = 0x1000 # use netCDF-4/HDF5 format. 119 NC_CLASSICAL_MODE = 0x0100 # enforce classic model with NC_NETCDF4. 120 # mode for both nc_open and nc_create. 121 NC_SHARE = 0x0800 # share updates, limit cache. 122 NC_MPIIO = 0x2000 123 NC_MPIPOSIX = 0x4000 124 NC_PNETXCDF = 0x8000 125 # future mode for nc_open and nc_create 126 NC_LOCK = 0x0400 127 128 # formats; there are more than one format since 3.6. 129 NC_FORMAT_CLASSIC = 1 130 NC_FORMAT_64BIT = 2 131 NC_FORMAT_NETCDF4 = 3 132 NC_FORMAT_NETCDF4_CLASSIC = 4 133 134 # for nc__open and nc__create. 135 NC_SIZEHINT_DEFAULT = 0 136 137 # in nc__enddef, align to chunk size. 138 NC_ALIGN_CHUNK = -1 139 140 # size argument to ncdimdef for unlimit. 141 NC_UNLIMITED = 0 142 143 # attribute ID for global attributes. 144 NC_GLOBAL = -1 145 146 # interfacial max; nothing to do with netCDF internal implementation. 147 NC_MAX_DIMS = 1024 # max dimensions per file. 148 NC_MAX_ATTRS = 8192 # max global or per variable attributes. 149 NC_MAX_VARS = 8192 # max variables per file. 150 NC_MAX_NAME = 256 # max length of a name. 151 NC_MAX_VAR_DIMS = NC_MAX_DIMS # max per variable dimensions. 152 153 # endianness for HDF5 files. 154 NC_ENDIAN_NATIVE = 0 155 NC_ENDIAN_LITTLE = 1 156 NC_ENDIAN_BIG = 2 157 # chunk or continuous for HDF5 files. 158 NC_CHUNKED = 0 159 NC_CONTIGUOUS = 1 160 # checksum for HDF5 files. 161 NC_NOCHECKSUM = 0 162 NC_FLETCHER32 = 1 163 # shuffle for HDF5 files. 164 NC_NOSHUFFLE = 0 165 NC_SHUFFLE = 1 166 167 # errors. 168 NC_NOERR = 0 # no error. 169 170 NC2_ERR = -1 # for all errors in the v2 API. 171 NC_EBADID = -33 # Not a netcdf id. 172 NC_ENFILE = -34 # Too many netcdfs open. 173 NC_EEXIST = -35 # netcdf file exists && NC_NOCLOBBER. 174 NC_EINVAL = -36 # Invalid Argument. 175 NC_EPERM = -37 # Write to read only. 176 NC_ENOTINDEFINE = -38 # Operation not allowed in data mode. 177 NC_EINDEFINE = -39 # Operation not allowed in define mode. 178 NC_EINVALCOORDS = -40 # Index exceeds dimension bound. 179 NC_EMAXDIMS = -41 # NC_MAX_DIMS exceeded. 180 NC_ENAMEINUSE = -42 # String match to name in use. 181 NC_ENOTATT = -43 # Attribute not found. 182 NC_EMAXATTS = -44 # NC_MAX_ATTRS exceeded. 183 NC_EBADTYPE = -45 # Not a netcdf data type. 184 NC_EBADDIM = -46 # Invalid dimension id or name. 185 NC_EUNLIMPOS = -47 # NC_UNLIMITED in the wrong index. 186 NC_EMAXVARS = -48 # NC_MAX_VARS exceeded. 187 NC_ENOTVAR = -49 # Variable not found. 188 NC_EGLOBAL = -50 # Action prohibited on NC_GLOBAL varid. 189 NC_ENOTNC = -51 # Not a netcdf file. 190 NC_ESTS = -52 # In Fortran, string too short. 191 NC_EMAXNAME = -53 # NC_MAX_NAME exceeded. 192 NC_EUNLIMIT = -54 # NC_UNLIMITED size already in use. 193 NC_ENORECVARS = -55 # nc_rec op when there are no record vars. 194 NC_ECHAR = -56 # Attempt to convert between text & numbers. 195 NC_EEDGE = -57 # Start+count exceeds dimension bound. 196 NC_ESTRIDE = -58 # Illegal stride. 197 NC_EBADNAME = -59 # Attribute or variable name contains illegal 198 # characters. 199 200 # following must match value in ncx.h 201 NC_ERANGE = -60 # Math result not representable. 202 NC_ENOMEM = -61 # Memory allocation (malloc) failure. 203 NC_EVARSIZE = -62 # One or more variable sizes violate format 204 # constraints. 205 NC_EDIMSIZE = -63 # Invalid dimension size. 206 NC_ETRUNC = -64 # File likely truncated or possibly corrupted. 207 NC_EAXISTYPE = -65 # Unknown axis type. 208 209 # following errors are added for DAP. 210 NC_EDAP = -66 # Generic DAP error. 211 NC_ECURL = -67 # Generic libcurl error. 212 NC_EIO = -68 # Generic IO error. 213 NC_ENODATA = -69 # Attempt to access variable with no data. 214 NC_EDAPSVC = -70 # DAP Server side error. 215 NC_EDAS = -71 # Malformed or inaccessible DAS. 216 NC_EDDS = -72 # Malformed or inaccessible DDS. 217 NC_EDATADDS = -73 # Malformed or inaccessible DATADDS. 218 NC_EDAPURL = -74 # Malformed DAP URL. 219 NC_EDAPCONSTRAINT = -75 # Malformed DAP Constraint. 220 221 # following was added in support of netcdf-4. Make all netcdf-4 error codes 222 # < -100 so that errors can be added to netcdf-3 if needed. 223 NC4_FIRST_ERROR = -100 224 NC_EHDFERR = -101 # Error at HDF5 layer. 225 NC_ECANTREAD = -102 # Can't read. 226 NC_ECANTWRITE = -103 # Can't write. 227 NC_ECANTCREATE = -104 # Can't create. 228 NC_EFILEMETA = -105 # Problem with file metadata. 229 NC_EDIMMETA = -106 # Problem with dimension metadata. 230 NC_EATTMETA = -107 # Problem with attribute metadata. 231 NC_EVARMETA = -108 # Problem with variable metadata. 232 NC_ENOCOMPOUND = -109 # Not a compound type. 233 NC_EATTEXISTS = -110 # Attribute already exists. 234 NC_ENOTNC4 = -111 # Attempting netcdf-4 operation on netcdf-3 file. 235 NC_ESTRICTNC3 = -112 # Attempting netcdf-4 operation on strict nc3 236 # netcdf-4 file. 237 NC_ENOTNC3 = -113 # Attempting netcdf-3 operation on netcdf-4 file. 238 NC_ENOPAR = -114 # Parallel operation on file opened for non-parallel 239 # access. 240 NC_EPARINIT = -115 # Error initializing for parallel access. 241 NC_EBADGRPID = -116 # Bad group ID. 242 NC_EBADTYPID = -117 # Bad type ID. 243 NC_ETYPDEFINED = -118 # Type has already been defined and may not be 244 # edited. 245 NC_EBADFIELD = -119 # Bad field ID. 246 NC_EBADCLASS = -120 # Bad class. 247 NC_EMAPTYPE = -121 # Mapped access for atomic types only. 248 NC_ELATEFILL = -122 # Attempt to define fill value when data already 249 # exists. 250 NC_ELATEDEF = -123 # Attempt to define var properties, like deflate, after 251 # enddef. 252 NC_EDIMSCALE = -124 # Probem with HDF5 dimscales. 253 NC_ENOGRP = -125 # No group found. 254 NC_ESTORAGE = -126 # Can't specify both contiguous and chunking. 255 NC_EBADCHUNK = -127 # Bad chunksize. 256 NC_ENOTBUILT = -128 # Attempt to use feature that was not turned on when 257 # netCDF was built. 258 NC4_LAST_ERROR = -128 259
260 - def __init__(self, path=None, omode=None, libname='libnetcdf.so'):
261 """ 262 @keyword path: the file to open. 263 @type path: str 264 @keyword omode: opening mode. 265 @type omode: int 266 """ 267 from ctypes import c_char_p 268 self.lib = get_lib(libname) 269 self.ncid = None 270 # set up return type. 271 self.nc_strerror.restype = c_char_p 272 self.nc_inq_libvers.restype = c_char_p 273 # open file. 274 if path is not None: 275 self.open_file(path, omode)
276 - def __getattr__(self, key):
277 if key.startswith('nc_'): 278 return getattr(self.lib, key)
279
280 - def open_file(self, path, omode=None):
281 """ 282 Open a NetCDF file. 283 284 @keyword path: the file to open. 285 @type path: str 286 @keyword omode: opening mode. 287 @type omode: int 288 @return: ncid 289 @rtype: int 290 """ 291 from ctypes import c_int, byref 292 omode = self.NC_NOWRITE if omode is None else omode 293 if self.ncid is not None: 294 raise IOError('ncid %d has been opened'%self.ncid) 295 ncid = c_int() 296 retval = self.nc_open(path, omode, byref(ncid)) 297 if retval != self.NC_NOERR: 298 raise IOError(self.nc_strerror(retval)) 299 self.ncid = ncid.value 300 return self.ncid
301 - def close_file(self):
302 """ 303 Close the associated NetCDF file. 304 305 @return: nothing 306 """ 307 retval = self.nc_close(self.ncid) 308 if retval != self.NC_NOERR: 309 raise IOError(self.nc_strerror(retval)) 310 self.ncid = None
311
312 - def get_dim(self, name):
313 """ 314 Get the dimension of the given name. 315 316 @param name: the name of the dimension. 317 @type name: str 318 @return: the dimension (length). 319 @rtype: int 320 """ 321 from ctypes import c_int, c_long, byref 322 dimid = c_int() 323 length = c_long() 324 retval = self.nc_inq_dimid(self.ncid, name, byref(dimid)) 325 if retval != self.NC_NOERR: 326 raise IOError(self.nc_strerror(retval)) 327 retval = self.nc_inq_dimlen(self.ncid, dimid, byref(length)) 328 if retval != self.NC_NOERR: 329 raise IOError(self.nc_strerror(retval)) 330 return length.value
331
332 - def get_array(self, name, shape, dtype):
333 """ 334 Load ndarray from netCDF file. 335 336 @param name: the data to be loaded. 337 @type name: str 338 @param shape: the shape of ndarray. 339 @type shape: tuple 340 @param dtype: the dtype of ndarray. 341 @type dtype: str 342 @return: the loaded ndarray. 343 @rtype: numpy.ndarray 344 """ 345 from ctypes import POINTER, c_int, c_float, c_double, byref 346 from numpy import empty 347 # get value ID. 348 varid = c_int() 349 retval = self.nc_inq_varid(self.ncid, name, byref(varid)) 350 if retval != self.NC_NOERR: 351 raise IOError(self.nc_strerror(retval)) 352 # prepare array and loader. 353 arr = empty(shape, dtype=dtype) 354 if dtype == 'int32': 355 func = self.nc_get_var_int 356 elif dtype == 'float32': 357 func = self.nc_get_var_float 358 elif dtype == 'float64': 359 func = self.nc_get_var_double 360 else: 361 raise TypeError('now surrport only int, float, double, and char') 362 # load array. 363 retval = func(self.ncid, varid, arr.ctypes._as_parameter_) 364 if retval != self.NC_NOERR: 365 raise IOError(self.nc_strerror(retval)) 366 return arr
367
368 - def get_lines(self, name, shape):
369 """ 370 Load string from netCDF file. 371 372 @param name: the data to be loaded. 373 @type name: str 374 @param shape: the shape of ndarray. Must be 1 or 2. 375 @type shape: tuple 376 @return: the loaded ndarray. 377 @rtype: numpy.ndarray 378 """ 379 from ctypes import POINTER, c_int, c_char, byref 380 from numpy import empty 381 if len(shape) > 2: 382 raise IndexError('array should have no more than two dimension') 383 # get value ID. 384 varid = c_int() 385 retval = self.nc_inq_varid(self.ncid, name, byref(varid)) 386 if retval != self.NC_NOERR: 387 raise IOError(self.nc_strerror(retval)) 388 # prepare array and loader. 389 arr = empty(shape, dtype='byte') 390 # load string data. 391 retval = self.nc_get_var_text(self.ncid, varid, 392 arr.ctypes._as_parameter_) 393 if retval != self.NC_NOERR: 394 raise IOError(self.nc_strerror(retval)) 395 # convert to string. 396 shape = (1, shape[0]) if len(shape) < 2 else shape 397 arr = arr.reshape(shape) 398 lines = [] 399 for ii in range(shape[0]): 400 for ij in range(shape[1]): 401 if arr[ii,ij] == 0: 402 lines.append(arr[ii,:ij].tostring()) 403 break 404 if len(lines) <= ii: 405 lines.append(arr[ii,:].tostring()) 406 return lines
407