carray-netcdf 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/io/netcdf.rb ADDED
@@ -0,0 +1,533 @@
1
+ require "carray"
2
+ require "carray/netcdflib.so"
3
+
4
+ module NC
5
+
6
+ module_function
7
+
8
+ def nc_decode (fd, varid, data)
9
+ if fill_value = nc_get_att(fd, varid, "_FillValue")
10
+ data[:eq, fill_value] = UNDEF
11
+ end
12
+ if scale_factor = nc_get_att(fd, varid, "scale_factor")
13
+ data *= scale_factor
14
+ end
15
+ if add_offset = nc_get_att(fd, varid, "add_offset")
16
+ data += add_offset
17
+ end
18
+ return data
19
+ end
20
+
21
+ def nc_put_att_simple (fd, varid, name, val)
22
+ case val
23
+ when Float
24
+ nc_put_att(fd, varid, name, CA_DOUBLE(val))
25
+ when Integer
26
+ nc_put_att(fd, varid, name, CA_INT(val))
27
+ else
28
+ nc_put_att(fd, varid, name, val)
29
+ end
30
+ end
31
+
32
+ def nc_put_var_all (fd, varid, val)
33
+ data_type = NC.ca_type(nc_inq_vartype(fd, varid))
34
+ dim = (0...nc_inq_varndims(fd, varid)).collect do |dimid|
35
+ nc_inq_dimlen(fd, dimid)
36
+ end
37
+ data = CArray.new(data_type, dim) { val }
38
+ nc_put_var(fd, varid, data)
39
+ end
40
+
41
+ end
42
+
43
+ class NCObject
44
+
45
+ include NC
46
+
47
+ def get_attributes (file_id, var_id)
48
+ attrs = {}
49
+ varnatts = nc_inq_varnatts(file_id, var_id)
50
+ varnatts.times do |i|
51
+ attname = nc_inq_attname(file_id, var_id, i)
52
+ value = nc_get_att(file_id, var_id, attname)
53
+ attrs[attname] = value
54
+ end
55
+ return attrs.freeze
56
+ end
57
+
58
+ def attribute (name)
59
+ return @attributes[name]
60
+ end
61
+
62
+ attr_reader :attributes
63
+
64
+ end
65
+
66
+ class NCVar < NCObject
67
+
68
+ include NC
69
+
70
+ def initialize (ncfile, var_id)
71
+ @ncfile = ncfile
72
+ @file_id = ncfile.file_id
73
+ @var_id = var_id
74
+ @name = nc_inq_varname(@file_id, var_id)
75
+ @vartype = nc_inq_vartype(@file_id, var_id)
76
+ @dims = ncfile.dims.values_at(*nc_inq_vardimid(@file_id, var_id))
77
+ @shape = @dims.map{|d| d.len}
78
+ @attributes = get_attributes(@file_id, var_id)
79
+ @dims.freeze
80
+ @shape.freeze
81
+ end
82
+
83
+ attr_reader :name, :dims
84
+
85
+ def definition
86
+ {
87
+ type: @vartype,
88
+ dims: dims.map{|x| x.name },
89
+ attributes: @attributes.dup
90
+ }
91
+ end
92
+
93
+ def inspect
94
+ return "#{@name}#{@dims}"
95
+ end
96
+
97
+ def is_dim?
98
+ begin
99
+ nc_inq_dimlen(@file_id, @var_id)
100
+ return true
101
+ rescue RuntimeError
102
+ return false
103
+ end
104
+ end
105
+
106
+ def decode (value)
107
+ if @attributes.has_key?("_FillValue")
108
+ fill_value = @attributes["_FillValue"]
109
+ case value
110
+ when CArray
111
+ value[:eq, fill_value] = UNDEF
112
+ else
113
+ value = UNDEF if value == fill_value
114
+ end
115
+ end
116
+ if @attributes.has_key?("missing_value")
117
+ missing_values = [@attributes["missing_value"]].flatten
118
+ missing_values.each do |mv|
119
+ case value
120
+ when CArray
121
+ value[:eq, mv] = UNDEF
122
+ else
123
+ if value == mv
124
+ value = UNDEF
125
+ break
126
+ end
127
+ end
128
+ end
129
+ end
130
+ if @attributes.has_key?("scale_factor")
131
+ value *= @attributes["scale_factor"]
132
+ end
133
+ if @attributes.has_key?("add_offset")
134
+ value += @attributes["add_offset"]
135
+ end
136
+ return value
137
+ end
138
+
139
+ def to_ca
140
+ return self[]
141
+ end
142
+
143
+ def [] (*argv)
144
+ return get!(*argv)
145
+ end
146
+
147
+ def get (*argv)
148
+ if argv.size > 0 and argv[0].is_a?(Struct::CAIndexInfo)
149
+ info = argv.shift
150
+ else
151
+ info = CArray.scan_index(@shape, argv)
152
+ end
153
+ out = nil
154
+ case info.type
155
+ when CA_REG_ADDRESS
156
+ addr = info.index[0]
157
+ index = []
158
+ (0..@shape.size-1).reverse_each do |i|
159
+ index[i] = addr % @shape[i]
160
+ addr /= @shape[i]
161
+ end
162
+ out = get_var1(*index)
163
+ when CA_REG_FLATTEN
164
+ out = get_var[nil]
165
+ when CA_REG_POINT
166
+ out = get_var1(*info.index)
167
+ when CA_REG_ALL
168
+ out = get_var()
169
+ when CA_REG_BLOCK
170
+ start = []
171
+ count = []
172
+ stride = []
173
+ info.index.each do |idx|
174
+ case idx
175
+ when Array
176
+ start << idx[0]
177
+ count << idx[1]
178
+ stride << idx[2]
179
+ else
180
+ start << idx
181
+ count << 1
182
+ stride << 1
183
+ end
184
+ end
185
+ if stride.all?{|x| x == 1 }
186
+ out = get_vara(start, count)
187
+ else
188
+ out = get_vars(start, count, stride)
189
+ end
190
+ when CA_REG_SELECT, CA_REG_GRID
191
+ out = get_var[*argv]
192
+ else
193
+ raise "invalid index"
194
+ end
195
+ case out
196
+ when CArray
197
+ return out.compact
198
+ else
199
+ return out
200
+ end
201
+ end
202
+
203
+ def get! (*argv)
204
+ info = CArray.scan_index(@shape, argv)
205
+ case info.type
206
+ when CA_REG_METHOD_CALL
207
+ return decode(get_var)[*argv]
208
+ else
209
+ return decode(get(info, *argv))
210
+ end
211
+ end
212
+
213
+ def get_var1 (*index)
214
+ return nc_get_var1(@file_id, @var_id, index)
215
+ end
216
+
217
+ def get_var1! (*index)
218
+ return decode(get_var1(*index))
219
+ end
220
+
221
+ def get_var ()
222
+ return nc_get_var(@file_id, @var_id)
223
+ end
224
+
225
+ def get_var! ()
226
+ return decode(nc_get_var(@file_id, @var_id))
227
+ end
228
+
229
+ def get_vara (start, count)
230
+ return nc_get_vara(@file_id, @var_id, start, count)
231
+ end
232
+
233
+ def get_vara! (start, count)
234
+ return decode(nc_get_vara(@file_id, @var_id, start, count))
235
+ end
236
+
237
+ def get_vars (start, count, stride)
238
+ return nc_get_vars(@file_id, @var_id, start, count, stride)
239
+ end
240
+
241
+ def get_vars! (start, count, stride)
242
+ return decode(nc_get_vars(@file_id, @var_id, start, count, stride))
243
+ end
244
+
245
+ def get_varm (start, count, stride, imap)
246
+ return nc_get_varm(@file_id, @var_id, start, count, stride, imap)
247
+ end
248
+
249
+ def get_varm! (start, count, stride, imap)
250
+ return decode(nc_get_varm(@file_id, @var_id, start, count, stride, imap))
251
+ end
252
+
253
+ end
254
+
255
+ class NCDim < NCObject
256
+
257
+ include NC
258
+
259
+ def initialize (ncfile, dim_id)
260
+ @ncfile = ncfile
261
+ @file_id = ncfile.file_id
262
+ @dim_id = dim_id
263
+ @name = nc_inq_dimname(@file_id, @dim_id)
264
+ @len = nc_inq_dimlen(@file_id, @dim_id)
265
+ end
266
+
267
+ attr_reader :name, :len
268
+
269
+ def definition
270
+ return @len
271
+ end
272
+
273
+ def inspect
274
+ return "#{@name}=#{@len}"
275
+ end
276
+
277
+ def to_i
278
+ return @len
279
+ end
280
+
281
+ def to_ca
282
+ return self[]
283
+ end
284
+
285
+ def [] (*argv)
286
+ return @ncfile[name][*argv]
287
+ end
288
+
289
+ end
290
+
291
+ class NCFile < NCObject
292
+
293
+ include NC
294
+
295
+ def self.open (filename)
296
+ file_id = NC.open(filename)
297
+ return NCFile.new(file_id)
298
+ end
299
+
300
+ def initialize (file_id)
301
+ @file_id = file_id
302
+ @dims = []
303
+ @vars = []
304
+ @name2dim = {}
305
+ @name2var = {}
306
+ @attributes = get_attributes(@file_id, NC::NC_GLOBAL)
307
+ parse_metadata()
308
+ end
309
+
310
+ attr_reader :file_id, :dims, :vars
311
+
312
+ def parse_metadata ()
313
+ ndims = nc_inq_ndims(@file_id)
314
+ ndims.times do |i|
315
+ dim = NCDim.new(self, i)
316
+ @dims[i] = dim
317
+ @name2dim[dim.name] = dim
318
+ end
319
+ @dims.freeze
320
+ @name2dim.freeze
321
+ nvars = nc_inq_nvars(@file_id)
322
+ nvars.times do |i|
323
+ var = NCVar.new(self, i)
324
+ @vars[i] = var
325
+ @name2var[var.name] = var
326
+ end
327
+ @vars.freeze
328
+ @name2var.freeze
329
+ end
330
+
331
+ def definition
332
+ {
333
+ dims: @dims.map{|x| [x.name, x.definition] }.to_h,
334
+ vars: @vars.map{|x| [x.name, x.definition] }.to_h,
335
+ attributes: @attributes.dup
336
+ }
337
+ end
338
+
339
+ def [] (name)
340
+ return @name2var[name]
341
+ end
342
+
343
+ def dim (name)
344
+ return @name2dim[name]
345
+ end
346
+
347
+ def has_dim?(name)
348
+ return @name2dim.has_key?(name)
349
+ end
350
+
351
+ def has_var?(name)
352
+ return @name2var.has_key?(name)
353
+ end
354
+
355
+ end
356
+
357
+ class NCFileWriter
358
+
359
+ include NC
360
+
361
+ class Var
362
+
363
+ include NC
364
+
365
+ def initialize (ncfile, name, definition)
366
+ @ncfile = ncfile
367
+ @file_id = ncfile.file_id
368
+ @definition = definition
369
+ @name = name
370
+ @type = definition[:type]
371
+ @dims = definition[:dims]
372
+ @dim_ids = @dims.map{|key| @ncfile.dim(key).dim_id }
373
+ @shape = @dims.map{|key| @ncfile.dim(key).to_i }
374
+ @var_id = nc_def_var(@file_id, @name, @type, @dim_ids)
375
+ @attributes = definition[:attributes].map{|key, value| [key.to_s, value]}.to_h.freeze
376
+ @attributes.each do |name, value|
377
+ nc_put_att(@file_id, @var_id, name, value)
378
+ end
379
+ end
380
+
381
+ attr_reader :name, :attributes
382
+
383
+ def []= (*argv)
384
+ put(*argv)
385
+ end
386
+
387
+ def put (*argv)
388
+ value = argv.pop
389
+ info = CArray.scan_index(@shape, argv)
390
+ case info.type
391
+ when CA_REG_ADDRESS
392
+ addr = info.index[0]
393
+ index = []
394
+ (0..@shape.size-1).reverse_each do |i|
395
+ index[i] = addr % @shape[i]
396
+ addr /= @shape[i]
397
+ end
398
+ put_var1(index, value)
399
+ when CA_REG_FLATTEN
400
+ put_var(value)
401
+ when CA_REG_POINT
402
+ put_var1(info.index, value)
403
+ when CA_REG_ALL
404
+ put_var(value)
405
+ when CA_REG_BLOCK
406
+ start = []
407
+ count = []
408
+ stride = []
409
+ info.index.each do |idx|
410
+ case idx
411
+ when Array
412
+ start << idx[0]
413
+ count << idx[1]
414
+ stride << idx[2]
415
+ else
416
+ start << idx
417
+ count << 1
418
+ stride << 1
419
+ end
420
+ end
421
+ if stride.all?{|x| x == 1 }
422
+ put_vara(start, count, value)
423
+ else
424
+ put_vars(start, count, stride, value)
425
+ end
426
+ else
427
+ raise "invalid index"
428
+ end
429
+ end
430
+
431
+ def put_var1 (index, value)
432
+ return nc_put_var1(@file_id, @var_id, index, value)
433
+ end
434
+
435
+ def put_var (value)
436
+ return nc_put_var(@file_id, @var_id, value)
437
+ end
438
+
439
+ def put_vara (start, count, value)
440
+ return nc_put_vara(@file_id, @var_id, start, count, value)
441
+ end
442
+
443
+ def put_vars (start, count, stride, value)
444
+ return nc_put_vars(@file_id, @var_id, start, count, stride, value)
445
+ end
446
+
447
+ def get_varm (start, count, stride, imap, value)
448
+ return nc_put_varm(@file_id, @var_id, start, count, stride, imap, value)
449
+ end
450
+
451
+
452
+ end
453
+
454
+ class Dim
455
+
456
+ include NC
457
+
458
+ def initialize (ncfile, name, len)
459
+ @ncfile = ncfile
460
+ @file_id = ncfile.file_id
461
+ @name = name
462
+ @len = len
463
+ @dim_id = nc_def_dim(@file_id, @name, @len)
464
+ end
465
+
466
+ attr_reader :name, :dim_id
467
+
468
+ def to_i
469
+ return @len
470
+ end
471
+
472
+ end
473
+
474
+ def initialize (file)
475
+ @file_id = nc_create(file)
476
+ @dims = []
477
+ @name2dim = {}
478
+ @vars = []
479
+ @name2var = {}
480
+ @attributes = nil
481
+ end
482
+
483
+ attr_reader :file_id
484
+
485
+ def define (definition)
486
+ definition[:dims].each do |name, len|
487
+ dim = Dim.new(self, name.to_s, len.to_i)
488
+ @dims.push dim
489
+ @name2dim[name.to_s] = dim
490
+ end
491
+ definition[:vars].each do |name, info|
492
+ var = Var.new(self, name.to_s, info)
493
+ @vars.push var
494
+ @name2var[name.to_s] = var
495
+ end
496
+ @attributes = definition[:attributes].map{|key, value| [key.to_s, value]}.to_h.freeze
497
+ @attributes.each do |name, value|
498
+ nc_put_att(@file_id, NC_GLOBAL, name, value)
499
+ end
500
+ nc_enddef(@file_id)
501
+ end
502
+
503
+ def copy_dims (nc, name = nil)
504
+ if name
505
+ if @name2dim.has_key?(name) and @name2var.has_key?(name)
506
+ @name2var[name].put(nc[name].get)
507
+ end
508
+ else
509
+ nc.dims.each do |dim|
510
+ if @name2dim.has_key?(dim.name) and @name2var.has_key?(dim.name)
511
+ @name2var[dim.name].put(nc[dim.name].get)
512
+ end
513
+ end
514
+ end
515
+ end
516
+
517
+ def dim (name)
518
+ return @name2dim[name]
519
+ end
520
+
521
+ def [] (name)
522
+ return @name2var[name]
523
+ end
524
+
525
+ def []= (name, value)
526
+ return @name2var[name].put(value)
527
+ end
528
+
529
+ def close
530
+ nc_close(@file_id)
531
+ end
532
+
533
+ end