Class: Cdo

Inherits:
Object
  • Object
show all
Defined in:
lib/cdo.rb

Constant Summary collapse

NoOutputOperators =

hardcoded fallback list of output operators - from 1.8.0 there is an options for this: –operators_no_output this list works for cdo-1.6.4

%w[cdiread cmor codetab conv_cmor_table diff diffc diffn
diffp diffv dump_cmor_table dumpmap filedes gmtcells gmtxyz gradsdes griddes
griddes2 gridverify info infoc infon infop infos infov map ncode ndate
ngridpoints ngrids nlevel nmon npar ntime nvar nyear output outputarr
outputbounds outputboundscpt outputcenter outputcenter2 outputcentercpt
outputext outputf outputfld outputint outputkey outputsrv outputtab outputtri
outputts outputvector outputvrml outputxyz pardes partab partab2 seinfo
seinfoc seinfon seinfop showattribute showatts showattsglob showattsvar
showcode showdate showformat showgrid showlevel showltype showmon showname
showparam showstdname showtime showtimestamp showunit showvar showyear sinfo
sinfoc sinfon sinfop sinfov spartab specinfo tinfo vardes vct vct2 verifygrid
vlist xinfon zaxisdes]
TwoOutputOperators =
%w[trend samplegridicon mrotuv eoftime
eofspatial eof3dtime eof3dspatial eof3d eof complextorect complextopol]
MoreOutputOperators =
%w[distgrid eofcoeff eofcoeff3d intyear scatter splitcode
splitday splitgrid splithour splitlevel splitmon splitname splitparam splitrec
splitseas splitsel splittabnum splitvar splityear splityearmon splitzaxis]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cdo: 'cdo', returnFalseOnError: false, returnNilOnError: false, forceOutput: true, env: {}, debug: false, tempdir: Dir.tmpdir, logging: false, logFile: StringIO.new) ⇒ Cdo

Returns a new instance of Cdo.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/cdo.rb', line 40

def initialize(cdo: 'cdo',
               returnFalseOnError: false,
               returnNilOnError: false,
               forceOutput: true,
               env: {},
               debug: false,
               tempdir: Dir.tmpdir,
               logging: false,
               logFile: StringIO.new)

  # setup path to cdo executable
  @cdo = ENV.has_key?('CDO') ? ENV['CDO'] : cdo

  @operators              = getOperators(@cdo)
  @noOutputOperators      = @operators.select {|op,io| 0 == io}.keys

  @hasNetcdf              = loadOptionalLibs

  @forceOutput            = forceOutput
  @env                    = env
  @debug                  = ENV.has_key?('DEBUG') ? true : debug
  @returnNilOnError       = returnNilOnError
  @returnFalseOnError     = returnFalseOnError

  @tempStore              = CdoTempfileStore.new(tempdir)
  @logging                = logging
  @logFile                = logFile
  @logger                 = Logger.new(@logFile,'a')
  @logger.level           = Logger::INFO

  @config                 = getFeatures

  # create methods to descibe what can be done with the binary
  @config.each {|k,v|
    self.class.send :define_method, k.tr('-','_') do
      v
    end
  }

  # ignore return code 1 for diff operators (from 1.9.6 onwards)
  @exit_success = lambda {|operatorName|
    return 0 if version < '1.9.6'
    return 0 if 'diff' != operatorName[0,4]
    return 1
  }
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *args, &block) ⇒ Object (private)

Implementation of operator calls using ruby’s meta programming skills

args is expected to look like

