carray-netcdf 2.0.0 → 2.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +73 -2
- data/CONTRIBUTING.md +3 -3
- data/README.md +53 -21
- data/bin/ncheader +43 -8
- data/bin/ncvar +48 -12
- data/carray-netcdf.gemspec +4 -4
- data/docs/00_introduction.md +19 -30
- data/docs/01_getting_started.md +16 -25
- data/docs/02_reading.md +26 -26
- data/docs/03_writing.md +53 -44
- data/docs/04_codec.md +74 -82
- data/docs/05_groups.md +5 -5
- data/docs/06_low_level_api.md +161 -0
- data/docs/07_cookbook.md +167 -126
- data/docs/08_cheatsheet.md +77 -52
- data/docs/09_cli.md +4 -4
- data/lib/carray/netcdf/codec.rb +223 -29
- data/lib/carray/netcdf/dim.rb +8 -5
- data/lib/carray/netcdf/file.rb +184 -57
- data/lib/carray/netcdf/group.rb +301 -43
- data/lib/carray/netcdf/var.rb +311 -31
- data/lib/carray/netcdf/version.rb +1 -1
- data/lib/carray/netcdf.rb +99 -17
- metadata +7 -7
- data/docs/06_layer1.md +0 -160
data/lib/carray/netcdf.rb
CHANGED
|
@@ -3,7 +3,7 @@ module CA
|
|
|
3
3
|
#
|
|
4
4
|
# CA::NetCDF ← this module
|
|
5
5
|
#
|
|
6
|
-
#
|
|
6
|
+
# The low-level API (this module's functions): a CArray-flavored view of the netcdf
|
|
7
7
|
# gem's `nc_*` API. `extend self` exposes every method both as a module
|
|
8
8
|
# function and as an instance method, so it is usable two ways:
|
|
9
9
|
#
|
|
@@ -20,15 +20,15 @@ module CA
|
|
|
20
20
|
# speak CArray; scalar (`var1`), definition, inquiry, attribute and group
|
|
21
21
|
# calls fall through to {::NetCDF} verbatim.
|
|
22
22
|
#
|
|
23
|
-
#
|
|
23
|
+
# The low-level API is raw values only: no convention decoding, no mask handling.
|
|
24
24
|
# Reads return unmasked CArrays; writing a masked CArray raises. The mask
|
|
25
|
-
# / CF story lives in
|
|
25
|
+
# / CF story lives in the high-level API (the {File} / {Var} / {Codec} classes).
|
|
26
26
|
module NetCDF
|
|
27
27
|
|
|
28
28
|
include ::NetCDF
|
|
29
29
|
extend self
|
|
30
30
|
|
|
31
|
-
# @!group
|
|
31
|
+
# @!group Low-level API — array reads
|
|
32
32
|
|
|
33
33
|
# Read an entire variable.
|
|
34
34
|
#
|
|
@@ -82,17 +82,29 @@ module CA
|
|
|
82
82
|
# @param buffer [CArray, MemoryView, nil]
|
|
83
83
|
# @return [CArray]
|
|
84
84
|
def nc_get_varm(ncid, varid, start, count, stride, imap, buffer = nil)
|
|
85
|
+
# A buffer this function allocates is laid out row-major over
|
|
86
|
+
# `count`; any other imap needs a buffer laid out for it.
|
|
87
|
+
if buffer.nil? && imap != row_major_imap(count)
|
|
88
|
+
raise ArgumentError,
|
|
89
|
+
"nc_get_varm with imap #{imap.inspect} needs a buffer; without one, " \
|
|
90
|
+
"imap must be row-major over count (#{row_major_imap(count).inspect})"
|
|
91
|
+
end
|
|
85
92
|
::NetCDF.nc_get_varm(ncid, varid, start, count, stride, imap,
|
|
86
93
|
buffer || new_buffer(ncid, varid, count))
|
|
87
94
|
end
|
|
88
95
|
|
|
96
|
+
# @api private
|
|
97
|
+
private def row_major_imap(count)
|
|
98
|
+
count.each_index.map { |i| count[(i + 1)..].inject(1, :*) }
|
|
99
|
+
end
|
|
100
|
+
|
|
89
101
|
# @!endgroup
|
|
90
102
|
|
|
91
|
-
# @!group
|
|
103
|
+
# @!group Low-level API — array writes
|
|
92
104
|
#
|
|
93
105
|
# Accept a CArray as the buffer. netcdf requires a C-contiguous buffer,
|
|
94
106
|
# so non-entity views are materialized; masked input is rejected (fill
|
|
95
|
-
# encoding is
|
|
107
|
+
# encoding is the high-level API's job).
|
|
96
108
|
|
|
97
109
|
# Write an entire variable.
|
|
98
110
|
#
|
|
@@ -141,6 +153,11 @@ module CA
|
|
|
141
153
|
_, _, base, _, klass = nc_inq_user_type(ncid, xtype)
|
|
142
154
|
xtype = base if klass == NC_ENUM
|
|
143
155
|
end
|
|
156
|
+
if xtype == NC_STRING
|
|
157
|
+
raise NotImplementedError,
|
|
158
|
+
"no CArray type for an NC_STRING variable; read it with " \
|
|
159
|
+
"NetCDF.nc_get_var, which returns Strings"
|
|
160
|
+
end
|
|
144
161
|
data_type = NC_TO_CA[xtype] or
|
|
145
162
|
raise NotImplementedError, "no CArray type to read nc_type #{xtype} into; pass a buffer"
|
|
146
163
|
CArray.new(data_type, shape.empty? ? [1] : shape)
|
|
@@ -148,14 +165,18 @@ module CA
|
|
|
148
165
|
|
|
149
166
|
# A C-contiguous, unmasked CArray ready to hand to libnetcdf:
|
|
150
167
|
# materialize non-entity views, refuse masked input (encoding a fill
|
|
151
|
-
# value is the
|
|
168
|
+
# value is the codec's decision, not the low-level API's).
|
|
152
169
|
# @api private
|
|
153
170
|
private def writable_buffer(ca)
|
|
154
171
|
return ca unless ca.is_a?(CArray)
|
|
172
|
+
# A mask with nothing masked is only carried along: drop it, so the
|
|
173
|
+
# values go out as they are.
|
|
174
|
+
return ca.strip_mask(0) if ca.has_mask? && ! ca.any_masked?
|
|
155
175
|
if ca.has_mask?
|
|
156
176
|
raise ArgumentError,
|
|
157
|
-
"cannot write a masked CArray
|
|
158
|
-
"(e.g. #strip_mask(fill))
|
|
177
|
+
"cannot write a masked CArray without a codec to encode the mask; " \
|
|
178
|
+
"strip the mask first (e.g. #strip_mask(fill)), or write through " \
|
|
179
|
+
"a codec such as CF (a Conventions attribute naming CF selects it)"
|
|
159
180
|
end
|
|
160
181
|
ca.entity? ? ca : ca.copy
|
|
161
182
|
end
|
|
@@ -192,7 +213,8 @@ module CA
|
|
|
192
213
|
# @param type [Symbol, String, Integer]
|
|
193
214
|
# @return [Integer] an `NC_*` type constant
|
|
194
215
|
# @raise [ArgumentError] on an unrecognized type name
|
|
195
|
-
|
|
216
|
+
# @api private
|
|
217
|
+
private def nc_type_for(type)
|
|
196
218
|
return type if type.is_a?(Integer)
|
|
197
219
|
TYPE_TO_NC[type.to_s.to_sym] or
|
|
198
220
|
raise ArgumentError, "unknown NetCDF variable type: #{type.inspect}"
|
|
@@ -224,7 +246,8 @@ module CA
|
|
|
224
246
|
# @param value [Object] Ruby value or CArray
|
|
225
247
|
# @param nc_type [Integer] target `NC_*` type
|
|
226
248
|
# @return [void]
|
|
227
|
-
|
|
249
|
+
# @api private
|
|
250
|
+
private def put_att_typed(ncid, varid, name, value, nc_type)
|
|
228
251
|
suffix = PUT_ATT_SUFFIX[nc_type]
|
|
229
252
|
return nc_put_att(ncid, varid, name, value) unless suffix
|
|
230
253
|
fn = "nc_put_att_#{suffix}"
|
|
@@ -250,12 +273,69 @@ module CA
|
|
|
250
273
|
# @api private
|
|
251
274
|
private def put_att_takes_xtype?(fn) = PUT_ATT_XTYPE[fn]
|
|
252
275
|
|
|
276
|
+
# Marks an attribute {#readable_att} could not read.
|
|
277
|
+
# @api private
|
|
278
|
+
UNREADABLE_ATT = Object.new.freeze
|
|
279
|
+
|
|
280
|
+
# An attribute's value, or {UNREADABLE_ATT} with a warning when the
|
|
281
|
+
# netcdf gem cannot read its type (an enum-typed `_FillValue`, as
|
|
282
|
+
# netCDF4-python writes, is one): one such attribute should not stop
|
|
283
|
+
# the whole file from opening.
|
|
284
|
+
# @api private
|
|
285
|
+
private def readable_att(ncid, varid, name, owner)
|
|
286
|
+
value = nc_get_att(ncid, varid, name)
|
|
287
|
+
# A C writer's text often ends in a NUL; ncdump and xarray drop it.
|
|
288
|
+
# Done on the bytes: the text need not be valid UTF-8.
|
|
289
|
+
return value unless value.is_a?(String) && value.end_with?("\0")
|
|
290
|
+
String.new(value.b.sub(/\0+\z/n, ""), encoding: value.encoding)
|
|
291
|
+
rescue NotImplementedError => e
|
|
292
|
+
warn "carray-netcdf: attribute #{name.inspect} of #{owner} skipped: #{e.message}"
|
|
293
|
+
UNREADABLE_ATT
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# An attribute value frozen through, so a caller changing what `atts`
|
|
297
|
+
# returned cannot change what the codec reads next.
|
|
298
|
+
# @api private
|
|
299
|
+
private def deep_freeze(value)
|
|
300
|
+
value.each { |x| x.freeze } if value.is_a?(Array)
|
|
301
|
+
value.freeze
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Whether an `_Unsigned` attribute value says "true".
|
|
305
|
+
# @api private
|
|
306
|
+
private def unsigned_flag?(value) = value.to_s.strip.downcase == "true"
|
|
307
|
+
|
|
308
|
+
# Signed bits per `NC_*` type, for {#signed_wrap}.
|
|
309
|
+
# @api private
|
|
310
|
+
SIGNED_BITS = { NC_BYTE => 8, NC_SHORT => 16, NC_INT => 32, NC_INT64 => 64 }.freeze
|
|
311
|
+
|
|
312
|
+
# An `_Unsigned` variable stores unsigned values in a signed type, and
|
|
313
|
+
# its fill / range attributes are read in unsigned space; one given in
|
|
314
|
+
# unsigned space (255 for a byte) is written as the signed value with
|
|
315
|
+
# the same bits (-1).
|
|
316
|
+
# @api private
|
|
317
|
+
private def signed_wrap(value, var_type)
|
|
318
|
+
bits = SIGNED_BITS[var_type] or return value
|
|
319
|
+
wrap = ->(x) { x.is_a?(Integer) && x >= 2**(bits - 1) && x < 2**bits ? x - 2**bits : x }
|
|
320
|
+
value.is_a?(Array) ? value.map(&wrap) : wrap.(value)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# A CArray attribute value as the flat Array the netcdf gem takes. An
|
|
324
|
+
# attribute is one-dimensional, and has no masked elements.
|
|
325
|
+
# @api private
|
|
326
|
+
private def attribute_values(name, ca)
|
|
327
|
+
if ca.any_masked?
|
|
328
|
+
raise ArgumentError, "attribute #{name.inspect} has masked elements"
|
|
329
|
+
end
|
|
330
|
+
ca.to_a.flatten
|
|
331
|
+
end
|
|
332
|
+
|
|
253
333
|
# Write a variable attribute the way {Group#def_var} does:
|
|
254
334
|
#
|
|
255
|
-
# - a
|
|
256
|
-
#
|
|
257
|
-
#
|
|
258
|
-
#
|
|
335
|
+
# - a value for a {PACKED_SPACE_ATTS} name, a CArray included, takes
|
|
336
|
+
# the variable's on-disk type `var_type`, which libnetcdf requires of
|
|
337
|
+
# `_FillValue` and CF asks of the others;
|
|
338
|
+
# - another CArray keeps its own type;
|
|
259
339
|
# - anything else is left to the generic `nc_put_att` inference.
|
|
260
340
|
#
|
|
261
341
|
# @param ncid [Integer]
|
|
@@ -265,9 +345,11 @@ module CA
|
|
|
265
345
|
# @param var_type [Integer] the variable's `NC_*` type
|
|
266
346
|
# @return [void]
|
|
267
347
|
# @api private
|
|
268
|
-
def put_var_att(ncid, varid, name, value, var_type)
|
|
348
|
+
private def put_var_att(ncid, varid, name, value, var_type, unsigned: false)
|
|
349
|
+
value = attribute_values(name, value) if value.is_a?(CArray) && PACKED_SPACE_ATTS.include?(name)
|
|
350
|
+
value = signed_wrap(value, var_type) if unsigned && PACKED_SPACE_ATTS.include?(name)
|
|
269
351
|
if value.is_a?(CArray)
|
|
270
|
-
put_att_typed(ncid, varid, name, value
|
|
352
|
+
put_att_typed(ncid, varid, name, attribute_values(name, value), TYPE_TO_NC[value.data_type])
|
|
271
353
|
elsif PACKED_SPACE_ATTS.include?(name)
|
|
272
354
|
put_att_typed(ncid, varid, name, value, var_type)
|
|
273
355
|
else
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: carray-netcdf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- himotoyoshi
|
|
@@ -29,20 +29,20 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 0.
|
|
32
|
+
version: 0.2.0
|
|
33
33
|
- - "<"
|
|
34
34
|
- !ruby/object:Gem::Version
|
|
35
|
-
version: '0.
|
|
35
|
+
version: '0.3'
|
|
36
36
|
type: :runtime
|
|
37
37
|
prerelease: false
|
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
39
39
|
requirements:
|
|
40
40
|
- - ">="
|
|
41
41
|
- !ruby/object:Gem::Version
|
|
42
|
-
version: 0.
|
|
42
|
+
version: 0.2.0
|
|
43
43
|
- - "<"
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: '0.
|
|
45
|
+
version: '0.3'
|
|
46
46
|
description: |
|
|
47
47
|
A NetCDF interface for CArray, built on the netcdf gem. Data is
|
|
48
48
|
exchanged as CArray at every level, through Ruby's MemoryView protocol,
|
|
@@ -74,7 +74,7 @@ files:
|
|
|
74
74
|
- docs/03_writing.md
|
|
75
75
|
- docs/04_codec.md
|
|
76
76
|
- docs/05_groups.md
|
|
77
|
-
- docs/
|
|
77
|
+
- docs/06_low_level_api.md
|
|
78
78
|
- docs/07_cookbook.md
|
|
79
79
|
- docs/08_cheatsheet.md
|
|
80
80
|
- docs/09_cli.md
|
|
@@ -97,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
97
97
|
requirements:
|
|
98
98
|
- - ">="
|
|
99
99
|
- !ruby/object:Gem::Version
|
|
100
|
-
version: 3.
|
|
100
|
+
version: '3.2'
|
|
101
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
102
|
requirements:
|
|
103
103
|
- - ">="
|
data/docs/06_layer1.md
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# Layer 1 — raw `nc_*` API
|
|
2
|
-
|
|
3
|
-
Everyday code uses layer 2 (`File`, `Var`, ...). Layer 1 is the CArray-flavored view of the netcdf gem's `nc_*` functions — same names, same argument order, but numeric buffers are CArrays.
|
|
4
|
-
|
|
5
|
-
Use it when:
|
|
6
|
-
|
|
7
|
-
- You want to open by ncid or work outside the `File` object model.
|
|
8
|
-
- You want to stream a very large variable into a preallocated buffer (`nc_get_vara` with an explicit buffer = zero allocation per read).
|
|
9
|
-
- You're bridging to code that already speaks the C `nc_*` idiom.
|
|
10
|
-
- You want to skip the codec and every layer-2 semantics for a specific operation.
|
|
11
|
-
|
|
12
|
-
## The module
|
|
13
|
-
|
|
14
|
-
`CA::NetCDF` itself IS layer 1. It `include`s `::NetCDF` (the netcdf gem's module) so all `nc_*` functions and `NC_*` constants pass through, and overrides only the array I/O to speak CArray.
|
|
15
|
-
|
|
16
|
-
```ruby
|
|
17
|
-
require "carray-netcdf"
|
|
18
|
-
NC = CA::NetCDF
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Two idioms:
|
|
22
|
-
|
|
23
|
-
```ruby
|
|
24
|
-
# (a) call style — module functions
|
|
25
|
-
NC.nc_get_vara(ncid, varid, [0, 0], [3, 4]) # => CArray
|
|
26
|
-
NC::NC_DOUBLE # constants inherited from ::NetCDF
|
|
27
|
-
|
|
28
|
-
# (b) mixin style — `nc_*` become bare methods in your class
|
|
29
|
-
class Reader
|
|
30
|
-
include CA::NetCDF
|
|
31
|
-
def read_all(ncid, varid) = nc_get_var(ncid, varid) # bare, returns CArray
|
|
32
|
-
def define(ncid, name) = nc_def_dim(ncid, name, 100) # passes through
|
|
33
|
-
end
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## What layer 1 overrides vs passes through
|
|
37
|
-
|
|
38
|
-
Overridden (numeric array I/O):
|
|
39
|
-
|
|
40
|
-
- `nc_get_var`, `nc_get_vara`, `nc_get_vars`, `nc_get_varm`
|
|
41
|
-
- `nc_put_var`, `nc_put_vara`, `nc_put_vars`, `nc_put_varm`
|
|
42
|
-
|
|
43
|
-
Everything else — `nc_open`, `nc_create`, `nc_def_dim`, `nc_def_var`, `nc_put_att`, `nc_get_att`, `nc_inq_*`, `nc_def_grp`, `nc_close`, `NC_DOUBLE`, `NC_CLOBBER`, ... — comes straight from the netcdf gem, which this layer includes without modifying.
|
|
44
|
-
|
|
45
|
-
## `nc_get_var*` — buffer-less returns a CArray
|
|
46
|
-
|
|
47
|
-
Without a buffer argument, layer 1 allocates a CArray of the requested shape, typed from the variable's on-disk type (`NC_CHAR` as `:uint8`, an enum as its base type), and reads into it:
|
|
48
|
-
|
|
49
|
-
```ruby
|
|
50
|
-
ncid = ::NetCDF.nc_open("data.nc", ::NetCDF::NC_NOWRITE)
|
|
51
|
-
vid = ::NetCDF.nc_inq_varid(ncid, "temp")
|
|
52
|
-
|
|
53
|
-
ca = NC.nc_get_var(ncid, vid) # whole variable -> CArray
|
|
54
|
-
ca = NC.nc_get_vara(ncid, vid, [0, 0], [3, 4]) # hyperslab -> CArray
|
|
55
|
-
ca = NC.nc_get_vars(ncid, vid, [0, 0], [3, 4], [1, 2]) # strided -> CArray
|
|
56
|
-
ca = NC.nc_get_varm(ncid, vid, [0, 0], [3, 4], [1, 1], [4, 1]) # mapped -> CArray
|
|
57
|
-
|
|
58
|
-
::NetCDF.nc_close(ncid)
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
No codec runs — this is raw, no `_FillValue → UNDEF`, no scale/offset, no CATime. Use layer 2 for those.
|
|
62
|
-
|
|
63
|
-
A type with no CArray counterpart — opaque, `NC_STRING` — raises `NotImplementedError`; read an opaque variable into a buffer of your own.
|
|
64
|
-
|
|
65
|
-
## `nc_get_var*` — with a buffer, reads into it
|
|
66
|
-
|
|
67
|
-
Pass an existing CArray and layer 1 asks netcdf to read into that buffer. Perfect for streaming a slab at a time with **zero allocation per read**:
|
|
68
|
-
|
|
69
|
-
```ruby
|
|
70
|
-
ncid = ::NetCDF.nc_open("big.nc", ::NetCDF::NC_NOWRITE)
|
|
71
|
-
vid = ::NetCDF.nc_inq_varid(ncid, "temp")
|
|
72
|
-
|
|
73
|
-
buf = CArray.float32(73, 144) # one time slice
|
|
74
|
-
|
|
75
|
-
365.times do |day|
|
|
76
|
-
NC.nc_get_vara(ncid, vid, [day, 0, 0], [1, 73, 144], buf)
|
|
77
|
-
process(buf) # buf now holds day `day`
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
::NetCDF.nc_close(ncid)
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
The buffer must be C-contiguous (a plain entity CArray is; a view is not — the gem will refuse a non-contiguous buffer). Type conversion is handled by libnetcdf — you can read a short variable into a float64 buffer and libnetcdf converts.
|
|
84
|
-
|
|
85
|
-
## `nc_put_var*` — CArray as buffer
|
|
86
|
-
|
|
87
|
-
Symmetric. A contiguous CArray goes through as-is:
|
|
88
|
-
|
|
89
|
-
```ruby
|
|
90
|
-
NC.nc_put_vara(ncid, vid, [0, 0], [3, 4], CArray.float32(3, 4).seq!)
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
A non-contiguous CArray (a view / slice / reference) is materialized to a contiguous entity before the write:
|
|
94
|
-
|
|
95
|
-
```ruby
|
|
96
|
-
big = CArray.float32(100, 4).seq!
|
|
97
|
-
NC.nc_put_vara(ncid, vid, [0, 0], [3, 4], big[10..12, nil])
|
|
98
|
-
# big[10..12, nil] is a CABlock view; layer 1 does .copy on your behalf
|
|
99
|
-
# and passes the resulting entity to nc_put_vara.
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
A **masked** CArray is *refused*. Layer 1 does no fill encoding — that's layer 2's job (via the codec). Strip the mask first:
|
|
103
|
-
|
|
104
|
-
```ruby
|
|
105
|
-
ca = CA_SHORT([1, 2, 3])
|
|
106
|
-
ca[1] = UNDEF
|
|
107
|
-
NC.nc_put_vara(ncid, vid, [0], [3], ca) # ArgumentError
|
|
108
|
-
NC.nc_put_vara(ncid, vid, [0], [3], ca.strip_mask(-32768)) # OK, fill baked in
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## `nc_get_var1` / `nc_put_var1` — scalar
|
|
112
|
-
|
|
113
|
-
These are pure passthrough (the netcdf gem handles them directly), included in layer 1 so the mixin style has them:
|
|
114
|
-
|
|
115
|
-
```ruby
|
|
116
|
-
value = NC.nc_get_var1(ncid, vid, [0, 0]) # Numeric
|
|
117
|
-
NC.nc_put_var1(ncid, vid, [0, 0], 273.15) # writes one cell
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Layer 2's scalar decode/encode wraps these calls in a 1-cell CArray to run through the codec — see [Codecs](04_codec.md).
|
|
121
|
-
|
|
122
|
-
## Definition, inquiry, attribute — all passthrough
|
|
123
|
-
|
|
124
|
-
The netcdf gem covers everything else. In layer-1 code, use them bare (via mixin) or fully qualified:
|
|
125
|
-
|
|
126
|
-
```ruby
|
|
127
|
-
# create + define
|
|
128
|
-
ncid = NC.nc_create("out.nc", NC::NC_CLOBBER | NC::NC_NETCDF4)
|
|
129
|
-
tid = NC.nc_def_dim(ncid, "time", 24)
|
|
130
|
-
vid = NC.nc_def_var(ncid, "temp", NC::NC_FLOAT, [tid])
|
|
131
|
-
NC.nc_put_att(ncid, vid, "units", "K")
|
|
132
|
-
NC.nc_enddef(ncid)
|
|
133
|
-
|
|
134
|
-
# write and close
|
|
135
|
-
NC.nc_put_var(ncid, vid, CArray.float32(24).seq!)
|
|
136
|
-
NC.nc_close(ncid)
|
|
137
|
-
|
|
138
|
-
# inquire
|
|
139
|
-
ncid = NC.nc_open("out.nc", NC::NC_NOWRITE)
|
|
140
|
-
NC.nc_inq_ndims(ncid) # => 1
|
|
141
|
-
NC.nc_inq_nvars(ncid) # => 1
|
|
142
|
-
NC.nc_inq_varname(ncid, 0) # => "temp"
|
|
143
|
-
NC.nc_inq_vardimid(ncid, 0) # => [0]
|
|
144
|
-
NC.nc_inq_dimlen(ncid, 0) # => 24
|
|
145
|
-
NC.nc_close(ncid)
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Any of these can also be reached as `::NetCDF.nc_*` (unchanged), or through layer 2's higher-level surface.
|
|
149
|
-
|
|
150
|
-
## Coexisting with `::NetCDF` and future `Numo::NetCDF`
|
|
151
|
-
|
|
152
|
-
Layer 1 does **not** modify `::NetCDF`. It only adds behavior in the `CA::NetCDF` namespace. So an app that mixes carray-netcdf with a hypothetical `Numo::NetCDF` gem (which would follow the same pattern in its own namespace) can call:
|
|
153
|
-
|
|
154
|
-
```ruby
|
|
155
|
-
CA::NetCDF.nc_get_vara(ncid, vid, s, c) # -> CArray
|
|
156
|
-
Numo::NetCDF.nc_get_vara(ncid, vid, s, c) # -> Numo::NArray (hypothetical)
|
|
157
|
-
::NetCDF.nc_get_vara(ncid, vid, s, c, buf) # -> buf, any MemoryView producer (untouched)
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
with no clashes. This coexistence is exactly why layer 1 avoids monkey-patching `::NetCDF`.
|