cdo 1.2.7 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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