[opt1,...,optN,:input => iStream,:output => oStream, :options => ' ']
where iStream could be another CDO call (timmax(selname(Temp,U,V,ifile.nc))


334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'lib/cdo.rb', line 334

def method_missing(sym, *args, &block)
  operatorName = sym.to_s
  puts "Operator #{operatorName} is called" if @debug

  # exit eary on missing operator
  unless @operators.include?(operatorName)
    return false if @returnFalseOnError
    raise ArgumentError,"Operator #{operatorName} not found"
  end

  io, operatorParameters = Cdo.parseArgs(args)

  # mark calls for operators without output files
  io[:output] = $stdout if @noOutputOperators.include?(operatorName)

  _run(operatorName,operatorParameters,io)
end

Instance Attribute Details

#cdoObject

Returns the value of attribute cdo.



37
38
39
# File 'lib/cdo.rb', line 37

def cdo
  @cdo
end

#debugObject

Returns the value of attribute debug.



37
38
39
# File 'lib/cdo.rb', line 37

def debug
  @debug
end

#envObject

Returns the value of attribute env.



37
38
39
# File 'lib/cdo.rb', line 37

def env
  @env
end

#filetypesObject (readonly)

Returns the value of attribute filetypes.



38
39
40
# File 'lib/cdo.rb', line 38

def filetypes
  @filetypes
end

#forceOutputObject

Returns the value of attribute forceOutput.



37
38
39
# File 'lib/cdo.rb', line 37

def forceOutput
  @forceOutput
end

#hasNetcdfObject (readonly)

Returns the value of attribute hasNetcdf.



38
39
40
# File 'lib/cdo.rb', line 38

def hasNetcdf
  @hasNetcdf
end

#logFileObject

Returns the value of attribute logFile.



37
38
39
# File 'lib/cdo.rb', line 37

def logFile
  @logFile
end

#loggingObject

Returns the value of attribute logging.



37
38
39
# File 'lib/cdo.rb', line 37

def logging
  @logging
end

#operatorsObject (readonly)

Returns the value of attribute operators.



38
39
40
# File 'lib/cdo.rb', line 38

def operators
  @operators
end

#returnCdfObject

Returns the value of attribute returnCdf.



37
38
39
# File 'lib/cdo.rb', line 37

def returnCdf
  @returnCdf
end

Instance Method Details

#boundaryLevels(args) ⇒ Object

compute vertical boundary levels from full levels



465
466
467
468
469
470
471
472
473
# File 'lib/cdo.rb', line 465

def boundaryLevels(args)
  ilevels         = self.showlevel(:input => args[:input])[0].split.map(&:to_f)
  bound_levels    = Array.new(ilevels.size+1)
  bound_levels[0] = 0
  (1..ilevels.size).each {|i|
    bound_levels[i] =bound_levels[i-1] + 2*(ilevels[i-1]-bound_levels[i-1])
  }
  bound_levels
end

#checkObject

check if cdo backend is working



401
402
403
404
405
406
407
408
# File 'lib/cdo.rb', line 401

def check
  return false unless hasCdo

  retval = _call("#{@cdo} -V")
  pp retval if @debug

  return true
end

#cleanTempDirObject

remove tempfiles created from this or previous runs



457
458
459
# File 'lib/cdo.rb', line 457

def cleanTempDir
  @tempStore.cleanTempDir
end

#collectLogsObject

collect logging messages



378
379
380
381
382
383
384
385
# File 'lib/cdo.rb', line 378

def collectLogs
  if @logger.instance_variable_get(:'@logdev').filename.nil?
    @logFile.rewind
    return @logFile.read
  else
    return File.open(@logFile).readlines
  end
end

#hasCdo(path = @cdo) ⇒ Object

check if the CDO binary is present and works



393
394
395
396
397
398
# File 'lib/cdo.rb', line 393

def hasCdo(path=@cdo)
  executable = system("#{path} -V >/dev/null 2>&1")
  fullpath   = File.exists?(path) and File.executable?(path)

  return (executable or fullpath)
end

#help(operator = nil) ⇒ Object

show Cdo’s built-in help for given operator



368
369
370
371
372
373
374
375
# File 'lib/cdo.rb', line 368

def help(operator=nil)
  if operator.nil?
    puts _call([@cdo,'-h'].join(' '))[:stderr]
  else
    operator = operator.to_s
    puts _call([@cdo,'-h',operator].join(' ')).values_at(:stdout,:stderr)
  end
end

#openCdf(iFile) ⇒ Object

return cdf handle opened in append more



430
431
432
# File 'lib/cdo.rb', line 430

def openCdf(iFile)
  readCdf(iFile,'r+')
end

#readArray(iFile, varname) ⇒ Object

return narray for given variable name



435
436
437
438
439
440
441
442
443
# File 'lib/cdo.rb', line 435

def readArray(iFile,varname)
  filehandle = readCdf(iFile)
  if filehandle.var_names.include?(varname)
    # return the data array
    filehandle.var(varname).get
  else
    raise ArgumentError, "Cannot find variable '#{varname}'"
  end
end

#readCdf(iFile, mode = 'r') ⇒ Object

return cdf handle to given file readonly



421
422
423
424
425
426
427
# File 'lib/cdo.rb', line 421

def readCdf(iFile,mode='r')
  if @hasNetcdf then
    NumRu::NetCDF.open(iFile,mode)
  else
    raise LoadError,"Could not load ruby-netcdf"
  end
end

#readMaArray(iFile, varname) ⇒ Object

return a masked array for given variable name



446
447
448
449
450
451
452
453
454
# File 'lib/cdo.rb', line 446

def readMaArray(iFile,varname)
  filehandle = readCdf(iFile)
  if filehandle.var_names.include?(varname)
    # return the data array
    filehandle.var(varname).get_with_miss
  else
    raise ArgumentError,"Cannot find variable '#{varname}'"
  end
end

#showLogObject

print the loggin messaged



388
389
390
# File 'lib/cdo.rb', line 388

def showLog
  puts collectLogs
end

#thicknessOfLevels(args) ⇒ Object

compute level thicknesses from given full levels



476
477
478
479
480
481
482
483
484
# File 'lib/cdo.rb', line 476

def thicknessOfLevels(args)
  bound_levels = self.boundaryLevels(args)
  delta_levels    = []
  bound_levels.each_with_index {|v,i|
    next if 0 == i
    delta_levels << v - bound_levels[i-1]
  }
  delta_levels
end

#version(verbose = false) ⇒ Object

return CDO version string



411
412
413
414
415
416
417
418
# File 'lib/cdo.rb', line 411

def version(verbose=false)
  info = IO.popen("#{@cdo} -V 2>&1").readlines
  if verbose then
    return info.join
  else
    return info.first.split(/version/i).last.strip.split(' ').first
  end
end