Class: Cdo

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

Overview

Copyright 2011-2019 Ralf Mueller, [email protected]

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

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.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/cdo.rb', line 65

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,'daily')
  @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))


358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/cdo.rb', line 358

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.



62
63
64
# File 'lib/cdo.rb', line 62

def cdo
  @cdo
end

#debugObject

Returns the value of attribute debug.



62
63
64
# File 'lib/cdo.rb', line 62

def debug
  @debug
end

#envObject

Returns the value of attribute env.



62
63
64
# File 'lib/cdo.rb', line 62

def env
  @env
end

#filetypesObject (readonly)

Returns the value of attribute filetypes.



63
64
65
# File 'lib/cdo.rb', line 63

def filetypes
  @filetypes
end

#forceOutputObject

Returns the value of attribute forceOutput.



62
63
64
# File 'lib/cdo.rb', line 62

def forceOutput
  @forceOutput
end

#hasNetcdfObject (readonly)

Returns the value of attribute hasNetcdf.



63
64
65
# File 'lib/cdo.rb', line 63

def hasNetcdf
  @hasNetcdf
end

#logFileObject

Returns the value of attribute logFile.



62
63
64
# File 'lib/cdo.rb', line 62

def logFile
  @logFile
end

#loggingObject

Returns the value of attribute logging.



62
63
64
# File 'lib/cdo.rb', line 62

def logging
  @logging
end

#operatorsObject (readonly)

Returns the value of attribute operators.



63
64
65
# File 'lib/cdo.rb', line 63

def operators
  @operators
end

#returnCdfObject

Returns the value of attribute returnCdf.



62
63
64
# File 'lib/cdo.rb', line 62

def returnCdf
  @returnCdf
end

Instance Method Details

#boundaryLevels(args) ⇒ Object

compute vertical boundary levels from full levels



489
490
491
492
493
494
495
496
497
# File 'lib/cdo.rb', line 489

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



425
426
427
428
429
430
431
432
# File 'lib/cdo.rb', line 425

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



481
482
483
# File 'lib/cdo.rb', line 481

def cleanTempDir
  @tempStore.cleanTempDir
end

#collectLogsObject

collect logging messages



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

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



417
418
419
420
421
422
# File 'lib/cdo.rb', line 417

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



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

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



454
455
456
# File 'lib/cdo.rb', line 454

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

#readArray(iFile, varname) ⇒ Object

return narray for given variable name



459
460
461
462
463
464
465
466
467
# File 'lib/cdo.rb', line 459

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



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

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



470
471
472
473
474
475
476
477
478
# File 'lib/cdo.rb', line 470

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



412
413
414
# File 'lib/cdo.rb', line 412

def showLog
  puts collectLogs
end

#thicknessOfLevels(args) ⇒ Object

compute level thicknesses from given full levels



500
501
502
503
504
505
506
507
508
# File 'lib/cdo.rb', line 500

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



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

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