cdo 1.2.7 → 1.3.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.
Files changed (6) hide show
  1. data/gemspec +2 -2
  2. data/lib/cdo.rb +204 -279
  3. data/lib/cdo_lib.rb +415 -0
  4. data/test/test_cdo.rb +180 -181
  5. metadata +3 -3
  6. data/lib/cdo_oo.rb +0 -301
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cdo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.7
4
+ version: 1.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-11-19 00:00:00.000000000 Z
12
+ date: 2016-01-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: unifiedPlot
@@ -34,7 +34,7 @@ extensions: []
34
34
  extra_rdoc_files: []
35
35
  files:
36
36
  - lib/cdo.rb
37
- - lib/cdo_oo.rb
37
+ - lib/cdo_lib.rb
38
38
  - gemspec
39
39
  - LICENSE
40
40
  - test/test_cdo.rb
@@ -1,301 +0,0 @@
1
- require 'pp'
2
- require 'open3'
3
- require 'logger'
4
- require 'stringio'
5
-
6
- class Cdo
7
- OutputOperatorsPattern = /(diff|info|output|griddes|zaxisdes|show|ncode|ndate|nlevel|nmon|nvar|nyear|ntime|npar|gradsdes|pardes)/
8
-
9
- attr_accessor :cdo, :returnCdf, :forceOutput, :env, :debug
10
- attr_reader :operators, :filetypes
11
-
12
- def initialize(cdo: 'cdo',
13
- returnCdf: false,
14
- returnFalseOnError: false,
15
- forceOutput: true,
16
- env: {},
17
- debug: false)
18
-
19
- # setup path to cdo executable
20
- @cdo = ENV.has_key?('CDO') ? ENV['CDO'] : cdo
21
-
22
- @operators = getOperators
23
- @returnCdf = returnCdf
24
- @forceOutput = forceOutput
25
- @env = env
26
- @debug = ENV.has_key?('DEBUG') ? true : debug
27
-
28
- @filetypes = getFiletypes
29
- @returnFalseOnError = returnFalseOnError
30
-
31
- end
32
-
33
- private # {{{
34
-
35
- # split arguments into hash-like args and the rest
36
- def Cdo.parseArgs(args)
37
- operatorArgs = args.reject {|a| a.class == Hash}
38
- opts = operatorArgs.empty? ? '' : ',' + operatorArgs.join(',')
39
- io = args.find {|a| a.class == Hash}
40
- io = {} if io.nil?
41
- args.delete_if {|a| a.class == Hash}
42
- # join input streams together if possible
43
- io[:input] = io[:input].join(' ') if io[:input].respond_to?(:join)
44
-
45
- return [io,opts]
46
- end
47
-
48
- # collect the complete list of possible operators
49
- def getOperators
50
- cmd = @cdo + ' 2>&1'
51
- help = IO.popen(cmd).readlines.map {|l| l.chomp.lstrip}
52
- if 5 >= help.size
53
- warn "Operators could not get listed by running the CDO binary (#{@cdo})"
54
- pp help if @debug
55
- exit
56
- end
57
-
58
- @operators = help[(help.index("Operators:")+1)..help.index(help.find {|v| v =~ /CDO version/}) - 2].join(' ').split
59
- end
60
-
61
- # get supported IO filetypes form the binary
62
- def getFiletypes
63
- _, _, stderr, _ = Open3.popen3(@cdo + " -V")
64
- supported = stderr.readlines.map(&:chomp)
65
-
66
- supported.grep(/(Filetypes)/)[0].split(':')[1].split.map(&:downcase)
67
- end
68
-
69
-
70
- # Execute the given cdo call and return all outputs
71
- def _call(cmd)
72
- if (@debug)
73
- puts '# DEBUG ====================================================================='
74
- pp @env unless @env.empty?
75
- puts 'CMD: '
76
- puts cmd
77
- puts '# DEBUG ====================================================================='
78
- end
79
-
80
- stdin, stdout, stderr, wait_thr = Open3.popen3(@env,cmd)
81
- {
82
- :stdout => stdout.read,
83
- :stderr => stderr.read,
84
- :returncode => wait_thr.value.exitstatus
85
- }
86
- end
87
-
88
- # Error handling for the given command
89
- def _hasError(cmd,retvals)
90
- if @debug
91
- puts("RETURNCODE: #{retvals[:returncode]}")
92
- end
93
- if ( 0 != retvals[:returncode] )
94
- puts("Error in calling:")
95
- puts(">>> "+cmd+"<<<")
96
- puts(retvals[:stderr])
97
- return true
98
- else
99
- return false
100
- end
101
- end
102
-
103
- # command execution wrapper, which handles the possible return types
104
- def _run(cmd,ofile='',options=nil,returnCdf=false,force=nil,returnArray=nil,returnMaArray=nil)
105
- options = options.to_s
106
-
107
- options << '-f nc' if options.empty? and ( \
108
- ( returnCdf ) or \
109
- ( not returnArray.nil? ) or \
110
- ( not returnMaArray.nil?) \
111
- )
112
- cmd = "#{@cdo} -O #{options} #{cmd} "
113
-
114
- case ofile
115
- when $stdout
116
- retvals = _call(cmd)
117
- @logger.info(cmd+"\n") if @log
118
- unless _hasError(cmd,retvals)
119
- return retvals[:stdout].split($/).map {|l| l.chomp.strip}
120
- else
121
- raise ArgumentError,"CDO did NOT run successfully!"
122
- end
123
- else
124
- force = @forceOutput if force.nil?
125
- if force or not File.exists?(ofile.to_s)
126
- ofile = MyTempfile.path if ofile.nil?
127
- cmd << "#{ofile}"
128
- retvals = _call(cmd)
129
- @logger.info(cmd+"\n") if @log
130
- if _hasError(cmd,retvals)
131
- raise ArgumentError,"CDO did NOT run successfully!"
132
- end
133
- else
134
- warn "Use existing file '#{ofile}'" if @debug
135
- end
136
- end
137
-
138
- if not returnArray.nil?
139
- readArray(ofile,returnArray)
140
- elsif not returnMaArray.nil?
141
- readMaArray(ofile,returnMaArray)
142
- elsif returnCdf or @returnCdf
143
- readCdf(ofile)
144
- else
145
- ofile
146
- end
147
- end
148
-
149
- # Implementation of operator calls using ruby's meta programming skills
150
- #
151
- # args is expected to look like
152
- # [opt1,...,optN,:input => iStream,:output => oStream, :options => ' ']
153
- # where iStream could be another CDO call (timmax(selname(Temp,U,V,ifile.nc))
154
- def method_missing(sym, *args, &block)
155
- puts "Operator #{sym.to_s} is called" if @debug
156
-
157
- if @operators.include?(sym.to_s)
158
- io, opts = Cdo.parseArgs(args)
159
- if OutputOperatorsPattern.match(sym.to_s)
160
- _run(" -#{sym.to_s}#{opts} #{io[:input]} ",$stdout)
161
- else
162
- _run(" -#{sym.to_s}#{opts} #{io[:input]} ",io[:output],io[:options],io[:returnCdf],io[:force],io[:returnArray],io[:returnMaArray])
163
- end
164
- else
165
- return false if @returnFalseOnError
166
- raise ArgumentError,"Operator #{sym.to_s} not found"
167
- end
168
- end
169
-
170
- # load the netcdf bindings
171
- def loadCdf
172
- begin
173
- require "numru/netcdf_miss"
174
- rescue LoadError
175
- warn "Could not load ruby's netcdf bindings. Please install it."
176
- raise
177
- end
178
- end
179
-
180
- # }}}
181
-
182
- public # {{{
183
-
184
- # show Cdo's built-in help for given operator
185
- def help(operator=nil)
186
- if operator.nil?
187
- puts _call([@cdo,'-h'].join(' '))[:stderr]
188
- else
189
- operator = operator.to_s
190
- puts _call([@cdo,'-h',operator].join(' ')).values_at(:stdout,:stderr)
191
- end
192
- end
193
-
194
- # check if cdo backend is working
195
- def check
196
- return false unless system("#@cdo -h 1>/dev/null 2>&1")
197
-
198
- retval = _call("#@cdo -V")
199
- pp retval if @debug
200
-
201
- return true
202
- end
203
-
204
- def version
205
- IO.popen("#{@cdo} -V 2>&1").readlines.first.split(/version/i).last.strip.split(' ').first
206
- end
207
-
208
- # return cdf handle to given file readonly
209
- def readCdf(iFile)
210
- loadCdf
211
- NumRu::NetCDF.open(iFile)
212
- end
213
-
214
- # return cdf handle opened in append more
215
- def openCdf(iFile)
216
- loadCdf
217
- NumRu::NetCDF.open(iFile,'r+')
218
- end
219
-
220
- # return narray for given variable name
221
- def readArray(iFile,varname)
222
- filehandle = readCdf(iFile)
223
- if filehandle.var_names.include?(varname)
224
- # return the data array
225
- filehandle.var(varname).get
226
- else
227
- raise ArgumentError, "Cannot find variable '#{varname}'"
228
- end
229
- end
230
-
231
- # return a masked array for given variable name
232
- def readMaArray(iFile,varname)
233
- filehandle = readCdf(iFile)
234
- if filehandle.var_names.include?(varname)
235
- # return the data array
236
- filehandle.var(varname).get_with_miss
237
- else
238
- raise ArgumentError,"Cannot find variable '#{varname}'"
239
- end
240
- end
241
-
242
- # }}}
243
-
244
- # Addional operators: {{{
245
-
246
- # compute vertical boundary levels from full levels
247
- def boundaryLevels(args)
248
- ilevels = self.showlevel(:input => args[:input])[0].split.map(&:to_f)
249
- bound_levels = Array.new(ilevels.size+1)
250
- bound_levels[0] = 0
251
- (1..ilevels.size).each {|i|
252
- bound_levels[i] =bound_levels[i-1] + 2*(ilevels[i-1]-bound_levels[i-1])
253
- }
254
- bound_levels
255
- end
256
-
257
- # compute level thicknesses from given full levels
258
- def thicknessOfLevels(args)
259
- bound_levels = self.boundaryLevels(args)
260
- delta_levels = []
261
- bound_levels.each_with_index {|v,i|
262
- next if 0 == i
263
- delta_levels << v - bound_levels[i-1]
264
- }
265
- delta_levels
266
- end
267
-
268
- # }}}
269
-
270
- end
271
- #
272
- # Helper module for easy temp file handling
273
- module MyTempfile
274
- require 'tempfile'
275
- @@_tempfiles = []
276
- @@persistent_tempfiles = false
277
- @@N = 10000000
278
-
279
- def MyTempfile.setPersist(value)
280
- @@persistent_tempfiles = value
281
- end
282
-
283
- def MyTempfile.path
284
- unless @@persistent_tempfiles
285
- t = Tempfile.new(self.class.to_s)
286
- @@_tempfiles << t
287
- @@_tempfiles << t.path
288
- t.path
289
- else
290
- t = "_"+rand(@@N).to_s
291
- @@_tempfiles << t
292
- t
293
- end
294
- end
295
-
296
- def MyTempfile.showFiles
297
- @@_tempfiles.each {|f| print(f+" ") if f.kind_of? String}
298
- end
299
- end
300
-
301
- #vim:fdm=marker